decorator module by michele simionato michelesimionatolibero it michele simionato distributed under the terms of the bsd license see below http www phyast pitt edumichelespythondocumentation html included in nltk for its support of a nice memoization decorator the basic trick is to generate the source code for the decorated function with the right signature and to evaluate it uncomment the statement print sys stderr funcsrc in decorator to understand what is going on hack to keep nltk s tokenize module from colliding with the tokenize in the python standard library for retrocompatibility reasons we don t use a standard signature instead we use the string generated by this method basically from a signature we create a string and remove the default values returns an info dictionary containing name the name of the function str argnames the names of the arguments list defaults the values of the default arguments tuple signature the signature str fullsignature the full signature signature doc the docstring str module the module name str dict the function dict str def fself x1 y2 args kw pass info getinfof infoname f infoargnames self x y args kw infodefaults 1 2 infosignature self x y args kw infofullsignature signature self x1 y2 args kw convert signature to str pypy compatibility an improvement over functools updatewrapper the wrapper is a generic callable object it works by generating a copy of the wrapper with the right signature and by updating the copy not the original moreovoer model can be a dictionary with keys name doc module dict defaults helper used in decoratorfactory take a class with a caller method and return a callable decorator object it works by adding a suitable call method to the class it raises a typeerror if the class already has a nontrivial call method general purpose decorator factory takes a caller function as input and returns a decorator with the same attributes a caller function is any function like this def callerfunc args kw do something return funcargs kw here is an example of usage decorator def chattyf args kw printcalling r f name return fargs kw chatty name chatty chatty def f pass f calling f decorator can also take in input a class with a caller method in this case it converts the class into a factory of callable decorator objects see the documentation for an example import sys print sys stderr src for debugging purposes memoizedic is created at the first call legalese redistributions of source code must retain the above notice this list of conditions and the following disclaimer redistributions in bytecode form must reproduce the above notice this list of conditions and the following disclaimer in the documentation andor other materials provided with the distribution this software is provided by the holders and contributors as is and any express or implied warranties including but not limited to the implied warranties of merchantability and fitness for a particular purpose are disclaimed in no event shall the holders or contributors be liable for any direct indirect incidental special exemplary or consequential damages including but not limited to procurement of substitute goods or services loss of use data or profits or business interruption however caused and on any theory of liability whether in contract strict liability or tort including negligence or otherwise arising in any way out of the use of this software even if advised of the possibility of such damage the basic trick is to generate the source code for the decorated function with the right signature and to evaluate it uncomment the statement print sys stderr func_src in _decorator to understand what is going on hack to keep nltk s tokenize module from colliding with the tokenize in the python standard library for retrocompatibility reasons we don t use a standard signature instead we use the string generated by this method basically from a signature we create a string and remove the default values returns an info dictionary containing name the name of the function str argnames the names of the arguments list defaults the values of the default arguments tuple signature the signature str fullsignature the full signature signature doc the docstring str module the module name str dict the function __dict__ str def f self x 1 y 2 args kw pass info getinfo f info name f info argnames self x y args kw info defaults 1 2 info signature self x y args kw info fullsignature signature self x 1 y 2 args kw convert signature to str pypy compatibility an improvement over functools update_wrapper the wrapper is a generic callable object it works by generating a copy of the wrapper with the right signature and by updating the copy not the original moreovoer model can be a dictionary with keys name doc module dict defaults assume model is a function helper used in decorator_factory take a class with a caller method and return a callable decorator object it works by adding a suitable __call__ method to the class it raises a typeerror if the class already has a nontrivial __call__ method general purpose decorator factory takes a caller function as input and returns a decorator with the same attributes a caller function is any function like this def caller func args kw do something return func args kw here is an example of usage decorator def chatty f args kw print calling r f __name__ return f args kw chatty __name__ chatty chatty def f pass f calling f decorator can also take in input a class with a caller method in this case it converts the class into a factory of callable decorator objects see the documentation for an example the real meat is here import sys print sys stderr src for debugging purposes memoize_dic is created at the first call legalese redistributions of source code must retain the above notice this list of conditions and the following disclaimer redistributions in bytecode form must reproduce the above notice this list of conditions and the following disclaimer in the documentation and or other materials provided with the distribution this software is provided by the holders and contributors as is and any express or implied warranties including but not limited to the implied warranties of merchantability and fitness for a particular purpose are disclaimed in no event shall the holders or contributors be liable for any direct indirect incidental special exemplary or consequential damages including but not limited to procurement of substitute goods or services loss of use data or profits or business interruption however caused and on any theory of liability whether in contract strict liability or tort including negligence or otherwise arising in any way out of the use of this software even if advised of the possibility of such damage
__docformat__ = "restructuredtext en"






__all__ = ["decorator", "new_wrapper", "getinfo"]

import sys



OLD_SYS_PATH = sys.path[:]
sys.path = [p for p in sys.path if p and "nltk" not in str(p)]
import inspect

sys.path = OLD_SYS_PATH


def __legacysignature(signature):
    
    listsignature = str(signature)[1:-1].split(",")
    for counter, param in enumerate(listsignature):
        if param.count("=") > 0:
            listsignature[counter] = param[0 : param.index("=")].strip()
        else:
            listsignature[counter] = param.strip()
    return ", ".join(listsignature)


def getinfo(func):
    
    assert inspect.ismethod(func) or inspect.isfunction(func)
    argspec = inspect.getfullargspec(func)
    regargs, varargs, varkwargs = argspec[:3]
    argnames = list(regargs)
    if varargs:
        argnames.append(varargs)
    if varkwargs:
        argnames.append(varkwargs)
    fullsignature = inspect.signature(func)
    
    signature = __legacysignature(fullsignature)

    
    if hasattr(func, "__closure__"):
        _closure = func.__closure__
        _globals = func.__globals__
    else:
        _closure = func.func_closure
        _globals = func.func_globals

    return dict(
        name=func.__name__,
        argnames=argnames,
        signature=signature,
        fullsignature=fullsignature,
        defaults=func.__defaults__,
        doc=func.__doc__,
        module=func.__module__,
        dict=func.__dict__,
        globals=_globals,
        closure=_closure,
    )


def update_wrapper(wrapper, model, infodict=None):
    "akin to functools.update_wrapper"
    infodict = infodict or getinfo(model)
    wrapper.__name__ = infodict["name"]
    wrapper.__doc__ = infodict["doc"]
    wrapper.__module__ = infodict["module"]
    wrapper.__dict__.update(infodict["dict"])
    wrapper.__defaults__ = infodict["defaults"]
    wrapper.undecorated = model
    return wrapper


def new_wrapper(wrapper, model):
    
    if isinstance(model, dict):
        infodict = model
    else:  
        infodict = getinfo(model)
    assert (
        not "_wrapper_" in infodict["argnames"]
    ), '"_wrapper_" is a reserved argument name!'
    src = "lambda %(signature)s: _wrapper_(%(signature)s)" % infodict
    funcopy = eval(src, dict(_wrapper_=wrapper))
    return update_wrapper(funcopy, model, infodict)



def __call__(self, func):
    return new_wrapper(lambda *a, **k: self.call(func, *a, **k), func)


def decorator_factory(cls):
    
    attrs = set(dir(cls))
    if "__call__" in attrs:
        raise TypeError(
            "You cannot decorate a class with a nontrivial " "__call__ method"
        )
    if "call" not in attrs:
        raise TypeError("You cannot decorate a class without a " ".call method")
    cls.__call__ = __call__
    return cls


def decorator(caller):
    
    if inspect.isclass(caller):
        return decorator_factory(caller)

    def _decorator(func):  
        infodict = getinfo(func)
        argnames = infodict["argnames"]
        assert not (
            "_call_" in argnames or "_func_" in argnames
        ), "You cannot use _call_ or _func_ as argument names!"
        src = "lambda %(signature)s: _call_(_func_, %(signature)s)" % infodict
        
        dec_func = eval(src, dict(_func_=func, _call_=caller))
        return update_wrapper(dec_func, func, infodict)

    return update_wrapper(_decorator, caller)


def getattr_(obj, name, default_thunk):
    "Similar to .setdefault in dictionaries."
    try:
        return getattr(obj, name)
    except AttributeError:
        default = default_thunk()
        setattr(obj, name, default)
        return default


@decorator
def memoize(func, *args):
    dic = getattr_(func, "memoize_dic", dict)
    
    if args in dic:
        return dic[args]
    result = func(*args)
    dic[args] = result
    return result