From 5e3641ac23837b7bb23b7e739a584adf8527ca31 Mon Sep 17 00:00:00 2001 From: Labrys of Knossos Date: Thu, 1 Dec 2022 17:34:33 -0500 Subject: [PATCH] Updated decorator to 4.4.2 --- libs/common/decorator.py | 237 ++++++++++++++++++++------------------- 1 file changed, 120 insertions(+), 117 deletions(-) diff --git a/libs/common/decorator.py b/libs/common/decorator.py index 2479b6f7..b1f8b567 100644 --- a/libs/common/decorator.py +++ b/libs/common/decorator.py @@ -1,6 +1,6 @@ # ######################### LICENSE ############################ # -# Copyright (c) 2005-2021, Michele Simionato +# Copyright (c) 2005-2018, Michele Simionato # All rights reserved. # Redistribution and use in source and binary forms, with or without @@ -28,26 +28,55 @@ # DAMAGE. """ -Decorator module, see -https://github.com/micheles/decorator/blob/master/docs/documentation.md +Decorator module, see http://pypi.python.org/pypi/decorator for the documentation. """ +from __future__ import print_function + import re import sys import inspect import operator import itertools -from contextlib import _GeneratorContextManager -from inspect import getfullargspec, iscoroutinefunction, isgeneratorfunction +import collections + +__version__ = '4.4.2' + +if sys.version_info >= (3,): + from inspect import getfullargspec + + def get_init(cls): + return cls.__init__ +else: + FullArgSpec = collections.namedtuple( + 'FullArgSpec', 'args varargs varkw defaults ' + 'kwonlyargs kwonlydefaults annotations') + + def getfullargspec(f): + "A quick and dirty replacement for getfullargspec for Python 2.X" + return FullArgSpec._make(inspect.getargspec(f) + ([], None, {})) + + def get_init(cls): + return cls.__init__.__func__ + +try: + iscoroutinefunction = inspect.iscoroutinefunction +except AttributeError: + # let's assume there are no coroutine functions in old Python + def iscoroutinefunction(f): + return False +try: + from inspect import isgeneratorfunction +except ImportError: + # assume no generator function in old Python versions + def isgeneratorfunction(caller): + return False -__version__ = '5.1.1' DEF = re.compile(r'\s*def\s*([_\w][_\w\d]*)\s*\(') -POS = inspect.Parameter.POSITIONAL_OR_KEYWORD -EMPTY = inspect.Parameter.empty -# this is not used anymore in the core, but kept for backward compatibility +# basic functionality class FunctionMaker(object): """ An object with the ability to create functions with a given signature. @@ -71,7 +100,7 @@ class FunctionMaker(object): self.name = '_lambda_' self.doc = func.__doc__ self.module = func.__module__ - if inspect.isroutine(func): + if inspect.isfunction(func): argspec = getfullargspec(func) self.annotations = getattr(func, '__annotations__', {}) for a in ('args', 'varargs', 'varkw', 'defaults', 'kwonlyargs', @@ -114,9 +143,7 @@ class FunctionMaker(object): raise TypeError('You are decorating a non function: %s' % func) def update(self, func, **kw): - """ - Update the signature of func with the data in self - """ + "Update the signature of func with the data in self" func.__name__ = self.name func.__doc__ = getattr(self, 'doc', None) func.__dict__ = getattr(self, 'dict', {}) @@ -133,9 +160,7 @@ class FunctionMaker(object): func.__dict__.update(kw) def make(self, src_templ, evaldict=None, addsource=False, **attrs): - """ - Make a new function from a given template and update the signature - """ + "Make a new function from a given template and update the signature" src = src_templ % vars(self) # expand name and signature evaldict = evaldict or {} mo = DEF.search(src) @@ -196,128 +221,106 @@ class FunctionMaker(object): return self.make(body, evaldict, addsource, **attrs) -def fix(args, kwargs, sig): +def decorate(func, caller, extras=()): """ - Fix args and kwargs to be consistent with the signature + decorate(func, caller) decorates a function using a caller. + If the caller is a generator function, the resulting function + will be a generator function. """ - ba = sig.bind(*args, **kwargs) - ba.apply_defaults() # needed for test_dan_schult - return ba.args, ba.kwargs + evaldict = dict(_call_=caller, _func_=func) + es = '' + for i, extra in enumerate(extras): + ex = '_e%d_' % i + evaldict[ex] = extra + es += ex + ', ' - -def decorate(func, caller, extras=(), kwsyntax=False): - """ - Decorates a function/generator/coroutine using a caller. - If kwsyntax is True calling the decorated functions with keyword - syntax will pass the named arguments inside the ``kw`` dictionary, - even if such argument are positional, similarly to what functools.wraps - does. By default kwsyntax is False and the the arguments are untouched. - """ - sig = inspect.signature(func) - if iscoroutinefunction(caller): - async def fun(*args, **kw): - if not kwsyntax: - args, kw = fix(args, kw, sig) - return await caller(func, *(extras + args), **kw) - elif isgeneratorfunction(caller): - def fun(*args, **kw): - if not kwsyntax: - args, kw = fix(args, kw, sig) - for res in caller(func, *(extras + args), **kw): - yield res + if '3.5' <= sys.version < '3.6': + # with Python 3.5 isgeneratorfunction returns True for all coroutines + # however we know that it is NOT possible to have a generator + # coroutine in python 3.5: PEP525 was not there yet + generatorcaller = isgeneratorfunction( + caller) and not iscoroutinefunction(caller) else: - def fun(*args, **kw): - if not kwsyntax: - args, kw = fix(args, kw, sig) - return caller(func, *(extras + args), **kw) - fun.__name__ = func.__name__ - fun.__doc__ = func.__doc__ - fun.__wrapped__ = func - fun.__signature__ = sig - fun.__qualname__ = func.__qualname__ - # builtin functions like defaultdict.__setitem__ lack many attributes - try: - fun.__defaults__ = func.__defaults__ - except AttributeError: - pass - try: - fun.__kwdefaults__ = func.__kwdefaults__ - except AttributeError: - pass - try: - fun.__annotations__ = func.__annotations__ - except AttributeError: - pass - try: - fun.__module__ = func.__module__ - except AttributeError: - pass - try: - fun.__dict__.update(func.__dict__) - except AttributeError: - pass + generatorcaller = isgeneratorfunction(caller) + if generatorcaller: + fun = FunctionMaker.create( + func, "for res in _call_(_func_, %s%%(shortsignature)s):\n" + " yield res" % es, evaldict, __wrapped__=func) + else: + fun = FunctionMaker.create( + func, "return _call_(_func_, %s%%(shortsignature)s)" % es, + evaldict, __wrapped__=func) + if hasattr(func, '__qualname__'): + fun.__qualname__ = func.__qualname__ return fun -def decoratorx(caller): - """ - A version of "decorator" implemented via "exec" and not via the - Signature object. Use this if you are want to preserve the `.__code__` - object properties (https://github.com/micheles/decorator/issues/129). - """ - def dec(func): - return FunctionMaker.create( - func, - "return _call_(_func_, %(shortsignature)s)", - dict(_call_=caller, _func_=func), - __wrapped__=func, __qualname__=func.__qualname__) - return dec - - -def decorator(caller, _func=None, kwsyntax=False): - """ - decorator(caller) converts a caller function into a decorator - """ +def decorator(caller, _func=None): + """decorator(caller) converts a caller function into a decorator""" if _func is not None: # return a decorated function # this is obsolete behavior; you should use decorate instead - return decorate(_func, caller, (), kwsyntax) + return decorate(_func, caller) # else return a decorator function - sig = inspect.signature(caller) - dec_params = [p for p in sig.parameters.values() if p.kind is POS] - - def dec(func=None, *args, **kw): - na = len(args) + 1 - extras = args + tuple(kw.get(p.name, p.default) - for p in dec_params[na:] - if p.default is not EMPTY) - if func is None: - return lambda func: decorate(func, caller, extras, kwsyntax) + defaultargs, defaults = '', () + if inspect.isclass(caller): + name = caller.__name__.lower() + doc = 'decorator(%s) converts functions/generators into ' \ + 'factories of %s objects' % (caller.__name__, caller.__name__) + elif inspect.isfunction(caller): + if caller.__name__ == '': + name = '_lambda_' else: - return decorate(func, caller, extras, kwsyntax) - dec.__signature__ = sig.replace(parameters=dec_params) - dec.__name__ = caller.__name__ - dec.__doc__ = caller.__doc__ - dec.__wrapped__ = caller - dec.__qualname__ = caller.__qualname__ - dec.__kwdefaults__ = getattr(caller, '__kwdefaults__', None) - dec.__dict__.update(caller.__dict__) + name = caller.__name__ + doc = caller.__doc__ + nargs = caller.__code__.co_argcount + ndefs = len(caller.__defaults__ or ()) + defaultargs = ', '.join(caller.__code__.co_varnames[nargs-ndefs:nargs]) + if defaultargs: + defaultargs += ',' + defaults = caller.__defaults__ + else: # assume caller is an object with a __call__ method + name = caller.__class__.__name__.lower() + doc = caller.__call__.__doc__ + evaldict = dict(_call=caller, _decorate_=decorate) + dec = FunctionMaker.create( + '%s(func, %s)' % (name, defaultargs), + 'if func is None: return lambda func: _decorate_(func, _call, (%s))\n' + 'return _decorate_(func, _call, (%s))' % (defaultargs, defaultargs), + evaldict, doc=doc, module=caller.__module__, __wrapped__=caller) + if defaults: + dec.__defaults__ = (None,) + defaults return dec # ####################### contextmanager ####################### # +try: # Python >= 3.2 + from contextlib import _GeneratorContextManager +except ImportError: # Python >= 2.5 + from contextlib import GeneratorContextManager as _GeneratorContextManager + class ContextManager(_GeneratorContextManager): - def __init__(self, g, *a, **k): - _GeneratorContextManager.__init__(self, g, a, k) - def __call__(self, func): - def caller(f, *a, **k): - with self.__class__(self.func, *self.args, **self.kwds): - return f(*a, **k) - return decorate(func, caller) + """Context manager decorator""" + return FunctionMaker.create( + func, "with _self_: return _func_(%(shortsignature)s)", + dict(_self_=self, _func_=func), __wrapped__=func) +init = getfullargspec(_GeneratorContextManager.__init__) +n_args = len(init.args) +if n_args == 2 and not init.varargs: # (self, genobj) Python 2.7 + def __init__(self, g, *a, **k): + return _GeneratorContextManager.__init__(self, g(*a, **k)) + ContextManager.__init__ = __init__ +elif n_args == 2 and init.varargs: # (self, gen, *a, **k) Python 3.4 + pass +elif n_args == 4: # (self, gen, args, kwds) Python 3.5 + def __init__(self, g, *a, **k): + return _GeneratorContextManager.__init__(self, g, a, k) + ContextManager.__init__ = __init__ + _contextmanager = decorator(ContextManager)