示例#1
0
def func2clean_signature_str(func) ->\
    'without __annotations__, __defaults__, __kwdefaults__':

    ## |  function(code, globals[, name[, argdefs[, closure]]])
    ## |
    ## |  Create a function object from a code object and a dictionary.
    ## |  The optional name string overrides the name from the code object.
    ## |  The optional argdefs tuple specifies the default argument values.
    ## |  The optional closure tuple supplies the bindings for free variables.
    if 1:
        code = func.__code__
        closure = code.co_freevars
        try:
            _clean = FunctionType(code, {}, closure=func.__closure__)
        except:
            print(func.__closure__)
            raise
    else:
        # fail when func contains freevars:
        def _clean():
            # to provide a clean env to extract signature string
            # used by func2code_signature_str
            pass

        _clean.__code__ = func.__code__
    sig = signature(_clean)
    return str(sig)
示例#2
0
def _update_function(oldfunc: FunctionType, newfunc: FunctionType):
    """Update a function object."""
    logger.info(f"Patch function {oldfunc.__qualname__}")
    oldfunc.__doc__ = newfunc.__doc__
    oldfunc.__dict__.update(newfunc.__dict__)
    oldfunc.__annotations__ = newfunc.__annotations__
    oldfunc.__code__ = newfunc.__code__
    oldfunc.__defaults__ = newfunc.__defaults__
示例#3
0
def conform(self, obj1: types.FunctionType, obj2: types.FunctionType):
    fv1 = obj1.__code__.co_freevars
    fv2 = obj2.__code__.co_freevars
    if fv1 != fv2:
        msg = (
            f"Cannot replace closure `{obj1.__name__}` because the free "
            f"variables changed. Before: {fv1}; after: {fv2}."
        )
        if ("__class__" in (fv1 or ())) ^ ("__class__" in (fv2 or ())):
            msg += " Note: The use of `super` entails the `__class__` free variable."
        raise ConformException(msg)
    obj1.__code__ = obj2.__code__
    obj1.__defaults__ = obj2.__defaults__
    obj1.__kwdefaults__ = obj2.__kwdefaults__
示例#4
0
def inline(pre_func: FunctionType, func: FunctionType,
           pre_func_arguments: dict):
    """Insert `prefunc` at the beginning of `func` and return the corresponding
    function.

    `pre_func` should not have a return statement (else a ValueError is raised).
    `pre_func_arguments` keys should be identical as `pre_func` arguments names else a
    TypeError is raised.

    This approach takes less CPU instructions than the standard decorator approach.

    Example:

    def pre_func(b, c):
        a = "hello"
        print(a + " " + b + " " + c)

    def func(x, y):
        z = x + 2 * y
        return z ** 2

    The returned function corresponds to:

    def inlined(x, y):
        a = "hello"
        print(a)
        z = x + 2 * y
        return z ** 2
    """

    new_func = FunctionType(
        func.__code__,
        func.__globals__,
        func.__name__,
        func.__defaults__,
        func.__closure__,
    )

    if not has_no_return(pre_func):
        raise ValueError("`pre_func` returns something")

    pinned_pre_func = pin_arguments(pre_func, pre_func_arguments)
    pinned_pre_func_code = pinned_pre_func.__code__
    pinned_pre_func_co_consts = pinned_pre_func_code.co_consts
    pinned_pre_func_co_names = pinned_pre_func_code.co_names
    pinned_pre_func_co_varnames = pinned_pre_func_code.co_varnames
    pinned_pre_func_instructions = tuple(get_instructions(pinned_pre_func))
    pinned_pre_func_instructions_without_return = pinned_pre_func_instructions[:
                                                                               -2]

    func_code = func.__code__
    func_co_consts = func_code.co_consts
    func_co_names = func_code.co_names
    func_co_varnames = func_code.co_varnames

    func_instructions = tuple(get_instructions(func))
    shifted_func_instructions = shift_instructions(
        func_instructions,
        len(b"".join(pinned_pre_func_instructions_without_return)))

    new_co_consts = remove_duplicates(func_co_consts +
                                      pinned_pre_func_co_consts)
    new_co_names = remove_duplicates(func_co_names + pinned_pre_func_co_names)
    new_co_varnames = remove_duplicates(func_co_varnames +
                                        pinned_pre_func_co_varnames)

    trans_co_consts = get_transitions(pinned_pre_func_co_consts, new_co_consts)
    trans_co_names = get_transitions(pinned_pre_func_co_names, new_co_names)
    trans_co_varnames = get_transitions(pinned_pre_func_co_varnames,
                                        new_co_varnames)

    transitions = {
        **get_b_transitions(trans_co_consts, OpCode.LOAD_CONST, OpCode.LOAD_CONST),
        **get_b_transitions(trans_co_names, OpCode.LOAD_GLOBAL, OpCode.LOAD_GLOBAL),
        **get_b_transitions(trans_co_names, OpCode.LOAD_METHOD, OpCode.LOAD_METHOD),
        **get_b_transitions(trans_co_names, OpCode.LOAD_ATTR, OpCode.LOAD_ATTR),
        **get_b_transitions(trans_co_names, OpCode.STORE_ATTR, OpCode.STORE_ATTR),
        **get_b_transitions(trans_co_varnames, OpCode.LOAD_FAST, OpCode.LOAD_FAST),
        **get_b_transitions(trans_co_varnames, OpCode.STORE_FAST, OpCode.STORE_FAST),
    }

    new_pinned_pre_func_instructions = tuple(
        transitions.get(instruction, instruction)
        for instruction in pinned_pre_func_instructions_without_return)

    new_instructions = new_pinned_pre_func_instructions + shifted_func_instructions
    new_co_code = b"".join(new_instructions)

    nfcode = new_func.__code__

    python_version = sys.version_info

    if python_version.minor != 8:
        new_func.__code__ = CodeType(
            nfcode.co_argcount,
            nfcode.co_kwonlyargcount,
            len(new_co_varnames),
            nfcode.co_stacksize,
            nfcode.co_flags,
            new_co_code,
            new_co_consts,
            new_co_names,
            new_co_varnames,
            nfcode.co_filename,
            nfcode.co_name,
            nfcode.co_firstlineno,
            nfcode.co_lnotab,
            nfcode.co_freevars,
            nfcode.co_cellvars,
        )

        return new_func

    new_func.__code__ = CodeType(
        nfcode.co_argcount,
        nfcode.co_posonlyargcount,
        nfcode.co_kwonlyargcount,
        len(new_co_varnames),
        nfcode.co_stacksize,
        nfcode.co_flags,
        new_co_code,
        new_co_consts,
        new_co_names,
        new_co_varnames,
        nfcode.co_filename,
        nfcode.co_name,
        nfcode.co_firstlineno,
        nfcode.co_lnotab,
        nfcode.co_freevars,
        nfcode.co_cellvars,
    )

    return new_func
示例#5
0
def pin_arguments(func: FunctionType, arguments: dict):
    """Transform `func` in a function with no arguments.

    Example:

    def func(a, b):
        c = 4
        print(str(a) + str(c))

        return b

    The function returned by pin_arguments(func, {"a": 10, "b": 11}) is equivalent to:

    def pinned_func():
        c = 4
        print(str(10) + str(c))

        return 11

    This function is in some ways equivalent to functools.partials but with a faster
    runtime.

    `arguments` keys should be identical as `func` arguments names else a TypeError is
    raised.
    """

    if signature(func).parameters.keys() != set(arguments):
        raise TypeError("`arguments` and `func` arguments do not correspond")

    func_code = func.__code__
    func_co_consts = func_code.co_consts
    func_co_varnames = func_code.co_varnames

    new_co_consts = remove_duplicates(func_co_consts +
                                      tuple(arguments.values()))
    new_co_varnames = tuple(item for item in func_co_varnames
                            if item not in arguments)

    trans_co_varnames2_co_consts = {
        func_co_varnames.index(key): new_co_consts.index(value)
        for key, value in arguments.items()
    }

    trans_co_varnames = get_transitions(func_co_varnames, new_co_varnames)

    transitions = {
        **get_b_transitions(trans_co_varnames2_co_consts, OpCode.LOAD_FAST, OpCode.LOAD_CONST),
        **get_b_transitions(trans_co_varnames, OpCode.LOAD_FAST, OpCode.LOAD_FAST),
        **get_b_transitions(trans_co_varnames, OpCode.STORE_FAST, OpCode.STORE_FAST),
    }

    func_instructions = get_instructions(func)
    new_func_instructions = tuple(
        transitions.get(instruction, instruction)
        for instruction in func_instructions)

    new_co_code = b"".join(new_func_instructions)

    new_func = FunctionType(
        func.__code__,
        func.__globals__,
        func.__name__,
        func.__defaults__,
        func.__closure__,
    )

    nfcode = new_func.__code__

    python_version = sys.version_info

    if python_version.minor != 8:
        new_func.__code__ = CodeType(
            0,
            0,
            len(new_co_varnames),
            nfcode.co_stacksize,
            nfcode.co_flags,
            new_co_code,
            new_co_consts,
            nfcode.co_names,
            new_co_varnames,
            nfcode.co_filename,
            nfcode.co_name,
            nfcode.co_firstlineno,
            nfcode.co_lnotab,
            nfcode.co_freevars,
            nfcode.co_cellvars,
        )

        return new_func

    new_func.__code__ = CodeType(
        0,
        0,
        0,
        len(new_co_varnames),
        nfcode.co_stacksize,
        nfcode.co_flags,
        new_co_code,
        new_co_consts,
        nfcode.co_names,
        new_co_varnames,
        nfcode.co_filename,
        nfcode.co_name,
        nfcode.co_firstlineno,
        nfcode.co_lnotab,
        nfcode.co_freevars,
        nfcode.co_cellvars,
    )

    return new_func
示例#6
0
def allow_goto(f: types.FunctionType):
    assert isinstance(
        f, types.FunctionType), "expect a function, got a {}".format(f)
    f.__code__ = _allow_goto(f.__code__, f.__globals__)
    return f