diff options
author | Paul McGuire <ptmcg@austin.rr.com> | 2019-09-06 22:50:28 -0500 |
---|---|---|
committer | Paul McGuire <ptmcg@austin.rr.com> | 2019-09-06 22:50:28 -0500 |
commit | 76d9748718eb3fbf16b2e352fd524ae123dd9f47 (patch) | |
tree | ab27d33e31acf51c9d56db0699032df6eca0f78b | |
parent | 87016995f05a0b4927eef22f767a349d85e6afd2 (diff) | |
download | pyparsing-git-76d9748718eb3fbf16b2e352fd524ae123dd9f47.tar.gz |
First take on unittest integration methods and classes
-rw-r--r-- | CHANGES | 3 | ||||
-rw-r--r-- | pyparsing.py | 106 | ||||
-rw-r--r-- | simple_unit_tests.py | 24 | ||||
-rw-r--r-- | unitTests.py | 373 |
4 files changed, 307 insertions, 199 deletions
@@ -73,6 +73,9 @@ Version 3.0.0a1 pp.__diag__.enable_all_warnings() +- New namespace, assert methods and classes added to support writing unit tests. + (MORE TBD) + - Fixed handling of ParseSyntaxExceptions raised as part of Each expressions, when sub-expressions contain '-' backtrack suppression. As part of resolution to a question posted by John diff --git a/pyparsing.py b/pyparsing.py index e47aa54..0e41493 100644 --- a/pyparsing.py +++ b/pyparsing.py @@ -96,7 +96,7 @@ classes inherit from. Use the docstrings for examples of how to: """ __version__ = "3.0.0a1" -__versionTime__ = "02 Sep 2019 16:36 UTC" +__versionTime__ = "07 Sep 2019 03:49 UTC" __author__ = "Paul McGuire <ptmcg@users.sourceforge.net>" import string @@ -114,8 +114,9 @@ from datetime import datetime from operator import itemgetter, attrgetter import itertools from functools import wraps - from itertools import filterfalse +from contextlib import contextmanager +import unittest try: from _thread import RLock @@ -6704,6 +6705,102 @@ pyparsing_unicode.ไทย = pyparsing_unicode.Thai pyparsing_unicode.देवनागरी = pyparsing_unicode.Devanagari +class pyparsing_test: + """ + namespace class for classes useful in writing unit tests + """ + class ParseTestContext: + def __init__(self): + self._save_context = {} + + def __enter__(self): + self._save_context['default_whitespace'] = ParserElement.DEFAULT_WHITE_CHARS + self._save_context['default_keyword_chars'] = Keyword.DEFAULT_KEYWORD_CHARS + self._save_context['literal_string_class'] = ParserElement._literalStringClass + self._save_context['packrat_enabled'] = ParserElement._packratEnabled + return self + + def __exit__(self, *args): + # reset pyparsing global state + if ParserElement.DEFAULT_WHITE_CHARS != self._save_context['default_whitespace']: + ParserElement.setDefaultWhitespaceChars(self._save_context['default_whitespace']) + Keyword.DEFAULT_KEYWORD_CHARS = self._save_context['default_keyword_chars'] + ParserElement.inlineLiteralsUsing(self._save_context['literal_string_class']) + for diagname in __diag__._all_names: + __diag__.disable(diagname) + ParserElement._packratEnabled = self._save_context['packrat_enabled'] + + + class ParseResultsAsserts(unittest.TestCase): + + def assertParseResultsEquals(self, result, expected_list=None, expected_dict=None, msg=None): + """ + Unit test assertion to compare a ParseResults object with an optional expected_list, + and compare any defined results names with an optional expected_dict. + :param result: + :param expected_list: + :param expected_dict: + :param msg: + :return: + """ + if expected_list is not None: + self.assertEqual(expected_list, result.asList(), msg=msg) + if expected_dict is not None: + self.assertEqual(expected_dict, result.asDict(), msg=msg) + + def assertRunTestResults(self, run_tests_report, expected_parse_results=None, msg=None): + """ + Unit test assertion to evaluate output of ParserElement.runTests(). If a list of + list-dict tuples is given as the expected_parse_results argument, then these are zipped + with the report tuples returned by runTests and evaluated using assertParseResultsEquals. + Finally, asserts that the overall runTests() success value is True. + + :param run_tests_report: tuple(bool, [tuple(str, ParseResults or Exception)]) returned from runTests + :param expected_parse_results (optional): [tuple(str, list, dict, Exception)] + :param msg: + :return: None + """ + run_test_success, run_test_results = run_tests_report + + if expected_parse_results is not None: + merged = [(*rpt, expected) for rpt, expected in zip(run_test_results, expected_parse_results)] + for test_string, result, expected in merged: + # expected should be a tuple containing a list and/or a dict or an exception, + # and optional failure message string + # an empty tuple will skip any result validation + fail_msg = next((exp for exp in expected if isinstance(exp, str)), None) + expected_exception = next((exp for exp in expected + if isinstance(exp, type) and issubclass(exp, Exception)), None) + if expected_exception is not None: + with self.assertRaises(expected_exception=expected_exception, msg=fail_msg or msg): + if isinstance(result, Exception): + raise result + else: + expected_list = next((exp for exp in expected if isinstance(exp, list)), None) + expected_dict = next((exp for exp in expected if isinstance(exp, dict)), None) + if (expected_list, expected_dict) != (None, None): + self.assertParseResultsEquals(result, + expected_list=expected_list, expected_dict=expected_dict, + msg=fail_msg or msg) + else: + # warning here maybe? + print("no validation for {!r}".format(test_string)) + + # do this last, in case some specific test results can be reported instead + self.assertTrue(run_test_success, msg=msg if msg is not None else "failed runTests") + + @contextmanager + def assertRaisesParseException(self, exc_type=ParseException, msg=None): + with self.assertRaises(exc_type, msg=msg): + yield + + +# build list of built-in expressions, for future reference if a global default value +# gets updated +_builtin_exprs = [v for v in itertools.chain(vars().values(), vars(pyparsing_common).values()) + if isinstance(v, ParserElement)] + + if __name__ == "__main__": selectToken = CaselessLiteral("select") @@ -6774,8 +6871,3 @@ if __name__ == "__main__": pyparsing_common.uuid.runTests(""" 12345678-1234-5678-1234-567812345678 """) - -# build list of built-in expressions, for future reference if a global default value -# gets updated -_builtin_exprs = [v for v in itertools.chain(vars().values(), vars(pyparsing_common).values()) - if isinstance(v, ParserElement)] diff --git a/simple_unit_tests.py b/simple_unit_tests.py index 6fdab44..f0493cf 100644 --- a/simple_unit_tests.py +++ b/simple_unit_tests.py @@ -11,6 +11,7 @@ import unittest import pyparsing as pp from collections import namedtuple from datetime import datetime +ppt = pp.pyparsing_test # Test spec data class for specifying simple pyparsing test cases PpTestSpec = namedtuple("PpTestSpec", "desc expr text parse_fn " @@ -18,7 +19,7 @@ PpTestSpec = namedtuple("PpTestSpec", "desc expr text parse_fn " PpTestSpec.__new__.__defaults__ = ('', pp.Empty(), '', 'parseString', None, None, None) -class PyparsingExpressionTestCase(unittest.TestCase): +class PyparsingExpressionTestCase(ppt.ParseResultsAsserts, unittest.TestCase): """ Base pyparsing testing class to parse various pyparsing expressions against given text strings. Subclasses must define a class attribute 'tests' which @@ -50,10 +51,9 @@ class PyparsingExpressionTestCase(unittest.TestCase): if test_spec.parse_fn == 'parseString': print(result.dump()) # compare results against given list and/or dict - if test_spec.expected_list is not None: - self.assertEqual(result.asList(), test_spec.expected_list) - if test_spec.expected_dict is not None: - self.assertEqual(result.asDict(), test_spec.expected_dict) + self.assertParseResultsEquals(result, + expected_list=test_spec.expected_list, + expected_dict=test_spec.expected_dict) elif test_spec.parse_fn == 'transformString': print(result) # compare results against given list and/or dict @@ -66,13 +66,13 @@ class PyparsingExpressionTestCase(unittest.TestCase): self.assertEqual([result], test_spec.expected_list) else: # expect fail - try: - parsefn(test_spec.text) - except Exception as exc: - print(pp.ParseException.explain(exc)) - self.assertEqual(exc.loc, test_spec.expected_fail_locn) - else: - self.assertTrue(False, "failed to raise expected exception") + with self.assertRaisesParseException(): + try: + parsefn(test_spec.text) + except Exception as exc: + print(pp.ParseException.explain(exc)) + self.assertEqual(exc.loc, test_spec.expected_fail_locn) + raise # =========== TEST DEFINITIONS START HERE ============== diff --git a/unitTests.py b/unitTests.py index 1bf584c..fb39e08 100644 --- a/unitTests.py +++ b/unitTests.py @@ -11,6 +11,7 @@ from unittest import TestCase, TestSuite, TextTestRunner import datetime from pyparsing import ParseException import pyparsing as pp +ppt = pp.pyparsing_test import sys from io import StringIO @@ -60,7 +61,7 @@ class resetting(object): BUFFER_OUTPUT = True -class ParseTestCase(TestCase): +class ParseTestCase(ppt.ParseResultsAsserts, TestCase): def __init__(self): super().__init__(methodName='_runTest') @@ -75,7 +76,8 @@ class ParseTestCase(TestCase): sys.stdout = buffered_stdout sys.stderr = buffered_stdout print(">>>> Starting test", str(self)) - self.runTest() + with ppt.ParseTestContext(): + self.runTest() finally: print("<<<< End of test", str(self)) @@ -525,7 +527,7 @@ class ParseCommaSeparatedValuesTest(ParseTestCase): for line, tests in zip(testData, testVals): print("Parsing: %r ->" % line, end=' ') results = ppc.comma_separated_list.parseString(line) - print(results.asList()) + print(results) for t in tests: if not(len(results) > t[0] and results[t[0]] == t[1]): print("$$$", results.dump()) @@ -991,19 +993,13 @@ class ParseExpressionResultsAccumulateTest(ParseTestCase): list_of_num=delimitedList(hexnum | num | name, ",") tokens = list_of_num.parseString('1, 0x2, 3, 0x4, aaa') - for k, llen, lst in (("base10", 2, ['1', '3']), - ("hex", 2, ['0x2', '0x4']), - ("word", 1, ['aaa'])): - print(k, tokens[k]) - self.assertEqual(len(tokens[k]), llen, "Wrong length for key %s, %s" % (k, str(tokens[k].asList()))) - self.assertEqual(lst, tokens[k].asList(), - "Incorrect list returned for key %s, %s" % (k, str(tokens[k].asList()))) - self.assertEqual(tokens.base10.asList(), ['1', '3'], - "Incorrect list for attribute base10, %s" % str(tokens.base10.asList())) - self.assertEqual(tokens.hex.asList(), ['0x2', '0x4'], - "Incorrect list for attribute hex, %s" % str(tokens.hex.asList())) - self.assertEqual(tokens.word.asList(), ['aaa'], - "Incorrect list for attribute word, %s" % str(tokens.word.asList())) + print(tokens.dump()) + self.assertParseResultsEquals(tokens, + expected_list=['1', '0x2', '3', '0x4', 'aaa'], + expected_dict={'base10': ['1', '3'], + 'hex': ['0x2', '0x4'], + 'word': ['aaa']}, + ) from pyparsing import Literal, Word, nums, Group, Dict, alphas, \ quotedString, oneOf, delimitedList, removeQuotes, alphanums @@ -1022,10 +1018,11 @@ class ParseExpressionResultsAccumulateTest(ParseTestCase): test="""Q(x,y,z):-Bloo(x,"Mitsis",y),Foo(y,z,1243),y>28,x<12,x>3""" queryRes = Query.parseString(test) - print("pred", queryRes.pred) - self.assertEqual(queryRes.pred.asList(), [['y', '>', '28'], ['x', '<', '12'], ['x', '>', '3']], - "Incorrect list for attribute pred, %s" % str(queryRes.pred.asList())) print(queryRes.dump()) + self.assertParseResultsEquals(queryRes.pred, + expected_list=[['y', '>', '28'], ['x', '<', '12'], ['x', '>', '3']], + msg="Incorrect list for attribute pred, %s" % str(queryRes.pred.asList()) + ) class ReStringRangeTest(ParseTestCase): def runTest(self): @@ -1121,14 +1118,12 @@ class SkipToParserTests(ParseTestCase): num_word = Word(nums, asKeyword=True).setName("int") def test(expr, test_string, expected_list, expected_dict): - try: - result = expr.parseString(test_string) - except Exception as pe: - if any(expected is not None for expected in (expected_list, expected_dict)): - self.assertTrue(False, "{} failed to parse {!r}".format(expr, test_string)) + if (expected_list, expected_dict) == (None, None): + with self.assertRaises(Exception, msg="{} failed to parse {!r}".format(expr, test_string)): + expr.parseString(test_string) else: - self.assertEqual(result.asList(), expected_list) - self.assertEqual(result.asDict(), expected_dict) + result = expr.parseString(test_string) + self.assertParseResultsEquals(result, expected_list=expected_list, expected_dict=expected_dict) # ellipses for SkipTo e = ... + Literal("end") @@ -1248,10 +1243,7 @@ class EllipsisRepetionWithResultsNamesTest(ParseTestCase): for obs, exp in zip(results, expected): test, result = obs exp_list, exp_dict = exp - self.assertEqual(result.asList(), exp_list, - "list mismatch {!r}, expected {!r}, actual {!r}".format(test, exp_list, result.asList())) - self.assertEqual(result.asDict(), exp_dict, - "dict mismatch {!r}, expected {!r}, actual {!r}".format(test, exp_dict, result.asDict())) + self.assertParseResultsEquals(result, expected_list=exp_list, expected_dict=exp_dict) parser = label('label') + val[...]('values') @@ -1268,10 +1260,7 @@ class EllipsisRepetionWithResultsNamesTest(ParseTestCase): for obs, exp in zip(results, expected): test, result = obs exp_list, exp_dict = exp - self.assertEqual(result.asList(), exp_list, - "list mismatch {!r}, expected {!r}, actual {!r}".format(test, exp_list, result.asList())) - self.assertEqual(result.asDict(), exp_dict, - "dict mismatch {!r}, expected {!r}, actual {!r}".format(test, exp_dict, result.asDict())) + self.assertParseResultsEquals(result, expected_list=exp_list, expected_dict=exp_dict) pt = pp.Group(val('x') + pp.Suppress(',') + val('y')) parser = label('label') + pt[...]("points") @@ -1290,10 +1279,7 @@ class EllipsisRepetionWithResultsNamesTest(ParseTestCase): for obs, exp in zip(results, expected): test, result = obs exp_list, exp_dict = exp - self.assertEqual(result.asList(), exp_list, - "list mismatch {!r}, expected {!r}, actual {!r}".format(test, exp_list, result.asList())) - self.assertEqual(result.asDict(), exp_dict, - "dict mismatch {!r}, expected {!r}, actual {!r}".format(test, exp_dict, result.asDict())) + self.assertParseResultsEquals(result, expected_list=exp_list, expected_dict=exp_dict) class CustomQuotesTest(ParseTestCase): @@ -1386,7 +1372,7 @@ class RepeaterTest(ParseTestCase): for tst, result in tests: found = False for tokens, start, end in seq.scanString(tst): - print(tokens.asList()) + print(tokens) found = True if not found: print("No expression match in", tst) @@ -1413,7 +1399,7 @@ class RepeaterTest(ParseTestCase): for tst, result in tests: found = False for tokens, start, end in compoundSeq.scanString(tst): - print("match:", tokens.asList()) + print("match:", tokens) found = True break if not found: @@ -1433,7 +1419,7 @@ class RepeaterTest(ParseTestCase): for tst, result in tests: found = False for tokens, start, end in eSeq.scanString(tst): - print(tokens.asList()) + print(tokens) found = True if not found: print("No match in", tst) @@ -1446,15 +1432,15 @@ class RecursiveCombineTest(ParseTestCase): testInput = "myc(114)r(11)dd" Stream=Forward() Stream << Optional(Word(alphas)) + Optional("(" + Word(nums) + ")" + Stream) - expected = Stream.parseString(testInput).asList() - print(["".join(expected)]) + expected = [''.join(Stream.parseString(testInput))] + print(expected) Stream=Forward() Stream << Combine(Optional(Word(alphas)) + Optional("(" + Word(nums) + ")" + Stream)) - testVal = Stream.parseString(testInput).asList() + testVal = Stream.parseString(testInput) print(testVal) - self.assertEqual("".join(testVal), "".join(expected), "Failed to process Combine with recursive content") + self.assertParseResultsEquals(testVal, expected_list=expected) class InfixNotationGrammarTest1(ParseTestCase): def runTest(self): @@ -1502,10 +1488,12 @@ class InfixNotationGrammarTest1(ParseTestCase): [[1, '+', [2, '*', ['-', [3, '^', 4]], '*', 5], '+', ['-', ['+', ['-', 6]]]]] [[3, '!', '!']]""".split('\n') expected = [ast.literal_eval(x.strip()) for x in expected] - for t, e in zip(test, expected): - print(t, "->", e, "got", expr.parseString(t).asList()) - self.assertEqual(expr.parseString(t).asList(), e, - "mismatched results for infixNotation: got %s, expected %s" % (expr.parseString(t).asList(), e)) + for test_str, exp_list in zip(test, expected): + result = expr.parseString(test_str) + print(test_str, "->", exp_list, "got", result) + self.assertParseResultsEquals(result, expected_list=exp_list, + msg="mismatched results for infixNotation: got %s, expected %s" % + (result.asList(), exp_list)) class InfixNotationGrammarTest2(ParseTestCase): def runTest(self): @@ -1583,8 +1571,10 @@ class InfixNotationGrammarTest2(ParseTestCase): print("r =", boolVars["r"]) print() for t in test: - res = boolExpr.parseString(t)[0] - print(t, '\n', res, '=', bool(res), '\n') + res = boolExpr.parseString(t) + print(t, '\n', res[0], '=', bool(res[0]), '\n') + expected = eval(t, {}, boolVars) + self.assertEquals(expected, bool(res[0])) class InfixNotationGrammarTest3(ParseTestCase): @@ -1647,14 +1637,14 @@ class InfixNotationGrammarTest4(ParseTestCase): f = booleanExpr(word) + pp.StringEnd() tests = [ - ("bar = foo", "[['bar', '=', 'foo']]"), - ("bar = foo & baz = fee", "['&', [['bar', '=', 'foo'], ['baz', '=', 'fee']]]"), + ("bar = foo", [['bar', '=', 'foo']]), + ("bar = foo & baz = fee", ['&', [['bar', '=', 'foo'], ['baz', '=', 'fee']]]), ] for test, expected in tests: print(test) results = f.parseString(test) print(results) - self.assertEqual(str(results), expected, "failed to match expected results, got '%s'" % str(results)) + self.assertParseResultsEquals(results, expected_list=expected) print() class InfixNotationGrammarTest5(ParseTestCase): @@ -1805,8 +1795,9 @@ class ParseResultsWithNamedTupleTest(ParseTestCase): res = expr.parseString("A") print(repr(res)) print(res.Achar) - self.assertEqual(res.Achar, ("A", "Z"), - "Failed accessing named results containing a tuple, got {0!r}".format(res.Achar)) + self.assertParseResultsEquals(res, expected_dict={'Achar': ('A', 'Z')}, + msg="Failed accessing named results containing a tuple, " + "got {0!r}".format(res.Achar)) class ParseHTMLTagsTest(ParseTestCase): @@ -1831,7 +1822,7 @@ class ParseHTMLTagsTest(ParseTestCase): bodyStart, bodyEnd = pp.makeHTMLTags("BODY") resIter = iter(results) for t, s, e in (bodyStart | bodyEnd).scanString(test): - print(test[s:e], "->", t.asList()) + print(test[s:e], "->", t) (expectedType, expectedEmpty, expectedBG, expectedFG) = next(resIter) print(t.dump()) @@ -1876,11 +1867,6 @@ class UpcaseDowncaseUnicode(ParseTestCase): kw = pp.Keyword('mykey', caseless=True).setParseAction(ppc.upcaseTokens)('rname') ret = kw.parseString('mykey') print(ret.rname) - self.assertEqual(ret.rname, 'MYKEY', "failed to upcase with named result") - - kw = pp.Keyword('mykey', caseless=True).setParseAction(ppc.upcaseTokens)('rname') - ret = kw.parseString('mykey') - print(ret.rname) self.assertEqual(ret.rname, 'MYKEY', "failed to upcase with named result (pyparsing_common)") kw = pp.Keyword('MYKEY', caseless=True).setParseAction(ppc.downcaseTokens)('rname') @@ -1966,7 +1952,7 @@ class ParseUsingRegex(ParseTestCase): self.assertTrue(testMatch(namedGrouping, '"foo bar" baz', True, '"foo bar"'), "Re: (16) failed, expected pass") ret = namedGrouping.parseString('"zork" blah') - print(ret.asList()) + print(ret) print(list(ret.items())) print(ret.content) self.assertEqual(ret.content, 'zork', "named group lookup failed") @@ -1996,7 +1982,8 @@ class RegexAsTypeTest(ParseTestCase): result = expr.parseString(test_str) print(result.dump()) print(expected_group_list) - self.assertEqual(result.asList(), expected_group_list, "incorrect group list returned by Regex)") + self.assertParseResultsEquals(result, expected_list=expected_group_list, + msg="incorrect group list returned by Regex)") print("return as re.match instance") expr = pp.Regex(r"\w+ (?P<num1>\d+) (?P<num2>\d+) (?P<last_word>\w+)", asMatch=True) @@ -2084,16 +2071,8 @@ class PrecededByTest(ParseTestCase): ]: print(expr.searchString(s)) result = sum(expr.searchString(s)) - print(result) - - self.assertEqual(result.asList(), expected_list, - "Erroneous tokens for {0}: expected {1}, got {2}".format(expr, - expected_list, - result.asList())) - self.assertEqual(result.asDict(), expected_dict, - "Erroneous named results for {0}: expected {1}, got {2}".format(expr, - expected_dict, - result.asDict())) + print(result.dump()) + self.assertParseResultsEquals(result, expected_list, expected_dict) class CountedArrayTest(ParseTestCase): def runTest(self): @@ -2106,10 +2085,9 @@ class CountedArrayTest(ParseTestCase): r = OneOrMore(countedField).parseString(testString) print(testString) - print(r.asList()) + print(r) - self.assertEqual(r.asList(), [[5, 7], [0, 1, 2, 3, 4, 5], [], [5, 4, 3]], - "Failed matching countedArray, got " + str(r.asList())) + self.assertParseResultsEquals(r, expected_list=[[5, 7], [0, 1, 2, 3, 4, 5], [], [5, 4, 3]]) class CountedArrayTest2(ParseTestCase): # addresses bug raised by Ralf Vosseler @@ -2124,10 +2102,9 @@ class CountedArrayTest2(ParseTestCase): dummy = Word("A") r = OneOrMore(dummy ^ countedField).parseString(testString) print(testString) - print(r.asList()) + print(r) - self.assertEqual(r.asList(), [[5, 7], [0, 1, 2, 3, 4, 5], [], [5, 4, 3]], - "Failed matching countedArray, got " + str(r.asList())) + self.assertParseResultsEquals(r, expected_list=[[5, 7], [0, 1, 2, 3, 4, 5], [], [5, 4, 3]]) class CountedArrayTest3(ParseTestCase): # test case where counter is not a decimal integer @@ -2144,10 +2121,9 @@ class CountedArrayTest3(ParseTestCase): r = OneOrMore(countedField).parseString(testString) print(testString) - print(r.asList()) + print(r) - self.assertEqual(r.asList(), [[5, 7], [0, 1, 2, 3, 4, 5], [], [5, 4, 3]], - "Failed matching countedArray, got " + str(r.asList())) + self.assertParseResultsEquals(r, expected_list=[[5, 7], [0, 1, 2, 3, 4, 5], [], [5, 4, 3]]) class LineStartTest(ParseTestCase): def runTest(self): @@ -2257,13 +2233,13 @@ class LineAndStringEndTest(ParseTestCase): for test, expected in tests: res1 = bnf1.parseString(test) print(res1, '=?', expected) - self.assertEqual(res1.asList(), expected, - "Failed lineEnd/stringEnd test (1): " + repr(test)+ " -> " + str(res1.asList())) + self.assertParseResultsEquals(res1, expected_list=expected, + msg="Failed lineEnd/stringEnd test (1): " + repr(test)+ " -> " + str(res1)) res2 = bnf2.searchString(test)[0] - print(res2.asList(), '=?', expected[-1:]) - self.assertEqual(res2.asList(), expected[-1:], - "Failed lineEnd/stringEnd test (2): " + repr(test)+ " -> " + str(res2.asList())) + print(res2, '=?', expected[-1:]) + self.assertParseResultsEquals(res2, expected_list=expected[-1:], + msg="Failed lineEnd/stringEnd test (2): " + repr(test)+ " -> " + str(res2)) res3 = bnf3.parseString(test) first = res3[0] @@ -2289,16 +2265,15 @@ class LineAndStringEndTest(ParseTestCase): ] for i, (src, expected) in enumerate(tests): print(i, repr(src).replace('\\\\', '\\'), end=' ') - try: - res = k.parseString(src, parseAll=True).asList() - except ParseException as pe: - res = None - print(res) - self.assertEqual(res, expected, "Failed on parseAll=True test %d" % i) + 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) class VariableParseActionArgsTest(ParseTestCase): def runTest(self): - pa3 = lambda s, l, t: t pa2 = lambda l, t: t pa1 = lambda t: t @@ -2439,8 +2414,9 @@ class VariableParseActionArgsTest(ParseTestCase): I | J | K | L | M | N | O | P | Q | R | S | U | V | B | T) testString = "VUTSRQPONMLKJIHGFEDCBA" res = gg.parseString(testString) - print(res.asList()) - self.assertEqual(res.asList(), list(testString), "Failed to parse using variable length parse actions") + print(res) + self.assertParseResultsEquals(res, expected_list=list(testString), + msg="Failed to parse using variable length parse actions") A = Literal("A").setParseAction(ClassAsPA0) B = Literal("B").setParseAction(ClassAsPA1) @@ -2457,7 +2433,7 @@ class VariableParseActionArgsTest(ParseTestCase): "Failed to parse using variable length parse actions " "using class constructors as parse actions") -class EnablePackratParsing(ParseTestCase): +class EnablePackratParsing(TestCase): def runTest(self): from pyparsing import ParserElement ParserElement.enablePackrat() @@ -3719,12 +3695,26 @@ class TokenMapTest(ParseTestCase): from pyparsing import tokenMap, Word, hexnums, OneOrMore parser = OneOrMore(Word(hexnums)).setParseAction(tokenMap(int, 16)) - success, results = parser.runTests(""" + success, report = parser.runTests(""" 00 11 22 aa FF 0a 0d 1a - """, printResults=False) - self.assertTrue(success, "failed to parse hex integers") - print(results) - self.assertEqual(results[0][-1].asList(), [0, 17, 34, 170, 255, 10, 13, 26], "tokenMap parse action failed") + """) + # WAS: + # self.assertTrue(success, "failed to parse hex integers") + # print(results) + # self.assertEqual(results[0][-1].asList(), [0, 17, 34, 170, 255, 10, 13, 26], "tokenMap parse action failed") + + # USING JUST assertParseResultsEquals + # results = [rpt[1] for rpt in report] + # self.assertParseResultsEquals(results[0], [0, 17, 34, 170, 255, 10, 13, 26], + # msg="tokenMap parse action failed") + + # if I hadn't unpacked the return from runTests, I could have just passed it directly, + # instead of reconstituting as a tuple + self.assertRunTestResults((success, report), + [ + ([0, 17, 34, 170, 255, 10, 13, 26], "tokenMap parse action failed"), + ], + msg="failed to parse hex integers") class ParseFileTest(ParseTestCase): @@ -3860,56 +3850,71 @@ class ParseFatalExceptionTest(ParseTestCase): from pyparsing import Word, nums, ParseFatalException - success = False - try: + with self.assertRaisesParseException(exc_type=ParseFatalException, + msg="failed to raise ErrorStop exception"): expr = "ZZZ" - Word(nums) expr.parseString("ZZZ bad") - except ParseFatalException as pfe: - print('ParseFatalException raised correctly') - success = True - except Exception as e: - print(type(e)) - print(e) - self.assertTrue(success, "bad handling of syntax error") + # WAS: + # success = False + # try: + # expr = "ZZZ" - Word(nums) + # expr.parseString("ZZZ bad") + # except ParseFatalException as pfe: + # print('ParseFatalException raised correctly') + # success = True + # except Exception as e: + # print(type(e)) + # print(e) + # + # self.assertTrue(success, "bad handling of syntax error") class InlineLiteralsUsingTest(ParseTestCase): def runTest(self): from pyparsing import ParserElement, Suppress, Literal, CaselessLiteral, Word, alphas, oneOf, CaselessKeyword, nums - with resetting(ParserElement, "_literalStringClass"): - ParserElement.inlineLiteralsUsing(Suppress) - wd = Word(alphas) - result = (wd + ',' + wd + oneOf("! . ?")).parseString("Hello, World!") - self.assertEqual(len(result), 3, "inlineLiteralsUsing(Suppress) failed!") - - ParserElement.inlineLiteralsUsing(Literal) - result = (wd + ',' + wd + oneOf("! . ?")).parseString("Hello, World!") - self.assertEqual(len(result), 4, "inlineLiteralsUsing(Literal) failed!") + wd = Word(alphas) - ParserElement.inlineLiteralsUsing(CaselessKeyword) - result = ("SELECT" + wd + "FROM" + wd).parseString("select color from colors") - self.assertEqual(result.asList(), "SELECT color FROM colors".split(), - "inlineLiteralsUsing(CaselessKeyword) failed!") + ParserElement.inlineLiteralsUsing(Suppress) + result = (wd + ',' + wd + oneOf("! . ?")).parseString("Hello, World!") + self.assertEqual(len(result), 3, "inlineLiteralsUsing(Suppress) failed!") - ParserElement.inlineLiteralsUsing(CaselessLiteral) - result = ("SELECT" + wd + "FROM" + wd).parseString("select color from colors") - self.assertEqual(result.asList(), "SELECT color FROM colors".split(), - "inlineLiteralsUsing(CaselessLiteral) failed!") + ParserElement.inlineLiteralsUsing(Literal) + result = (wd + ',' + wd + oneOf("! . ?")).parseString("Hello, World!") + self.assertEqual(len(result), 4, "inlineLiteralsUsing(Literal) failed!") - integer = Word(nums) - ParserElement.inlineLiteralsUsing(Literal) - date_str = integer("year") + '/' + integer("month") + '/' + integer("day") - result = date_str.parseString("1999/12/31") - self.assertEqual(result.asList(), ['1999', '/', '12', '/', '31'], "inlineLiteralsUsing(example 1) failed!") + ParserElement.inlineLiteralsUsing(CaselessKeyword) + result = ("SELECT" + wd + "FROM" + wd).parseString("select color from colors") + # WAS: + # self.assertEqual(result.asList(), "SELECT color FROM colors".split(), + # "inlineLiteralsUsing(CaselessKeyword) failed!") + self.assertParseResultsEquals(result, "SELECT color FROM colors".split(), + msg="inlineLiteralsUsing(CaselessKeyword) failed!") - # change to Suppress - ParserElement.inlineLiteralsUsing(Suppress) - date_str = integer("year") + '/' + integer("month") + '/' + integer("day") + ParserElement.inlineLiteralsUsing(CaselessLiteral) + result = ("SELECT" + wd + "FROM" + wd).parseString("select color from colors") + # self.assertEqual(result.asList(), "SELECT color FROM colors".split(), + # "inlineLiteralsUsing(CaselessLiteral) failed!") + self.assertParseResultsEquals(result, "SELECT color FROM colors".split(), + msg="inlineLiteralsUsing(CaselessLiteral) failed!") - result = date_str.parseString("1999/12/31") # -> ['1999', '12', '31'] - self.assertEqual(result.asList(), ['1999', '12', '31'], "inlineLiteralsUsing(example 2) failed!") + integer = Word(nums) + ParserElement.inlineLiteralsUsing(Literal) + date_str = integer("year") + '/' + integer("month") + '/' + integer("day") + result = date_str.parseString("1999/12/31") + # self.assertEqual(result.asList(), ['1999', '/', '12', '/', '31'], "inlineLiteralsUsing(example 1) failed!") + self.assertParseResultsEquals(result, expected_list=['1999', '/', '12', '/', '31'], + msg="inlineLiteralsUsing(example 1) failed!") + + # change to Suppress + ParserElement.inlineLiteralsUsing(Suppress) + date_str = integer("year") + '/' + integer("month") + '/' + integer("day") + + result = date_str.parseString("1999/12/31") # -> ['1999', '12', '31'] + # self.assertEqual(result.asList(), ['1999', '12', '31'], "inlineLiteralsUsing(example 2) failed!") + self.assertParseResultsEquals(result, expected_list=['1999', '12', '31'], + msg="inlineLiteralsUsing(example 2) failed!") class CloseMatchTest(ParseTestCase): def runTest(self): @@ -3945,19 +3950,13 @@ class DefaultKeywordCharsTest(ParseTestCase): def runTest(self): import pyparsing as pp - try: + with self.assertRaisesParseException(msg="failed to fail matching keyword using updated keyword chars"): pp.Keyword("start").parseString("start1000") - except pp.ParseException: - pass - else: - self.assertTrue(False, "failed to fail on default keyword chars") try: pp.Keyword("start", identChars=pp.alphas).parseString("start1000") except pp.ParseException: self.assertTrue(False, "failed to match keyword using updated keyword chars") - else: - pass with resetting(pp.Keyword, "DEFAULT_KEYWORD_CHARS"): pp.Keyword.setDefaultKeywordChars(pp.alphas) @@ -3965,22 +3964,14 @@ class DefaultKeywordCharsTest(ParseTestCase): pp.Keyword("start").parseString("start1000") except pp.ParseException: self.assertTrue(False, "failed to match keyword using updated keyword chars") - else: - pass - try: + with self.assertRaisesParseException(msg="failed to fail matching keyword using updated keyword chars"): pp.CaselessKeyword("START").parseString("start1000") - except pp.ParseException: - pass - else: - self.assertTrue(False, "failed to fail on default keyword chars") try: pp.CaselessKeyword("START", identChars=pp.alphas).parseString("start1000") except pp.ParseException: self.assertTrue(False, "failed to match keyword using updated keyword chars") - else: - pass with resetting(pp.Keyword, "DEFAULT_KEYWORD_CHARS"): pp.Keyword.setDefaultKeywordChars(pp.alphas) @@ -3988,8 +3979,6 @@ class DefaultKeywordCharsTest(ParseTestCase): pp.CaselessKeyword("START").parseString("start1000") except pp.ParseException: self.assertTrue(False, "failed to match keyword using updated keyword chars") - else: - pass class ColTest(ParseTestCase): def runTest(self): @@ -4053,7 +4042,8 @@ class ParseActionNestingTest(ParseTestCase): vals.addParseAction(add_total) results = vals.parseString("244 23 13 2343") print(results.dump()) - self.assertEqual(results.int_values.asDict(), {}, "noop parse action changed ParseResults structure") + self.assertParseResultsEquals(results, expected_dict={'int_values': [244, 23, 13, 2343], 'total': 2623}, + msg="noop parse action changed ParseResults structure") name = pp.Word(pp.alphas)('name') score = pp.Word(pp.nums + '.')('score') @@ -4107,15 +4097,12 @@ class ParseResultsNamesInGroupWithDictTest(ParseTestCase): test_string = '"Golden Gate Bridge" 37.819722 -122.478611 height=746 span=4200' site.runTests(test_string) - # U = list_num.parseString(test_string) - # self.assertTrue("LIT_NUM" not in U.LIST.LIST_VALUES, "results name retained as sub in ungrouped named result") - a, aEnd = pp.makeHTMLTags('a') attrs = a.parseString("<a href='blah'>") print(attrs.dump()) - self.assertEqual(attrs.startA.href, 'blah') - self.assertEqual(attrs.asDict(), {'startA': {'href': 'blah', 'tag': 'a', 'empty': False}, - 'href': 'blah', 'tag': 'a', 'empty': False}) + self.assertParseResultsEquals(attrs, + expected_dict = {'startA': {'href': 'blah', 'tag': 'a', 'empty': False}, + 'href': 'blah', 'tag': 'a', 'empty': False}) class FollowedByTest(ParseTestCase): @@ -4193,8 +4180,10 @@ class UnicodeTests(ParseTestCase): hello = "Καλημέρα, κόσμε!" result = greet.parseString(hello) print(result) - self.assertTrue(result.asList() == ['Καλημέρα', ',', 'κόσμε', '!'], - "Failed to parse Greek 'Hello, World!' using pyparsing_unicode.Greek.alphas") + self.assertParseResultsEquals(result, + expected_list=['Καλημέρα', ',', 'κόσμε', '!'], + msg="Failed to parse Greek 'Hello, World!' using " + "pyparsing_unicode.Greek.alphas") # define a custom unicode range using multiple inheritance class Turkish_set(ppu.Latin1, ppu.LatinA): @@ -4223,9 +4212,10 @@ class UnicodeTests(ParseTestCase): nüfus=4279677""" result = pp.Dict(pp.OneOrMore(pp.Group(key_value))).parseString(sample) - print(result.asDict()) - self.assertEqual(result.asDict(), {'şehir': 'İzmir', 'ülke': 'Türkiye', 'nüfus': 4279677}, - "Failed to parse Turkish key-value pairs") + print(result.dump()) + self.assertParseResultsEquals(result, + expected_dict={'şehir': 'İzmir', 'ülke': 'Türkiye', 'nüfus': 4279677}, + msg="Failed to parse Turkish key-value pairs") class IndentedBlockExampleTest(ParseTestCase): @@ -4508,12 +4498,18 @@ class ParseResultsWithNameMatchFirst(ParseTestCase): expr_a = pp.Literal('not') + pp.Literal('the') + pp.Literal('bird') expr_b = pp.Literal('the') + pp.Literal('bird') expr = (expr_a | expr_b)('rexp') - expr.runTests("""\ + + success, report = expr.runTests("""\ not the bird the bird """) - self.assertEqual(list(expr.parseString('not the bird')['rexp']), 'not the bird'.split()) - self.assertEqual(list(expr.parseString('the bird')['rexp']), 'the bird'.split()) + results = [rpt[1] for rpt in report] + self.assertParseResultsEquals(results[0], + ['not', 'the', 'bird'], + {'rexp': ['not', 'the', 'bird']}) + self.assertParseResultsEquals(results[1], + ['the', 'bird'], + {'rexp': ['the', 'bird']}) # test compatibility mode, no longer restoring pre-2.3.1 behavior with resetting(pp.__compat__, "collect_all_And_tokens"), \ @@ -4525,12 +4521,17 @@ class ParseResultsWithNameMatchFirst(ParseTestCase): with self.assertWarns(UserWarning, msg="failed to warn of And within alternation"): expr = (expr_a | expr_b)('rexp') - expr.runTests(""" + success, report = expr.runTests(""" not the bird the bird """) - self.assertEqual(list(expr.parseString('not the bird')['rexp']), 'not the bird'.split()) - self.assertEqual(list(expr.parseString('the bird')['rexp']), 'the bird'.split()) + results = [rpt[1] for rpt in report] + self.assertParseResultsEquals(results[0], + ['not', 'the', 'bird'], + {'rexp': ['not', 'the', 'bird']}) + self.assertParseResultsEquals(results[1], + ['the', 'bird'], + {'rexp': ['the', 'bird']}) class ParseResultsWithNameOr(ParseTestCase): @@ -4543,16 +4544,29 @@ class ParseResultsWithNameOr(ParseTestCase): not the bird the bird """) - self.assertEqual(list(expr.parseString('not the bird')['rexp']), 'not the bird'.split()) - self.assertEqual(list(expr.parseString('the bird')['rexp']), 'the bird'.split()) + result = expr.parseString('not the bird') + self.assertParseResultsEquals(result, + ['not', 'the', 'bird'], + {'rexp': ['not', 'the', 'bird']}) + result = expr.parseString('the bird') + self.assertParseResultsEquals(result, + ['the', 'bird'], + {'rexp': ['the', 'bird']}) expr = (expr_a | expr_b)('rexp') expr.runTests("""\ not the bird the bird """) - self.assertEqual(list(expr.parseString('not the bird')['rexp']), 'not the bird'.split()) - self.assertEqual(list(expr.parseString('the bird')['rexp']), 'the bird'.split()) + result = expr.parseString('not the bird') + self.assertParseResultsEquals(result, + ['not', 'the', 'bird'], + {'rexp': + ['not', 'the', 'bird']}) + result = expr.parseString('the bird') + self.assertParseResultsEquals(result, + ['the', 'bird'], + {'rexp': ['the', 'bird']}) # test compatibility mode, no longer restoring pre-2.3.1 behavior with resetting(pp.__compat__, "collect_all_And_tokens"), \ @@ -5118,7 +5132,6 @@ def makeTestSuite(): test_case_classes.remove(PyparsingTestInit) # test_case_classes.remove(ParseASMLTest) - test_case_classes.remove(EnablePackratParsing) if IRON_PYTHON_ENV: test_case_classes.remove(OriginalTextForTest) |