示例#1
0
    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
示例#2
0
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