diff options
author | robot-contrib <robot-contrib@yandex-team.com> | 2023-10-17 22:08:28 +0300 |
---|---|---|
committer | robot-contrib <robot-contrib@yandex-team.com> | 2023-10-17 22:26:26 +0300 |
commit | 35f4b62b12d72e4f7147e179e4e5f78a291acf60 (patch) | |
tree | d55e7660729d6de65c669186a8de86f9c4efe1c6 | |
parent | cff48010dc0763aea3c7a1233f7a7f75e04b37dd (diff) | |
download | ydb-35f4b62b12d72e4f7147e179e4e5f78a291acf60.tar.gz |
Update contrib/python/executing to 2.0.0
-rw-r--r-- | contrib/python/executing/.dist-info/METADATA | 13 | ||||
-rw-r--r-- | contrib/python/executing/README.md | 2 | ||||
-rw-r--r-- | contrib/python/executing/executing/_position_node_finder.py | 286 | ||||
-rw-r--r-- | contrib/python/executing/executing/executing.py | 139 | ||||
-rw-r--r-- | contrib/python/executing/executing/version.py | 2 | ||||
-rw-r--r-- | contrib/python/executing/ya.make | 2 |
6 files changed, 294 insertions, 150 deletions
diff --git a/contrib/python/executing/.dist-info/METADATA b/contrib/python/executing/.dist-info/METADATA index bf103f9c95..5ec917a59e 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: 1.2.0 +Version: 2.0.0 Summary: Get the currently executing AST node of a frame, and other information Home-page: https://github.com/alexmojaki/executing Author: Alex Hall @@ -8,8 +8,6 @@ Author-email: alex.mojaki@gmail.com License: MIT Classifier: License :: OSI Approved :: MIT License Classifier: Programming Language :: Python -Classifier: Programming Language :: Python :: 2 -Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.5 Classifier: Programming Language :: Python :: 3.6 @@ -18,18 +16,21 @@ 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 Description-Content-Type: text/markdown License-File: LICENSE.txt -Requires-Dist: typing ; python_version < "3.5" Provides-Extra: tests -Requires-Dist: asttokens ; 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 -[![Build Status](https://github.com/alexmojaki/executing/workflows/Tests/badge.svg?branch=master)](https://github.com/alexmojaki/executing/actions) [![Coverage Status](https://coveralls.io/repos/github/alexmojaki/executing/badge.svg?branch=master)](https://coveralls.io/github/alexmojaki/executing?branch=master) [![Supports Python versions 2.7 and 3.5+, including PyPy](https://img.shields.io/pypi/pyversions/executing.svg)](https://pypi.python.org/pypi/executing) +[![Build Status](https://github.com/alexmojaki/executing/workflows/Tests/badge.svg?branch=master)](https://github.com/alexmojaki/executing/actions) [![Coverage Status](https://coveralls.io/repos/github/alexmojaki/executing/badge.svg?branch=master)](https://coveralls.io/github/alexmojaki/executing?branch=master) [![Supports Python versions 3.5+, including PyPy](https://img.shields.io/pypi/pyversions/executing.svg)](https://pypi.python.org/pypi/executing) This mini-package lets you get information about what a frame is currently doing, particularly the AST node being executed. diff --git a/contrib/python/executing/README.md b/contrib/python/executing/README.md index 46bc01596a..1b4dbd8f07 100644 --- a/contrib/python/executing/README.md +++ b/contrib/python/executing/README.md @@ -1,6 +1,6 @@ # executing -[![Build Status](https://github.com/alexmojaki/executing/workflows/Tests/badge.svg?branch=master)](https://github.com/alexmojaki/executing/actions) [![Coverage Status](https://coveralls.io/repos/github/alexmojaki/executing/badge.svg?branch=master)](https://coveralls.io/github/alexmojaki/executing?branch=master) [![Supports Python versions 2.7 and 3.5+, including PyPy](https://img.shields.io/pypi/pyversions/executing.svg)](https://pypi.python.org/pypi/executing) +[![Build Status](https://github.com/alexmojaki/executing/workflows/Tests/badge.svg?branch=master)](https://github.com/alexmojaki/executing/actions) [![Coverage Status](https://coveralls.io/repos/github/alexmojaki/executing/badge.svg?branch=master)](https://coveralls.io/github/alexmojaki/executing?branch=master) [![Supports Python versions 3.5+, including PyPy](https://img.shields.io/pypi/pyversions/executing.svg)](https://pypi.python.org/pypi/executing) This mini-package lets you get information about what a frame is currently doing, particularly the AST node being executed. diff --git a/contrib/python/executing/executing/_position_node_finder.py b/contrib/python/executing/executing/_position_node_finder.py index 8b3aec0866..8ca21a67bd 100644 --- a/contrib/python/executing/executing/_position_node_finder.py +++ b/contrib/python/executing/executing/_position_node_finder.py @@ -1,4 +1,5 @@ import ast +import sys import dis from types import CodeType, FrameType from typing import Any, Callable, Iterator, Optional, Sequence, Set, Tuple, Type, Union, cast @@ -16,7 +17,8 @@ def parents(node: EnhancedAST) -> Iterator[EnhancedAST]: node = node.parent yield node else: - break + break # pragma: no mutate + def node_and_parents(node: EnhancedAST) -> Iterator[EnhancedAST]: yield node @@ -42,9 +44,12 @@ def mangled_name(node: EnhancedAST) -> str: elif isinstance(node, (ast.FunctionDef, ast.ClassDef, ast.AsyncFunctionDef)): name = node.name elif isinstance(node, ast.ExceptHandler): - name = node.name or "exc" + assert node.name + name = node.name + elif sys.version_info >= (3,12) and isinstance(node,ast.TypeVar): + name=node.name else: - raise TypeError("no node to mangle") + raise TypeError("no node to mangle for type "+repr(type(node))) if name.startswith("__") and not name.endswith("__"): @@ -52,7 +57,8 @@ def mangled_name(node: EnhancedAST) -> str: while not (isinstance(parent,ast.ClassDef) and child not in parent.bases): if not hasattr(parent,"parent"): - break + break # pragma: no mutate + parent,child=parent.parent,parent else: class_name=parent.name.lstrip("_") @@ -64,7 +70,7 @@ def mangled_name(node: EnhancedAST) -> str: return name -@lru_cache(128) +@lru_cache(128) # pragma: no mutate def get_instructions(code: CodeType) -> list[dis.Instruction]: return list(dis.get_instructions(code, show_caches=True)) @@ -154,8 +160,11 @@ class PositionNodeFinder(object): self.test_for_decorator(self.result, lasti) + # verify if self.decorator is None: self.verify(self.result, self.instruction(lasti)) + else: + assert_(self.decorator in self.result.decorator_list) def test_for_decorator(self, node: EnhancedAST, index: int) -> None: if ( @@ -169,21 +178,24 @@ class PositionNodeFinder(object): # index opname # ------------------ - # index-4 PRECALL + # index-4 PRECALL (only in 3.11) # index-2 CACHE # index CALL <- the call instruction # ... CACHE some CACHE instructions # maybe multiple other bytecode blocks for other decorators - # index-4 PRECALL + # index-4 PRECALL (only in 3.11) # index-2 CACHE # index CALL <- index of the next loop # ... CACHE some CACHE instructions # index+x STORE_* the ast-node of this instruction points to the decorated thing - if self.opname(index - 4) != "PRECALL" or self.opname(index) != "CALL": - break + if not ( + (self.opname(index - 4) == "PRECALL" or sys.version_info >= (3, 12)) + and self.opname(index) == "CALL" + ): # pragma: no mutate + break # pragma: no mutate index += 2 @@ -198,7 +210,8 @@ class PositionNodeFinder(object): self.decorator = node return - index += 4 + if sys.version_info < (3, 12): + index += 4 def known_issues(self, node: EnhancedAST, instruction: dis.Instruction) -> None: if instruction.opname in ("COMPARE_OP", "IS_OP", "CONTAINS_OP") and isinstance( @@ -232,6 +245,16 @@ class PositionNodeFinder(object): # Comprehension and generators get not fixed for now. raise KnownIssue("chain comparison inside %s can not be fixed" % (node)) + if ( + sys.version_info[:3] == (3, 11, 1) + and isinstance(node, ast.Compare) + and instruction.opname == "CALL" + and any(isinstance(n, ast.Assert) for n in node_and_parents(node)) + ): + raise KnownIssue( + "known bug in 3.11.1 https://github.com/python/cpython/issues/95921" + ) + if isinstance(node, ast.Assert): # pytest assigns the position of the assertion to all expressions of the rewritten assertion. # All the rewritten expressions get mapped to ast.Assert, which is the wrong ast-node. @@ -242,6 +265,40 @@ class PositionNodeFinder(object): # TODO: investigate raise KnownIssue("pattern matching ranges seems to be wrong") + if ( + sys.version_info >= (3, 12) + and isinstance(node, ast.Call) + and isinstance(node.func, ast.Name) + and node.func.id == "super" + ): + # super is optimized to some instructions which do not map nicely to a Call + + # find the enclosing function + func = node.parent + while hasattr(func, "parent") and not isinstance( + func, (ast.AsyncFunctionDef, ast.FunctionDef) + ): + + func = func.parent + + # get the first function argument (self/cls) + first_arg = None + + if hasattr(func, "args"): + args = [*func.args.posonlyargs, *func.args.args] + if args: + first_arg = args[0].arg + + if (instruction.opname, instruction.argval) in [ + ("LOAD_DEREF", "__class__"), + ("LOAD_FAST", first_arg), + ("LOAD_DEREF", first_arg), + ]: + raise KnownIssue("super optimization") + + if self.is_except_cleanup(instruction, node): + raise KnownIssue("exeption cleanup does not belong to the last node in a except block") + if instruction.opname == "STORE_NAME" and instruction.argval == "__classcell__": # handle stores to __classcell__ as KnownIssue, # because they get complicated if they are used in `if` or `for` loops @@ -259,6 +316,14 @@ class PositionNodeFinder(object): raise KnownIssue("store __classcell__") + if ( + instruction.opname == "CALL" + and not isinstance(node,ast.Call) + and any(isinstance(p, ast.Assert) for p in parents(node)) + and sys.version_info >= (3, 11, 2) + ): + raise KnownIssue("exception generation maps to condition") + @staticmethod def is_except_cleanup(inst: dis.Instruction, node: EnhancedAST) -> bool: if inst.opname not in ( @@ -268,6 +333,7 @@ class PositionNodeFinder(object): "STORE_GLOBAL", "DELETE_NAME", "DELETE_FAST", + "DELETE_DEREF", "DELETE_GLOBAL", ): return False @@ -291,8 +357,26 @@ class PositionNodeFinder(object): # except TypeError as msg: # print("Sorry:", msg, file=file) + if ( + isinstance(node, ast.Name) + and isinstance(node.ctx,ast.Store) + and inst.opname.startswith("STORE_") + and mangled_name(node) == inst.argval + ): + # Storing the variable is valid and no exception cleanup, if the name is correct + return False + + if ( + isinstance(node, ast.Name) + and isinstance(node.ctx,ast.Del) + and inst.opname.startswith("DELETE_") + and mangled_name(node) == inst.argval + ): + # Deleting the variable is valid and no exception cleanup, if the name is correct + return False + return any( - isinstance(n, ast.ExceptHandler) and mangled_name(n) == inst.argval + isinstance(n, ast.ExceptHandler) and n.name and mangled_name(n) == inst.argval for n in parents(node) ) @@ -353,6 +437,13 @@ class PositionNodeFinder(object): # call to the generator function return + if ( + sys.version_info >= (3, 12) + and inst_match(("LOAD_FAST_AND_CLEAR", "STORE_FAST")) + and node_match((ast.ListComp, ast.SetComp, ast.DictComp)) + ): + return + if inst_match(("CALL", "CALL_FUNCTION_EX")) and node_match( (ast.ClassDef, ast.Call) ): @@ -371,6 +462,7 @@ class PositionNodeFinder(object): if ( ( inst_match("LOAD_METHOD", argval="join") + or inst_match("LOAD_ATTR", argval="join") # 3.12 or inst_match(("CALL", "BUILD_STRING")) ) and node_match(ast.BinOp, left=ast.Constant, op=ast.Mod) @@ -383,8 +475,6 @@ class PositionNodeFinder(object): # data: int return - if self.is_except_cleanup(instruction, node): - return if inst_match(("DELETE_NAME", "DELETE_FAST")) and node_match( ast.Name, id=instruction.argval, ctx=ast.Del @@ -458,25 +548,162 @@ class PositionNodeFinder(object): ): return - if inst_match(("JUMP_IF_TRUE_OR_POP", "JUMP_IF_FALSE_OR_POP")) and node_match( - ast.BoolOp - ): + if inst_match( + ( + "JUMP_IF_TRUE_OR_POP", + "JUMP_IF_FALSE_OR_POP", + "POP_JUMP_IF_TRUE", + "POP_JUMP_IF_FALSE", + ) + ) and node_match(ast.BoolOp): # and/or short circuit return if inst_match("DELETE_SUBSCR") and node_match(ast.Subscript, ctx=ast.Del): return - if node_match(ast.Name, ctx=ast.Load) and inst_match( - ("LOAD_NAME", "LOAD_FAST", "LOAD_GLOBAL"), argval=mangled_name(node) + if ( + node_match(ast.Name, ctx=ast.Load) + or ( + node_match(ast.Name, ctx=ast.Store) + and isinstance(node.parent, ast.AugAssign) + ) + ) and inst_match( + ( + "LOAD_NAME", + "LOAD_FAST", + "LOAD_FAST_CHECK", + "LOAD_GLOBAL", + "LOAD_DEREF", + "LOAD_FROM_DICT_OR_DEREF", + ), + argval=mangled_name(node), ): return if node_match(ast.Name, ctx=ast.Del) and inst_match( - ("DELETE_NAME", "DELETE_GLOBAL"), argval=mangled_name(node) + ("DELETE_NAME", "DELETE_GLOBAL", "DELETE_DEREF"), argval=mangled_name(node) + ): + return + + if node_match(ast.Constant) and inst_match( + "LOAD_CONST", argval=cast(ast.Constant, node).value ): return + if node_match( + (ast.ListComp, ast.SetComp, ast.DictComp, ast.GeneratorExp, ast.For) + ) and inst_match(("GET_ITER", "FOR_ITER")): + return + + if sys.version_info >= (3, 12): + if node_match(ast.UnaryOp, op=ast.UAdd) and inst_match( + "CALL_INTRINSIC_1", argrepr="INTRINSIC_UNARY_POSITIVE" + ): + return + + if node_match(ast.Subscript) and inst_match("BINARY_SLICE"): + return + + if node_match(ast.ImportFrom) and inst_match( + "CALL_INTRINSIC_1", argrepr="INTRINSIC_IMPORT_STAR" + ): + return + + if ( + node_match(ast.Yield) or isinstance(node.parent, ast.GeneratorExp) + ) and inst_match("CALL_INTRINSIC_1", argrepr="INTRINSIC_ASYNC_GEN_WRAP"): + return + + if node_match(ast.Name) and inst_match("LOAD_DEREF",argval="__classdict__"): + return + + if node_match(ast.TypeVar) and ( + inst_match("CALL_INTRINSIC_1", argrepr="INTRINSIC_TYPEVAR") + or inst_match( + "CALL_INTRINSIC_2", argrepr="INTRINSIC_TYPEVAR_WITH_BOUND" + ) + or inst_match( + "CALL_INTRINSIC_2", argrepr="INTRINSIC_TYPEVAR_WITH_CONSTRAINTS" + ) + or inst_match(("STORE_FAST", "STORE_DEREF"), argrepr=mangled_name(node)) + ): + return + + if node_match(ast.TypeVarTuple) and ( + inst_match("CALL_INTRINSIC_1", argrepr="INTRINSIC_TYPEVARTUPLE") + or inst_match(("STORE_FAST", "STORE_DEREF"), argrepr=node.name) + ): + return + + if node_match(ast.ParamSpec) and ( + inst_match("CALL_INTRINSIC_1", argrepr="INTRINSIC_PARAMSPEC") + + or inst_match(("STORE_FAST", "STORE_DEREF"), argrepr=node.name)): + return + + + if node_match(ast.TypeAlias): + if( + inst_match("CALL_INTRINSIC_1", argrepr="INTRINSIC_TYPEALIAS") + or inst_match( + ("STORE_NAME", "STORE_FAST", "STORE_DEREF"), argrepr=node.name.id + ) + or inst_match("CALL") + ): + return + + + if node_match(ast.ClassDef) and node.type_params: + if inst_match( + ("STORE_DEREF", "LOAD_DEREF", "LOAD_FROM_DICT_OR_DEREF"), + argrepr=".type_params", + ): + return + + if inst_match(("STORE_FAST", "LOAD_FAST"), argrepr=".generic_base"): + return + + if inst_match( + "CALL_INTRINSIC_1", argrepr="INTRINSIC_SUBSCRIPT_GENERIC" + ): + return + + if inst_match("LOAD_DEREF",argval="__classdict__"): + return + + if node_match((ast.FunctionDef,ast.AsyncFunctionDef)) and node.type_params: + if inst_match("CALL"): + return + + if inst_match( + "CALL_INTRINSIC_2", argrepr="INTRINSIC_SET_FUNCTION_TYPE_PARAMS" + ): + return + + if inst_match("LOAD_FAST",argval=".defaults"): + return + + if inst_match("LOAD_FAST",argval=".kwdefaults"): + return + + if inst_match("STORE_NAME", argval="__classdictcell__"): + # this is a general thing + return + + + # f-strings + + if node_match(ast.JoinedStr) and ( + inst_match("LOAD_ATTR", argval="join") + or inst_match(("LIST_APPEND", "CALL")) + ): + return + + if node_match(ast.FormattedValue) and inst_match("FORMAT_VALUE"): + return + + # old verifier typ: Type = type(None) @@ -498,7 +725,7 @@ class PositionNodeFinder(object): UNARY_INVERT=ast.Invert, )[op_name] extra_filter = lambda e: isinstance(cast(ast.UnaryOp, e).op, op_type) - elif op_name in ("LOAD_ATTR", "LOAD_METHOD", "LOOKUP_METHOD"): + elif op_name in ("LOAD_ATTR", "LOAD_METHOD", "LOOKUP_METHOD","LOAD_SUPER_ATTR"): typ = ast.Attribute ctx = ast.Load extra_filter = lambda e: mangled_name(e) == instruction.argval @@ -550,11 +777,26 @@ class PositionNodeFinder(object): def opname(self, index: int) -> str: return self.instruction(index).opname + extra_node_types=() + if sys.version_info >= (3,12): + extra_node_types = (ast.type_param,) + def find_node( self, index: int, - match_positions: Sequence[str]=("lineno", "end_lineno", "col_offset", "end_col_offset"), - typ: tuple[Type, ...]=(ast.expr, ast.stmt, ast.excepthandler, ast.pattern), + match_positions: Sequence[str] = ( + "lineno", + "end_lineno", + "col_offset", + "end_col_offset", + ), + typ: tuple[Type, ...] = ( + ast.expr, + ast.stmt, + ast.excepthandler, + ast.pattern, + *extra_node_types, + ), ) -> EnhancedAST: position = self.instruction(index).positions assert position is not None and position.lineno is not None diff --git a/contrib/python/executing/executing/executing.py b/contrib/python/executing/executing/executing.py index c28091d3f8..7727c42232 100644 --- a/contrib/python/executing/executing/executing.py +++ b/contrib/python/executing/executing/executing.py @@ -25,115 +25,41 @@ SOFTWARE. import __future__ import ast import dis -import functools import inspect import io import linecache import re import sys import types -from collections import defaultdict, namedtuple +from collections import defaultdict from copy import deepcopy +from functools import lru_cache from itertools import islice +from itertools import zip_longest from operator import attrgetter +from pathlib import Path from threading import RLock -from typing import TYPE_CHECKING, Any, Callable, Dict, Iterable, Iterator, List, Optional, Sequence, Set, Sized, Tuple, Type, TypeVar, Union, cast +from tokenize import detect_encoding +from typing import TYPE_CHECKING, Any, Callable, Dict, Iterable, Iterator, List, Optional, Sequence, Set, Sized, Tuple, \ + Type, TypeVar, Union, cast if TYPE_CHECKING: # pragma: no cover from asttokens import ASTTokens, ASTText from asttokens.asttokens import ASTTextBase -function_node_types = (ast.FunctionDef,) # type: Tuple[Type, ...] -if sys.version_info[0] == 3: - function_node_types += (ast.AsyncFunctionDef,) +function_node_types = (ast.FunctionDef, ast.AsyncFunctionDef) # type: Tuple[Type, ...] -if sys.version_info[0] == 3: - # noinspection PyUnresolvedReferences - from functools import lru_cache - # noinspection PyUnresolvedReferences - from tokenize import detect_encoding - from itertools import zip_longest - # noinspection PyUnresolvedReferences,PyCompatibility - from pathlib import Path - - cache = lru_cache(maxsize=None) - text_type = str -else: - from lib2to3.pgen2.tokenize import detect_encoding, cookie_re as encoding_pattern # type: ignore[attr-defined] - from itertools import izip_longest as zip_longest - - class Path(object): - pass - - - def cache(func): - # type: (Callable) -> Callable - d = {} # type: Dict[Tuple, Callable] - - @functools.wraps(func) - def wrapper(*args): - # type: (Any) -> Any - if args in d: - return d[args] - result = d[args] = func(*args) - return result - - return wrapper - - - # noinspection PyUnresolvedReferences - text_type = unicode +cache = lru_cache(maxsize=None) # Type class used to expand out the definition of AST to include fields added by this library # It's not actually used for anything other than type checking though! class EnhancedAST(ast.AST): parent = None # type: EnhancedAST -if sys.version_info >= (3, 4): - # noinspection PyUnresolvedReferences - _get_instructions = dis.get_instructions - from dis import Instruction as _Instruction - - class Instruction(_Instruction): - lineno = None # type: int -else: - class Instruction(namedtuple('Instruction', 'offset argval opname starts_line')): - lineno = None # type: int - - from dis import HAVE_ARGUMENT, EXTENDED_ARG, hasconst, opname, findlinestarts, hasname - - # Based on dis.disassemble from 2.7 - # Left as similar as possible for easy diff - - def _get_instructions(co): - # type: (types.CodeType) -> Iterator[Instruction] - code = co.co_code - linestarts = dict(findlinestarts(co)) - n = len(code) - i = 0 - extended_arg = 0 - while i < n: - offset = i - c = code[i] - op = ord(c) - lineno = linestarts.get(i) - argval = None - i = i + 1 - if op >= HAVE_ARGUMENT: - oparg = ord(code[i]) + ord(code[i + 1]) * 256 + extended_arg - extended_arg = 0 - i = i + 2 - if op == EXTENDED_ARG: - extended_arg = oparg * 65536 - - if op in hasconst: - argval = co.co_consts[oparg] - elif op in hasname: - argval = co.co_names[oparg] - elif opname[op] == 'LOAD_FAST': - argval = co.co_varnames[oparg] - yield Instruction(offset, argval, opname[op], lineno) + +class Instruction(dis.Instruction): + lineno = None # type: int # Type class used to expand out the definition of AST to include fields added by this library @@ -157,7 +83,7 @@ def assert_(condition, message=""): def get_instructions(co): # type: (types.CodeType) -> Iterator[EnhancedInstruction] lineno = co.co_firstlineno - for inst in _get_instructions(co): + for inst in dis.get_instructions(co): inst = cast(EnhancedInstruction, inst) lineno = inst.starts_line or lineno assert_(lineno) @@ -224,29 +150,9 @@ class Source(object): """ self.filename = filename - text = ''.join(lines) - - if not isinstance(text, text_type): - encoding = self.detect_encoding(text) - # noinspection PyUnresolvedReferences - text = text.decode(encoding) - lines = [line.decode(encoding) for line in lines] - - self.text = text + self.text = ''.join(lines) self.lines = [line.rstrip('\r\n') for line in lines] - if sys.version_info[0] == 3: - ast_text = text - else: - # In python 2 it's a syntax error to parse unicode - # with an encoding declaration, so we remove it but - # leave empty lines in its place to keep line numbers the same - ast_text = ''.join([ - '\n' if i < 2 and encoding_pattern.match(line) - else line - for i, line in enumerate(lines) - ]) - self._nodes_by_line = defaultdict(list) self.tree = None self._qualnames = {} @@ -254,7 +160,7 @@ class Source(object): self._asttext = None # type: Optional[ASTText] try: - self.tree = ast.parse(ast_text, filename=filename) + self.tree = ast.parse(self.text, filename=filename) except (SyntaxError, ValueError): pass else: @@ -289,7 +195,7 @@ class Source(object): def get_lines(): # type: () -> List[str] - return linecache.getlines(cast(text_type, filename), module_globals) + return linecache.getlines(cast(str, filename), module_globals) # Save the current linecache entry, then ensure the cache is up to date. entry = linecache.cache.get(filename) # type: ignore[attr-defined] @@ -320,8 +226,7 @@ class Source(object): @classmethod def lazycache(cls, frame): # type: (types.FrameType) -> None - if sys.version_info >= (3, 5): - linecache.lazycache(frame.f_code.co_filename, frame.f_globals) + linecache.lazycache(frame.f_code.co_filename, frame.f_globals) @classmethod def executing(cls, frame_or_tb): @@ -456,7 +361,7 @@ class Source(object): @staticmethod def decode_source(source): - # type: (Union[str, bytes]) -> text_type + # type: (Union[str, bytes]) -> str if isinstance(source, bytes): encoding = Source.detect_encoding(source) return source.decode(encoding) @@ -548,10 +453,7 @@ class QualnameVisitor(ast.NodeVisitor): def visit_FunctionDef(self, node, name=None): # type: (ast.AST, Optional[str]) -> None - if sys.version_info[0] == 3: - assert isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef, ast.Lambda)), node - else: - assert isinstance(node, (ast.FunctionDef, ast.Lambda)), node + assert isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef, ast.Lambda)), node self.add_qualname(node, name) self.stack.append('<locals>') children = [] # type: Sequence[ast.AST] @@ -684,8 +586,7 @@ class SentinelNodeFinder(object): elif op_name in ('LOAD_NAME', 'LOAD_GLOBAL', 'LOAD_FAST', 'LOAD_DEREF', 'LOAD_CLASSDEREF'): typ = ast.Name ctx = ast.Load - if sys.version_info[0] == 3 or instruction.argval: - extra_filter = lambda e: e.id == instruction.argval + extra_filter = lambda e: e.id == instruction.argval elif op_name in ('COMPARE_OP', 'IS_OP', 'CONTAINS_OP'): typ = ast.Compare extra_filter = lambda e: len(e.ops) == 1 diff --git a/contrib/python/executing/executing/version.py b/contrib/python/executing/executing/version.py index e916218b9c..d97070a2a7 100644 --- a/contrib/python/executing/executing/version.py +++ b/contrib/python/executing/executing/version.py @@ -1 +1 @@ -__version__ = '1.2.0'
\ No newline at end of file +__version__ = '2.0.0'
\ No newline at end of file diff --git a/contrib/python/executing/ya.make b/contrib/python/executing/ya.make index 626c0fd559..2f9d4339d5 100644 --- a/contrib/python/executing/ya.make +++ b/contrib/python/executing/ya.make @@ -2,7 +2,7 @@ PY3_LIBRARY() -VERSION(1.2.0) +VERSION(2.0.0) LICENSE(MIT) |