Bump pyparsing from 3.0.9 to 3.1.1 (#2131)

* Bump pyparsing from 3.0.9 to 3.1.1

Bumps [pyparsing](https://github.com/pyparsing/pyparsing) from 3.0.9 to 3.1.1.
- [Release notes](https://github.com/pyparsing/pyparsing/releases)
- [Changelog](https://github.com/pyparsing/pyparsing/blob/master/CHANGES)
- [Commits](https://github.com/pyparsing/pyparsing/compare/pyparsing_3.0.9...3.1.1)

---
updated-dependencies:
- dependency-name: pyparsing
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update pyparsing==3.1.1

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
This commit is contained in:
dependabot[bot] 2023-08-24 12:09:51 -07:00 committed by GitHub
parent d0c7f25a3f
commit 3debeada2a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 1306 additions and 797 deletions

View file

@ -56,7 +56,7 @@ self-explanatory class names, and the use of :class:`'+'<And>`,
:class:`'|'<MatchFirst>`, :class:`'^'<Or>` and :class:`'&'<Each>` operators.
The :class:`ParseResults` object returned from
:class:`ParserElement.parseString` can be
:class:`ParserElement.parse_string` can be
accessed as a nested list, a dictionary, or an object with named
attributes.
@ -85,11 +85,11 @@ classes inherit from. Use the docstrings for examples of how to:
and :class:`'&'<Each>` operators to combine simple expressions into
more complex ones
- associate names with your parsed results using
:class:`ParserElement.setResultsName`
:class:`ParserElement.set_results_name`
- access the parsed data, which is returned as a :class:`ParseResults`
object
- find some helpful expression short-cuts like :class:`delimitedList`
and :class:`oneOf`
- find some helpful expression short-cuts like :class:`DelimitedList`
and :class:`one_of`
- find more useful common expressions in the :class:`pyparsing_common`
namespace class
"""
@ -106,30 +106,22 @@ class version_info(NamedTuple):
@property
def __version__(self):
return (
"{}.{}.{}".format(self.major, self.minor, self.micro)
f"{self.major}.{self.minor}.{self.micro}"
+ (
"{}{}{}".format(
"r" if self.releaselevel[0] == "c" else "",
self.releaselevel[0],
self.serial,
),
f"{'r' if self.releaselevel[0] == 'c' else ''}{self.releaselevel[0]}{self.serial}",
"",
)[self.releaselevel == "final"]
)
def __str__(self):
return "{} {} / {}".format(__name__, self.__version__, __version_time__)
return f"{__name__} {self.__version__} / {__version_time__}"
def __repr__(self):
return "{}.{}({})".format(
__name__,
type(self).__name__,
", ".join("{}={!r}".format(*nv) for nv in zip(self._fields, self)),
)
return f"{__name__}.{type(self).__name__}({', '.join('{}={!r}'.format(*nv) for nv in zip(self._fields, self))})"
__version_info__ = version_info(3, 0, 9, "final", 0)
__version_time__ = "05 May 2022 07:02 UTC"
__version_info__ = version_info(3, 1, 1, "final", 1)
__version_time__ = "29 Jul 2023 22:27 UTC"
__version__ = __version_info__.__version__
__versionTime__ = __version_time__
__author__ = "Paul McGuire <ptmcg.gm+pyparsing@gmail.com>"
@ -139,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, assignment]
from .core import _builtin_exprs as core_builtin_exprs
from .helpers import *
from .helpers import * # type: ignore[misc, assignment]
from .helpers import _builtin_exprs as helper_builtin_exprs
from .unicode import unicode_set, UnicodeRangeList, pyparsing_unicode as unicode
@ -153,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
@ -174,7 +166,9 @@ __all__ = [
"CaselessKeyword",
"CaselessLiteral",
"CharsNotIn",
"CloseMatch",
"Combine",
"DelimitedList",
"Dict",
"Each",
"Empty",
@ -227,9 +221,11 @@ __all__ = [
"alphas8bit",
"any_close_tag",
"any_open_tag",
"autoname_elements",
"c_style_comment",
"col",
"common_html_entity",
"condition_as_parse_action",
"counted_array",
"cpp_style_comment",
"dbl_quoted_string",
@ -241,6 +237,7 @@ __all__ = [
"html_comment",
"identchars",
"identbodychars",
"infix_notation",
"java_style_comment",
"line",
"line_end",
@ -255,8 +252,12 @@ __all__ = [
"null_debug_action",
"nums",
"one_of",
"original_text_for",
"printables",
"punc8bit",
"pyparsing_common",
"pyparsing_test",
"pyparsing_unicode",
"python_style_comment",
"quoted_string",
"remove_quotes",
@ -267,28 +268,20 @@ __all__ = [
"srange",
"string_end",
"string_start",
"token_map",
"trace_parse_action",
"ungroup",
"unicode_set",
"unicode_string",
"with_attribute",
"indentedBlock",
"original_text_for",
"ungroup",
"infix_notation",
"locatedExpr",
"with_class",
"CloseMatch",
"token_map",
"pyparsing_common",
"pyparsing_unicode",
"unicode_set",
"condition_as_parse_action",
"pyparsing_test",
# pre-PEP8 compatibility names
"__versionTime__",
"anyCloseTag",
"anyOpenTag",
"cStyleComment",
"commonHTMLEntity",
"conditionAsParseAction",
"countedArray",
"cppStyleComment",
"dblQuotedString",
@ -296,9 +289,12 @@ __all__ = [
"delimitedList",
"dictOf",
"htmlComment",
"indentedBlock",
"infixNotation",
"javaStyleComment",
"lineEnd",
"lineStart",
"locatedExpr",
"makeHTMLTags",
"makeXMLTags",
"matchOnlyAtCol",
@ -308,6 +304,7 @@ __all__ = [
"nullDebugAction",
"oneOf",
"opAssoc",
"originalTextFor",
"pythonStyleComment",
"quotedString",
"removeQuotes",
@ -317,15 +314,12 @@ __all__ = [
"sglQuotedString",
"stringEnd",
"stringStart",
"tokenMap",
"traceParseAction",
"unicodeString",
"withAttribute",
"indentedBlock",
"originalTextFor",
"infixNotation",
"locatedExpr",
"withClass",
"tokenMap",
"conditionAsParseAction",
"autoname_elements",
"common",
"unicode",
"testing",
]

View file

@ -1,7 +1,7 @@
# actions.py
from .exceptions import ParseException
from .util import col
from .util import col, replaced_by_pep8
class OnlyOnce:
@ -38,7 +38,7 @@ def match_only_at_col(n):
def verify_col(strg, locn, toks):
if col(locn, strg) != n:
raise ParseException(strg, locn, "matched token not at column {}".format(n))
raise ParseException(strg, locn, f"matched token not at column {n}")
return verify_col
@ -148,15 +148,13 @@ def with_attribute(*args, **attr_dict):
raise ParseException(
s,
l,
"attribute {!r} has value {!r}, must be {!r}".format(
attrName, tokens[attrName], attrValue
),
f"attribute {attrName!r} has value {tokens[attrName]!r}, must be {attrValue!r}",
)
return pa
with_attribute.ANY_VALUE = object()
with_attribute.ANY_VALUE = object() # type: ignore [attr-defined]
def with_class(classname, namespace=""):
@ -195,13 +193,25 @@ def with_class(classname, namespace=""):
1 4 0 1 0
1,3 2,3 1,1
"""
classattr = "{}:class".format(namespace) if namespace else "class"
classattr = f"{namespace}:class" if namespace else "class"
return with_attribute(**{classattr: classname})
# pre-PEP8 compatibility symbols
replaceWith = replace_with
removeQuotes = remove_quotes
withAttribute = with_attribute
withClass = with_class
matchOnlyAtCol = match_only_at_col
# fmt: off
@replaced_by_pep8(replace_with)
def replaceWith(): ...
@replaced_by_pep8(remove_quotes)
def removeQuotes(): ...
@replaced_by_pep8(with_attribute)
def withAttribute(): ...
@replaced_by_pep8(with_class)
def withClass(): ...
@replaced_by_pep8(match_only_at_col)
def matchOnlyAtCol(): ...
# fmt: on

View file

@ -1,6 +1,6 @@
# common.py
from .core import *
from .helpers import delimited_list, any_open_tag, any_close_tag
from .helpers import DelimitedList, any_open_tag, any_close_tag
from datetime import datetime
@ -22,17 +22,17 @@ class pyparsing_common:
Parse actions:
- :class:`convertToInteger`
- :class:`convertToFloat`
- :class:`convertToDate`
- :class:`convertToDatetime`
- :class:`stripHTMLTags`
- :class:`upcaseTokens`
- :class:`downcaseTokens`
- :class:`convert_to_integer`
- :class:`convert_to_float`
- :class:`convert_to_date`
- :class:`convert_to_datetime`
- :class:`strip_html_tags`
- :class:`upcase_tokens`
- :class:`downcase_tokens`
Example::
pyparsing_common.number.runTests('''
pyparsing_common.number.run_tests('''
# any int or real number, returned as the appropriate type
100
-100
@ -42,7 +42,7 @@ class pyparsing_common:
1e-12
''')
pyparsing_common.fnumber.runTests('''
pyparsing_common.fnumber.run_tests('''
# any int or real number, returned as float
100
-100
@ -52,19 +52,19 @@ class pyparsing_common:
1e-12
''')
pyparsing_common.hex_integer.runTests('''
pyparsing_common.hex_integer.run_tests('''
# hex numbers
100
FF
''')
pyparsing_common.fraction.runTests('''
pyparsing_common.fraction.run_tests('''
# fractions
1/2
-3/4
''')
pyparsing_common.mixed_integer.runTests('''
pyparsing_common.mixed_integer.run_tests('''
# mixed fractions
1
1/2
@ -73,8 +73,8 @@ class pyparsing_common:
''')
import uuid
pyparsing_common.uuid.setParseAction(tokenMap(uuid.UUID))
pyparsing_common.uuid.runTests('''
pyparsing_common.uuid.set_parse_action(token_map(uuid.UUID))
pyparsing_common.uuid.run_tests('''
# uuid
12345678-1234-5678-1234-567812345678
''')
@ -260,8 +260,8 @@ class pyparsing_common:
Example::
date_expr = pyparsing_common.iso8601_date.copy()
date_expr.setParseAction(pyparsing_common.convertToDate())
print(date_expr.parseString("1999-12-31"))
date_expr.set_parse_action(pyparsing_common.convert_to_date())
print(date_expr.parse_string("1999-12-31"))
prints::
@ -287,8 +287,8 @@ class pyparsing_common:
Example::
dt_expr = pyparsing_common.iso8601_datetime.copy()
dt_expr.setParseAction(pyparsing_common.convertToDatetime())
print(dt_expr.parseString("1999-12-31T23:59:59.999"))
dt_expr.set_parse_action(pyparsing_common.convert_to_datetime())
print(dt_expr.parse_string("1999-12-31T23:59:59.999"))
prints::
@ -326,9 +326,9 @@ class pyparsing_common:
# strip HTML links from normal text
text = '<td>More info at the <a href="https://github.com/pyparsing/pyparsing/wiki">pyparsing</a> wiki page</td>'
td, td_end = makeHTMLTags("TD")
table_text = td + SkipTo(td_end).setParseAction(pyparsing_common.stripHTMLTags)("body") + td_end
print(table_text.parseString(text).body)
td, td_end = make_html_tags("TD")
table_text = td + SkipTo(td_end).set_parse_action(pyparsing_common.strip_html_tags)("body") + td_end
print(table_text.parse_string(text).body)
Prints::
@ -348,7 +348,7 @@ class pyparsing_common:
.streamline()
.set_name("commaItem")
)
comma_separated_list = delimited_list(
comma_separated_list = DelimitedList(
Opt(quoted_string.copy() | _commasepitem, default="")
).set_name("comma separated list")
"""Predefined expression of 1 or more printable words or quoted strings, separated by commas."""
@ -363,7 +363,7 @@ class pyparsing_common:
url = Regex(
# https://mathiasbynens.be/demo/url-regex
# https://gist.github.com/dperini/729294
r"^" +
r"(?P<url>" +
# protocol identifier (optional)
# short syntax // still required
r"(?:(?:(?P<scheme>https?|ftp):)?\/\/)" +
@ -405,18 +405,26 @@ class pyparsing_common:
r"(\?(?P<query>[^#]*))?" +
# fragment (optional)
r"(#(?P<fragment>\S*))?" +
r"$"
r")"
).set_name("url")
"""URL (http/https/ftp scheme)"""
# fmt: on
# pre-PEP8 compatibility names
convertToInteger = convert_to_integer
"""Deprecated - use :class:`convert_to_integer`"""
convertToFloat = convert_to_float
"""Deprecated - use :class:`convert_to_float`"""
convertToDate = convert_to_date
"""Deprecated - use :class:`convert_to_date`"""
convertToDatetime = convert_to_datetime
"""Deprecated - use :class:`convert_to_datetime`"""
stripHTMLTags = strip_html_tags
"""Deprecated - use :class:`strip_html_tags`"""
upcaseTokens = upcase_tokens
"""Deprecated - use :class:`upcase_tokens`"""
downcaseTokens = downcase_tokens
"""Deprecated - use :class:`downcase_tokens`"""
_builtin_exprs = [

File diff suppressed because it is too large Load diff

View file

@ -1,3 +1,4 @@
# mypy: ignore-errors
import railroad
import pyparsing
import typing
@ -17,11 +18,13 @@ import inspect
jinja2_template_source = """\
{% if not embed %}
<!DOCTYPE html>
<html>
<head>
{% endif %}
{% if not head %}
<style type="text/css">
<style>
.railroad-heading {
font-family: monospace;
}
@ -29,8 +32,10 @@ jinja2_template_source = """\
{% else %}
{{ head | safe }}
{% endif %}
{% if not embed %}
</head>
<body>
{% endif %}
{{ body | safe }}
{% for diagram in diagrams %}
<div class="railroad-group">
@ -41,8 +46,10 @@ jinja2_template_source = """\
</div>
</div>
{% endfor %}
{% if not embed %}
</body>
</html>
{% endif %}
"""
template = Template(jinja2_template_source)
@ -127,7 +134,7 @@ class EditablePartial(Generic[T]):
return self.func(*args, **kwargs)
def railroad_to_html(diagrams: List[NamedDiagram], **kwargs) -> str:
def railroad_to_html(diagrams: List[NamedDiagram], embed=False, **kwargs) -> str:
"""
Given a list of NamedDiagram, produce a single HTML string that visualises those diagrams
:params kwargs: kwargs to be passed in to the template
@ -137,13 +144,17 @@ def railroad_to_html(diagrams: List[NamedDiagram], **kwargs) -> str:
if diagram.diagram is None:
continue
io = StringIO()
try:
css = kwargs.get('css')
diagram.diagram.writeStandalone(io.write, css=css)
except AttributeError:
diagram.diagram.writeSvg(io.write)
title = diagram.name
if diagram.index == 0:
title += " (root)"
data.append({"title": title, "text": "", "svg": io.getvalue()})
return template.render(diagrams=data, **kwargs)
return template.render(diagrams=data, embed=embed, **kwargs)
def resolve_partial(partial: "EditablePartial[T]") -> T:
@ -398,7 +409,6 @@ def _apply_diagram_item_enhancements(fn):
show_results_names: bool = False,
show_groups: bool = False,
) -> typing.Optional[EditablePartial]:
ret = fn(
element,
parent,
@ -555,9 +565,11 @@ def _to_diagram_element(
else:
ret = EditablePartial.from_call(railroad.Group, label="", item="")
elif isinstance(element, pyparsing.TokenConverter):
ret = EditablePartial.from_call(
AnnotatedItem, label=type(element).__name__.lower(), item=""
)
label = type(element).__name__.lower()
if label == "tokenconverter":
ret = EditablePartial.from_call(railroad.Sequence, items=[])
else:
ret = EditablePartial.from_call(AnnotatedItem, label=label, item="")
elif isinstance(element, pyparsing.Opt):
ret = EditablePartial.from_call(railroad.Optional, item="")
elif isinstance(element, pyparsing.OneOrMore):
@ -571,10 +583,12 @@ def _to_diagram_element(
elif isinstance(element, pyparsing.Empty) and not element.customName:
# Skip unnamed "Empty" elements
ret = None
elif len(exprs) > 1:
elif isinstance(element, pyparsing.ParseElementEnhance):
ret = EditablePartial.from_call(railroad.Sequence, items=[])
elif len(exprs) > 0 and not element_results_name:
ret = EditablePartial.from_call(railroad.Group, item="", label=name)
elif len(exprs) > 0:
ret = EditablePartial.from_call(railroad.Sequence, items=[])
else:
terminal = EditablePartial.from_call(railroad.Terminal, element.defaultName)
ret = terminal

View file

@ -4,7 +4,13 @@ import re
import sys
import typing
from .util import col, line, lineno, _collapse_string_to_ranges
from .util import (
col,
line,
lineno,
_collapse_string_to_ranges,
replaced_by_pep8,
)
from .unicode import pyparsing_unicode as ppu
@ -19,6 +25,20 @@ _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",
"pstr",
"parser_element",
"args",
)
# Performance tuning: we construct a *lot* of these, so keep this
# constructor as small and fast as possible
def __init__(
@ -35,7 +55,7 @@ class ParseBaseException(Exception):
else:
self.msg = msg
self.pstr = pstr
self.parser_element = self.parserElement = elem
self.parser_element = elem
self.args = (pstr, loc, msg)
@staticmethod
@ -64,7 +84,7 @@ class ParseBaseException(Exception):
if isinstance(exc, ParseBaseException):
ret.append(exc.line)
ret.append(" " * (exc.column - 1) + "^")
ret.append("{}: {}".format(type(exc).__name__, exc))
ret.append(f"{type(exc).__name__}: {exc}")
if depth > 0:
callers = inspect.getinnerframes(exc.__traceback__, context=depth)
@ -74,7 +94,9 @@ class ParseBaseException(Exception):
f_self = frm.f_locals.get("self", None)
if isinstance(f_self, ParserElement):
if frm.f_code.co_name not in ("parseImpl", "_parseNoCache"):
if not frm.f_code.co_name.startswith(
("parseImpl", "_parseNoCache")
):
continue
if id(f_self) in seen:
continue
@ -82,21 +104,19 @@ class ParseBaseException(Exception):
self_type = type(f_self)
ret.append(
"{}.{} - {}".format(
self_type.__module__, self_type.__name__, f_self
)
f"{self_type.__module__}.{self_type.__name__} - {f_self}"
)
elif f_self is not None:
self_type = type(f_self)
ret.append("{}.{}".format(self_type.__module__, self_type.__name__))
ret.append(f"{self_type.__module__}.{self_type.__name__}")
else:
code = frm.f_code
if code.co_name in ("wrapper", "<module>"):
continue
ret.append("{}".format(code.co_name))
ret.append(code.co_name)
depth -= 1
if not depth:
@ -110,7 +130,7 @@ class ParseBaseException(Exception):
internal factory method to simplify creating one type of ParseException
from another - avoids having __init__ signature conflicts among subclasses
"""
return cls(pe.pstr, pe.loc, pe.msg, pe.parserElement)
return cls(pe.pstr, pe.loc, pe.msg, pe.parser_element)
@property
def line(self) -> str:
@ -140,6 +160,15 @@ class ParseBaseException(Exception):
"""
return col(self.loc, self.pstr)
# pre-PEP8 compatibility
@property
def parserElement(self):
return self.parser_element
@parserElement.setter
def parserElement(self, elem):
self.parser_element = elem
def __str__(self) -> str:
if self.pstr:
if self.loc >= len(self.pstr):
@ -154,14 +183,14 @@ class ParseBaseException(Exception):
foundstr = (", found %r" % found).replace(r"\\", "\\")
else:
foundstr = ""
return "{}{} (at char {}), (line:{}, col:{})".format(
self.msg, foundstr, self.loc, self.lineno, self.column
)
return f"{self.msg}{foundstr} (at char {self.loc}), (line:{self.lineno}, col:{self.column})"
def __repr__(self):
return str(self)
def mark_input_line(self, marker_string: str = None, *, markerString=">!<") -> str:
def mark_input_line(
self, marker_string: typing.Optional[str] = None, *, markerString: str = ">!<"
) -> str:
"""
Extracts the exception line from the input string, and marks
the location of the exception with a special symbol.
@ -214,7 +243,10 @@ class ParseBaseException(Exception):
"""
return self.explain_exception(self, depth)
markInputline = mark_input_line
# fmt: off
@replaced_by_pep8(mark_input_line)
def markInputline(self): ...
# fmt: on
class ParseException(ParseBaseException):
@ -264,4 +296,4 @@ class RecursiveGrammarException(Exception):
self.parseElementTrace = parseElementList
def __str__(self) -> str:
return "RecursiveGrammarException: {}".format(self.parseElementTrace)
return f"RecursiveGrammarException: {self.parseElementTrace}"

View file

@ -1,73 +1,22 @@
# helpers.py
import html.entities
import re
import sys
import typing
from . import __diag__
from .core import *
from .util import _bslash, _flatten, _escape_regex_range_chars
from .util import (
_bslash,
_flatten,
_escape_regex_range_chars,
replaced_by_pep8,
)
#
# global helpers
#
def delimited_list(
expr: Union[str, ParserElement],
delim: Union[str, ParserElement] = ",",
combine: bool = False,
min: typing.Optional[int] = None,
max: typing.Optional[int] = None,
*,
allow_trailing_delim: bool = False,
) -> ParserElement:
"""Helper to define a delimited list of expressions - the delimiter
defaults to ','. By default, the list elements and delimiters can
have intervening whitespace, and comments, but this can be
overridden by passing ``combine=True`` in the constructor. If
``combine`` is set to ``True``, the matching tokens are
returned as a single token string, with the delimiters included;
otherwise, the matching tokens are returned as a list of tokens,
with the delimiters suppressed.
If ``allow_trailing_delim`` is set to True, then the list may end with
a delimiter.
Example::
delimited_list(Word(alphas)).parse_string("aa,bb,cc") # -> ['aa', 'bb', 'cc']
delimited_list(Word(hexnums), delim=':', combine=True).parse_string("AA:BB:CC:DD:EE") # -> ['AA:BB:CC:DD:EE']
"""
if isinstance(expr, str_type):
expr = ParserElement._literalStringClass(expr)
dlName = "{expr} [{delim} {expr}]...{end}".format(
expr=str(expr.copy().streamline()),
delim=str(delim),
end=" [{}]".format(str(delim)) if allow_trailing_delim else "",
)
if not combine:
delim = Suppress(delim)
if min is not None:
if min < 1:
raise ValueError("min must be greater than 0")
min -= 1
if max is not None:
if min is not None and max <= min:
raise ValueError("max must be greater than, or equal to min")
max -= 1
delimited_list_expr = expr + (delim + expr)[min, max]
if allow_trailing_delim:
delimited_list_expr += Opt(delim)
if combine:
return Combine(delimited_list_expr).set_name(dlName)
else:
return delimited_list_expr.set_name(dlName)
def counted_array(
expr: ParserElement,
int_expr: typing.Optional[ParserElement] = None,
@ -187,7 +136,7 @@ def match_previous_expr(expr: ParserElement) -> ParserElement:
theseTokens = _flatten(t.as_list())
if theseTokens != matchTokens:
raise ParseException(
s, l, "Expected {}, found{}".format(matchTokens, theseTokens)
s, l, f"Expected {matchTokens}, found{theseTokens}"
)
rep.set_parse_action(must_match_these_tokens, callDuringTry=True)
@ -218,7 +167,7 @@ def one_of(
- ``caseless`` - treat all literals as caseless - (default= ``False``)
- ``use_regex`` - as an optimization, will
generate a :class:`Regex` object; otherwise, will generate
a :class:`MatchFirst` object (if ``caseless=True`` or ``asKeyword=True``, or if
a :class:`MatchFirst` object (if ``caseless=True`` or ``as_keyword=True``, or if
creating a :class:`Regex` raises an exception) - (default= ``True``)
- ``as_keyword`` - enforce :class:`Keyword`-style matching on the
generated expressions - (default= ``False``)
@ -262,6 +211,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)
@ -293,15 +243,13 @@ def one_of(
try:
if all(len(sym) == 1 for sym in symbols):
# symbols are just single characters, create range regex pattern
patt = "[{}]".format(
"".join(_escape_regex_range_chars(sym) for sym in symbols)
)
patt = f"[{''.join(_escape_regex_range_chars(sym) for sym in symbols)}]"
else:
patt = "|".join(re.escape(sym) for sym in symbols)
# wrap with \b word break markers if defining as keywords
if asKeyword:
patt = r"\b(?:{})\b".format(patt)
patt = rf"\b(?:{patt})\b"
ret = Regex(patt, flags=re_flags).set_name(" | ".join(symbols))
@ -390,7 +338,7 @@ def original_text_for(
src = "this is test <b> bold <i>text</i> </b> normal text "
for tag in ("b", "i"):
opener, closer = make_html_tags(tag)
patt = original_text_for(opener + SkipTo(closer) + closer)
patt = original_text_for(opener + ... + closer)
print(patt.search_string(src)[0])
prints::
@ -426,7 +374,7 @@ def ungroup(expr: ParserElement) -> ParserElement:
def locatedExpr(expr: ParserElement) -> ParserElement:
"""
(DEPRECATED - future code should use the Located class)
(DEPRECATED - future code should use the :class:`Located` class)
Helper to decorate a returned token with its starting and ending
locations in the input string.
@ -437,12 +385,12 @@ def locatedExpr(expr: ParserElement) -> ParserElement:
- ``value`` - the actual parsed results
Be careful if the input text contains ``<TAB>`` characters, you
may want to call :class:`ParserElement.parseWithTabs`
may want to call :class:`ParserElement.parse_with_tabs`
Example::
wd = Word(alphas)
for match in locatedExpr(wd).searchString("ljsdf123lksdjjf123lkkjj1222"):
for match in locatedExpr(wd).search_string("ljsdf123lksdjjf123lkkjj1222"):
print(match)
prints::
@ -471,6 +419,7 @@ def nested_expr(
closing delimiters (``"("`` and ``")"`` are the default).
Parameters:
- ``opener`` - opening character for a nested list
(default= ``"("``); can also be a pyparsing expression
- ``closer`` - closing character for a nested list
@ -507,7 +456,7 @@ def nested_expr(
c_function = (decl_data_type("type")
+ ident("name")
+ LPAR + Opt(delimited_list(arg), [])("args") + RPAR
+ LPAR + Opt(DelimitedList(arg), [])("args") + RPAR
+ code_body("body"))
c_function.ignore(c_style_comment)
@ -539,6 +488,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(
@ -695,12 +646,15 @@ common_html_entity = Regex("&(?P<entity>" + "|".join(_htmlEntityMap) + ");").set
)
def replace_html_entity(t):
def replace_html_entity(s, l, t):
"""Helper parser action to replace common HTML entities with their special characters"""
return _htmlEntityMap.get(t.entity)
class OpAssoc(Enum):
"""Enumeration of operator associativity
- used in constructing InfixNotationOperatorSpec for :class:`infix_notation`"""
LEFT = 1
RIGHT = 2
@ -742,6 +696,7 @@ def infix_notation(
improve your parser performance.
Parameters:
- ``base_expr`` - expression representing the most basic operand to
be used in the expression
- ``op_list`` - list of tuples, one for each operator precedence level
@ -764,11 +719,11 @@ def infix_notation(
``set_parse_action(*fn)``
(:class:`ParserElement.set_parse_action`)
- ``lpar`` - expression for matching left-parentheses; if passed as a
str, then will be parsed as Suppress(lpar). If lpar is passed as
str, then will be parsed as ``Suppress(lpar)``. If lpar is passed as
an expression (such as ``Literal('(')``), then it will be kept in
the parsed results, and grouped with them. (default= ``Suppress('(')``)
- ``rpar`` - expression for matching right-parentheses; if passed as a
str, then will be parsed as Suppress(rpar). If rpar is passed as
str, then will be parsed as ``Suppress(rpar)``. If rpar is passed as
an expression (such as ``Literal(')')``), then it will be kept in
the parsed results, and grouped with them. (default= ``Suppress(')')``)
@ -800,9 +755,13 @@ def infix_notation(
(5+3)*6
[[[5, '+', 3], '*', 6]]
(5+x)*y
[[[5, '+', 'x'], '*', 'y']]
-2--11
[[['-', 2], '-', ['-', 11]]]
"""
# captive version of FollowedBy that does not do parse actions or capture results names
class _FB(FollowedBy):
def parseImpl(self, instring, loc, doActions=True):
@ -823,19 +782,25 @@ def infix_notation(
else:
lastExpr = base_expr | (lpar + ret + rpar)
arity: int
rightLeftAssoc: opAssoc
pa: typing.Optional[ParseAction]
opExpr1: ParserElement
opExpr2: ParserElement
for i, operDef in enumerate(op_list):
opExpr, arity, rightLeftAssoc, pa = (operDef + (None,))[:4]
opExpr, arity, rightLeftAssoc, pa = (operDef + (None,))[:4] # type: ignore[assignment]
if isinstance(opExpr, str_type):
opExpr = ParserElement._literalStringClass(opExpr)
opExpr = typing.cast(ParserElement, opExpr)
if arity == 3:
if not isinstance(opExpr, (tuple, list)) or len(opExpr) != 2:
raise ValueError(
"if numterms=3, opExpr must be a tuple or list of two expressions"
)
opExpr1, opExpr2 = opExpr
term_name = "{}{} term".format(opExpr1, opExpr2)
term_name = f"{opExpr1}{opExpr2} term"
else:
term_name = "{} term".format(opExpr)
term_name = f"{opExpr} term"
if not 1 <= arity <= 3:
raise ValueError("operator must be unary (1), binary (2), or ternary (3)")
@ -843,7 +808,8 @@ def infix_notation(
if rightLeftAssoc not in (OpAssoc.LEFT, OpAssoc.RIGHT):
raise ValueError("operator must indicate right or left associativity")
thisExpr: Forward = Forward().set_name(term_name)
thisExpr: ParserElement = Forward().set_name(term_name)
thisExpr = typing.cast(Forward, thisExpr)
if rightLeftAssoc is OpAssoc.LEFT:
if arity == 1:
matchExpr = _FB(lastExpr + opExpr) + Group(lastExpr + opExpr[1, ...])
@ -890,7 +856,7 @@ def infix_notation(
def indentedBlock(blockStatementExpr, indentStack, indent=True, backup_stacks=[]):
"""
(DEPRECATED - use IndentedBlock class instead)
(DEPRECATED - use :class:`IndentedBlock` class instead)
Helper method for defining space-delimited indentation blocks,
such as those used to define block statements in Python source code.
@ -1063,22 +1029,28 @@ _builtin_exprs: List[ParserElement] = [
]
# compatibility function, superseded by DelimitedList class
def delimited_list(
expr: Union[str, ParserElement],
delim: Union[str, ParserElement] = ",",
combine: bool = False,
min: typing.Optional[int] = None,
max: typing.Optional[int] = None,
*,
allow_trailing_delim: bool = False,
) -> ParserElement:
"""(DEPRECATED - use :class:`DelimitedList` class)"""
return DelimitedList(
expr, delim, combine, min, max, allow_trailing_delim=allow_trailing_delim
)
# pre-PEP8 compatible names
delimitedList = delimited_list
countedArray = counted_array
matchPreviousLiteral = match_previous_literal
matchPreviousExpr = match_previous_expr
oneOf = one_of
dictOf = dict_of
originalTextFor = original_text_for
nestedExpr = nested_expr
makeHTMLTags = make_html_tags
makeXMLTags = make_xml_tags
anyOpenTag, anyCloseTag = any_open_tag, any_close_tag
commonHTMLEntity = common_html_entity
replaceHTMLEntity = replace_html_entity
# fmt: off
opAssoc = OpAssoc
infixNotation = infix_notation
anyOpenTag = any_open_tag
anyCloseTag = any_close_tag
commonHTMLEntity = common_html_entity
cStyleComment = c_style_comment
htmlComment = html_comment
restOfLine = rest_of_line
@ -1086,3 +1058,43 @@ dblSlashComment = dbl_slash_comment
cppStyleComment = cpp_style_comment
javaStyleComment = java_style_comment
pythonStyleComment = python_style_comment
@replaced_by_pep8(DelimitedList)
def delimitedList(): ...
@replaced_by_pep8(DelimitedList)
def delimited_list(): ...
@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

View file

@ -1,18 +1,25 @@
# results.py
from collections.abc import MutableMapping, Mapping, MutableSequence, Iterator
from collections.abc import (
MutableMapping,
Mapping,
MutableSequence,
Iterator,
Sequence,
Container,
)
import pprint
from weakref import ref as wkref
from typing import Tuple, Any
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, p2):
self.tup = (p1, p2)
def __init__(self, p1: "ParseResults", p2: int):
self.tup: Tuple[ParseResults, int] = (p1, p2)
def __getitem__(self, i):
return self.tup[i]
@ -47,7 +54,7 @@ class ParseResults:
result = date_str.parse_string("1999/12/31")
def test(s, fn=repr):
print("{} -> {}".format(s, fn(eval(s))))
print(f"{s} -> {fn(eval(s))}")
test("list(result)")
test("result[0]")
test("result['month']")
@ -70,27 +77,33 @@ class ParseResults:
- year: '1999'
"""
_null_values: Tuple[Any, ...] = (None, [], "", ())
_null_values: Tuple[Any, ...] = (None, [], ())
__slots__ = [
_name: str
_parent: "ParseResults"
_all_names: Set[str]
_modal: bool
_toklist: List[Any]
_tokdict: Dict[str, Any]
__slots__ = (
"_name",
"_parent",
"_all_names",
"_modal",
"_toklist",
"_tokdict",
"__weakref__",
]
)
class List(list):
"""
Simple wrapper class to distinguish parsed list results that should be preserved
as actual Python lists, instead of being converted to :class:`ParseResults`:
as actual Python lists, instead of being converted to :class:`ParseResults`::
LBRACK, RBRACK = map(pp.Suppress, "[]")
element = pp.Forward()
item = ppc.integer
element_list = LBRACK + pp.delimited_list(element) + RBRACK
element_list = LBRACK + pp.DelimitedList(element) + RBRACK
# add parse actions to convert from ParseResults to actual Python collection types
def as_python_list(t):
@ -107,7 +120,7 @@ class ParseResults:
(2,3,4)
''', post_parse=lambda s, r: (r[0], type(r[0])))
prints:
prints::
100
(100, <class 'int'>)
@ -127,8 +140,7 @@ class ParseResults:
if not isinstance(contained, list):
raise TypeError(
"{} may only be constructed with a list,"
" not {}".format(cls.__name__, type(contained).__name__)
f"{cls.__name__} may only be constructed with a list, not {type(contained).__name__}"
)
return list.__new__(cls)
@ -159,6 +171,7 @@ class ParseResults:
def __init__(
self, toklist=None, name=None, asList=True, modal=True, isinstance=isinstance
):
self._tokdict: Dict[str, _ParseResultsWithOffset]
self._modal = modal
if name is not None and name != "":
if isinstance(name, int):
@ -210,7 +223,7 @@ class ParseResults:
]
sub = v
if isinstance(sub, ParseResults):
sub._parent = wkref(self)
sub._parent = self
def __delitem__(self, i):
if isinstance(i, (int, slice)):
@ -263,7 +276,7 @@ class ParseResults:
"""
Since ``keys()`` returns an iterator, this method is helpful in bypassing
code that looks for the existence of any defined results names."""
return bool(self._tokdict)
return not not self._tokdict
def pop(self, *args, **kwargs):
"""
@ -311,9 +324,7 @@ class ParseResults:
if k == "default":
args = (args[0], v)
else:
raise TypeError(
"pop() got an unexpected keyword argument {!r}".format(k)
)
raise TypeError(f"pop() got an unexpected keyword argument {k!r}")
if isinstance(args[0], int) or len(args) == 1 or args[0] in self:
index = args[0]
ret = self[index]
@ -423,12 +434,15 @@ class ParseResults:
raise AttributeError(name)
return ""
def __add__(self, other) -> "ParseResults":
def __add__(self, other: "ParseResults") -> "ParseResults":
ret = self.copy()
ret += other
return ret
def __iadd__(self, other) -> "ParseResults":
def __iadd__(self, other: "ParseResults") -> "ParseResults":
if not other:
return self
if other._tokdict:
offset = len(self._toklist)
addoffset = lambda a: offset if a < 0 else a + offset
@ -441,7 +455,7 @@ class ParseResults:
for k, v in otherdictitems:
self[k] = v
if isinstance(v[0], ParseResults):
v[0]._parent = wkref(self)
v[0]._parent = self
self._toklist += other._toklist
self._all_names |= other._all_names
@ -456,7 +470,7 @@ class ParseResults:
return other + self
def __repr__(self) -> str:
return "{}({!r}, {})".format(type(self).__name__, self._toklist, self.as_dict())
return f"{type(self).__name__}({self._toklist!r}, {self.as_dict()})"
def __str__(self) -> str:
return (
@ -532,7 +546,10 @@ class ParseResults:
def copy(self) -> "ParseResults":
"""
Returns a new copy of a :class:`ParseResults` object.
Returns a new shallow copy of a :class:`ParseResults` object. `ParseResults`
items contained within the source are shared with the copy. Use
:class:`ParseResults.deepcopy()` to create a copy with its own separate
content values.
"""
ret = ParseResults(self._toklist)
ret._tokdict = self._tokdict.copy()
@ -541,6 +558,27 @@ class ParseResults:
ret._name = self._name
return ret
def deepcopy(self) -> "ParseResults":
"""
Returns a new deep copy of a :class:`ParseResults` object.
"""
ret = self.copy()
# replace values with copies if they are of known mutable types
for i, obj in enumerate(self._toklist):
if isinstance(obj, ParseResults):
self._toklist[i] = obj.deepcopy()
elif isinstance(obj, (str, bytes)):
pass
elif isinstance(obj, MutableMapping):
self._toklist[i] = dest = type(obj)()
for k, v in obj.items():
dest[k] = v.deepcopy() if isinstance(v, ParseResults) else v
elif isinstance(obj, Container):
self._toklist[i] = type(obj)(
v.deepcopy() if isinstance(v, ParseResults) else v for v in obj
)
return ret
def get_name(self):
r"""
Returns the results name for this token expression. Useful when several
@ -569,20 +607,17 @@ class ParseResults:
if self._name:
return self._name
elif self._parent:
par = self._parent()
def find_in_parent(sub):
par: "ParseResults" = self._parent
parent_tokdict_items = par._tokdict.items()
return next(
(
k
for k, vlist in par._tokdict.items()
for k, vlist in parent_tokdict_items
for v, loc in vlist
if sub is v
if v is self
),
None,
)
return find_in_parent(self) if par else None
elif (
len(self) == 1
and len(self._tokdict) == 1
@ -623,7 +658,7 @@ class ParseResults:
for k, v in items:
if out:
out.append(NL)
out.append("{}{}- {}: ".format(indent, (" " * _depth), k))
out.append(f"{indent}{(' ' * _depth)}- {k}: ")
if isinstance(v, ParseResults):
if v:
out.append(
@ -685,7 +720,7 @@ class ParseResults:
num = Word(nums)
func = Forward()
term = ident | num | Group('(' + func + ')')
func <<= ident + Group(Optional(delimited_list(term)))
func <<= ident + Group(Optional(DelimitedList(term)))
result = func.parse_string("fna a,b,(fnb c,d,200),100")
result.pprint(width=40)
@ -705,7 +740,7 @@ class ParseResults:
self._toklist,
(
self._tokdict.copy(),
self._parent is not None and self._parent() or None,
None,
self._all_names,
self._name,
),
@ -714,9 +749,6 @@ class ParseResults:
def __setstate__(self, state):
self._toklist, (self._tokdict, par, inAccumNames, self._name) = state
self._all_names = set(inAccumNames)
if par is not None:
self._parent = wkref(par)
else:
self._parent = None
def __getnewargs__(self):
@ -738,6 +770,7 @@ class ParseResults:
iter(obj)
except Exception:
return False
# str's are iterable, but in pyparsing, we don't want to iterate over them
else:
return not isinstance(obj, str_type)
@ -752,8 +785,11 @@ class ParseResults:
return ret
asList = as_list
"""Deprecated - use :class:`as_list`"""
asDict = as_dict
"""Deprecated - use :class:`as_dict`"""
getName = get_name
"""Deprecated - use :class:`get_name`"""
MutableMapping.register(ParseResults)

View file

@ -222,7 +222,7 @@ class pyparsing_test:
)
else:
# warning here maybe?
print("no validation for {!r}".format(test_string))
print(f"no validation for {test_string!r}")
# do this last, in case some specific test results can be reported instead
self.assertTrue(
@ -265,15 +265,18 @@ class pyparsing_test:
if expand_tabs:
s = s.expandtabs()
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 != " ":
@ -303,7 +306,7 @@ class pyparsing_test:
header0 = (
lead
+ "".join(
"{}{}".format(" " * 99, (i + 1) % 100)
f"{' ' * 99}{(i + 1) % 100}"
for i in range(max(max_line_len // 100, 1))
)
+ "\n"
@ -313,10 +316,7 @@ class pyparsing_test:
header1 = (
header0
+ lead
+ "".join(
" {}".format((i + 1) % 10)
for i in range(-(-max_line_len // 10))
)
+ "".join(f" {(i + 1) % 10}" for i in range(-(-max_line_len // 10)))
+ "\n"
)
header2 = lead + "1234567890" * (-(-max_line_len // 10)) + "\n"
@ -324,7 +324,7 @@ class pyparsing_test:
header1
+ header2
+ "\n".join(
"{:{}d}:{}{}".format(i, lineno_width, line, eol_mark)
f"{i:{lineno_width}d}:{line}{eol_mark}"
for i, line in enumerate(s_lines, start=start_line)
)
+ "\n"

View file

@ -64,27 +64,27 @@ class unicode_set:
@_lazyclassproperty
def printables(cls):
"all non-whitespace characters in this range"
"""all non-whitespace characters in this range"""
return "".join(filterfalse(str.isspace, cls._chars_for_ranges))
@_lazyclassproperty
def alphas(cls):
"all alphabetic characters in this range"
"""all alphabetic characters in this range"""
return "".join(filter(str.isalpha, cls._chars_for_ranges))
@_lazyclassproperty
def nums(cls):
"all numeric digit characters in this range"
"""all numeric digit characters in this range"""
return "".join(filter(str.isdigit, cls._chars_for_ranges))
@_lazyclassproperty
def alphanums(cls):
"all alphanumeric characters in this range"
"""all alphanumeric characters in this range"""
return cls.alphas + cls.nums
@_lazyclassproperty
def identchars(cls):
"all characters in this range that are valid identifier characters, plus underscore '_'"
"""all characters in this range that are valid identifier characters, plus underscore '_'"""
return "".join(
sorted(
set(
@ -100,13 +100,13 @@ class unicode_set:
def identbodychars(cls):
"""
all characters in this range that are valid identifier body characters,
plus the digits 0-9
plus the digits 0-9, and · (Unicode MIDDLE DOT)
"""
return "".join(
sorted(
set(
cls.identchars
+ "0123456789"
+ "0123456789·"
+ "".join(
[c for c in cls._chars_for_ranges if ("_" + c).isidentifier()]
)
@ -114,6 +114,16 @@ class unicode_set:
)
)
@_lazyclassproperty
def identifier(cls):
"""
a pyparsing Word expression for an identifier using this range's definitions for
identchars and identbodychars
"""
from pyparsing import Word
return Word(cls.identchars, cls.identbodychars)
class pyparsing_unicode(unicode_set):
"""
@ -128,32 +138,32 @@ class pyparsing_unicode(unicode_set):
]
class BasicMultilingualPlane(unicode_set):
"Unicode set for the Basic Multilingual Plane"
"""Unicode set for the Basic Multilingual Plane"""
_ranges: UnicodeRangeList = [
(0x0020, 0xFFFF),
]
class Latin1(unicode_set):
"Unicode set for Latin-1 Unicode Character Range"
"""Unicode set for Latin-1 Unicode Character Range"""
_ranges: UnicodeRangeList = [
(0x0020, 0x007E),
(0x00A0, 0x00FF),
]
class LatinA(unicode_set):
"Unicode set for Latin-A Unicode Character Range"
"""Unicode set for Latin-A Unicode Character Range"""
_ranges: UnicodeRangeList = [
(0x0100, 0x017F),
]
class LatinB(unicode_set):
"Unicode set for Latin-B Unicode Character Range"
"""Unicode set for Latin-B Unicode Character Range"""
_ranges: UnicodeRangeList = [
(0x0180, 0x024F),
]
class Greek(unicode_set):
"Unicode set for Greek Unicode Character Ranges"
"""Unicode set for Greek Unicode Character Ranges"""
_ranges: UnicodeRangeList = [
(0x0342, 0x0345),
(0x0370, 0x0377),
@ -193,7 +203,7 @@ class pyparsing_unicode(unicode_set):
]
class Cyrillic(unicode_set):
"Unicode set for Cyrillic Unicode Character Range"
"""Unicode set for Cyrillic Unicode Character Range"""
_ranges: UnicodeRangeList = [
(0x0400, 0x052F),
(0x1C80, 0x1C88),
@ -206,7 +216,7 @@ class pyparsing_unicode(unicode_set):
]
class Chinese(unicode_set):
"Unicode set for Chinese Unicode Character Range"
"""Unicode set for Chinese Unicode Character Range"""
_ranges: UnicodeRangeList = [
(0x2E80, 0x2E99),
(0x2E9B, 0x2EF3),
@ -229,8 +239,7 @@ class pyparsing_unicode(unicode_set):
]
class Japanese(unicode_set):
"Unicode set for Japanese Unicode Character Range, combining Kanji, Hiragana, and Katakana ranges"
_ranges: UnicodeRangeList = []
"""Unicode set for Japanese Unicode Character Range, combining Kanji, Hiragana, and Katakana ranges"""
class Kanji(unicode_set):
"Unicode set for Kanji Unicode Character Range"
@ -240,7 +249,7 @@ class pyparsing_unicode(unicode_set):
]
class Hiragana(unicode_set):
"Unicode set for Hiragana Unicode Character Range"
"""Unicode set for Hiragana Unicode Character Range"""
_ranges: UnicodeRangeList = [
(0x3041, 0x3096),
(0x3099, 0x30A0),
@ -252,7 +261,7 @@ class pyparsing_unicode(unicode_set):
]
class Katakana(unicode_set):
"Unicode set for Katakana Unicode Character Range"
"""Unicode set for Katakana Unicode Character Range"""
_ranges: UnicodeRangeList = [
(0x3099, 0x309C),
(0x30A0, 0x30FF),
@ -265,8 +274,18 @@ class pyparsing_unicode(unicode_set):
(0x1F213,),
]
漢字 = Kanji
カタカナ = Katakana
ひらがな = Hiragana
_ranges = (
Kanji._ranges
+ Hiragana._ranges
+ Katakana._ranges
)
class Hangul(unicode_set):
"Unicode set for Hangul (Korean) Unicode Character Range"
"""Unicode set for Hangul (Korean) Unicode Character Range"""
_ranges: UnicodeRangeList = [
(0x1100, 0x11FF),
(0x302E, 0x302F),
@ -288,17 +307,17 @@ class pyparsing_unicode(unicode_set):
Korean = Hangul
class CJK(Chinese, Japanese, Hangul):
"Unicode set for combined Chinese, Japanese, and Korean (CJK) Unicode Character Range"
"""Unicode set for combined Chinese, Japanese, and Korean (CJK) Unicode Character Range"""
class Thai(unicode_set):
"Unicode set for Thai Unicode Character Range"
"""Unicode set for Thai Unicode Character Range"""
_ranges: UnicodeRangeList = [
(0x0E01, 0x0E3A),
(0x0E3F, 0x0E5B)
]
class Arabic(unicode_set):
"Unicode set for Arabic Unicode Character Range"
"""Unicode set for Arabic Unicode Character Range"""
_ranges: UnicodeRangeList = [
(0x0600, 0x061B),
(0x061E, 0x06FF),
@ -306,7 +325,7 @@ class pyparsing_unicode(unicode_set):
]
class Hebrew(unicode_set):
"Unicode set for Hebrew Unicode Character Range"
"""Unicode set for Hebrew Unicode Character Range"""
_ranges: UnicodeRangeList = [
(0x0591, 0x05C7),
(0x05D0, 0x05EA),
@ -320,33 +339,23 @@ class pyparsing_unicode(unicode_set):
]
class Devanagari(unicode_set):
"Unicode set for Devanagari Unicode Character Range"
"""Unicode set for Devanagari Unicode Character Range"""
_ranges: UnicodeRangeList = [
(0x0900, 0x097F),
(0xA8E0, 0xA8FF)
]
# fmt: on
pyparsing_unicode.Japanese._ranges = (
pyparsing_unicode.Japanese.Kanji._ranges
+ pyparsing_unicode.Japanese.Hiragana._ranges
+ pyparsing_unicode.Japanese.Katakana._ranges
)
pyparsing_unicode.BMP = pyparsing_unicode.BasicMultilingualPlane
BMP = BasicMultilingualPlane
# add language identifiers using language Unicode
pyparsing_unicode.العربية = pyparsing_unicode.Arabic
pyparsing_unicode.中文 = pyparsing_unicode.Chinese
pyparsing_unicode.кириллица = pyparsing_unicode.Cyrillic
pyparsing_unicode.Ελληνικά = pyparsing_unicode.Greek
pyparsing_unicode.עִברִית = pyparsing_unicode.Hebrew
pyparsing_unicode.日本語 = pyparsing_unicode.Japanese
pyparsing_unicode.Japanese.漢字 = pyparsing_unicode.Japanese.Kanji
pyparsing_unicode.Japanese.カタカナ = pyparsing_unicode.Japanese.Katakana
pyparsing_unicode.Japanese.ひらがな = pyparsing_unicode.Japanese.Hiragana
pyparsing_unicode.한국어 = pyparsing_unicode.Korean
pyparsing_unicode.ไทย = pyparsing_unicode.Thai
pyparsing_unicode.वनगर = pyparsing_unicode.Devanagari
العربية = Arabic
中文 = Chinese
кириллица = Cyrillic
Ελληνικά = Greek
עִברִית = Hebrew
日本語 = Japanese
한국어 = Korean
ไทย = Thai
वनगर = Devanagari
# fmt: on

View file

@ -1,12 +1,14 @@
# util.py
import inspect
import warnings
import types
import collections
import itertools
from functools import lru_cache
from typing import List, Union, Iterable
from functools import lru_cache, wraps
from typing import Callable, List, Union, Iterable, TypeVar, cast
_bslash = chr(92)
C = TypeVar("C", bound=Callable)
class __config_flags:
@ -20,18 +22,15 @@ class __config_flags:
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(),
)
f"{cls.__name__}.{dname} {cls._type_desc} is {str(getattr(cls, dname)).upper()}"
f" and cannot be overridden",
stacklevel=3,
)
return
if dname in cls._all_names:
setattr(cls, dname, value)
else:
raise ValueError("no such {} {!r}".format(cls._type_desc, dname))
raise ValueError(f"no such {cls._type_desc} {dname!r}")
enable = classmethod(lambda cls, name: cls._set(name, True))
disable = classmethod(lambda cls, name: cls._set(name, False))
@ -45,7 +44,7 @@ def col(loc: int, strg: str) -> int:
Note: the default parsing behavior is to expand tabs in the input string
before starting the parsing process. See
:class:`ParserElement.parseString` for more
:class:`ParserElement.parse_string` for more
information on parsing strings containing ``<TAB>`` s, and suggested
methods to maintain a consistent view of the parsed string, the parse
location, and line and column positions within the parsed string.
@ -60,7 +59,7 @@ def lineno(loc: int, strg: str) -> int:
The first line is number 1.
Note - the default parsing behavior is to expand tabs in the input string
before starting the parsing process. See :class:`ParserElement.parseString`
before starting the parsing process. See :class:`ParserElement.parse_string`
for more information on parsing strings containing ``<TAB>`` s, and
suggested methods to maintain a consistent view of the parsed string, the
parse location, and line and column positions within the parsed string.
@ -102,19 +101,24 @@ class _UnboundedCache:
class _FifoCache:
def __init__(self, size):
self.not_in_cache = not_in_cache = object()
cache = collections.OrderedDict()
cache = {}
keyring = [object()] * size
cache_get = cache.get
cache_pop = cache.pop
keyiter = itertools.cycle(range(size))
def get(_, key):
return cache_get(key, not_in_cache)
def set_(_, key, value):
cache[key] = value
while len(cache) > size:
cache.popitem(last=False)
i = next(keyiter)
cache_pop(keyring[i], None)
keyring[i] = key
def clear(_):
cache.clear()
keyring[:] = [object()] * size
self.size = size
self.get = types.MethodType(get, self)
@ -189,9 +193,9 @@ def _collapse_string_to_ranges(
is_consecutive.value = next(is_consecutive.counter)
return is_consecutive.value
is_consecutive.prev = 0
is_consecutive.counter = itertools.count()
is_consecutive.value = -1
is_consecutive.prev = 0 # type: ignore [attr-defined]
is_consecutive.counter = itertools.count() # type: ignore [attr-defined]
is_consecutive.value = -1 # type: ignore [attr-defined]
def escape_re_range_char(c):
return "\\" + c if c in r"\^-][" else c
@ -215,9 +219,7 @@ def _collapse_string_to_ranges(
else:
sep = "" if ord(last) == ord(first) + 1 else "-"
ret.append(
"{}{}{}".format(
escape_re_range_char(first), sep, escape_re_range_char(last)
)
f"{escape_re_range_char(first)}{sep}{escape_re_range_char(last)}"
)
else:
ret = [escape_re_range_char(c) for c in s]
@ -233,3 +235,50 @@ def _flatten(ll: list) -> list:
else:
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.
# Unwrap staticmethod/classmethod
fn = getattr(fn, "__func__", fn)
# (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]:
@wraps(fn)
def _inner(self, *args, **kwargs):
# warnings.warn(
# f"Deprecated - use {fn.__name__}", DeprecationWarning, stacklevel=3
# )
return fn(self, *args, **kwargs)
else:
@wraps(fn)
def _inner(*args, **kwargs):
# warnings.warn(
# f"Deprecated - use {fn.__name__}", DeprecationWarning, stacklevel=3
# )
return fn(*args, **kwargs)
_inner.__doc__ = f"""Deprecated - use :class:`{fn.__name__}`"""
_inner.__name__ = compat_name
_inner.__annotations__ = fn.__annotations__
if isinstance(fn, types.FunctionType):
_inner.__kwdefaults__ = fn.__kwdefaults__
elif isinstance(fn, type) and hasattr(fn, "__init__"):
_inner.__kwdefaults__ = fn.__init__.__kwdefaults__
else:
_inner.__kwdefaults__ = None
_inner.__qualname__ = fn.__qualname__
return cast(C, _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)

View file

@ -32,7 +32,7 @@ plexapi==4.13.4
portend==3.2.0
profilehooks==1.12.0
PyJWT==2.8.0
pyparsing==3.0.9
pyparsing==3.1.1
python-dateutil==2.8.2
python-twitter==3.5
pytz==2023.3