Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
629d978
fix(3.13): show_caches is deprecated
15r10nk Jan 27, 2024
d2e50a1
fix(3.13): added new rules to the verification
15r10nk Jan 27, 2024
a855297
build(3.13): added 3.13 to ci workflow
15r10nk Jan 27, 2024
7d6a107
fix(3.13): fixed typing errors
15r10nk Jan 27, 2024
6bca899
fix(3.13): handle STORE_FAST_STORE_FAST and similar instructions as k…
15r10nk Jan 27, 2024
6fb0fac
fix(3.13): loading of __class__ is mapped to the last element of the …
15r10nk Jan 27, 2024
8c2d3a3
fix(3.13): handle CALL_KW like method calls which are only located by…
15r10nk Jan 27, 2024
58f7489
fix(3.13): a lambda can also have nonlocal variables
15r10nk Jan 27, 2024
b9a2921
fix(3.13): a async function can also have nonlocal variables
15r10nk Jan 27, 2024
edad4a3
fix(3.13): COMPARE_OP maps always to ast.Compare
15r10nk Jan 30, 2024
0490384
fix(3.13): a type variable can also have nonlocal variables
15r10nk Jan 30, 2024
14f94d7
test(3.13): handle optimization of `not not x`
15r10nk Jan 30, 2024
27b446f
test: fixed tests for 3.13.0b1
15r10nk Jun 4, 2024
e1e4ac8
fix: allow to LOAD_FAST variables for TypeVars
15r10nk Jun 5, 2024
315d1df
fix: skip files with raise an recursion error in 3.13, because the re…
15r10nk Jun 26, 2024
6425ffb
test(3.13): added sample_results
15r10nk Jan 30, 2024
d55eb52
test: skip module tests for now
15r10nk Jul 19, 2024
dc03e8c
refactor: review changes
15r10nk Aug 13, 2024
c4f1862
fix: handle __firstlineno__
15r10nk Aug 15, 2024
960dee1
fix: removed unused verification
15r10nk Aug 23, 2024
de6b2fa
doc: review changes
15r10nk Aug 25, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ jobs:
runs-on: ubuntu-20.04
strategy:
matrix:
python-version: [3.8, 3.9, '3.10', 3.11, 3.12-dev]
python-version: [3.8, 3.9, '3.10', 3.11, 3.12-dev,3.13-dev]

steps:
- uses: actions/checkout@v2
Expand All @@ -31,7 +31,7 @@ jobs:
pip install mypy==0.910
python -m mypy executing --exclude=executing/_position_node_finder.py
# fromJson because https://github.community/t/passing-an-array-literal-to-contains-function-causes-syntax-error/17213/3
if: ${{ !contains(fromJson('["pypy-3.6", "3.11","3.12-dev"]'), matrix.python-version) }}
if: ${{ !contains(fromJson('["pypy-3.6", "3.11","3.12-dev","3.13-dev"]'), matrix.python-version) }}
# pypy < 3.8 very doesn't work
- name: Mypy testing (3.11)
run: |
Expand Down
104 changes: 94 additions & 10 deletions executing/_position_node_finder.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 = (
Expand Down Expand Up @@ -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
Expand All @@ -141,7 +141,7 @@ def __init__(self, frame: FrameType, stmts: Set[EnhancedAST], tree: ast.Module,
# 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.
Expand All @@ -156,15 +156,18 @@ def __init__(self, frame: FrameType, stmts: Set[EnhancedAST], tree: ast.Module,
typ=typ,
)

self.result = self.fix_result(self.result, self.instruction(lasti))
instruction = self.instruction(lasti)
assert instruction is not None

self.known_issues(self.result, self.instruction(lasti))
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)

Expand Down Expand Up @@ -352,6 +355,35 @@ def known_issues(self, node: EnhancedAST, instruction: dis.Instruction) -> None:
):
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 (
Expand Down Expand Up @@ -731,6 +763,52 @@ def node_match(node_type: Union[Type, Tuple[Type, ...]], **kwargs: Any) -> bool:
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

Expand Down Expand Up @@ -799,11 +877,14 @@ def node_match(node_type: Union[Type, Tuple[Type, ...]], **kwargs: Any) -> bool:

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):
Expand All @@ -826,7 +907,10 @@ def find_node(
*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(
Expand Down
Loading