diff options
author | robot-piglet <robot-piglet@yandex-team.com> | 2024-09-24 09:19:19 +0300 |
---|---|---|
committer | robot-piglet <robot-piglet@yandex-team.com> | 2024-09-24 09:26:58 +0300 |
commit | 7b52d3a3ff9ea0bda3fb19634cdfb3a3ac6a52e2 (patch) | |
tree | c3546e6d2cb5edcb48c4dfeb7575c0e5952d679b /contrib | |
parent | 53f13049cb8f79b2b6ac95251fd6a03e4b8c7ba2 (diff) | |
download | ydb-7b52d3a3ff9ea0bda3fb19634cdfb3a3ac6a52e2.tar.gz |
Intermediate changes
commit_hash:4fd110744a809cc923058a0e9949e65d8fa64e4d
Diffstat (limited to 'contrib')
-rw-r--r-- | contrib/python/executing/.dist-info/METADATA | 22 | ||||
-rw-r--r-- | contrib/python/executing/executing/_position_node_finder.py | 130 | ||||
-rw-r--r-- | contrib/python/executing/executing/executing.py | 11 | ||||
-rw-r--r-- | contrib/python/executing/executing/version.py | 2 | ||||
-rw-r--r-- | contrib/python/executing/ya.make | 2 |
5 files changed, 138 insertions, 29 deletions
diff --git a/contrib/python/executing/.dist-info/METADATA b/contrib/python/executing/.dist-info/METADATA index b598e4907d..45ff9aa881 100644 --- a/contrib/python/executing/.dist-info/METADATA +++ b/contrib/python/executing/.dist-info/METADATA @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: executing -Version: 2.0.1 +Version: 2.1.0 Summary: Get the currently executing AST node of a frame, and other information Home-page: https://github.com/alexmojaki/executing Author: Alex Hall @@ -9,25 +9,23 @@ License: MIT Classifier: License :: OSI Approved :: MIT License Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 3 -Classifier: Programming Language :: Python :: 3.5 -Classifier: Programming Language :: Python :: 3.6 -Classifier: Programming Language :: Python :: 3.7 Classifier: Programming Language :: Python :: 3.8 Classifier: Programming Language :: Python :: 3.9 Classifier: Programming Language :: Python :: 3.10 Classifier: Programming Language :: Python :: 3.11 Classifier: Programming Language :: Python :: 3.12 -Requires-Python: >=3.5 +Classifier: Programming Language :: Python :: 3.13 +Requires-Python: >=3.8 Description-Content-Type: text/markdown License-File: LICENSE.txt Provides-Extra: tests -Requires-Dist: asttokens >=2.1.0 ; extra == 'tests' -Requires-Dist: ipython ; extra == 'tests' -Requires-Dist: pytest ; extra == 'tests' -Requires-Dist: coverage ; extra == 'tests' -Requires-Dist: coverage-enable-subprocess ; extra == 'tests' -Requires-Dist: littleutils ; extra == 'tests' -Requires-Dist: rich ; (python_version >= "3.11") and extra == 'tests' +Requires-Dist: asttokens>=2.1.0; extra == "tests" +Requires-Dist: ipython; extra == "tests" +Requires-Dist: pytest; extra == "tests" +Requires-Dist: coverage; extra == "tests" +Requires-Dist: coverage-enable-subprocess; extra == "tests" +Requires-Dist: littleutils; extra == "tests" +Requires-Dist: rich; python_version >= "3.11" and extra == "tests" # executing diff --git a/contrib/python/executing/executing/_position_node_finder.py b/contrib/python/executing/executing/_position_node_finder.py index 8ca21a67bd..7a814150da 100644 --- a/contrib/python/executing/executing/_position_node_finder.py +++ b/contrib/python/executing/executing/_position_node_finder.py @@ -72,7 +72,7 @@ def mangled_name(node: EnhancedAST) -> str: @lru_cache(128) # pragma: no mutate def get_instructions(code: CodeType) -> list[dis.Instruction]: - return list(dis.get_instructions(code, show_caches=True)) + return list(dis.get_instructions(code)) types_cmp_issue_fix = ( @@ -114,7 +114,7 @@ class PositionNodeFinder(object): """ def __init__(self, frame: FrameType, stmts: Set[EnhancedAST], tree: ast.Module, lasti: int, source: Source): - self.bc_list = get_instructions(frame.f_code) + self.bc_dict={bc.offset:bc for bc in get_instructions(frame.f_code) } self.source = source self.decorator: Optional[EnhancedAST] = None @@ -141,7 +141,7 @@ class PositionNodeFinder(object): # we ignore here the start position and try to find the ast-node just by end position and expected node type # This is save, because there can only be one attribute ending at a specific point in the source code. typ = (ast.Attribute,) - elif self.opname(lasti) == "CALL": + elif self.opname(lasti) in ("CALL", "CALL_KW"): # A CALL instruction can be a method call, in which case the lineno and col_offset gets changed by the compiler. # Therefore we ignoring here this attributes and searchnig for a Call-node only by end_col_offset and end_lineno. # This is save, because there can only be one method ending at a specific point in the source code. @@ -156,13 +156,18 @@ class PositionNodeFinder(object): typ=typ, ) - self.known_issues(self.result, self.instruction(lasti)) + instruction = self.instruction(lasti) + assert instruction is not None + + self.result = self.fix_result(self.result, instruction) + + self.known_issues(self.result, instruction) self.test_for_decorator(self.result, lasti) # verify if self.decorator is None: - self.verify(self.result, self.instruction(lasti)) + self.verify(self.result, instruction) else: assert_(self.decorator in self.result.decorator_list) @@ -213,6 +218,32 @@ class PositionNodeFinder(object): if sys.version_info < (3, 12): index += 4 + def fix_result( + self, node: EnhancedAST, instruction: dis.Instruction + ) -> EnhancedAST: + if ( + sys.version_info >= (3, 12, 5) + and instruction.opname in ("GET_ITER", "FOR_ITER") + and isinstance(node.parent, ast.For) + and node is node.parent.iter + ): + # node positions have changed in 3.12.5 + # https://github.com/python/cpython/issues/93691 + # `for` calls __iter__ and __next__ during execution, the calling + # expression of these calls was the ast.For node since cpython 3.11 (see test_iter). + # cpython 3.12.5 changed this to the `iter` node of the loop, to make tracebacks easier to read. + # This keeps backward compatibility with older executing versions. + + # there are also cases like: + # + # for a in iter(l): pass + # + # where `iter(l)` would be otherwise the resulting node for the `iter()` call and the __iter__ call of the for implementation. + # keeping the old behaviour makes it possible to distinguish both cases. + + return node.parent + return node + def known_issues(self, node: EnhancedAST, instruction: dis.Instruction) -> None: if instruction.opname in ("COMPARE_OP", "IS_OP", "CONTAINS_OP") and isinstance( node, types_cmp_issue @@ -324,6 +355,35 @@ class PositionNodeFinder(object): ): raise KnownIssue("exception generation maps to condition") + if sys.version_info >= (3, 13): + if instruction.opname in ( + "STORE_FAST_STORE_FAST", + "STORE_FAST_LOAD_FAST", + "LOAD_FAST_LOAD_FAST", + ): + raise KnownIssue(f"can not map {instruction.opname} to two ast nodes") + + if instruction.opname == "LOAD_FAST" and instruction.argval == "__class__": + # example: + # class T: + # def a(): + # super() + # some_node # <- there is a LOAD_FAST for this node because we use super() + + raise KnownIssue( + f"loading of __class__ is accociated with a random node at the end of a class if you use super()" + ) + + if ( + instruction.opname == "COMPARE_OP" + and isinstance(node, ast.UnaryOp) + and isinstance(node.operand,ast.Compare) + and isinstance(node.op, ast.Not) + ): + # work around for + # https://github.com/python/cpython/issues/114671 + self.result = node.operand + @staticmethod def is_except_cleanup(inst: dis.Instruction, node: EnhancedAST) -> bool: if inst.opname not in ( @@ -703,6 +763,52 @@ class PositionNodeFinder(object): if node_match(ast.FormattedValue) and inst_match("FORMAT_VALUE"): return + if sys.version_info >= (3, 13): + + if inst_match("NOP"): + return + + if inst_match("TO_BOOL") and node_match(ast.BoolOp): + return + + if inst_match("CALL_KW") and node_match((ast.Call, ast.ClassDef)): + return + + if inst_match("LOAD_FAST", argval=".type_params"): + return + + if inst_match("LOAD_FAST", argval="__classdict__"): + return + + if inst_match("LOAD_FAST") and node_match( + ( + ast.FunctionDef, + ast.ClassDef, + ast.TypeAlias, + ast.TypeVar, + ast.Lambda, + ast.AsyncFunctionDef, + ) + ): + # These are loads for closure variables. + # It is difficult to check that this is actually closure variable, see: + # https://github.com/alexmojaki/executing/pull/80#discussion_r1716027317 + return + + if ( + inst_match("LOAD_FAST") + and node_match(ast.TypeAlias) + and node.name.id == instruction.argval + ): + return + + if inst_match("STORE_NAME",argval="__static_attributes__"): + # the node is the first node in the body + return + + if inst_match("LOAD_FAST") and isinstance(node.parent,ast.TypeVar): + return + # old verifier @@ -771,11 +877,14 @@ class PositionNodeFinder(object): raise VerifierFailure(title, node, instruction) - def instruction(self, index: int) -> dis.Instruction: - return self.bc_list[index // 2] + def instruction(self, index: int) -> Optional[dis.Instruction]: + return self.bc_dict.get(index,None) def opname(self, index: int) -> str: - return self.instruction(index).opname + i=self.instruction(index) + if i is None: + return "CACHE" + return i.opname extra_node_types=() if sys.version_info >= (3,12): @@ -798,7 +907,10 @@ class PositionNodeFinder(object): *extra_node_types, ), ) -> EnhancedAST: - position = self.instruction(index).positions + instruction = self.instruction(index) + assert instruction is not None + + position = instruction.positions assert position is not None and position.lineno is not None return only( diff --git a/contrib/python/executing/executing/executing.py b/contrib/python/executing/executing/executing.py index 7727c42232..5cf117e18c 100644 --- a/contrib/python/executing/executing/executing.py +++ b/contrib/python/executing/executing/executing.py @@ -273,16 +273,15 @@ class Source(object): node_finder = NodeFinder(frame, stmts, tree, lasti, source) node = node_finder.result decorator = node_finder.decorator + + if node: + new_stmts = {statement_containing_node(node)} + assert_(new_stmts <= stmts) + stmts = new_stmts except Exception: if TESTING: raise - assert stmts is not None - if node: - new_stmts = {statement_containing_node(node)} - assert_(new_stmts <= stmts) - stmts = new_stmts - executing_cache[key] = args = source, node, stmts, decorator return Executing(frame, *args) diff --git a/contrib/python/executing/executing/version.py b/contrib/python/executing/executing/version.py index 9d909dcc3c..b15121b0fe 100644 --- a/contrib/python/executing/executing/version.py +++ b/contrib/python/executing/executing/version.py @@ -1 +1 @@ -__version__ = '2.0.1'
\ No newline at end of file +__version__ = '2.1.0'
\ No newline at end of file diff --git a/contrib/python/executing/ya.make b/contrib/python/executing/ya.make index b676502282..b437b26981 100644 --- a/contrib/python/executing/ya.make +++ b/contrib/python/executing/ya.make @@ -2,7 +2,7 @@ PY3_LIBRARY() -VERSION(2.0.1) +VERSION(2.1.0) LICENSE(MIT) |