alexmojaki / pure_eval

Safely evaluate AST nodes without side effects
MIT License
42 stars 16 forks source link

0.2.3: not ready to to be used with python 3.9 list, tuple (lower case) #21

Open kloczek opened 4 days ago

kloczek commented 4 days ago

Pyton 3.8 just has been EOSed month ago (https://endoflife.date/python) so I've been trying to use pyupgrade --py39-plus to check is current code ready to automatic upgrade. Looks like it is not ready to be automatically updated because it uses (upper case) List, Tuple. Please have look on https://flycoolman.com/coding/python/list-tuple-dict-vs-list-tuple-dict/

Here is the patch generated by `pypgrade --py39-plus` ```patch --- a/pure_eval/core.py +++ b/pure_eval/core.py @@ -4,7 +4,8 @@ from collections import ChainMap, OrderedDict, deque from contextlib import suppress from types import FrameType -from typing import Any, Tuple, Iterable, List, Mapping, Dict, Union, Set +from typing import Any, Tuple, List, Dict, Union, Set +from collections.abc import Iterable, Mapping from pure_eval.my_getattr_static import getattr_static from pure_eval.utils import ( @@ -308,7 +309,7 @@ def _handle_container( self, node: Union[ast.List, ast.Tuple, ast.Set, ast.Dict] - ) -> Union[List, Tuple, Set, Dict]: + ) -> Union[list, tuple, set, dict]: """Handle container nodes, including List, Set, Tuple and Dict""" if isinstance(node, ast.Dict): elts = node.keys @@ -342,7 +343,7 @@ except TypeError: raise CannotEval - def find_expressions(self, root: ast.AST) -> Iterable[Tuple[ast.expr, Any]]: + def find_expressions(self, root: ast.AST) -> Iterable[tuple[ast.expr, Any]]: """ Find all expressions in the given tree that can be safely evaluated. This is a low level API, typically you will use `interesting_expressions_grouped`. @@ -362,7 +363,7 @@ yield node, value - def interesting_expressions_grouped(self, root: ast.AST) -> List[Tuple[List[ast.expr], Any]]: + def interesting_expressions_grouped(self, root: ast.AST) -> list[tuple[list[ast.expr], Any]]: """ Find all interesting expressions in the given tree that can be safely evaluated, grouping equivalent nodes together. @@ -422,7 +423,7 @@ return True -def group_expressions(expressions: Iterable[Tuple[ast.expr, Any]]) -> List[Tuple[List[ast.expr], Any]]: +def group_expressions(expressions: Iterable[tuple[ast.expr, Any]]) -> list[tuple[list[ast.expr], Any]]: """ Organise expression nodes and their values such that equivalent nodes are together. Two nodes are considered equivalent if they have the same structure, --- a/tests/test_core.py +++ b/tests/test_core.py @@ -73,7 +73,7 @@ foo, Foo, Foo.method, foo.method ) - check_eval("typing.List", typing, typing.List) + check_eval("typing.List", typing, list) def test_eval_dict(): @@ -445,10 +445,7 @@ def subscript_item(node): - if sys.version_info < (3, 9): - return node.slice.value - else: - return node.slice + return node.slice def test_evaluator_wrong_getitem(): --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -91,7 +91,7 @@ def test_safe_name_direct(): assert safe_name(list) == "list" - assert safe_name(typing.List) == "List" + assert safe_name(list) == "List" assert safe_name(typing.Union) == "Union" assert safe_name(typing.Optional) == "Optional" assert safe_name(3) is None ```

With that patch pytest fails with

```console + PYTHONPATH=/home/tkloczko/rpmbuild/BUILDROOT/python-pure-eval-0.2.3-2.fc37.x86_64/usr/lib64/python3.10/site-packages:/home/tkloczko/rpmbuild/BUILDROOT/python-pure-eval-0.2.3-2.fc37.x86_64/usr/lib/python3.10/site-packages + /usr/bin/pytest -ra -m 'not network' ==================================================================================== test session starts ==================================================================================== platform linux -- Python 3.10.14, pytest-8.2.2, pluggy-1.5.0 rootdir: /home/tkloczko/rpmbuild/BUILD/pure_eval-0.2.3 configfile: pyproject.toml collected 47 items tests/test_core.py ..F.............. [ 36%] tests/test_getattr_static.py ....................... [ 85%] tests/test_utils.py ....F.. [100%] ========================================================================================= FAILURES ========================================================================================== ______________________________________________________________________________________ test_eval_attrs ______________________________________________________________________________________ def test_eval_attrs(): class Foo: bar = 9 @property def prop(self): return 0 def method(self): pass foo = Foo() foo.spam = 44 check_eval( "foo.bar + foo.spam + Foo.bar", foo.bar, foo.spam, Foo.bar, foo.bar + foo.spam, foo.bar + foo.spam + Foo.bar, foo, Foo ) check_eval( "Foo.spam + Foo.prop + foo.prop + foo.method() + Foo.method", foo, Foo, Foo.method, foo.method ) > check_eval("typing.List", typing, list) tests/test_core.py:76: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ source = 'typing.List', total = True, expected_values = (, ) frame = evaluator = def check_eval(source, *expected_values, total=True): frame = inspect.currentframe().f_back evaluator = Evaluator.from_frame(frame) root = ast.parse(source) values = [] for node, value in evaluator.find_expressions(root): expr = ast.Expression(body=node) ast.copy_location(expr, node) code = compile(expr, "", "eval") expected = eval(code, frame.f_globals, frame.f_locals) assert value == expected values.append(value) if total: > assert value in expected_values E AssertionError: assert typing.List in (, ) tests/test_core.py:26: AssertionError ___________________________________________________________________________________ test_safe_name_direct ___________________________________________________________________________________ def test_safe_name_direct(): assert safe_name(list) == "list" > assert safe_name(list) == "List" E AssertionError: assert 'list' == 'List' E E - List E ? ^ E + list E ? ^ tests/test_utils.py:94: AssertionError ================================================================================== short test summary info ================================================================================== FAILED tests/test_core.py::test_eval_attrs - AssertionError: assert typing.List in (, ) FAILED tests/test_utils.py::test_safe_name_direct - AssertionError: assert 'list' == 'List' =============================================================================== 2 failed, 45 passed in 3.11s ================================================================================ ```
kloczek commented 3 days ago

Copied here patch has been generated after apply https://github.com/alexmojaki/pure_eval/pull/22