Annotate core modules

This commit is contained in:
Veronica Berglyd Olsen
2023-08-08 22:37:48 +02:00
parent d658a993c0
commit eeadfc95e3
16 changed files with 232 additions and 246 deletions
+8 -8
View File
@@ -30,7 +30,7 @@ import logging
import unicodedata
import xml.etree.ElementTree as ET
from typing import Any, Literal
from typing import Any, Literal, TypeGuard
from pathlib import Path
from datetime import datetime
from configparser import ConfigParser
@@ -134,7 +134,7 @@ def checkPath(value: Any, default: Path) -> Path:
# Validator Functions
# =============================================================================================== #
def isHandle(value: Any) -> bool:
def isHandle(value: Any) -> TypeGuard[str]:
"""Check if a string is a valid novelWriter handle.
Note: This is case sensitive. Must be lower case!
"""
@@ -148,7 +148,7 @@ def isHandle(value: Any) -> bool:
return True
def isTitleTag(value: Any) -> bool:
def isTitleTag(value: Any) -> TypeGuard[str]:
"""Check if a string is a valid title tag string."""
if not isinstance(value, str):
return False
@@ -288,8 +288,7 @@ def transferCase(source: str, target: str) -> str:
def fuzzyTime(seconds: int) -> str:
"""Converts a time difference in seconds into a fuzzy time string.
"""
"""Convert a time difference in seconds into a fuzzy time string."""
if seconds < 0:
return QCoreApplication.translate(
"Common", "in the future"
@@ -349,8 +348,7 @@ def fuzzyTime(seconds: int) -> str:
def numberToRoman(value: int, toLower: bool = False) -> str:
"""Convert an integer to a Roman number.
"""
"""Convert an integer to a Roman number."""
if not isinstance(value, int):
return "NAN"
if value < 1 or value > 4999:
@@ -423,12 +421,14 @@ def jsonEncode(data: dict | list | tuple, n: int = 0, nmax: int = 0) -> str:
return "".join(buffer)
def xmlIndent(tree: ET.Element | ET.ElementTree):
def xmlIndent(tree: ET.Element | ET.ElementTree) -> None:
"""A modified version of the XML indent function in the standard
library. It behaves more closely to how the one from lxml does.
"""
if isinstance(tree, ET.ElementTree):
tree = tree.getroot()
if not isinstance(tree, ET.Element):
return
indentations = ["\n"]
+16 -16
View File
@@ -153,7 +153,7 @@ class BuildSettings:
The settings can be packed/unpacked to/from a dictionary for JSON.
"""
def __init__(self):
def __init__(self) -> None:
self._name = ""
self._uuid = str(uuid.uuid4())
self._path = Path.home()
@@ -239,12 +239,12 @@ class BuildSettings:
# Setters
##
def setName(self, name: str):
def setName(self, name: str) -> None:
"""Set the build setting display name."""
self._name = str(name)
return
def setBuildID(self, value: str | uuid.UUID):
def setBuildID(self, value: str | uuid.UUID) -> None:
"""Set a UUID build ID."""
value = checkUuid(value, "")
if not value:
@@ -253,7 +253,7 @@ class BuildSettings:
self._uuid = value
return
def setLastPath(self, path: Path | str | None):
def setLastPath(self, path: Path | str | None) -> None:
"""Set the last used build path."""
if isinstance(path, str):
path = Path(path)
@@ -264,41 +264,41 @@ class BuildSettings:
self._changed = True
return
def setLastBuildName(self, name: str):
def setLastBuildName(self, name: str) -> None:
"""Set the last used build name."""
self._build = str(name).strip()
self._changed = True
return
def setLastFormat(self, value: nwBuildFmt):
def setLastFormat(self, value: nwBuildFmt) -> None:
"""Set the last used build format."""
if isinstance(value, nwBuildFmt):
self._format = value
self._changed = True
return
def setFiltered(self, tHandle: str):
def setFiltered(self, tHandle: str) -> None:
"""Set an item as filtered."""
self._excluded.discard(tHandle)
self._included.discard(tHandle)
self._changed = True
return
def setIncluded(self, tHandle: str):
def setIncluded(self, tHandle: str) -> None:
"""Set an item as explicitly included."""
self._excluded.discard(tHandle)
self._included.add(tHandle)
self._changed = True
return
def setExcluded(self, tHandle: str):
def setExcluded(self, tHandle: str) -> None:
"""Set an item as explicitly excluded."""
self._excluded.add(tHandle)
self._included.discard(tHandle)
self._changed = True
return
def setAllowRoot(self, tHandle: str, state: bool):
def setAllowRoot(self, tHandle: str, state: bool) -> None:
"""Set a specific root folder as allowed or not."""
if state is True:
self._skipRoot.discard(tHandle)
@@ -386,7 +386,7 @@ class BuildSettings:
return result
def resetChangedState(self):
def resetChangedState(self) -> None:
"""Reset the changed status of the settings object. This must be
called when the changes have been safely saved or passed on.
"""
@@ -410,7 +410,7 @@ class BuildSettings:
}
}
def unpack(self, data: dict):
def unpack(self, data: dict) -> None:
"""Unpack a dictionary and populate the class."""
settings = data.get("settings", {})
content = data.get("content", {})
@@ -454,13 +454,13 @@ class BuildCollection:
project folder.
"""
def __init__(self, project: NWProject):
def __init__(self, project: NWProject) -> None:
self._project = project
self._builds = {}
self._loadCollection()
return
def __len__(self):
def __len__(self) -> int:
"""Return the number of builds."""
return len(self._builds)
@@ -476,7 +476,7 @@ class BuildCollection:
build.unpack(self._builds[buildID])
return build
def setBuild(self, build: BuildSettings):
def setBuild(self, build: BuildSettings) -> None:
"""Set build settings data in the collection."""
if isinstance(build, BuildSettings):
buildID = build.buildID
@@ -484,7 +484,7 @@ class BuildCollection:
self._saveCollection()
return
def removeBuild(self, buildID: str):
def removeBuild(self, buildID: str) -> None:
"""Remove the a build from the collection."""
self._builds.pop(buildID, None)
self._saveCollection()
+7 -6
View File
@@ -80,7 +80,7 @@ class DocMerger:
and a new doc label. Calling this function resets the class.
"""
srcItem = self._project.tree[srcHandle]
if srcItem is None:
if srcItem is None or srcItem.itemParent is None:
return None
newHandle = self._project.newFile(docLabel, srcItem.itemParent)
@@ -210,7 +210,7 @@ class DocSplitter:
"""An iterator that will write each document in the buffer, and
return its new handle, parent handle, and sibling handle.
"""
if self._srcHandle is None or self._srcItem is None:
if self._srcHandle is None or self._srcItem is None or self._parHandle is None:
return
pHandle = self._parHandle
@@ -385,9 +385,10 @@ class ProjectBuilder:
aDoc = project.storage.getDocument(hChapter)
aDoc.writeDocument(f"## {lblNewChapter}\n\n")
hScene = project.newFile(lblNewScene, hChapter)
aDoc = project.storage.getDocument(hScene)
aDoc.writeDocument(f"### {lblNewScene}\n\n")
if hChapter:
hScene = project.newFile(lblNewScene, hChapter)
aDoc = project.storage.getDocument(hScene)
aDoc.writeDocument(f"### {lblNewScene}\n\n")
project.newRoot(nwItemClass.PLOT)
project.newRoot(nwItemClass.CHARACTER)
@@ -418,7 +419,7 @@ class ProjectBuilder:
aDoc.writeDocument(f"## {chTitle}\n\n% Synopsis: {chSynop}\n\n")
# Create chapter scenes
if numScenes > 0:
if numScenes > 0 and cHandle:
for sc in range(numScenes):
scTitle = self.tr("Scene {0}").format(f"{ch+1:d}.{sc+1:d}")
sHandle = project.newFile(scTitle, cHandle)
+3 -3
View File
@@ -54,7 +54,7 @@ class NWBuildDocument:
__slots__ = ("_project", "_build", "_queue", "_error", "_cache")
def __init__(self, project: NWProject, build: BuildSettings):
def __init__(self, project: NWProject, build: BuildSettings) -> None:
self._project = project
self._build = build
self._queue = []
@@ -91,12 +91,12 @@ class NWBuildDocument:
# Methods
##
def addDocument(self, tHandle: str):
def addDocument(self, tHandle: str) -> None:
"""Add a document to the build queue manually."""
self._queue.append(tHandle)
return
def queueAll(self):
def queueAll(self) -> None:
"""Queue all document as defined by the build settings."""
self._queue = []
filtered = self._build.buildItemFilter(self._project)
+23 -20
View File
@@ -26,13 +26,16 @@ from __future__ import annotations
import uuid
import logging
from typing import Any
from typing import TYPE_CHECKING, Any
from novelwriter.common import (
checkBool, checkInt, checkStringNone, checkUuid, isHandle, simplified
)
from novelwriter.core.status import NWStatus
if TYPE_CHECKING: # pragma: no cover
from novelwriter.core.project import NWProject
logger = logging.getLogger(__name__)
@@ -43,7 +46,7 @@ class NWProjectData:
the list of project items.
"""
def __init__(self, project):
def __init__(self, project: NWProject) -> None:
self._project = project
@@ -184,13 +187,13 @@ class NWProjectData:
# Methods
##
def incSaveCount(self):
def incSaveCount(self) -> None:
"""Increment the save count by one."""
self._saveCount += 1
self._project.setProjectChanged(True)
return
def incAutoCount(self):
def incAutoCount(self) -> None:
"""Increment the auto save count by one."""
self._autoCount += 1
self._project.setProjectChanged(True)
@@ -208,7 +211,7 @@ class NWProjectData:
# Setters
##
def setUuid(self, value: Any):
def setUuid(self, value: Any) -> None:
"""Set the project id."""
value = checkUuid(value, "")
if not value:
@@ -218,74 +221,74 @@ class NWProjectData:
self._project.setProjectChanged(True)
return
def setName(self, value: str | None):
def setName(self, value: str | None) -> None:
"""Set a new project name."""
if value != self._name:
self._name = simplified(str(value or ""))
self._project.setProjectChanged(True)
return
def setTitle(self, value: str | None):
def setTitle(self, value: str | None) -> None:
"""Set a new novel title."""
if value != self._title:
self._title = simplified(str(value or ""))
self._project.setProjectChanged(True)
return
def setAuthor(self, value: str | None):
def setAuthor(self, value: str | None) -> None:
"""Set the author value."""
if value != self._title:
self._author = simplified(str(value or ""))
self._project.setProjectChanged(True)
return
def setSaveCount(self, value: Any):
def setSaveCount(self, value: Any) -> None:
"""Set the save count from last session."""
self._saveCount = checkInt(value, 0)
self._project.setProjectChanged(True)
return
def setAutoCount(self, value: Any):
def setAutoCount(self, value: Any) -> None:
"""Set the auto save count from last session."""
self._autoCount = checkInt(value, 0)
self._project.setProjectChanged(True)
return
def setEditTime(self, value: Any):
def setEditTime(self, value: Any) -> None:
"""Set the edit time from last session."""
self._editTime = checkInt(value, 0)
self._project.setProjectChanged(True)
return
def setDoBackup(self, value: Any):
def setDoBackup(self, value: Any) -> None:
"""Set the do write backup flag."""
if value != self._doBackup:
self._doBackup = checkBool(value, False)
self._project.setProjectChanged(True)
return
def setLanguage(self, value: str | None):
def setLanguage(self, value: str | None) -> None:
"""Set the project language."""
if value != self._language:
self._language = checkStringNone(value, None)
self._project.setProjectChanged(True)
return
def setSpellCheck(self, value: Any):
def setSpellCheck(self, value: Any) -> None:
"""Set the spell check flag."""
if value != self._spellCheck:
self._spellCheck = checkBool(value, False)
self._project.setProjectChanged(True)
return
def setSpellLang(self, value: str | None):
def setSpellLang(self, value: str | None) -> None:
"""Set the spell check language."""
if value != self._spellLang:
self._spellLang = checkStringNone(value, None)
self._project.setProjectChanged(True)
return
def setLastHandle(self, value: str | None, component: str):
def setLastHandle(self, value: str | None, component: str) -> None:
"""Set a last used handle into the handle registry for a given
component.
"""
@@ -294,7 +297,7 @@ class NWProjectData:
self._project.setProjectChanged(True)
return
def setLastHandles(self, value: dict):
def setLastHandles(self, value: dict) -> None:
"""Set the full last handles dictionary to a new set of values.
This is intended to be used at project load.
"""
@@ -305,7 +308,7 @@ class NWProjectData:
self._project.setProjectChanged(True)
return
def setInitCounts(self, novel: Any = None, notes: Any = None):
def setInitCounts(self, novel: Any = None, notes: Any = None) -> None:
"""Set the word count totals for novel and note files."""
if novel is not None:
self._initCounts[0] = checkInt(novel, 0)
@@ -315,7 +318,7 @@ class NWProjectData:
self._currCounts[1] = checkInt(notes, 0)
return
def setCurrCounts(self, novel: Any = None, notes: Any = None):
def setCurrCounts(self, novel: Any = None, notes: Any = None) -> None:
"""Set the word count totals for novel and note files."""
if novel is not None:
self._currCounts[0] = checkInt(novel, 0)
@@ -323,7 +326,7 @@ class NWProjectData:
self._currCounts[1] = checkInt(notes, 0)
return
def setAutoReplace(self, value: dict):
def setAutoReplace(self, value: dict) -> None:
"""Set the auto-replace dictionary."""
if isinstance(value, dict):
self._autoReplace = {}
+15 -11
View File
@@ -110,7 +110,7 @@ class ProjectXMLReader:
Rev 1: Drops the titleFormat section of settings.
"""
def __init__(self, path):
def __init__(self, path: str | Path) -> None:
self._path = Path(path)
self._state = XMLReadState.NO_ACTION
self._root = ""
@@ -236,7 +236,7 @@ class ProjectXMLReader:
# Internal Functions
##
def _parseProjectMeta(self, xSection: ET.Element, data: NWProjectData):
def _parseProjectMeta(self, xSection: ET.Element, data: NWProjectData) -> None:
"""Parse the project section of the XML file."""
logger.debug("Parsing <project> section")
@@ -267,7 +267,7 @@ class ProjectXMLReader:
return
def _parseProjectSettings(self, xSection: ET.Element, data: NWProjectData):
def _parseProjectSettings(self, xSection: ET.Element, data: NWProjectData) -> None:
"""Parse the settings section of the XML file."""
logger.debug("Parsing <settings> section")
@@ -307,7 +307,9 @@ class ProjectXMLReader:
return
def _parseProjectContent(self, xSection: ET.Element, data: NWProjectData, content: list):
def _parseProjectContent(
self, xSection: ET.Element, data: NWProjectData, content: list
) -> None:
"""Parse the content section of the XML file."""
logger.debug("Parsing <content> section")
@@ -362,7 +364,9 @@ class ProjectXMLReader:
return
def _parseProjectContentLegacy(self, xSection: ET.Element, data: NWProjectData, content: list):
def _parseProjectContentLegacy(
self, xSection: ET.Element, data: NWProjectData, content: list
) -> None:
"""Parse the content section of the XML file for older versions."""
logger.debug("Parsing <content> section (legacy format)")
@@ -438,7 +442,7 @@ class ProjectXMLReader:
return
def _parseStatusImport(self, xItem: ET.Element, sObject: NWStatus):
def _parseStatusImport(self, xItem: ET.Element, sObject: NWStatus) -> None:
"""Parse a status or importance entry."""
for xEntry in xItem:
if xEntry.tag == "entry":
@@ -447,7 +451,7 @@ class ProjectXMLReader:
green = checkInt(xEntry.attrib.get("green", 0), 0)
blue = checkInt(xEntry.attrib.get("blue", 0), 0)
count = checkInt(xEntry.attrib.get("count", 0), 0)
sObject.write(key, xEntry.text, (red, green, blue), count)
sObject.write(key, xEntry.text or "", (red, green, blue), count)
return
def _parseDictKeyText(self, xItem: ET.Element) -> dict:
@@ -460,7 +464,7 @@ class ProjectXMLReader:
result[xEntry.attrib["key"]] = checkString(xEntry.text, "")
return result
def _parseDictTagText(self, xItem):
def _parseDictTagText(self, xItem) -> dict:
"""Parse a dictionary stored with key as the tag and the value
as the text property.
"""
@@ -476,7 +480,7 @@ class ProjectXMLWriter:
very latest spec.
"""
def __init__(self, path):
def __init__(self, path: str | Path) -> None:
self._path = Path(path)
self._error = None
return
@@ -585,13 +589,13 @@ class ProjectXMLWriter:
def _packSingleValue(
self, xParent: ET.Element, name: str, value: str | None, attrib: dict | None = None
):
) -> None:
"""Pack a single value into an XML element."""
xItem = ET.SubElement(xParent, name, attrib=attrib or {})
xItem.text = str(value) or ""
return
def _packDictKeyValue(self, xParent: ET.Element, name: str, data: dict):
def _packDictKeyValue(self, xParent: ET.Element, name: str, data: dict) -> None:
"""Pack the entries of a dictionary into an XML element."""
xItem = ET.SubElement(xParent, name)
for key, value in data.items():
+2 -2
View File
@@ -47,7 +47,7 @@ class NWSessionLog:
format. That is, one JSON object per line.
"""
def __init__(self, project: NWProject):
def __init__(self, project: NWProject) -> None:
self._project = project
self._start = 0.0
return
@@ -65,7 +65,7 @@ class NWSessionLog:
# Methods
##
def startSession(self):
def startSession(self) -> None:
"""Start the writing session."""
self._start = time()
return
+5 -5
View File
@@ -45,7 +45,7 @@ class NWSpellEnchant:
between spell check tools.
"""
def __init__(self, project: NWProject):
def __init__(self, project: NWProject) -> None:
self._project = project
self._dictObj = FakeEnchant()
self._userDict = UserDictionary(project)
@@ -165,7 +165,7 @@ class NWSpellEnchant:
class FakeEnchant:
"""Fallback for when Enchant is selected, but not installed."""
def __init__(self):
def __init__(self) -> None:
class FakeProvider:
name = ""
@@ -189,7 +189,7 @@ class FakeEnchant:
class UserDictionary:
def __init__(self, project: NWProject):
def __init__(self, project: NWProject) -> None:
self._project = project
self._words = set()
self._path = None
@@ -210,7 +210,7 @@ class UserDictionary:
self._words.add(word)
return True
def load(self):
def load(self) -> None:
"""Load the user's dictionary."""
self._path = self._project.storage.getMetaFile(nwFiles.DICT_FILE)
if not isinstance(self._path, Path):
@@ -224,7 +224,7 @@ class UserDictionary:
logException()
return
def save(self):
def save(self) -> None:
"""Save the user's dictionary."""
if self._path is None:
self._path = self._project.storage.getMetaFile(nwFiles.DICT_FILE)
+43 -61
View File
@@ -27,6 +27,8 @@ from __future__ import annotations
import random
import logging
from typing import ItemsView, Iterator, KeysView, Literal, TypeGuard, ValuesView
from PyQt5.QtGui import QIcon, QPainter, QPainterPath, QPixmap, QColor
from PyQt5.QtCore import QRectF, Qt
@@ -41,9 +43,9 @@ class NWStatus:
STATUS = 1
IMPORT = 2
def __init__(self, type):
def __init__(self, kind: Literal[1, 2]) -> None:
self._type = type
self._type = kind
self._store = {}
self._default = None
@@ -66,7 +68,7 @@ class NWStatus:
return
def write(self, key, name, col, count=None):
def write(self, key: str | None, name: str, col: tuple, count: int | None = None) -> str:
"""Add or update a status entry. If the key is invalid, a new
key is generated.
"""
@@ -96,10 +98,8 @@ class NWStatus:
return key
def remove(self, key):
"""Remove an entry in the list, but not if the count is larger
than 0.
"""
def remove(self, key: str) -> bool:
"""Remove an entry in the list, except if the count > 0."""
if key not in self._store:
return False
if self._store[key]["count"] > 0:
@@ -116,59 +116,49 @@ class NWStatus:
return True
def check(self, value):
"""Check the key against the stored status names.
"""
def check(self, value: str) -> str:
"""Check the key against the stored status names."""
if self._isKey(value) and value in self._store:
return value
elif self._default is not None:
return self._default
else:
return ""
return ""
def name(self, key):
"""Return the name associated with a given key.
"""
def name(self, key: str) -> str:
"""Return the name associated with a given key."""
if key in self._store:
return self._store[key]["name"]
elif self._default is not None:
return self._store[self._default]["name"]
else:
return ""
return ""
def cols(self, key):
def cols(self, key: str) -> tuple[int, int, int]:
"""Return the colours associated with a given key.
"""
if key in self._store:
return self._store[key]["cols"]
elif self._default is not None:
return self._store[self._default]["cols"]
else:
return (100, 100, 100)
return 100, 100, 100
def count(self, key):
"""Return the count associated with a given key.
"""
def count(self, key: str) -> int:
"""Return the count associated with a given key."""
if key in self._store:
return self._store[key]["count"]
elif self._default is not None:
return self._store[self._default]["count"]
else:
return 0
return 0
def icon(self, key):
"""Return the icon associated with a given key.
"""
def icon(self, key: str) -> QIcon:
"""Return the icon associated with a given key."""
if key in self._store:
return self._store[key]["icon"]
elif self._default is not None:
return self._store[self._default]["icon"]
else:
return self._defaultIcon
return self._defaultIcon
def reorder(self, order):
"""Reorder the items according to list.
"""
def reorder(self, order: list[str]) -> bool:
"""Reorder the items according to list."""
if len(order) != len(self._store):
logger.error("Length mismatch between new and old order")
return False
@@ -188,23 +178,20 @@ class NWStatus:
return True
def resetCounts(self):
"""Clear the counts of references to the status entries.
"""
def resetCounts(self) -> None:
"""Clear the counts of references to the status entries."""
for key in self._store:
self._store[key]["count"] = 0
return
def increment(self, key):
"""Increment the counter for a given entry.
"""
def increment(self, key: str) -> None:
"""Increment the counter for a given entry."""
if key in self._store:
self._store[key]["count"] += 1
return
def pack(self):
"""Pack the status entries into a dictionary.
"""
def pack(self) -> Iterator[tuple[str, dict]]:
"""Pack the status entries into a dictionary."""
for key, data in self._store.items():
yield (data["name"], {
"key": key,
@@ -215,25 +202,22 @@ class NWStatus:
})
return
def unpack(self, data):
"""Unpack a data dictionary and set the class values.
"""
def unpack(self, data: dict) -> None:
"""Unpack a data dictionary and set the class values."""
self._store = {}
self._default = None
for key, entry in data.items():
label = entry.get("label", "")
colour = entry.get("colour", (100, 100, 100))
count = entry.get("count", 0)
self.write(key, label, colour, count)
return True
return
##
# Internal Functions
##
def _newKey(self):
def _newKey(self) -> str:
"""Generate a new key for a status flag. This method is
recursive, but should only fail if there is an issue with the
random number generator or the user has added a lot of status
@@ -245,9 +229,8 @@ class NWStatus:
key = self._newKey()
return key
def _isKey(self, value):
"""Check if a value is a key or not.
"""
def _isKey(self, value: str | None) -> TypeGuard[str]:
"""Check if a value is a key or not."""
if not isinstance(value, str):
return False
if len(value) != 7:
@@ -259,9 +242,8 @@ class NWStatus:
return False
return True
def _createIcon(self, red, green, blue):
"""Generate an icon for a status label.
"""
def _createIcon(self, red: int, green: int, blue: int) -> QIcon:
"""Generate an icon for a status label."""
pixmap = QPixmap(self._iPX, self._iPX)
pixmap.fill(Qt.transparent)
@@ -276,22 +258,22 @@ class NWStatus:
# Iterator Bits
##
def __len__(self):
def __len__(self) -> int:
return len(self._store)
def __getitem__(self, key):
def __getitem__(self, key: str) -> dict:
return self._store[key]
def __iter__(self):
def __iter__(self) -> Iterator[dict]:
return iter(self._store)
def keys(self):
def keys(self) -> KeysView[str]:
return self._store.keys()
def items(self):
def items(self) -> ItemsView[str, dict]:
return self._store.items()
def values(self):
def values(self) -> ValuesView[dict]:
return self._store.values()
# END Class NWStatus
+9 -9
View File
@@ -55,7 +55,7 @@ class NWStorage:
MODE_INPLACE = 1
MODE_ARCHIVE = 2
def __init__(self, project: NWProject):
def __init__(self, project: NWProject) -> None:
self._project = project
self._storagePath = None
self._runtimePath = None
@@ -63,7 +63,7 @@ class NWStorage:
self._openMode = self.MODE_INACTIVE
return
def clear(self):
def clear(self) -> None:
"""Reset internal variables."""
self._storagePath = None
self._runtimePath = None
@@ -145,7 +145,7 @@ class NWStorage:
return True
return True
def closeSession(self):
def closeSession(self) -> None:
"""Run tasks related to closing the session."""
self.clearLockFile()
self.clear()
@@ -353,11 +353,11 @@ class _LegacyStorage:
file/folder layout to the current project format.
"""
def __init__(self, project: NWProject):
def __init__(self, project: NWProject) -> None:
self._project = project
return
def legacyDataFolder(self, path: Path, child: Path):
def legacyDataFolder(self, path: Path, child: Path) -> None:
"""Handle the content of a legacy data folder from a version 1.0
project.
"""
@@ -396,7 +396,7 @@ class _LegacyStorage:
return
def deprecatedFiles(self, path: Path):
def deprecatedFiles(self, path: Path) -> None:
"""Handle files that are no longer used by novelWriter."""
self._convertOldWordList( # Changed in 2.1 Beta 1
path / "meta" / "wordlist.txt",
@@ -440,7 +440,7 @@ class _LegacyStorage:
# Internal Functions
##
def _convertOldWordList(self, wordList: Path, wordJson: Path):
def _convertOldWordList(self, wordList: Path, wordJson: Path) -> None:
"""Convert the old word list plain text file to new format."""
if wordJson.exists() or not wordList.exists():
# If the new file already exists, we won't overwrite it
@@ -466,7 +466,7 @@ class _LegacyStorage:
return
def _convertOldLogFile(self, sessLog: Path, sessJson: Path):
def _convertOldLogFile(self, sessLog: Path, sessJson: Path) -> None:
"""Convert the old text log file format to the new JSON Lines
format.
"""
@@ -507,7 +507,7 @@ class _LegacyStorage:
return
def _convertOldOptionsFile(self, optsOld: Path, optsNew: Path):
def _convertOldOptionsFile(self, optsOld: Path, optsNew: Path) -> None:
"""Convert the old options state file format to the format."""
if optsNew.exists() or not optsOld.exists():
# If the new file already exists, we won't overwrite it
+12 -12
View File
@@ -49,12 +49,12 @@ class ToHtml(Tokenizer):
M_EXPORT = 1 # Tweak output for saving to HTML or printing
M_EBOOK = 2 # Tweak output for converting to epub
def __init__(self, project: NWProject):
def __init__(self, project: NWProject) -> None:
super().__init__(project)
self._genMode = self.M_EXPORT
self._cssStyles = True
self._fullHTML = []
self._fullHTML: list[str] = []
# Internals
self._trMap = {}
@@ -67,14 +67,14 @@ class ToHtml(Tokenizer):
##
@property
def fullHTML(self):
def fullHTML(self) -> list[str]:
return self._fullHTML
##
# Setters
##
def setPreview(self, doComments: bool, doSynopsis: bool):
def setPreview(self, doComments: bool, doSynopsis: bool) -> None:
"""If we're using this class to generate markdown preview, we
need to make a few changes to formatting, which is managed by
these flags.
@@ -85,14 +85,14 @@ class ToHtml(Tokenizer):
self._doSynopsis = doSynopsis
return
def setStyles(self, cssStyles: bool):
def setStyles(self, cssStyles: bool) -> None:
"""Enable or disable CSS styling. Some elements may still have
class tags.
"""
self._cssStyles = cssStyles
return
def setReplaceUnicode(self, doReplace: bool):
def setReplaceUnicode(self, doReplace: bool) -> None:
"""Set the translation map to either minimal or full unicode for
html entities replacement.
"""
@@ -113,7 +113,7 @@ class ToHtml(Tokenizer):
"""Return the size of the full HTML result."""
return sum([len(x) for x in self._fullHTML])
def doPreProcessing(self):
def doPreProcessing(self) -> None:
"""Extend the auto-replace to also properly encode some unicode
characters into their respective HTML entities.
"""
@@ -121,7 +121,7 @@ class ToHtml(Tokenizer):
self._text = self._text.translate(self._trMap)
return
def doConvert(self):
def doConvert(self) -> None:
"""Convert the list of text tokens into a HTML document saved
to _result.
"""
@@ -299,7 +299,7 @@ class ToHtml(Tokenizer):
return
def saveHtml5(self, path: str | Path):
def saveHtml5(self, path: str | Path) -> None:
"""Save the data to an HTML file."""
with open(path, mode="w", encoding="utf-8") as fObj:
fObj.write((
@@ -326,7 +326,7 @@ class ToHtml(Tokenizer):
logger.info("Wrote file: %s", path)
return
def saveHtmlJson(self, path: str | Path):
def saveHtmlJson(self, path: str | Path) -> None:
"""Save the data to a JSON file."""
timeStamp = time()
data = {
@@ -347,7 +347,7 @@ class ToHtml(Tokenizer):
logger.info("Wrote file: %s", path)
return
def replaceTabs(self, nSpaces: int = 8, spaceChar: str = "&nbsp;"):
def replaceTabs(self, nSpaces: int = 8, spaceChar: str = "&nbsp;") -> None:
"""Replace tabs with spaces in the html."""
htmlText = []
tabSpace = spaceChar*nSpaces
@@ -357,7 +357,7 @@ class ToHtml(Tokenizer):
self._fullHTML = htmlText
return
def getStyleSheet(self) -> list:
def getStyleSheet(self) -> list[str]:
"""Generate a stylesheet for the current settings."""
styles = []
if not self._cssStyles:
+34 -34
View File
@@ -44,7 +44,7 @@ from novelwriter.core.project import NWProject
logger = logging.getLogger(__name__)
def stripEscape(text):
def stripEscape(text) -> str:
"""Helper function to strip escaped Markdown characters from
paragraph text.
"""
@@ -100,7 +100,7 @@ class Tokenizer(ABC):
A_IND_L = 0x0100 # Left indentation
A_IND_R = 0x0200 # Right indentation
def __init__(self, project: NWProject):
def __init__(self, project: NWProject) -> None:
self._project = project
@@ -191,116 +191,116 @@ class Tokenizer(ABC):
# Setters
##
def setTitleFormat(self, hFormat: str):
def setTitleFormat(self, hFormat: str) -> None:
"""Set the title format pattern."""
self._fmtTitle = hFormat.strip()
return
def setChapterFormat(self, hFormat: str):
def setChapterFormat(self, hFormat: str) -> None:
"""Set the chapert format pattern."""
self._fmtChapter = hFormat.strip()
return
def setUnNumberedFormat(self, hFormat: str):
def setUnNumberedFormat(self, hFormat: str) -> None:
"""Set the unnumbered format pattern."""
self._fmtUnNum = hFormat.strip()
return
def setSceneFormat(self, hFormat: str, hide: bool):
def setSceneFormat(self, hFormat: str, hide: bool) -> None:
"""Set the scene format pattern and hidden status."""
self._fmtScene = hFormat.strip()
self._hideScene = hide
return
def setSectionFormat(self, hFormat: str, hide: bool):
def setSectionFormat(self, hFormat: str, hide: bool) -> None:
"""Set the section format pattern and hidden status."""
self._fmtSection = hFormat.strip()
self._hideSection = hide
return
def setFont(self, family: str, size: int, isFixed: bool = False):
def setFont(self, family: str, size: int, isFixed: bool = False) -> None:
"""Set the build font."""
self._textFont = family
self._textSize = round(int(size))
self._textFixed = isFixed
return
def setLineHeight(self, height: float):
def setLineHeight(self, height: float) -> None:
"""Set the line height between 0.5 and 5.0."""
self._lineHeight = min(max(float(height), 0.5), 5.0)
return
def setBlockIndent(self, indent: float):
def setBlockIndent(self, indent: float) -> None:
"""Set the block indent between 0.0 and 10.0."""
self._blockIndent = min(max(float(indent), 0.0), 10.0)
return
def setJustify(self, state: bool):
def setJustify(self, state: bool) -> None:
"""Enable or disable text justification."""
self._doJustify = state
return
def setTitleMargins(self, upper: float, lower: float):
def setTitleMargins(self, upper: float, lower: float) -> None:
"""Set the upper and lower title margin."""
self._marginTitle = (float(upper), float(lower))
return
def setHead1Margins(self, upper: float, lower: float):
def setHead1Margins(self, upper: float, lower: float) -> None:
"""Set the upper and lower header 1 margin."""
self._marginHead1 = (float(upper), float(lower))
return
def setHead2Margins(self, upper: float, lower: float):
def setHead2Margins(self, upper: float, lower: float) -> None:
"""Set the upper and lower header 2 margin."""
self._marginHead2 = (float(upper), float(lower))
return
def setHead3Margins(self, upper: float, lower: float):
def setHead3Margins(self, upper: float, lower: float) -> None:
"""Set the upper and lower header 3 margin."""
self._marginHead3 = (float(upper), float(lower))
return
def setHead4Margins(self, upper: float, lower: float):
def setHead4Margins(self, upper: float, lower: float) -> None:
"""Set the upper and lower header 4 margin."""
self._marginHead4 = (float(upper), float(lower))
return
def setTextMargins(self, upper: float, lower: float):
def setTextMargins(self, upper: float, lower: float) -> None:
"""Set the upper and lower text margin."""
self._marginText = (float(upper), float(lower))
return
def setMetaMargins(self, upper: float, lower: float):
def setMetaMargins(self, upper: float, lower: float) -> None:
"""Set the upper and lower meta text margin."""
self._marginMeta = (float(upper), float(lower))
return
def setLinkHeaders(self, state: bool):
def setLinkHeaders(self, state: bool) -> None:
"""Enable or disable adding an anchor before headers."""
self._linkHeaders = state
return
def setBodyText(self, state: bool):
def setBodyText(self, state: bool) -> None:
"""Include body text in build."""
self._doBodyText = state
return
def setSynopsis(self, state: bool):
def setSynopsis(self, state: bool) -> None:
"""Include synopsis comments in build."""
self._doSynopsis = state
return
def setComments(self, state: bool):
def setComments(self, state: bool) -> None:
"""Include comments in build."""
self._doComments = state
return
def setKeywords(self, state: bool):
def setKeywords(self, state: bool) -> None:
"""Include keywords in build."""
self._doKeywords = state
return
def setKeepMarkdown(self, state: bool):
def setKeepMarkdown(self, state: bool) -> None:
"""Keep original markdown during build."""
self._keepMarkdown = state
return
@@ -310,7 +310,7 @@ class Tokenizer(ABC):
##
@abstractmethod
def doConvert(self):
def doConvert(self) -> None:
raise NotImplementedError
def addRootHeading(self, tHandle: str) -> bool:
@@ -365,7 +365,7 @@ class Tokenizer(ABC):
return True
def doPreProcessing(self):
def doPreProcessing(self) -> None:
"""Run trough the various replace dictionaries."""
# Process the user's auto-replace dictionary
autoReplace = self._project.data.autoReplace
@@ -382,7 +382,7 @@ class Tokenizer(ABC):
return
def tokenizeText(self):
def tokenizeText(self) -> None:
"""Scan the text for either lines starting with specific
characters that indicate headers, comments, commands etc, or
just contain plain text. In the case of plain text, apply the
@@ -742,14 +742,14 @@ class Tokenizer(ABC):
return True
def saveRawMarkdown(self, path: str | Path):
def saveRawMarkdown(self, path: str | Path) -> None:
"""Save the raw text to a plain text file."""
with open(path, mode="w", encoding="utf-8") as outFile:
for nwdPage in self._allMarkdown:
outFile.write(nwdPage)
return
def saveRawMarkdownJSON(self, path: str | Path):
def saveRawMarkdownJSON(self, path: str | Path) -> None:
"""Save the raw text to a JSON file."""
timeStamp = time()
data = {
@@ -773,30 +773,30 @@ class Tokenizer(ABC):
class HeadingFormatter:
def __init__(self, project: NWProject):
def __init__(self, project: NWProject) -> None:
self._project = project
self._chCount = 0
self._scChCount = 0
self._scAbsCount = 0
return
def incChapter(self):
def incChapter(self) -> None:
"""Increment the chapter counter."""
self._chCount += 1
return
def incScene(self):
def incScene(self) -> None:
"""Increment the scene counters."""
self._scChCount += 1
self._scAbsCount += 1
return
def resetScene(self):
def resetScene(self) -> None:
"""Reset the chapter scene counter."""
self._scChCount = 0
return
def apply(self, hFormat: str, text: str):
def apply(self, hFormat: str, text: str) -> str:
"""Apply formatting to a specific heading."""
hFormat = hFormat.replace(nwHeadFmt.TITLE, text)
hFormat = hFormat.replace(nwHeadFmt.CH_NUM, str(self._chCount))
+8 -10
View File
@@ -45,12 +45,10 @@ class ToMarkdown(Tokenizer):
M_STD = 0 # Standard Markdown
M_GH = 1 # GitHub Markdown
def __init__(self, project: NWProject):
def __init__(self, project: NWProject) -> None:
super().__init__(project)
self._genMode = self.M_STD
self._fullMD = []
self._fullMD: list[str] = []
return
##
@@ -58,7 +56,7 @@ class ToMarkdown(Tokenizer):
##
@property
def fullMD(self) -> list:
def fullMD(self) -> list[str]:
"""Return the markdown as a list."""
return self._fullMD
@@ -66,11 +64,11 @@ class ToMarkdown(Tokenizer):
# Setters
##
def setStandardMarkdown(self):
def setStandardMarkdown(self) -> None:
self._genMode = self.M_STD
return
def setGitHubMarkdown(self):
def setGitHubMarkdown(self) -> None:
self._genMode = self.M_GH
return
@@ -82,7 +80,7 @@ class ToMarkdown(Tokenizer):
"""Return the size of the full Markdown result."""
return sum([len(x) for x in self._fullMD])
def doConvert(self):
def doConvert(self) -> None:
"""Convert the list of text tokens into a HTML document saved
to theResult.
"""
@@ -175,14 +173,14 @@ class ToMarkdown(Tokenizer):
return
def saveMarkdown(self, path: str | Path):
def saveMarkdown(self, path: str | Path) -> None:
"""Save the data to a plain text file."""
with open(path, mode="w", encoding="utf-8") as outFile:
outFile.write("".join(self._fullMD))
logger.info("Wrote file: %s", path)
return
def replaceTabs(self, nSpaces: int = 8, spaceChar: str = " "):
def replaceTabs(self, nSpaces: int = 8, spaceChar: str = " ") -> None:
"""Replace tabs with spaces."""
spaces = spaceChar*nSpaces
self._fullMD = [p.replace("\t", spaces) for p in self._fullMD]
+44 -46
View File
@@ -98,7 +98,7 @@ class ToOdt(Tokenizer):
Test with: https://odfvalidator.org/
"""
def __init__(self, project: NWProject, isFlat: bool):
def __init__(self, project: NWProject, isFlat: bool) -> None:
super().__init__(project)
self._isFlat = isFlat # Flat: .fodt, otherwise .odt
@@ -188,7 +188,7 @@ class ToOdt(Tokenizer):
# Setters
##
def setLanguage(self, language: str):
def setLanguage(self, language: str) -> None:
"""Set language for the document."""
if language:
langBits = language.split("_")
@@ -197,7 +197,7 @@ class ToOdt(Tokenizer):
self._dCountry = langBits[1]
return
def setColourHeaders(self, state: bool):
def setColourHeaders(self, state: bool) -> None:
"""Enable/disable coloured headings and comments."""
self._colourHead = state
return
@@ -205,7 +205,7 @@ class ToOdt(Tokenizer):
def setPageLayout(
self, width: int | float, height: int | float,
top: int | float, bottom: int | float, left: int | float, right: int | float
):
) -> None:
"""Set the document page size and margins in millimetres."""
self._mDocWidth = f"{width/10.0:.3f}cm"
self._mDocHeight = f"{height/10.0:.3f}cm"
@@ -219,7 +219,7 @@ class ToOdt(Tokenizer):
# Class Methods
##
def initDocument(self):
def initDocument(self) -> None:
"""Initialises a new open document XML tree."""
# Initialise Variables
# ====================
@@ -381,7 +381,7 @@ class ToOdt(Tokenizer):
return
def doConvert(self):
def doConvert(self) -> None:
"""Convert the list of text tokens into XML elements."""
self._result = "" # Not used, but cleared just in case
@@ -599,7 +599,7 @@ class ToOdt(Tokenizer):
def _addTextPar(
self, styleName: str, oStyle: ODTParagraphStyle, tText: str, tFmt: str = "",
isHead: bool = False, oLevel: str | None = None
):
) -> None:
"""Add a text paragraph to the text XML element."""
tAttr = {}
tAttr[_mkTag("text", "style-name")] = self._paraStyle(styleName, oStyle)
@@ -726,7 +726,7 @@ class ToOdt(Tokenizer):
# Style Elements
##
def _pageStyles(self):
def _pageStyles(self) -> None:
"""Set the default page style."""
tAttr = {}
tAttr[_mkTag("style", "name")] = "PM1"
@@ -756,7 +756,7 @@ class ToOdt(Tokenizer):
return
def _defaultStyles(self):
def _defaultStyles(self) -> None:
"""Set the default styles."""
# Add Paragraph Family Style
# ==========================
@@ -829,7 +829,7 @@ class ToOdt(Tokenizer):
return
def _useableStyles(self):
def _useableStyles(self) -> None:
"""Set the usable styles."""
# Add Text Body Style
# ===================
@@ -1002,7 +1002,7 @@ class ToOdt(Tokenizer):
return
def _writeHeader(self):
def _writeHeader(self) -> None:
"""Write the header elements."""
tAttr = {}
tAttr[_mkTag("style", "name")] = "Standard"
@@ -1048,7 +1048,7 @@ class ODTParagraphStyle:
VALID_CLASS = ["text", "chapter"]
VALID_WEIGHT = ["normal", "inherit", "bold"]
def __init__(self):
def __init__(self) -> None:
# Attributes
self._mAttr = {
@@ -1087,26 +1087,26 @@ class ODTParagraphStyle:
# Attribute Setters
##
def setDisplayName(self, value: str | None):
def setDisplayName(self, value: str | None) -> None:
self._mAttr["display-name"][1] = value
return
def setParentStyleName(self, value: str | None):
def setParentStyleName(self, value: str | None) -> None:
self._mAttr["parent-style-name"][1] = value
return
def setNextStyleName(self, value: str | None):
def setNextStyleName(self, value: str | None) -> None:
self._mAttr["next-style-name"][1] = value
return
def setOutlineLevel(self, value: str | None):
def setOutlineLevel(self, value: str | None) -> None:
if value in self.VALID_LEVEL:
self._mAttr["default-outline-level"][1] = value
else:
self._mAttr["default-outline-level"][1] = None
return
def setClass(self, value: str | None):
def setClass(self, value: str | None) -> None:
if value in self.VALID_CLASS:
self._mAttr["class"][1] = value
else:
@@ -1117,41 +1117,41 @@ class ODTParagraphStyle:
# Paragraph Setters
##
def setMarginTop(self, value: str | None):
def setMarginTop(self, value: str | None) -> None:
self._pAttr["margin-top"][1] = value
return
def setMarginBottom(self, value: str | None):
def setMarginBottom(self, value: str | None) -> None:
self._pAttr["margin-bottom"][1] = value
return
def setMarginLeft(self, value: str | None):
def setMarginLeft(self, value: str | None) -> None:
self._pAttr["margin-left"][1] = value
return
def setMarginRight(self, value: str | None):
def setMarginRight(self, value: str | None) -> None:
self._pAttr["margin-right"][1] = value
return
def setLineHeight(self, value: str | None):
def setLineHeight(self, value: str | None) -> None:
self._pAttr["line-height"][1] = value
return
def setTextAlign(self, value: str | None):
def setTextAlign(self, value: str | None) -> None:
if value in self.VALID_ALIGN:
self._pAttr["text-align"][1] = value
else:
self._pAttr["text-align"][1] = None
return
def setBreakBefore(self, value: str | None):
def setBreakBefore(self, value: str | None) -> None:
if value in self.VALID_BREAK:
self._pAttr["break-before"][1] = value
else:
self._pAttr["break-before"][1] = None
return
def setBreakAfter(self, value: str | None):
def setBreakAfter(self, value: str | None) -> None:
if value in self.VALID_BREAK:
self._pAttr["break-after"][1] = value
else:
@@ -1162,30 +1162,30 @@ class ODTParagraphStyle:
# Text Setters
##
def setFontName(self, value: str | None):
def setFontName(self, value: str | None) -> None:
self._tAttr["font-name"][1] = value
return
def setFontFamily(self, value: str | None):
def setFontFamily(self, value: str | None) -> None:
self._tAttr["font-family"][1] = value
return
def setFontSize(self, value: str | None):
def setFontSize(self, value: str | None) -> None:
self._tAttr["font-size"][1] = value
return
def setFontWeight(self, value: str | None):
def setFontWeight(self, value: str | None) -> None:
if value in self.VALID_WEIGHT:
self._tAttr["font-weight"][1] = value
else:
self._tAttr["font-weight"][1] = None
return
def setColor(self, value: str | None):
def setColor(self, value: str | None) -> None:
self._tAttr["color"][1] = value
return
def setOpacity(self, value: str | None):
def setOpacity(self, value: str | None) -> None:
self._tAttr["opacity"][1] = value
return
@@ -1193,7 +1193,7 @@ class ODTParagraphStyle:
# Methods
##
def checkNew(self, refStyle: ODTParagraphStyle):
def checkNew(self, refStyle: ODTParagraphStyle) -> bool:
"""Check if there are new settings in refStyle that differ from
those in the current object.
"""
@@ -1217,7 +1217,7 @@ class ODTParagraphStyle:
)
return sha256(theString.encode()).hexdigest()
def packXML(self, xParent: ET.Element, name: str):
def packXML(self, xParent: ET.Element, name: str) -> None:
"""Pack the content into an xml element."""
theAttr = {}
theAttr[_mkTag("style", "name")] = name
@@ -1259,8 +1259,7 @@ class ODTTextStyle:
VALID_LSTYLE = ["none", "solid"]
VALID_LTYPE = ["none", "single", "double"]
def __init__(self):
def __init__(self) -> None:
# Text Attributes
self._tAttr = {
"font-weight": ["fo", None],
@@ -1268,35 +1267,34 @@ class ODTTextStyle:
"text-line-through-style": ["style", None],
"text-line-through-type": ["style", None],
}
return
##
# Setters
##
def setFontWeight(self, value: str | None):
def setFontWeight(self, value: str | None) -> None:
if value in self.VALID_WEIGHT:
self._tAttr["font-weight"][1] = value
else:
self._tAttr["font-weight"][1] = None
return
def setFontStyle(self, value: str | None):
def setFontStyle(self, value: str | None) -> None:
if value in self.VALID_STYLE:
self._tAttr["font-style"][1] = value
else:
self._tAttr["font-style"][1] = None
return
def setStrikeStyle(self, value: str | None):
def setStrikeStyle(self, value: str | None) -> None:
if value in self.VALID_LSTYLE:
self._tAttr["text-line-through-style"][1] = value
else:
self._tAttr["text-line-through-style"][1] = None
return
def setStrikeType(self, value: str | None):
def setStrikeType(self, value: str | None) -> None:
if value in self.VALID_LTYPE:
self._tAttr["text-line-through-type"][1] = value
else:
@@ -1307,7 +1305,7 @@ class ODTTextStyle:
# Methods
##
def packXML(self, xParent: ET.Element, name: str):
def packXML(self, xParent: ET.Element, name: str) -> None:
"""Pack the content into an xml element."""
theAttr = {}
theAttr[_mkTag("style", "name")] = name
@@ -1357,7 +1355,7 @@ class XMLParagraph:
object and attribute is written to,
"""
def __init__(self, xRoot: ET.Element):
def __init__(self, xRoot: ET.Element) -> None:
self._xRoot = xRoot
self._xTail = ET.Element("")
@@ -1370,7 +1368,7 @@ class XMLParagraph:
return
def appendText(self, tText: str):
def appendText(self, tText: str) -> None:
"""Append text to the XML element. We do this one character at
the time in order to be able to process line breaks, tabs and
spaces separately. Multiple spaces are concatenated into a
@@ -1435,7 +1433,7 @@ class XMLParagraph:
return
def appendSpan(self, tText: str, tFmt: str):
def appendSpan(self, tText: str, tFmt: str) -> None:
"""Append a text span to the XML element. The span is always
closed since we do not allow nested spans (like Libre Office).
Therefore we return to the root element level when we're done
@@ -1449,7 +1447,7 @@ class XMLParagraph:
self._nState = X_ROOT_TAIL
return
def checkError(self):
def checkError(self) -> tuple[int, str]:
"""Check that the number of characters written matches the
number of characters received.
"""
@@ -1463,7 +1461,7 @@ class XMLParagraph:
# Internal Functions
##
def _processSpaces(self, nSpaces: int):
def _processSpaces(self, nSpaces: int) -> None:
"""Add spaces to paragraph. The first space is always written
as-is (unless it's the first character of the paragraph). The
second space uses the dedicated tag for spaces, and from the
+2 -2
View File
@@ -1,6 +1,6 @@
<?xml version='1.0' encoding='utf-8'?>
<novelWriterXML appVersion="2.1-beta1" hexVersion="0x020100b1" fileVersion="1.5" fileRevision="1" timeStamp="2023-08-08 10:33:12">
<project id="e2be99af-f9bf-4403-857a-c3d1ac25abea" saveCount="1516" autoCount="237" editTime="75236">
<novelWriterXML appVersion="2.1-beta1" hexVersion="0x020100b1" fileVersion="1.5" fileRevision="1" timeStamp="2023-08-08 22:19:23">
<project id="e2be99af-f9bf-4403-857a-c3d1ac25abea" saveCount="1517" autoCount="237" editTime="75241">
<name>Sample Project</name>
<title>Sample Project</title>
<author>Jane Smith</author>
+1 -1
View File
@@ -344,7 +344,7 @@ def testCoreStatus_PackUnpack(mockRnd):
# Unpack
theStatus = NWStatus(NWStatus.STATUS)
assert theStatus.unpack({
theStatus.unpack({
statusKeys[0]: {"label": "New0", "colour": (100, 100, 100), "count": countTo[0]},
statusKeys[1]: {"label": "New1", "colour": (150, 150, 150), "count": countTo[1]},
statusKeys[2]: {"label": "New2", "colour": (200, 200, 200), "count": countTo[2]},