diff --git a/.codecov.yml b/.codecov.yml index 9166a2e1..45ebc6c7 100644 --- a/.codecov.yml +++ b/.codecov.yml @@ -10,9 +10,7 @@ coverage: project: default: threshold: 1% - patch: - default: - threshold: 1% + patch: no changes: no parsers: diff --git a/.github/workflows/syntax.yml b/.github/workflows/syntax.yml new file mode 100644 index 00000000..f75369fe --- /dev/null +++ b/.github/workflows/syntax.yml @@ -0,0 +1,27 @@ +name: Flake 8 Checks + +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + +jobs: + checkSyntax: + runs-on: ubuntu-latest + steps: + - name: Python Setup + uses: actions/setup-python@v1 + with: + python-version: 3.7 + architecture: x64 + - name: Checkout novelWriter + uses: actions/checkout@v2 + - name: Install flake8 + run: pip install flake8 + - name: Check for Syntax Error on novelWriter + run: flake8 nw --count --select=E9,F63,F7,F82 --show-source --statistics + - name: Check for Syntax Error on Tests + run: flake8 tests --count --select=E9,F63,F7,F82 --show-source --statistics + - name: Check for Code Style on novelWriter + run: flake8 nw --count --max-line-length=99 --select E1,E231,E27,E4,E5,E7,E9,W,F --show-source --statistics diff --git a/.readthedocs.yml b/.readthedocs.yml new file mode 100644 index 00000000..ca15383e --- /dev/null +++ b/.readthedocs.yml @@ -0,0 +1,25 @@ +# Read the Docs configuration file +# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details + +# Required +version: 2 + +# Build documentation in the docs/ directory with Sphinx +sphinx: + configuration: docs/source/conf.py + +# Build documentation with MkDocs +#mkdocs: +# configuration: mkdocs.yml + +# Optionally build your docs in additional formats such as PDF +formats: + - htmlzip + - epub + - pdf + +# Optionally set the version of Python and requirements required to build your docs +python: + version: 3.7 + install: + - requirements: docs/source/requirements.txt diff --git a/docs/source/requirements.txt b/docs/source/requirements.txt new file mode 100644 index 00000000..483a4e96 --- /dev/null +++ b/docs/source/requirements.txt @@ -0,0 +1 @@ +sphinx_rtd_theme diff --git a/nw/__init__.py b/nw/__init__.py index 3efc4ce2..1d6e5504 100644 --- a/nw/__init__.py +++ b/nw/__init__.py @@ -153,7 +153,7 @@ def main(sysArgs=None): # Parse Options try: - inOpts, inRemain = getopt.getopt(sysArgs,shortOpt,longOpt) + inOpts, inRemain = getopt.getopt(sysArgs, shortOpt, longOpt) except getopt.GetoptError as E: print(helpMsg) print("ERROR: %s" % str(E)) @@ -163,7 +163,7 @@ def main(sysArgs=None): cmdOpen = inRemain[0] for inOpt, inArg in inOpts: - if inOpt in ("-h","--help"): + if inOpt in ("-h", "--help"): print(helpMsg) sys.exit() elif inOpt in ("-v", "--version"): @@ -179,7 +179,7 @@ def main(sysArgs=None): elif inOpt == "--logfile": logFile = inArg toFile = True - elif inOpt in ("-q","--quiet"): + elif inOpt in ("-q", "--quiet"): toStd = False elif inOpt == "--verbose": debugLevel = VERBOSE @@ -205,7 +205,7 @@ def main(sysArgs=None): if path.isfile(logFile+".bak"): remove(logFile+".bak") if path.isfile(logFile): - rename(logFile,logFile+".bak") + rename(logFile, logFile+".bak") fHandle = logging.FileHandler(logFile) fHandle.setLevel(debugLevel) @@ -239,13 +239,13 @@ def main(sysArgs=None): ) try: - import PyQt5.QtSvg - except: + import PyQt5.QtSvg # noqa: F401 + except ImportError: errorData.append("Python module 'PyQt5.QtSvg' is missing.") try: - import lxml - except: + import lxml # noqa: F401 + except ImportError: errorData.append("Python module 'lxml' is missing.") if errorData: diff --git a/nw/common.py b/nw/common.py index 0f4d6ef4..71aa4873 100644 --- a/nw/common.py +++ b/nw/common.py @@ -26,7 +26,6 @@ """ import logging -import nw from datetime import datetime @@ -38,11 +37,11 @@ def checkString(checkValue, defaultValue, allowNone=False): """Check if a variable is a string or a none. """ if allowNone: - if checkValue == None: + if checkValue is None: return None if checkValue == "None": return None - if isinstance(checkValue,str): + if isinstance(checkValue, str): return str(checkValue) return defaultValue @@ -50,20 +49,20 @@ def checkInt(checkValue, defaultValue, allowNone=False): """Check if a variable is an integer or a none. """ if allowNone: - if checkValue == None: + if checkValue is None: return None if checkValue == "None": return None try: return int(checkValue) - except: + except Exception: return defaultValue def checkBool(checkValue, defaultValue, allowNone=False): """Check if a variable is a boolean or a none. """ if allowNone: - if checkValue == None: + if checkValue is None: return None if checkValue == "None": return None @@ -109,7 +108,7 @@ def colRange(rgbStart, rgbEnd, nStep): elif nStep == 2: return [rgbStart, rgbEnd] - dC = [0,0,0] + dC = [0, 0, 0] for c in range(3): cA = rgbStart[c] cB = rgbEnd[c] @@ -139,11 +138,11 @@ def formatInt(theInt): theVal /= 1000.0 if theVal < 1000.0: if theVal < 10.0: - return "%4.2f%s" % (theVal,pF) + return "%4.2f%s" % (theVal, pF) elif theVal < 100.0: - return "%4.1f%s" % (theVal,pF) + return "%4.1f%s" % (theVal, pF) else: - return "%3.0f%s" % (theVal,pF) + return "%3.0f%s" % (theVal, pF) return "%d" % theInt @@ -169,11 +168,11 @@ def splitVersionNumber(vString): nBits = len(vBits) if nBits > 0: - vMajor = checkInt(vBits[0],0) + vMajor = checkInt(vBits[0], 0) if nBits > 1: - vMinor = checkInt(vBits[1],0) + vMinor = checkInt(vBits[1], 0) if nBits > 2: - vPatch = checkInt(vBits[2],0) + vPatch = checkInt(vBits[2], 0) vInt = vMajor*10000 + vMinor*100 + vPatch diff --git a/nw/config.py b/nw/config.py index 97e78c90..3c3915b7 100644 --- a/nw/config.py +++ b/nw/config.py @@ -29,7 +29,6 @@ import logging import configparser import json import sys -import nw from os import path, mkdir, unlink, rename from time import time @@ -260,12 +259,12 @@ class Config: self.homePath = path.expanduser("~") self.lastPath = self.homePath self.appPath = getattr(sys, "_MEIPASS", path.abspath(path.dirname(__file__))) - self.appRoot = path.join(self.appPath,path.pardir) - self.assetPath = path.join(self.appPath,"assets") - self.themeRoot = path.join(self.assetPath,"themes") - self.graphPath = path.join(self.assetPath,"graphics") - self.dictPath = path.join(self.assetPath,"dict") - self.iconPath = path.join(self.assetPath,"icons") + self.appRoot = path.join(self.appPath, path.pardir) + self.assetPath = path.join(self.appPath, "assets") + self.themeRoot = path.join(self.assetPath, "themes") + self.graphPath = path.join(self.assetPath, "graphics") + self.dictPath = path.join(self.assetPath, "dict") + self.iconPath = path.join(self.assetPath, "icons") self.appIcon = path.join(self.iconPath, "novelwriter.svg") logger.verbose("App path: %s" % self.appPath) @@ -549,85 +548,85 @@ class Config: ## Main cnfSec = "Main" cnfParse.add_section(cnfSec) - cnfParse.set(cnfSec,"timestamp", formatTimeStamp(time())) - cnfParse.set(cnfSec,"theme", str(self.guiTheme)) - cnfParse.set(cnfSec,"syntax", str(self.guiSyntax)) - cnfParse.set(cnfSec,"icons", str(self.guiIcons)) - cnfParse.set(cnfSec,"guidark", str(self.guiDark)) - cnfParse.set(cnfSec,"guifont", str(self.guiFont)) - cnfParse.set(cnfSec,"guifontsize", str(self.guiFontSize)) + cnfParse.set(cnfSec, "timestamp", formatTimeStamp(time())) + cnfParse.set(cnfSec, "theme", str(self.guiTheme)) + cnfParse.set(cnfSec, "syntax", str(self.guiSyntax)) + cnfParse.set(cnfSec, "icons", str(self.guiIcons)) + cnfParse.set(cnfSec, "guidark", str(self.guiDark)) + cnfParse.set(cnfSec, "guifont", str(self.guiFont)) + cnfParse.set(cnfSec, "guifontsize", str(self.guiFontSize)) ## Sizes cnfSec = "Sizes" cnfParse.add_section(cnfSec) - cnfParse.set(cnfSec,"geometry", self._packList(self.winGeometry)) - cnfParse.set(cnfSec,"treecols", self._packList(self.treeColWidth)) - cnfParse.set(cnfSec,"projcols", self._packList(self.projColWidth)) - cnfParse.set(cnfSec,"mainpane", self._packList(self.mainPanePos)) - cnfParse.set(cnfSec,"docpane", self._packList(self.docPanePos)) - cnfParse.set(cnfSec,"viewpane", self._packList(self.viewPanePos)) - cnfParse.set(cnfSec,"outlinepane", self._packList(self.outlnPanePos)) - cnfParse.set(cnfSec,"fullscreen", str(self.isFullScreen)) + cnfParse.set(cnfSec, "geometry", self._packList(self.winGeometry)) + cnfParse.set(cnfSec, "treecols", self._packList(self.treeColWidth)) + cnfParse.set(cnfSec, "projcols", self._packList(self.projColWidth)) + cnfParse.set(cnfSec, "mainpane", self._packList(self.mainPanePos)) + cnfParse.set(cnfSec, "docpane", self._packList(self.docPanePos)) + cnfParse.set(cnfSec, "viewpane", self._packList(self.viewPanePos)) + cnfParse.set(cnfSec, "outlinepane", self._packList(self.outlnPanePos)) + cnfParse.set(cnfSec, "fullscreen", str(self.isFullScreen)) ## Project cnfSec = "Project" cnfParse.add_section(cnfSec) - cnfParse.set(cnfSec,"autosaveproject", str(self.autoSaveProj)) - cnfParse.set(cnfSec,"autosavedoc", str(self.autoSaveDoc)) + cnfParse.set(cnfSec, "autosaveproject", str(self.autoSaveProj)) + cnfParse.set(cnfSec, "autosavedoc", str(self.autoSaveDoc)) ## Editor cnfSec = "Editor" cnfParse.add_section(cnfSec) - cnfParse.set(cnfSec,"textfont", str(self.textFont)) - cnfParse.set(cnfSec,"textsize", str(self.textSize)) - cnfParse.set(cnfSec,"fixedwidth", str(self.textFixedW)) - cnfParse.set(cnfSec,"width", str(self.textWidth)) - cnfParse.set(cnfSec,"margin", str(self.textMargin)) - cnfParse.set(cnfSec,"tabwidth", str(self.tabWidth)) - cnfParse.set(cnfSec,"focuswidth", str(self.focusWidth)) - cnfParse.set(cnfSec,"hidefocusfooter", str(self.hideFocusFooter)) - cnfParse.set(cnfSec,"justify", str(self.doJustify)) - cnfParse.set(cnfSec,"autoselect", str(self.autoSelect)) - cnfParse.set(cnfSec,"autoreplace", str(self.doReplace)) - cnfParse.set(cnfSec,"repsquotes", str(self.doReplaceSQuote)) - cnfParse.set(cnfSec,"repdquotes", str(self.doReplaceDQuote)) - cnfParse.set(cnfSec,"repdash", str(self.doReplaceDash)) - cnfParse.set(cnfSec,"repdots", str(self.doReplaceDots)) - cnfParse.set(cnfSec,"fmtsinglequote", self._packList(self.fmtSingleQuotes)) - cnfParse.set(cnfSec,"fmtdoublequote", self._packList(self.fmtDoubleQuotes)) - cnfParse.set(cnfSec,"spelltool", str(self.spellTool)) - cnfParse.set(cnfSec,"spellcheck", str(self.spellLanguage)) - cnfParse.set(cnfSec,"showtabsnspaces", str(self.showTabsNSpaces)) - cnfParse.set(cnfSec,"showlineendings", str(self.showLineEndings)) - cnfParse.set(cnfSec,"bigdoclimit", str(self.bigDocLimit)) - cnfParse.set(cnfSec,"showfullpath", str(self.showFullPath)) - cnfParse.set(cnfSec,"highlightquotes", str(self.highlightQuotes)) - cnfParse.set(cnfSec,"highlightemph", str(self.highlightEmph)) + cnfParse.set(cnfSec, "textfont", str(self.textFont)) + cnfParse.set(cnfSec, "textsize", str(self.textSize)) + cnfParse.set(cnfSec, "fixedwidth", str(self.textFixedW)) + cnfParse.set(cnfSec, "width", str(self.textWidth)) + cnfParse.set(cnfSec, "margin", str(self.textMargin)) + cnfParse.set(cnfSec, "tabwidth", str(self.tabWidth)) + cnfParse.set(cnfSec, "focuswidth", str(self.focusWidth)) + cnfParse.set(cnfSec, "hidefocusfooter", str(self.hideFocusFooter)) + cnfParse.set(cnfSec, "justify", str(self.doJustify)) + cnfParse.set(cnfSec, "autoselect", str(self.autoSelect)) + cnfParse.set(cnfSec, "autoreplace", str(self.doReplace)) + cnfParse.set(cnfSec, "repsquotes", str(self.doReplaceSQuote)) + cnfParse.set(cnfSec, "repdquotes", str(self.doReplaceDQuote)) + cnfParse.set(cnfSec, "repdash", str(self.doReplaceDash)) + cnfParse.set(cnfSec, "repdots", str(self.doReplaceDots)) + cnfParse.set(cnfSec, "fmtsinglequote", self._packList(self.fmtSingleQuotes)) + cnfParse.set(cnfSec, "fmtdoublequote", self._packList(self.fmtDoubleQuotes)) + cnfParse.set(cnfSec, "spelltool", str(self.spellTool)) + cnfParse.set(cnfSec, "spellcheck", str(self.spellLanguage)) + cnfParse.set(cnfSec, "showtabsnspaces", str(self.showTabsNSpaces)) + cnfParse.set(cnfSec, "showlineendings", str(self.showLineEndings)) + cnfParse.set(cnfSec, "bigdoclimit", str(self.bigDocLimit)) + cnfParse.set(cnfSec, "showfullpath", str(self.showFullPath)) + cnfParse.set(cnfSec, "highlightquotes", str(self.highlightQuotes)) + cnfParse.set(cnfSec, "highlightemph", str(self.highlightEmph)) ## Backup cnfSec = "Backup" cnfParse.add_section(cnfSec) - cnfParse.set(cnfSec,"backuppath", str(self.backupPath)) - cnfParse.set(cnfSec,"backuponclose", str(self.backupOnClose)) - cnfParse.set(cnfSec,"askbeforebackup",str(self.askBeforeBackup)) + cnfParse.set(cnfSec, "backuppath", str(self.backupPath)) + cnfParse.set(cnfSec, "backuponclose", str(self.backupOnClose)) + cnfParse.set(cnfSec, "askbeforebackup", str(self.askBeforeBackup)) ## State cnfSec = "State" cnfParse.add_section(cnfSec) - cnfParse.set(cnfSec,"showrefpanel", str(self.showRefPanel)) - cnfParse.set(cnfSec,"viewcomments", str(self.viewComments)) - cnfParse.set(cnfSec,"viewsynopsis", str(self.viewSynopsis)) - cnfParse.set(cnfSec,"searchcase", str(self.searchCase)) - cnfParse.set(cnfSec,"searchword", str(self.searchWord)) - cnfParse.set(cnfSec,"searchregex", str(self.searchRegEx)) - cnfParse.set(cnfSec,"searchloop", str(self.searchLoop)) - cnfParse.set(cnfSec,"searchnextfile", str(self.searchNextFile)) - cnfParse.set(cnfSec,"searchmatchcap", str(self.searchMatchCap)) + cnfParse.set(cnfSec, "showrefpanel", str(self.showRefPanel)) + cnfParse.set(cnfSec, "viewcomments", str(self.viewComments)) + cnfParse.set(cnfSec, "viewsynopsis", str(self.viewSynopsis)) + cnfParse.set(cnfSec, "searchcase", str(self.searchCase)) + cnfParse.set(cnfSec, "searchword", str(self.searchWord)) + cnfParse.set(cnfSec, "searchregex", str(self.searchRegEx)) + cnfParse.set(cnfSec, "searchloop", str(self.searchLoop)) + cnfParse.set(cnfSec, "searchnextfile", str(self.searchNextFile)) + cnfParse.set(cnfSec, "searchmatchcap", str(self.searchMatchCap)) ## Path cnfSec = "Path" cnfParse.add_section(cnfSec) - cnfParse.set(cnfSec,"lastpath", str(self.lastPath)) + cnfParse.set(cnfSec, "lastpath", str(self.lastPath)) # Write config file cnfPath = path.join(self.confPath, self.confFile) @@ -862,7 +861,7 @@ class Config: for i in range(listLen): try: outData.append(castTo(inData[i])) - except: + except Exception: outData.append(listDefault[i]) return outData @@ -902,16 +901,16 @@ class Config: """Cheks if we have the optional packages used by some features. """ try: - import enchant + import enchant # noqa: F401 self.hasEnchant = True logger.debug("Checking package 'pyenchant': Ok") - except: + except Exception: self.hasEnchant = False logger.debug("Checking package 'pyenchant': Missing") try: self.hasAssistant = which("assistant") - except: + except Exception: self.hasAssistant = False if self.hasAssistant: logger.debug("Checking executable 'assistant': Ok") diff --git a/nw/core/document.py b/nw/core/document.py index 0c29f995..022e608e 100644 --- a/nw/core/document.py +++ b/nw/core/document.py @@ -192,7 +192,7 @@ class NWDoc(): unlink(chkFile) logger.debug("Deleted: %s" % chkFile) except Exception as e: - self.makeAlert(["Could not delete document file.",str(e)], nwAlert.ERROR) + self.makeAlert(["Could not delete document file.", str(e)], nwAlert.ERROR) return False return True diff --git a/nw/core/index.py b/nw/core/index.py index b32c8f38..c542a459 100644 --- a/nw/core/index.py +++ b/nw/core/index.py @@ -27,7 +27,6 @@ import logging import json -import nw from os import path from time import time @@ -135,7 +134,7 @@ class NWIndex(): if path.isfile(indexFile): logger.debug("Loading index file") try: - with open(indexFile,mode="r",encoding="utf8") as inFile: + with open(indexFile, mode="r", encoding="utf8") as inFile: theJson = inFile.read() theData = json.loads(theJson) except Exception as e: @@ -222,7 +221,7 @@ class NWIndex(): if len(self.textCounts[tHandle]) != 3: self.indexBroken = True - except: + except Exception: self.indexBroken = True if self.indexBroken: @@ -520,7 +519,7 @@ class NWIndex(): return isGood # If we're still here, we better check that the references exist - for n in range(1,nBits): + for n in range(1, nBits): if theBits[n] in self.tagIndex: isGood[n] = self.TAG_CLASS[theBits[0]].name == self.tagIndex[theBits[n]][2] @@ -603,8 +602,6 @@ class NWIndex(): by tHandle. """ theRefs = {} - - tItem = self.theProject.projTree[tHandle] if tHandle is None: return theRefs diff --git a/nw/core/item.py b/nw/core/item.py index 0eb4ef26..6e8048f1 100644 --- a/nw/core/item.py +++ b/nw/core/item.py @@ -26,7 +26,6 @@ """ import logging -import nw from lxml import etree @@ -68,24 +67,24 @@ class NWItem(): def packXML(self, xParent): """Packs all the data in the class instance into an XML object. """ - xPack = etree.SubElement(xParent,"item",attrib={ + xPack = etree.SubElement(xParent, "item", attrib={ "handle" : str(self.itemHandle), "order" : str(self.itemOrder), "parent" : str(self.parHandle), }) - xSub = self._subPack(xPack,"name", text=str(self.itemName)) - xSub = self._subPack(xPack,"type", text=str(self.itemType.name)) - xSub = self._subPack(xPack,"class", text=str(self.itemClass.name)) - xSub = self._subPack(xPack,"status", text=str(self.itemStatus)) + self._subPack(xPack, "name", text=str(self.itemName)) + self._subPack(xPack, "type", text=str(self.itemType.name)) + self._subPack(xPack, "class", text=str(self.itemClass.name)) + self._subPack(xPack, "status", text=str(self.itemStatus)) if self.itemType == nwItemType.FILE: - xSub = self._subPack(xPack,"exported", text=str(self.isExported)) - xSub = self._subPack(xPack,"layout", text=str(self.itemLayout.name)) - xSub = self._subPack(xPack,"charCount", text=str(self.charCount), none=False) - xSub = self._subPack(xPack,"wordCount", text=str(self.wordCount), none=False) - xSub = self._subPack(xPack,"paraCount", text=str(self.paraCount), none=False) - xSub = self._subPack(xPack,"cursorPos", text=str(self.cursorPos), none=False) + self._subPack(xPack, "exported", text=str(self.isExported)) + self._subPack(xPack, "layout", text=str(self.itemLayout.name)) + self._subPack(xPack, "charCount", text=str(self.charCount), none=False) + self._subPack(xPack, "wordCount", text=str(self.wordCount), none=False) + self._subPack(xPack, "paraCount", text=str(self.paraCount), none=False) + self._subPack(xPack, "cursorPos", text=str(self.cursorPos), none=False) else: - xSub = self._subPack(xPack,"expanded", text=str(self.isExpanded)) + self._subPack(xPack, "expanded", text=str(self.isExpanded)) return def unpackXML(self, xItem): @@ -130,12 +129,12 @@ class NWItem(): def _subPack(xParent, name, attrib=None, text=None, none=True): """Packs the values into an xml element. """ - if not none and (text == None or text == "None"): + if not none and (text is None or text == "None"): return None xSub = etree.SubElement(xParent, name, attrib=attrib) if text is not None: xSub.text = text - return xSub + return ## # Set Item Values @@ -233,18 +232,18 @@ class NWItem(): """Save the expanded status of an item in the project tree. """ if isinstance(expState, str): - self.isExpanded = expState == str(True) + self.isExpanded = (expState == str(True)) else: - self.isExpanded = expState == True + self.isExpanded = (expState == True) # noqa: E712 return def setExported(self, expState): """Save the export flag. """ if isinstance(expState, str): - self.isExported = expState == str(True) + self.isExported = (expState == str(True)) else: - self.isExported = expState == True + self.isExported = (expState == True) # noqa: E712 return ## diff --git a/nw/core/options.py b/nw/core/options.py index a91f6f68..9a58bf7c 100644 --- a/nw/core/options.py +++ b/nw/core/options.py @@ -153,15 +153,15 @@ class OptionState(): def setValue(self, setGroup, setName, setValue): """Saves a value, with a given group and name. """ - if not setGroup in self.validMap: + if setGroup not in self.validMap: logger.error("Unknown option group '%s'" % setGroup) return False - if not setName in self.validMap[setGroup]: + if setName not in self.validMap[setGroup]: logger.error("Unknown option name '%s'" % setName) return False - if not setGroup in self.theState: + if setGroup not in self.theState: self.theState[setGroup] = {} self.theState[setGroup][setName] = setValue diff --git a/nw/core/project.py b/nw/core/project.py index 3404370e..1844c9cb 100644 --- a/nw/core/project.py +++ b/nw/core/project.py @@ -26,7 +26,6 @@ """ import logging -import json import nw from os import path, mkdir, listdir, unlink, rename, rmdir @@ -206,15 +205,15 @@ class NWProject(): self.spellCheck = False self.autoOutline = True self.statusItems = NWStatus() - self.statusItems.addEntry("New", (100, 100, 100)) - self.statusItems.addEntry("Note", (200, 50, 0)) - self.statusItems.addEntry("Draft", (200, 150, 0)) - self.statusItems.addEntry("Finished",( 50, 200, 0)) + self.statusItems.addEntry("New", (100, 100, 100)) + self.statusItems.addEntry("Note", (200, 50, 0)) + self.statusItems.addEntry("Draft", (200, 150, 0)) + self.statusItems.addEntry("Finished", ( 50, 200, 0)) self.importItems = NWStatus() - self.importItems.addEntry("New", (100, 100, 100)) - self.importItems.addEntry("Minor", (200, 50, 0)) - self.importItems.addEntry("Major", (200, 150, 0)) - self.importItems.addEntry("Main", ( 50, 200, 0)) + self.importItems.addEntry("New", (100, 100, 100)) + self.importItems.addEntry("Minor", (200, 50, 0)) + self.importItems.addEntry("Major", (200, 150, 0)) + self.importItems.addEntry("Main", ( 50, 200, 0)) self.lastEdited = None self.lastViewed = None self.lastWCount = 0 @@ -265,27 +264,28 @@ class NWProject(): if popMinimal: # Creating a minimal project with a few root folders and a # single chapter folder with a single file. - nHandle = self.newRoot("Novel", nwItemClass.NOVEL) - xHandle = self.newRoot("Plot", nwItemClass.PLOT) - xHandle = self.newRoot("Characters", nwItemClass.CHARACTER) - xHandle = self.newRoot("World", nwItemClass.WORLD) - tHandle = self.newFile("Title Page", nwItemClass.NOVEL, nHandle) - dHandle = self.newFolder("New Chapter", nwItemClass.NOVEL, nHandle) - cHandle = self.newFile("New Chapter", nwItemClass.NOVEL, dHandle) - sHandle = self.newFile("New Scene", nwItemClass.NOVEL, dHandle) + xHandle = {} + xHandle[1] = self.newRoot("Novel", nwItemClass.NOVEL) + xHandle[2] = self.newRoot("Plot", nwItemClass.PLOT) + xHandle[3] = self.newRoot("Characters", nwItemClass.CHARACTER) + xHandle[4] = self.newRoot("World", nwItemClass.WORLD) + xHandle[5] = self.newFile("Title Page", nwItemClass.NOVEL, xHandle[1]) + xHandle[6] = self.newFolder("New Chapter", nwItemClass.NOVEL, xHandle[1]) + xHandle[7] = self.newFile("New Chapter", nwItemClass.NOVEL, xHandle[6]) + xHandle[8] = self.newFile("New Scene", nwItemClass.NOVEL, xHandle[6]) - self.projTree.setFileItemLayout(tHandle, nwItemLayout.TITLE) - self.projTree.setFileItemLayout(cHandle, nwItemLayout.CHAPTER) + self.projTree.setFileItemLayout(xHandle[5], nwItemLayout.TITLE) + self.projTree.setFileItemLayout(xHandle[7], nwItemLayout.CHAPTER) - aDoc.openDocument(tHandle, showStatus=False) + aDoc.openDocument(xHandle[5], showStatus=False) aDoc.saveDocument(titlePage) aDoc.clearDocument() - aDoc.openDocument(cHandle, showStatus=False) + aDoc.openDocument(xHandle[7], showStatus=False) aDoc.saveDocument("## New Chapter\n\n") aDoc.clearDocument() - aDoc.openDocument(sHandle, showStatus=False) + aDoc.openDocument(xHandle[8], showStatus=False) aDoc.saveDocument("### New Scene\n\n") aDoc.clearDocument() @@ -421,7 +421,7 @@ class NWProject(): try: nwXML = etree.parse(fileName) except Exception as e: - self.makeAlert(["Failed to parse project xml.",str(e)], nwAlert.ERROR) + self.makeAlert(["Failed to parse project xml.", str(e)], nwAlert.ERROR) # Trying to open backup file instead backFile = fileName[:-3]+"bak" @@ -430,7 +430,7 @@ class NWProject(): try: nwXML = etree.parse(backFile) except Exception as e: - self.makeAlert(["Failed to parse project xml.",str(e)], nwAlert.ERROR) + self.makeAlert(["Failed to parse project xml.", str(e)], nwAlert.ERROR) self.clearProject() return False else: @@ -794,7 +794,7 @@ class NWProject(): logger.debug("Created folder %s" % baseDir) except Exception as e: self.theParent.makeAlert( - ["Could not create backup folder.",str(e)], + ["Could not create backup folder.", str(e)], nwAlert.ERROR ) return False @@ -823,7 +823,7 @@ class NWProject(): logger.info("Backup written to: %s" % archName) except Exception as e: self.theParent.makeAlert( - ["Could not write backup archive.",str(e)], + ["Could not write backup archive.", str(e)], nwAlert.ERROR ) return False @@ -838,7 +838,6 @@ class NWProject(): project path, or if the folder doesn't exist, look for the zip file in the assets folder. """ - projName = projData.get("projName", "Sample Project") projPath = projData.get("projPath", None) if projPath is None: logger.error("No project path set for the example project") @@ -1231,7 +1230,7 @@ class NWProject(): mkdir(thePath) logger.debug("Created folder %s" % thePath) except Exception as e: - self.makeAlert(["Could not create folder.",str(e)], nwAlert.ERROR) + self.makeAlert(["Could not create folder.", str(e)], nwAlert.ERROR) return False return True @@ -1243,7 +1242,8 @@ class NWProject(): for aValue in theValue: if not isinstance(aValue, str): aValue = str(aValue) - if aValue == "" and not allowNone: continue + if aValue == "" and not allowNone: + continue xItem = etree.SubElement(xParent, theName) xItem.text = aValue return @@ -1402,7 +1402,7 @@ class NWProject(): try: rmdir(theData) logger.info("Removed folder: %s" % theFolder) - except: + except Exception: errList.append("Failed to remove: %s" % theFolder) return errList diff --git a/nw/core/spellcheck.py b/nw/core/spellcheck.py index d4740b56..e904eae5 100644 --- a/nw/core/spellcheck.py +++ b/nw/core/spellcheck.py @@ -75,7 +75,7 @@ class NWSpellCheck(): newWord = newWord.strip() self.PROJW.append(newWord) try: - with open(self.projectDict,mode="a+",encoding="utf-8") as outFile: + with open(self.projectDict, mode="a+", encoding="utf-8") as outFile: outFile.write("%s\n" % newWord) except Exception as e: logger.error("Failed to add word to project word list %s" % str(self.projectDict)) @@ -149,7 +149,7 @@ class NWSpellEnchant(NWSpellCheck): self.theDict = enchant.Dict(theLang) self.spellLanguage = theLang logger.debug("Enchant spell checking for language %s loaded" % theLang) - except: + except Exception: logger.error("Failed to load enchant spell checking for language %s" % theLang) self.theDict = NWSpellEnchantDummy() self.spellLanguage = None @@ -186,7 +186,7 @@ class NWSpellEnchant(NWSpellCheck): for spTag, spProvider in enchant.list_dicts(): spName = "%s [%s]" % (self.expandLanguage(spTag), spProvider.name) retList.append((spTag, spName)) - except: + except Exception: logger.error("Failed to list languages for enchant spell checking") return retList @@ -197,7 +197,7 @@ class NWSpellEnchantDummy: """ def __init__(self): return - + def check(self, theWord): return True @@ -231,9 +231,9 @@ class NWSpellSimple(NWSpellCheck): """Load a dictionary as a list from the app assets folder. """ self.WORDS = [] - dictFile = path.join(self.mainConf.dictPath,theLang+".dict") + dictFile = path.join(self.mainConf.dictPath, theLang+".dict") try: - with open(dictFile,mode="r",encoding="utf-8") as wordsFile: + with open(dictFile, mode="r", encoding="utf-8") as wordsFile: for theLine in wordsFile: if len(theLine) == 0 or theLine.startswith("#"): continue @@ -258,7 +258,7 @@ class NWSpellSimple(NWSpellCheck): this function as fast as possible as it is called for every word by the syntax highlighter. """ - theWord = theWord.replace(self.mainConf.fmtApostrophe,"'").lower() + theWord = theWord.replace(self.mainConf.fmtApostrophe, "'").lower() return theWord in self.WORDS def suggestWords(self, theWord): @@ -282,7 +282,7 @@ class NWSpellSimple(NWSpellCheck): continue if firstUp: aWord = aWord[0].upper() + aWord[1:] - aWord = aWord.replace("'",self.mainConf.fmtApostrophe) + aWord = aWord.replace("'", self.mainConf.fmtApostrophe) theOptions.append(aWord) return theOptions diff --git a/nw/core/status.py b/nw/core/status.py index 3dd26b35..eac1a52a 100644 --- a/nw/core/status.py +++ b/nw/core/status.py @@ -26,7 +26,6 @@ """ import logging -import nw from lxml import etree @@ -121,7 +120,7 @@ class NWStatus(): main project file. """ for n in range(self.theLength): - xSub = etree.SubElement(xParent,"entry",attrib={ + xSub = etree.SubElement(xParent, "entry", attrib={ "blue" : str(self.theColours[n][2]), "green" : str(self.theColours[n][1]), "red" : str(self.theColours[n][0]), @@ -138,18 +137,18 @@ class NWStatus(): for xChild in xParent: theLabels.append(xChild.text) if "red" in xChild.attrib: - cR = checkInt(xChild.attrib["red"],0,False) + cR = checkInt(xChild.attrib["red"], 0, False) else: cR = 0 if "green" in xChild.attrib: - cG = checkInt(xChild.attrib["green"],0,False) + cG = checkInt(xChild.attrib["green"], 0, False) else: cG = 0 if "blue" in xChild.attrib: - cB = checkInt(xChild.attrib["blue"],0,False) + cB = checkInt(xChild.attrib["blue"], 0, False) else: cB = 0 - theColours.append((cR,cG,cB)) + theColours.append((cR, cG, cB)) if len(theLabels) > 0: self.theLabels = [] diff --git a/nw/core/tohtml.py b/nw/core/tohtml.py index 95d3089f..74c5a676 100644 --- a/nw/core/tohtml.py +++ b/nw/core/tohtml.py @@ -27,7 +27,6 @@ import logging import re -import nw from nw.core.tokenizer import Tokenizer from nw.constants import nwUnicode, nwLabels, nwKeyWords @@ -153,12 +152,6 @@ class ToHtml(Tokenizer): h3 = "h3" h4 = "h4" - alignHead = self.A_LEFT - if self.doJustify: - alignPar = self.A_JUSTIFY - else: - alignPar = self.A_LEFT - self.theResult = "" thisPar = [] diff --git a/nw/core/tokenizer.py b/nw/core/tokenizer.py index 2ea8f330..0314d9af 100644 --- a/nw/core/tokenizer.py +++ b/nw/core/tokenizer.py @@ -418,11 +418,11 @@ class Tokenizer(): rxThis = theRX.globalMatch(aLine, 0) while rxThis.hasNext(): rxMatch = rxThis.next() - for n in range(1,len(theKeys)): + for n in range(1, len(theKeys)): if theKeys[n] is not None: xPos = rxMatch.capturedStart(n) xLen = rxMatch.capturedLength(n) - fmtPos.append([xPos,xLen,theKeys[n]]) + fmtPos.append([xPos, xLen, theKeys[n]]) # Save the line as is, but append the array of formatting locations # sorted by position @@ -686,7 +686,7 @@ class Tokenizer(): theTitle = theTitle.replace(r"%sc%", str(self.numChScene)) theTitle = theTitle.replace(r"%sca%", str(self.numAbsScene)) if r"%chw%" in theTitle: - theTitle = theTitle.replace(r"%chw%", numberToWord(self.numChapter,"en")) + theTitle = theTitle.replace(r"%chw%", numberToWord(self.numChapter, "en")) if r"%chi%" in theTitle: theTitle = theTitle.replace(r"%chi%", numberToRoman(self.numChapter, True)) if r"%chI%" in theTitle: diff --git a/nw/core/tools.py b/nw/core/tools.py index b46f0db8..ff1976e9 100644 --- a/nw/core/tools.py +++ b/nw/core/tools.py @@ -28,7 +28,6 @@ """ import logging -import nw logger = logging.getLogger(__name__) @@ -73,12 +72,13 @@ def countWords(theText): charCount -= 2 countPara = False - theBuff = aLine.replace("–"," ").replace("—"," ") + theBuff = aLine.replace("–", " ").replace("—", " ") wordCount += len(theBuff.split()) charCount += theLen if countPara and prevEmpty: paraCount += 1 - prevEmpty = countPara == False + + prevEmpty = not countPara return charCount, wordCount, paraCount @@ -139,48 +139,31 @@ def _numberToWordEN(numVal): tenVal = (numVal-oneVal) % 100 hunVal = (numVal-tenVal-oneVal) % 1000 - if hunVal == 100: hunWord = "One Hundred" - if hunVal == 200: hunWord = "Two Hundred" - if hunVal == 300: hunWord = "Three Hundred" - if hunVal == 400: hunWord = "Four Hundred" - if hunVal == 500: hunWord = "Five Hundred" - if hunVal == 600: hunWord = "Six Hundred" - if hunVal == 700: hunWord = "Seven Hundred" - if hunVal == 800: hunWord = "Eight Hundred" - if hunVal == 900: hunWord = "Nine Hundred" - - if tenVal == 20: tenWord = "Twenty" - if tenVal == 30: tenWord = "Thirty" - if tenVal == 40: tenWord = "Forty" - if tenVal == 50: tenWord = "Fifty" - if tenVal == 60: tenWord = "Sixty" - if tenVal == 70: tenWord = "Seventy" - if tenVal == 80: tenWord = "Eighty" - if tenVal == 90: tenWord = "Ninety" + theHundreds = { + 100: "One Hundred", 200: "Two Hundred", 300: "Three Hundred", + 400: "Four Hundred", 500: "Five Hundred", 600: "Six Hundred", + 700: "Seven Hundred", 800: "Eight Hundred", 900: "Nine Hundred", + } + theTens = { + 20: "Twenty", 30: "Thirty", 40: "Forty", 50: "Fifty", + 60: "Sixty", 70: "Seventy", 80: "Eighty", 90: "Ninety", + } + theTeens = { + 0: "Ten", 1: "Eleven", 2: "Twelve", 3: "Thirteen", 4: "Fourteen", + 5: "Fifteen", 6: "Sixteen", 7: "Seventeen", 8: "Eighteen", 9: "Nineteen", + } + theOnes = { + 0: "", 1: "One", 2: "Two", 3: "Three", 4: "Four", + 5: "Five", 6: "Six", 7: "Seven", 8: "Eight", 9: "Nine", + } + hunWord = theHundreds.get(hunVal, "") + tenWord = theTens.get(tenVal, "") if tenVal == 10: - if oneVal == 0: oneWord = "Ten" - if oneVal == 1: oneWord = "Eleven" - if oneVal == 2: oneWord = "Twelve" - if oneVal == 3: oneWord = "Thirteen" - if oneVal == 4: oneWord = "Fourteen" - if oneVal == 5: oneWord = "Fifteen" - if oneVal == 6: oneWord = "Sixteen" - if oneVal == 7: oneWord = "Seventeen" - if oneVal == 8: oneWord = "Eighteen" - if oneVal == 9: oneWord = "Nineteen" + oneWord = theTeens.get(oneVal, "") numWord = ("%s %s" % (hunWord, oneWord)).strip() else: - if oneVal == 0: oneWord = "" - if oneVal == 1: oneWord = "One" - if oneVal == 2: oneWord = "Two" - if oneVal == 3: oneWord = "Three" - if oneVal == 4: oneWord = "Four" - if oneVal == 5: oneWord = "Five" - if oneVal == 6: oneWord = "Six" - if oneVal == 7: oneWord = "Seven" - if oneVal == 8: oneWord = "Eight" - if oneVal == 9: oneWord = "Nine" + oneWord = theOnes.get(oneVal, "") if tenVal == 0: numWord = ("%s %s" % (hunWord, oneWord)).strip() else: diff --git a/nw/core/tree.py b/nw/core/tree.py index bafb5306..a9e77326 100644 --- a/nw/core/tree.py +++ b/nw/core/tree.py @@ -27,7 +27,6 @@ import logging import json -import nw from os import path from lxml import etree @@ -114,7 +113,7 @@ class NWTree(): """Pack the content of the tree into an XML object. """ xContent = etree.SubElement(xParent, "content", attrib={ - "count":str(self._theLength)} + "count": str(self._theLength)} ) for tHandle in self._treeOrder: tItem = self.__getitem__(tHandle) @@ -154,7 +153,7 @@ class NWTree(): outFile.write(" Table of Contents\n") outFile.write("===================\n") outFile.write("\n") - outFile.write(" %-25s %-9s %s\n" %("File Name","Class","Document Label")) + outFile.write(" %-25s %-9s %s\n" %("File Name", "Class", "Document Label")) outFile.write("-"*80+"\n") for tHandle in sorted(self._treeOrder): tItem = self.__getitem__(tHandle) diff --git a/nw/gui/about.py b/nw/gui/about.py index 02f9fa39..6a25af89 100644 --- a/nw/gui/about.py +++ b/nw/gui/about.py @@ -146,9 +146,9 @@ class GuiAbout(QDialog): aboutMsg += ( "
"
- "Author: {author:s}
"
- "Credit: {credit:s}
"
- "License: {license:s}"
+ "Author: {author:s}
"
+ "Credit: {credit:s}
"
+ "License: {license:s}"
"
"
- "Author: {author:s}
"
- "Credit: {credit:s}
"
- "License: {license:s}"
+ "Author: {author:s}
"
+ "Credit: {credit:s}
"
+ "License: {license:s}"
"
"
- "Author: {author:s}
"
- "Credit: {credit:s}
"
- "License: {license:s}"
+ "Author: {author:s}
"
+ "Credit: {credit:s}
"
+ "License: {license:s}"
"