summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorptmcg <ptmcg@austin.rr.com>2022-07-14 03:35:10 -0500
committerptmcg <ptmcg@austin.rr.com>2022-07-14 03:35:10 -0500
commit06c12f685b5d0e29f01abc842dff8d268d93ffbf (patch)
tree720b0faafd07b602965918c88dc981958dba50ee
parentc74949d0cafbe7c136a54777b9b340c37f00b484 (diff)
downloadpyparsing-git-06c12f685b5d0e29f01abc842dff8d268d93ffbf.tar.gz
Add type annotations
-rw-r--r--pyparsing/__init__.py23
-rw-r--r--pyparsing/core.py100
-rw-r--r--pyparsing/diagram/__init__.py1
-rw-r--r--pyparsing/exceptions.py6
-rw-r--r--pyparsing/helpers.py3
-rw-r--r--pyparsing/results.py12
-rw-r--r--pyparsing/testing.py12
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 != " ":