| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139 |
- 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
|