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-07-07 21:02:12 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-pyleri (Old) and /work/SRC/openSUSE:Factory/.python-pyleri.new.1982 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-pyleri" Tue Jul 7 21:02:12 2026 rev:5 rq:1364109 version:1.5.1 Changes: -------- --- /work/SRC/openSUSE:Factory/python-pyleri/python-pyleri.changes 2026-04-22 17:20:58.488954218 +0200 +++ /work/SRC/openSUSE:Factory/.python-pyleri.new.1982/python-pyleri.changes 2026-07-07 21:04:11.744326010 +0200 @@ -1,0 +2,6 @@ +Mon Jul 6 19:34:23 UTC 2026 - Dirk Müller <[email protected]> + +- update to 1.5.1: + * Stop greedy choice after full input match + +------------------------------------------------------------------- Old: ---- pyleri-1.5.0.tar.gz New: ---- pyleri-1.5.1.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-pyleri.spec ++++++ --- /var/tmp/diff_new_pack.2nU2of/_old 2026-07-07 21:04:13.848398861 +0200 +++ /var/tmp/diff_new_pack.2nU2of/_new 2026-07-07 21:04:13.852399000 +0200 @@ -19,7 +19,7 @@ %{?!python_module:%define python_module() python-%{**} python3-%{**}} Name: python-pyleri -Version: 1.5.0 +Version: 1.5.1 Release: 0 Summary: Python Left-Right Parser License: MIT ++++++ pyleri-1.5.0.tar.gz -> pyleri-1.5.1.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyleri-1.5.0/README.md new/pyleri-1.5.1/README.md --- old/pyleri-1.5.0/README.md 2025-11-12 21:19:22.000000000 +0100 +++ new/pyleri-1.5.1/README.md 2026-07-06 13:30:45.000000000 +0200 @@ -1,5 +1,6 @@ [](https://github.com/cesbit/pyleri/actions) [](https://github.com/cesbit/pyleri/releases) +[](https://github.com/cesbit/pyleri/stargazers) Python Left-Right Parser ======================== diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyleri-1.5.0/examples/thingsdb_grammar.py new/pyleri-1.5.1/examples/thingsdb_grammar.py --- old/pyleri-1.5.0/examples/thingsdb_grammar.py 1970-01-01 01:00:00.000000000 +0100 +++ new/pyleri-1.5.1/examples/thingsdb_grammar.py 2026-07-06 13:30:45.000000000 +0200 @@ -0,0 +1,245 @@ +"""ThingsDB (v1.8.2) language file. +""" +import re +from pyleri import List +from pyleri import Repeat +from pyleri import Token +from pyleri import Grammar +from pyleri import Sequence +from pyleri import Optional +from pyleri import Tokens +from pyleri import Ref +from pyleri import Keyword +from pyleri import Prio +from pyleri import Regex +from pyleri import THIS +from pyleri import Choice + + +class LangDef(Grammar): + + RE_KEYWORDS = re.compile('^[A-Za-z_][0-9A-Za-z_]{0,254}(?![0-9A-Za-z_])') + x_array = Token('[') + x_assign = Tokens('+= -= *= /= %= &= ^= |= =') + x_block = Token('{') + x_chain = Tokens('.. .') + x_closure = Token('|') + x_function = Token('(') + x_index = Token('[') + x_parenthesis = Token('(') + x_preopr = Regex('^(\\s*~)*(\\s*!|\\s*[\\-+](?=[^0-9]))*') + x_ternary = Token('?') + x_thing = Token('{') + x_ano = Token('&{') + x_template = Token('`') + template = Sequence( + x_template, + Repeat(Choice( + Regex('^([^`{}]|``|{{|}})+'), + Sequence( + Token('{'), + THIS, + Token('}') + ), + most_greedy=False), 0, None), + x_template + ) + t_false = Keyword('false') + t_float = Regex( + r'[-+]?(inf|nan|[0-9]*\.[0-9]+(e[+-][0-9]+)?)' + r'(?![0-9A-Za-z_\.])') + t_int = Regex( + r'[-+]?((0b[01]+)|(0o[0-8]+)|(0x[0-9a-fA-F]+)|([0-9]+))' + r'(?![0-9A-Za-z_\.])') + t_nil = Keyword('nil') + t_regex = Regex('^/((?:.(?!(?<![\\\\])/))*.?)/[a-z]*') + t_string = Regex('^(((?:\'(?:[^\']*)\')+)|((?:"(?:[^"]*)")+))') + t_true = Keyword('true') + name = Regex('^[A-Za-z_][0-9A-Za-z_]{0,254}(?![0-9A-Za-z_])') + var = Regex('^[A-Za-z_][0-9A-Za-z_]{0,254}(?![0-9A-Za-z_])') + chain = Ref() + closure = Sequence( + x_closure, + List(var, Token(','), 0, None, True), + Token('|'), + THIS + ) + t_ano = Sequence( + x_ano, + List(Sequence( + name, + Token(':'), + Optional(THIS) + ), Token(','), 0, None, True), + Token('}') + ) + thing = Sequence( + x_thing, + List(Sequence( + name, + Token(':'), + Optional(THIS) + ), Token(','), 0, None, True), + Token('}') + ) + array = Sequence( + x_array, + List(THIS, Token(','), 0, None, True), + Token(']') + ) + function = Sequence( + x_function, + List(THIS, Token(','), 0, None, True), + Token(')') + ) + instance = Repeat(thing, 1, 1) + enum_ = Sequence( + x_thing, + Choice( + name, + closure, + most_greedy=False), + Token('}') + ) + opr0_mul_div_mod = Tokens('* / %') + opr1_add_sub = Tokens('+ -') + opr2_bitwise_shift = Tokens('<< >>') + opr3_bitwise_and = Tokens('&') + opr4_bitwise_xor = Tokens('^') + opr5_bitwise_or = Tokens('|') + opr6_compare = Tokens('== != <= >= < >') + opr7_cmp_and = Token('&&') + opr8_cmp_or = Token('||') + opr9_ternary = Sequence( + x_ternary, + THIS, + Token(':') + ) + operations = Sequence( + THIS, + Choice( + opr9_ternary, + opr8_cmp_or, + opr7_cmp_and, + opr2_bitwise_shift, + opr6_compare, + opr5_bitwise_or, + opr4_bitwise_xor, + opr3_bitwise_and, + opr1_add_sub, + opr0_mul_div_mod, + most_greedy=False), + THIS + ) + assign = Sequence( + x_assign, + THIS + ) + name_opt_more = Sequence( + name, + Optional(Choice( + function, + assign, + most_greedy=False)) + ) + var_opt_more = Sequence( + var, + Optional(Choice( + function, + assign, + instance, + enum_, + most_greedy=False)) + ) + slice = List(Optional(THIS), Token(':'), 0, 3, False) + index = Repeat(Sequence( + x_index, + slice, + Token(']'), + Optional(Sequence( + x_assign, + THIS + )) + ), 0, None) + block = Sequence( + x_block, + List(THIS, Repeat(Token(';'), 1, None), 1, None, True), + Token('}') + ) + parenthesis = Sequence( + x_parenthesis, + THIS, + Token(')') + ) + k_if = Keyword('if') + k_else = Keyword('else') + k_return = Keyword('return') + k_for = Keyword('for') + k_in = Keyword('in') + k_continue = Keyword('continue') + k_break = Keyword('break') + if_statement = Sequence( + k_if, + Token('('), + THIS, + Token(')'), + THIS, + Optional(Sequence( + k_else, + THIS + )) + ) + return_statement = Sequence( + k_return, + List(THIS, Token(','), 0, 3, False) + ) + for_statement = Sequence( + k_for, + Token('('), + List(var, Token(','), 1, None, False), + k_in, + THIS, + Token(')'), + THIS + ) + expression = Sequence( + x_preopr, + Choice( + chain, + t_false, + t_nil, + t_true, + t_float, + t_int, + t_string, + t_regex, + t_ano, + template, + var_opt_more, + thing, + array, + parenthesis, + most_greedy=False), + index, + Optional(chain) + ) + statement = Prio( + k_continue, + k_break, + Choice( + if_statement, + return_statement, + for_statement, + closure, + expression, + block, + most_greedy=False), + operations + ) + START = List(statement, Repeat(Token(';'), 1, None), 0, None, True) + chain = Sequence( + x_chain, + name_opt_more, + index, + Optional(chain) + ) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyleri-1.5.0/pyleri/__init__.py new/pyleri-1.5.1/pyleri/__init__.py --- old/pyleri-1.5.0/pyleri/__init__.py 2025-11-12 21:19:22.000000000 +0100 +++ new/pyleri-1.5.1/pyleri/__init__.py 2026-07-06 13:30:45.000000000 +0200 @@ -41,4 +41,4 @@ __author__ = 'Jeroen van der Heijden' __maintainer__ = 'Jeroen van der Heijden' __email__ = '[email protected]' -__version__ = '1.5.0' +__version__ = '1.5.1' diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyleri-1.5.0/pyleri/choice.py new/pyleri-1.5.1/pyleri/choice.py --- old/pyleri-1.5.0/pyleri/choice.py 2025-11-12 21:19:22.000000000 +0100 +++ new/pyleri-1.5.1/pyleri/choice.py 2026-07-06 13:30:45.000000000 +0200 @@ -37,6 +37,8 @@ if is_valid and pos > mg_pos: node.children = children mg_is_valid, mg_pos = is_valid, pos + if pos == root._len_string: + break if mg_is_valid: root._append_tree(tree, node, mg_pos) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyleri-1.5.0/setup.py new/pyleri-1.5.1/setup.py --- old/pyleri-1.5.0/setup.py 2025-11-12 21:19:22.000000000 +0100 +++ new/pyleri-1.5.1/setup.py 2026-07-06 13:30:45.000000000 +0200 @@ -1,10 +1,7 @@ """setup.py - -Upload to PyPI, Thx to: http://peterdowns.com/posts/first-time-with-pypi.html - -python3 setup.py sdist -twine upload --repository pypitest dist/pyleri-x.x.x.tar.gz -twine upload --repository pypi dist/pyleri-x.x.x.tar.gz +python -m build +twine upload --repository pypitest dist/pyleri-x.x.x* +twine upload --repository pypi dist/pyleri-x.x.x* """ from setuptools import setup from pyleri import __version__ as version diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyleri-1.5.0/test/test_choice.py new/pyleri-1.5.1/test/test_choice.py --- old/pyleri-1.5.0/test/test_choice.py 2025-11-12 21:19:22.000000000 +0100 +++ new/pyleri-1.5.1/test/test_choice.py 2026-07-06 13:30:45.000000000 +0200 @@ -7,10 +7,12 @@ from pyleri import ( KeywordError, create_grammar, + Repeat, Sequence, Choice, Keyword, Regex, + Token, ) # nopep8 @@ -28,6 +30,20 @@ self.assertTrue(grammar.parse(' hi iris ').is_valid) self.assertFalse(grammar.parse(' hi sasha ').is_valid) + def test_choice_most_greedy_selects_full_match(self): + short = Token('a') + long = Token('aa') + grammar = create_grammar(Repeat(Choice(short, long))) + + result = grammar.parse('aa') + repeat_node = result.tree.children[0] + choice_node = repeat_node.children[0] + + self.assertTrue(result.is_valid) + self.assertEqual(len(repeat_node.children), 1) + self.assertEqual(choice_node.children[0].element, long) + self.assertEqual(choice_node.children[0].string, 'aa') + def test_choice_first_match(self): k_hi = Keyword('hi') k_iris = Keyword('iris')
