Update jaraco.functools-3.3.0

This commit is contained in:
JonnyWong16 2021-10-14 21:15:17 -07:00
commit b3ae6bd695
No known key found for this signature in database
GPG key ID: B1F1F9807184697A
2 changed files with 400 additions and 362 deletions

View file

@ -1 +0,0 @@
__path__ = __import__('pkgutil').extend_path(__path__, __name__)

View file

@ -1,30 +1,11 @@
from __future__ import (
absolute_import, unicode_literals, print_function, division,
)
import functools import functools
import time import time
import warnings
import inspect import inspect
import collections import collections
from itertools import count import types
import itertools
__metaclass__ = type import more_itertools
try:
from functools import lru_cache
except ImportError:
try:
from backports.functools_lru_cache import lru_cache
except ImportError:
try:
from functools32 import lru_cache
except ImportError:
warnings.warn("No lru_cache available")
import more_itertools.recipes
def compose(*funcs): def compose(*funcs):
@ -32,9 +13,8 @@ def compose(*funcs):
Compose any number of unary functions into a single unary function. Compose any number of unary functions into a single unary function.
>>> import textwrap >>> import textwrap
>>> from six import text_type >>> stripped = str.strip(textwrap.dedent(compose.__doc__))
>>> stripped = text_type.strip(textwrap.dedent(compose.__doc__)) >>> compose(str.strip, textwrap.dedent)(compose.__doc__) == stripped
>>> compose(text_type.strip, textwrap.dedent)(compose.__doc__) == stripped
True True
Compose also allows the innermost function to take arbitrary arguments. Compose also allows the innermost function to take arbitrary arguments.
@ -47,6 +27,7 @@ def compose(*funcs):
def compose_two(f1, f2): def compose_two(f1, f2):
return lambda *args, **kwargs: f1(f2(*args, **kwargs)) return lambda *args, **kwargs: f1(f2(*args, **kwargs))
return functools.reduce(compose_two, funcs) return functools.reduce(compose_two, funcs)
@ -60,9 +41,11 @@ def method_caller(method_name, *args, **kwargs):
>>> lower('MyString') >>> lower('MyString')
'mystring' 'mystring'
""" """
def call_method(target): def call_method(target):
func = getattr(target, method_name) func = getattr(target, method_name)
return func(*args, **kwargs) return func(*args, **kwargs)
return call_method return call_method
@ -97,11 +80,13 @@ def once(func):
>>> add_three(0) >>> add_three(0)
0 0
""" """
@functools.wraps(func) @functools.wraps(func)
def wrapper(*args, **kwargs): def wrapper(*args, **kwargs):
if not hasattr(wrapper, 'saved_result'): if not hasattr(wrapper, 'saved_result'):
wrapper.saved_result = func(*args, **kwargs) wrapper.saved_result = func(*args, **kwargs)
return wrapper.saved_result return wrapper.saved_result
wrapper.reset = lambda: vars(wrapper).__delitem__('saved_result') wrapper.reset = lambda: vars(wrapper).__delitem__('saved_result')
return wrapper return wrapper
@ -153,9 +138,14 @@ def method_cache(method, cache_wrapper=None):
>>> a.method.cache_clear() >>> a.method.cache_clear()
Same for a method that hasn't yet been called.
>>> c = MyClass()
>>> c.method.cache_clear()
Another cache wrapper may be supplied: Another cache wrapper may be supplied:
>>> cache = lru_cache(maxsize=2) >>> cache = functools.lru_cache(maxsize=2)
>>> MyClass.method2 = method_cache(lambda self: 3, cache_wrapper=cache) >>> MyClass.method2 = method_cache(lambda self: 3, cache_wrapper=cache)
>>> a = MyClass() >>> a = MyClass()
>>> a.method2() >>> a.method2()
@ -168,15 +158,18 @@ def method_cache(method, cache_wrapper=None):
http://code.activestate.com/recipes/577452-a-memoize-decorator-for-instance-methods/ http://code.activestate.com/recipes/577452-a-memoize-decorator-for-instance-methods/
for another implementation and additional justification. for another implementation and additional justification.
""" """
cache_wrapper = cache_wrapper or lru_cache() cache_wrapper = cache_wrapper or functools.lru_cache()
def wrapper(self, *args, **kwargs): def wrapper(self, *args, **kwargs):
# it's the first call, replace the method with a cached, bound method # it's the first call, replace the method with a cached, bound method
bound_method = functools.partial(method, self) bound_method = types.MethodType(method, self)
cached_method = cache_wrapper(bound_method) cached_method = cache_wrapper(bound_method)
setattr(self, method.__name__, cached_method) setattr(self, method.__name__, cached_method)
return cached_method(*args, **kwargs) return cached_method(*args, **kwargs)
# Support cache clear even before cache has been created.
wrapper.cache_clear = lambda: None
return _special_method_cache(method, cache_wrapper) or wrapper return _special_method_cache(method, cache_wrapper) or wrapper
@ -200,7 +193,7 @@ def _special_method_cache(method, cache_wrapper):
def proxy(self, *args, **kwargs): def proxy(self, *args, **kwargs):
if wrapper_name not in vars(self): if wrapper_name not in vars(self):
bound = functools.partial(method, self) bound = types.MethodType(method, self)
cache = cache_wrapper(bound) cache = cache_wrapper(bound)
setattr(self, wrapper_name, cache) setattr(self, wrapper_name, cache)
else: else:
@ -221,8 +214,10 @@ def apply(transform):
>>> list(get_numbers(4)) >>> list(get_numbers(4))
[6, 5, 4] [6, 5, 4]
""" """
def wrap(func): def wrap(func):
return compose(transform, func) return compose(transform, func)
return wrap return wrap
@ -239,13 +234,16 @@ def result_invoke(action):
>>> x = add_two(2, 3) >>> x = add_two(2, 3)
5 5
""" """
def wrap(func): def wrap(func):
@functools.wraps(func) @functools.wraps(func)
def wrapper(*args, **kwargs): def wrapper(*args, **kwargs):
result = func(*args, **kwargs) result = func(*args, **kwargs)
action(result) action(result)
return result return result
return wrapper return wrapper
return wrap return wrap
@ -273,6 +271,7 @@ class Throttler:
""" """
Rate-limit a function (or other callable) Rate-limit a function (or other callable)
""" """
def __init__(self, func, max_rate=float('Inf')): def __init__(self, func, max_rate=float('Inf')):
if isinstance(func, Throttler): if isinstance(func, Throttler):
func = func.func func = func.func
@ -304,9 +303,11 @@ def first_invoke(func1, func2):
any parameters (for its side-effect) and then invoke func2 any parameters (for its side-effect) and then invoke func2
with whatever parameters were passed, returning its result. with whatever parameters were passed, returning its result.
""" """
def wrapper(*args, **kwargs): def wrapper(*args, **kwargs):
func1() func1()
return func2(*args, **kwargs) return func2(*args, **kwargs)
return wrapper return wrapper
@ -317,7 +318,7 @@ def retry_call(func, cleanup=lambda: None, retries=0, trap=()):
exception. On the final attempt, allow any exceptions exception. On the final attempt, allow any exceptions
to propagate. to propagate.
""" """
attempts = count() if retries == float('inf') else range(retries) attempts = itertools.count() if retries == float('inf') else range(retries)
for attempt in attempts: for attempt in attempts:
try: try:
return func() return func()
@ -341,12 +342,15 @@ def retry(*r_args, **r_kwargs):
>>> my_func.__doc__ >>> my_func.__doc__
'this is my funk' 'this is my funk'
""" """
def decorate(func): def decorate(func):
@functools.wraps(func) @functools.wraps(func)
def wrapper(*f_args, **f_kwargs): def wrapper(*f_args, **f_kwargs):
bound = functools.partial(func, *f_args, **f_kwargs) bound = functools.partial(func, *f_args, **f_kwargs)
return retry_call(bound, *r_args, **r_kwargs) return retry_call(bound, *r_args, **r_kwargs)
return wrapper return wrapper
return decorate return decorate
@ -362,7 +366,7 @@ def print_yielded(func):
None None
""" """
print_all = functools.partial(map, print) print_all = functools.partial(map, print)
print_results = compose(more_itertools.recipes.consume, print_all, func) print_results = compose(more_itertools.consume, print_all, func)
return functools.wraps(func)(print_results) return functools.wraps(func)(print_results)
@ -375,10 +379,12 @@ def pass_none(func):
text text
>>> print_text(None) >>> print_text(None)
""" """
@functools.wraps(func) @functools.wraps(func)
def wrapper(param, *args, **kwargs): def wrapper(param, *args, **kwargs):
if param is not None: if param is not None:
return func(param, *args, **kwargs) return func(param, *args, **kwargs)
return wrapper return wrapper
@ -408,17 +414,9 @@ def assign_params(func, namespace):
>>> assign_params(Handler().meth, dict(arg='crystal', foo='clear'))() >>> assign_params(Handler().meth, dict(arg='crystal', foo='clear'))()
crystal crystal
""" """
try:
sig = inspect.signature(func) sig = inspect.signature(func)
params = sig.parameters.keys() params = sig.parameters.keys()
except AttributeError: call_ns = {k: namespace[k] for k in params if k in namespace}
spec = inspect.getargspec(func)
params = spec.args
call_ns = {
k: namespace[k]
for k in params
if k in namespace
}
return functools.partial(func, **call_ns) return functools.partial(func, **call_ns)
@ -464,4 +462,45 @@ def save_method_args(method):
attr = args_and_kwargs(args, kwargs) attr = args_and_kwargs(args, kwargs)
setattr(self, attr_name, attr) setattr(self, attr_name, attr)
return method(self, *args, **kwargs) return method(self, *args, **kwargs)
return wrapper return wrapper
def except_(*exceptions, replace=None, use=None):
"""
Replace the indicated exceptions, if raised, with the indicated
literal replacement or evaluated expression (if present).
>>> safe_int = except_(ValueError)(int)
>>> safe_int('five')
>>> safe_int('5')
5
Specify a literal replacement with ``replace``.
>>> safe_int_r = except_(ValueError, replace=0)(int)
>>> safe_int_r('five')
0
Provide an expression to ``use`` to pass through particular parameters.
>>> safe_int_pt = except_(ValueError, use='args[0]')(int)
>>> safe_int_pt('five')
'five'
"""
def decorate(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
try:
return func(*args, **kwargs)
except exceptions:
try:
return eval(use)
except TypeError:
return replace
return wrapper
return decorate