Source code for snakeoil.currying

"""
Function currying, generating a functor with a set of args/defaults pre bound.

:py:func:`pre_curry` and :py:func:`post_curry` return "normal" python functions
The difference between :py:func:`pre_curry` and :py:func:`functools.partial`
is this

>>> from functools import partial
>>> from snakeoil.currying import pre_curry
>>> def func(arg=None, self=None):
...   return arg, self
>>> curry = pre_curry(func, True)
>>> part = partial(func, True)
>>> class Test:
...   curry = pre_curry(func, True)
...   part = partial(func, True)
...   def __repr__(self):
...      return '<Test object>'
>>> curry()
(True, None)
>>> Test().curry()
(True, <Test object>)
>>> part()
(True, None)
>>> Test().part()
(True, None)

If your curried function is not used as a class attribute the results should be
identical. Because :py:func:`functools.partial` has an implementation in C
while :py:func:`pre_curry` is python you should use :py:func:`functools.partial`
if possible.
"""

import sys
from functools import partial

from .compatibility import IGNORED_EXCEPTIONS

__all__ = ("pre_curry", "post_curry", "pretty_docs")


[docs] def pre_curry(func, *args, **kwargs): """passed in args are prefixed, with further args appended Unlike partial, this is usable as an instancemethod. """ if not kwargs: def callit(*moreargs, **morekwargs): return func(*(args + moreargs), **morekwargs) elif not args: def callit(*moreargs, **morekwargs): kw = kwargs.copy() kw.update(morekwargs) return func(*moreargs, **kw) else: def callit(*moreargs, **morekwargs): kw = kwargs.copy() kw.update(morekwargs) return func(*(args + moreargs), **kw) callit.func = func return callit
[docs] def post_curry(func, *args, **kwargs): """passed in args are appended to any further args supplied""" if not kwargs: def callit(*moreargs, **morekwargs): return func(*(moreargs + args), **morekwargs) elif not args: def callit(*moreargs, **morekwargs): kw = morekwargs.copy() kw.update(kwargs) return func(*moreargs, **kw) else: def callit(*moreargs, **morekwargs): kw = morekwargs.copy() kw.update(kwargs) return func(*(moreargs + args), **kw) callit.func = func return callit
[docs] def pretty_docs(wrapped, extradocs=None, name=None): """ Modify wrapped, so that it 'looks' like what it's wrapping. This is primarily useful for introspection reasons- doc generators, direct user interaction with an object in the interpretter, etc. :param wrapped: functor to modify :param extradocs: ``__doc__`` override for wrapped; else it pulls from wrapped's target functor :param name: ``__name__`` override for wrapped; else it pulls from wrapped's target functor for the name. """ wrapped.__module__ = wrapped.func.__module__ doc = wrapped.func.__doc__ if extradocs is None: wrapped.__doc__ = doc else: wrapped.__doc__ = extradocs if name: wrapped.__name__ = name return wrapped
def wrap_exception(recast_exception, *args, **kwds): # set this here so that 2to3 will rewrite it. try: if not issubclass(recast_exception, Exception): raise ValueError( "recast_exception must be an %s derivative: got %r" % (Exception, recast_exception) ) except TypeError as e: raise TypeError( "recast_exception must be an %s derivative; got %r, failed %r", (Exception.__name__, recast_exception, e), ) ignores = kwds.pop("ignores", (recast_exception,)) pass_error = kwds.pop("pass_error", None) return wrap_exception_complex( partial(_simple_throw, recast_exception, args, kwds, pass_error), ignores ) def _simple_throw( recast_exception, recast_args, recast_kwds, pass_error, exception, functor, args, kwds, ): if pass_error: recast_kwds[pass_error] = exception return recast_exception(*recast_args, **recast_kwds) def wrap_exception_complex(creation_func, ignores): try: if ( not hasattr(ignores, "__iter__") and issubclass(ignores, Exception) or ignores is Exception ): ignores = (ignores,) ignores = tuple(ignores) except TypeError as e: raise TypeError( "ignores must be either a tuple of %s, or a %s: got %r, error %r" % (Exception.__name__, Exception.__name__, ignores, e) ) if not all(issubclass(x, Exception) for x in ignores): raise TypeError( "ignores has a non %s derivative in it: %r" % (Exception.__name__, ignores) ) return partial(_inner_wrap_exception, creation_func, ignores) def _inner_wrap_exception(exception_maker, ignores, functor): def _wrap_exception(*args, **kwargs): try: return functor(*args, **kwargs) except IGNORED_EXCEPTIONS: raise except ignores: raise except Exception as e: raise exception_maker(e, functor, args, kwargs) from e _wrap_exception.func = functor return pretty_docs(_wrap_exception, name=functor.__name__)