Skip to content

Commit

Permalink
Merge pull request #80 from alexmojaki/3.13
Browse files Browse the repository at this point in the history
3.13 support
  • Loading branch information
15r10nk authored Aug 26, 2024
2 parents 230ef11 + de6b2fa commit ea6a695
Show file tree
Hide file tree
Showing 28 changed files with 58,429 additions and 19 deletions.
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

0 comments on commit ea6a695

Please sign in to comment.