Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package python-pyleri for openSUSE:Factory 
checked in at 2026-04-22 17:20:56
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-pyleri (Old)
 and      /work/SRC/openSUSE:Factory/.python-pyleri.new.11940 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-pyleri"

Wed Apr 22 17:20:56 2026 rev:4 rq:1348801 version:1.5.0

Changes:
--------
--- /work/SRC/openSUSE:Factory/python-pyleri/python-pyleri.changes      
2024-04-07 22:13:20.674727600 +0200
+++ /work/SRC/openSUSE:Factory/.python-pyleri.new.11940/python-pyleri.changes   
2026-04-22 17:20:58.488954218 +0200
@@ -1,0 +2,8 @@
+Mon Apr 20 14:33:55 UTC 2026 - John Paul Adrian Glaubitz 
<[email protected]>
+
+- Update to 1.5.0
+  * Added Typing.
+  * Dropped support for Python 3.7 and 3.8.
+- Update upstream URL in Source field
+
+-------------------------------------------------------------------

Old:
----
  pyleri-1.4.3.tar.gz

New:
----
  pyleri-1.5.0.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ python-pyleri.spec ++++++
--- /var/tmp/diff_new_pack.pXB575/_old  2026-04-22 17:20:59.224984616 +0200
+++ /var/tmp/diff_new_pack.pXB575/_new  2026-04-22 17:20:59.228984781 +0200
@@ -1,7 +1,7 @@
 #
 # spec file for package python-pyleri
 #
-# Copyright (c) 2024 SUSE LLC
+# Copyright (c) 2026 SUSE LLC and contributors
 # Copyright (c) 2021, Martin Hauke <[email protected]>
 #
 # All modifications and additions to the file contributed by third parties
@@ -19,12 +19,12 @@
 
 %{?!python_module:%define python_module() python-%{**} python3-%{**}}
 Name:           python-pyleri
-Version:        1.4.3
+Version:        1.5.0
 Release:        0
 Summary:        Python Left-Right Parser
 License:        MIT
 URL:            https://github.com/transceptor-technology/pyleri
-Source:         
https://github.com/transceptor-technology/pyleri/archive/refs/tags/%{version}.tar.gz#/pyleri-%{version}.tar.gz
+Source:         
https://github.com/transceptor-technology/pyleri/archive/refs/tags/v%{version}.tar.gz#/pyleri-%{version}.tar.gz
 BuildRequires:  %{python_module pip}
 BuildRequires:  %{python_module pytest}
 BuildRequires:  %{python_module setuptools}

++++++ pyleri-1.4.3.tar.gz -> pyleri-1.5.0.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pyleri-1.4.3/.github/workflows/ci.yml 
new/pyleri-1.5.0/.github/workflows/ci.yml
--- old/pyleri-1.4.3/.github/workflows/ci.yml   2024-03-26 10:19:45.000000000 
+0100
+++ new/pyleri-1.5.0/.github/workflows/ci.yml   2025-11-12 21:19:22.000000000 
+0100
@@ -12,22 +12,25 @@
     runs-on: ubuntu-latest
     strategy:
       matrix:
-        python-version: ["3.7", "3.8", "3.9", "3.10"]
+        python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"]
 
     steps:
-      - uses: actions/checkout@v3
+      - uses: actions/checkout@v5
       - name: Set up Python ${{ matrix.python-version }}
-        uses: actions/setup-python@v4
+        uses: actions/setup-python@v6
         with:
           python-version: ${{ matrix.python-version }}
       - name: Install dependencies
         run: |
           python -m pip install --upgrade pip
-          pip install pytest pycodestyle
+          pip install pytest pycodestyle pyright
           if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
       - name: Run tests with pytest
         run: |
           pytest
       - name: Lint with PyCodeStyle
         run: |
-          find . -name \*.py -exec pycodestyle {} +
\ No newline at end of file
+          find . -name \*.py -exec pycodestyle {} +
+      - name: Check with PyRight
+        run: |
+          pyright
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pyleri-1.4.3/pyleri/__init__.py 
new/pyleri-1.5.0/pyleri/__init__.py
--- old/pyleri-1.4.3/pyleri/__init__.py 2024-03-26 10:19:45.000000000 +0100
+++ new/pyleri-1.5.0/pyleri/__init__.py 2025-11-12 21:19:22.000000000 +0100
@@ -41,4 +41,4 @@
 __author__ = 'Jeroen van der Heijden'
 __maintainer__ = 'Jeroen van der Heijden'
 __email__ = '[email protected]'
-__version__ = '1.4.3'
+__version__ = '1.5.0'
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pyleri-1.4.3/pyleri/choice.py 
new/pyleri-1.5.0/pyleri/choice.py
--- old/pyleri-1.4.3/pyleri/choice.py   2024-03-26 10:19:45.000000000 +0100
+++ new/pyleri-1.5.0/pyleri/choice.py   2025-11-12 21:19:22.000000000 +0100
@@ -6,6 +6,7 @@
 
 :copyright: 2021, Jeroen van der Heijden <[email protected]>
 """
+import typing as t
 from .elements import Element, NamedElement, c_export, go_export, java_export
 
 
@@ -15,7 +16,7 @@
 
     def __init__(
             self,
-            *elements: Element,
+            *elements: t.Union[Element, str],
             most_greedy: bool = True):
         self._elements = self._validate_elements(elements)
         self._get_node_result = \
@@ -43,6 +44,7 @@
         return mg_is_valid, mg_pos
 
     def _stop_at_first_match(self, root, tree, rule, s, node):
+        is_valid, pos = True, node.start
         for elem in self._elements:
             children = []
             is_valid, pos = root._walk(elem, node.start, children, rule, True)
@@ -66,10 +68,8 @@
                 new_indent,
                 classes)) for elem in self._elements])
         return 'Choice(\n{val},\n{indent}most_greedy={mg})'.format(
-            mg='{mg}'.format(
-                indent=py_indent * (indent + 1),
-                mg=('False', 'True')[
-                    self._get_node_result == self._most_greedy_result]),
+            mg=('False', 'True')[
+                self._get_node_result == self._most_greedy_result],
             val=value,
             indent=py_indent * new_indent)
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pyleri-1.4.3/pyleri/elements.py 
new/pyleri-1.5.0/pyleri/elements.py
--- old/pyleri-1.4.3/pyleri/elements.py 2024-03-26 10:19:45.000000000 +0100
+++ new/pyleri-1.5.0/pyleri/elements.py 2025-11-12 21:19:22.000000000 +0100
@@ -5,6 +5,10 @@
 :copyright: 2021, Jeroen van der Heijden <[email protected]>
 """
 import typing as t
+from abc import abstractmethod
+if t.TYPE_CHECKING:
+    from .grammar import Grammar
+    from .node import Node
 
 
 def camel_case(s: str) -> str:
@@ -17,10 +21,17 @@
     return ''.join(p[0].upper() + p[1:] for p in s.split('_') if p)
 
 
-_cref = None
+_cref: t.Optional['Element'] = None
+T = t.TypeVar('T', bound='Element')
 
 
-def c_export(func):
+def c_export(func: t.Callable[
+            [T, str, int, t.Set[str], str],
+            str
+        ]) -> t.Callable[
+            [T, str, int, t.Set[str], t.Optional['Element']],
+            str
+        ]:
 
     def wrapper(self, c_indent, indent, enums, ref=None):
         global _cref
@@ -35,8 +46,13 @@
     return wrapper
 
 
-def go_export(func):
-
+def go_export(func: t.Callable[
+            [T, str, int, t.Set[str], str],
+            str
+        ]) -> t.Callable[
+            [T, str, int, t.Set[str]],
+            str
+        ]:
     def wrapper(self, go_indent, indent, enums):
         gid = getattr(self, 'name', getattr(self, '_name', 'NoGid'))
         if gid != 'NoGid':
@@ -47,12 +63,19 @@
     return wrapper
 
 
-def java_export(func):
+def java_export(func: t.Callable[
+            [T, str, int, t.Set[str], t.Set[str], t.Optional[str]],
+            str
+        ]) -> t.Callable[
+            [T, str, int, t.Set[str], t.Set[str]],
+            str
+        ]:
 
     def wrapper(self, java_indent, indent, enums, classes):
         classes.add('jleri.{}'.format(self.__class__.__name__.lstrip('_')))
         gid = getattr(self, 'name', getattr(self, '_name', None))
         if gid is not None:
+            assert isinstance(gid, str)
             gid = gid.upper()
             enums.add(gid)
         return func(self, java_indent, indent, enums, classes, gid)
@@ -63,8 +86,8 @@
 class Element:
 
     __slots__ = tuple()
-
-    name: t.Optional[str]
+    name: str
+    _name: str
 
     @staticmethod
     def _validate_element(element: t.Union[str, 'Element']) -> 'Element':
@@ -77,9 +100,35 @@
             'but received type: {}'.format(type(element)))
 
     @classmethod
-    def _validate_elements(cls, elements) -> t.List['Element']:
+    def _validate_elements(cls,
+                           elements: t.Tuple[t.Union[str, 'Element'], ...]
+                           ) -> t.List['Element']:
         return [cls._validate_element(elem) for elem in elements]
 
+    @abstractmethod
+    def _get_node_result(self,
+                         root: 'Grammar',
+                         tree: t.List['Node'],
+                         rule: 'Element',
+                         s: str,
+                         node: 'Node') -> t.Tuple[bool, int]:
+        pass
+
+    def _export_py(self, *args, **kwargs) -> str:
+        return "must_be_called_on_named_element"
+
+    def _export_js(self, *args, **kwargs) -> str:
+        return "must_be_called_on_named_element"
+
+    def _export_c(self, *args, **kwargs) -> str:
+        return "must_be_called_on_named_element"
+
+    def _export_go(self, *args, **kwargs) -> str:
+        return "must_be_called_on_named_element"
+
+    def _export_java(self, *args, **kwargs) -> str:
+        return "must_be_called_on_named_element"
+
 
 class NamedElement(Element):
 
@@ -87,67 +136,97 @@
 
     def _export_js(
             self,
-            js_indent: int,
+            js_indent: str,
             indent: int,
-            classes,
-            cname) -> str:
+            classes: t.Set[str],
+            cname: t.Optional[str]) -> str:
         classes.add(self.__class__.__name__.lstrip('_'))
         if hasattr(self, 'name') and indent > 0:
             return '{}.{}'.format(cname, self.name) if cname else self.name
         indent = 0 if indent < 0 else 1 if indent == 0 else indent
         return self._run_export_js(js_indent, indent, classes, cname)
 
-    def _export_js_elements(self, js_indent, indent, classes, cname):
+    def _export_js_elements(
+            self,
+            js_indent: str,
+            indent: int,
+            classes: t.Set[str],
+            cname: t.Optional[str]) -> str:
         new_indent = indent + 1
         value = ',\n'.join(['{indent}{elem}'.format(
             indent=js_indent * new_indent,
             elem=elem._export_js(
                 js_indent,
-                new_indent, classes, cname)) for elem in self._elements])
+                new_indent, classes, cname))
+            for elem in self._elements])  # type: ignore
         return '{class_name}(\n{value}\n{indent})'.format(
             class_name=self.__class__.__name__.lstrip('_'),
             value=value,
             indent=js_indent * indent)
 
-    def _run_export_js(self, js_indent, indent, classes, cname):
+    def _run_export_js(self,
+                       js_indent: str,
+                       indent: int,
+                       classes: t.Set[str],
+                       cname: t.Optional[str]) -> str:
         return 'not_implemented'
 
-    def _export_py(self, py_indent, indent, classes):
+    def _export_py(self,
+                   py_indent: str,
+                   indent: int,
+                   classes: t.Set[str]) -> str:
         classes.add(self.__class__.__name__.lstrip('_'))
         if hasattr(self, 'name') and indent:
             return self.name
         return self._run_export_py(py_indent, indent or 1, classes)
 
-    def _export_py_elements(self, py_indent, indent, classes):
+    def _export_py_elements(self,
+                            py_indent: str,
+                            indent: int,
+                            classes: t.Set[str]) -> str:
         new_indent = indent + 1
         value = ',\n'.join(['{indent}{elem}'.format(
             indent=py_indent * new_indent,
             elem=elem._export_py(
                 py_indent,
-                new_indent, classes)) for elem in self._elements])
+                new_indent,
+                classes))
+            for elem in self._elements])  # type: ignore
         return '{class_name}(\n{value}\n{indent})'.format(
             class_name=self.__class__.__name__.lstrip('_'),
             value=value,
             indent=py_indent * indent)
 
-    def _run_export_py(self, py_indent, indent, classes):
+    def _run_export_py(self,
+                       py_indent: str,
+                       indent: int,
+                       classes: t.Set[str]) -> str:
         return 'not_implemented'
 
     @c_export
-    def _export_c(self, c_indent, indent, enums, gid):
+    def _export_c(self,
+                  c_indent: str,
+                  indent: int,
+                  enums: t.Set[str],
+                  gid: str) -> str:
         if hasattr(self, 'name') and indent:
             return self.name
-        return self._run_export_c(c_indent, indent or 1, enums)
+        return self._run_export_c(c_indent, indent or 1, enums)  # type: ignore
 
     @c_export
-    def _export_c_elements(self, c_indent, indent, enums, gid):
+    def _export_c_elements(self,
+                           c_indent: str,
+                           indent: int,
+                           enums: t.Set[str],
+                           gid: str) -> str:
         new_indent = indent + 1
         value = ',\n'.join(['{indent}{elem}'.format(
             indent=c_indent * new_indent,
             elem=elem._export_c(
                 c_indent,
                 new_indent,
-                enums)) for elem in self._elements])
+                enums))
+            for elem in self._elements])  # type: ignore
         return \
             'cleri_{class_name}(\n{gid},\n{num},\n{value}\n{indent})'.format(
                 class_name=self.__class__.__name__.lstrip('_').lower(),
@@ -156,28 +235,41 @@
                     gid=gid),
                 num='{indent}{num}'.format(
                     indent=c_indent * (indent + 1),
-                    num=len(self._elements)),
+                    num=len(self._elements)),  # type: ignore
                 value=value,
                 indent=c_indent * indent)
 
-    def _run_export_c(self, c_indent, indent, enums):
+    @c_export
+    def _run_export_c(self,
+                      c_indent: str,
+                      indent: int,
+                      enums: t.Set[str],
+                      gid: str) -> str:
         return 'not_implemented'
 
     @go_export
-    def _export_go(self, go_indent, indent, enums, gid):
+    def _export_go(self,
+                   go_indent: str,
+                   indent: int,
+                   enums: t.Set[str],
+                   gid: str) -> str:
         if hasattr(self, 'name') and indent:
             return camel_case(self.name)
         return self._run_export_go(go_indent, indent or 1, enums)
 
     @go_export
-    def _export_go_elements(self, go_indent, indent, enums, gid):
+    def _export_go_elements(self,
+                            go_indent: str,
+                            indent: int,
+                            enums: t.Set[str],
+                            gid: str) -> str:
         new_indent = indent + 1
         value = ',\n'.join(['{indent}{elem}'.format(
             indent=go_indent * new_indent,
             elem=elem._export_go(
                 go_indent,
                 new_indent,
-                enums)) for elem in self._elements])
+                enums)) for elem in self._elements])  # type: ignore
         return '{class_name}(\n{gid},\n{value},\n{indent})'.format(
             class_name='goleri.New' + self.__class__.__name__.lstrip('_'),
             gid='{indent}{gid}'.format(
@@ -186,19 +278,33 @@
             value=value,
             indent=go_indent * indent)
 
-    def _run_export_go(self, go_indent, indent, enums):
+    @go_export
+    def _run_export_go(self,
+                       go_indent: str,
+                       indent: int,
+                       enums: t.Set[str],
+                       gid: str) -> str:
         return 'not_implemented'
 
     @java_export
-    def _export_java(self, java_indent, indent, enums, classes, gid):
+    def _export_java(self,
+                     java_indent: str,
+                     indent: int,
+                     enums: t.Set[str],
+                     classes: t.Set[str],
+                     gid: t.Optional[str]) -> str:
         if hasattr(self, 'name') and indent > 0:
             return self.name.upper()
         return self._run_export_java(
             java_indent, abs(indent) or 1, enums, classes)
 
     @java_export
-    def _export_java_elements(
-            self, java_indent, indent, enums, classes, gid):
+    def _export_java_elements(self,
+                              java_indent: str,
+                              indent: int,
+                              enums: t.Set[str],
+                              classes: t.Set[str],
+                              gid: t.Optional[str]) -> str:
         new_indent = indent + 1
         value = ',\n'.join(['{indent}{elem}'.format(
             indent=java_indent * new_indent,
@@ -206,7 +312,7 @@
                 java_indent,
                 new_indent,
                 enums,
-                classes)) for elem in self._elements])
+                classes)) for elem in self._elements])  # type: ignore
         return '{class_name}({gid}\n{value}\n{indent})'.format(
             class_name='new ' + self.__class__.__name__.lstrip('_'),
             gid='' if gid is None else '\n{indent}Ids.{gid},'.format(
@@ -215,7 +321,13 @@
             value=value,
             indent=java_indent * indent)
 
-    def _run_export_java(self, java_indent, indent, enums, classes):
+    @java_export
+    def _run_export_java(self,
+                         java_indent: str,
+                         indent: int,
+                         enums: t.Set[str],
+                         classes: t.Set[str],
+                         gid: t.Optional[str]):
         return 'not_implemented'
 
 # Added this import to the bottom to prevent circular import cycle.
@@ -224,4 +336,4 @@
 #       is sub-classed from the 'NamedElement' class.
 
 
-from .token import Token  # nopep8
+from .token import Token  # nopep8  # noqa: E402
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pyleri-1.4.3/pyleri/endofstatement.py 
new/pyleri-1.5.0/pyleri/endofstatement.py
--- old/pyleri-1.4.3/pyleri/endofstatement.py   2024-03-26 10:19:45.000000000 
+0100
+++ new/pyleri-1.5.0/pyleri/endofstatement.py   2025-11-12 21:19:22.000000000 
+0100
@@ -5,9 +5,10 @@
 
 :copyright: 2021, Jeroen van der Heijden <[email protected]>
 """
+from .elements import Element
 
 
-class _EndOfStatement:
+class _EndOfStatement(Element):
 
     def __repr__(self):
         return 'end_of_statement'
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pyleri-1.4.3/pyleri/grammar.py 
new/pyleri-1.5.0/pyleri/grammar.py
--- old/pyleri-1.4.3/pyleri/grammar.py  2024-03-26 10:19:45.000000000 +0100
+++ new/pyleri-1.5.0/pyleri/grammar.py  2025-11-12 21:19:22.000000000 +0100
@@ -14,6 +14,7 @@
 
 import re
 import time
+import typing as t
 from .node import Node
 from .expecting import Expecting
 from .endofstatement import end_of_statement
@@ -36,9 +37,9 @@
 class _KeepOrder(dict):
 
     def __init__(self, *args):
-        self._order = []
-        self._refs = {}
-        self._re_keywords = _RE_KEYWORDS
+        self._order: t.List[str] = []
+        self._refs: t.Dict[str, Ref] = {}
+        self._re_keywords: re.Pattern = _RE_KEYWORDS
         self._has_keywords = False
         super().__setitem__('_order', self._order)
         super().__setitem__('_refs', self._refs)
@@ -55,7 +56,7 @@
             for elem in element._elements:
                 self._check_keywords(elem)
 
-    def __setitem__(self, key, value):
+    def __setitem__(self, key: str, value: t.Any):
         if key not in self:
             for k in self._order:
                 if k.upper() == key.upper():
@@ -78,6 +79,7 @@
                 raise ReKeywordsChangedError(
                     'RE_KEYWORDS must be set on top of Grammar before '
                     'keywords are set.')
+            assert isinstance(value, re.Pattern)
             self._re_keywords = value
 
         if isinstance(value, NamedElement):
@@ -92,24 +94,24 @@
         super().__setitem__(key, value)
 
 
-def _used_checker(elem, used):
+def _used_checker(elem: Element, used: t.Set[str]):
     if hasattr(elem, 'name') and not isinstance(elem, Ref):
         if elem.name in used:
             return
         used.add(elem.name)
     if hasattr(elem, '_element'):
-        _used_checker(elem._element, used)
+        _used_checker(elem._element, used)  # type: ignore
         if hasattr(elem, '_delimiter'):
-            _used_checker(elem._delimiter, used)
+            _used_checker(elem._delimiter, used)  # type: ignore
     elif hasattr(elem, '_elements'):
-        for el in elem._elements:
+        for el in elem._elements:  # type: ignore
             _used_checker(el, used)
 
 
 class _OrderedClass(type):
 
     @classmethod
-    def __prepare__(mcs, name, bases):
+    def __prepare__(mcs, name, bases, **kwds):
         return _KeepOrder()
 
     def __new__(mcs, name, bases, attrs, **kwargs):
@@ -122,7 +124,7 @@
             raise MissingStartError(
                 'Grammar is missing the required START element entry point.')
         if bases:
-            used = set()
+            used: t.Set[str] = set()
             _used_checker(attrs['START'], used)
             elems = {
                 elem for elem in attrs['_order']
@@ -137,7 +139,8 @@
 
 class Grammar(metaclass=_OrderedClass):
 
-    __slots__ = ('_element', '_string', '_expecting', '_cached_kw_match')
+    _order: t.List[str]
+    _refs: t.Dict[str, Ref]
 
     RE_KEYWORDS = _RE_KEYWORDS
 
@@ -333,9 +336,9 @@
         Note: usually you should only initialize a Grammar instance
         once in a project.
         """
-        self._element = self.START
-        self._string = None
-        self._expecting = None
+        self._element: Element = self.START  # type: ignore
+        self._string = ''
+        self._expecting = Expecting()
         self._cached_kw_match = {}
 
     def export_js(
@@ -453,12 +456,12 @@
             self,
             target=C_TARGET,
             c_indent=C_INDENTATION,
-            headerf=None) -> str:
+            headerf=None) -> t.Tuple[str, str]:
         """Export the grammar to a c (source and header) file which can be
         used with the libcleri module."""
         language = []
         indent = 0
-        enums = set()
+        enums: t.Set[str] = set()
         for name in self._order:
             elem = getattr(self, name, None)
             if not isinstance(elem, Element):
@@ -487,7 +490,7 @@
         if not pattern.startswith('^'):
             pattern = '^' + pattern
 
-        enums = ',\n'.join([
+        enum_str = ',\n'.join([
             '{}{}'.format(c_indent, gid)
             for gid in sorted(enums)]) + ','
 
@@ -515,7 +518,7 @@
                         '%Y-%m-%d %H:%M:%S',
                         time.localtime()),
                     language='\n'.join(language),
-                    enums=enums))
+                    enums=enum_str))
 
     def export_go(
             self,
@@ -633,7 +636,7 @@
             enums=enum_str,
             public='public ' if is_public else '')
 
-    def parse(self, string) -> Result:
+    def parse(self, string: str) -> Result:
         """Parse some string to the Grammar.
 
         Returns a nodeResult with the following attributes:
@@ -646,11 +649,12 @@
          - tree: the parse_tree containing a structured
                  result for the given string.
         """
+        self._s = ''
         self._string = string
         self._expecting = Expecting()
         self._cached_kw_match.clear()
         self._len_string = len(string)
-        self._pos = None
+        self._pos = -1
         tree = Node(self._element, string, 0, self._len_string)
         node_res = Result(*self._walk(
             self._element,
@@ -686,7 +690,12 @@
         node.end = pos
         tree.append(node)
 
-    def _walk(self, element, pos, tree, rule, is_required):
+    def _walk(self,
+              element: Element,
+              pos: int,
+              tree: t.List[Node],
+              rule: Element,
+              is_required: bool) -> t.Tuple[bool, int]:
         if self._pos != pos:
             self._s = self._string[pos:].lstrip()
             self._pos = self._len_string - len(self._s)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pyleri-1.4.3/pyleri/keyword.py 
new/pyleri-1.5.0/pyleri/keyword.py
--- old/pyleri-1.4.3/pyleri/keyword.py  2024-03-26 10:19:45.000000000 +0100
+++ new/pyleri-1.5.0/pyleri/keyword.py  2025-11-12 21:19:22.000000000 +0100
@@ -6,6 +6,7 @@
 
 :copyright: 2021, Jeroen van der Heijden <[email protected]>
 """
+import typing as t
 from .elements import NamedElement, c_export, go_export, java_export
 
 
@@ -36,11 +37,11 @@
             re_match = \
                 root._cached_kw_match[node.start] = \
                 root.RE_KEYWORDS.match(s)
-        is_valid = \
-            re_match and \
+        is_valid = bool(
+            re_match and
             (re_match.group(0) == self._keyword or
              (self._ign_case and
-              re_match.group(0).lower() == self._keyword.lower()))
+              re_match.group(0).lower() == self._keyword.lower())))
         if is_valid:
             root._append_tree(tree, node, node.start + len(self._keyword))
         else:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pyleri-1.4.3/pyleri/list.py 
new/pyleri-1.5.0/pyleri/list.py
--- old/pyleri-1.4.3/pyleri/list.py     2024-03-26 10:19:45.000000000 +0100
+++ new/pyleri-1.5.0/pyleri/list.py     2025-11-12 21:19:22.000000000 +0100
@@ -21,7 +21,7 @@
 
     def __init__(
             self,
-            element: Element,
+            element: t.Union[str, Element],
             delimiter: t.Union[str, Element] = ',',
             mi: int = 0,
             ma: t.Optional[int] = None,
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pyleri-1.4.3/pyleri/node.py 
new/pyleri-1.5.0/pyleri/node.py
--- old/pyleri-1.4.3/pyleri/node.py     2024-03-26 10:19:45.000000000 +0100
+++ new/pyleri-1.5.0/pyleri/node.py     2025-11-12 21:19:22.000000000 +0100
@@ -19,7 +19,7 @@
             end: t.Optional[int] = None):
         self.element = element
         self.start = start
-        self.end = end
+        self.end = end or 0
         self._string = string
         self.children: t.List['Node'] = []
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pyleri-1.4.3/pyleri/optional.py 
new/pyleri-1.5.0/pyleri/optional.py
--- old/pyleri-1.4.3/pyleri/optional.py 2024-03-26 10:19:45.000000000 +0100
+++ new/pyleri-1.5.0/pyleri/optional.py 2025-11-12 21:19:22.000000000 +0100
@@ -2,21 +2,22 @@
 
 :copyright: 2021, Jeroen van der Heijden <[email protected]>
 """
-from .elements import NamedElement, c_export, go_export, java_export
+import typing as t
+from .elements import Element, NamedElement, c_export, go_export, java_export
 
 
 class Optional(NamedElement):
 
     __slots__ = ('_element',)
 
-    def __init__(self, element):
+    def __init__(self, element: t.Union[str, Element]):
         self._element = self._validate_element(element)
 
     @property
     def _elements(self):
         yield self._element
 
-    def _get_node_result(self, root, tree, rule, _s, node):
+    def _get_node_result(self, root, tree, rule, s, node):
         is_valid, pos = root._walk(
             self._element,
             node.start,
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pyleri-1.4.3/pyleri/prio.py 
new/pyleri-1.5.0/pyleri/prio.py
--- old/pyleri-1.4.3/pyleri/prio.py     2024-03-26 10:19:45.000000000 +0100
+++ new/pyleri-1.5.0/pyleri/prio.py     2025-11-12 21:19:22.000000000 +0100
@@ -17,6 +17,7 @@
         self._elements = self._validate_elements(elements)
 
     def _get_node_result(self, root, tree, rule, s, node):
+        assert isinstance(rule, Rule)
         if rule._depth == _Prio.MAX_RECURSION:
             raise MaxRecursionError(
                 'Max recursion depth of {} is reached'
@@ -47,8 +48,8 @@
     def _run_export_py(self, py_indent, indent, classes):
         return self._export_py_elements(py_indent, indent, classes)
 
-    def _run_export_c(self, c_indent, indent, enums):
-        return self._export_c_elements(c_indent, indent, enums)
+    def _run_export_c(self, c_indent, indent, enums):  # type: ignore
+        return self._export_c_elements(c_indent, indent, enums)  # type: ignore
 
     def _run_export_go(self, go_indent, indent, enums):
         return self._export_go_elements(go_indent, indent, enums)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pyleri-1.4.3/pyleri/ref.py 
new/pyleri-1.5.0/pyleri/ref.py
--- old/pyleri-1.4.3/pyleri/ref.py      2024-03-26 10:19:45.000000000 +0100
+++ new/pyleri-1.5.0/pyleri/ref.py      2025-11-12 21:19:22.000000000 +0100
@@ -29,7 +29,7 @@
     def _run_export_py(self, py_indent, indent, classes):
         return 'Ref()'
 
-    def _run_export_c(self, c_indent, indent, enums):
+    def _run_export_c(self, c_indent, indent, enums):  # type: ignore
         return 'cleri_ref()'
 
     def _run_export_go(self, go_indent, indent, classes):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pyleri-1.4.3/pyleri/repeat.py 
new/pyleri-1.5.0/pyleri/repeat.py
--- old/pyleri-1.4.3/pyleri/repeat.py   2024-03-26 10:19:45.000000000 +0100
+++ new/pyleri-1.5.0/pyleri/repeat.py   2025-11-12 21:19:22.000000000 +0100
@@ -12,14 +12,14 @@
 
     def __init__(
             self,
-            element: Element,
+            element: t.Union[Element, str],
             mi: int = 0,
             ma: t.Optional[int] = None):
         self._element = self._validate_element(element)
 
         if not isinstance(mi, int) or mi < 0:
             raise TypeError('Repeat(): "mi" must be an integer value larger '
-                            'than or equal to 0, got: {mi}'.format(mi))
+                            'than or equal to 0, got: {mi}'.format(mi=mi))
 
         self._min = mi
 
@@ -101,6 +101,7 @@
 
     @java_export
     def _run_export_java(self, java_indent, indent, enums, classes, gid):
+        assert isinstance(self._element, NamedElement)
         return 'new Repeat({}{}, {}, {})'.format(
             '' if gid is None else 'Ids.{}, '.format(gid),
             self._element._export_java(java_indent, indent, enums, classes),
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pyleri-1.4.3/pyleri/result.py 
new/pyleri-1.5.0/pyleri/result.py
--- old/pyleri-1.4.3/pyleri/result.py   2024-03-26 10:19:45.000000000 +0100
+++ new/pyleri-1.5.0/pyleri/result.py   2025-11-12 21:19:22.000000000 +0100
@@ -10,6 +10,7 @@
 if t.TYPE_CHECKING:
     from .node import Node
     from .expecting import Expecting
+    from .elements import Element
 
 
 TOSTR = (Keyword, Token, Tokens, _EndOfStatement)
@@ -22,7 +23,7 @@
     def __init__(self, is_valid: bool, pos: int, tree: 'Node'):
         self.is_valid: bool = is_valid
         self.pos: int = pos
-        self.expecting: t.Optional['Expecting'] = None
+        self.expecting: t.Set[Element] = set()
         self.tree: 'Node' = tree
 
     def as_str(self, translate=None, line_number=False):
@@ -36,7 +37,8 @@
         else:
             res = ['error at position {}'.format(self.pos)]
         arr = []
-        for elem in (self.expecting):
+        assert self.expecting is not None
+        for elem in self.expecting:
             expectstr = translate(elem) if translate else None
             if expectstr is None and isinstance(elem, TOSTR):
                 expectstr = str(elem)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pyleri-1.4.3/pyleri/rule.py 
new/pyleri-1.5.0/pyleri/rule.py
--- old/pyleri-1.4.3/pyleri/rule.py     2024-03-26 10:19:45.000000000 +0100
+++ new/pyleri-1.5.0/pyleri/rule.py     2025-11-12 21:19:22.000000000 +0100
@@ -12,7 +12,7 @@
     def __init__(self, element: Element):
         self._element = element
 
-    def _get_node_result(self, root, tree, rule, _s, node):
+    def _get_node_result(self, root, tree, rule, s, node):
         self._tested = {}
         self._tree = {}
         self._depth = -1
@@ -32,7 +32,7 @@
     def _run_export_py(self, py_indent, indent, classes):
         return self._element._export_py(py_indent, indent, classes)
 
-    def _run_export_c(self, c_indent, indent, enums):
+    def _run_export_c(self, c_indent, indent, enums):  # type: ignore
         name = getattr(self, 'name', None)
         if name is not None:
             self._element._name = name
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pyleri-1.4.3/pyleri/sequence.py 
new/pyleri-1.5.0/pyleri/sequence.py
--- old/pyleri-1.4.3/pyleri/sequence.py 2024-03-26 10:19:45.000000000 +0100
+++ new/pyleri-1.5.0/pyleri/sequence.py 2025-11-12 21:19:22.000000000 +0100
@@ -2,18 +2,20 @@
 
 :copyright: 2021, Jeroen van der Heijden <[email protected]>
 """
-from .elements import Element, NamedElement, c_export
+import typing as t
+from .elements import Element, NamedElement
 
 
 class Sequence(NamedElement):
 
     __slots__ = ('_elements',)
 
-    def __init__(self, *elements: Element):
+    def __init__(self, *elements: t.Union[Element, str]):
         self._elements = self._validate_elements(elements)
 
-    def _get_node_result(self, root, tree, rule, _s, node):
+    def _get_node_result(self, root, tree, rule, s, node):
         pos = node.start
+        is_valid = True
 
         for elem in self._elements:
             is_valid, pos = root._walk(elem, pos, node.children, rule, True)
@@ -30,8 +32,8 @@
     def _run_export_py(self, py_indent, indent, classes):
         return self._export_py_elements(py_indent, indent, classes)
 
-    def _run_export_c(self, c_indent, indent, enums):
-        return self._export_c_elements(c_indent, indent, enums)
+    def _run_export_c(self, c_indent, indent, enums):  # type: ignore
+        return self._export_c_elements(c_indent, indent, enums)  # type: ignore
 
     def _run_export_go(self, go_indent, indent, enums):
         return self._export_go_elements(go_indent, indent, enums)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pyleri-1.4.3/pyleri/this.py 
new/pyleri-1.5.0/pyleri/this.py
--- old/pyleri-1.4.3/pyleri/this.py     2024-03-26 10:19:45.000000000 +0100
+++ new/pyleri-1.5.0/pyleri/this.py     2025-11-12 21:19:22.000000000 +0100
@@ -3,11 +3,13 @@
 :copyright: 2021, Jeroen van der Heijden <[email protected]>
 """
 from .elements import Element
+from .rule import Rule
 
 
 class This(Element):
 
-    def _get_node_result(self, root, tree, rule, _s, node):
+    def _get_node_result(self, root, tree, rule, s, node):
+        assert isinstance(rule, Rule)
         if node.start not in rule._tested:
             rule._tested[node.start] = root._walk(
                 rule._element,
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pyleri-1.4.3/pyleri/token.py 
new/pyleri-1.5.0/pyleri/token.py
--- old/pyleri-1.4.3/pyleri/token.py    2024-03-26 10:19:45.000000000 +0100
+++ new/pyleri-1.5.0/pyleri/token.py    2025-11-12 21:19:22.000000000 +0100
@@ -2,6 +2,7 @@
 
 :copyright: 2021, Jeroen van der Heijden <[email protected]>
 """
+import typing as t
 from .elements import NamedElement, c_export, go_export, java_export
 
 
@@ -35,19 +36,32 @@
         return 'Token(\'{}\')'.format(self._token.replace('\'', '\\\''))
 
     @c_export
-    def _run_export_c(self, js_indent, indent, enums, gid):
+    def _run_export_c(self,
+                      js_indent: str,
+                      indent: int,
+                      enums: t.Set[str],
+                      gid: str) -> str:
         return 'cleri_token({}, "{}")'.format(
             gid,
             self._token.replace('"', '\\"'))
 
     @go_export
-    def _run_export_go(self, go_indent, indent, enums, gid):
+    def _run_export_go(self,
+                       go_indent: str,
+                       indent: int,
+                       enums: t.Set[str],
+                       gid: str) -> str:
         return 'goleri.NewToken({}, "{}")'.format(
             gid,
             self._token.replace('"', '\\"'))
 
     @java_export
-    def _run_export_java(self, java_indent, indent, enums, classes, gid):
+    def _run_export_java(self,
+                         java_indent: str,
+                         indent: int,
+                         enums: t.Set[str],
+                         classes: t.Set[str],
+                         gid: t.Optional[str]) -> str:
         return 'new Token({}"{}")'.format(
             '' if gid is None else 'Ids.{}, '.format(gid),
             self._token.replace('"', '\\"'))
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pyleri-1.4.3/pyleri/tokens.py 
new/pyleri-1.5.0/pyleri/tokens.py
--- old/pyleri-1.4.3/pyleri/tokens.py   2024-03-26 10:19:45.000000000 +0100
+++ new/pyleri-1.5.0/pyleri/tokens.py   2025-11-12 21:19:22.000000000 +0100
@@ -2,6 +2,7 @@
 
 :copyright: 2021, Jeroen van der Heijden <[email protected]>
 """
+import typing as t
 from .elements import NamedElement, c_export, go_export, java_export
 
 
@@ -26,7 +27,11 @@
 
         return False, node.start
 
-    def _run_export_js(self, js_indent, indent, classes, cname):
+    def _run_export_js(self,
+                       js_indent: str,
+                       indent: int,
+                       classes: t.Set[str],
+                       cname) -> str:
         return 'Tokens(\'{}\')'.format(
             ' '.join(self._tokens).replace('\'', '\\\''))
 
@@ -41,13 +46,22 @@
             ' '.join(self._tokens).replace('"', '\\"'))
 
     @go_export
-    def _run_export_go(self, go_indent, indent, enums, gid):
+    def _run_export_go(self,
+                       go_indent: str,
+                       indent: int,
+                       enums: t.Set[str],
+                       gid: str) -> str:
         return 'goleri.NewTokens({}, "{}")'.format(
             gid,
             ' '.join(self._tokens).replace('"', '\\"'))
 
     @java_export
-    def _run_export_java(self, java_indent, indent, enums, classes, gid):
+    def _run_export_java(self,
+                         java_indent: str,
+                         indent: int,
+                         enums: t.Set[str],
+                         classes: t.Set[str],
+                         gid: t.Optional[str]) -> str:
         return 'new Tokens({}"{}")'.format(
             '' if gid is None else 'Ids.{}, '.format(gid),
             ' '.join(self._tokens).replace('"', '\\"'))
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pyleri-1.4.3/test/test_keyword.py 
new/pyleri-1.5.0/test/test_keyword.py
--- old/pyleri-1.4.3/test/test_keyword.py       2024-03-26 10:19:45.000000000 
+0100
+++ new/pyleri-1.5.0/test/test_keyword.py       2025-11-12 21:19:22.000000000 
+0100
@@ -1,6 +1,7 @@
 import unittest
 import os
 import sys
+import re
 if os.environ.get('USELIB') != '1':
     sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))
 
@@ -44,7 +45,9 @@
             str(grammar.parse('Hi Iris')))
 
     def test_keyword_alt_regkw(self):
-        grammar = create_grammar(Choice(Keyword('hi'), Token('HI')), r'[a-z]+')
+        grammar = create_grammar(
+            Choice(Keyword('hi'), Token('HI')),
+            re.compile(r'[a-z]+'))
         self.assertTrue(grammar.parse('hi').is_valid)
         self.assertTrue(grammar.parse('HI').is_valid)
         self.assertFalse(grammar.parse('').is_valid)

Reply via email to