python/tests: Add pytest-based tests#51
Conversation
Summary of ChangesHello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request initiates the migration of existing test scripts to a Highlights
🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console. Changelog
Activity
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here. You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension. Footnotes
|
There was a problem hiding this comment.
Code Review
This pull request introduces pytest-based tests, which is a significant improvement for the project's testing infrastructure. The use of pytest features like tmp_path and the explicit passing of modules are commendable. I've provided a couple of suggestions to further enhance the new test code by improving its readability and effectiveness.
85888b7 to
138bb99
Compare
|
Okay. @jplevyak has conditionally approved this; James to have Claude do the needful later. 👍 |
138bb99 to
46e1d66
Compare
Refresh: diagnosis & fix for the Python bindingA quick overview of what we landed, since the prior write-ups were spread across The problem (primary bug)Upstream commit r->prod->internal ? -1 : (r->prod->index * 10000 + r->index)The Cython binding in Concretely, for the canonical test grammar: def d_S(t): '''S : d '+' d''' ; return t[0] + t[2]
def d_number(t):'''d : "[0-9]+" ''' ; return int(t[0])
Parser().parse('87+5').getStructure()
Why we fixed it on the Cython side (not by reverting
|
|
/gemini review |
There was a problem hiding this comment.
Code Review
This pull request updates the action resolution logic in dparser.pyx to handle composite action indices using a new caching and pending action mechanism. It also fixes a bug where non-speculative actions were incorrectly skipped during the final pass and introduces regression tests for basic arithmetic and the speculative gate. Feedback focuses on the potential for incorrect action mapping due to the non-deterministic order of reductions when using pop(0), performance overhead in the cache for rules without actions, and the fragility of extracting nonterminal names from docstrings.
| pending = p._pending.get(nonterm_name) | ||
| if pending: | ||
| action_tuple = pending.pop(0) | ||
| p._action_cache[action_index] = action_tuple |
There was a problem hiding this comment.
The logic for resolving actions using pending.pop(0) is non-deterministic and potentially incorrect. It assumes that the order in which rules are first encountered during parsing matches the order in which functions are declared in the Python module. However, the order of reductions is determined by the input string and the parser's state machine, not the grammar declaration order. If a nonterminal has multiple rules and they are encountered out of order, the wrong Python action will be associated with the rule and cached. A more robust mapping based on the actual rule index within the production (e.g., using action_index % 10000) should be established during parser initialization.
| cached = p._action_cache.get(action_index) | ||
| if cached is not None: | ||
| action_tuple = <tuple>cached | ||
| if action_tuple is None: |
There was a problem hiding this comment.
The cache lookup does not distinguish between a missing key and a key associated with None. If a rule has no associated Python action, action_tuple remains None, and the resolution logic (including string decoding and dictionary lookups) will be re-executed every time that action_index is encountered. Consider using a sentinel value or checking for key existence in the cache to improve performance for rules without actions.
| self.actions.append((f, arg_types, speculative)) | ||
| action_tuple = (f, arg_types, speculative) | ||
| self.actions.append(action_tuple) | ||
| nonterm_name = f.__doc__.strip().split(':', 1)[0].strip() |
There was a problem hiding this comment.
Extracting the nonterminal name by splitting the docstring on the first colon is fragile. It may fail or produce incorrect results if the docstring contains comments, leading whitespace, or multiple rules before the first colon. A more robust parsing of the docstring or an explicit way to associate functions with nonterminals would be more reliable.
Converts test.py to pytest using explicit `modules=` parameter to avoid frame introspection, `tmp_path` for generated grammar files, and standard assertions. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
46e1d66 to
756c658
Compare
Summary
test_basic_arithmetic.py, a pytest conversion of the existingtest.pymodules=parameter to explicitly pass grammar actions, avoiding fragileinspect.currentframe()introspectiontmp_pathfixture to isolate generated grammar filesPlan for remaining tests
test.pytest_basic_arithmetic.pytest2.pytest_expression_eval.pytest3.pytest_expression_eval.pynodesargument; needspytest-forkedor subprocess isolation due to SWIG cleanup SEGVtest4.pytest_speculative_parsing.pyspecargument,dparser.Reject,syntax_error_fntest5.pytest_speculative_parsing.pyspec_onlyargument, speculative-only actionstest6.pytest_string_replace.pysargument,getStringLeft()test7.pytest_parse_nodes.pynodes/thisarguments,start_loc,buf,end,end_skipEach file will follow the same pattern:
d_*functions at module scope, aparserfixture usingmodules=sys.modules[__name__]andtmp_path, and standardassertstatements.Test plan
python -m pytest python/tests/test_basic_arithmetic.py -vpasses locally🤖 Generated with Claude Code