From c1154682b542d2295efda4f894265a0abac38a4a Mon Sep 17 00:00:00 2001 From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com> Date: Tue, 9 Apr 2024 17:01:05 +0200 Subject: [PATCH] Fix annotation errors --- novelwriter/__init__.py | 13 ++++++++++--- novelwriter/common.py | 7 +++++-- novelwriter/core/buildsettings.py | 2 +- novelwriter/core/projectxml.py | 2 +- novelwriter/core/spellcheck.py | 6 +++--- novelwriter/core/tokenizer.py | 2 +- novelwriter/core/toodt.py | 2 +- novelwriter/dialogs/preferences.py | 2 +- novelwriter/error.py | 2 +- novelwriter/gui/doceditor.py | 8 ++++---- novelwriter/gui/noveltree.py | 2 +- novelwriter/gui/outline.py | 6 +++--- novelwriter/gui/projtree.py | 2 +- novelwriter/guimain.py | 4 ++-- novelwriter/tools/dictionaries.py | 4 ++-- novelwriter/tools/manusbuild.py | 16 ++++++++-------- novelwriter/tools/manussettings.py | 2 +- novelwriter/tools/welcome.py | 2 +- pkgutils.py | 20 +++++++++++++------- 19 files changed, 60 insertions(+), 44 deletions(-) diff --git a/novelwriter/__init__.py b/novelwriter/__init__.py index 5bd809a8..fa3ac8e2 100644 --- a/novelwriter/__init__.py +++ b/novelwriter/__init__.py @@ -23,16 +23,21 @@ along with this program. If not, see . """ from __future__ import annotations -import sys import getopt import logging +import sys + +from typing import TYPE_CHECKING from PyQt5.QtWidgets import QApplication, QErrorMessage -from novelwriter.error import exceptionHandler, logException from novelwriter.config import Config +from novelwriter.error import exceptionHandler, logException from novelwriter.shared import SharedData +if TYPE_CHECKING: # pragma: no cover + from novelwriter.guimain import GuiMain + # Package Meta # ============ @@ -60,7 +65,7 @@ CONFIG = Config() SHARED = SharedData() -def main(sysArgs: list | None = None): +def main(sysArgs: list | None = None) -> GuiMain | None: """Parse command line, set up logging, and launch main GUI.""" if sysArgs is None: sysArgs = sys.argv[1:] @@ -239,4 +244,6 @@ def main(sysArgs: list | None = None): sys.exit(nwApp.exec()) + return None + # END Function main diff --git a/novelwriter/common.py b/novelwriter/common.py index c148e75d..ff36bae2 100644 --- a/novelwriter/common.py +++ b/novelwriter/common.py @@ -449,7 +449,7 @@ def xmlIndent(tree: ET.Element | ET.ElementTree) -> None: indentations = ["\n"] - def indentChildren(elem, level): + def indentChildren(elem: ET.Element, level: int) -> None: chLevel = level + 1 try: chIndent = indentations[chLevel] @@ -472,6 +472,8 @@ def xmlIndent(tree: ET.Element | ET.ElementTree) -> None: if last is not None: last.tail = indentations[level] + return + if len(tree): indentChildren(tree, 0) tree.tail = "\n" @@ -534,8 +536,9 @@ class NWConfigParser(ConfigParser): helper functions, and support for lists. """ - def __init__(self): + def __init__(self) -> None: super().__init__() + return def rdStr(self, section: str, option: str, default: str) -> str: """Read string value.""" diff --git a/novelwriter/core/buildsettings.py b/novelwriter/core/buildsettings.py index 84ee5a1c..3957ed27 100644 --- a/novelwriter/core/buildsettings.py +++ b/novelwriter/core/buildsettings.py @@ -382,7 +382,7 @@ class BuildSettings: postponed = [] - def allowRoot(rHandle): + def allowRoot(rHandle: str | None) -> None: if rHandle in postponed and rHandle in result and rHandle is not None: result[rHandle] = (True, FilterMode.ROOT) postponed.remove(rHandle) diff --git a/novelwriter/core/projectxml.py b/novelwriter/core/projectxml.py index c99c8abf..52db58f6 100644 --- a/novelwriter/core/projectxml.py +++ b/novelwriter/core/projectxml.py @@ -449,7 +449,7 @@ class ProjectXMLReader: result[xEntry.attrib["key"]] = checkString(xEntry.text, "") return result - def _parseDictTagText(self, xItem) -> dict: + def _parseDictTagText(self, xItem: ET.Element) -> dict: """Parse a dictionary stored with key as the tag and the value as the text property. """ diff --git a/novelwriter/core/spellcheck.py b/novelwriter/core/spellcheck.py index 51804d83..6dc47785 100644 --- a/novelwriter/core/spellcheck.py +++ b/novelwriter/core/spellcheck.py @@ -74,7 +74,7 @@ class NWSpellEnchant: # Setters ## - def setLanguage(self, language: str | None): + def setLanguage(self, language: str | None) -> None: """Load a dictionary for the language specified in the config. If that fails, we load a mock dictionary so that lookups don't crash. Note that enchant will allow loading an empty string as @@ -182,10 +182,10 @@ class FakeEnchant: def check(self, word: str) -> bool: return True - def suggest(self, word) -> list[str]: + def suggest(self, word: str) -> list[str]: return [] - def add_to_session(self, word: str): + def add_to_session(self, word: str) -> None: return # END Class FakeEnchant diff --git a/novelwriter/core/tokenizer.py b/novelwriter/core/tokenizer.py index 8cd84816..5c26d8ea 100644 --- a/novelwriter/core/tokenizer.py +++ b/novelwriter/core/tokenizer.py @@ -49,7 +49,7 @@ ESCAPES = {r"\*": "*", r"\~": "~", r"\_": "_", r"\[": "[", r"\]": "]", r"\ ": "" RX_ESC = re.compile("|".join([re.escape(k) for k in ESCAPES.keys()]), flags=re.DOTALL) -def stripEscape(text) -> str: +def stripEscape(text: str) -> str: """Strip escaped Markdown characters from paragraph text.""" if "\\" in text: return RX_ESC.sub(lambda x: ESCAPES[x.group(0)], text) diff --git a/novelwriter/core/toodt.py b/novelwriter/core/toodt.py index 2801fc5b..22418f32 100644 --- a/novelwriter/core/toodt.py +++ b/novelwriter/core/toodt.py @@ -548,7 +548,7 @@ class ToOdt(Tokenizer): oVers = _mkTag("office", "version") xSett = ET.Element(oRoot, attrib={oVers: X_VERS}) - def putInZip(name, xObj, zipObj): + def putInZip(name: str, xObj: ET.Element, zipObj: ZipFile) -> None: with zipObj.open(name, mode="w") as fObj: xml = ET.ElementTree(xObj) xml.write(fObj, encoding="utf-8", xml_declaration=True) diff --git a/novelwriter/dialogs/preferences.py b/novelwriter/dialogs/preferences.py index 7d1e4671..a8900786 100644 --- a/novelwriter/dialogs/preferences.py +++ b/novelwriter/dialogs/preferences.py @@ -795,7 +795,7 @@ class GuiPreferences(QDialog): return @pyqtSlot() - def _selectTextFont(self): + def _selectTextFont(self) -> None: """Open the QFontDialog and set a font for the font style.""" current = QFont() current.setFamily(CONFIG.textFont) diff --git a/novelwriter/error.py b/novelwriter/error.py index 0801d55d..86641011 100644 --- a/novelwriter/error.py +++ b/novelwriter/error.py @@ -54,7 +54,7 @@ def logException() -> None: return -def formatException(exc) -> str: +def formatException(exc: BaseException) -> str: """Format an exception as a string the same way the default exception handler does. """ diff --git a/novelwriter/gui/doceditor.py b/novelwriter/gui/doceditor.py index 0b0e7568..1f56a2b9 100644 --- a/novelwriter/gui/doceditor.py +++ b/novelwriter/gui/doceditor.py @@ -381,7 +381,7 @@ class GuiDocEditor(QPlainTextEdit): return - def loadText(self, tHandle: str, tLine=None) -> bool: + def loadText(self, tHandle: str, tLine: int | None = None) -> bool: """Load text from a document into the editor. If we have an I/O error, we must handle this and clear the editor so that we don't risk overwriting the file if it exists. This can for instance @@ -1080,7 +1080,7 @@ class GuiDocEditor(QPlainTextEdit): return @pyqtSlot() - def _cursorMoved(self): + def _cursorMoved(self) -> None: """Triggered when the cursor moved in the editor.""" self.docFooter.updateLineCount(self.textCursor()) return @@ -2186,7 +2186,7 @@ class MetaCompleter(QMenu): # Internal Functions ## - def _emitComplete(self, pos: int, length: int, value: str): + def _emitComplete(self, pos: int, length: int, value: str) -> None: """Emit the signal to indicate a selection has been made.""" self.complete.emit(pos, length, value) return @@ -2967,7 +2967,7 @@ class GuiDocEditHeader(QWidget): # Events ## - def mousePressEvent(self, event: QMouseEvent): + def mousePressEvent(self, event: QMouseEvent) -> None: """Capture a click on the title and ensure that the item is selected in the project tree. """ diff --git a/novelwriter/gui/noveltree.py b/novelwriter/gui/noveltree.py index ef52577d..7eea96d8 100644 --- a/novelwriter/gui/noveltree.py +++ b/novelwriter/gui/noveltree.py @@ -344,7 +344,7 @@ class GuiNovelToolBar(QWidget): # Internal Functions ## - def _addLastColAction(self, colType, actionLabel) -> None: + def _addLastColAction(self, colType: NovelTreeColumn, actionLabel: str) -> None: """Add a column selection entry to the last column menu.""" aLast = self.mLastCol.addAction(actionLabel) aLast.setCheckable(True) diff --git a/novelwriter/gui/outline.py b/novelwriter/gui/outline.py index 0d07e5ab..4ed2679f 100644 --- a/novelwriter/gui/outline.py +++ b/novelwriter/gui/outline.py @@ -190,7 +190,7 @@ class GuiOutlineView(QWidget): return @pyqtSlot(str) - def _rootItemChanged(self, tHandle) -> None: + def _rootItemChanged(self, tHandle: str) -> None: """Handle root novel changed or needs to be refreshed.""" self.outlineTree.refreshTree(rootHandle=(tHandle or None), overRide=True) return @@ -428,7 +428,7 @@ class GuiOutlineTree(QTreeWidget): ## @property - def hiddenColumns(self): + def hiddenColumns(self) -> dict[nwOutline, bool]: return self._colHidden ## @@ -586,7 +586,7 @@ class GuiOutlineTree(QTreeWidget): # Internal Functions ## - def _loadHeaderState(self): + def _loadHeaderState(self) -> None: """Load the state of the main tree header, that is, column order and column width. """ diff --git a/novelwriter/gui/projtree.py b/novelwriter/gui/projtree.py index 2ade45d8..5ba158af 100644 --- a/novelwriter/gui/projtree.py +++ b/novelwriter/gui/projtree.py @@ -453,7 +453,7 @@ class GuiProjectToolBar(QWidget): def _buildRootMenu(self) -> None: """Build the rood folder menu.""" - def addClass(itemClass): + def addClass(itemClass: nwItemClass) -> None: aNew = self.mAddRoot.addAction(trConst(nwLabels.CLASS_NAME[itemClass])) aNew.setIcon(SHARED.theme.getIcon(nwLabels.CLASS_ICON[itemClass])) aNew.triggered.connect(lambda: self.projTree.newTreeItem(nwItemType.ROOT, itemClass)) diff --git a/novelwriter/guimain.py b/novelwriter/guimain.py index 47b288d0..105d2652 100644 --- a/novelwriter/guimain.py +++ b/novelwriter/guimain.py @@ -944,7 +944,7 @@ class GuiMain(QMainWindow): # Events ## - def closeEvent(self, event: QCloseEvent): + def closeEvent(self, event: QCloseEvent) -> None: """Capture the closing event of the GUI and call the close function to handle all the close process steps. """ @@ -1201,7 +1201,7 @@ class GuiMain(QMainWindow): return @pyqtSlot() - def _toggleViewerPanelVisibility(self): + def _toggleViewerPanelVisibility(self) -> None: """Toggle the visibility of the document viewer panel.""" CONFIG.showViewerPanel = not CONFIG.showViewerPanel self.docViewerPanel.setVisible(CONFIG.showViewerPanel) diff --git a/novelwriter/tools/dictionaries.py b/novelwriter/tools/dictionaries.py index a944c745..a2607e63 100644 --- a/novelwriter/tools/dictionaries.py +++ b/novelwriter/tools/dictionaries.py @@ -179,7 +179,7 @@ class GuiDictionaries(QDialog): ## @pyqtSlot() - def _doBrowseHunspell(self): + def _doBrowseHunspell(self) -> None: """Browse for a Free/Libre Office dictionary.""" ffilter = formatFileFilter([ (self.tr("Free or Libre Office extension"), "*.sox *.oxt"), "*" @@ -193,7 +193,7 @@ class GuiDictionaries(QDialog): return @pyqtSlot() - def _doImportHunspell(self): + def _doImportHunspell(self) -> None: """Import a hunspell dictionary from .sox or .oxt file.""" procErr = self.tr("Could not process dictionary file") if self._installPath: diff --git a/novelwriter/tools/manusbuild.py b/novelwriter/tools/manusbuild.py index 301e4432..68b5e867 100644 --- a/novelwriter/tools/manusbuild.py +++ b/novelwriter/tools/manusbuild.py @@ -60,7 +60,7 @@ class GuiManuscriptBuild(QDialog): D_KEY = QtUserRole - def __init__(self, parent: QWidget, build: BuildSettings): + def __init__(self, parent: QWidget, build: BuildSettings) -> None: super().__init__(parent=parent) logger.debug("Create: GuiManuscriptBuild") @@ -260,7 +260,7 @@ class GuiManuscriptBuild(QDialog): ## @pyqtSlot("QAbstractButton*") - def _dialogButtonClicked(self, button: QAbstractButton): + def _dialogButtonClicked(self, button: QAbstractButton) -> None: """Handle button clicks from the dialog button box.""" role = self.dlgButtons.buttonRole(button) if role == QtRoleAction: @@ -273,7 +273,7 @@ class GuiManuscriptBuild(QDialog): return @pyqtSlot() - def _doSelectPath(self): + def _doSelectPath(self) -> None: """Select a folder for output.""" bPath = Path(self.buildPath.text()) bPath = bPath if bPath.is_dir() else self._build.lastPath @@ -285,7 +285,7 @@ class GuiManuscriptBuild(QDialog): return @pyqtSlot() - def _doResetBuildName(self): + def _doResetBuildName(self) -> None: """Generate a default build name.""" bName = f"{SHARED.project.data.name} - {self._build.name}" self.buildName.setText(bName) @@ -293,7 +293,7 @@ class GuiManuscriptBuild(QDialog): return @pyqtSlot() - def _resetProgress(self): + def _resetProgress(self) -> None: """Set the progress bar back to 0.""" self.buildProgress.setValue(0) return @@ -350,7 +350,7 @@ class GuiManuscriptBuild(QDialog): return items[0].data(self.D_KEY) return None - def _saveSettings(self): + def _saveSettings(self) -> None: """Save the user GUI settings.""" winWidth = CONFIG.rpxInt(self.width()) winHeight = CONFIG.rpxInt(self.height()) @@ -369,7 +369,7 @@ class GuiManuscriptBuild(QDialog): return - def _populateContentList(self): + def _populateContentList(self) -> None: """Build the content list.""" rootMap = {} filtered = self._build.buildItemFilter(SHARED.project) @@ -398,7 +398,7 @@ class GuiManuscriptBuild(QDialog): return - def _openOutputFolder(self): + def _openOutputFolder(self) -> None: """Open the build folder in the system's file explorer.""" openExternalPath(Path(self.buildPath.text())) return diff --git a/novelwriter/tools/manussettings.py b/novelwriter/tools/manussettings.py index c909f8a5..18786ffa 100644 --- a/novelwriter/tools/manussettings.py +++ b/novelwriter/tools/manussettings.py @@ -1312,7 +1312,7 @@ class _FormatTab(NScrollableForm): return @pyqtSlot() - def _pageSizeValueChanged(self): + def _pageSizeValueChanged(self) -> None: """The user has changed the page size spin boxes, so we flip the page size box to Custom. """ diff --git a/novelwriter/tools/welcome.py b/novelwriter/tools/welcome.py index c8f26727..2c08cdf5 100644 --- a/novelwriter/tools/welcome.py +++ b/novelwriter/tools/welcome.py @@ -753,7 +753,7 @@ class _NewProjectForm(QWidget): return @pyqtSlot() - def _syncSwitches(self): + def _syncSwitches(self) -> None: """Check if the add notes option should also be switched off.""" addPlot = self.addPlot.isChecked() addChar = self.addChar.isChecked() diff --git a/pkgutils.py b/pkgutils.py index 2144000a..9d51551d 100755 --- a/pkgutils.py +++ b/pkgutils.py @@ -47,7 +47,7 @@ def extractVersion(beQuiet: bool = False) -> tuple[str, str, str]: """Extract the novelWriter version number without having to import anything else from the main package. """ - def getValue(text): + def getValue(text: str) -> str: bits = text.partition("=") return bits[2].strip().strip('"') @@ -176,7 +176,7 @@ def cleanBuildDirs() -> None: print("Cleaning up build environment ...") print("") - def removeFolder(rmDir): + def removeFolder(rmDir: str) -> None: if os.path.isdir(rmDir): try: shutil.rmtree(rmDir) @@ -320,7 +320,7 @@ def buildQtI18nTS(sysArgs: list[str]) -> None: print("=============================") try: - from PyQt6.lupdate import lupdate + from PyQt6.lupdate.lupdate import lupdate except ImportError: print("ERROR: This command requires lupdate from PyQt6") print("On Debian/Ubuntu, install: pyqt6-dev-tools") @@ -911,7 +911,7 @@ def makeAppImage(sysArgs: list[str]) -> list[str]: import argparse try: - import python_appimage # noqa F401 + import python_appimage # noqa: F401 # type: ignore except ImportError: print( "ERROR: Package 'python-appimage' is missing on this system.\n" @@ -1252,12 +1252,12 @@ def makeWindowsEmbedded(sysArgs: list[str]) -> None: # Clean Up Files # ============== - def unlinkIfFound(delFile): + def unlinkIfFound(delFile: str) -> None: if os.path.isfile(delFile): os.unlink(delFile) print("Deleted: %s" % delFile) - def deleteFolder(delPath): + def deleteFolder(delPath: str) -> None: if os.path.isdir(delPath): shutil.rmtree(delPath) print("Deleted: %s" % delPath) @@ -1561,6 +1561,9 @@ def xdgUninstall() -> None: def winInstall() -> None: """Will attempt to install icons and make a launcher for Windows.""" + if sys.platform != "win32": + raise Exception("This method only runs on Windows") + import winreg try: import win32com.client @@ -1654,7 +1657,7 @@ def winInstall() -> None: print("") print("Creating registry keys ...") - def setKey(kPath, kName, kVal): + def setKey(kPath: str, kName: str, kVal: str) -> None: winreg.CreateKey(winreg.HKEY_CURRENT_USER, kPath) regKey = winreg.OpenKey(winreg.HKEY_CURRENT_USER, kPath, 0, winreg.KEY_WRITE) winreg.SetValueEx(regKey, kName, 0, winreg.REG_SZ, kVal) @@ -1688,6 +1691,9 @@ def winInstall() -> None: def winUninstall() -> None: """Will attempt to uninstall icons previously installed.""" + if sys.platform != "win32": + raise Exception("This method only runs on Windows") + import winreg try: import win32com.client