From 670ba225d0f1759a87c2c12c424de1a20e3ba092 Mon Sep 17 00:00:00 2001 From: ptmcg Date: Sun, 25 Feb 2024 15:34:33 -0600 Subject: [PATCH] Convert legacy string formatting to f-strings; expand on some docstrings and comments --- CHANGES | 3 + examples/SimpleCalc.py | 2 +- examples/TAP.py | 10 +-- examples/adventureEngine.py | 22 +++--- examples/apicheck.py | 8 +-- examples/btpyparse.py | 2 +- examples/cpp_enum_parser.py | 2 +- examples/datetime_parse_actions.py | 9 +-- examples/delta_time.py | 2 +- examples/dfmparse.py | 7 +- examples/gen_ctypes.py | 8 +-- examples/partial_gene_match.py | 2 +- examples/searchparser.py | 4 +- examples/simpleBool.py | 4 +- examples/statemachine/statemachine.py | 16 ++--- .../statemachine/trafficlightstate.pystate | 2 +- pyparsing/__init__.py | 2 +- pyparsing/actions.py | 1 - pyparsing/core.py | 55 ++++++++------- pyparsing/diagram/__init__.py | 2 +- pyparsing/exceptions.py | 16 +++-- pyparsing/helpers.py | 6 +- pyparsing/results.py | 26 ++----- tests/test_unit.py | 68 +++++++++---------- update_pyparsing_timestamp.py | 2 +- 25 files changed, 135 insertions(+), 146 deletions(-) diff --git a/CHANGES b/CHANGES index 32483477..f21e377f 100644 --- a/CHANGES +++ b/CHANGES @@ -36,6 +36,9 @@ Version 3.1.2 - in development - Some code refactoring to reduce code nesting, PRs submitted by InSync. +- All internal string expressions using '%' string interpolation and `str.format()` + converted to f-strings. + Version 3.1.1 - July, 2023 -------------------------- diff --git a/examples/SimpleCalc.py b/examples/SimpleCalc.py index 7ace9aea..23b2893d 100644 --- a/examples/SimpleCalc.py +++ b/examples/SimpleCalc.py @@ -52,7 +52,7 @@ # elif op[0].isalpha(): # if op in variables: # return variables[op] -# raise Exception("invalid identifier '%s'" % op) +# raise Exception(f"invalid identifier {op!r}") # else: # return float( op ) diff --git a/examples/TAP.py b/examples/TAP.py index 788a656a..b41e9510 100644 --- a/examples/TAP.py +++ b/examples/TAP.py @@ -148,15 +148,15 @@ def summary(self, showPassed=False, showAll=False): testListStr = lambda tl: "[" + ",".join(str(t.num) for t in tl) + "]" summaryText = [] if showPassed or showAll: - summaryText.append("PASSED: %s" % testListStr(self.passedTests)) + summaryText.append(f"PASSED: {testListStr(self.passedTests)}") if self.failedTests or showAll: - summaryText.append("FAILED: %s" % testListStr(self.failedTests)) + summaryText.append(f"FAILED: {testListStr(self.failedTests)}") if self.skippedTests or showAll: - summaryText.append("SKIPPED: %s" % testListStr(self.skippedTests)) + summaryText.append(f"SKIPPED: {testListStr(self.skippedTests)}") if self.todoTests or showAll: - summaryText.append("TODO: %s" % testListStr(self.todoTests)) + summaryText.append(f"TODO: {testListStr(self.todoTests)}") if self.bonusTests or showAll: - summaryText.append("BONUS: %s" % testListStr(self.bonusTests)) + summaryText.append(f"BONUS: {testListStr(self.bonusTests)}") if self.passedSuite: summaryText.append("PASSED") else: diff --git a/examples/adventureEngine.py b/examples/adventureEngine.py index 4f27d793..7010181f 100644 --- a/examples/adventureEngine.py +++ b/examples/adventureEngine.py @@ -76,9 +76,9 @@ def describe(self): is_form = "are" else: is_form = "is" - print("There {} {} here.".format(is_form, enumerate_items(visibleItems))) + print(f"There {is_form} {enumerate_items(visibleItems)} here.") else: - print("You see %s." % (enumerate_items(visibleItems))) + print(f"You see {enumerate_items(visibleItems)}.") class Exit(Room): @@ -220,7 +220,7 @@ def _do_command(self, player): else: print(subj.cantTakeMessage) else: - print("There is no %s here." % subj) + print(f"There is no {subj} here.") class DropCommand(Command): @@ -239,7 +239,7 @@ def _do_command(self, player): rm.add_item(subj) player.drop(subj) else: - print("You don't have %s." % (a_or_an(subj))) + print(f"You don't have {a_or_an(subj)}.") class InventoryCommand(Command): @@ -251,7 +251,7 @@ def help_description(): return "INVENTORY or INV or I - lists what items you have" def _do_command(self, player): - print("You have %s." % enumerate_items(player.inv)) + print(f"You have {enumerate_items(player.inv)}.") class LookCommand(Command): @@ -340,7 +340,7 @@ def _do_command(self, player): else: print("You can't use that here.") else: - print("There is no %s here to use." % self.subject) + print(f"There is no {self.subject} here to use.") class OpenCommand(Command): @@ -364,7 +364,7 @@ def _do_command(self, player): else: print("You can't open that.") else: - print("There is no %s here to open." % self.subject) + print(f"There is no {self.subject} here to open.") class CloseCommand(Command): @@ -388,7 +388,7 @@ def _do_command(self, player): else: print("You can't close that.") else: - print("There is no %s here to close." % self.subject) + print(f"There is no {self.subject} here to close.") class QuitCommand(Command): @@ -428,7 +428,7 @@ def _do_command(self, player): QuitCommand, HelpCommand, ]: - print(" - %s" % cmd.help_description()) + print(f" - {cmd.help_description()}") print() @@ -515,7 +515,7 @@ def make_bnf(self): def validate_item_name(self, s, l, t): iname = " ".join(t) if iname not in Item.items: - raise AppParseException(s, l, "No such item '%s'." % iname) + raise AppParseException(s, l, f"No such item '{iname}'.") return iname def parse_cmd(self, cmdstr): @@ -556,7 +556,7 @@ def moveTo(self, rm): def take(self, it): if it.isDeadly: - print("Aaaagh!...., the %s killed me!" % it) + print(f"Aaaagh!...., the {it} killed me!") self.gameOver = True else: self.inv.append(it) diff --git a/examples/apicheck.py b/examples/apicheck.py index 366ad066..358dd6f2 100644 --- a/examples/apicheck.py +++ b/examples/apicheck.py @@ -49,12 +49,12 @@ def apiProc(name, numargs): while 1: try: t, s, e = next(api_scanner) - print("found %s on line %d" % (t.procname, lineno(s, test))) + print(f"found {t.procname} on line {lineno(s, test)}") except ParseSyntaxException as pe: - print("invalid arg count on line", pe.lineno) - print(pe.lineno, ":", pe.line) + print(f"invalid arg count on line {pe.lineno}") + print(f"{pe.lineno} : {pe.line}") # reset api scanner to start after this exception location - test = "\n" * (pe.lineno - 1) + test[pe.loc + 1 :] + test = "\n" * (pe.lineno - 1) + test[pe.loc + 1:] api_scanner = apiRef.scanString(test) except StopIteration: break diff --git a/examples/btpyparse.py b/examples/btpyparse.py index 3531761d..be5cb0b4 100644 --- a/examples/btpyparse.py +++ b/examples/btpyparse.py @@ -30,7 +30,7 @@ def __init__(self, name): self.name = name def __repr__(self): - return 'Macro("%s")' % self.name + return f'Macro("{self.name}")' def __eq__(self, other): return self.name == other.name diff --git a/examples/cpp_enum_parser.py b/examples/cpp_enum_parser.py index 77eb3a73..1b015097 100644 --- a/examples/cpp_enum_parser.py +++ b/examples/cpp_enum_parser.py @@ -49,5 +49,5 @@ for entry in item.names: if entry.value != "": idx = int(entry.value) - print("%s_%s = %d" % (item.enum.upper(), entry.name.upper(), idx)) + print(f"{item.enum.upper()}_{entry.name.upper()} = {idx}") idx += 1 diff --git a/examples/datetime_parse_actions.py b/examples/datetime_parse_actions.py index ff386562..b1121418 100644 --- a/examples/datetime_parse_actions.py +++ b/examples/datetime_parse_actions.py @@ -1,6 +1,6 @@ # parseActions.py # -# A sample program a parser to match a date string of the form "YYYY/MM/DD", +# A sample parser to match a date string of the form "YYYY/MM/DD", # and return it as a datetime, or raise an exception if not a valid date. # # Copyright 2012, Paul T. McGuire @@ -36,12 +36,7 @@ def convert_to_datetime(s, loc, tokens): # on the integer expression above return datetime(tokens.year, tokens.month, tokens.day).date() except Exception as ve: - errmsg = "'%s/%s/%s' is not a valid date, %s" % ( - tokens.year, - tokens.month, - tokens.day, - ve, - ) + errmsg = f"'{tokens.year}/{tokens.month}/{tokens.day}' is not a valid date, {ve}" raise pp.ParseException(s, loc, errmsg) diff --git a/examples/delta_time.py b/examples/delta_time.py index cdd58f48..9b502901 100644 --- a/examples/delta_time.py +++ b/examples/delta_time.py @@ -450,7 +450,7 @@ def verify_offset(instring, parsed): else: parsed["verify_offset"] = "FAIL" - print("(relative to %s)" % datetime.now()) + print(f"(relative to {datetime.now()})") success, report = time_expression.runTests(tests, postParse=verify_offset) assert success diff --git a/examples/dfmparse.py b/examples/dfmparse.py index 5d9b1b14..cc5a0aa2 100644 --- a/examples/dfmparse.py +++ b/examples/dfmparse.py @@ -100,7 +100,7 @@ def to_chr(x): # a single matched pair of quotes around it. delphi_string = Combine( OneOrMore(CONCAT | pound_char | unquoted_sglQuotedString), adjacent=False -).setParseAction(lambda s, l, t: "'%s'" % t[0]) +).setParseAction(lambda s, l, t: f"'{t[0]}'") string_value = delphi_string | base16_value @@ -219,9 +219,10 @@ def main(testfiles=None, action=printer): except Exception: failures.append(f) + nl = "\n" if failures: - print("\nfailed while processing %s" % ", ".join(failures)) - print("\nsucceeded on %d of %d files" % (success, len(testfiles))) + print(f"{nl}failed while processing {', '.join(failures)}") + print(f"{nl}succeeded on {success} of {len(testfiles)} files") if len(retval) == 1 and len(testfiles) == 1: # if only one file is parsed, return the parseResults directly diff --git a/examples/gen_ctypes.py b/examples/gen_ctypes.py index 0eb0b7b7..65d2b21d 100644 --- a/examples/gen_ctypes.py +++ b/examples/gen_ctypes.py @@ -130,7 +130,7 @@ def typeAsCtypes(typestr): if typestr in typemap: return typemap[typestr] if typestr.endswith("*"): - return "POINTER(%s)" % typeAsCtypes(typestr.rstrip(" *")) + return f"POINTER({typeAsCtypes(typestr.rstrip(' *'))})" return typestr @@ -178,7 +178,7 @@ def typeAsCtypes(typestr): ) ) for udtype in user_defined_types: - print("class %s(Structure): pass" % typemap[udtype]) + print(f"class {typemap[udtype]}(Structure): pass") print() print("# constant definitions") @@ -192,7 +192,7 @@ def typeAsCtypes(typestr): print("{}.restype = {}".format(prefix, typeAsCtypes(fn.fn_type))) if fn.varargs: - print("# warning - %s takes variable argument list" % prefix) + print(f"# warning - {prefix} takes variable argument list") del fn.fn_args[-1] if fn.fn_args.asList() != [["void"]]: @@ -202,4 +202,4 @@ def typeAsCtypes(typestr): ) ) else: - print("%s.argtypes = ()" % (prefix)) + print(f"{prefix}.argtypes = ()") diff --git a/examples/partial_gene_match.py b/examples/partial_gene_match.py index fe62e772..39ec15f2 100644 --- a/examples/partial_gene_match.py +++ b/examples/partial_gene_match.py @@ -47,7 +47,7 @@ for t, startLoc, endLoc in searchseq.scanString(g.gene, overlap=True): if show_header: # only need to show the header once - print("%s/%s/%s (%d)" % (g.gene_id, g.organism, g.location, g.gene_len)) + print(f"{g.gene_id}/{g.organism}/{g.location} ({g.gene_len})") print("-" * 24) show_header = False diff --git a/examples/searchparser.py b/examples/searchparser.py index db00e44e..98f078fc 100644 --- a/examples/searchparser.py +++ b/examples/searchparser.py @@ -301,8 +301,8 @@ def Test(self): print(item) r = self.Parse(item) e = self.tests[item] - print("Result: %s" % r) - print("Expect: %s" % e) + print(f"Result: {r}") + print(f"Expect: {e}") if e == r: print("Test OK") else: diff --git a/examples/simpleBool.py b/examples/simpleBool.py index 530a53ad..a26857f3 100644 --- a/examples/simpleBool.py +++ b/examples/simpleBool.py @@ -60,8 +60,8 @@ def __init__(self, t): self.args = t[0][0::2] def __str__(self) -> str: - sep = " %s " % self.repr_symbol - return "(" + sep.join(map(str, self.args)) + ")" + sep = f" {self.repr_symbol} " + return f"({sep.join(map(str, self.args))})" def __bool__(self) -> bool: return self.eval_fn(bool(a) for a in self.args) diff --git a/examples/statemachine/statemachine.py b/examples/statemachine/statemachine.py index 761a181d..4126bdeb 100644 --- a/examples/statemachine/statemachine.py +++ b/examples/statemachine/statemachine.py @@ -75,7 +75,7 @@ def expand_state_definition(source, loc, tokens): baseStateClass = tokens.name statedef.extend( [ - "class %s(object):" % baseStateClass, + f"class {baseStateClass}(object):", " def __str__(self):", " return self.__class__.__name__", " @classmethod", @@ -173,7 +173,7 @@ def expand_named_state_definition(source, loc, tokens): # define base class for state classes statedef.extend( [ - "class %s(object):" % baseStateClass, + f"class {baseStateClass}(object):", " from statemachine import InvalidTransitionException as BaseTransitionException", " class InvalidTransitionException(BaseTransitionException): pass", " def __str__(self):", @@ -186,10 +186,10 @@ def expand_named_state_definition(source, loc, tokens): " try:", " return cls.tnmap[name]()", " except KeyError:", - " raise cls.InvalidTransitionException('%s does not support transition %r'% (cls.__name__, name))", + " raise cls.InvalidTransitionException(f'{cls.__name__} does not support transition {name!r}'", " def __bad_tn(name):", " def _fn(cls):", - " raise cls.InvalidTransitionException('%s does not support transition %r'% (cls.__name__, name))", + " raise cls.InvalidTransitionException(f'{cls.__name__} does not support transition {name!r}'", " _fn.__name__ = name", " return _fn", ] @@ -207,9 +207,9 @@ def expand_named_state_definition(source, loc, tokens): # define state transition methods for valid transitions from each state for s in states: trns = list(fromTo[s].items()) - # statedef.append("%s.tnmap = {%s}" % (s, ", ".join("%s:%s" % tn for tn in trns))) + # statedef.append(f"{s}.tnmap = {{{', '.join('%s:%s' % tn for tn in trns)}}}") statedef.extend( - "{}.{} = classmethod(lambda cls: {}())".format(s, tn_, to_) + f"{s}.{tn_} = classmethod(lambda cls: {to_}())" for tn_, to_ in trns ) @@ -286,8 +286,8 @@ class SuffixImporter: @classmethod def trigger_url(cls): if cls.suffix is None: - raise ValueError("%s.suffix is not set" % cls.__name__) - return "suffix:%s" % cls.suffix + raise ValueError(f"{cls.__name__}.suffix is not set") + return f"suffix:{cls.suffix}" @classmethod def register(cls): diff --git a/examples/statemachine/trafficlightstate.pystate b/examples/statemachine/trafficlightstate.pystate index 87901892..f42bc902 100644 --- a/examples/statemachine/trafficlightstate.pystate +++ b/examples/statemachine/trafficlightstate.pystate @@ -26,7 +26,7 @@ Green.cars_can_go = True # setup some class level methods def flash_crosswalk(s): def flash(): - print("%s...%s...%s" % (s, s, s)) + print(f"{s}...{s}...{s}") return flash diff --git a/pyparsing/__init__.py b/pyparsing/__init__.py index d97e127b..beef5e74 100644 --- a/pyparsing/__init__.py +++ b/pyparsing/__init__.py @@ -121,7 +121,7 @@ def __repr__(self): __version_info__ = version_info(3, 1, 2, "final", 1) -__version_time__ = "02 Oct 2023 03:34 UTC" +__version_time__ = "25 Feb 2024 17:23 UTC" __version__ = __version_info__.__version__ __versionTime__ = __version_time__ __author__ = "Paul McGuire " diff --git a/pyparsing/actions.py b/pyparsing/actions.py index a7443566..ce51b395 100644 --- a/pyparsing/actions.py +++ b/pyparsing/actions.py @@ -111,7 +111,6 @@ def with_attribute(*args, **attr_dict):
1,3 2,3 1,1
this has no type
- ''' div,div_end = make_html_tags("div") diff --git a/pyparsing/core.py b/pyparsing/core.py index a62560e5..c4195a7d 100644 --- a/pyparsing/core.py +++ b/pyparsing/core.py @@ -571,6 +571,7 @@ def set_results_name( Example:: + integer = Word(nums) date_str = (integer.set_results_name("year") + '/' + integer.set_results_name("month") + '/' + integer.set_results_name("day")) @@ -1081,7 +1082,7 @@ def enable_left_recursion( elif cache_size_limit > 0: ParserElement.recursion_memos = _LRUMemo(capacity=cache_size_limit) # type: ignore[assignment] else: - raise NotImplementedError("Memo size of %s" % cache_size_limit) + raise NotImplementedError(f"Memo size of {cache_size_limit}") ParserElement._left_recursion_enabled = True @staticmethod @@ -1779,7 +1780,7 @@ def ignore(self, other: "ParserElement") -> "ParserElement": Example:: - patt = Word(alphas)[1, ...] + patt = Word(alphas)[...] patt.parse_string('ablaj /* comment */ lskjd') # -> ['ablaj'] @@ -1894,8 +1895,11 @@ def set_name(self, name: str) -> "ParserElement": Example:: - Word(nums).parse_string("ABC") # -> Exception: Expected W:(0-9) (at char 0), (line:1, col:1) - Word(nums).set_name("integer").parse_string("ABC") # -> Exception: Expected integer (at char 0), (line:1, col:1) + integer = Word(nums) + integer.parse_string("ABC") # -> Exception: Expected W:(0-9) (at char 0), (line:1, col:1) + + integer.set_name("integer") + integer.parse_string("ABC") # -> Exception: Expected integer (at char 0), (line:1, col:1) """ self.customName = name self.errmsg = f"Expected {self.name}" @@ -2144,6 +2148,7 @@ def run_tests( success = True NL = Literal(r"\n").add_parse_action(replace_with("\n")).ignore(quoted_string) BOM = "\ufeff" + nlstr = "\n" for t in tests: if comment_specified and comment.matches(t, False) or comments and not t: comments.append( @@ -2153,7 +2158,7 @@ def run_tests( if not t: continue out = [ - "\n" + "\n".join(comments) if comments else "", + f"{nlstr}{nlstr.join(comments) if comments else ''}", pyparsing_test.with_line_numbers(t) if with_line_numbers else t, ] comments = [] @@ -2162,9 +2167,9 @@ def run_tests( t = NL.transform_string(t.lstrip(BOM)) result = self.parse_string(t, parse_all=parseAll) except ParseBaseException as pe: - fatal = "(FATAL)" if isinstance(pe, ParseFatalException) else "" + fatal = "(FATAL) " if isinstance(pe, ParseFatalException) else "" out.append(pe.explain()) - out.append(f"FAIL{fatal}: {pe}") + out.append(f"FAIL: {fatal}{pe}") if ParserElement.verbose_stacktrace: out.extend(traceback.format_tb(pe.__traceback__)) success = success and failureTests @@ -2367,9 +2372,9 @@ class Literal(Token): Example:: - Literal('blah').parse_string('blah') # -> ['blah'] - Literal('blah').parse_string('blahfooblah') # -> ['blah'] - Literal('blah').parse_string('bla') # -> Exception: Expected "blah" + Literal('abc').parse_string('abc') # -> ['abc'] + Literal('abc').parse_string('abcdef') # -> ['abc'] + Literal('abc').parse_string('ab') # -> Exception: Expected "abc" For case-insensitive matching, use :class:`CaselessLiteral`. @@ -2399,7 +2404,7 @@ def __init__(self, match_string: str = "", *, matchString: str = ""): self.match = match_string self.matchLen = len(match_string) self.firstMatchChar = match_string[:1] - self.errmsg = "Expected " + self.name + self.errmsg = f"Expected {self.name}" self.mayReturnEmpty = False self.mayIndexError = False @@ -2575,7 +2580,7 @@ def __init__(self, match_string: str = "", *, matchString: str = ""): super().__init__(match_string.upper()) # Preserve the defining literal. self.returnString = match_string - self.errmsg = "Expected " + self.name + self.errmsg = f"Expected {self.name}" def parseImpl(self, instring, loc, doActions=True): if instring[loc : loc + self.matchLen].upper() == self.match: @@ -2750,7 +2755,7 @@ class Word(Token): integer = Word(nums) # equivalent to Word("0123456789") or Word(srange("0-9")) # a word with a leading capital, and zero or more lowercase - capital_word = Word(alphas.upper(), alphas.lower()) + capitalized_word = Word(alphas.upper(), alphas.lower()) # hostnames are alphanumeric, with leading alpha, and '-' hostname = Word(alphas, alphanums + '-') @@ -2827,7 +2832,7 @@ def __init__( self.maxLen = exact self.minLen = exact - self.errmsg = "Expected " + self.name + self.errmsg = f"Expected {self.name}" self.mayIndexError = False self.asKeyword = asKeyword if self.asKeyword: @@ -3030,7 +3035,7 @@ def __init__( "Regex may only be constructed with a string or a compiled RE object" ) - self.errmsg = "Expected " + self.name + self.errmsg = f"Expected {self.name}" self.mayIndexError = False self.asGroupList = asGroupList self.asMatch = asMatch @@ -3282,7 +3287,7 @@ def __init__( except re.error: raise ValueError(f"invalid pattern {self.pattern!r} passed to Regex") - self.errmsg = "Expected " + self.name + self.errmsg = f"Expected {self.name}" self.mayIndexError = False self.mayReturnEmpty = True @@ -3397,7 +3402,7 @@ def __init__( self.maxLen = exact self.minLen = exact - self.errmsg = "Expected " + self.name + self.errmsg = f"Expected {self.name}" self.mayReturnEmpty = self.minLen == 0 self.mayIndexError = False @@ -3470,7 +3475,7 @@ def __init__(self, ws: str = " \t\r\n", min: int = 1, max: int = 0, exact: int = ) # self.leave_whitespace() self.mayReturnEmpty = True - self.errmsg = "Expected " + self.name + self.errmsg = f"Expected {self.name}" self.minLen = min @@ -3782,7 +3787,7 @@ def ignore(self, other) -> ParserElement: return self def _generateDefaultName(self) -> str: - return f"{self.__class__.__name__}:({self.exprs})" + return f"{type(self).__name__}:({self.exprs})" def streamline(self) -> ParserElement: if self.streamlined: @@ -3821,7 +3826,7 @@ def streamline(self) -> ParserElement: self.mayReturnEmpty |= other.mayReturnEmpty self.mayIndexError |= other.mayIndexError - self.errmsg = "Expected " + str(self) + self.errmsg = f"Expected {self}" return self @@ -4567,7 +4572,7 @@ def validate(self, validateTrace=None) -> None: self._checkRecursion([]) def _generateDefaultName(self) -> str: - return f"{self.__class__.__name__}:({self.expr})" + return f"{type(self).__name__}:({self.expr})" # Compatibility synonyms # fmt: off @@ -4782,7 +4787,7 @@ def __init__( retreat = 0 self.exact = True self.retreat = retreat - self.errmsg = "not preceded by " + str(expr) + self.errmsg = f"not preceded by {expr}" self.skipWhitespace = False self.parseAction.append(lambda s, l, t: t.__delitem__(slice(None, None))) @@ -4887,7 +4892,7 @@ def __init__(self, expr: Union[ParserElement, str]): self.skipWhitespace = False self.mayReturnEmpty = True - self.errmsg = "Found unwanted token, " + str(self.expr) + self.errmsg = f"Found unwanted token, {self.expr}" def parseImpl(self, instring, loc, doActions=True): if self.expr.can_parse_next(instring, loc, do_actions=doActions): @@ -5566,7 +5571,7 @@ def _generateDefaultName(self) -> str: else: retString = "None" finally: - return f"{self.__class__.__name__}: {retString}" + return f"{type(self).__name__}: {retString}" def copy(self) -> ParserElement: if self.expr is not None: @@ -5877,7 +5882,7 @@ def z(*paArgs): thisFunc = f.__name__ s, l, t = paArgs[-3:] if len(paArgs) > 3: - thisFunc = f"{paArgs[0].__class__.__name__}.{thisFunc}" + thisFunc = f"{type(paArgs[0]).__name__}.{thisFunc}" sys.stderr.write(f">>entering {thisFunc}(line: {line(l, s)!r}, {l}, {t!r})\n") try: ret = f(*paArgs) diff --git a/pyparsing/diagram/__init__.py b/pyparsing/diagram/__init__.py index 267f3447..700d0b56 100644 --- a/pyparsing/diagram/__init__.py +++ b/pyparsing/diagram/__init__.py @@ -473,7 +473,7 @@ def _to_diagram_element( :param show_groups: bool flag indicating whether to show groups using bounding box """ exprs = element.recurse() - name = name_hint or element.customName or element.__class__.__name__ + name = name_hint or element.customName or type(element).__name__ # Python's id() is used to provide a unique identifier for elements el_id = id(element) diff --git a/pyparsing/exceptions.py b/pyparsing/exceptions.py index 5d21223a..6229985f 100644 --- a/pyparsing/exceptions.py +++ b/pyparsing/exceptions.py @@ -14,11 +14,11 @@ from .unicode import pyparsing_unicode as ppu -class ExceptionWordUnicode(ppu.Latin1, ppu.LatinA, ppu.LatinB, ppu.Greek, ppu.Cyrillic): +class _ExceptionWordUnicodeSet(ppu.Latin1, ppu.LatinA, ppu.LatinB, ppu.Greek, ppu.Cyrillic): pass -_extract_alphanums = _collapse_string_to_ranges(ExceptionWordUnicode.alphanums) +_extract_alphanums = _collapse_string_to_ranges(_ExceptionWordUnicodeSet.alphanums) _exception_word_extractor = re.compile("([" + _extract_alphanums + "]{1,16})|.") @@ -83,7 +83,7 @@ def explain_exception(exc, depth=16): ret = [] if isinstance(exc, ParseBaseException): ret.append(exc.line) - ret.append(f"{' ' * (exc.column - 1)}^") + ret.append(" " * (exc.column - 1) + "^") ret.append(f"{type(exc).__name__}: {exc}") if depth <= 0: @@ -218,8 +218,10 @@ def explain(self, depth=16) -> str: Example:: + # an expression to parse 3 integers expr = pp.Word(pp.nums) * 3 try: + # a failing parse - the third integer is prefixed with "A" expr.parse_string("123 456 A789") except pp.ParseException as pe: print(pe.explain(depth=0)) @@ -252,16 +254,16 @@ class ParseException(ParseBaseException): Example:: + integer = Word(nums).set_name("integer") try: - Word(nums).set_name("integer").parse_string("ABC") + integer.parse_string("ABC") except ParseException as pe: print(pe) - print("column: {}".format(pe.column)) + print(f"column: {pe.column}") prints:: - Expected integer (at char 0), (line:1, col:1) - column: 1 + Expected integer (at char 0), (line:1, col:1) column: 1 """ diff --git a/pyparsing/helpers.py b/pyparsing/helpers.py index 9a12f8dd..bd03a5da 100644 --- a/pyparsing/helpers.py +++ b/pyparsing/helpers.py @@ -536,7 +536,7 @@ def nested_expr( ) else: ret <<= Group(Suppress(opener) + ZeroOrMore(ret | content) + Suppress(closer)) - ret.set_name("nested %s%s expression" % (opener, closer)) + ret.set_name(f"nested {opener}{closer} expression") return ret @@ -582,7 +582,7 @@ def _makeTags(tagStr, xml, suppress_LT=Suppress("<"), suppress_GT=Suppress(">")) ) closeTag = Combine(Literal("", adjacent=False) - openTag.set_name("<%s>" % resname) + openTag.set_name(f"<{resname}>") # add start results name in parse action now that ungrouped names are not reported at two levels openTag.add_parse_action( lambda t: t.__setitem__( @@ -591,7 +591,7 @@ def _makeTags(tagStr, xml, suppress_LT=Suppress("<"), suppress_GT=Suppress(">")) ) closeTag = closeTag( "end" + "".join(resname.replace(":", " ").title().split()) - ).set_name("" % resname) + ).set_name(f"") openTag.tag = resname closeTag.tag = resname openTag.tag_body = SkipTo(closeTag()) diff --git a/pyparsing/results.py b/pyparsing/results.py index 31b33102..cd935bdf 100644 --- a/pyparsing/results.py +++ b/pyparsing/results.py @@ -688,34 +688,22 @@ def dump(self, indent="", full=True, include_list=True, _depth=0) -> str: return "".join(out) v = self + incr = " " + nl = "\n" for i, vv in enumerate(v): if isinstance(vv, ParseResults): - out.append( - "\n{}{}[{}]:\n{}{}{}".format( - indent, - (" " * (_depth)), - i, - indent, - (" " * (_depth + 1)), - vv.dump( + vv_dump = vv.dump( indent=indent, full=full, include_list=include_list, _depth=_depth + 1, - ), + ) + out.append( + f"{nl}{indent}{incr * _depth}[{i}]:{nl}{indent}{incr * (_depth + 1)}{vv_dump}" ) - ) else: out.append( - "\n%s%s[%d]:\n%s%s%s" - % ( - indent, - (" " * (_depth)), - i, - indent, - (" " * (_depth + 1)), - str(vv), - ) + f"{nl}{indent}{incr * _depth}[{i}]:{nl}{indent}{incr * (_depth + 1)}{vv}" ) return "".join(out) diff --git a/tests/test_unit.py b/tests/test_unit.py index 349fc486..670c26a3 100644 --- a/tests/test_unit.py +++ b/tests/test_unit.py @@ -561,7 +561,7 @@ def test(fnam, num_expected_toks, resCheckList): self.assertEqual( num_expected_toks, len(flatten(iniData.asList())), - "file %s not parsed correctly" % fnam, + f"file {fnam} not parsed correctly", ) for chkkey, chkexpect in resCheckList: var = iniData @@ -831,8 +831,7 @@ def testParseCommaSeparatedValues(self): print("$$$", results[0]) self.assertTrue( len(results) > t[0] and results[t[0]] == t[1], - "failed on %s, item %d s/b '%s', got '%s'" - % (line, t[0], t[1], str(results.asList())), + f"failed on {line}, item {t[0]:d} s/b '{t[1]}', got '{results.asList()}'", ) def testParseEBNF(self): @@ -901,7 +900,7 @@ def test(strng, numToks, expectedErrloc=0): self.assertEqual( numToks, len(tokens), - f"error matching IDL string, {strng} -> {str(tokens)}", + f"error matching IDL string, {strng} -> {tokens}", ) except ParseException as err: print(err.line) @@ -910,7 +909,7 @@ def test(strng, numToks, expectedErrloc=0): self.assertEqual( 0, numToks, - f"unexpected ParseException while parsing {strng}, {str(err)}", + f"unexpected ParseException while parsing {strng}, {err}", ) self.assertEqual( expectedErrloc, @@ -1143,7 +1142,7 @@ def testQuotedStrings(self): self.assertTrue( len(sglStrings) == 1 and (sglStrings[0][1] == 17 and sglStrings[0][2] == 66), - "single quoted string escaped quote failure (%s)" % str(sglStrings[0]), + f"single quoted string escaped quote failure ({sglStrings[0]})", ) with self.subTest(): @@ -1155,7 +1154,7 @@ def testQuotedStrings(self): self.assertTrue( len(dblStrings) == 1 and (dblStrings[0][1] == 83 and dblStrings[0][2] == 132), - "double quoted string escaped quote failure (%s)" % str(dblStrings[0]), + f"double quoted string escaped quote failure ({dblStrings[0]})", ) with self.subTest(): @@ -1172,8 +1171,7 @@ def testQuotedStrings(self): and allStrings[1][1] == 83 and allStrings[1][2] == 132 ), - "quoted string escaped quote failure (%s)" - % ([str(s[0]) for s in allStrings]), + f"quoted string escaped quote failure ({[str(s[0]) for s in allStrings]})", ) dblQuoteTest = r""" @@ -1189,7 +1187,7 @@ def testQuotedStrings(self): self.assertTrue( len(sglStrings) == 1 and (sglStrings[0][1] == 17 and sglStrings[0][2] == 66), - "single quoted string escaped quote failure (%s)" % str(sglStrings[0]), + f"single quoted string escaped quote failure ({sglStrings[0]})", ) with self.subTest(): @@ -1201,7 +1199,7 @@ def testQuotedStrings(self): self.assertTrue( len(dblStrings) == 1 and (dblStrings[0][1] == 83 and dblStrings[0][2] == 132), - "double quoted string escaped quote failure (%s)" % str(dblStrings[0]), + f"double quoted string escaped quote failure ({dblStrings[0]})", ) with self.subTest(): @@ -1217,8 +1215,7 @@ def testQuotedStrings(self): and allStrings[1][1] == 83 and allStrings[1][2] == 132 ), - "quoted string escaped quote failure (%s)" - % ([str(s[0]) for s in allStrings]), + f"quoted string escaped quote failure ({[str(s[0]) for s in allStrings]})", ) print( @@ -1471,7 +1468,7 @@ def testParseExpressionResults(self): self.assertEqual( ln, len(results[key]), - "expected %d elements in %s, found %s" % (ln, key, str(results[key])), + f"expected {ln:d} elements in {key}, found {results[key]}", ) def testParseKeyword(self): @@ -1486,10 +1483,10 @@ def test(s, litShouldPass, kwShouldPass): except Exception: print("failed") if litShouldPass: - self.fail("Literal failed to match %s, should have" % s) + self.fail(f"Literal failed to match {s}, should have") else: if not litShouldPass: - self.fail("Literal matched %s, should not have" % s) + self.fail(f"Literal matched {s}, should not have") print("Match Keyword", end=" ") try: @@ -1497,10 +1494,10 @@ def test(s, litShouldPass, kwShouldPass): except Exception: print("failed") if kwShouldPass: - self.fail("Keyword failed to match %s, should have" % s) + self.fail(f"Keyword failed to match {s}, should have") else: if not kwShouldPass: - self.fail("Keyword matched %s, should not have" % s) + self.fail(f"Keyword matched {s}, should not have") test("ifOnlyIfOnly", True, False) test("if(OnlyIfOnly)", True, True) @@ -1555,7 +1552,7 @@ def testParseExpressionResultsAccumulate(self): self.assertParseResultsEquals( queryRes.pred, expected_list=[["y", ">", "28"], ["x", "<", "12"], ["x", ">", "3"]], - msg="Incorrect list for attribute pred, %s" % str(queryRes.pred.asList()), + msg=f"Incorrect list for attribute pred, {queryRes.pred.asList()}", ) def testReStringRange(self): @@ -2021,7 +2018,7 @@ def testRepeater(self): self.assertEqual( expected, found, - f"Failed repeater for test: {tst}, matching {str(seq)}", + f"Failed repeater for test: {tst}, matching {seq}", ) print() @@ -2041,7 +2038,7 @@ def testRepeater(self): self.assertEqual( expected, found, - f"Failed repeater for test: {tst}, matching {str(seq)}", + f"Failed repeater for test: {tst}, matching {seq}", ) print() @@ -2073,7 +2070,7 @@ def testRepeater(self): self.assertEqual( expected, found, - f"Failed repeater for test: {tst}, matching {str(seq)}", + f"Failed repeater for test: {tst}, matching {seq}", ) print() @@ -2093,7 +2090,7 @@ def testRepeater(self): self.assertEqual( expected, found, - f"Failed repeater for test: {tst}, matching {str(seq)}", + f"Failed repeater for test: {tst}, matching {seq}", ) def testRepeater2(self): @@ -2308,8 +2305,8 @@ def __init__(self, t): self.args = t[0][0::2] def __str__(self): - sep = " %s " % self.reprsymbol - return "(" + sep.join(map(str, self.args)) + ")" + sep = f" {self.reprsymbol} " + return f"({sep.join(map(str, self.args))})" class BoolAnd(BoolOperand): reprsymbol = "&" @@ -2424,7 +2421,7 @@ def evaluate_int(t): for t in test: count = 0 print( - "%r => %s (count=%d)" % (t, expr.parseString(t, parseAll=True), count) + f"{t!r} => {expr.parseString(t, parseAll=True)} (count={count:d})" ) self.assertEqual(1, count, "count evaluated too many times!") @@ -3962,8 +3959,7 @@ def testMatch(expression, instring, shouldPass, expectedString=None): return True except pp.ParseException: print( - "%s incorrectly failed to match %s" - % (repr(expression), repr(instring)) + f"{expression!r} incorrectly failed to match {instring!r}" ) else: try: @@ -4796,7 +4792,7 @@ def testSingleArgException(self): def testOriginalTextFor(self): def rfn(t): - return "%s:%d" % (t.src, len("".join(t))) + return f"{t.src}:{len(''.join(t))}" makeHTMLStartTag = lambda tag: pp.originalTextFor( pp.makeHTMLTags(tag)[0], asString=False @@ -5536,11 +5532,11 @@ def testGreedyQuotedStrings(self): strs = pp.delimitedList(expr).searchString(src) print(strs) self.assertTrue( - bool(strs), "no matches found for test expression '%s'" % expr + bool(strs), f"no matches found for test expression '{expr}'" ) for lst in strs: self.assertEqual( - 2, len(lst), "invalid match found for test expression '%s'" % expr + 2, len(lst), f"invalid match found for test expression '{expr}'" ) src = """'ms1',1,0,'2009-12-22','2009-12-22 10:41:22') ON DUPLICATE KEY UPDATE sent_count = sent_count + 1, mtime = '2009-12-22 10:41:22';""" @@ -6059,7 +6055,7 @@ def validate(token): self.assertEqual( ["de"], result.asList(), - "failed to select longest match, chose %s" % result, + f"failed to select longest match, chose {result}", ) except ParseException: failed = True @@ -7276,7 +7272,7 @@ def testCloseMatch(self): ) print( r[0], - "exc: %s" % r[1] + f"exc: {r[1]}" if exp is None and isinstance(r[1], Exception) else ("no match", "match")[r[1].mismatches == exp], ) @@ -7305,7 +7301,7 @@ def testCloseMatchCaseless(self): ) print( r[0], - "exc: %s" % r[1] + f"exc: {r[1]}" if exp is None and isinstance(r[1], Exception) else ("no match", "match")[r[1].mismatches == exp], ) @@ -7953,7 +7949,7 @@ def testIndentedBlockTest2(self): stmt <<= pattern def key_parse_action(toks): - print("Parsing '%s'..." % toks[0]) + print(f"Parsing '{toks[0]}'...") key.setParseAction(key_parse_action) header = pp.Suppress("[") + pp.Literal("test") + pp.Suppress("]") @@ -9559,7 +9555,7 @@ def testOptionalWithResultsNameAndNoMatch(self): testGrammar.parseString("AC", parseAll=True) except pp.ParseException as pe: print(pe.pstr, "->", pe) - self.fail("error in Optional matching of string %s" % pe.pstr) + self.fail(f"error in Optional matching of string {pe.pstr}") def testReturnOfFurthestException(self): # test return of furthest exception diff --git a/update_pyparsing_timestamp.py b/update_pyparsing_timestamp.py index fcf95ba7..7f46fa94 100644 --- a/update_pyparsing_timestamp.py +++ b/update_pyparsing_timestamp.py @@ -3,7 +3,7 @@ from pyparsing import quoted_string nw = datetime.utcnow() -now_string = '"%s"' % (nw.strftime("%d %b %Y %X")[:-3] + " UTC") +now_string = f'"{nw.strftime("%d %b %Y %X")[:-3]} UTC"' print(now_string) quoted_time = quoted_string()