diff options
author | Devin J. Pohly <djpohly@gmail.com> | 2022-06-17 15:00:11 -0500 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-06-17 15:00:11 -0500 |
commit | 51675ea2b9da964dbfc600a76fa88e1f2e8d988e (patch) | |
tree | 023367180fe033ddcc6e25c965884c7515a3edcd | |
parent | cbbbbc9022c179c8d710c7009b0dd587eeb338d4 (diff) | |
download | pyparsing-git-51675ea2b9da964dbfc600a76fa88e1f2e8d988e.tar.gz |
Explicitly declare compatibility alias functions (#414)
This allows static type checkers to find and check these functions
correctly, and it removes the need to fiddle around with stack frames to
get the aliases defined.
-rw-r--r-- | pyparsing/core.py | 159 | ||||
-rw-r--r-- | pyparsing/exceptions.py | 8 | ||||
-rw-r--r-- | pyparsing/helpers.py | 52 | ||||
-rw-r--r-- | pyparsing/util.py | 74 |
4 files changed, 203 insertions, 90 deletions
diff --git a/pyparsing/core.py b/pyparsing/core.py index 50bfc9b..6b4ab71 100644 --- a/pyparsing/core.py +++ b/pyparsing/core.py @@ -41,12 +41,12 @@ from .util import ( _flatten, LRUMemo as _LRUMemo, UnboundedMemo as _UnboundedMemo, + replaced_by_pep8, ) from .exceptions import * from .actions import * from .results import ParseResults, _ParseResultsWithOffset from .unicode import pyparsing_unicode -from .util import replaces_prePEP8_function _MAX_INT = sys.maxsize str_type: Tuple[type, ...] = (str, bytes) @@ -324,7 +324,6 @@ def _trim_arity(func, max_limit=3): return wrapper -@replaces_prePEP8_function("conditionAsParseAction") def condition_as_parse_action( fn: ParseCondition, message: str = None, fatal: bool = False ) -> ParseAction: @@ -401,7 +400,6 @@ class ParserElement(ABC): _literalStringClass: type = None # type: ignore[assignment] @staticmethod - @replaces_prePEP8_function("setDefaultWhitespaceChars") def set_default_whitespace_chars(chars: str) -> None: r""" Overrides the default whitespace chars @@ -423,7 +421,6 @@ class ParserElement(ABC): expr.whiteChars = set(chars) @staticmethod - @replaces_prePEP8_function("inlineLiteralsUsing") def inline_literals_using(cls: type) -> None: """ Set class to be used for inclusion of string literals into a parser. @@ -523,7 +520,6 @@ class ParserElement(ABC): cpy.whiteChars = set(ParserElement.DEFAULT_WHITE_CHARS) return cpy - @replaces_prePEP8_function("setResultsName") def set_results_name( self, name: str, list_all_matches: bool = False, *, listAllMatches: bool = False ) -> "ParserElement": @@ -568,7 +564,6 @@ class ParserElement(ABC): newself.modalResults = not listAllMatches return newself - @replaces_prePEP8_function("setBreak") def set_break(self, break_flag: bool = True) -> "ParserElement": """ Method to invoke the Python pdb debugger when this element is @@ -592,7 +587,6 @@ class ParserElement(ABC): self._parse = self._parse._originalParseMethod # type: ignore [attr-defined, assignment] return self - @replaces_prePEP8_function("setParseAction") def set_parse_action(self, *fns: ParseAction, **kwargs) -> "ParserElement": """ Define one or more actions to perform when successfully matching parse element definition. @@ -679,7 +673,6 @@ class ParserElement(ABC): ) return self - @replaces_prePEP8_function("addParseAction") def add_parse_action(self, *fns: ParseAction, **kwargs) -> "ParserElement": """ Add one or more parse actions to expression's list of parse actions. See :class:`set_parse_action`. @@ -692,7 +685,6 @@ class ParserElement(ABC): ) return self - @replaces_prePEP8_function("addCondition") def add_condition(self, *fns: ParseCondition, **kwargs) -> "ParserElement": """Add a boolean predicate function to expression's list of parse actions. See :class:`set_parse_action` for function call signatures. Unlike ``set_parse_action``, @@ -728,7 +720,6 @@ class ParserElement(ABC): ) return self - @replaces_prePEP8_function("setFailAction") def set_fail_action(self, fn: ParseFailAction) -> "ParserElement": """ Define action to perform if parsing fails at this expression. @@ -879,7 +870,6 @@ class ParserElement(ABC): return loc, ret_tokens - @replaces_prePEP8_function("tryParse") def try_parse(self, instring: str, loc: int, raise_fatal: bool = False) -> int: try: return self._parse(instring, loc, doActions=False)[0] @@ -988,7 +978,6 @@ class ParserElement(ABC): ParserElement._parse = ParserElement._parseNoCache @staticmethod - @replaces_prePEP8_function("enableLeftRecursion") def enable_left_recursion( cache_size_limit: typing.Optional[int] = None, *, force=False ) -> None: @@ -1037,7 +1026,6 @@ class ParserElement(ABC): ParserElement._left_recursion_enabled = True @staticmethod - @replaces_prePEP8_function("enablePackrat") def enable_packrat(cache_size_limit: int = 128, *, force: bool = False) -> None: """ Enables "packrat" parsing, which adds memoizing to the parsing logic. @@ -1081,7 +1069,6 @@ class ParserElement(ABC): ParserElement.packrat_cache = _FifoCache(cache_size_limit) ParserElement._parse = ParserElement._parseCache - @replaces_prePEP8_function("parseString") def parse_string( self, instring: str, parse_all: bool = False, *, parseAll: bool = False ) -> ParseResults: @@ -1151,7 +1138,6 @@ class ParserElement(ABC): else: return tokens - @replaces_prePEP8_function("scanString") def scan_string( self, instring: str, @@ -1241,7 +1227,6 @@ class ParserElement(ABC): # catch and re-raise exception from here, clears out pyparsing internal stack trace raise exc.with_traceback(None) - @replaces_prePEP8_function("transformString") def transform_string(self, instring: str, *, debug: bool = False) -> str: """ Extension to :class:`scan_string`, to modify matching text with modified tokens that may @@ -1288,7 +1273,6 @@ class ParserElement(ABC): # catch and re-raise exception from here, clears out pyparsing internal stack trace raise exc.with_traceback(None) - @replaces_prePEP8_function("searchString") def search_string( self, instring: str, @@ -1696,7 +1680,6 @@ class ParserElement(ABC): """ return Suppress(self) - @replaces_prePEP8_function("ignoreWhitespace") def ignore_whitespace(self, recursive: bool = True) -> "ParserElement": """ Enables the skipping of whitespace before matching the characters in the @@ -1707,7 +1690,6 @@ class ParserElement(ABC): self.skipWhitespace = True return self - @replaces_prePEP8_function("leaveWhitespace") def leave_whitespace(self, recursive: bool = True) -> "ParserElement": """ Disables the skipping of whitespace before matching the characters in the @@ -1719,7 +1701,6 @@ class ParserElement(ABC): self.skipWhitespace = False return self - @replaces_prePEP8_function("setWhitespaceChars") def set_whitespace_chars( self, chars: Union[Set[str], str], copy_defaults: bool = False ) -> "ParserElement": @@ -1731,7 +1712,6 @@ class ParserElement(ABC): self.copyDefaultWhiteChars = copy_defaults return self - @replaces_prePEP8_function("parseWithTabs") def parse_with_tabs(self) -> "ParserElement": """ Overrides default behavior to expand ``<TAB>`` s to spaces before parsing the input string. @@ -1769,7 +1749,6 @@ class ParserElement(ABC): self.ignoreExprs.append(Suppress(other.copy())) return self - @replaces_prePEP8_function("setDebugActions") def set_debug_actions( self, start_action: DebugStartAction, @@ -1796,7 +1775,6 @@ class ParserElement(ABC): self.debug = True return self - @replaces_prePEP8_function("setDebug") def set_debug(self, flag: bool = True) -> "ParserElement": """ Enable display of debugging messages while doing pattern matching. @@ -1856,7 +1834,6 @@ class ParserElement(ABC): Child classes must define this method, which defines how the ``default_name`` is set. """ - @replaces_prePEP8_function("setName") def set_name(self, name: str) -> "ParserElement": """ Define name for this expression, makes debugging and exception messages clearer. @@ -1902,7 +1879,6 @@ class ParserElement(ABC): """ self._checkRecursion([]) - @replaces_prePEP8_function("parseFile") def parse_file( self, file_or_filename: Union[str, Path, TextIO], @@ -1967,7 +1943,6 @@ class ParserElement(ABC): except ParseBaseException: return False - @replaces_prePEP8_function("runTests") def run_tests( self, tests: Union[str, List[str]], @@ -2217,9 +2192,88 @@ class ParserElement(ABC): # we were passed a file-like object, just write to it output_html.write(railroad_to_html(railroad, embed=embed)) + # Compatibility synonyms + # fmt: off + @staticmethod + @replaced_by_pep8(inline_literals_using) + def inlineLiteralsUsing(): ... + + @staticmethod + @replaced_by_pep8(set_default_whitespace_chars) + def setDefaultWhitespaceChars(): ... + + @replaced_by_pep8(set_results_name) + def setResultsName(self): ... + + @replaced_by_pep8(set_break) + def setBreak(self): ... + + @replaced_by_pep8(set_parse_action) + def setParseAction(self): ... + + @replaced_by_pep8(add_parse_action) + def addParseAction(self): ... + + @replaced_by_pep8(add_condition) + def addCondition(self): ... + + @replaced_by_pep8(set_fail_action) + def setFailAction(self): ... + + @replaced_by_pep8(try_parse) + def tryParse(self): ... + + @staticmethod + @replaced_by_pep8(enable_left_recursion) + def enableLeftRecursion(): ... + + @staticmethod + @replaced_by_pep8(enable_packrat) + def enablePackrat(): ... + + @replaced_by_pep8(parse_string) + def parseString(self): ... + + @replaced_by_pep8(scan_string) + def scanString(self): ... + + @replaced_by_pep8(transform_string) + def transformString(self): ... + + @replaced_by_pep8(search_string) + def searchString(self): ... + + @replaced_by_pep8(ignore_whitespace) + def ignoreWhitespace(self): ... + + @replaced_by_pep8(leave_whitespace) + def leaveWhitespace(self): ... + + @replaced_by_pep8(set_whitespace_chars) + def setWhitespaceChars(self): ... + + @replaced_by_pep8(parse_with_tabs) + def parseWithTabs(self): ... + + @replaced_by_pep8(set_debug_actions) + def setDebugActions(self): ... + + @replaced_by_pep8(set_debug) + def setDebug(self): ... + + @replaced_by_pep8(set_name) + def setName(self): ... + + @replaced_by_pep8(parse_file) + def parseFile(self): ... + + @replaced_by_pep8(run_tests) + def runTests(self): ... + canParseNext = can_parse_next resetCache = reset_cache defaultName = default_name + # fmt: on class _PendingSkip(ParserElement): @@ -3650,7 +3704,6 @@ class ParseExpression(ParserElement): self._defaultName = None return self - @replaces_prePEP8_function("leaveWhitespace") def leave_whitespace(self, recursive: bool = True) -> ParserElement: """ Extends ``leave_whitespace`` defined in base class, and also invokes ``leave_whitespace`` on @@ -3664,7 +3717,6 @@ class ParseExpression(ParserElement): e.leave_whitespace(recursive) return self - @replaces_prePEP8_function("ignoreWhitespace") def ignore_whitespace(self, recursive: bool = True) -> ParserElement: """ Extends ``ignore_whitespace`` defined in base class, and also invokes ``leave_whitespace`` on @@ -3771,6 +3823,16 @@ class ParseExpression(ParserElement): return super()._setResultsName(name, listAllMatches) + # Compatibility synonyms + # fmt: off + @replaced_by_pep8(leave_whitespace) + def leaveWhitespace(self): ... + + @replaced_by_pep8(ignore_whitespace) + def ignoreWhitespace(self): ... + # fmt: on + + class And(ParseExpression): """ @@ -4397,7 +4459,6 @@ class ParseElementEnhance(ParserElement): else: raise ParseException(instring, loc, "No expression defined", self) - @replaces_prePEP8_function("leaveWhitespace") def leave_whitespace(self, recursive: bool = True) -> ParserElement: super().leave_whitespace(recursive) @@ -4407,7 +4468,6 @@ class ParseElementEnhance(ParserElement): self.expr.leave_whitespace(recursive) return self - @replaces_prePEP8_function("ignoreWhitespace") def ignore_whitespace(self, recursive: bool = True) -> ParserElement: super().ignore_whitespace(recursive) @@ -4453,6 +4513,16 @@ class ParseElementEnhance(ParserElement): def _generateDefaultName(self) -> str: return f"{self.__class__.__name__}:({str(self.expr)})" + # Compatibility synonyms + # fmt: off + @replaced_by_pep8(leave_whitespace) + def leaveWhitespace(self): ... + + @replaced_by_pep8(ignore_whitespace) + def ignoreWhitespace(self): ... + # fmt: on + + class IndentedBlock(ParseElementEnhance): """ @@ -5316,12 +5386,10 @@ class Forward(ParseElementEnhance): raise prev_loc, prev_peek = memo[peek_key] = new_loc, new_peek - @replaces_prePEP8_function("leaveWhitespace") def leave_whitespace(self, recursive: bool = True) -> ParserElement: self.skipWhitespace = False return self - @replaces_prePEP8_function("ignoreWhitespace") def ignore_whitespace(self, recursive: bool = True) -> ParserElement: self.skipWhitespace = True return self @@ -5382,6 +5450,16 @@ class Forward(ParseElementEnhance): return super()._setResultsName(name, list_all_matches) + # Compatibility synonyms + # fmt: off + @replaced_by_pep8(leave_whitespace) + def leaveWhitespace(self): ... + + @replaced_by_pep8(ignore_whitespace) + def ignoreWhitespace(self): ... + # fmt: on + + class TokenConverter(ParseElementEnhance): """ @@ -5739,7 +5817,6 @@ def srange(s: str) -> str: return "" -@replaces_prePEP8_function("tokenMap") def token_map(func, *args) -> ParseAction: """Helper to define a parse action by mapping a function to all elements of a :class:`ParseResults` list. If any additional args are passed, @@ -5822,7 +5899,7 @@ _builtin_exprs: List[ParserElement] = [ ] # backward compatibility names -nullDebugAction = null_debug_action +# fmt: off sglQuotedString = sgl_quoted_string dblQuotedString = dbl_quoted_string quotedString = quoted_string @@ -5831,4 +5908,16 @@ lineStart = line_start lineEnd = line_end stringStart = string_start stringEnd = string_end -traceParseAction = trace_parse_action + +@replaced_by_pep8(null_debug_action) +def nullDebugAction(): ... + +@replaced_by_pep8(trace_parse_action) +def traceParseAction(): ... + +@replaced_by_pep8(condition_as_parse_action) +def conditionAsParseAction(): ... + +@replaced_by_pep8(token_map) +def tokenMap(): ... +# fmt: on diff --git a/pyparsing/exceptions.py b/pyparsing/exceptions.py index caefdff..ed88ad2 100644 --- a/pyparsing/exceptions.py +++ b/pyparsing/exceptions.py @@ -9,7 +9,7 @@ from .util import ( line, lineno, _collapse_string_to_ranges, - replaces_prePEP8_function, + replaced_by_pep8, ) from .unicode import pyparsing_unicode as ppu @@ -163,7 +163,6 @@ class ParseBaseException(Exception): def __repr__(self): return str(self) - @replaces_prePEP8_function("markInputline") def mark_input_line(self, marker_string: str = None, *, markerString=">!<") -> str: """ Extracts the exception line from the input string, and marks @@ -217,6 +216,11 @@ class ParseBaseException(Exception): """ return self.explain_exception(self, depth) + # fmt: off + @replaced_by_pep8(mark_input_line) + def markInputline(self): ... + # fmt: on + class ParseException(ParseBaseException): """ diff --git a/pyparsing/helpers.py b/pyparsing/helpers.py index 1b48cfc..e4f2b93 100644 --- a/pyparsing/helpers.py +++ b/pyparsing/helpers.py @@ -9,14 +9,13 @@ from .util import ( _bslash, _flatten, _escape_regex_range_chars, - replaces_prePEP8_function, + replaced_by_pep8, ) # # global helpers # -@replaces_prePEP8_function("delimitedList") def delimited_list( expr: Union[str, ParserElement], delim: Union[str, ParserElement] = ",", @@ -72,7 +71,6 @@ def delimited_list( return delimited_list_expr.set_name(dlName) -@replaces_prePEP8_function("countedArray") def counted_array( expr: ParserElement, int_expr: typing.Optional[ParserElement] = None, @@ -133,7 +131,6 @@ def counted_array( return (intExpr + array_expr).set_name("(len) " + str(expr) + "...") -@replaces_prePEP8_function("matchPreviousLiteral") def match_previous_literal(expr: ParserElement) -> ParserElement: """Helper to define an expression that is indirectly defined from the tokens matched in a previous expression, that is, it looks for @@ -167,7 +164,6 @@ def match_previous_literal(expr: ParserElement) -> ParserElement: return rep -@replaces_prePEP8_function("matchPreviousExpr") def match_previous_expr(expr: ParserElement) -> ParserElement: """Helper to define an expression that is indirectly defined from the tokens matched in a previous expression, that is, it looks for @@ -204,7 +200,6 @@ def match_previous_expr(expr: ParserElement) -> ParserElement: return rep -@replaces_prePEP8_function("oneOf") def one_of( strs: Union[typing.Iterable[str], str], caseless: bool = False, @@ -330,7 +325,6 @@ def one_of( ) -@replaces_prePEP8_function("dictOf") def dict_of(key: ParserElement, value: ParserElement) -> ParserElement: """Helper to easily and clearly define a dictionary by specifying the respective patterns for the key and value. Takes care of @@ -371,7 +365,6 @@ def dict_of(key: ParserElement, value: ParserElement) -> ParserElement: return Dict(OneOrMore(Group(key + value))) -@replaces_prePEP8_function("originalTextFor") def original_text_for( expr: ParserElement, as_string: bool = True, *, asString: bool = True ) -> ParserElement: @@ -467,7 +460,6 @@ def locatedExpr(expr: ParserElement) -> ParserElement: ) -@replaces_prePEP8_function("nestedExpr") def nested_expr( opener: Union[str, ParserElement] = "(", closer: Union[str, ParserElement] = ")", @@ -655,7 +647,6 @@ def _makeTags(tagStr, xml, suppress_LT=Suppress("<"), suppress_GT=Suppress(">")) return openTag, closeTag -@replaces_prePEP8_function("makeHTMLTags") def make_html_tags( tag_str: Union[str, ParserElement] ) -> Tuple[ParserElement, ParserElement]: @@ -683,7 +674,6 @@ def make_html_tags( return _makeTags(tag_str, False) -@replaces_prePEP8_function("makeXMLTags") def make_xml_tags( tag_str: Union[str, ParserElement] ) -> Tuple[ParserElement, ParserElement]: @@ -707,7 +697,6 @@ common_html_entity = Regex("&(?P<entity>" + "|".join(_htmlEntityMap) + ");").set ) -@replaces_prePEP8_function("replaceHTMLEntity") def replace_html_entity(s, l, t): """Helper parser action to replace common HTML entities with their special characters""" return _htmlEntityMap.get(t.entity) @@ -739,7 +728,6 @@ InfixNotationOperatorSpec = Union[ ] -@replaces_prePEP8_function("infixNotation") def infix_notation( base_expr: ParserElement, op_list: List[InfixNotationOperatorSpec], @@ -1089,6 +1077,7 @@ _builtin_exprs: List[ParserElement] = [ # pre-PEP8 compatible names +# fmt: off opAssoc = OpAssoc anyOpenTag = any_open_tag anyCloseTag = any_close_tag @@ -1100,3 +1089,40 @@ dblSlashComment = dbl_slash_comment cppStyleComment = cpp_style_comment javaStyleComment = java_style_comment pythonStyleComment = python_style_comment + +@replaced_by_pep8(delimited_list) +def delimitedList(): ... + +@replaced_by_pep8(counted_array) +def countedArray(): ... + +@replaced_by_pep8(match_previous_literal) +def matchPreviousLiteral(): ... + +@replaced_by_pep8(match_previous_expr) +def matchPreviousExpr(): ... + +@replaced_by_pep8(one_of) +def oneOf(): ... + +@replaced_by_pep8(dict_of) +def dictOf(): ... + +@replaced_by_pep8(original_text_for) +def originalTextFor(): ... + +@replaced_by_pep8(nested_expr) +def nestedExpr(): ... + +@replaced_by_pep8(make_html_tags) +def makeHTMLTags(): ... + +@replaced_by_pep8(make_xml_tags) +def makeXMLTags(): ... + +@replaced_by_pep8(replace_html_entity) +def replaceHTMLEntity(): ... + +@replaced_by_pep8(infix_notation) +def infixNotation(): ... +# fmt: on diff --git a/pyparsing/util.py b/pyparsing/util.py index 43bacda..efeccd3 100644 --- a/pyparsing/util.py +++ b/pyparsing/util.py @@ -5,9 +5,10 @@ import types import collections import itertools from functools import lru_cache, wraps -from typing import List, Union, Iterable +from typing import Callable, List, Union, Iterable, TypeVar, cast _bslash = chr(92) +C = TypeVar("C", bound=Callable) class __config_flags: @@ -229,50 +230,43 @@ def _flatten(ll: list) -> list: ret.append(i) return ret +def _make_synonym_function(compat_name: str, fn: C) -> C: + # In a future version, uncomment the code in the internal _inner() functions + # to begin emitting DeprecationWarnings. -def replaces_prePEP8_function(old_name): - """ - Decorator for new PEP8 functions, to define compatibility synonym using given old name. - - In a future version, uncomment the code in the internal _inner() functions to begin - emitting DeprecationWarnings. - - (Presence of 'self' arg in signature is used by explain_exception() methods, so we take - some extra steps to add it if present in decorated function.) - """ - - def make_synonym_function(compat_name, fn): - if "self" == list(inspect.signature(fn).parameters)[0]: + # Unwrap staticmethod/classmethod + fn = getattr(fn, "__func__", fn) - @wraps(fn) - def _inner(self, *args, **kwargs): - # warnings.warn( - # f"Deprecated - use {fn.__name__}", DeprecationWarning, stacklevel=3 - # ) - return fn(self, *args, **kwargs) + # (Presence of 'self' arg in signature is used by explain_exception() methods, so we take + # some extra steps to add it if present in decorated function.) + if "self" == list(inspect.signature(fn).parameters)[0]: - else: - - @wraps(fn) - def _inner(*args, **kwargs): - # warnings.warn( - # f"Deprecated - use {fn.__name__}", DeprecationWarning, stacklevel=3 - # ) - return fn(*args, **kwargs) + @wraps(fn) + def _inner(self, *args, **kwargs): + # warnings.warn( + # f"Deprecated - use {fn.__name__}", DeprecationWarning, stacklevel=3 + # ) + return fn(self, *args, **kwargs) - _inner.__doc__ = f"""Deprecated - use :class:`{fn.__name__}`""" - _inner.__name__ = compat_name - _inner.__annotations__ = fn.__annotations__ - _inner.__kwdefaults__ = fn.__kwdefaults__ - _inner.__qualname__ = fn.__qualname__ - return _inner + else: - ns = inspect.currentframe().f_back.f_locals + @wraps(fn) + def _inner(*args, **kwargs): + # warnings.warn( + # f"Deprecated - use {fn.__name__}", DeprecationWarning, stacklevel=3 + # ) + return fn(*args, **kwargs) - def _inner(fn): - # define synonym and add to containing namespace - ns[old_name] = make_synonym_function(old_name, fn) + _inner.__doc__ = f"""Deprecated - use :class:`{fn.__name__}`""" + _inner.__name__ = compat_name + _inner.__annotations__ = fn.__annotations__ + _inner.__kwdefaults__ = fn.__kwdefaults__ + _inner.__qualname__ = fn.__qualname__ + return cast(C, _inner) - return fn - return _inner +def replaced_by_pep8(fn: C) -> Callable[[Callable], C]: + """ + Decorator for pre-PEP8 compatibility synonyms, to link them to the new function. + """ + return lambda other: _make_synonym_function(other.__name__, fn) |