diff options
Diffstat (limited to 'Lib/functools.py')
| -rw-r--r-- | Lib/functools.py | 159 | 
1 files changed, 152 insertions, 7 deletions
diff --git a/Lib/functools.py b/Lib/functools.py index 053e44e3e8..9403e8e843 100644 --- a/Lib/functools.py +++ b/Lib/functools.py @@ -3,16 +3,24 @@  # Python module wrapper for _functools C module  # to allow utilities written in Python to be added  # to the functools module. -# Written by Nick Coghlan <ncoghlan at gmail.com> -# and Raymond Hettinger <python at rcn.com> -#   Copyright (C) 2006-2010 Python Software Foundation. +# Written by Nick Coghlan <ncoghlan at gmail.com>, +# Raymond Hettinger <python at rcn.com>, +# and Ćukasz Langa <lukasz at langa.pl>. +#   Copyright (C) 2006-2013 Python Software Foundation.  # See C source code for _functools credits/copyright  __all__ = ['update_wrapper', 'wraps', 'WRAPPER_ASSIGNMENTS', 'WRAPPER_UPDATES', -           'total_ordering', 'cmp_to_key', 'lru_cache', 'reduce', 'partial'] +           'total_ordering', 'cmp_to_key', 'lru_cache', 'reduce', 'partial', +           'singledispatch'] -from _functools import partial, reduce +try: +    from _functools import reduce +except ModuleNotFoundError: +    pass +from abc import get_cache_token  from collections import namedtuple +from types import MappingProxyType +from weakref import WeakKeyDictionary  try:      from _thread import RLock  except: @@ -135,7 +143,30 @@ def cmp_to_key(mycmp):  try:      from _functools import cmp_to_key -except ImportError: +except ModuleNotFoundError: +    pass + + +################################################################################ +### partial() argument application +################################################################################ + +def partial(func, *args, **keywords): +    """new function with partial application of the given arguments +    and keywords. +    """ +    def newfunc(*fargs, **fkeywords): +        newkeywords = keywords.copy() +        newkeywords.update(fkeywords) +        return func(*(args + fargs), **newkeywords) +    newfunc.func = func +    newfunc.args = args +    newfunc.keywords = keywords +    return newfunc + +try: +    from _functools import partial +except ModuleNotFoundError:      pass @@ -220,7 +251,6 @@ def lru_cache(maxsize=128, typed=False):      PREV, NEXT, KEY, RESULT = 0, 1, 2, 3   # names for the link fields      def decorating_function(user_function): -          cache = {}          hits = misses = 0          full = False @@ -329,3 +359,118 @@ def lru_cache(maxsize=128, typed=False):          return update_wrapper(wrapper, user_function)      return decorating_function + + +################################################################################ +### singledispatch() - single-dispatch generic function decorator +################################################################################ + +def _compose_mro(cls, haystack): +    """Calculates the MRO for a given class `cls`, including relevant abstract +    base classes from `haystack`. + +    """ +    bases = set(cls.__mro__) +    mro = list(cls.__mro__) +    for needle in haystack: +        if (needle in bases or not hasattr(needle, '__mro__') +                            or not issubclass(cls, needle)): +            continue   # either present in the __mro__ already or unrelated +        for index, base in enumerate(mro): +            if not issubclass(base, needle): +                break +        if base in bases and not issubclass(needle, base): +            # Conflict resolution: put classes present in __mro__ and their +            # subclasses first. See test_mro_conflicts() in test_functools.py +            # for examples. +            index += 1 +        mro.insert(index, needle) +    return mro + +def _find_impl(cls, registry): +    """Returns the best matching implementation for the given class `cls` in +    `registry`. Where there is no registered implementation for a specific +    type, its method resolution order is used to find a more generic +    implementation. + +    Note: if `registry` does not contain an implementation for the base +    `object` type, this function may return None. + +    """ +    mro = _compose_mro(cls, registry.keys()) +    match = None +    for t in mro: +        if match is not None: +            # If `match` is an ABC but there is another unrelated, equally +            # matching ABC. Refuse the temptation to guess. +            if (t in registry and not issubclass(match, t) +                              and match not in cls.__mro__): +                raise RuntimeError("Ambiguous dispatch: {} or {}".format( +                    match, t)) +            break +        if t in registry: +            match = t +    return registry.get(match) + +def singledispatch(func): +    """Single-dispatch generic function decorator. + +    Transforms a function into a generic function, which can have different +    behaviours depending upon the type of its first argument. The decorated +    function acts as the default implementation, and additional +    implementations can be registered using the 'register()' attribute of +    the generic function. + +    """ +    registry = {} +    dispatch_cache = WeakKeyDictionary() +    cache_token = None + +    def dispatch(typ): +        """generic_func.dispatch(type) -> <function implementation> + +        Runs the dispatch algorithm to return the best available implementation +        for the given `type` registered on `generic_func`. + +        """ +        nonlocal cache_token +        if cache_token is not None: +            current_token = get_cache_token() +            if cache_token != current_token: +                dispatch_cache.clear() +                cache_token = current_token +        try: +            impl = dispatch_cache[typ] +        except KeyError: +            try: +                impl = registry[typ] +            except KeyError: +                impl = _find_impl(typ, registry) +            dispatch_cache[typ] = impl +        return impl + +    def register(typ, func=None): +        """generic_func.register(type, func) -> func + +        Registers a new implementation for the given `type` on a `generic_func`. + +        """ +        nonlocal cache_token +        if func is None: +            return lambda f: register(typ, f) +        registry[typ] = func +        if cache_token is None and hasattr(typ, '__abstractmethods__'): +            cache_token = get_cache_token() +        dispatch_cache.clear() +        return func + +    def wrapper(*args, **kw): +        return dispatch(args[0].__class__)(*args, **kw) + +    registry[object] = func +    wrapper.register = register +    wrapper.dispatch = dispatch +    wrapper.registry = MappingProxyType(registry) +    wrapper._clear_cache = dispatch_cache.clear +    update_wrapper(wrapper, func) +    return wrapper  | 
