You cannot select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
	
	
		
			140 lines
		
	
	
		
			4.0 KiB
		
	
	
	
		
			Python
		
	
			
		
		
	
	
			140 lines
		
	
	
		
			4.0 KiB
		
	
	
	
		
			Python
		
	
 | 
						|
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
 |