Imported Upstream version 2.4.6 upstream/2.4.6
authorDongHun Kwak <dh0128.kwak@samsung.com>
Mon, 18 Jul 2022 05:42:34 +0000 (14:42 +0900)
committerDongHun Kwak <dh0128.kwak@samsung.com>
Mon, 18 Jul 2022 05:42:34 +0000 (14:42 +0900)
CHANGES
PKG-INFO
pyparsing.egg-info/PKG-INFO
pyparsing.py
unitTests.py

diff --git a/CHANGES b/CHANGES
index 907cfaaebce3bd8050b4c7a21062e080a65ae07c..11a5a3659f54deb44413d558819f85d51b5b5b24 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -2,6 +2,34 @@
 Change Log
 ==========
 
+Version 2.4.6 - December, 2019
+------------------------------
+- Fixed typos in White mapping of whitespace characters, to use
+  correct "\u" prefix instead of "u\".
+
+- Fix bug in left-associative ternary operators defined using
+  infixNotation. First reported on StackOverflow by user Jeronimo.
+
+- Backport of pyparsing_test namespace from 3.0.0, including
+  TestParseResultsAsserts mixin class defining unittest-helper
+  methods:
+  . def assertParseResultsEquals(
+            self, result, expected_list=None, expected_dict=None, msg=None)
+  . def assertParseAndCheckList(
+            self, expr, test_string, expected_list, msg=None, verbose=True)
+  . def assertParseAndCheckDict(
+            self, expr, test_string, expected_dict, msg=None, verbose=True)
+  . def assertRunTestResults(
+            self, run_tests_report, expected_parse_results=None, msg=None)
+  . def assertRaisesParseException(self, exc_type=ParseException, msg=None)
+
+  To use the methods in this mixin class, declare your unittest classes as:
+
+    from pyparsing import pyparsing_test as ppt
+    class MyParserTest(ppt.TestParseResultsAsserts, unittest.TestCase):
+        ...
+
+
 Version 2.4.5 - November, 2019
 ------------------------------
 - Fixed encoding when setup.py reads README.rst to include the
index 2ce99ea087607d797cea3f6d130cfcf100cac290..84f60cafca2b4b7c3da8342938e6f178d745ef5e 100644 (file)
--- a/PKG-INFO
+++ b/PKG-INFO
@@ -1,6 +1,6 @@
 Metadata-Version: 1.2
 Name: pyparsing
-Version: 2.4.5
+Version: 2.4.6
 Summary: Python parsing module
 Home-page: https://github.com/pyparsing/pyparsing/
 Author: Paul McGuire
index 2ce99ea087607d797cea3f6d130cfcf100cac290..84f60cafca2b4b7c3da8342938e6f178d745ef5e 100644 (file)
@@ -1,6 +1,6 @@
 Metadata-Version: 1.2
 Name: pyparsing
-Version: 2.4.5
+Version: 2.4.6
 Summary: Python parsing module
 Home-page: https://github.com/pyparsing/pyparsing/
 Author: Paul McGuire
index 9a2dd7bf368a7906e2d0232f2a2abd179c5b2826..4d2f98e46e8a0be7184416d56321b03aa0eb0bae 100644 (file)
@@ -95,8 +95,8 @@ classes inherit from. Use the docstrings for examples of how to:
    namespace class
 """
 
-__version__ = "2.4.5"
-__versionTime__ = "09 Nov 2019 23:03 UTC"
+__version__ = "2.4.6"
+__versionTime__ = "24 Dec 2019 04:27 UTC"
 __author__ = "Paul McGuire <ptmcg@users.sourceforge.net>"
 
 import string
@@ -114,6 +114,7 @@ from datetime import datetime
 from operator import itemgetter
 import itertools
 from functools import wraps
+from contextlib import contextmanager
 
 try:
     # Python 3
@@ -184,6 +185,7 @@ __diag__.warn_ungrouped_named_tokens_in_collection = False
 __diag__.warn_name_set_on_empty_Forward = False
 __diag__.warn_on_multiple_string_args_to_oneof = False
 __diag__.enable_debug_on_named_expressions = False
+__diag__._all_names = [nm for nm in vars(__diag__) if nm.startswith("enable_") or nm.startswith("warn_")]
 
 def _enable_all_warnings():
     __diag__.warn_multiple_tokens_in_named_alternation = True
@@ -3630,24 +3632,24 @@ class White(Token):
         '\n': '<LF>',
         '\r': '<CR>',
         '\f': '<FF>',
-        'u\00A0': '<NBSP>',
-        'u\1680': '<OGHAM_SPACE_MARK>',
-        'u\180E': '<MONGOLIAN_VOWEL_SEPARATOR>',
-        'u\2000': '<EN_QUAD>',
-        'u\2001': '<EM_QUAD>',
-        'u\2002': '<EN_SPACE>',
-        'u\2003': '<EM_SPACE>',
-        'u\2004': '<THREE-PER-EM_SPACE>',
-        'u\2005': '<FOUR-PER-EM_SPACE>',
-        'u\2006': '<SIX-PER-EM_SPACE>',
-        'u\2007': '<FIGURE_SPACE>',
-        'u\2008': '<PUNCTUATION_SPACE>',
-        'u\2009': '<THIN_SPACE>',
-        'u\200A': '<HAIR_SPACE>',
-        'u\200B': '<ZERO_WIDTH_SPACE>',
-        'u\202F': '<NNBSP>',
-        'u\205F': '<MMSP>',
-        'u\3000': '<IDEOGRAPHIC_SPACE>',
+        u'\u00A0': '<NBSP>',
+        u'\u1680': '<OGHAM_SPACE_MARK>',
+        u'\u180E': '<MONGOLIAN_VOWEL_SEPARATOR>',
+        u'\u2000': '<EN_QUAD>',
+        u'\u2001': '<EM_QUAD>',
+        u'\u2002': '<EN_SPACE>',
+        u'\u2003': '<EM_SPACE>',
+        u'\u2004': '<THREE-PER-EM_SPACE>',
+        u'\u2005': '<FOUR-PER-EM_SPACE>',
+        u'\u2006': '<SIX-PER-EM_SPACE>',
+        u'\u2007': '<FIGURE_SPACE>',
+        u'\u2008': '<PUNCTUATION_SPACE>',
+        u'\u2009': '<THIN_SPACE>',
+        u'\u200A': '<HAIR_SPACE>',
+        u'\u200B': '<ZERO_WIDTH_SPACE>',
+        u'\u202F': '<NNBSP>',
+        u'\u205F': '<MMSP>',
+        u'\u3000': '<IDEOGRAPHIC_SPACE>',
         }
     def __init__(self, ws=" \t\r\n", min=1, max=0, exact=0):
         super(White, self).__init__()
@@ -6064,7 +6066,7 @@ def infixNotation(baseExpr, opList, lpar=Suppress('('), rpar=Suppress(')')):
                     matchExpr = _FB(lastExpr + lastExpr) + Group(lastExpr + OneOrMore(lastExpr))
             elif arity == 3:
                 matchExpr = (_FB(lastExpr + opExpr1 + lastExpr + opExpr2 + lastExpr)
-                             + Group(lastExpr + opExpr1 + lastExpr + opExpr2 + lastExpr))
+                             + Group(lastExpr + OneOrMore(opExpr1 + lastExpr + opExpr2 + lastExpr)))
             else:
                 raise ValueError("operator must be unary (1), binary (2), or ternary (3)")
         elif rightLeftAssoc == opAssoc.RIGHT:
@@ -6835,6 +6837,187 @@ if PY_3:
     setattr(pyparsing_unicode, u"देवनागरी", pyparsing_unicode.Devanagari)
 
 
+class pyparsing_test:
+    """
+    namespace class for classes useful in writing unit tests
+    """
+
+    class reset_pyparsing_context:
+        """
+        Context manager to be used when writing unit tests that modify pyparsing config values:
+         - packrat parsing
+         - default whitespace characters.
+         - default keyword characters
+         - literal string auto-conversion class
+         - __diag__ settings
+
+        Example:
+            with reset_pyparsing_context():
+                # test that literals used to construct a grammar are automatically suppressed
+                ParserElement.inlineLiteralsUsing(Suppress)
+
+                term = Word(alphas) | Word(nums)
+                group = Group('(' + term[...] + ')')
+
+                # assert that the '()' characters are not included in the parsed tokens
+                self.assertParseAndCheckLisst(group, "(abc 123 def)", ['abc', '123', 'def'])
+
+            # after exiting context manager, literals are converted to Literal expressions again
+        """
+
+        def __init__(self):
+            self._save_context = {}
+
+        def save(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
+            self._save_context["packrat_parse"] = ParserElement._parse
+            self._save_context["__diag__"] = {
+                name: getattr(__diag__, name) for name in __diag__._all_names
+            }
+            self._save_context["__compat__"] = {
+                "collect_all_And_tokens": __compat__.collect_all_And_tokens
+            }
+            return self
+
+        def restore(self):
+            # 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 name, value in self._save_context["__diag__"].items():
+                setattr(__diag__, name, value)
+            ParserElement._packratEnabled = self._save_context["packrat_enabled"]
+            ParserElement._parse = self._save_context["packrat_parse"]
+            __compat__.collect_all_And_tokens = self._save_context["__compat__"]
+
+        def __enter__(self):
+            return self.save()
+
+        def __exit__(self, *args):
+            return self.restore()
+
+    class TestParseResultsAsserts:
+        """
+        A mixin class to add parse results assertion methods to normal unittest.TestCase classes.
+        """
+        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.
+            """
+            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 assertParseAndCheckList(
+            self, expr, test_string, expected_list, msg=None, verbose=True
+        ):
+            """
+            Convenience wrapper assert to test a parser element and input string, and assert that
+            the resulting ParseResults.asList() is equal to the expected_list.
+            """
+            result = expr.parseString(test_string, parseAll=True)
+            if verbose:
+                print(result.dump())
+            self.assertParseResultsEquals(result, expected_list=expected_list, msg=msg)
+
+        def assertParseAndCheckDict(
+            self, expr, test_string, expected_dict, msg=None, verbose=True
+        ):
+            """
+            Convenience wrapper assert to test a parser element and input string, and assert that
+            the resulting ParseResults.asDict() is equal to the expected_dict.
+            """
+            result = expr.parseString(test_string, parseAll=True)
+            if verbose:
+                print(result.dump())
+            self.assertParseResultsEquals(result, expected_dict=expected_dict, 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)]
+            """
+            run_test_success, run_test_results = run_tests_report
+
+            if expected_parse_results is not None:
+                merged = [
+                    (rpt[0], rpt[1], 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
+
+
 if __name__ == "__main__":
 
     selectToken    = CaselessLiteral("select")
index 906fcf0af63b29bf7508b21c42e3bc6b6949ff9a..3508394593b4e16d01f87d19a03c1d4e7ea1e52b 100644 (file)
@@ -11,7 +11,7 @@ from __future__ import division
 
 from unittest import TestCase, TestSuite, TextTestRunner
 import datetime
-from pyparsing import ParseException
+from pyparsing import ParseException, pyparsing_test as ppt
 import pyparsing as pp
 
 import sys
@@ -82,7 +82,7 @@ class AutoReset(object):
 
 BUFFER_OUTPUT = True
 
-class ParseTestCase(TestCase):
+class ParseTestCase(ppt.TestParseResultsAsserts, TestCase):
     def __init__(self):
         super(ParseTestCase, self).__init__(methodName='_runTest')
         self.expect_traceback = False
@@ -99,7 +99,8 @@ class ParseTestCase(TestCase):
                         sys.stdout = buffered_stdout
                         sys.stderr = buffered_stdout
                     print_(">>>> Starting test",str(self))
-                    self.runTest()
+                    with ppt.reset_pyparsing_context():
+                        self.runTest()
 
                 finally:
                     print_("<<<< End of test",str(self))
@@ -4731,6 +4732,27 @@ class UndesirableButCommonPracticesTest(ParseTestCase):
         """)
 
 
+class ChainedTernaryOperator(ParseTestCase):
+    def runTest(self):
+        import pyparsing as pp
+
+        TERNARY_INFIX = pp.infixNotation(
+            pp.pyparsing_common.integer, [
+                (("?", ":"), 3, pp.opAssoc.LEFT),
+        ])
+        self.assertParseAndCheckList(TERNARY_INFIX,
+                                     "1?1:0?1:0",
+                                     [[1, '?', 1, ':', 0, '?', 1, ':', 0]])
+
+        TERNARY_INFIX = pp.infixNotation(
+            pp.pyparsing_common.integer, [
+                (("?", ":"), 3, pp.opAssoc.RIGHT),
+        ])
+        self.assertParseAndCheckList(TERNARY_INFIX,
+                                     "1?1:0?1:0",
+                                     [[1, '?', 1, ':', [0, '?', 1, ':', 0]]])
+
+
 class MiscellaneousParserTests(ParseTestCase):
     def runTest(self):
         self.expect_warning = True