diff --git a/pyparsing/exceptions.py b/pyparsing/exceptions.py index 1aaea56f..e259c908 100644 --- a/pyparsing/exceptions.py +++ b/pyparsing/exceptions.py @@ -85,7 +85,7 @@ def explain_exception(exc, depth=16): ret = [] if isinstance(exc, ParseBaseException): ret.append(exc.line) - ret.append(" " * (exc.column - 1) + "^") + ret.append(f"{' ' * (exc.column - 1)}^") ret.append(f"{type(exc).__name__}: {exc}") if depth <= 0: diff --git a/tests/test_simple_unit.py b/tests/test_simple_unit.py index 85b750ef..317e2689 100644 --- a/tests/test_simple_unit.py +++ b/tests/test_simple_unit.py @@ -16,18 +16,24 @@ TestParseResultsAsserts = ppt.TestParseResultsAsserts # Test spec data class for specifying simple pyparsing test cases -PpTestSpec = namedtuple( - "PpTestSpec", - "desc expr text parse_fn " "expected_list expected_dict expected_fail_locn", +PyparsingTest = namedtuple( + "PyparsingTest", + "desc expr text parse_fn expected_list expected_dict expected_fail_locn", ) -PpTestSpec.__new__.__defaults__ = ("", pp.Empty(), "", "parseString", None, None, None) +# fmt: off +PyparsingTest.__new__.__defaults__ = ( + "", pp.Empty(), "", "parse_string", None, None, None, +) +# fmt: on + +NL = "\n" class PyparsingExpressionTestCase(ppt.TestParseResultsAsserts, unittest.TestCase): """ Base pyparsing testing class to parse various pyparsing expressions against given text strings. Subclasses must define a class attribute 'tests' which - is a list of PpTestSpec instances. + is a list of PyparsingTest instances. """ tests = [] @@ -47,37 +53,37 @@ def runTest(self): with self.subTest(test_spec=test_spec): test_spec.expr.streamline() print( - f"\n{test_spec.desc} - {type(test_spec.expr).__name__}({test_spec.expr})" + f"{NL}{test_spec.desc} - {type(test_spec.expr).__name__}({test_spec.expr})" ) - parsefn = getattr(test_spec.expr, test_spec.parse_fn) + parse_function = getattr(test_spec.expr, test_spec.parse_fn) if test_spec.expected_fail_locn is None: # expect success - result = parsefn(test_spec.text) - if test_spec.parse_fn == "parseString": - print(result.dump()) + subtest_result = parse_function(test_spec.text) + if test_spec.parse_fn == "parse_string": + print(subtest_result.dump()) # compare results against given list and/or dict self.assertParseResultsEquals( - result, + subtest_result, expected_list=test_spec.expected_list, expected_dict=test_spec.expected_dict, ) elif test_spec.parse_fn == "transformString": - print(result) + print(subtest_result) # compare results against given list and/or dict if test_spec.expected_list is not None: - self.assertEqual([result], test_spec.expected_list) + self.assertEqual([subtest_result], test_spec.expected_list) elif test_spec.parse_fn == "searchString": - print(result) + print(subtest_result) # compare results against given list and/or dict if test_spec.expected_list is not None: - self.assertEqual([result], test_spec.expected_list) + self.assertEqual([subtest_result], test_spec.expected_list) else: # expect fail with self.assertRaisesParseException(): try: - parsefn(test_spec.text) - except Exception as exc: + parse_function(test_spec.text) + except pp.ParseBaseException as exc: print(pp.ParseException.explain(exc)) self.assertEqual(exc.loc, test_spec.expected_fail_locn) raise @@ -88,37 +94,37 @@ def runTest(self): class TestLiteral(PyparsingExpressionTestCase): tests = [ - PpTestSpec( + PyparsingTest( desc="Simple match", expr=pp.Literal("xyz"), text="xyz", expected_list=["xyz"], ), - PpTestSpec( + PyparsingTest( desc="Simple match after skipping whitespace", expr=pp.Literal("xyz"), text=" xyz", expected_list=["xyz"], ), - PpTestSpec( + PyparsingTest( desc="Simple fail - parse an empty string", expr=pp.Literal("xyz"), text="", expected_fail_locn=0, ), - PpTestSpec( + PyparsingTest( desc="Simple fail - parse a mismatching string", expr=pp.Literal("xyz"), text="xyu", expected_fail_locn=0, ), - PpTestSpec( + PyparsingTest( desc="Simple fail - parse a partially matching string", expr=pp.Literal("xyz"), text="xy", expected_fail_locn=0, ), - PpTestSpec( + PyparsingTest( desc="Fail - parse a partially matching string by matching individual letters", expr=pp.Literal("x") + pp.Literal("y") + pp.Literal("z"), text="xy", @@ -129,7 +135,7 @@ class TestLiteral(PyparsingExpressionTestCase): class TestCaselessLiteral(PyparsingExpressionTestCase): tests = [ - PpTestSpec( + PyparsingTest( desc="Match colors, converting to consistent case", expr=( pp.CaselessLiteral("RED") @@ -144,19 +150,19 @@ class TestCaselessLiteral(PyparsingExpressionTestCase): class TestWord(PyparsingExpressionTestCase): tests = [ - PpTestSpec( + PyparsingTest( desc="Simple Word match", expr=pp.Word("xy"), text="xxyxxyy", expected_list=["xxyxxyy"], ), - PpTestSpec( + PyparsingTest( desc="Simple Word match of two separate Words", expr=pp.Word("x") + pp.Word("y"), text="xxxxxyy", expected_list=["xxxxx", "yy"], ), - PpTestSpec( + PyparsingTest( desc="Simple Word match of two separate Words - implicitly skips whitespace", expr=pp.Word("x") + pp.Word("y"), text="xxxxx yy", @@ -167,26 +173,15 @@ class TestWord(PyparsingExpressionTestCase): class TestCombine(PyparsingExpressionTestCase): tests = [ - PpTestSpec( + PyparsingTest( desc="Parsing real numbers - fail, parsed numbers are in pieces", expr=(pp.Word(pp.nums) + "." + pp.Word(pp.nums))[...], text="1.2 2.3 3.1416 98.6", - expected_list=[ - "1", - ".", - "2", - "2", - ".", - "3", - "3", - ".", - "1416", - "98", - ".", - "6", - ], + # fmt: off + expected_list=["1", ".", "2", "2", ".", "3", "3", ".", "1416", "98", ".", "6"], + # fmt: on ), - PpTestSpec( + PyparsingTest( desc="Parsing real numbers - better, use Combine to combine multiple tokens into one", expr=pp.Combine(pp.Word(pp.nums) + "." + pp.Word(pp.nums))[...], text="1.2 2.3 3.1416 98.6", @@ -197,53 +192,29 @@ class TestCombine(PyparsingExpressionTestCase): class TestRepetition(PyparsingExpressionTestCase): tests = [ - PpTestSpec( + PyparsingTest( desc="Match several words", expr=(pp.Word("x") | pp.Word("y"))[...], text="xxyxxyyxxyxyxxxy", expected_list=["xx", "y", "xx", "yy", "xx", "y", "x", "y", "xxx", "y"], ), - PpTestSpec( + PyparsingTest( desc="Match several words, skipping whitespace", expr=(pp.Word("x") | pp.Word("y"))[...], text="x x y xxy yxx y xyx xxy", - expected_list=[ - "x", - "x", - "y", - "xx", - "y", - "y", - "xx", - "y", - "x", - "y", - "x", - "xx", - "y", - ], + # fmt: off + expected_list=["x", "x", "y", "xx", "y", "y", "xx", "y", "x", "y", "x", "xx", "y"], + # fmt: on ), - PpTestSpec( + PyparsingTest( desc="Match several words, skipping whitespace (old style)", expr=pp.OneOrMore(pp.Word("x") | pp.Word("y")), text="x x y xxy yxx y xyx xxy", - expected_list=[ - "x", - "x", - "y", - "xx", - "y", - "y", - "xx", - "y", - "x", - "y", - "x", - "xx", - "y", - ], + # fmt: off + expected_list=["x", "x", "y", "xx", "y", "y", "xx", "y", "x", "y", "x", "xx", "y"], + # fmt: on ), - PpTestSpec( + PyparsingTest( desc="Match words and numbers - show use of results names to collect types of tokens", expr=(pp.Word(pp.alphas)("alpha*") | pp.pyparsing_common.integer("int*"))[ ... @@ -255,41 +226,41 @@ class TestRepetition(PyparsingExpressionTestCase): "int": [23084, 8234, 934], }, ), - PpTestSpec( - desc="Using delimited_list (comma is the default delimiter)", - expr=pp.delimited_list(pp.Word(pp.alphas)), + PyparsingTest( + desc="Using DelimitedList (comma is the default delimiter)", + expr=pp.DelimitedList(pp.Word(pp.alphas)), text="xxyx,xy,y,xxyx,yxx, xy", expected_list=["xxyx", "xy", "y", "xxyx", "yxx", "xy"], ), - PpTestSpec( - desc="Using delimited_list (comma is the default delimiter) with trailing delimiter", - expr=pp.delimited_list(pp.Word(pp.alphas), allow_trailing_delim=True), + PyparsingTest( + desc="Using DelimitedList (comma is the default delimiter) with trailing delimiter", + expr=pp.DelimitedList(pp.Word(pp.alphas), allow_trailing_delim=True), text="xxyx,xy,y,xxyx,yxx, xy,", expected_list=["xxyx", "xy", "y", "xxyx", "yxx", "xy"], ), - PpTestSpec( - desc="Using delimited_list (comma is the default delimiter) with minimum size", - expr=pp.delimited_list(pp.Word(pp.alphas), min=3), + PyparsingTest( + desc="Using DelimitedList (comma is the default delimiter) with minimum size", + expr=pp.DelimitedList(pp.Word(pp.alphas), min=3), text="xxyx,xy", expected_fail_locn=7, ), - PpTestSpec( - desc="Using delimited_list (comma is the default delimiter) with maximum size", - expr=pp.delimited_list(pp.Word(pp.alphas), max=3), + PyparsingTest( + desc="Using DelimitedList (comma is the default delimiter) with maximum size", + expr=pp.DelimitedList(pp.Word(pp.alphas), max=3), text="xxyx,xy,y,xxyx,yxx, xy,", expected_list=["xxyx", "xy", "y"], ), - PpTestSpec( - desc="Using delimited_list, with ':' delimiter", - expr=pp.delimited_list( + PyparsingTest( + desc="Using DelimitedList, with ':' delimiter", + expr=pp.DelimitedList( pp.Word(pp.hexnums, exact=2), delim=":", combine=True ), text="0A:4B:73:21:FE:76", expected_list=["0A:4B:73:21:FE:76"], ), - PpTestSpec( - desc="Using delimited_list, with ':' delimiter", - expr=pp.delimited_list( + PyparsingTest( + desc="Using DelimitedList, with ':' delimiter", + expr=pp.DelimitedList( pp.Word(pp.hexnums, exact=2), delim=":", combine=True, @@ -303,21 +274,21 @@ class TestRepetition(PyparsingExpressionTestCase): class TestResultsName(PyparsingExpressionTestCase): tests = [ - PpTestSpec( + PyparsingTest( desc="Match with results name", expr=pp.Literal("xyz").set_results_name("value"), text="xyz", expected_dict={"value": "xyz"}, expected_list=["xyz"], ), - PpTestSpec( + PyparsingTest( desc="Match with results name - using naming short-cut", expr=pp.Literal("xyz")("value"), text="xyz", expected_dict={"value": "xyz"}, expected_list=["xyz"], ), - PpTestSpec( + PyparsingTest( desc="Define multiple results names", expr=pp.Word(pp.alphas, pp.alphanums)("key") + "=" @@ -332,7 +303,7 @@ class TestResultsName(PyparsingExpressionTestCase): class TestGroups(PyparsingExpressionTestCase): EQ = pp.Suppress("=") tests = [ - PpTestSpec( + PyparsingTest( desc="Define multiple results names in groups", expr=pp.Group( pp.Word(pp.alphas)("key") + EQ + pp.pyparsing_common.number("value") @@ -340,8 +311,11 @@ class TestGroups(PyparsingExpressionTestCase): text="range=5280 long=-138.52 lat=46.91", expected_list=[["range", 5280], ["long", -138.52], ["lat", 46.91]], ), - PpTestSpec( - desc="Define multiple results names in groups - use Dict to define results names using parsed keys", + PyparsingTest( + desc=( + "Define multiple results names in groups" + " - use Dict to define results names using parsed keys" + ), expr=pp.Dict( pp.Group(pp.Word(pp.alphas) + EQ + pp.pyparsing_common.number)[...] ), @@ -349,7 +323,7 @@ class TestGroups(PyparsingExpressionTestCase): expected_list=[["range", 5280], ["long", -138.52], ["lat", 46.91]], expected_dict={"lat": 46.91, "long": -138.52, "range": 5280}, ), - PpTestSpec( + PyparsingTest( desc="Define multiple value types", expr=pp.Dict( pp.Group( @@ -381,48 +355,46 @@ class TestGroups(PyparsingExpressionTestCase): class TestParseAction(PyparsingExpressionTestCase): tests = [ - PpTestSpec( + PyparsingTest( desc="Parsing real numbers - use parse action to convert to float at parse time", expr=pp.Combine(pp.Word(pp.nums) + "." + pp.Word(pp.nums)).add_parse_action( lambda t: float(t[0]) )[...], text="1.2 2.3 3.1416 98.6", - expected_list=[ - 1.2, - 2.3, - 3.1416, - 98.6, - ], # note, these are now floats, not strs + # note, these are now floats, not strs + expected_list=[1.2, 2.3, 3.1416, 98.6], ), - PpTestSpec( + PyparsingTest( desc="Match with numeric string converted to int", expr=pp.Word("0123456789").addParseAction(lambda t: int(t[0])), text="12345", expected_list=[12345], # note - result is type int, not str ), - PpTestSpec( + PyparsingTest( desc="Use two parse actions to convert numeric string, then convert to datetime", expr=pp.Word(pp.nums).add_parse_action( - lambda t: int(t[0]), lambda t: datetime.fromtimestamp(t[0], timezone.utc) + lambda t: int(t[0]), + lambda t: datetime.fromtimestamp(t[0], timezone.utc), ), text="1537415628", expected_list=[datetime(2018, 9, 20, 3, 53, 48, tzinfo=timezone.utc)], ), - PpTestSpec( + PyparsingTest( desc="Use tokenMap for parse actions that operate on a single-length token", expr=pp.Word(pp.nums).add_parse_action( - pp.token_map(int), pp.token_map(lambda t: datetime.fromtimestamp(t, timezone.utc)) + pp.token_map(int), + pp.token_map(lambda t: datetime.fromtimestamp(t, timezone.utc)), ), text="1537415628", expected_list=[datetime(2018, 9, 20, 3, 53, 48, tzinfo=timezone.utc)], ), - PpTestSpec( + PyparsingTest( desc="Using a built-in function that takes a sequence of strs as a parse action", expr=pp.Word(pp.hexnums, exact=2)[...].add_parse_action(":".join), text="0A4B7321FE76", expected_list=["0A:4B:73:21:FE:76"], ), - PpTestSpec( + PyparsingTest( desc="Using a built-in function that takes a sequence of strs as a parse action", expr=pp.Word(pp.hexnums, exact=2)[...].add_parse_action(sorted), text="0A4B7321FE76", @@ -444,7 +416,7 @@ def compute_stats_parse_action(t): t["max"] = max(t) tests = [ - PpTestSpec( + PyparsingTest( desc="A parse action that adds new key-values", expr=pp.pyparsing_common.integer[...].addParseAction( compute_stats_parse_action @@ -458,29 +430,25 @@ def compute_stats_parse_action(t): class TestRegex(PyparsingExpressionTestCase): tests = [ - PpTestSpec( + PyparsingTest( desc="Parsing real numbers - using Regex instead of Combine", expr=pp.Regex(r"\d+\.\d+").add_parse_action(lambda t: float(t[0]))[...], text="1.2 2.3 3.1416 98.6", - expected_list=[ - 1.2, - 2.3, - 3.1416, - 98.6, - ], # note, these are now floats, not strs + # note, these are now floats, not strs + expected_list=[1.2, 2.3, 3.1416, 98.6], ), ] class TestParseCondition(PyparsingExpressionTestCase): tests = [ - PpTestSpec( + PyparsingTest( desc="Define a condition to only match numeric values that are multiples of 7", expr=pp.Word(pp.nums).addCondition(lambda t: int(t[0]) % 7 == 0)[...], text="14 35 77 12 28", expected_list=["14", "35", "77"], ), - PpTestSpec( + PyparsingTest( desc="Separate conversion to int and condition into separate parse action/conditions", expr=pp.Word(pp.nums) .add_parse_action(lambda t: int(t[0])) @@ -507,7 +475,7 @@ def markup_convert(t): return f"<{htmltag}>{t.body}" tests = [ - PpTestSpec( + PyparsingTest( desc="Use transformString to convert simple markup to HTML", expr=( pp.one_of(markup_convert_map)("markup_symbol") @@ -526,19 +494,19 @@ def markup_convert(t): class TestCommonHelperExpressions(PyparsingExpressionTestCase): tests = [ - PpTestSpec( + PyparsingTest( desc="A comma-delimited list of words", - expr=pp.delimited_list(pp.Word(pp.alphas)), + expr=pp.DelimitedList(pp.Word(pp.alphas)), text="this, that, blah,foo, bar", expected_list=["this", "that", "blah", "foo", "bar"], ), - PpTestSpec( + PyparsingTest( desc="A counted array of words", expr=pp.Group(pp.counted_array(pp.Word("ab")))[...], text="2 aaa bbb 0 3 abab bbaa abbab", expected_list=[["aaa", "bbb"], [], ["abab", "bbaa", "abbab"]], ), - PpTestSpec( + PyparsingTest( desc="skipping comments with ignore", expr=( pp.pyparsing_common.identifier("lhs") @@ -549,11 +517,14 @@ class TestCommonHelperExpressions(PyparsingExpressionTestCase): expected_list=["abc_100", "=", 3.1416], expected_dict={"lhs": "abc_100", "rhs": 3.1416}, ), - PpTestSpec( - desc="some pre-defined expressions in pyparsing_common, and building a dotted identifier with delimted_list", + PyparsingTest( + desc=( + "some pre-defined expressions in pyparsing_common, and" + " building a dotted identifier with DelimitedList" + ), expr=( pp.pyparsing_common.number("id_num") - + pp.delimitedList(pp.pyparsing_common.identifier, ".", combine=True)( + + pp.DelimitedList(pp.pyparsing_common.identifier, ".", combine=True)( "name" ) + pp.pyparsing_common.ipv4_address("ip_address") @@ -566,19 +537,19 @@ class TestCommonHelperExpressions(PyparsingExpressionTestCase): "ip_address": "192.168.10.199", }, ), - PpTestSpec( + PyparsingTest( desc="using one_of (shortcut for Literal('a') | Literal('b') | Literal('c'))", expr=pp.one_of("a b c")[...], text="a b a b b a c c a b b", expected_list=["a", "b", "a", "b", "b", "a", "c", "c", "a", "b", "b"], ), - PpTestSpec( + PyparsingTest( desc="parsing nested parentheses", expr=pp.nested_expr(), text="(a b (c) d (e f g ()))", expected_list=[["a", "b", ["c"], "d", ["e", "f", "g", []]]], ), - PpTestSpec( + PyparsingTest( desc="parsing nested braces", expr=( pp.Keyword("if") @@ -602,33 +573,37 @@ class TestCommonHelperExpressions(PyparsingExpressionTestCase): class TestWhitespaceMethods(PyparsingExpressionTestCase): tests = [ # These test the single-element versions - PpTestSpec( + PyparsingTest( desc="The word foo", expr=pp.Literal("foo").ignore_whitespace(), text=" foo ", expected_list=["foo"], ), - PpTestSpec( + PyparsingTest( desc="The word foo", expr=pp.Literal("foo").leave_whitespace(), text=" foo ", expected_fail_locn=0, ), - PpTestSpec( + PyparsingTest( desc="The word foo", expr=pp.Literal("foo").ignore_whitespace(), text="foo", expected_list=["foo"], ), - PpTestSpec( + PyparsingTest( desc="The word foo", expr=pp.Literal("foo").leave_whitespace(), text="foo", expected_list=["foo"], ), # These test the composite elements - PpTestSpec( - desc="If we recursively leave whitespace on the parent, this whitespace-dependent grammar will succeed, even if the children themselves skip whitespace", + PyparsingTest( + desc=( + "If we recursively leave whitespace on the parent, this" + " whitespace-dependent grammar will succeed, even if the" + " children themselves skip whitespace" + ), expr=pp.And( [ pp.Literal(" foo").ignore_whitespace(), @@ -639,8 +614,12 @@ class TestWhitespaceMethods(PyparsingExpressionTestCase): expected_list=[" foo", " bar"], ), # - PpTestSpec( - desc="If we recursively ignore whitespace in our parsing, this whitespace-dependent grammar will fail, even if the children themselves keep whitespace", + PyparsingTest( + desc=( + "If we recursively ignore whitespace in our parsing, this" + " whitespace-dependent grammar will fail, even if the children" + " themselves keep whitespace" + ), expr=pp.And( [ pp.Literal(" foo").leave_whitespace(), @@ -650,8 +629,11 @@ class TestWhitespaceMethods(PyparsingExpressionTestCase): text=" foo bar", expected_fail_locn=1, ), - PpTestSpec( - desc="If we leave whitespace on the parent, but it isn't recursive, this whitespace-dependent grammar will fail", + PyparsingTest( + desc=( + "If we leave whitespace on the parent, but it isn't recursive," + " this whitespace-dependent grammar will fail" + ), expr=pp.And( [ pp.Literal(" foo").ignore_whitespace(), @@ -662,8 +644,12 @@ class TestWhitespaceMethods(PyparsingExpressionTestCase): expected_fail_locn=5, ), # These test the Enhance classes - PpTestSpec( - desc="If we recursively leave whitespace on the parent, this whitespace-dependent grammar will succeed, even if the children themselves skip whitespace", + PyparsingTest( + desc=( + "If we recursively leave whitespace on the parent," + " this whitespace-dependent grammar will succeed, even" + " if the children themselves skip whitespace" + ), expr=pp.Optional(pp.Literal(" foo").ignore_whitespace()).leave_whitespace( recursive=True ), @@ -671,16 +657,23 @@ class TestWhitespaceMethods(PyparsingExpressionTestCase): expected_list=[" foo"], ), # - PpTestSpec( - desc="If we ignore whitespace on the parent, but it isn't recursive, parsing will fail because we skip to the first character 'f' before the internal expr can see it", + PyparsingTest( + desc=( + "If we ignore whitespace on the parent, but it isn't recursive," + " parsing will fail because we skip to the first character" + " 'f' before the internal expr can see it" + ), expr=pp.Optional(pp.Literal(" foo").leave_whitespace()).ignore_whitespace( recursive=True ), text=" foo", expected_list=[], ), - # PpTestSpec( - # desc="If we leave whitespace on the parent, this whitespace-dependent grammar will succeed, even if the children themselves skip whitespace", + # PyparsingTest( + # desc=( + # "If we leave whitespace on the parent, this whitespace-dependent" + # " grammar will succeed, even if the children themselves skip whitespace" + # ), # expr=pp.Optional(pp.Literal(" foo").ignoreWhitespace()).leaveWhitespace( # recursive=False # ), diff --git a/tests/test_unit.py b/tests/test_unit.py index 0c2625cd..71e7e45d 100644 --- a/tests/test_unit.py +++ b/tests/test_unit.py @@ -87,7 +87,7 @@ def current_method_name(level=2): def __(): - return current_method_name(3) + ": " + return f"{current_method_name(3)}: " class TestCase(unittest.TestCase): @@ -822,7 +822,7 @@ def testParseCommaSeparatedValues(self): ], ] for line, tests in zip(testData, testVals): - print("Parsing: %r ->" % line, end=" ") + print(f"Parsing: {line!r} ->", end=" ") results = ppc.comma_separated_list.parseString(line, parseAll=True) print(results) for t in tests: @@ -904,7 +904,7 @@ def test(strng, numToks, expectedErrloc=0): ) except ParseException as err: print(err.line) - print(" " * (err.column - 1) + "^") + print(f"{' ' * (err.column - 1)}^") print(err) self.assertEqual( 0, @@ -914,8 +914,7 @@ def test(strng, numToks, expectedErrloc=0): self.assertEqual( expectedErrloc, err.loc, - "expected ParseException at %d, found exception at %d" - % (expectedErrloc, err.loc), + f"expected ParseException at {expectedErrloc}, found exception at {err.loc}", ) test( @@ -1399,13 +1398,13 @@ def testCommentParser(self): ablsjdflj */ """ - foundLines = [ + found_lines = [ pp.lineno(s, testdata) for t, s, e in pp.cStyleComment.scanString(testdata) ] self.assertEqual( list(range(11))[2:], - foundLines, - "only found C comments on lines " + str(foundLines), + found_lines, + f"only found C comments on lines {found_lines}", ) testdata = """ @@ -1422,13 +1421,13 @@ def testCommentParser(self): ablsjdflj --> """ - foundLines = [ + found_lines = [ pp.lineno(s, testdata) for t, s, e in pp.htmlComment.scanString(testdata) ] self.assertEqual( list(range(11))[2:], - foundLines, - "only found HTML comments on lines " + str(foundLines), + found_lines, + f"only found HTML comments on lines {found_lines}", ) # test C++ single line comments that have line terminated with '\' (should continue comment to following line) @@ -2339,7 +2338,7 @@ def __init__(self, t): self.arg = t[0][1] def __str__(self): - return "~" + str(self.arg) + return f"~{self.arg}" def __bool__(self): if isinstance(self.arg, str): @@ -2408,8 +2407,8 @@ def evaluate_int(t): expr = pp.infixNotation( operand, [ - ("!", 1, pp.opAssoc.LEFT), - ("^", 2, pp.opAssoc.LEFT), + (factop, 1, pp.opAssoc.LEFT), + (expop, 2, pp.opAssoc.LEFT), (signop, 1, pp.opAssoc.RIGHT), (multop, 2, pp.opAssoc.LEFT), (plusop, 2, pp.opAssoc.LEFT), @@ -2420,9 +2419,7 @@ def evaluate_int(t): test = ["9"] for t in test: count = 0 - print( - f"{t!r} => {expr.parseString(t, parseAll=True)} (count={count:d})" - ) + print(f"{t!r} => {expr.parseString(t, parseAll=True)} (count={count})") self.assertEqual(1, count, "count evaluated too many times!") def testInfixNotationWithParseActions(self): @@ -2701,7 +2698,7 @@ def testParseResultsPickle(self): self.assertEqual( result.dump(), newresult.dump(), - "Error pickling ParseResults object (protocol=%d)" % protocol, + f"Error pickling ParseResults object (protocol={protocol})", ) def testParseResultsPickle2(self): @@ -3958,9 +3955,7 @@ def testMatch(expression, instring, shouldPass, expectedString=None): ) return True except pp.ParseException: - print( - f"{expression!r} incorrectly failed to match {instring!r}" - ) + print(f"{expression!r} incorrectly failed to match {instring!r}") else: try: result = expression.parseString(instring, parseAll=False) @@ -4548,10 +4543,7 @@ def testLineAndStringEnd(self): self.assertParseResultsEquals( res1, expected_list=expected, - msg="Failed lineEnd/stringEnd test (1): " - + repr(test) - + " -> " - + str(res1), + msg=f"Failed lineEnd/stringEnd test (1): {test!r} -> {res1}", ) res2 = bnf2.searchString(test)[0] @@ -4559,10 +4551,7 @@ def testLineAndStringEnd(self): self.assertParseResultsEquals( res2, expected_list=expected[-1:], - msg="Failed lineEnd/stringEnd test (2): " - + repr(test) - + " -> " - + str(res2), + msg=f"Failed lineEnd/stringEnd test (2): {test!r} -> {res2}", ) res3 = bnf3.parseString(test, parseAll=True) @@ -4573,10 +4562,7 @@ def testLineAndStringEnd(self): self.assertEqual( rest, test[len(first) + 1 :], - "Failed lineEnd/stringEnd test (3): " - + repr(test) - + " -> " - + str(res3.asList()), + msg=f"Failed lineEnd/stringEnd test (3): {test!r} -> {res3.as_list()}", ) print() @@ -4591,15 +4577,16 @@ def testLineAndStringEnd(self): (r"aaa\n", None), ] for i, (src, expected) in enumerate(tests): - print(i, repr(src).replace("\\\\", "\\"), end=" ") - if expected is None: - with self.assertRaisesParseException(): - k.parseString(src, parseAll=True) - else: - res = k.parseString(src, parseAll=True) - self.assertParseResultsEquals( - res, expected, msg="Failed on parseAll=True test %d" % i - ) + with self.subTest("", src=src, expected=expected): + print(i, repr(src).replace("\\\\", "\\"), end=" ") + if expected is None: + with self.assertRaisesParseException(): + k.parseString(src, parseAll=True) + else: + res = k.parseString(src, parseAll=True) + self.assertParseResultsEquals( + res, expected, msg=f"Failed on parseAll=True test {i}" + ) def testVariableParseActionArgs(self): pa3 = lambda s, l, t: t @@ -5362,7 +5349,7 @@ def testWordMaxGreaterThanZeroAndAsKeyword1(self): setup.bool_operand[...], setup.test_string, setup.test_string.split(), - msg=__() + "Failed to parse Word(max=1, asKeyword=True)", + msg=f"{__()}Failed to parse Word(max=1, asKeyword=True)", verbose=True, ) @@ -5371,7 +5358,7 @@ def testWordMaxGreaterThanZeroAndAsKeyword2(self): setup = self.setup_testWordMaxGreaterThanZeroAndAsKeyword() with self.assertRaisesParseException( - msg=__() + "failed to detect Word with max > 0 and asKeyword=True" + msg=f"{__()}Failed to detect Word with max > 0 and asKeyword=True" ): setup.bool_operand.parseString("abc", parseAll=True) @@ -5659,11 +5646,8 @@ def testRequiredEach(self): self.assertEqual( set(res1), set(res2), - "Failed RequiredEachTest, expected " - + str(res1.asList()) - + " and " - + str(res2.asList()) - + "to contain same words in any order", + f"Failed RequiredEachTest, expected {res1.as_list()}" + f" and {res2.as_list} to contain the same words in any order", ) def testOptionalEachTest1(self): @@ -5686,10 +5670,7 @@ def testOptionalEachTest1(self): self.assertEqual( p1res.asList(), p2res.asList(), - "Each failed to match with nested Optionals, " - + str(p1res.asList()) - + " should match " - + str(p2res.asList()), + f"Each failed to match with nested Optionals, {p1res.as_list()} should match {p2res.as_list()}", ) def testOptionalEachTest2(self): @@ -6102,7 +6083,7 @@ def testUnicodeExpression(self): self.assertEqual( r"""Expected {'a' | 'ᄑ'}""", pe.msg, - "Invalid error message raised, got %r" % pe.msg, + f"Invalid error message raised, got {pe.msg!r}", ) def testSetName(self): @@ -6269,7 +6250,7 @@ def testOneOrMoreStop(self): for ender in (END, "END", pp.CaselessKeyword("END")): expr = BEGIN + pp.OneOrMore(body_word, stopOn=ender) + END self.assertEqual( - expr, test, "Did not successfully stop on ending expression %r" % ender + expr, test, f"Did not successfully stop on ending expression {ender!r}" ) expr = BEGIN + body_word[1, ...].stopOn(ender) + END @@ -6277,7 +6258,7 @@ def testOneOrMoreStop(self): expr, test, test.split(), - "Did not successfully stop on ending expression %r" % ender, + f"Did not successfully stop on ending expression {ender!r}", ) expr = BEGIN + body_word[1, ...:ender] + END @@ -6285,7 +6266,7 @@ def testOneOrMoreStop(self): expr, test, test.split(), - "Did not successfully stop on ending expression %r" % ender, + f"Did not successfully stop on ending expression {ender!r}", ) expr = BEGIN + body_word[(1, ...):ender] + END @@ -6293,7 +6274,7 @@ def testOneOrMoreStop(self): expr, test, test.split(), - "Did not successfully stop on ending expression %r" % ender, + f"Did not successfully stop on ending expression {ender!r}", ) number = pp.Word(pp.nums + ",.()").setName("number with optional commas") @@ -6304,7 +6285,7 @@ def testOneOrMoreStop(self): parser, " XXX Y/123 1,234.567890", ["XXX Y/123", "1,234.567890"], - "Did not successfully stop on ending expression %r" % number, + f"Did not successfully stop on ending expression {number!r}", verbose=True, ) @@ -6318,7 +6299,7 @@ def testZeroOrMoreStop(self): expr, test, test.split(), - "Did not successfully stop on ending expression %r" % ender, + f"Did not successfully stop on ending expression {ender!r}", ) expr = BEGIN + body_word[...].stopOn(ender) + END @@ -6326,7 +6307,7 @@ def testZeroOrMoreStop(self): expr, test, test.split(), - "Did not successfully stop on ending expression %r" % ender, + f"Did not successfully stop on ending expression {ender!r}", ) expr = BEGIN + body_word[...:ender] + END @@ -6334,7 +6315,7 @@ def testZeroOrMoreStop(self): expr, test, test.split(), - "Did not successfully stop on ending expression %r" % ender, + f"Did not successfully stop on ending expression {ender!r}", ) expr = BEGIN + body_word[:ender] + END @@ -6342,7 +6323,7 @@ def testZeroOrMoreStop(self): expr, test, test.split(), - "Did not successfully stop on ending expression %r" % ender, + f"Did not successfully stop on ending expression {ender!r}", ) def testNestedAsDict(self): @@ -7272,9 +7253,11 @@ def testCloseMatch(self): ) print( r[0], - f"exc: {r[1]}" - if exp is None and isinstance(r[1], Exception) - else ("no match", "match")[r[1].mismatches == exp], + ( + f"exc: {r[1]}" + if exp is None and isinstance(r[1], Exception) + else ("no match", "match")[r[1].mismatches == exp] + ), ) def testCloseMatchCaseless(self): @@ -7301,9 +7284,11 @@ def testCloseMatchCaseless(self): ) print( r[0], - f"exc: {r[1]}" - if exp is None and isinstance(r[1], Exception) - else ("no match", "match")[r[1].mismatches == exp], + ( + f"exc: {r[1]}" + if exp is None and isinstance(r[1], Exception) + else ("no match", "match")[r[1].mismatches == exp] + ), ) def testDefaultKeywordChars(self): @@ -7526,13 +7511,13 @@ def add_total(tokens): ) def testParseActionWithDelimitedList(self): - class AnnotatedToken(object): + class AnnotatedToken: def __init__(self, kind, elements): self.kind = kind self.elements = elements def __str__(self): - return "AnnotatedToken(%r, %r)" % (self.kind, self.elements) + return f"AnnotatedToken({self.kind!r}, {self.elements!r})" def __eq__(self, other): return ( @@ -10050,7 +10035,11 @@ def testForwardExceptionText2(self): for s, expr, expected in ( (v, pp.nested_expr(), "Expected ')'"), (v, pp.Combine(pp.nested_expr(), adjacent=False), "Expected ')'"), - (v, pp.QuotedString("(", endQuoteChar=")"), "Expected quoted string, starting with ( ending with ), found '('"), + ( + v, + pp.QuotedString("(", endQuoteChar=")"), + "Expected quoted string, starting with ( ending with ), found '('", + ), (w, pp.nested_expr(content=pp.sgl_quoted_string), "Expected ')'"), ("", pp.nested_expr(), ""), ("", pp.Word("A"), ""), @@ -10068,7 +10057,9 @@ def testForwardExceptionText2(self): expr[1, ...].parse_string(s, parse_all=True) print(ctx.exception) - with self.subTest("parse DelimitedList(expr)", expr=expr, s=s, expected=expected): + with self.subTest( + "parse DelimitedList(expr)", expr=expr, s=s, expected=expected + ): with self.assertRaisesParseException(expected_msg=expected) as ctx: pp.DelimitedList(expr).parse_string(s, parse_all=True) print(ctx.exception) @@ -10180,49 +10171,39 @@ def testForwardsDoProperStreamlining(self): self.assertEqual(len(w3.exprs), 3) test_exception_messages_tests = ( - ( - pp.Word(pp.alphas), - "123", - "Expected W:(A-Za-z), found '123'" - ), - ( - pp.Word(pp.alphas).set_name("word"), - "123", - "Expected word, found '123'" - ), + (pp.Word(pp.alphas), "123", "Expected W:(A-Za-z), found '123'"), + (pp.Word(pp.alphas).set_name("word"), "123", "Expected word, found '123'"), ( pp.Group(pp.Word(pp.alphas).set_name("word")), "123", - "Expected word, found '123'" + "Expected word, found '123'", ), ( pp.OneOrMore(pp.Word(pp.alphas).set_name("word")), "123", - "Expected word, found '123'" + "Expected word, found '123'", ), ( pp.DelimitedList(pp.Word(pp.alphas).set_name("word")), "123", - "Expected word, found '123'" + "Expected word, found '123'", ), ( pp.Suppress(pp.Word(pp.alphas).set_name("word")), "123", - "Expected word, found '123'" + "Expected word, found '123'", ), ( pp.Forward() << pp.Word(pp.alphas).set_name("word"), "123", - "Expected word, found '123'" + "Expected word, found '123'", ), ) def test_exception_messages(self, tests=test_exception_messages_tests): for expr, input_str, expected_msg in tests: with self.subTest(expr=expr, input_str=input_str): - with self.assertRaisesParseException( - expected_msg=expected_msg - ): + with self.assertRaisesParseException(expected_msg=expected_msg): expr.parse_string(input_str) @@ -10482,9 +10463,7 @@ def test_math(self): self.assertEqual(expr.parseString("1+2*3", parseAll=True)[0], 1 + 2 * 3) self.assertEqual(expr.parseString("1*2+3", parseAll=True)[0], 1 * 2 + 3) self.assertEqual(expr.parseString("1*2^3", parseAll=True)[0], 1 * 2**3) - self.assertEqual( - expr.parseString("4^3^2^1", parseAll=True)[0], 4**3**2**1 - ) + self.assertEqual(expr.parseString("4^3^2^1", parseAll=True)[0], 4**3**2**1) def test_terminate_empty(self): """Recursion with ``Empty`` terminates"""