diff --git a/novelwriter/common.py b/novelwriter/common.py index ec4bd5bd..e0c2dc67 100644 --- a/novelwriter/common.py +++ b/novelwriter/common.py @@ -45,52 +45,54 @@ logger = logging.getLogger(__name__) # Checker Functions # =============================================================================================== # -def checkString(value, default, allowNone=False): +def checkStringNone(value, default): """Check if a variable is a string or a None. """ - if allowNone and (value is None or value == "None"): + if value is None or value == "None": return None if isinstance(value, str): return str(value) return default -def checkInt(value, default, allowNone=False): - """Check if a variable is an integer or a None. +def checkString(value, default): + """Check if a variable is a string. + """ + if isinstance(value, str): + return str(value) + return default + + +def checkInt(value, default): + """Check if a variable is an integer. """ - if allowNone and (value is None or value == "None"): - return None try: return int(value) except Exception: return default -def checkFloat(value, default, allowNone=False): - """Check if a variable is a float or a None. +def checkFloat(value, default): + """Check if a variable is a float. """ - if allowNone and (value is None or value == "None"): - return None try: return float(value) except Exception: return default -def checkBool(value, default, allowNone=False): - """Check if a variable is a boolean or a None. +def checkBool(value, default): + """Check if a variable is a boolean. """ - if allowNone and (value is None or value == "None"): - return None - - if isinstance(value, str): + if isinstance(value, bool): + return value + elif isinstance(value, str): if value == "True": return True elif value == "False": return False else: return default - elif isinstance(value, int): if value == 1: return True @@ -98,7 +100,6 @@ def checkBool(value, default, allowNone=False): return False else: return default - return default @@ -174,16 +175,6 @@ def hexToInt(value, default=0): return default -def checkIntRange(value, first, last, default): - """Check that an int is in a given range. If it isn't, return the - default value. - """ - if isinstance(value, int): - if value >= first and value <= last: - return value - return default - - def minmax(value, minVal, maxVal): """Make sure an integer is between min and max value (inclusive). """ @@ -263,7 +254,7 @@ def splitVersionNumber(value): and patch, and computes an integer value aabbcc. """ if not isinstance(value, str): - return [0, 0, 0, 0] + return 0, 0, 0, 0 vMajor = 0 vMinor = 0 @@ -282,7 +273,7 @@ def splitVersionNumber(value): vInt = vMajor*10000 + vMinor*100 + vPatch - return [vMajor, vMinor, vPatch, vInt] + return vMajor, vMinor, vPatch, vInt def transferCase(theSource, theTarget): diff --git a/novelwriter/core/index.py b/novelwriter/core/index.py index 24e38097..3c8c1fd7 100644 --- a/novelwriter/core/index.py +++ b/novelwriter/core/index.py @@ -668,23 +668,17 @@ class TagsIndex: def tagHandle(self, tagKey): """Get the handle of a given tag. """ - if tagKey in self._tags: - return self._tags.get(tagKey).get("handle") - return None + return self._tags.get(tagKey, {}).get("handle", None) def tagHeading(self, tagKey): """Get the heading of a given tag. """ - if tagKey in self._tags: - return self._tags.get(tagKey).get("heading") - return nwHeaders.TT_NONE + return self._tags.get(tagKey, {}).get("heading", nwHeaders.TT_NONE) def tagClass(self, tagKey): """Get the class of a given tag. """ - if tagKey in self._tags: - return self._tags.get(tagKey).get("class") - return None + return self._tags.get(tagKey, {}).get("class", None) ## # Pack/Unpack diff --git a/novelwriter/core/options.py b/novelwriter/core/options.py index e1fcbc66..9d840e0f 100644 --- a/novelwriter/core/options.py +++ b/novelwriter/core/options.py @@ -188,8 +188,7 @@ class OptionState: the default value. """ if group in self._theState: - if name in self._theState[group]: - return checkBool(self._theState[group].get(name, default), default) + return checkBool(self._theState[group].get(name, default), default) return default def getEnum(self, group, name, lookup, default): diff --git a/novelwriter/core/project.py b/novelwriter/core/project.py index dcfa66d7..25e2862b 100644 --- a/novelwriter/core/project.py +++ b/novelwriter/core/project.py @@ -44,7 +44,7 @@ from novelwriter.core.document import NWDoc from novelwriter.enum import nwItemType, nwItemClass, nwItemLayout, nwAlert from novelwriter.error import logException from novelwriter.common import ( - checkString, checkBool, checkInt, isHandle, formatTimeStamp, + checkString, checkBool, checkInt, checkStringNone, isHandle, formatTimeStamp, makeFileNameSafe, hexToInt, minmax, simplified ) from novelwriter.constants import trConst, nwFiles, nwLabels @@ -187,7 +187,7 @@ class NWProject: return False newDoc = NWDoc(self, tHandle) - if newDoc.readDocument().strip(): + if (newDoc.readDocument() or "").strip(): return False hshText = "#"*minmax(hLevel, 1, 4) @@ -593,13 +593,13 @@ class NWProject: if xItem.text is None: continue if xItem.tag == "name": - self.projName = checkString(simplified(xItem.text), "") + self.projName = simplified(checkString(xItem.text, "")) logger.verbose("Working Title: '%s'", self.projName) elif xItem.tag == "title": - self.bookTitle = checkString(simplified(xItem.text), "") + self.bookTitle = simplified(checkString(xItem.text, "")) logger.verbose("Title is '%s'", self.bookTitle) elif xItem.tag == "author": - author = checkString(simplified(xItem.text), "") + author = simplified(checkString(xItem.text, "")) if author: self.bookAuthors.append(author) logger.verbose("Author: '%s'", author) @@ -618,25 +618,25 @@ class NWProject: if xItem.tag == "doBackup": self.doBackup = checkBool(xItem.text, False) elif xItem.tag == "language": - self.projLang = checkString(xItem.text, None, True) + self.projLang = checkStringNone(xItem.text, None) elif xItem.tag == "spellCheck": self.spellCheck = checkBool(xItem.text, False) elif xItem.tag == "spellLang": - self.projSpell = checkString(xItem.text, None, True) + self.projSpell = checkStringNone(xItem.text, None) elif xItem.tag == "lastEdited": - self.lastEdited = checkString(xItem.text, None, True) + self.lastEdited = checkStringNone(xItem.text, None) elif xItem.tag == "lastViewed": - self.lastViewed = checkString(xItem.text, None, True) + self.lastViewed = checkStringNone(xItem.text, None) elif xItem.tag == "lastNovel": - self.lastNovel = checkString(xItem.text, None, True) + self.lastNovel = checkStringNone(xItem.text, None) elif xItem.tag == "lastOutline": - self.lastOutline = checkString(xItem.text, None, True) + self.lastOutline = checkStringNone(xItem.text, None) elif xItem.tag == "lastWordCount": - self.lastWCount = checkInt(xItem.text, 0, False) + self.lastWCount = checkInt(xItem.text, 0) elif xItem.tag == "novelWordCount": - self.lastNovelWC = checkInt(xItem.text, 0, False) + self.lastNovelWC = checkInt(xItem.text, 0) elif xItem.tag == "notesWordCount": - self.lastNotesWC = checkInt(xItem.text, 0, False) + self.lastNotesWC = checkInt(xItem.text, 0) elif xItem.tag == "status": self.statusItems.unpackXML(xItem) elif xItem.tag == "importance": @@ -645,12 +645,12 @@ class NWProject: for xEntry in xItem: if xEntry.tag == "entry" and "key" in xEntry.attrib: self.autoReplace[xEntry.attrib["key"]] = checkString( - xEntry.text, None, False + xEntry.text, "ERROR" ) elif xItem.tag == "titleFormat": titleFormat = self.titleFormat.copy() for xEntry in xItem: - titleFormat[xEntry.tag] = checkString(xEntry.text, "", False) + titleFormat[xEntry.tag] = checkString(xEntry.text, "") self.setTitleFormat(titleFormat) elif xChild.tag == "content": @@ -1091,7 +1091,7 @@ class NWProject: def setSpellLang(self, theLang): """Set the project-specific spell check language. """ - theLang = checkString(theLang, None, True) + theLang = checkStringNone(theLang, None) if self.projSpell != theLang: self.projSpell = theLang self.setProjectChanged(True) @@ -1101,7 +1101,7 @@ class NWProject: def setProjectLang(self, theLang): """Set the project-specific language. """ - theLang = checkString(theLang, None, True) + theLang = checkStringNone(theLang, None) if self.projLang != theLang: self.projLang = theLang self._loadProjectLocalisation() diff --git a/novelwriter/core/tokenizer.py b/novelwriter/core/tokenizer.py index 3a3b52f1..c55e3659 100644 --- a/novelwriter/core/tokenizer.py +++ b/novelwriter/core/tokenizer.py @@ -304,16 +304,10 @@ class Tokenizer(ABC): if self._theItem is None: return False - self._theText = "" - if theText is not None: - # If the text is set, just use that - self._theText = theText - else: - # Otherwise, load it from file - theDoc = NWDoc(self.theProject, theHandle) - theText = theDoc.readDocument() - if theText: - self._theText = theText + if theText is None: + theText = NWDoc(self.theProject, theHandle).readDocument() or "" + + self._theText = theText docSize = len(self._theText) if docSize > nwConst.MAX_DOCSIZE: diff --git a/novelwriter/tools/writingstats.py b/novelwriter/tools/writingstats.py index 8d372641..5fcdd605 100644 --- a/novelwriter/tools/writingstats.py +++ b/novelwriter/tools/writingstats.py @@ -39,7 +39,7 @@ from PyQt5.QtWidgets import ( from novelwriter.enum import nwAlert from novelwriter.error import formatException -from novelwriter.common import formatTime, checkInt, checkIntRange, checkIntTuple +from novelwriter.common import formatTime, checkInt, checkIntTuple, minmax from novelwriter.custom import QSwitch from novelwriter.constants import nwConst, nwFiles @@ -116,7 +116,7 @@ class GuiWritingStats(QDialog): hHeader.setTextAlignment(self.C_IDLE, Qt.AlignRight) hHeader.setTextAlignment(self.C_COUNT, Qt.AlignRight) - sortCol = checkIntRange(pOptions.getInt("GuiWritingStats", "sortCol", 0), 0, 2, 0) + sortCol = minmax(pOptions.getInt("GuiWritingStats", "sortCol", 0), 0, 2) sortOrder = checkIntTuple( pOptions.getInt("GuiWritingStats", "sortOrder", Qt.DescendingOrder), (Qt.AscendingOrder, Qt.DescendingOrder), Qt.DescendingOrder diff --git a/tests/test_base/test_base_common.py b/tests/test_base/test_base_common.py index e93026b2..aa2d44e1 100644 --- a/tests/test_base/test_base_common.py +++ b/tests/test_base/test_base_common.py @@ -29,8 +29,8 @@ from tools import writeFile from novelwriter.guimain import GuiMain from novelwriter.common import ( - checkString, checkInt, checkFloat, checkBool, checkHandle, isHandle, - isTitleTag, isItemClass, isItemType, isItemLayout, hexToInt, checkIntRange, + checkStringNone, checkString, checkInt, checkFloat, checkBool, checkHandle, + isHandle, isTitleTag, isItemClass, isItemType, isItemLayout, hexToInt, minmax, checkIntTuple, formatInt, formatTimeStamp, formatTime, simplified, splitVersionNumber, transferCase, fuzzyTime, numberToRoman, jsonEncode, readTextFile, makeFileNameSafe, ensureFolder, sha256sum, getGuiItem, @@ -38,17 +38,29 @@ from novelwriter.common import ( ) +@pytest.mark.base +def testBaseCommon_CheckStringNone(): + """Test the checkStringNone function. + """ + assert checkStringNone("Stuff", "NotNone") == "Stuff" + assert checkStringNone("None", "NotNone") is None + assert checkStringNone(None, "NotNone") is None + assert checkStringNone(1, "NotNone") == "NotNone" + assert checkStringNone(1.0, "NotNone") == "NotNone" + assert checkStringNone(True, "NotNone") == "NotNone" + +# END Test testBaseCommon_CheckStringNone + + @pytest.mark.base def testBaseCommon_CheckString(): """Test the checkString function. """ - assert checkString(None, "NotNone", True) is None - assert checkString("None", "NotNone", True) is None - assert checkString("None", "NotNone", False) == "None" - assert checkString(None, "NotNone", False) == "NotNone" - assert checkString(1, "NotNone", False) == "NotNone" - assert checkString(1.0, "NotNone", False) == "NotNone" - assert checkString(True, "NotNone", False) == "NotNone" + assert checkString("None", "NotNone") == "None" + assert checkString(None, "NotNone") == "NotNone" + assert checkString(1, "NotNone") == "NotNone" + assert checkString(1.0, "NotNone") == "NotNone" + assert checkString(True, "NotNone") == "NotNone" # END Test testBaseCommon_CheckString @@ -57,12 +69,12 @@ def testBaseCommon_CheckString(): def testBaseCommon_CheckInt(): """Test the checkInt function. """ - assert checkInt(None, 3, True) is None - assert checkInt("None", 3, True) is None - assert checkInt(None, 3, False) == 3 - assert checkInt(1, 3, False) == 1 - assert checkInt(1.0, 3, False) == 1 - assert checkInt(True, 3, False) == 1 + assert checkInt(None, 3) == 3 + assert checkInt("1", 3) == 1 + assert checkInt("1.0", 3) == 3 + assert checkInt(1, 3) == 1 + assert checkInt(1.0, 3) == 1 + assert checkInt(True, 3) == 1 # END Test testBaseCommon_CheckInt @@ -71,12 +83,12 @@ def testBaseCommon_CheckInt(): def testBaseCommon_CheckFloat(): """Test the checkFloat function. """ - assert checkFloat(None, 3.0, True) is None - assert checkFloat("None", 3.0, True) is None - assert checkFloat(None, 3.0, False) == 3.0 - assert checkFloat(1, 3.0, False) == 1.0 - assert checkFloat(1.0, 3.0, False) == 1.0 - assert checkFloat(True, 3.0, False) == 1.0 + assert checkFloat(None, 3.0) == 3.0 + assert checkFloat("1", 3.0) == 1.0 + assert checkFloat("1.0", 3.0) == 1.0 + assert checkFloat(1, 3.0) == 1.0 + assert checkFloat(1.0, 3.0) == 1.0 + assert checkFloat(True, 3.0) == 1.0 # END Test testBaseCommon_CheckInt @@ -85,17 +97,18 @@ def testBaseCommon_CheckFloat(): def testBaseCommon_CheckBool(): """Test the checkBool function. """ - assert checkBool(None, 3, True) is None - assert checkBool("None", 3, True) is None - assert checkBool("True", False, False) is True - assert checkBool("False", True, False) is False - assert checkBool("Boo", None, False) is None - assert checkBool(0, None, False) is False - assert checkBool(1, None, False) is True - assert checkBool(2, None, False) is None - assert checkBool(0.0, None, False) is None - assert checkBool(1.0, None, False) is None - assert checkBool(2.0, None, False) is None + assert checkBool("True", False) is True + assert checkBool("False", True) is False + assert checkBool("Boo", False) is False + assert checkBool("Boo", True) is True + assert checkBool(0, True) is False + assert checkBool(1, False) is True + assert checkBool(2, True) is True + assert checkBool(2, False) is False + assert checkBool(0.0, True) is True + assert checkBool(1.0, False) is False + assert checkBool(2.0, True) is True + assert checkBool(2.0, False) is False # END Test testBaseCommon_CheckBool @@ -229,18 +242,6 @@ def testBaseCommon_HexToInt(): # END Test testBaseCommon_HexToInt -@pytest.mark.base -def testBaseCommon_CheckIntRange(): - """Test the checkIntRange function. - """ - assert checkIntRange(5, 0, 9, 3) == 5 - assert checkIntRange(5, 0, 4, 3) == 3 - assert checkIntRange(5, 0, 5, 3) == 5 - assert checkIntRange(0, 0, 5, 3) == 0 - -# END Test testBaseCommon_CheckIntRange - - @pytest.mark.base def testBaseCommon_MinMax(): """Test the minmax function. @@ -311,16 +312,16 @@ def testBaseCommon_SplitVersionNumber(): """Test the splitVersionNumber function. """ # OK Values - assert splitVersionNumber("1") == [1, 0, 0, 10000] - assert splitVersionNumber("1.2") == [1, 2, 0, 10200] - assert splitVersionNumber("1.2.3") == [1, 2, 3, 10203] - assert splitVersionNumber("1.2.3.4") == [1, 2, 3, 10203] - assert splitVersionNumber("99.99.99") == [99, 99, 99, 999999] + assert splitVersionNumber("1") == (1, 0, 0, 10000) + assert splitVersionNumber("1.2") == (1, 2, 0, 10200) + assert splitVersionNumber("1.2.3") == (1, 2, 3, 10203) + assert splitVersionNumber("1.2.3.4") == (1, 2, 3, 10203) + assert splitVersionNumber("99.99.99") == (99, 99, 99, 999999) # Failed Values - assert splitVersionNumber(None) == [0, 0, 0, 0] - assert splitVersionNumber(1234) == [0, 0, 0, 0] - assert splitVersionNumber("1.2abc") == [1, 0, 0, 10000] + assert splitVersionNumber(None) == (0, 0, 0, 0) + assert splitVersionNumber(1234) == (0, 0, 0, 0) + assert splitVersionNumber("1.2abc") == (1, 0, 0, 10000) # END Test testBaseCommon_SplitVersionNumber