1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139
|
import ast
import sys
import dis
from typing import cast, Any,Iterator
import types
def assert_(condition, message=""):
# type: (Any, str) -> None
"""
Like an assert statement, but unaffected by -O
:param condition: value that is expected to be truthy
:type message: Any
"""
if not condition:
raise AssertionError(str(message))
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:
from collections import namedtuple
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)
def get_instructions(co):
# type: (types.CodeType) -> Iterator[EnhancedInstruction]
lineno = co.co_firstlineno
for inst in _get_instructions(co):
inst = cast(EnhancedInstruction, inst)
lineno = inst.starts_line or lineno
assert_(lineno)
inst.lineno = lineno
yield inst
# 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
# 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 EnhancedInstruction(Instruction):
_copied = None # type: bool
def mangled_name(node):
# type: (EnhancedAST) -> str
"""
Parameters:
node: the node which should be mangled
name: the name of the node
Returns:
The mangled name of `node`
"""
function_class_types=(ast.FunctionDef, ast.ClassDef, ast.AsyncFunctionDef)
if isinstance(node, ast.Attribute):
name = node.attr
elif isinstance(node, ast.Name):
name = node.id
elif isinstance(node, (ast.alias)):
name = node.asname or node.name.split(".")[0]
elif isinstance(node, function_class_types):
name = node.name
elif isinstance(node, ast.ExceptHandler):
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")
if name.startswith("__") and not name.endswith("__"):
parent,child=node.parent,node
while not (isinstance(parent,ast.ClassDef) and child not in parent.bases):
if not hasattr(parent,"parent"):
break # pragma: no mutate
parent,child=parent.parent,parent
else:
class_name=parent.name.lstrip("_")
if class_name!="" and child not in parent.decorator_list:
return "_" + class_name + name
return name
|