summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul McGuire <ptmcg@austin.rr.com>2019-08-10 06:57:36 -0500
committerPaul McGuire <ptmcg@austin.rr.com>2019-08-10 06:57:36 -0500
commit675e87a859ce9f7bfefb091e9d6198c4a7f5eafd (patch)
treea980f282c739cf701283d08b2559886e2dc7d009
parent7c1db54c6b4de188d7bebcc7372b926a13fd3da4 (diff)
downloadpyparsing-git-675e87a859ce9f7bfefb091e9d6198c4a7f5eafd.tar.gz
Rework __diag__ and __compat__ to be actual classes instead of just namespaces, to add helpful behavior and methods
-rw-r--r--CHANGES21
-rw-r--r--pyparsing.py75
-rw-r--r--unitTests.py147
3 files changed, 180 insertions, 63 deletions
diff --git a/CHANGES b/CHANGES
index 68503c7..fc8d270 100644
--- a/CHANGES
+++ b/CHANGES
@@ -33,6 +33,23 @@ Version 2.5.0a1
to help identify those expressions in your parsers that
will have changed as a result.
+- Expanded __diag__ and __compat__ to actual classes instead of
+ just namespaces, to add some helpful behavior:
+ - enable() and .disable() methods to give extra
+ help when setting or clearing flags (detects invalid
+ flag names, detects when trying to set a __compat__ flag
+ that is no longer settable). Use these methods now to
+ set or clear flags, instead of directly setting to True or
+ False.
+
+ import pyparsing as pp
+ pp.__diag__.enable("warn_multiple_tokens_in_named_alternation")
+
+ - __diag__.enable_all_warnings() is another helper that sets
+ all "warn*" diagnostics to True.
+
+ pp.__diag__.enable_all_warnings()
+
- 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
@@ -41,6 +58,10 @@ Version 2.5.0a1
- Fixed bug in CloseMatch where end location was incorrectly
computed; and updated partial_gene_match.py example.
+- Fixed bug in indentedBlock with a parser using two different
+ types of nested indented blocks with different indent values,
+ but sharing the same indent stack, submitted by renzbagaporo.
+
- BigQueryViewParser.py added to examples directory, PR submitted
by Michael Smedberg, nice work!
diff --git a/pyparsing.py b/pyparsing.py
index aa49a06..43f1abc 100644
--- a/pyparsing.py
+++ b/pyparsing.py
@@ -96,7 +96,7 @@ classes inherit from. Use the docstrings for examples of how to:
"""
__version__ = "2.5.0a1"
-__versionTime__ = "09 Aug 2019 11:27 UTC"
+__versionTime__ = "10 Aug 2019 11:56 UTC"
__author__ = "Paul McGuire <ptmcg@users.sourceforge.net>"
import string
@@ -127,24 +127,52 @@ from collections.abc import MutableMapping, Mapping
from collections import OrderedDict
from types import SimpleNamespace
-# version compatibility configuration
-__compat__ = SimpleNamespace()
-__compat__.__doc__ = """
+
+class __config_flags:
+ """Internal class for defining compatibility and debugging flags"""
+ _all_names = []
+ _fixed_names = []
+ _type_desc = "configuration"
+
+ @classmethod
+ def _set(cls, dname, value):
+ if dname in cls._fixed_names:
+ warnings.warn("{}.{} {} is {} and cannot be overridden".format(cls.__name__,
+ dname,
+ cls._type_desc,
+ str(getattr(cls, dname)).upper()))
+ if dname in cls._all_names:
+ setattr(cls, dname, value)
+ else:
+ raise ValueError("no such {} {!r}".format(cls._type_desc, dname))
+
+ enable = classmethod(lambda cls, name: cls._set(name, True))
+ disable = classmethod(lambda cls, name: cls._set(name, False))
+
+class __compat__(__config_flags):
+ """
A cross-version compatibility configuration for pyparsing features that will be
released in a future version. By setting values in this configuration to True,
those features can be enabled in prior versions for compatibility development
and testing.
- collect_all_And_tokens - flag to enable fix for Issue #63 that fixes erroneous grouping
- of results names when an And expression is nested within an Or or MatchFirst;
+ of results names when an And expression is nested within an Or or MatchFirst;
maintained for compatibility, but setting to False no longer restores pre-2.3.1
behavior
-"""
-__compat__.collect_all_And_tokens = True
+ """
+ _type_desc = "compatibility"
+
+ collect_all_And_tokens = True
-__diag__ = SimpleNamespace()
-__diag__.__doc__ = """
-Diagnostic configuration (all default to False)
+ _all_names = [__ for __ in locals() if not __.startswith('_')]
+ _fixed_names = """
+ collect_all_And_tokens
+ """.split()
+
+class __diag__(__config_flags):
+ """
+ Diagnostic configuration (all default to False)
- warn_multiple_tokens_in_named_alternation - flag to enable warnings when a results
name is defined on a MatchFirst or Or expression with one or more And subexpressions
- warn_ungrouped_named_tokens_in_collection - flag to enable warnings when a results
@@ -156,12 +184,27 @@ Diagnostic configuration (all default to False)
incorrectly called with multiple str arguments
- enable_debug_on_named_expressions - flag to auto-enable debug on all subsequent
calls to ParserElement.setName()
-"""
-__diag__.warn_multiple_tokens_in_named_alternation = False
-__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
+ """
+ _type_desc = "diagnostic"
+
+ warn_multiple_tokens_in_named_alternation = False
+ warn_ungrouped_named_tokens_in_collection = False
+ warn_name_set_on_empty_Forward = False
+ warn_on_multiple_string_args_to_oneof = False
+ enable_debug_on_named_expressions = False
+
+ _all_names = [__ for __ in locals() if not __.startswith('_')]
+ _warning_names = [name for name in _all_names if name.startswith("warn")]
+ _debug_names = [name for name in _all_names if name.startswith("enable_debug")]
+
+ @classmethod
+ def enable_all_warnings(cls):
+ for name in cls._warning_names:
+ cls.enable(name)
+
+# hide abstract class
+del __config_flags
+
# ~ sys.stderr.write("testing pyparsing module, version %s, %s\n" % (__version__, __versionTime__))
diff --git a/unitTests.py b/unitTests.py
index 8601be1..a081480 100644
--- a/unitTests.py
+++ b/unitTests.py
@@ -4356,6 +4356,17 @@ class IndentedBlockScanTest(ParseTestCase):
self.assertEqual(len(r6), 1)
+class InvalidDiagSettingTest(ParseTestCase):
+ def runTest(self):
+ import pyparsing as pp
+
+ with self.assertRaises(ValueError, msg="failed to raise exception when setting non-existent __diag__"):
+ pp.__diag__.enable("xyzzy")
+
+ with self.assertWarns(UserWarning, msg="failed to warn disabling 'collect_all_And_tokens"):
+ pp.__compat__.disable("collect_all_And_tokens")
+
+
class ParseResultsWithNameMatchFirst(ParseTestCase):
def runTest(self):
import pyparsing as pp
@@ -4370,9 +4381,10 @@ class ParseResultsWithNameMatchFirst(ParseTestCase):
self.assertEqual(list(expr.parseString('the bird')['rexp']), 'the bird'.split())
# test compatibility mode, no longer restoring pre-2.3.1 behavior
- with AutoReset(pp.__compat__, "collect_all_And_tokens"):
+ with AutoReset(pp.__compat__, "collect_all_And_tokens"), \
+ AutoReset(pp.__diag__, "warn_multiple_tokens_in_named_alternation"):
pp.__compat__.collect_all_And_tokens = False
- pp.__diag__.warn_multiple_tokens_in_named_alternation = True
+ pp.__diag__.enable("warn_multiple_tokens_in_named_alternation")
expr_a = pp.Literal('not') + pp.Literal('the') + pp.Literal('bird')
expr_b = pp.Literal('the') + pp.Literal('bird')
if PY_3:
@@ -4410,9 +4422,10 @@ class ParseResultsWithNameOr(ParseTestCase):
self.assertEqual(list(expr.parseString('the bird')['rexp']), 'the bird'.split())
# test compatibility mode, no longer restoring pre-2.3.1 behavior
- with AutoReset(pp.__compat__, "collect_all_And_tokens"):
+ with AutoReset(pp.__compat__, "collect_all_And_tokens"), \
+ AutoReset(pp.__diag__, "warn_multiple_tokens_in_named_alternation"):
pp.__compat__.collect_all_And_tokens = False
- pp.__diag__.warn_multiple_tokens_in_named_alternation = True
+ pp.__diag__.enable("warn_multiple_tokens_in_named_alternation")
expr_a = pp.Literal('not') + pp.Literal('the') + pp.Literal('bird')
expr_b = pp.Literal('the') + pp.Literal('bird')
if PY_3:
@@ -4562,16 +4575,17 @@ class WarnUngroupedNamedTokensTest(ParseTestCase):
import pyparsing as pp
ppc = pp.pyparsing_common
- pp.__diag__.warn_ungrouped_named_tokens_in_collection = True
+ with AutoReset(pp.__diag__, "warn_ungrouped_named_tokens_in_collection"):
+ pp.__diag__.enable("warn_ungrouped_named_tokens_in_collection")
- COMMA = pp.Suppress(',').setName("comma")
- coord = (ppc.integer('x') + COMMA + ppc.integer('y'))
+ COMMA = pp.Suppress(',').setName("comma")
+ coord = (ppc.integer('x') + COMMA + ppc.integer('y'))
- # this should emit a warning
- if PY_3:
- with self.assertWarns(UserWarning, msg="failed to warn with named repetition of"
- " ungrouped named expressions"):
- path = coord[...].setResultsName('path')
+ # this should emit a warning
+ if PY_3:
+ with self.assertWarns(UserWarning, msg="failed to warn with named repetition of"
+ " ungrouped named expressions"):
+ path = coord[...].setResultsName('path')
class WarnNameSetOnEmptyForwardTest(ParseTestCase):
@@ -4582,13 +4596,14 @@ class WarnNameSetOnEmptyForwardTest(ParseTestCase):
def runTest(self):
import pyparsing as pp
- pp.__diag__.warn_name_set_on_empty_Forward = True
+ with AutoReset(pp.__diag__, "warn_name_set_on_empty_Forward"):
+ pp.__diag__.enable("warn_name_set_on_empty_Forward")
- base = pp.Forward()
+ base = pp.Forward()
- if PY_3:
- with self.assertWarns(UserWarning, msg="failed to warn when naming an empty Forward expression"):
- base("x")
+ if PY_3:
+ with self.assertWarns(UserWarning, msg="failed to warn when naming an empty Forward expression"):
+ base("x")
class WarnOnMultipleStringArgsToOneOfTest(ParseTestCase):
@@ -4599,11 +4614,12 @@ class WarnOnMultipleStringArgsToOneOfTest(ParseTestCase):
def runTest(self):
import pyparsing as pp
- pp.__diag__.warn_on_multiple_string_args_to_oneof = True
+ with AutoReset(pp.__diag__, "warn_on_multiple_string_args_to_oneof"):
+ pp.__diag__.enable("warn_on_multiple_string_args_to_oneof")
- if PY_3:
- with self.assertWarns(UserWarning, msg="failed to warn when incorrectly calling oneOf(string, string)"):
- a = pp.oneOf('A', 'B')
+ if PY_3:
+ with self.assertWarns(UserWarning, msg="failed to warn when incorrectly calling oneOf(string, string)"):
+ a = pp.oneOf('A', 'B')
class EnableDebugOnNamedExpressionsTest(ParseTestCase):
@@ -4615,33 +4631,34 @@ class EnableDebugOnNamedExpressionsTest(ParseTestCase):
import pyparsing as pp
import textwrap
- test_stdout = StringIO()
-
- with AutoReset(sys, 'stdout', 'stderr'):
- sys.stdout = test_stdout
- sys.stderr = test_stdout
-
- pp.__diag__.enable_debug_on_named_expressions = True
- integer = pp.Word(pp.nums).setName('integer')
+ with AutoReset(pp.__diag__, "enable_debug_on_named_expressions"):
+ test_stdout = StringIO()
- integer[...].parseString("1 2 3")
-
- expected_debug_output = textwrap.dedent("""\
- Match integer at loc 0(1,1)
- Matched integer -> ['1']
- Match integer at loc 1(1,2)
- Matched integer -> ['2']
- Match integer at loc 3(1,4)
- Matched integer -> ['3']
- Match integer at loc 5(1,6)
- Exception raised:Expected integer, found end of text (at char 5), (line:1, col:6)
- """)
- output = test_stdout.getvalue()
- print_(output)
- self.assertEquals(output,
- expected_debug_output,
- "failed to auto-enable debug on named expressions "
- "using enable_debug_on_named_expressions")
+ with AutoReset(sys, 'stdout', 'stderr'):
+ sys.stdout = test_stdout
+ sys.stderr = test_stdout
+
+ pp.__diag__.enable("enable_debug_on_named_expressions")
+ integer = pp.Word(pp.nums).setName('integer')
+
+ integer[...].parseString("1 2 3")
+
+ expected_debug_output = textwrap.dedent("""\
+ Match integer at loc 0(1,1)
+ Matched integer -> ['1']
+ Match integer at loc 1(1,2)
+ Matched integer -> ['2']
+ Match integer at loc 3(1,4)
+ Matched integer -> ['3']
+ Match integer at loc 5(1,6)
+ Exception raised:Expected integer, found end of text (at char 5), (line:1, col:6)
+ """)
+ output = test_stdout.getvalue()
+ print_(output)
+ self.assertEquals(output,
+ expected_debug_output,
+ "failed to auto-enable debug on named expressions "
+ "using enable_debug_on_named_expressions")
class UndesirableButCommonPracticesTest(ParseTestCase):
@@ -4671,6 +4688,42 @@ class UndesirableButCommonPracticesTest(ParseTestCase):
abc
""")
+class EnableWarnDiagsTest(ParseTestCase):
+ def runTest(self):
+ import pyparsing as pp
+ import pprint
+
+ def filtered_vars(var_dict):
+ dunders = [nm for nm in var_dict if nm.startswith('__')]
+ return {k: v for k, v in var_dict.items()
+ if isinstance(v, bool) and k not in dunders}
+
+ pprint.pprint(filtered_vars(vars(pp.__diag__)), width=30)
+
+ warn_names = pp.__diag__._warning_names
+ other_names = pp.__diag__._debug_names
+
+ # make sure they are off by default
+ for diag_name in warn_names:
+ self.assertFalse(getattr(pp.__diag__, diag_name), "__diag__.{} not set to True".format(diag_name))
+
+ with AutoReset(pp.__diag__, *warn_names):
+ # enable all warn_* diag_names
+ pp.__diag__.enable_all_warnings()
+ pprint.pprint(filtered_vars(vars(pp.__diag__)), width=30)
+
+ # make sure they are on after being enabled
+ for diag_name in warn_names:
+ self.assertTrue(getattr(pp.__diag__, diag_name), "__diag__.{} not set to True".format(diag_name))
+
+ # non-warn diag_names must be enabled individually
+ for diag_name in other_names:
+ self.assertFalse(getattr(pp.__diag__, diag_name), "__diag__.{} not set to True".format(diag_name))
+
+ # make sure they are off after AutoReset
+ for diag_name in warn_names:
+ self.assertFalse(getattr(pp.__diag__, diag_name), "__diag__.{} not set to True".format(diag_name))
+
class MiscellaneousParserTests(ParseTestCase):
def runTest(self):