Skip to content

Commit

Permalink
Add warning suppression detection for all diagnostic warnings
Browse files Browse the repository at this point in the history
  • Loading branch information
ptmcg committed Nov 12, 2021
1 parent b429eb6 commit 16b766b
Show file tree
Hide file tree
Showing 4 changed files with 107 additions and 21 deletions.
6 changes: 4 additions & 2 deletions CHANGES
Expand Up @@ -4,8 +4,10 @@ Change Log

Version 3.0.6 -
---------------
- Refactored warning suppression code to preserve internal results names,
which, while undocumented, had been adopted by some projects.
- 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.
Expand Down
2 changes: 1 addition & 1 deletion pyparsing/__init__.py
Expand Up @@ -126,7 +126,7 @@ def __repr__(self):


__version_info__ = version_info(3, 0, 6, "final", 0)
__version_time__ = "12 Nov 2021 13:44 UTC"
__version_time__ = "12 Nov 2021 16:06 UTC"
__version__ = __version_info__.__version__
__versionTime__ = __version_time__
__author__ = "Paul McGuire <ptmcg.gm+pyparsing@gmail.com>"
Expand Down
59 changes: 52 additions & 7 deletions pyparsing/core.py
Expand Up @@ -471,6 +471,18 @@ def __init__(self, savelist: bool = 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

Expand Down Expand Up @@ -3987,8 +3999,17 @@ def _generateDefaultName(self):
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, "
Expand Down Expand Up @@ -4087,8 +4108,17 @@ def _generateDefaultName(self):
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, "
Expand Down Expand Up @@ -5103,6 +5133,8 @@ def __or__(self, other):
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 '<<='",
Expand All @@ -5113,7 +5145,11 @@ def __or__(self, other):

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,
Expand All @@ -5122,7 +5158,12 @@ def __del__(self):
)

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",
Expand Down Expand Up @@ -5259,7 +5300,11 @@ def copy(self):
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 "
Expand Down
61 changes: 50 additions & 11 deletions tests/test_unit.py
Expand Up @@ -114,13 +114,13 @@ def assertRaises(self, expected_exception_type: Any, msg: Any = None):

@contextlib.contextmanager
def assertDoesNotWarn(self, warning_type: type = UserWarning, msg: str = None):
if msg is None:
msg = "unexpected warning raised"
with warnings.catch_warnings(record=True) as w:
warnings.simplefilter("error")
try:
yield
except Exception as e:
if msg is None:
msg = "unexpected warning {} raised".format(e)
if isinstance(e, warning_type):
self.fail("{}: {}".format(msg, e))
else:
Expand Down Expand Up @@ -180,7 +180,7 @@ def runTest(self):
# test assertDoesNotWarn raises an AssertionError
with self.assertRaises(AssertionError):
with self.assertDoesNotWarn(
msg="failed to warn when naming an empty Forward expression",
msg="warned when parsing with an empty Forward expression warning was suppressed",
):
base = pp.Forward()
try:
Expand Down Expand Up @@ -7409,6 +7409,11 @@ def testParseResultsWithNameMatchFirst(self):
):
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
Expand Down Expand Up @@ -7470,6 +7475,11 @@ def testParseResultsWithNameOr(self):
):
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
Expand Down Expand Up @@ -7641,17 +7651,19 @@ def testWarnUngroupedNamedTokens(self):
):
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
)
):
Expand Down Expand Up @@ -7681,6 +7693,12 @@ def testWarnNameSetOnEmptyForward(self):
):
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
Expand All @@ -7705,13 +7723,23 @@ def testWarnParsingEmptyForward(self):

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:
pass

def testWarnIncorrectAssignmentToForward(self):
"""
- warn_on_parse_using_empty_Forward - flag to enable warnings when a Forward
Expand Down Expand Up @@ -7741,6 +7769,17 @@ def a_method():
):
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
Expand Down

0 comments on commit 16b766b

Please sign in to comment.