From eeadfc95e3b7d75c6df63e148e7aed67633f9a56 Mon Sep 17 00:00:00 2001 From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com> Date: Tue, 8 Aug 2023 22:37:48 +0200 Subject: [PATCH] Annotate core modules --- novelwriter/common.py | 16 ++--- novelwriter/core/buildsettings.py | 32 ++++----- novelwriter/core/coretools.py | 13 ++-- novelwriter/core/docbuild.py | 6 +- novelwriter/core/projectdata.py | 43 ++++++------ novelwriter/core/projectxml.py | 26 ++++--- novelwriter/core/sessions.py | 4 +- novelwriter/core/spellcheck.py | 10 +-- novelwriter/core/status.py | 104 ++++++++++++---------------- novelwriter/core/storage.py | 18 ++--- novelwriter/core/tohtml.py | 24 +++---- novelwriter/core/tokenizer.py | 68 +++++++++--------- novelwriter/core/tomd.py | 18 +++-- novelwriter/core/toodt.py | 90 ++++++++++++------------ sample/nwProject.nwx | 4 +- tests/test_core/test_core_status.py | 2 +- 16 files changed, 232 insertions(+), 246 deletions(-) diff --git a/novelwriter/common.py b/novelwriter/common.py index 35db1c9c..17fb6403 100644 --- a/novelwriter/common.py +++ b/novelwriter/common.py @@ -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"] diff --git a/novelwriter/core/buildsettings.py b/novelwriter/core/buildsettings.py index ff869351..64196ed1 100644 --- a/novelwriter/core/buildsettings.py +++ b/novelwriter/core/buildsettings.py @@ -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() diff --git a/novelwriter/core/coretools.py b/novelwriter/core/coretools.py index 114c92ff..094c8c55 100644 --- a/novelwriter/core/coretools.py +++ b/novelwriter/core/coretools.py @@ -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) diff --git a/novelwriter/core/docbuild.py b/novelwriter/core/docbuild.py index c3301e0f..f5020bba 100644 --- a/novelwriter/core/docbuild.py +++ b/novelwriter/core/docbuild.py @@ -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) diff --git a/novelwriter/core/projectdata.py b/novelwriter/core/projectdata.py index 18927271..9b0cc36c 100644 --- a/novelwriter/core/projectdata.py +++ b/novelwriter/core/projectdata.py @@ -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 = {} diff --git a/novelwriter/core/projectxml.py b/novelwriter/core/projectxml.py index 055fe4ef..c12e0dc0 100644 --- a/novelwriter/core/projectxml.py +++ b/novelwriter/core/projectxml.py @@ -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 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 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 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 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(): diff --git a/novelwriter/core/sessions.py b/novelwriter/core/sessions.py index aa9da7e8..dfbf720e 100644 --- a/novelwriter/core/sessions.py +++ b/novelwriter/core/sessions.py @@ -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 diff --git a/novelwriter/core/spellcheck.py b/novelwriter/core/spellcheck.py index b5cac3a2..d7d55d91 100644 --- a/novelwriter/core/spellcheck.py +++ b/novelwriter/core/spellcheck.py @@ -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) diff --git a/novelwriter/core/status.py b/novelwriter/core/status.py index 88b4467a..ac26641f 100644 --- a/novelwriter/core/status.py +++ b/novelwriter/core/status.py @@ -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 diff --git a/novelwriter/core/storage.py b/novelwriter/core/storage.py index 84e4ee35..d8740c0e 100644 --- a/novelwriter/core/storage.py +++ b/novelwriter/core/storage.py @@ -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 diff --git a/novelwriter/core/tohtml.py b/novelwriter/core/tohtml.py index 20c01701..8459ae59 100644 --- a/novelwriter/core/tohtml.py +++ b/novelwriter/core/tohtml.py @@ -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 = " "): + def replaceTabs(self, nSpaces: int = 8, spaceChar: str = " ") -> 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: diff --git a/novelwriter/core/tokenizer.py b/novelwriter/core/tokenizer.py index e1a9aea4..f5d54d29 100644 --- a/novelwriter/core/tokenizer.py +++ b/novelwriter/core/tokenizer.py @@ -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)) diff --git a/novelwriter/core/tomd.py b/novelwriter/core/tomd.py index e634de74..c9859318 100644 --- a/novelwriter/core/tomd.py +++ b/novelwriter/core/tomd.py @@ -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] diff --git a/novelwriter/core/toodt.py b/novelwriter/core/toodt.py index 93b8afac..b69af17b 100644 --- a/novelwriter/core/toodt.py +++ b/novelwriter/core/toodt.py @@ -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 diff --git a/sample/nwProject.nwx b/sample/nwProject.nwx index 41d8853b..cb3d212e 100644 --- a/sample/nwProject.nwx +++ b/sample/nwProject.nwx @@ -1,6 +1,6 @@ - - + + Sample Project Sample Project Jane Smith diff --git a/tests/test_core/test_core_status.py b/tests/test_core/test_core_status.py index fdfa1588..a42701b9 100644 --- a/tests/test_core/test_core_status.py +++ b/tests/test_core/test_core_status.py @@ -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]},