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