def modify(self, code, *, inner=False): initial_bytecode = Bytecode.from_code(code) modified_bytecode = Bytecode() modified_bytecode.first_lineno = initial_bytecode.first_lineno modified_bytecode.argcount = code.co_argcount modified_bytecode.argnames = initial_bytecode.argnames modified_bytecode.name = initial_bytecode.name modified_bytecode.freevars = code.co_freevars modified_bytecode.cellvars = code.co_cellvars first_line_no = initial_bytecode.first_lineno if inner: modified_bytecode.extend([ Instr('LOAD_NAME', arg=self._command, lineno=first_line_no), Instr('LOAD_CONST', arg=DebugCommand.STEP_OVER, lineno=first_line_no), Instr('COMPARE_OP', arg=Compare.EQ, lineno=first_line_no), Instr('STORE_NAME', arg='is_over', lineno=first_line_no), ]) # добавляем инструкции отладки перед первой строкой модуля if not inner: modified_bytecode.extend( self._get_trace_func_call_instructions(first_line_no)) previous_line_no = first_line_no for instr in initial_bytecode: if not isinstance(instr, Instr): modified_bytecode.append(instr) continue if isinstance(instr.arg, types.CodeType): old_instr_name = instr.name new_co = self.modify(instr.arg, inner=True) instr.set(old_instr_name, new_co) skip = Label() if instr.lineno != previous_line_no: if inner: modified_bytecode.extend([ Instr('LOAD_NAME', arg='is_over', lineno=instr.lineno), Instr('POP_JUMP_IF_TRUE', arg=skip, lineno=instr.lineno) ]) modified_bytecode.extend([ Instr('LOAD_NAME', arg=self._command, lineno=instr.lineno), Instr('LOAD_CONST', arg=DebugCommand.STEP_OUT, lineno=instr.lineno), Instr('COMPARE_OP', arg=Compare.EQ, lineno=instr.lineno), Instr('POP_JUMP_IF_TRUE', arg=skip, lineno=instr.lineno) ]) modified_bytecode.extend( self._get_trace_func_call_instructions(instr.lineno)) if inner: modified_bytecode.append(skip) previous_line_no = instr.lineno modified_bytecode.append(instr) code = modified_bytecode.to_code() return code
def as_namedlist(name, bases, namespace: dict): try: module = sys._getframe(1).f_globals.get('__name__', '__main__') except (AttributeError, ValueError): module = '__main__' namespace = {**namespace} annotations: dict = namespace.get('__annotations__') try: filepath = sys.modules[module].__file__ except (AttributeError, IndexError): filepath = "<unknown>" if annotations is not None: for i, (k, v) in enumerate(annotations.items()): if k in namespace: raise AttributeError getter_code = Bytecode() getter_code.filename = filepath getter_code.argcount = 1 getter_code.argnames.append('self') getter_code.append(Instr('LOAD_FAST', 'self')) getter_code.append(Instr('LOAD_CONST', i)) getter_code.append(Instr('BINARY_SUBSCR')) getter_code.append(Instr('RETURN_VALUE')) getter_code.flags = CompilerFlags.OPTIMIZED | CompilerFlags.NEWLOCALS | CompilerFlags.NOFREE getter_fn = property(get_func_from_code(getter_code.to_code(), k)) setter_code = Bytecode() setter_code.filename = filepath setter_code.argcount = 2 setter_code.argnames.extend(['self', 'value']) setter_code.append(Instr('LOAD_FAST', 'value')) setter_code.append(Instr('LOAD_FAST', 'self')) setter_code.append(Instr('LOAD_CONST', i)) setter_code.append(Instr('STORE_SUBSCR')) setter_code.append(Instr('LOAD_CONST', None)) setter_code.append(Instr('RETURN_VALUE')) setter_code.flags = CompilerFlags.OPTIMIZED | CompilerFlags.NEWLOCALS | CompilerFlags.NOFREE setter_fn = getter_fn.setter( get_func_from_code(setter_code.to_code(), k)) namespace[k] = setter_fn init_code = Bytecode() init_code.name = '__init__' init_code.filename = filepath ary_num = len(annotations) args = list(annotations) init_code.argcount = ary_num + 1 init_code.argnames.extend(['self', *args]) if ary_num: init_code.append(Instr('LOAD_FAST', 'self')) if ary_num >= 4: init_code.append(Instr('DUP_TOP')) for i in range((ary_num - 2) // 2): init_code.append(Instr('DUP_TOP_TWO')) if ary_num % 2: init_code.append(Instr('DUP_TOP')) else: for i in range(ary_num - 1): init_code.append(Instr('DUP_TOP')) for i in range(ary_num): init_code.append(Instr("LOAD_FAST", args[i])) init_code.append(Instr("LIST_APPEND", ary_num - i)) init_code.append(Instr('LOAD_CONST', None)) init_code.append(Instr('RETURN_VALUE')) init_code.flags = CompilerFlags.OPTIMIZED | CompilerFlags.NEWLOCALS | CompilerFlags.NOFREE namespace['__init__'] = get_func_from_code(init_code.to_code(), '__init__') fmt = '{}({})'.format(name, ', '.join(f'{arg}={{!r}}' for arg in args)) str_code = Bytecode() str_code.argcount = 1 str_code.argnames.append('self') str_code.append(Instr('LOAD_CONST', fmt.format)) str_code.append(Instr('LOAD_FAST', 'self')) str_code.append(Instr('CALL_FUNCTION_EX', 0)) str_code.append(Instr('RETURN_VALUE')) str_code.flags = CompilerFlags.OPTIMIZED | CompilerFlags.NEWLOCALS | CompilerFlags.NOFREE namespace['__str__'] = get_func_from_code(str_code.to_code(), '__str__') return bases if any(issubclass(t, list) for t in bases) else (*bases, list), namespace