示例#1
0
    def newObjCpt(self, elt):

        argNames = self.__code__.co_varnames[:self.__code__.co_argcount]
        argValues = [elt]

        wfunc = FunctionType(self.__code__, self.__globals__)

        class Process(object):
            __call__ = staticmethod(wfunc)

            def __setattr__(s, name, value):
                argValues[argNames.index(name)] = value
                wfunc.__defaults__ = tuple(argValues)

            def __getattr__(s, name):
                return argValues[argNames.index(name)]

            @classmethod
            def __cptDefs__(cls):
                cpts = {
                    name: cpt
                    for (name, cpt) in zip(argNames, self.__defaults__)
                    if isinstance(cpt, _cptDef)
                }
                return sorted(cpts.items(), key=lambda item: item[1].id)

        process = Process()

        argValues += [p.newObjCpt(process) for p in self.__defaults__[1:]]

        wfunc.__defaults__ = tuple(argValues)
        return process
示例#2
0
 def chained_function(meta, func, mod):
     d = _NSChainedDict(mod.__dict__, func.__globals__)
     newfunc = FunctionType(func.__code__, d)
     newfunc.__doc__ = func.__doc__
     newfunc.__defaults__ = func.__defaults__
     newfunc.__kwdefaults__ = func.__kwdefaults__
     return newfunc
示例#3
0
 def chained_function(meta, func, mod):
     d = ModuleChainedDict(mod.__dict__, func.__globals__)
     newfunc = FunctionType(func.__code, d)
     newfunc.__doc__ = func.__doc__
     newfunc.__defaults__ = newfunc.__defaults__
     newfunc.__kwdefaults__ = func.__kwdefaults__
     return newfunc
示例#4
0
    def newObjCpt(self, elt):

        argNames = self.__code__.co_varnames[:self.__code__.co_argcount]
        argValues = [elt]

        wfunc = FunctionType(self.__code__, self.__globals__)

        class Process(object):
            __call__ = staticmethod(wfunc)

            def __setattr__(s, name, value):
                argValues[argNames.index(name)] = value
                wfunc.__defaults__ = tuple(argValues)

            def __getattr__(s, name):
                return argValues[argNames.index(name)]

            @classmethod
            def __cptDefs__(cls):
                cpts= {name : cpt for (name, cpt) in zip( argNames, self.__defaults__) if isinstance(cpt, _cptDef)}
                return  sorted(cpts.items(),key =lambda item:item[1].id)

        process = Process()

        argValues += [p.newObjCpt(process) for p in   self.__defaults__[1:]]

        wfunc.__defaults__ = tuple(argValues)
        return process
示例#5
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__
示例#6
0
def create_function(name, first_arg, args, defaults, function_code):
    formatted_args = ", ".join("{}".format(arg) for arg in args)
    formatted_args = (formatted_args +
                      ", **kwargs" if args else formatted_args + " **kwargs")
    function_code = "def {}({}, {}): {}".format(name, first_arg,
                                                formatted_args, function_code)
    compiled_func = compile(function_code, name, "exec")
    function = FunctionType(compiled_func.co_consts[0], globals(), name)
    function.__defaults__ = tuple(defaults)
    return function
示例#7
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__
示例#8
0
def parameter_dependency_from_model(name: str, model_cls):
    """
    Takes a pydantic model class as input and creates
    a dependency with corresponding
    Query parameter definitions that can be used for GET
    requests.

    This will only work, if the fields defined in the
    input model can be turned into
    suitable query parameters. Otherwise fastapi
    will complain down the road.

    Arguments:
        name: Name for the dependency function.
        model_cls: A ``BaseModel`` inheriting model class as input.
    """
    names = []
    annotations: Dict[str, type] = {}
    defaults = []
    for field_model in model_cls.__fields__.values():
        if field_model.name not in ["self"]:
            field_info = field_model.field_info

            if field_model.name not in ignore_in_docs:
                names.append(field_model.name)
                annotations[field_model.name] = field_model.outer_type_
                defaults.append(
                    Query(field_model.default,
                          description=field_info.description))

    code = inspect.cleandoc("""
    def %s(%s):
        return %s(%s)
    """ % (
        name,
        ", ".join(names),
        model_cls.__name__,
        ", ".join(["%s=%s" % (name, name) for name in names]),
    ))

    compiled = compile(code, "string", "exec")
    env = {model_cls.__name__: model_cls}
    env.update(**globals())
    func = FunctionType(compiled.co_consts[0], env, name)
    func.__annotations__ = annotations
    func.__defaults__ = (*defaults, )

    return func
示例#9
0
def modfuncglobals(func, *ds, **kw):
    '''Add globals to a function.'''
    dp = dictproxy()
    newfunc = FunctionType(func.__code__, dictproxy(dp, func.__globals__))
    newfunc.__name__ = func.__name__
    newfunc.__doc__ = func.__doc__
    newfunc.__defaults__ = func.__defaults__
    newfunc.__kwdefaults__ = func.__kwdefaults__
    newfunc.__scopes__ = dp.maps

    def add(*ds, **kw):
        for d in ds[::-1]:
            newfunc.__scopes__.insert(0, d)
        if kw:
            newfunc.add(kw)
        return newfunc

    newfunc.add = add
    newfunc.add(*ds, **kw)
    return newfunc
示例#10
0
def func2not_implemented(func):
    sig_str = func2clean_signature_str(func)
    if sig_str not in cache__sig_str2code:
        code = signature_str2not_implemented_func(sig_str).__code__
        cache__sig_str2code[sig_str] = code  # code really depends on sig_str
        #cache__not_implemented_codes.add(code)
        #print('cache__not_implemented_codes', len(cache__not_implemented_codes))
    else:
        code = cache__sig_str2code[sig_str]
    code_without_freevars = _replace_codestring(func.__code__, code)
    new = FunctionType(code_without_freevars, globals())
    new.__dict__.update(vars(func))

    new.__defaults__ = func.__defaults__
    new.__kwdefaults__ = func.__kwdefaults__
    new.__doc__ = func.__doc__
    new.__name__ = func.__name__
    new.__qualname__ = func.__qualname__
    new.__module__ = func.__module__
    new.__annotations__ = func.__annotations__
    new.__dict__ = dict(func.__dict__)
    return new
    set_func_not_implemented(func)
    return func
示例#11
0
def patched_function(function):
    new_function = FunctionType(six.get_function_code(function), globals())
    if six.PY3:
        new_function.__kwdefaults__ = function.__kwdefaults__
    new_function.__defaults__ = function.__defaults__
    return new_function
示例#12
0
def patched_function(function):
    new_function = FunctionType(six.get_function_code(function), globals())
    if six.PY3:
        new_function.__kwdefaults__ = function.__kwdefaults__
    new_function.__defaults__ = function.__defaults__
    return new_function
示例#13
0
def rebuild_func(
    func,
    cls=None,
    *wrappers,
    defaults_copy=None,
    kwdefaults_copy=None,
    dict_copy=None,
    closure_copy=None,
    global_ns=None,
    name=None,
    qualname=None,
    module=False,
    doc=False,
    annotations=False,
):
    """Create a copy of `func` that can be used as a method by any `cls`

    The main perk is giving a `cls` the ability to use *any* Python
    `function` as a bound method, regardless of where it was defined.
    In other words, it doesn't matter if a function was defined in the
    global scope, another module, or in the body of another class.
    Most importantly, functions that use zero-argument super will behave
    exactly the same as if they had been defined in `cls`.

    Unfortunately, there is one limitation.
    Wrapped functions not defined in the scope of a class body will not
    be able to use zero argument super. If they are wrapped *after*
    being bound with this function, they should have no problem working

    For convenience, any *wrappers provided will be applied after binding
    in the same order as decorator syntax, top to bottom or right to left
    ie. wrappers *(a, b ,c) will be equivalent to a(b(c(func)))

    There are nearly limitless uses for this black magic, but some
    good examples are the ability to make true deepcopies of functions,
    changing default/kwonly defaults, and tricking functions into using
    global references to objects not in the module it was defined in.

    To simplify the application of making true deepcopies of functions,
    which requires deepcopying of the original function's mutable
    attributes: __defaults__, __kwdefaults__, __dict__, and __closure__,
    the arguments `defaults_copy`, `kwdefaults_copy, `dict_copy`, and
    `closure_copy`, respectively, accept a callable that takes a single
    argument (the object to be copied) and returns an object of the
    same type (and length if applicable) to the original.
    If `defaults_copy` and `kwdefaults_copy` return None, the new
    function will no longer have default values.

    Otherwise, shallow copies of __defaults__, __kwdefaults__, and
    __dict__ are used, along with a deepcopy of __closure__.
    
    Parameters:

    :: `defaults_copy`
        called as defaults_copy(func.__defaults__) to replace
        `func.__defaults__`, the attribute that stores a tuple
        containing the default values of positional arguments.
        
        Removes default positional arguments entirely if it returns None

    :: `kwdefaults_copy`
        called as kwdefaults_copy(func.__kwdefaults__) to replace
        `func.__kwdefaults__`, the attribute that stores a mapping
        containing the default values of keyword only arguments.

        Removes default kwonly arguments entirely if it returns None
        
    :: `dict_copy`
        called as dict_copy(func.__dict__) to replace `func.__dict__`

    :: `closure_copy`
        called as closure_copy(func.__closure__) to replace
        `func.__closure__`, the attribute that stores references
        to any nonlocal names used by `func`

    :: `global_ns`
        used to replace func.__globals__
        
        Will raise NameError if it doesn't contain all global references
        used by `func`.
        
    :: `name`
        a string that replaces func.__name__

    :: `qualname`
        if None, will generate a new __qualname__ automatically,
        which depends on the values in `name`, `func`, and `cls`

        Otherwise, a string

    :: `module`
        can be any type and is used to replace `func.__module__`

    :: `doc`
        can be any type and is used to replace `func.__doc__`
        
    :: `annotations`
        None to clear or mapping to replace `func.__annotations__`

        By default is a shallow copy of `func.__annotations__`
    """

    # validate func
    if not is_func(func):
        raise TypeError('func must be function object')
    else:
        f = func

    # validate cls
    if cls is None:
        class_was_not_none = False
    elif not is_heap_type(cls):
        raise TypeError('cls must be None or a Python class')
    else:
        class_was_not_none = True

    # validate defaults_copy
    defaults = f.__defaults__
    if defaults_copy is None:
        defaults = None if defaults is None else tuple_copy(defaults)
    elif not callable(defaults_copy):
        raise TypeError('defaults_copy must be a callable')
    else:
        defaults = defaults_copy(defaults)

    # validate kwdefaults_copy
    kwdefaults = f.__kwdefaults__
    if kwdefaults_copy is None:
        kwdefaults = None if kwdefaults is None else {**kwdefaults}
    elif not callable(kwdefaults_copy):
        raise TypeError('kwdefaults_copy must be a callable')
    else:
        kwdefaults = kwdefaults_copy(kwdefaults)

    # validate dict_copy
    new_dict = descriptor_getattr(f, '__dict__')
    if dict_copy is None:
        new_dict = {**new_dict}
    elif not callable(dict_copy):
        raise TypeError('dict_copy must be a callable')
    else:
        new_dict = dict_copy(new_dict)

    # validate closure_copy
    closure = f.__closure__
    if closure_copy is None:
        if is_tuple(closure):
            closure = deepcopy_closure(closure)
    elif not callable(closure_copy):
        raise TypeError('closure_copy must be a callable')
    else:
        closure = closure_copy(closure)

    # validate name
    if name is None:
        name = f.__name__
    elif not is_str(name):
        raise TypeError('function __name__ must be a string')

    # validate qualname
    if qualname is None:
        qualname = f'{cls.__name__}.{name}' if class_was_not_none else name
    elif not is_str(qualname):
        raise TypeError('function __qualname__ must be a string')

    # validate annotations
    f_annotations = f.__annotations__
    if annotations is False:
        annotations = None if f_annotations is None else {**f_annotations}
    elif not is_mapping(annotations):
        raise TypeError('annotations must be None or a mapping')

    # validate global_ns
    sentinel = object()
    if global_ns is None:
        global_ns = f.__globals__
    elif not is_dict(global_ns):
        raise TypeError('function __globals__ must be a dict')
    builtins = global_ns.get('__builtins__', sentinel)
    if builtins is sentinel:
        builtin_ns = {}
    elif is_dict(builtins):
        builtin_ns = builtins
    # the only time __builtins__ can be a module is if it IS builtins
    elif builtins is _builtins:
        builtin_ns = builtins.__dict__
    else:
        raise TypeError('f.__globals__["__builtins__"] must be a dict'
                        f'or the "builtins" module, not {builtins!r}')
    missing = set()
    code = f.__code__
    co_freevars = code.co_freevars
    co_flags = code.co_flags
    co_names = code.co_names
    co_code = code.co_code

    for co_name in co_names:
        if iskeyword(co_name):
            continue
        ob = global_ns.get(co_name, sentinel)
        if ob is sentinel:
            ob = builtin_ns.get(co_name, sentinel)
            if ob is sentinel:
                if class_was_not_none and co_name == '__class__':
                    continue
                missing.add(co_name)
    module = f.__module__ if module is False else module
    doc = f.__doc__ if doc is False else module
    if class_was_not_none:
        has_classcell = 'super' in co_names
        # replace LOAD_GLOBAL('__class__') opcodes with LOAD_DEREF('__class__')
        if '__class__' in co_names:
            j = co_names.index('__class__')
            #co_names = co_names[:j] + co_names[j+1:]
            bytecode = bytearray(co_code)
            for instr in dis.Bytecode(code):
                if instr.opname == 'LOAD_GLOBAL' and instr.argval == '__class__':
                    has_classcell = True
                    closure = closure or ()
                    i = len(closure)
                    # if len(closure) >= 256,
                    # the LOAD_DEREF opcode will use EXTENDED_ARG
                    if i > 255:
                        repl = b'\x90\x01\x88' + i.to_bytes(3, 'little')
                    else:
                        repl = b'\x88' + i.to_bytes(1, 'little')
                    x = instr.offset
                    bytecode[x:x + 2] = repl
            co_code = bytes(bytecode)
        # only add a class cell if necessary
        if has_classcell:
            cell = build_class_cell(cls)
            closure = closure or ()
            # insert/replace the __class__ cell if it is already in freevars
            if '__class__' in co_freevars:
                i = co_freevars.index('__class__')
                j = len(closure) == len(co_freevars)
                closure = (*closure[:i], cell, *closure[i + j:])
            # add if it doesn't
            else:
                co_freevars += ('__class__', )
                closure += (cell, )
    # ensure NOFREE flag is set if there are no frevars
    if not co_freevars:
        co_flags = (co_flags | NOFREE)
        if f.__closure__ is not None:
            raise TypeError('function closure was a tuple when its'
                            'code.co_freevars was empty')
    # since python 3.6, the NOFREE flag actually does what it's
    # supposed to do, so it must be unset to enable access of __closure__
    else:
        co_flags = (co_flags | NOFREE) ^ NOFREE
    code = update_code(code,
                       freevars=co_freevars,
                       flags=co_flags,
                       names=co_names,
                       code=co_code)
    method = FunctionType(code, global_ns, closure=closure)
    method.__defaults__ = defaults
    method.__kwdefaults__ = kwdefaults
    method.__name__ = name
    method.__qualname__ = qualname
    method.__annotations__ = annotations
    method.__module__ = module
    method.__doc__ = doc
    FunctionType.__dict__['__dict__'].__set__(method, new_dict)
    for wrapper in reversed(wrappers):
        method = wrapper(method)
    return method