Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Backport recent fixes to 5.7.5 release with Python 3.8 and later compatibility #340

Merged
merged 5 commits into from
Jun 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion .github/workflows/default.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ jobs:
python-version: [
'3.12',
'3.11',
'3.10',
'3.9',
'3.8',
]

steps:
Expand All @@ -33,7 +36,7 @@ jobs:
python -m pip install --upgrade pip
pip install --upgrade --requirement requirements-test.txt
- name: lint
# if: ${{ matrix.python-version == '3.12' }}
if: ${{ matrix.python-version == '3.12' }}
run: |
make lint
- name: pytest
Expand All @@ -43,6 +46,7 @@ jobs:
run: |
make examples
- name: documentation
if: ${{ matrix.python-version == '3.12' }}
run: |
make documentation
- name: distributions
Expand Down
5 changes: 4 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ authors = [
]
description = "TatSu takes a grammar in a variation of EBNF as input, and outputs a memoizing PEG/Packrat parser in Python."
readme = "README.rst"
requires-python = ">=3.11"
requires-python = ">=3.8"
keywords = []
license = {file = "LICENSE.TXT"}
classifiers = [
Expand All @@ -21,6 +21,9 @@ classifiers = [
"Intended Audience :: Science/Research",
"Environment :: Console",
"Operating System :: OS Independent",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Topic :: Software Development :: Code Generators",
Expand Down
2 changes: 1 addition & 1 deletion ruff.toml
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ lint.ignore = [
]
exclude = []

target-version = "py312"
target-version = "py38"

[lint.per-file-ignores]

Expand Down
2 changes: 1 addition & 1 deletion tatsu/_version.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = '5.12.2b1'
__version__ = '5.7.5'
2 changes: 2 additions & 0 deletions tatsu/bootstrap.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@

# ruff: noqa: C405, COM812, I001, F401, PLR1702, PLC2801, SIM117

from __future__ import annotations

import sys
from pathlib import Path

Expand Down
21 changes: 10 additions & 11 deletions tatsu/collections/orderedset.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
# NOTE: from https://github.com/LuminosoInsight/ordered-set/blob/master/ordered_set.py
from __future__ import annotations

import itertools
from collections.abc import (
Iterable,
Iterator,
Mapping,
MutableSequence,
MutableSet,
Sequence,
)
from typing import Any, TypeVar
from typing import Any, Iterable, MutableSet, Sequence, TypeVar

T = TypeVar('T')

Expand All @@ -29,7 +28,7 @@ def __getitem__(self, i):
self._list_cache = list(self._map.keys())
return self._list_cache[i]

def copy(self) -> 'OrderedSet[T]':
def copy(self) -> OrderedSet[T]:
return self.__class__(self)

def __getstate__(self):
Expand Down Expand Up @@ -78,25 +77,25 @@ def __repr__(self) -> str:
def __eq__(self, other: Any) -> bool:
return all(item in other for item in self)

def union(self, *other: Iterable[T]) -> 'OrderedSet[T]':
def union(self, *other: Iterable[T]) -> OrderedSet[T]:
# do not split `str`
outer = tuple(
[o] if not isinstance(o, set | Mapping | MutableSequence) else o
[o] if not isinstance(o, (set, Mapping, MutableSequence)) else o
for o in other
)
inner = itertools.chain([self], *outer)
items = itertools.chain.from_iterable(inner)
return type(self)(itertools.chain(items))

def __and__(self, other: Iterable[Iterable[T]]) -> 'OrderedSet[T]':
def __and__(self, other: Iterable[Iterable[T]]) -> OrderedSet[T]:
return self.intersection(other)

def intersection(self, *other: Iterable[Iterable[T]]) -> 'OrderedSet[T]':
def intersection(self, *other: Iterable[Iterable[T]]) -> OrderedSet[T]:
common = set.intersection(*other) # type: ignore[var-annotated, arg-type]
items = (item for item in self if item in common)
return type(self)(items)

def difference(self, *other: Iterable[T]) -> 'OrderedSet[T]':
def difference(self, *other: Iterable[T]) -> OrderedSet[T]:
other = set.union(*other) # type: ignore[assignment, arg-type]
items = (item for item in self if item not in other)
return type(self)(items)
Expand All @@ -109,7 +108,7 @@ def issuperset(self, other: set[T]) -> bool:
return False
return all(item in self for item in other)

def symmetric_difference(self, other: set[T]) -> 'OrderedSet[T]':
def symmetric_difference(self, other: set[T]) -> OrderedSet[T]:
cls = type(self)
diff1 = cls(self).difference(other)
diff2 = cls(other).difference(self)
Expand Down
4 changes: 2 additions & 2 deletions tatsu/g2e/semantics.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,12 +85,12 @@ def syntactic_predicate(self, ast):
return None

def optional(self, ast):
if isinstance(ast, model.Group | model.Optional | model.Closure):
if isinstance(ast, (model.Group, model.Optional, model.Closure)):
ast = ast.exp
return model.Optional(ast)

def closure(self, ast):
if isinstance(ast, model.Group | model.Optional):
if isinstance(ast, (model.Group, model.Optional)):
ast = ast.exp
return model.Closure(ast)

Expand Down
2 changes: 1 addition & 1 deletion tatsu/grammars.py
Original file line number Diff line number Diff line change
Expand Up @@ -865,7 +865,7 @@ def _nullable(self):

@staticmethod
def param_repr(p):
if isinstance(p, int | float) or (isinstance(p, str) and p.isalnum()):
if isinstance(p, (int, float)) or (isinstance(p, str) and p.isalnum()):
return str(p)
else:
return repr(p)
Expand Down
9 changes: 8 additions & 1 deletion tatsu/infos.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import copy
import dataclasses
import re
import sys
from collections.abc import Callable, Mapping
from itertools import starmap
from typing import Any, NamedTuple
Expand All @@ -11,6 +12,12 @@
from .tokenizing import Tokenizer
from .util.unicode_characters import C_DERIVE

if sys.version_info < (3, 10):
import builtins

def zip(*iterables, strict=False):
return builtins.zip(*iterables)


class UndefinedStr(str):
pass
Expand Down Expand Up @@ -234,7 +241,7 @@ class RuleResult(NamedTuple):
newstate: Any


@dataclasses.dataclass(slots=True)
@dataclasses.dataclass(**({'slots': True} if sys.version_info >= (3, 10) else {}))
class ParseState:
pos: int = 0
ast: AST = dataclasses.field(default_factory=AST)
Expand Down
2 changes: 2 additions & 0 deletions tatsu/mixins/indent.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from __future__ import annotations

import io
from contextlib import contextmanager

Expand Down
2 changes: 2 additions & 0 deletions tatsu/ngcodegen/objectmodel.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from __future__ import annotations

import builtins
from collections import namedtuple

Expand Down
4 changes: 3 additions & 1 deletion tatsu/ngcodegen/python.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@

# ruff: noqa: C405, COM812, I001, F401, PLR1702, PLC2801, SIM117

from __future__ import annotations

import sys
from pathlib import Path

Expand Down Expand Up @@ -94,7 +96,7 @@ def walk_Grammar(self, grammar: grammars.Grammar):

def walk_Rule(self, rule: grammars.Rule):
def param_repr(p):
if isinstance(p, int | float):
if isinstance(p, (int, float)):
return str(p)
else:
return repr(p.split('::')[0])
Expand Down
4 changes: 2 additions & 2 deletions tatsu/objectmodel.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ def with_parent(node):
return node

def children_of(child):
if isinstance(child, weakref.ReferenceType | weakref.ProxyType):
if isinstance(child, (weakref.ReferenceType, weakref.ProxyType)):
return
elif isinstance(child, Node):
yield with_parent(child)
Expand All @@ -121,7 +121,7 @@ def children_of(child):
if name.startswith('_'):
continue
yield from children_of(value)
elif isinstance(child, list | tuple):
elif isinstance(child, (list, tuple)):
yield from (
with_parent(c) for c in child if isinstance(c, Node)
)
Expand Down
2 changes: 2 additions & 0 deletions tatsu/parser.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from __future__ import annotations

import re
from typing import Any

Expand Down
2 changes: 2 additions & 0 deletions tatsu/synth.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from __future__ import annotations

from collections.abc import Mapping
from typing import Any

Expand Down
10 changes: 5 additions & 5 deletions tatsu/util/_common.py
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ def decode_match(match):

def isiter(value):
return isinstance(value, Iterable) and not isinstance(
value, str | bytes | bytearray,
value, (str, bytes, bytearray),
)


Expand Down Expand Up @@ -241,19 +241,19 @@ def timestamp():


def asjson(obj, seen=None): # noqa: PLR0911, PLR0912
if obj is None or isinstance(obj, int | float | str | bool):
if obj is None or isinstance(obj, (int, float, str, bool)):
return obj

if seen is None:
seen = set()
elif id(obj) in seen:
return f'{type(obj).__name__}@{id(obj)}'

if isinstance(obj, Mapping | AsJSONMixin) or isiter(obj):
if isinstance(obj, (Mapping, AsJSONMixin)) or isiter(obj):
seen.add(id(obj))

try:
if isinstance(obj, weakref.ReferenceType | weakref.ProxyType):
if isinstance(obj, (weakref.ReferenceType, weakref.ProxyType)):
return f'{obj.__class__.__name__}@0x{hex(id(obj)).upper()[2:]}'
elif hasattr(obj, '__json__'):
return obj.__json__(seen=seen)
Expand Down Expand Up @@ -304,7 +304,7 @@ def plainjson(obj):
for name, value in obj.items()
if name not in {'__class__', 'parseinfo'}
}
elif isinstance(obj, weakref.ReferenceType | weakref.ProxyType):
elif isinstance(obj, (weakref.ReferenceType, weakref.ProxyType)):
return '@ref'
elif isinstance(obj, str) and obj.startswith('@'):
return '@ref'
Expand Down
2 changes: 1 addition & 1 deletion tatsu/walkers.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ def __init__(self):
)._walker_cache # pylint: disable=no-member

def walk(self, node: Node | list[Node], *args, **kwargs) -> Any:
if isinstance(node, list | tuple):
if isinstance(node, (list, tuple)):
return [self.walk(n, *args, **kwargs) for n in node]

if isinstance(node, Mapping):
Expand Down
2 changes: 1 addition & 1 deletion test/grammar/semantics_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ def test_builder_basetype_codegen(self):
self.assertTrue(hasattr(ast, 'a'))
self.assertTrue(hasattr(ast, 'b'))

self.assertTrue(issubclass(D, A | B | C))
self.assertTrue(issubclass(D, (A, B, C)))

def test_optional_attributes(self):
grammar = r"""
Expand Down
Loading