Imported Upstream version 3.0.6 upstream/3.0.6
authorDongHun Kwak <dh0128.kwak@samsung.com>
Mon, 18 Jul 2022 05:42:37 +0000 (14:42 +0900)
committerDongHun Kwak <dh0128.kwak@samsung.com>
Mon, 18 Jul 2022 05:42:37 +0000 (14:42 +0900)
CHANGES
PKG-INFO
pyparsing.egg-info/PKG-INFO
pyparsing/__init__.py
pyparsing/core.py
pyparsing/helpers.py
tests/test_unit.py
tox.ini

diff --git a/CHANGES b/CHANGES
index d1d6726c2add81ff4e0eeafa16d7be0b1460bbac..99ac526d77d6ce81ae7af1dc3e729ba834a1e3bf 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -2,6 +2,17 @@
 Change Log
 ==========
 
+Version 3.0.6 -
+---------------
+- Added `suppress_warning()` method to individually suppress a warning on a
+  specific ParserElement. Used to refactor `original_text_for` to preserve
+  internal results names, which, while undocumented, had been adopted by
+  some projects.
+
+- Fix bug when `delimited_list` was called with a str literal instead of a
+  parse expression.
+
+
 Version 3.0.5 -
 ---------------
 - Added return type annotations for `col`, `line`, and `lineno`.
index 7d121071fc554d0483f5dbd9b14253bc621299bd..e1371c0022ac7869e32400fc103d75ebfce517b0 100644 (file)
--- a/PKG-INFO
+++ b/PKG-INFO
@@ -1,6 +1,6 @@
 Metadata-Version: 2.1
 Name: pyparsing
-Version: 3.0.5
+Version: 3.0.6
 Summary: Python parsing module
 Home-page: https://github.com/pyparsing/pyparsing/
 Author: Paul McGuire
index 7d121071fc554d0483f5dbd9b14253bc621299bd..e1371c0022ac7869e32400fc103d75ebfce517b0 100644 (file)
@@ -1,6 +1,6 @@
 Metadata-Version: 2.1
 Name: pyparsing
-Version: 3.0.5
+Version: 3.0.6
 Summary: Python parsing module
 Home-page: https://github.com/pyparsing/pyparsing/
 Author: Paul McGuire
index a9643b86e84eae595ae25beb2f31286400cd6f27..288618fe7cb914118520214f4746a2ef05088de0 100644 (file)
@@ -93,22 +93,41 @@ classes inherit from. Use the docstrings for examples of how to:
  - find more useful common expressions in the :class:`pyparsing_common`
    namespace class
 """
-from collections import namedtuple
+from typing import NamedTuple
 
-version_info = namedtuple("version_info", "major minor micro release_level serial")
-__version_info__ = version_info(3, 0, 5, "final", 0)
-__version__ = "{}.{}.{}".format(*__version_info__[:3]) + (
-    "{}{}{}".format(
-        "r" if __version_info__.release_level[0] == "c" else "",
-        __version_info__.release_level[0],
-        __version_info__.serial,
-    ),
-    "",
-)[__version_info__.release_level == "final"]
-__version_time__ = "5 November 2021 10:05 UTC"
-version_info.__str__ = lambda *args: "pyparsing {} - {}".format(
-    __version__, __version_time__
-)
+
+class version_info(NamedTuple):
+    major: int
+    minor: int
+    micro: int
+    releaselevel: str
+    serial: int
+
+    @property
+    def __version__(self):
+        return "{}.{}.{}".format(self.major, self.minor, self.micro) + (
+            "{}{}{}".format(
+                "r" if self.releaselevel[0] == "c" else "",
+                self.releaselevel[0],
+                self.serial,
+            ),
+            "",
+        )[self.releaselevel == "final"]
+
+    def __str__(self):
+        return "{} {} / {}".format(__name__, self.__version__, __version_time__)
+
+    def __repr__(self):
+        return "{}.{}({})".format(
+            __name__,
+            type(self).__name__,
+            ", ".join("{}={!r}".format(*nv) for nv in zip(self._fields, self)),
+        )
+
+
+__version_info__ = version_info(3, 0, 6, "final", 0)
+__version_time__ = "12 Nov 2021 16:06 UTC"
+__version__ = __version_info__.__version__
 __versionTime__ = __version_time__
 __author__ = "Paul McGuire <ptmcg.gm+pyparsing@gmail.com>"
 
index e2fbb6c2af79e668275687ffe3dc954db67dc354..ff24eee5074f5e8bdb6d95f3398c533af1d6e184 100644 (file)
@@ -468,6 +468,23 @@ class ParserElement(ABC):
         # avoid redundant calls to preParse
         self.callPreparse = True
         self.callDuringTry = False
+        self.suppress_warnings_ = []
+
+    def suppress_warning(self, warning_type: Diagnostics):
+        """
+        Suppress warnings emitted for a particular diagnostic on this expression.
+
+        Example::
+
+            base = pp.Forward()
+            base.suppress_warning(Diagnostics.warn_on_parse_using_empty_Forward)
+
+            # statement would normally raise a warning, but is now suppressed
+            print(base.parseString("x"))
+
+        """
+        self.suppress_warnings_.append(warning_type)
+        return self
 
     def copy(self) -> "ParserElement":
         """
@@ -1117,6 +1134,7 @@ class ParserElement(ABC):
         max_matches: int = _MAX_INT,
         overlap: bool = False,
         *,
+        debug: bool = False,
         maxMatches: int = _MAX_INT,
     ) -> Generator[Tuple[ParseResults, int, int], None, None]:
         """
@@ -1173,6 +1191,14 @@ class ParserElement(ABC):
                 else:
                     if nextLoc > loc:
                         matches += 1
+                        if debug:
+                            print(
+                                {
+                                    "tokens": tokens.asList(),
+                                    "start": preloc,
+                                    "end": nextLoc,
+                                }
+                            )
                         yield tokens, preloc, nextLoc
                         if overlap:
                             nextloc = preparseFn(instring, loc)
@@ -1191,7 +1217,7 @@ class ParserElement(ABC):
                 # catch and re-raise exception from here, clears out pyparsing internal stack trace
                 raise exc.with_traceback(None)
 
-    def transform_string(self, instring: str) -> str:
+    def transform_string(self, instring: str, *, debug: bool = False) -> str:
         """
         Extension to :class:`scan_string`, to modify matching text with modified tokens that may
         be returned from a parse action.  To use ``transform_string``, define a grammar and
@@ -1217,7 +1243,7 @@ class ParserElement(ABC):
         # keep string locs straight between transform_string and scan_string
         self.keepTabs = True
         try:
-            for t, s, e in self.scan_string(instring):
+            for t, s, e in self.scan_string(instring, debug=debug):
                 out.append(instring[lastE:s])
                 if t:
                     if isinstance(t, ParseResults):
@@ -1238,7 +1264,12 @@ class ParserElement(ABC):
                 raise exc.with_traceback(None)
 
     def search_string(
-        self, instring: str, max_matches: int = _MAX_INT, *, maxMatches: int = _MAX_INT
+        self,
+        instring: str,
+        max_matches: int = _MAX_INT,
+        *,
+        debug: bool = False,
+        maxMatches: int = _MAX_INT,
     ) -> ParseResults:
         """
         Another extension to :class:`scan_string`, simplifying the access to the tokens found
@@ -1263,7 +1294,7 @@ class ParserElement(ABC):
         maxMatches = min(maxMatches, max_matches)
         try:
             return ParseResults(
-                [t for t, s, e in self.scan_string(instring, maxMatches)]
+                [t for t, s, e in self.scan_string(instring, maxMatches, debug=debug)]
             )
         except ParseBaseException as exc:
             if ParserElement.verbose_stacktrace:
@@ -2054,10 +2085,14 @@ class ParserElement(ABC):
                 fatal = "(FATAL)" if isinstance(pe, ParseFatalException) else ""
                 out.append(pe.explain())
                 out.append("FAIL: " + str(pe))
+                if ParserElement.verbose_stacktrace:
+                    out.extend(traceback.format_tb(pe.__traceback__))
                 success = success and failureTests
                 result = pe
             except Exception as exc:
-                out.append("FAIL-EXCEPTION: " + str(exc))
+                out.append("FAIL-EXCEPTION: {}: {}".format(type(exc).__name__, exc))
+                if ParserElement.verbose_stacktrace:
+                    out.extend(traceback.format_tb(exc.__traceback__))
                 success = success and failureTests
                 result = exc
             else:
@@ -3661,12 +3696,17 @@ class ParseExpression(ParserElement):
         return ret
 
     def _setResultsName(self, name, listAllMatches=False):
-        if __diag__.warn_ungrouped_named_tokens_in_collection:
+        if (
+            __diag__.warn_ungrouped_named_tokens_in_collection
+            and Diagnostics.warn_ungrouped_named_tokens_in_collection
+            not in self.suppress_warnings_
+        ):
             for e in self.exprs:
                 if (
                     isinstance(e, ParserElement)
                     and e.resultsName
-                    and not e.resultsName.startswith("_NOWARN")
+                    and Diagnostics.warn_ungrouped_named_tokens_in_collection
+                    not in e.suppress_warnings_
                 ):
                     warnings.warn(
                         "{}: setting results name {!r} on {} expression "
@@ -3959,12 +3999,22 @@ class Or(ParseExpression):
         return "{" + " ^ ".join(str(e) for e in self.exprs) + "}"
 
     def _setResultsName(self, name, listAllMatches=False):
-        if __diag__.warn_multiple_tokens_in_named_alternation:
-            if any(isinstance(e, And) for e in self.exprs):
+        if (
+            __diag__.warn_multiple_tokens_in_named_alternation
+            and Diagnostics.warn_multiple_tokens_in_named_alternation
+            not in self.suppress_warnings_
+        ):
+            if any(
+                isinstance(e, And)
+                and Diagnostics.warn_multiple_tokens_in_named_alternation
+                not in e.suppress_warnings_
+                for e in self.exprs
+            ):
                 warnings.warn(
                     "{}: setting results name {!r} on {} expression "
                     "will return a list of all parsed tokens in an And alternative, "
-                    "in prior versions only the first token was returned".format(
+                    "in prior versions only the first token was returned; enclose"
+                    "contained argument in Group".format(
                         "warn_multiple_tokens_in_named_alternation",
                         name,
                         type(self).__name__,
@@ -4058,12 +4108,22 @@ class MatchFirst(ParseExpression):
         return "{" + " | ".join(str(e) for e in self.exprs) + "}"
 
     def _setResultsName(self, name, listAllMatches=False):
-        if __diag__.warn_multiple_tokens_in_named_alternation:
-            if any(isinstance(e, And) for e in self.exprs):
+        if (
+            __diag__.warn_multiple_tokens_in_named_alternation
+            and Diagnostics.warn_multiple_tokens_in_named_alternation
+            not in self.suppress_warnings_
+        ):
+            if any(
+                isinstance(e, And)
+                and Diagnostics.warn_multiple_tokens_in_named_alternation
+                not in e.suppress_warnings_
+                for e in self.exprs
+            ):
                 warnings.warn(
                     "{}: setting results name {!r} on {} expression "
-                    "may only return a single token for an And alternative, "
-                    "in future will return the full list of tokens".format(
+                    "will return a list of all parsed tokens in an And alternative, "
+                    "in prior versions only the first token was returned; enclose"
+                    "contained argument in Group".format(
                         "warn_multiple_tokens_in_named_alternation",
                         name,
                         type(self).__name__,
@@ -4706,12 +4766,17 @@ class _MultipleMatch(ParseElementEnhance):
         return loc, tokens
 
     def _setResultsName(self, name, listAllMatches=False):
-        if __diag__.warn_ungrouped_named_tokens_in_collection:
+        if (
+            __diag__.warn_ungrouped_named_tokens_in_collection
+            and Diagnostics.warn_ungrouped_named_tokens_in_collection
+            not in self.suppress_warnings_
+        ):
             for e in [self.expr] + self.expr.recurse():
                 if (
                     isinstance(e, ParserElement)
                     and e.resultsName
-                    and not e.resultsName.startswith("_NOWARN")
+                    and Diagnostics.warn_ungrouped_named_tokens_in_collection
+                    not in e.suppress_warnings_
                 ):
                     warnings.warn(
                         "{}: setting results name {!r} on {} expression "
@@ -5068,6 +5133,8 @@ class Forward(ParseElementEnhance):
         if (
             __diag__.warn_on_match_first_with_lshift_operator
             and caller_line == self.lshift_line
+            and Diagnostics.warn_on_match_first_with_lshift_operator
+            not in self.suppress_warnings_
         ):
             warnings.warn(
                 "using '<<' operator with '|' is probably an error, use '<<='",
@@ -5078,7 +5145,11 @@ class Forward(ParseElementEnhance):
 
     def __del__(self):
         # see if we are getting dropped because of '=' reassignment of var instead of '<<=' or '<<'
-        if self.expr is None and __diag__.warn_on_assignment_to_Forward:
+        if (
+            self.expr is None
+            and __diag__.warn_on_assignment_to_Forward
+            and Diagnostics.warn_on_assignment_to_Forward not in self.suppress_warnings_
+        ):
             warnings.warn_explicit(
                 "Forward defined here but no expression attached later using '<<=' or '<<'",
                 UserWarning,
@@ -5087,7 +5158,12 @@ class Forward(ParseElementEnhance):
             )
 
     def parseImpl(self, instring, loc, doActions=True):
-        if self.expr is None and __diag__.warn_on_parse_using_empty_Forward:
+        if (
+            self.expr is None
+            and __diag__.warn_on_parse_using_empty_Forward
+            and Diagnostics.warn_on_parse_using_empty_Forward
+            not in self.suppress_warnings_
+        ):
             # walk stack until parse_string, scan_string, search_string, or transform_string is found
             parse_fns = [
                 "parse_string",
@@ -5224,7 +5300,11 @@ class Forward(ParseElementEnhance):
             return ret
 
     def _setResultsName(self, name, list_all_matches=False):
-        if __diag__.warn_name_set_on_empty_Forward:
+        if (
+            __diag__.warn_name_set_on_empty_Forward
+            and Diagnostics.warn_name_set_on_empty_Forward
+            not in self.suppress_warnings_
+        ):
             if self.expr is None:
                 warnings.warn(
                     "{}: setting results name {!r} on {} expression "
index 9b1e2e132df1211d36cf1cc62bfb69d562f4d9cb..7d61197128a79ad26a059d81729a4bebf4a72286 100644 (file)
@@ -11,7 +11,7 @@ from .util import _bslash, _flatten, _escape_regex_range_chars
 # global helpers
 #
 def delimited_list(
-    expr: ParserElement,
+    expr: Union[str, ParserElement],
     delim: Union[str, ParserElement] = ",",
     combine: bool = False,
     *,
@@ -34,6 +34,9 @@ def delimited_list(
         delimited_list(Word(alphas)).parse_string("aa,bb,cc") # -> ['aa', 'bb', 'cc']
         delimited_list(Word(hexnums), delim=':', combine=True).parse_string("AA:BB:CC:DD:EE") # -> ['AA:BB:CC:DD:EE']
     """
+    if isinstance(expr, str_type):
+        expr = ParserElement._literalStringClass(expr)
+
     dlName = "{expr} [{delim} {expr}]...{end}".format(
         expr=str(expr.streamline()),
         delim=str(delim),
@@ -387,23 +390,17 @@ def original_text_for(
     locMarker = Empty().set_parse_action(lambda s, loc, t: loc)
     endlocMarker = locMarker.copy()
     endlocMarker.callPreparse = False
-    # prefix these transient names with _NOWARN to suppress ungrouped name warnings
-    matchExpr = (
-        locMarker("_NOWARN_original_start")
-        + expr
-        + endlocMarker("_NOWARN_original_end")
-    )
+    matchExpr = locMarker("_original_start") + expr + endlocMarker("_original_end")
     if asString:
-        extractText = lambda s, l, t: s[
-            t._NOWARN_original_start : t._NOWARN_original_end
-        ]
+        extractText = lambda s, l, t: s[t._original_start : t._original_end]
     else:
 
         def extractText(s, l, t):
-            t[:] = [s[t.pop("_NOWARN_original_start") : t.pop("_NOWARN_original_end")]]
+            t[:] = [s[t.pop("_original_start") : t.pop("_original_end")]]
 
     matchExpr.set_parse_action(extractText)
     matchExpr.ignoreExprs = expr.ignoreExprs
+    matchExpr.suppress_warning(Diagnostics.warn_ungrouped_named_tokens_in_collection)
     return matchExpr
 
 
index db1c8e09b169e550d081e2df42c5acd25b0449ba..4c41e7fd03a6eb0a030b14d330af70a0489603f8 100644 (file)
@@ -113,30 +113,30 @@ class TestCase(unittest.TestCase):
         return ar
 
     @contextlib.contextmanager
-    def assertDoesNotWarn(self, msg: str = None):
-        if msg is None:
-            msg = "unexpected warning raised"
+    def assertDoesNotWarn(self, warning_type: type = UserWarning, msg: str = None):
         with warnings.catch_warnings(record=True) as w:
             warnings.simplefilter("error")
             try:
                 yield
             except Exception as e:
-                self.fail("{}: {}".format(msg, e))
+                if msg is None:
+                    msg = "unexpected warning {} raised".format(e)
+                if isinstance(e, warning_type):
+                    self.fail("{}: {}".format(msg, e))
+                else:
+                    raise
 
 
 class Test01_PyparsingTestInit(TestCase):
     def runTest(self):
-        from pyparsing import (
-            __version__ as pyparsing_version,
-            __version_time__ as pyparsing_version_time,
-        )
-
         print(
             "Beginning test of pyparsing, version",
-            pyparsing_version,
-            pyparsing_version_time,
+            pp.__version__,
+            pp.__version_time__,
         )
         print("Python version", sys.version)
+        print("__version_info__     :", pp.__version_info__)
+        print("__version_info__ repr:", repr(pp.__version_info__))
 
 
 class Test01a_PyparsingEnvironmentTests(TestCase):
@@ -172,6 +172,23 @@ class Test01a_PyparsingEnvironmentTests(TestCase):
         self.assertTrue(all_success, "failed warnings enable test")
 
 
+class Test01b_PyparsingUnitTestUtilitiesTests(TestCase):
+    def runTest(self):
+        with ppt.reset_pyparsing_context():
+            pp.enable_diag(pp.Diagnostics.warn_on_parse_using_empty_Forward)
+
+            # test assertDoesNotWarn raises an AssertionError
+            with self.assertRaises(AssertionError):
+                with self.assertDoesNotWarn(
+                    msg="warned when parsing with an empty Forward expression warning was suppressed",
+                ):
+                    base = pp.Forward()
+                    try:
+                        print(base.parseString("x"))
+                    except ParseException as pe:
+                        pass
+
+
 class Test02_WithoutPackrat(ppt.TestParseResultsAsserts, TestCase):
     suite_context = None
     save_suite_context = None
@@ -222,6 +239,77 @@ class Test02_WithoutPackrat(ppt.TestParseResultsAsserts, TestCase):
             msg="failure in transformString",
         )
 
+    def testTransformStringWithLeadingWhitespace(self):
+        sample = "\n\ncheck"
+        sample = "    check"
+        keywords = pp.oneOf("aaa bbb", asKeyword=True)
+        ident = ~keywords + pp.Word(pp.alphas)
+        ident = pp.Combine(~keywords + pp.Word(pp.alphas))
+        # ident.add_parse_action(lambda t: t[0].upper())
+        ident.add_parse_action(ppc.upcaseTokens)
+        transformed = ident.transformString(sample)
+
+        print(ppt.with_line_numbers(sample))
+        print(ppt.with_line_numbers(transformed))
+        self.assertEqual(sample.replace("check", "CHECK"), transformed)
+
+    def testTransformStringWithLeadingNotAny(self):
+        sample = "print a100"
+        keywords = set("print read".split())
+        ident = pp.Word(pp.alphas, pp.alphanums).add_condition(
+            lambda t: t[0] not in keywords
+        )
+        print(ident.searchString(sample))
+
+    def testTransformStringWithExpectedLeadingWhitespace(self):
+        sample1 = "\n\ncheck aaa"
+        sample2 = "    check aaa"
+        keywords = pp.oneOf("aaa bbb", asKeyword=True)
+        # This construct only works with parse_string, not with scan_string or its siblings
+        # ident = ~keywords + pp.Word(pp.alphas)
+        ident = pp.Word(pp.alphas)
+        ident.add_parse_action(ppc.upcaseTokens)
+
+        for sample in sample1, sample2:
+            transformed = (keywords | ident).transformString(sample)
+            print(ppt.with_line_numbers(sample))
+            print(ppt.with_line_numbers(transformed))
+            self.assertEqual(sample.replace("check", "CHECK"), transformed)
+            print()
+
+    def testTransformStringWithLeadingWhitespaceFromTranslateProject(self):
+        from pyparsing import Keyword, Word, alphas, alphanums, Combine
+
+        block_start = (Keyword("{") | Keyword("BEGIN")).set_name("block_start")
+        block_end = (Keyword("}") | Keyword("END")).set_name("block_end")
+        reserved_words = block_start | block_end
+
+        # this is the first critical part of this test, an And with a leading NotAny
+        # This construct only works with parse_string, not with scan_string or its siblings
+        # name_id = ~reserved_words + Word(alphas, alphanums + "_").set_name("name_id")
+        name_id = Word(alphas, alphanums + "_").set_name("name_id")
+
+        dialog = name_id("block_id") + (Keyword("DIALOGEX") | Keyword("DIALOG"))(
+            "block_type"
+        )
+        string_table = Keyword("STRINGTABLE")("block_type")
+
+        test_string = (
+            """\r\nSTRINGTABLE\r\nBEGIN\r\n// Comment\r\nIDS_1 "Copied"\r\nEND\r\n"""
+        )
+        print("Original:")
+        print(repr(test_string))
+        print("Should match:")
+        # this is the second critical part of this test, an Or or MatchFirst including dialog
+        for parser in (dialog ^ string_table, dialog | string_table):
+            result = (reserved_words | parser).transformString(test_string)
+            print(repr(result))
+            self.assertEqual(
+                test_string,
+                result,
+                "Failed whitespace skipping with NotAny and MatchFirst/Or",
+            )
+
     def testUpdateDefaultWhitespace(self):
         prev_default_whitespace_chars = pp.ParserElement.DEFAULT_WHITE_CHARS
         try:
@@ -3008,6 +3096,7 @@ class Test02_WithoutPackrat(ppt.TestParseResultsAsserts, TestCase):
             for r in result:
                 print(r)
                 print(r.get("_info_"))
+            self.assertEqual([0, 15], [r["_info_"][1] for r in result])
 
     def testParseResultsFromDict(self):
         """test helper classmethod ParseResults.from_dict()"""
@@ -7320,6 +7409,11 @@ class Test02_WithoutPackrat(ppt.TestParseResultsAsserts, TestCase):
             ):
                 expr = (expr_a | expr_b)("rexp")
 
+            with self.assertDoesNotWarn(
+                UserWarning, msg="warned when And within alternation warning was suppressed"
+            ):
+                expr = (expr_a | expr_b).suppress_warning(pp.Diagnostics.warn_multiple_tokens_in_named_alternation)("rexp")
+
             success, report = expr.runTests(
                 """
                 not the bird
@@ -7381,6 +7475,11 @@ class Test02_WithoutPackrat(ppt.TestParseResultsAsserts, TestCase):
             ):
                 expr = (expr_a ^ expr_b)("rexp")
 
+            with self.assertDoesNotWarn(
+                UserWarning, msg="warned when And within alternation warning was suppressed"
+            ):
+                expr = (expr_a ^ expr_b).suppress_warning(pp.Diagnostics.warn_multiple_tokens_in_named_alternation)("rexp")
+
             expr.runTests(
                 """\
                 not the bird
@@ -7552,17 +7651,19 @@ class Test02_WithoutPackrat(ppt.TestParseResultsAsserts, TestCase):
             ):
                 path = coord[...].setResultsName("path")
 
-    def testDontWarnUngroupedNamedTokensIfUngroupedNamesStartWithNOWARN(self):
-        """
-        - warn_ungrouped_named_tokens_in_collection - flag to enable warnings when a results
-          name is defined on a containing expression with ungrouped subexpressions that also
-          have results names (default=True)
-        """
+            with self.assertDoesNotWarn(
+                UserWarning,
+                msg="warned when named repetition of"
+                " ungrouped named expressions warning was suppressed",
+            ):
+                path = coord[...].suppress_warning(pp.Diagnostics.warn_ungrouped_named_tokens_in_collection).setResultsName("path")
+
+    def testDontWarnUngroupedNamedTokensIfWarningSuppressed(self):
         with ppt.reset_pyparsing_context():
             pp.enable_diag(pp.Diagnostics.warn_ungrouped_named_tokens_in_collection)
 
             with self.assertDoesNotWarn(
-                msg="raised {} warning inner names start with '_NOWARN'".format(
+                msg="raised {} warning when warn on ungrouped named tokens was suppressed (original_text_for)".format(
                     pp.Diagnostics.warn_ungrouped_named_tokens_in_collection
                 )
             ):
@@ -7592,6 +7693,12 @@ class Test02_WithoutPackrat(ppt.TestParseResultsAsserts, TestCase):
             ):
                 base("x")
 
+            with self.assertDoesNotWarn(
+                UserWarning,
+                msg="warned when naming an empty Forward expression warning was suppressed",
+            ):
+                base.suppress_warning(pp.Diagnostics.warn_name_set_on_empty_Forward)("x")
+
     def testWarnParsingEmptyForward(self):
         """
         - warn_on_parse_using_empty_Forward - flag to enable warnings when a Forward
@@ -7616,8 +7723,18 @@ class Test02_WithoutPackrat(ppt.TestParseResultsAsserts, TestCase):
 
             with self.assertWarns(
                 UserWarning,
-                msg="failed to warn when naming an empty Forward expression",
+                msg="failed to warn when parsing using an empty Forward expression",
+            ):
+                try:
+                    print(base.parseString("x"))
+                except ParseException as pe:
+                    pass
+
+            with self.assertDoesNotWarn(
+                    UserWarning,
+                    msg="warned when parsing using an empty Forward expression warning was suppressed",
             ):
+                base.suppress_warning(pp.Diagnostics.warn_on_parse_using_empty_Forward)
                 try:
                     print(base.parseString("x"))
                 except ParseException as pe:
@@ -7652,6 +7769,17 @@ class Test02_WithoutPackrat(ppt.TestParseResultsAsserts, TestCase):
             ):
                 a_method()
 
+            def a_method():
+                base = pp.Forward().suppress_warning(pp.Diagnostics.warn_on_assignment_to_Forward)
+                base = pp.Word(pp.alphas)[...] | "(" + base + ")"
+
+            with self.assertDoesNotWarn(
+                UserWarning,
+                msg="warned when using '=' to assign expression to a Forward warning was suppressed",
+            ):
+                a_method()
+
+
     def testWarnOnMultipleStringArgsToOneOf(self):
         """
         - warn_on_multiple_string_args_to_oneof - flag to enable warnings when oneOf is
@@ -7709,6 +7837,14 @@ class Test02_WithoutPackrat(ppt.TestParseResultsAsserts, TestCase):
         print(bool_list2)
         self.assertEqual("bool [, bool]...", str(bool_list2))
 
+    def testDelimitedListOfStrLiterals(self):
+        expr = pp.delimitedList("ABC")
+        print(str(expr))
+        source = "ABC, ABC,ABC"
+        self.assertParseAndCheckList(
+            expr, source, [s.strip() for s in source.split(",")]
+        )
+
     def testEnableDebugOnNamedExpressions(self):
         """
         - enable_debug_on_named_expressions - flag to auto-enable debug on all subsequent
diff --git a/tox.ini b/tox.ini
index c746c130939e2029855555142c298dc9300132a6..f811816349d7f3516af98cd8fac88b55cda27366 100644 (file)
--- a/tox.ini
+++ b/tox.ini
@@ -1,6 +1,7 @@
 [tox]
+skip_missing_interpreters=true
 envlist =
-    py{36,37,38,39,310,py3}
+    py{36,37,38,39,310,py3},pyparsing_packaging
 
 [testenv]
 deps=coverage
@@ -8,3 +9,12 @@ extras=diagrams
 commands=
     coverage run --parallel --branch -m unittest
 
+[testenv:pyparsing_packaging]
+deps=
+    pretend
+    pytest
+commands=
+    python -c "import shutil,os,stat;os.path.exists('packaging') and shutil.rmtree('packaging', onerror=lambda fn, path, _:os.chmod(path,stat.S_IWRITE) or fn(path))"
+    git clone --depth 1 https://github.com/pypa/packaging.git
+    python -m pytest packaging/tests
+    python -c "import shutil,os,stat;shutil.rmtree('packaging', onerror=lambda fn, path, _:os.chmod(path,stat.S_IWRITE) or fn(path))"