diff options
author | ptmcg <ptmcg@austin.rr.com> | 2022-07-14 03:35:10 -0500 |
---|---|---|
committer | ptmcg <ptmcg@austin.rr.com> | 2022-07-14 03:35:10 -0500 |
commit | 06c12f685b5d0e29f01abc842dff8d268d93ffbf (patch) | |
tree | 720b0faafd07b602965918c88dc981958dba50ee | |
parent | c74949d0cafbe7c136a54777b9b340c37f00b484 (diff) | |
download | pyparsing-git-06c12f685b5d0e29f01abc842dff8d268d93ffbf.tar.gz |
Add type annotations
-rw-r--r-- | pyparsing/__init__.py | 23 | ||||
-rw-r--r-- | pyparsing/core.py | 100 | ||||
-rw-r--r-- | pyparsing/diagram/__init__.py | 1 | ||||
-rw-r--r-- | pyparsing/exceptions.py | 6 | ||||
-rw-r--r-- | pyparsing/helpers.py | 3 | ||||
-rw-r--r-- | pyparsing/results.py | 12 | ||||
-rw-r--r-- | pyparsing/testing.py | 12 |
7 files changed, 107 insertions, 50 deletions
diff --git a/pyparsing/__init__.py b/pyparsing/__init__.py index b7e09ce..0c0321d 100644 --- a/pyparsing/__init__.py +++ b/pyparsing/__init__.py @@ -105,10 +105,13 @@ class version_info(NamedTuple): @property def __version__(self): - return f"{self.major}.{self.minor}.{self.micro}" + ( - f"{'r' if self.releaselevel[0] == 'c' else ''}{self.releaselevel[0]}{self.serial}", - "", - )[self.releaselevel == "final"] + return ( + f"{self.major}.{self.minor}.{self.micro}" + + ( + f"{'r' if self.releaselevel[0] == 'c' else ''}{self.releaselevel[0]}{self.serial}", + "", + )[self.releaselevel == "final"] + ) def __str__(self): return f"{__name__} {self.__version__} / {__version_time__}" @@ -118,7 +121,7 @@ class version_info(NamedTuple): __version_info__ = version_info(3, 0, 10, "final", 0) -__version_time__ = "12 Jul 2022 01:22 UTC" +__version_time__ = "14 Jul 2022 07:55 UTC" __version__ = __version_info__.__version__ __versionTime__ = __version_time__ __author__ = "Paul McGuire <ptmcg.gm+pyparsing@gmail.com>" @@ -128,9 +131,9 @@ from .exceptions import * from .actions import * from .core import __diag__, __compat__ from .results import * -from .core import * +from .core import * # type: ignore[misc] from .core import _builtin_exprs as core_builtin_exprs -from .helpers import * +from .helpers import * # type: ignore[misc] from .helpers import _builtin_exprs as helper_builtin_exprs from .unicode import unicode_set, UnicodeRangeList, pyparsing_unicode as unicode @@ -142,11 +145,11 @@ from .common import ( # define backward compat synonyms if "pyparsing_unicode" not in globals(): - pyparsing_unicode = unicode + pyparsing_unicode = unicode # type: ignore[misc] if "pyparsing_common" not in globals(): - pyparsing_common = common + pyparsing_common = common # type: ignore[misc] if "pyparsing_test" not in globals(): - pyparsing_test = testing + pyparsing_test = testing # type: ignore[misc] core_builtin_exprs += common_builtin_exprs + helper_builtin_exprs diff --git a/pyparsing/core.py b/pyparsing/core.py index 0b0b2c8..fecebb4 100644 --- a/pyparsing/core.py +++ b/pyparsing/core.py @@ -449,7 +449,7 @@ class ParserElement(ABC): self.parseAction: List[ParseAction] = list() self.failAction: typing.Optional[ParseFailAction] = None self.customName: str = None # type: ignore[assignment] - self._defaultName: str = None # type: ignore[assignment] + self._defaultName: typing.Optional[str] = None self.resultsName: str = None # type: ignore[assignment] self.saveAsList = savelist self.skipWhitespace = True @@ -910,10 +910,23 @@ class ParserElement(ABC): Tuple[int, "Forward", bool], Tuple[int, Union[ParseResults, Exception]] ] = {} + class _CacheType(dict): + """ + class to help type checking + """ + + not_in_cache: bool + + def get(self, *args): + ... + + def set(self, *args): + ... + # argument cache for optimizing repeated calls when backtracking through recursive expressions packrat_cache = ( - {} - ) # this is set later by enabled_packrat(); this is here so that reset_cache() doesn't fail + _CacheType() + ) # set later by enable_packrat(); this is here so that reset_cache() doesn't fail packrat_cache_lock = RLock() packrat_cache_stats = [0, 0] @@ -1036,9 +1049,9 @@ class ParserElement(ABC): elif ParserElement._packratEnabled: raise RuntimeError("Packrat and Bounded Recursion are not compatible") if cache_size_limit is None: - ParserElement.recursion_memos = _UnboundedMemo() + ParserElement.recursion_memos = _UnboundedMemo() # type: ignore[assignment] elif cache_size_limit > 0: - ParserElement.recursion_memos = _LRUMemo(capacity=cache_size_limit) + ParserElement.recursion_memos = _LRUMemo(capacity=cache_size_limit) # type: ignore[assignment] else: raise NotImplementedError("Memo size of %s" % cache_size_limit) ParserElement._left_recursion_enabled = True @@ -1084,7 +1097,7 @@ class ParserElement(ABC): if cache_size_limit is None: ParserElement.packrat_cache = _UnboundedCache() else: - ParserElement.packrat_cache = _FifoCache(cache_size_limit) + ParserElement.packrat_cache = _FifoCache(cache_size_limit) # type: ignore[assignment] ParserElement._parse = ParserElement._parseCache def parse_string( @@ -1212,7 +1225,9 @@ class ParserElement(ABC): try: while loc <= instrlen and matches < maxMatches: try: - preloc = preparseFn(instring, loc) + preloc: int = preparseFn(instring, loc) + nextLoc: int + tokens: ParseResults nextLoc, tokens = parseFn(instring, preloc, callPreParse=False) except ParseException: loc = preloc + 1 @@ -1894,8 +1909,10 @@ class ParserElement(ABC): """ parseAll = parseAll or parse_all try: + file_or_filename = typing.cast(TextIO, file_or_filename) file_contents = file_or_filename.read() except AttributeError: + file_or_filename = typing.cast(str, file_or_filename) with open(file_or_filename, "r", encoding=encoding) as f: file_contents = f.read() try: @@ -2064,10 +2081,15 @@ class ParserElement(ABC): failureTests = failureTests or failure_tests postParse = postParse or post_parse if isinstance(tests, str_type): + tests = typing.cast(str, tests) line_strip = type(tests).strip tests = [line_strip(test_line) for test_line in tests.rstrip().splitlines()] - if isinstance(comment, str_type): - comment = Literal(comment) + comment_specified = comment is not None + if comment_specified: + if isinstance(comment, str_type): + comment = typing.cast(str, comment) + comment = Literal(comment) + comment = typing.cast(ParserElement, comment) if file is None: file = sys.stdout print_ = file.write @@ -2079,7 +2101,7 @@ class ParserElement(ABC): NL = Literal(r"\n").add_parse_action(replace_with("\n")).ignore(quoted_string) BOM = "\ufeff" for t in tests: - if comment is not None and comment.matches(t, False) or comments and not t: + if comment_specified and comment.matches(t, False) or comments and not t: comments.append( pyparsing_test.with_line_numbers(t) if with_line_numbers else t ) @@ -2861,10 +2883,10 @@ class Word(Token): try: self.re = re.compile(self.reString) except re.error: - self.re = None + self.re = None # type: ignore[assignment] else: self.re_match = self.re.match - self.parseImpl = self.parseImpl_regex + self.parseImpl = self.parseImpl_regex # type: ignore[assignment] def _generateDefaultName(self) -> str: def charsAsStr(s): @@ -3191,15 +3213,15 @@ class QuotedString(Token): if not endQuoteChar: raise ValueError("end_quote_char cannot be the empty string") - self.quoteChar = quote_char - self.quoteCharLen = len(quote_char) - self.firstQuoteChar = quote_char[0] - self.endQuoteChar = endQuoteChar - self.endQuoteCharLen = len(endQuoteChar) - self.escChar = escChar - self.escQuote = escQuote - self.unquoteResults = unquoteResults - self.convertWhitespaceEscapes = convertWhitespaceEscapes + self.quoteChar: str = quote_char + self.quoteCharLen: int = len(quote_char) + self.firstQuoteChar: str = quote_char[0] + self.endQuoteChar: str = endQuoteChar + self.endQuoteCharLen: int = len(endQuoteChar) + self.escChar: str = escChar or "" + self.escQuote: str = escQuote or "" + self.unquoteResults: bool = unquoteResults + self.convertWhitespaceEscapes: bool = convertWhitespaceEscapes sep = "" inner_pattern = "" @@ -3211,7 +3233,7 @@ class QuotedString(Token): if escChar: inner_pattern += rf"{sep}(?:{re.escape(escChar)}.)" sep = "|" - self.escCharReplacePattern = re.escape(self.escChar) + "(.)" + self.escCharReplacePattern = re.escape(escChar) + "(.)" if len(self.endQuoteChar) > 1: inner_pattern += ( @@ -3892,8 +3914,9 @@ class And(ParseExpression): and isinstance(e.exprs[-1], _PendingSkip) for e in self.exprs[:-1] ): + deleted_expr_marker = NoMatch() for i, e in enumerate(self.exprs[:-1]): - if e is None: + if e is deleted_expr_marker: continue if ( isinstance(e, ParseExpression) @@ -3901,17 +3924,19 @@ class And(ParseExpression): and isinstance(e.exprs[-1], _PendingSkip) ): e.exprs[-1] = e.exprs[-1] + self.exprs[i + 1] - self.exprs[i + 1] = None - self.exprs = [e for e in self.exprs if e is not None] + self.exprs[i + 1] = deleted_expr_marker + self.exprs = [e for e in self.exprs if e is not deleted_expr_marker] super().streamline() # link any IndentedBlocks to the prior expression + prev: ParserElement + cur: ParserElement for prev, cur in zip(self.exprs, self.exprs[1:]): # traverse cur or any first embedded expr of cur looking for an IndentedBlock # (but watch out for recursive grammar) seen = set() - while cur: + while True: if id(cur) in seen: break seen.add(id(cur)) @@ -3923,7 +3948,10 @@ class And(ParseExpression): ) break subs = cur.recurse() - cur = next(iter(subs), None) + next_first = next(iter(subs), None) + if next_first is None: + break + cur = typing.cast(ParserElement, next_first) self.mayReturnEmpty = all(e.mayReturnEmpty for e in self.exprs) return self @@ -4431,12 +4459,13 @@ class ParseElementEnhance(ParserElement): def __init__(self, expr: Union[ParserElement, str], savelist: bool = False): super().__init__(savelist) if isinstance(expr, str_type): + expr_str = typing.cast(str, expr) if issubclass(self._literalStringClass, Token): - expr = self._literalStringClass(expr) + expr = self._literalStringClass(expr_str) # type: ignore[call-arg] elif issubclass(type(self), self._literalStringClass): - expr = Literal(expr) + expr = Literal(expr_str) else: - expr = self._literalStringClass(Literal(expr)) + expr = self._literalStringClass(Literal(expr_str)) # type: ignore[assignment, call-arg] expr = typing.cast(ParserElement, expr) self.expr = expr if expr is not None: @@ -4720,6 +4749,7 @@ class PrecededBy(ParseElementEnhance): self.mayIndexError = False self.exact = False if isinstance(expr, str_type): + expr = typing.cast(str, expr) retreat = len(expr) self.exact = True elif isinstance(expr, (Literal, Keyword)): @@ -5241,7 +5271,7 @@ class Forward(ParseElementEnhance): def __init__(self, other: typing.Optional[Union[ParserElement, str]] = None): self.caller_frame = traceback.extract_stack(limit=2)[0] - super().__init__(other, savelist=False) + super().__init__(other, savelist=False) # type: ignore[arg-type] self.lshift_line = None def __lshift__(self, other) -> "Forward": @@ -5262,7 +5292,7 @@ class Forward(ParseElementEnhance): self.skipWhitespace = self.expr.skipWhitespace self.saveAsList = self.expr.saveAsList self.ignoreExprs.extend(self.expr.ignoreExprs) - self.lshift_line = traceback.extract_stack(limit=2)[-2] + self.lshift_line = traceback.extract_stack(limit=2)[-2] # type: ignore[assignment] return self def __ilshift__(self, other) -> "Forward": @@ -5876,7 +5906,11 @@ def autoname_elements() -> None: Utility to simplify mass-naming of parser elements, for generating railroad diagram with named subdiagrams. """ - for name, var in sys._getframe().f_back.f_locals.items(): + calling_frame = sys._getframe().f_back + if calling_frame is None: + return + calling_frame = typing.cast(types.FrameType, calling_frame) + for name, var in calling_frame.f_locals.items(): if isinstance(var, ParserElement) and not var.customName: var.set_name(name) diff --git a/pyparsing/diagram/__init__.py b/pyparsing/diagram/__init__.py index 25f10e9..8f2322d 100644 --- a/pyparsing/diagram/__init__.py +++ b/pyparsing/diagram/__init__.py @@ -1,3 +1,4 @@ +# mypy: ignore-errors import railroad import pyparsing import typing diff --git a/pyparsing/exceptions.py b/pyparsing/exceptions.py index aa08f63..73f6c02 100644 --- a/pyparsing/exceptions.py +++ b/pyparsing/exceptions.py @@ -25,6 +25,12 @@ _exception_word_extractor = re.compile("([" + _extract_alphanums + "]{1,16})|.") class ParseBaseException(Exception): """base exception class for all parsing runtime exceptions""" + loc: int + msg: str + pstr: str + parser_element: typing.Any # "ParserElement" + args: typing.Tuple[str, int, typing.Optional[str]] + __slots__ = ( "loc", "msg", diff --git a/pyparsing/helpers.py b/pyparsing/helpers.py index f3e0ab5..c4ac2a9 100644 --- a/pyparsing/helpers.py +++ b/pyparsing/helpers.py @@ -294,6 +294,7 @@ def one_of( symbols: List[str] = [] if isinstance(strs, str_type): + strs = typing.cast(str, strs) symbols = strs.split() elif isinstance(strs, Iterable): symbols = list(strs) @@ -570,6 +571,8 @@ def nested_expr( raise ValueError("opening and closing strings cannot be the same") if content is None: if isinstance(opener, str_type) and isinstance(closer, str_type): + opener = typing.cast(str, opener) + closer = typing.cast(str, closer) if len(opener) == 1 and len(closer) == 1: if ignoreExpr is not None: content = Combine( diff --git a/pyparsing/results.py b/pyparsing/results.py index b6fc538..5f4b62c 100644 --- a/pyparsing/results.py +++ b/pyparsing/results.py @@ -1,13 +1,14 @@ # results.py from collections.abc import MutableMapping, Mapping, MutableSequence, Iterator import pprint -from typing import Tuple, Any, Dict +from typing import Tuple, Any, Dict, Set, List str_type: Tuple[type, ...] = (str, bytes) _generator_type = type((_ for _ in ())) class _ParseResultsWithOffset: + tup: Tuple["ParseResults", int] __slots__ = ["tup"] def __init__(self, p1: "ParseResults", p2: int): @@ -71,6 +72,13 @@ class ParseResults: _null_values: Tuple[Any, ...] = (None, [], "", ()) + _name: str + _parent: "ParseResults" + _all_names: Set[str] + _modal: bool + _toklist: List[Any] + _tokdict: Dict[str, Any] + __slots__ = ( "_name", "_parent", @@ -419,7 +427,7 @@ class ParseResults: raise AttributeError(name) return "" - def __add__(self, other) -> "ParseResults": + def __add__(self, other: "ParseResults") -> "ParseResults": ret = self.copy() ret += other return ret diff --git a/pyparsing/testing.py b/pyparsing/testing.py index 2b0fcf4..6a254c1 100644 --- a/pyparsing/testing.py +++ b/pyparsing/testing.py @@ -267,14 +267,16 @@ class pyparsing_test: if mark_control is not None: mark_control = typing.cast(str, mark_control) if mark_control == "unicode": - tbl = str.maketrans( - {c: u for c, u in zip(range(0, 33), range(0x2400, 0x2433))} - | {127: 0x2421} - ) + transtable_map = { + c: u for c, u in zip(range(0, 33), range(0x2400, 0x2433)) + } + transtable_map[127] = 0x2421 + tbl = str.maketrans(transtable_map) eol_mark = "" else: + ord_mark_control = ord(mark_control) tbl = str.maketrans( - {c: mark_control for c in list(range(0, 32)) + [127]} + {c: ord_mark_control for c in list(range(0, 32)) + [127]} ) s = s.translate(tbl) if mark_spaces is not None and mark_spaces != " ": |