Mercurial > repos > shellac > guppy_basecaller
diff env/lib/python3.7/site-packages/boltons/funcutils.py @ 5:9b1c78e6ba9c draft default tip
"planemo upload commit 6c0a8142489327ece472c84e558c47da711a9142"
author | shellac |
---|---|
date | Mon, 01 Jun 2020 08:59:25 -0400 |
parents | 79f47841a781 |
children |
line wrap: on
line diff
--- a/env/lib/python3.7/site-packages/boltons/funcutils.py Thu May 14 16:47:39 2020 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1069 +0,0 @@ -# -*- coding: utf-8 -*- -"""Python's built-in :mod:`functools` module builds several useful -utilities on top of Python's first-class function -support. ``funcutils`` generally stays in the same vein, adding to and -correcting Python's standard metaprogramming facilities. -""" -from __future__ import print_function - -import sys -import re -import inspect -import functools -import itertools -from types import MethodType, FunctionType - -try: - xrange - make_method = MethodType -except NameError: - # Python 3 - make_method = lambda desc, obj, obj_type: MethodType(desc, obj) - basestring = (str, bytes) # Python 3 compat - _IS_PY2 = False -else: - _IS_PY2 = True - - -try: - _inspect_iscoroutinefunction = inspect.iscoroutinefunction -except AttributeError: - # Python 3.4 - _inspect_iscoroutinefunction = lambda func: False - - -try: - from boltons.typeutils import make_sentinel - NO_DEFAULT = make_sentinel(var_name='NO_DEFAULT') -except ImportError: - NO_DEFAULT = object() - -try: - from functools import partialmethod -except ImportError: - partialmethod = None - - -_IS_PY35 = sys.version_info >= (3, 5) -if not _IS_PY35: - # py35+ wants you to use signature instead, but - # inspect_formatargspec is way simpler for what it is. Copied the - # vendoring approach from alembic: - # https://github.com/sqlalchemy/alembic/blob/4cdad6aec32b4b5573a2009cc356cb4b144bd359/alembic/util/compat.py#L92 - from inspect import formatargspec as inspect_formatargspec -else: - from inspect import formatannotation - - def inspect_formatargspec( - args, varargs=None, varkw=None, defaults=None, - kwonlyargs=(), kwonlydefaults={}, annotations={}, - formatarg=str, - formatvarargs=lambda name: '*' + name, - formatvarkw=lambda name: '**' + name, - formatvalue=lambda value: '=' + repr(value), - formatreturns=lambda text: ' -> ' + text, - formatannotation=formatannotation): - """Copy formatargspec from python 3.7 standard library. - Python 3 has deprecated formatargspec and requested that Signature - be used instead, however this requires a full reimplementation - of formatargspec() in terms of creating Parameter objects and such. - Instead of introducing all the object-creation overhead and having - to reinvent from scratch, just copy their compatibility routine. - """ - - def formatargandannotation(arg): - result = formatarg(arg) - if arg in annotations: - result += ': ' + formatannotation(annotations[arg]) - return result - specs = [] - if defaults: - firstdefault = len(args) - len(defaults) - for i, arg in enumerate(args): - spec = formatargandannotation(arg) - if defaults and i >= firstdefault: - spec = spec + formatvalue(defaults[i - firstdefault]) - specs.append(spec) - if varargs is not None: - specs.append(formatvarargs(formatargandannotation(varargs))) - else: - if kwonlyargs: - specs.append('*') - if kwonlyargs: - for kwonlyarg in kwonlyargs: - spec = formatargandannotation(kwonlyarg) - if kwonlydefaults and kwonlyarg in kwonlydefaults: - spec += formatvalue(kwonlydefaults[kwonlyarg]) - specs.append(spec) - if varkw is not None: - specs.append(formatvarkw(formatargandannotation(varkw))) - result = '(' + ', '.join(specs) + ')' - if 'return' in annotations: - result += formatreturns(formatannotation(annotations['return'])) - return result - - -def get_module_callables(mod, ignore=None): - """Returns two maps of (*types*, *funcs*) from *mod*, optionally - ignoring based on the :class:`bool` return value of the *ignore* - callable. *mod* can be a string name of a module in - :data:`sys.modules` or the module instance itself. - """ - if isinstance(mod, basestring): - mod = sys.modules[mod] - types, funcs = {}, {} - for attr_name in dir(mod): - if ignore and ignore(attr_name): - continue - try: - attr = getattr(mod, attr_name) - except Exception: - continue - try: - attr_mod_name = attr.__module__ - except AttributeError: - continue - if attr_mod_name != mod.__name__: - continue - if isinstance(attr, type): - types[attr_name] = attr - elif callable(attr): - funcs[attr_name] = attr - return types, funcs - - -def mro_items(type_obj): - """Takes a type and returns an iterator over all class variables - throughout the type hierarchy (respecting the MRO). - - >>> sorted(set([k for k, v in mro_items(int) if not k.startswith('__') and 'bytes' not in k and not callable(v)])) - ['denominator', 'imag', 'numerator', 'real'] - """ - # TODO: handle slots? - return itertools.chain.from_iterable(ct.__dict__.items() - for ct in type_obj.__mro__) - - -def dir_dict(obj, raise_exc=False): - """Return a dictionary of attribute names to values for a given - object. Unlike ``obj.__dict__``, this function returns all - attributes on the object, including ones on parent classes. - """ - # TODO: separate function for handling descriptors on types? - ret = {} - for k in dir(obj): - try: - ret[k] = getattr(obj, k) - except Exception: - if raise_exc: - raise - return ret - - -def copy_function(orig, copy_dict=True): - """Returns a shallow copy of the function, including code object, - globals, closure, etc. - - >>> func = lambda: func - >>> func() is func - True - >>> func_copy = copy_function(func) - >>> func_copy() is func - True - >>> func_copy is not func - True - - Args: - orig (function): The function to be copied. Must be a - function, not just any method or callable. - copy_dict (bool): Also copy any attributes set on the function - instance. Defaults to ``True``. - """ - ret = FunctionType(orig.__code__, - orig.__globals__, - name=orig.__name__, - argdefs=getattr(orig, "__defaults__", None), - closure=getattr(orig, "__closure__", None)) - if copy_dict: - ret.__dict__.update(orig.__dict__) - return ret - - -def partial_ordering(cls): - """Class decorator, similar to :func:`functools.total_ordering`, - except it is used to define `partial orderings`_ (i.e., it is - possible that *x* is neither greater than, equal to, or less than - *y*). It assumes the presence of the ``__le__()`` and ``__ge__()`` - method, but nothing else. It will not override any existing - additional comparison methods. - - .. _partial orderings: https://en.wikipedia.org/wiki/Partially_ordered_set - - >>> @partial_ordering - ... class MySet(set): - ... def __le__(self, other): - ... return self.issubset(other) - ... def __ge__(self, other): - ... return self.issuperset(other) - ... - >>> a = MySet([1,2,3]) - >>> b = MySet([1,2]) - >>> c = MySet([1,2,4]) - >>> b < a - True - >>> b > a - False - >>> b < c - True - >>> a < c - False - >>> c > a - False - """ - def __lt__(self, other): return self <= other and not self >= other - def __gt__(self, other): return self >= other and not self <= other - def __eq__(self, other): return self >= other and self <= other - - if not hasattr(cls, '__lt__'): cls.__lt__ = __lt__ - if not hasattr(cls, '__gt__'): cls.__gt__ = __gt__ - if not hasattr(cls, '__eq__'): cls.__eq__ = __eq__ - - return cls - - -class InstancePartial(functools.partial): - """:class:`functools.partial` is a huge convenience for anyone - working with Python's great first-class functions. It allows - developers to curry arguments and incrementally create simpler - callables for a variety of use cases. - - Unfortunately there's one big gap in its usefulness: - methods. Partials just don't get bound as methods and - automatically handed a reference to ``self``. The - ``InstancePartial`` type remedies this by inheriting from - :class:`functools.partial` and implementing the necessary - descriptor protocol. There are no other differences in - implementation or usage. :class:`CachedInstancePartial`, below, - has the same ability, but is slightly more efficient. - - """ - if partialmethod is not None: # NB: See https://github.com/mahmoud/boltons/pull/244 - @property - def _partialmethod(self): - return partialmethod(self.func, *self.args, **self.keywords) - - def __get__(self, obj, obj_type): - return make_method(self, obj, obj_type) - - - -class CachedInstancePartial(functools.partial): - """The ``CachedInstancePartial`` is virtually the same as - :class:`InstancePartial`, adding support for method-usage to - :class:`functools.partial`, except that upon first access, it - caches the bound method on the associated object, speeding it up - for future accesses, and bringing the method call overhead to - about the same as non-``partial`` methods. - - See the :class:`InstancePartial` docstring for more details. - """ - if partialmethod is not None: # NB: See https://github.com/mahmoud/boltons/pull/244 - @property - def _partialmethod(self): - return partialmethod(self.func, *self.args, **self.keywords) - - def __get__(self, obj, obj_type): - # These assignments could've been in __init__, but there was - # no simple way to do it without breaking one of PyPy or Py3. - self.__name__ = None - self.__doc__ = self.func.__doc__ - self.__module__ = self.func.__module__ - - name = self.__name__ - if name is None: - for k, v in mro_items(obj_type): - if v is self: - self.__name__ = name = k - if obj is None: - return make_method(self, obj, obj_type) - try: - # since this is a data descriptor, this block - # is probably only hit once (per object) - return obj.__dict__[name] - except KeyError: - obj.__dict__[name] = ret = make_method(self, obj, obj_type) - return ret - - -partial = CachedInstancePartial - - -def format_invocation(name='', args=(), kwargs=None): - """Given a name, positional arguments, and keyword arguments, format - a basic Python-style function call. - - >>> print(format_invocation('func', args=(1, 2), kwargs={'c': 3})) - func(1, 2, c=3) - >>> print(format_invocation('a_func', args=(1,))) - a_func(1) - >>> print(format_invocation('kw_func', kwargs=[('a', 1), ('b', 2)])) - kw_func(a=1, b=2) - - """ - kwargs = kwargs or {} - a_text = ', '.join([repr(a) for a in args]) - if isinstance(kwargs, dict): - kwarg_items = [(k, kwargs[k]) for k in sorted(kwargs)] - else: - kwarg_items = kwargs - kw_text = ', '.join(['%s=%r' % (k, v) for k, v in kwarg_items]) - - all_args_text = a_text - if all_args_text and kw_text: - all_args_text += ', ' - all_args_text += kw_text - - return '%s(%s)' % (name, all_args_text) - - -def format_exp_repr(obj, pos_names, req_names=None, opt_names=None, opt_key=None): - """Render an expression-style repr of an object, based on attribute - names, which are assumed to line up with arguments to an initializer. - - >>> class Flag(object): - ... def __init__(self, length, width, depth=None): - ... self.length = length - ... self.width = width - ... self.depth = depth - ... - - That's our Flag object, here are some example reprs for it: - - >>> flag = Flag(5, 10) - >>> print(format_exp_repr(flag, ['length', 'width'], [], ['depth'])) - Flag(5, 10) - >>> flag2 = Flag(5, 15, 2) - >>> print(format_exp_repr(flag2, ['length'], ['width', 'depth'])) - Flag(5, width=15, depth=2) - - By picking the pos_names, req_names, opt_names, and opt_key, you - can fine-tune how you want the repr to look. - - Args: - obj (object): The object whose type name will be used and - attributes will be checked - pos_names (list): Required list of attribute names which will be - rendered as positional arguments in the output repr. - req_names (list): List of attribute names which will always - appear in the keyword arguments in the output repr. Defaults to None. - opt_names (list): List of attribute names which may appear in - the keyword arguments in the output repr, provided they pass - the *opt_key* check. Defaults to None. - opt_key (callable): A function or callable which checks whether - an opt_name should be in the repr. Defaults to a - ``None``-check. - - """ - cn = obj.__class__.__name__ - req_names = req_names or [] - opt_names = opt_names or [] - uniq_names, all_names = set(), [] - for name in req_names + opt_names: - if name in uniq_names: - continue - uniq_names.add(name) - all_names.append(name) - - if opt_key is None: - opt_key = lambda v: v is None - assert callable(opt_key) - - args = [getattr(obj, name, None) for name in pos_names] - - kw_items = [(name, getattr(obj, name, None)) for name in all_names] - kw_items = [(name, val) for name, val in kw_items - if not (name in opt_names and opt_key(val))] - - return format_invocation(cn, args, kw_items) - - -def format_nonexp_repr(obj, req_names=None, opt_names=None, opt_key=None): - """Format a non-expression-style repr - - Some object reprs look like object instantiation, e.g., App(r=[], mw=[]). - - This makes sense for smaller, lower-level objects whose state - roundtrips. But a lot of objects contain values that don't - roundtrip, like types and functions. - - For those objects, there is the non-expression style repr, which - mimic's Python's default style to make a repr like so: - - >>> class Flag(object): - ... def __init__(self, length, width, depth=None): - ... self.length = length - ... self.width = width - ... self.depth = depth - ... - >>> flag = Flag(5, 10) - >>> print(format_nonexp_repr(flag, ['length', 'width'], ['depth'])) - <Flag length=5 width=10> - - If no attributes are specified or set, utilizes the id, not unlike Python's - built-in behavior. - - >>> print(format_nonexp_repr(flag)) - <Flag id=...> - """ - cn = obj.__class__.__name__ - req_names = req_names or [] - opt_names = opt_names or [] - uniq_names, all_names = set(), [] - for name in req_names + opt_names: - if name in uniq_names: - continue - uniq_names.add(name) - all_names.append(name) - - if opt_key is None: - opt_key = lambda v: v is None - assert callable(opt_key) - - items = [(name, getattr(obj, name, None)) for name in all_names] - labels = ['%s=%r' % (name, val) for name, val in items - if not (name in opt_names and opt_key(val))] - if not labels: - labels = ['id=%s' % id(obj)] - ret = '<%s %s>' % (cn, ' '.join(labels)) - return ret - - - -# # # -# # # Function builder -# # # - - -def wraps(func, injected=None, expected=None, **kw): - """Decorator factory to apply update_wrapper() to a wrapper function. - - Modeled after built-in :func:`functools.wraps`. Returns a decorator - that invokes update_wrapper() with the decorated function as the wrapper - argument and the arguments to wraps() as the remaining arguments. - Default arguments are as for update_wrapper(). This is a convenience - function to simplify applying partial() to update_wrapper(). - - Same example as in update_wrapper's doc but with wraps: - - >>> from boltons.funcutils import wraps - >>> - >>> def print_return(func): - ... @wraps(func) - ... def wrapper(*args, **kwargs): - ... ret = func(*args, **kwargs) - ... print(ret) - ... return ret - ... return wrapper - ... - >>> @print_return - ... def example(): - ... '''docstring''' - ... return 'example return value' - >>> - >>> val = example() - example return value - >>> example.__name__ - 'example' - >>> example.__doc__ - 'docstring' - """ - return partial(update_wrapper, func=func, build_from=None, - injected=injected, expected=expected, **kw) - - -def update_wrapper(wrapper, func, injected=None, expected=None, build_from=None, **kw): - """Modeled after the built-in :func:`functools.update_wrapper`, - this function is used to make your wrapper function reflect the - wrapped function's: - - * Name - * Documentation - * Module - * Signature - - The built-in :func:`functools.update_wrapper` copies the first three, but - does not copy the signature. This version of ``update_wrapper`` can copy - the inner function's signature exactly, allowing seamless usage - and :mod:`introspection <inspect>`. Usage is identical to the - built-in version:: - - >>> from boltons.funcutils import update_wrapper - >>> - >>> def print_return(func): - ... def wrapper(*args, **kwargs): - ... ret = func(*args, **kwargs) - ... print(ret) - ... return ret - ... return update_wrapper(wrapper, func) - ... - >>> @print_return - ... def example(): - ... '''docstring''' - ... return 'example return value' - >>> - >>> val = example() - example return value - >>> example.__name__ - 'example' - >>> example.__doc__ - 'docstring' - - In addition, the boltons version of update_wrapper supports - modifying the outer signature. By passing a list of - *injected* argument names, those arguments will be removed from - the outer wrapper's signature, allowing your decorator to provide - arguments that aren't passed in. - - Args: - - wrapper (function) : The callable to which the attributes of - *func* are to be copied. - func (function): The callable whose attributes are to be copied. - injected (list): An optional list of argument names which - should not appear in the new wrapper's signature. - expected (list): An optional list of argument names (or (name, - default) pairs) representing new arguments introduced by - the wrapper (the opposite of *injected*). See - :meth:`FunctionBuilder.add_arg()` for more details. - build_from (function): The callable from which the new wrapper - is built. Defaults to *func*, unless *wrapper* is partial object - built from *func*, in which case it defaults to *wrapper*. - Useful in some specific cases where *wrapper* and *func* have the - same arguments but differ on which are keyword-only and positional-only. - update_dict (bool): Whether to copy other, non-standard - attributes of *func* over to the wrapper. Defaults to True. - inject_to_varkw (bool): Ignore missing arguments when a - ``**kwargs``-type catch-all is present. Defaults to True. - hide_wrapped (bool): Remove reference to the wrapped function(s) - in the updated function. - - In opposition to the built-in :func:`functools.update_wrapper` bolton's - version returns a copy of the function and does not modifiy anything in place. - For more in-depth wrapping of functions, see the - :class:`FunctionBuilder` type, on which update_wrapper was built. - """ - if injected is None: - injected = [] - elif isinstance(injected, basestring): - injected = [injected] - else: - injected = list(injected) - - expected_items = _parse_wraps_expected(expected) - - if isinstance(func, (classmethod, staticmethod)): - raise TypeError('wraps does not support wrapping classmethods and' - ' staticmethods, change the order of wrapping to' - ' wrap the underlying function: %r' - % (getattr(func, '__func__', None),)) - - update_dict = kw.pop('update_dict', True) - inject_to_varkw = kw.pop('inject_to_varkw', True) - hide_wrapped = kw.pop('hide_wrapped', False) - if kw: - raise TypeError('unexpected kwargs: %r' % kw.keys()) - - if isinstance(wrapper, functools.partial) and func is wrapper.func: - build_from = build_from or wrapper - - fb = FunctionBuilder.from_func(build_from or func) - - for arg in injected: - try: - fb.remove_arg(arg) - except MissingArgument: - if inject_to_varkw and fb.varkw is not None: - continue # keyword arg will be caught by the varkw - raise - - for arg, default in expected_items: - fb.add_arg(arg, default) # may raise ExistingArgument - - if fb.is_async: - fb.body = 'return await _call(%s)' % fb.get_invocation_str() - else: - fb.body = 'return _call(%s)' % fb.get_invocation_str() - - execdict = dict(_call=wrapper, _func=func) - fully_wrapped = fb.get_func(execdict, with_dict=update_dict) - - if hide_wrapped and hasattr(fully_wrapped, '__wrapped__'): - del fully_wrapped.__dict__['__wrapped__'] - elif not hide_wrapped: - fully_wrapped.__wrapped__ = func # ref to the original function (#115) - - return fully_wrapped - - -def _parse_wraps_expected(expected): - # expected takes a pretty powerful argument, it's processed - # here. admittedly this would be less trouble if I relied on - # OrderedDict (there's an impl of that in the commit history if - # you look - if expected is None: - expected = [] - elif isinstance(expected, basestring): - expected = [(expected, NO_DEFAULT)] - - expected_items = [] - try: - expected_iter = iter(expected) - except TypeError as e: - raise ValueError('"expected" takes string name, sequence of string names,' - ' iterable of (name, default) pairs, or a mapping of ' - ' {name: default}, not %r (got: %r)' % (expected, e)) - for argname in expected_iter: - if isinstance(argname, basestring): - # dict keys and bare strings - try: - default = expected[argname] - except TypeError: - default = NO_DEFAULT - else: - # pairs - try: - argname, default = argname - except (TypeError, ValueError): - raise ValueError('"expected" takes string name, sequence of string names,' - ' iterable of (name, default) pairs, or a mapping of ' - ' {name: default}, not %r') - if not isinstance(argname, basestring): - raise ValueError('all "expected" argnames must be strings, not %r' % (argname,)) - - expected_items.append((argname, default)) - - return expected_items - - -class FunctionBuilder(object): - """The FunctionBuilder type provides an interface for programmatically - creating new functions, either based on existing functions or from - scratch. - - Values are passed in at construction or set as attributes on the - instance. For creating a new function based of an existing one, - see the :meth:`~FunctionBuilder.from_func` classmethod. At any - point, :meth:`~FunctionBuilder.get_func` can be called to get a - newly compiled function, based on the values configured. - - >>> fb = FunctionBuilder('return_five', doc='returns the integer 5', - ... body='return 5') - >>> f = fb.get_func() - >>> f() - 5 - >>> fb.varkw = 'kw' - >>> f_kw = fb.get_func() - >>> f_kw(ignored_arg='ignored_val') - 5 - - Note that function signatures themselves changed quite a bit in - Python 3, so several arguments are only applicable to - FunctionBuilder in Python 3. Except for *name*, all arguments to - the constructor are keyword arguments. - - Args: - name (str): Name of the function. - doc (str): `Docstring`_ for the function, defaults to empty. - module (str): Name of the module from which this function was - imported. Defaults to None. - body (str): String version of the code representing the body - of the function. Defaults to ``'pass'``, which will result - in a function which does nothing and returns ``None``. - args (list): List of argument names, defaults to empty list, - denoting no arguments. - varargs (str): Name of the catch-all variable for positional - arguments. E.g., "args" if the resultant function is to have - ``*args`` in the signature. Defaults to None. - varkw (str): Name of the catch-all variable for keyword - arguments. E.g., "kwargs" if the resultant function is to have - ``**kwargs`` in the signature. Defaults to None. - defaults (tuple): A tuple containing default argument values for - those arguments that have defaults. - kwonlyargs (list): Argument names which are only valid as - keyword arguments. **Python 3 only.** - kwonlydefaults (dict): A mapping, same as normal *defaults*, - but only for the *kwonlyargs*. **Python 3 only.** - annotations (dict): Mapping of type hints and so - forth. **Python 3 only.** - filename (str): The filename that will appear in - tracebacks. Defaults to "boltons.funcutils.FunctionBuilder". - indent (int): Number of spaces with which to indent the - function *body*. Values less than 1 will result in an error. - dict (dict): Any other attributes which should be added to the - functions compiled with this FunctionBuilder. - - All of these arguments are also made available as attributes which - can be mutated as necessary. - - .. _Docstring: https://en.wikipedia.org/wiki/Docstring#Python - - """ - - if _IS_PY2: - _argspec_defaults = {'args': list, - 'varargs': lambda: None, - 'varkw': lambda: None, - 'defaults': lambda: None} - - @classmethod - def _argspec_to_dict(cls, f): - args, varargs, varkw, defaults = inspect.getargspec(f) - return {'args': args, - 'varargs': varargs, - 'varkw': varkw, - 'defaults': defaults} - - else: - _argspec_defaults = {'args': list, - 'varargs': lambda: None, - 'varkw': lambda: None, - 'defaults': lambda: None, - 'kwonlyargs': list, - 'kwonlydefaults': dict, - 'annotations': dict} - - @classmethod - def _argspec_to_dict(cls, f): - argspec = inspect.getfullargspec(f) - return dict((attr, getattr(argspec, attr)) - for attr in cls._argspec_defaults) - - _defaults = {'doc': str, - 'dict': dict, - 'is_async': lambda: False, - 'module': lambda: None, - 'body': lambda: 'pass', - 'indent': lambda: 4, - "annotations": dict, - 'filename': lambda: 'boltons.funcutils.FunctionBuilder'} - - _defaults.update(_argspec_defaults) - - _compile_count = itertools.count() - - def __init__(self, name, **kw): - self.name = name - for a, default_factory in self._defaults.items(): - val = kw.pop(a, None) - if val is None: - val = default_factory() - setattr(self, a, val) - - if kw: - raise TypeError('unexpected kwargs: %r' % kw.keys()) - return - - # def get_argspec(self): # TODO - - if _IS_PY2: - def get_sig_str(self, with_annotations=True): - """Return function signature as a string. - - with_annotations is ignored on Python 2. On Python 3 signature - will omit annotations if it is set to False. - """ - return inspect_formatargspec(self.args, self.varargs, - self.varkw, []) - - def get_invocation_str(self): - return inspect_formatargspec(self.args, self.varargs, - self.varkw, [])[1:-1] - else: - def get_sig_str(self, with_annotations=True): - """Return function signature as a string. - - with_annotations is ignored on Python 2. On Python 3 signature - will omit annotations if it is set to False. - """ - if with_annotations: - annotations = self.annotations - else: - annotations = {} - - return inspect_formatargspec(self.args, - self.varargs, - self.varkw, - [], - self.kwonlyargs, - {}, - annotations) - - _KWONLY_MARKER = re.compile(r""" - \* # a star - \s* # followed by any amount of whitespace - , # followed by a comma - \s* # followed by any amount of whitespace - """, re.VERBOSE) - - def get_invocation_str(self): - kwonly_pairs = None - formatters = {} - if self.kwonlyargs: - kwonly_pairs = dict((arg, arg) - for arg in self.kwonlyargs) - formatters['formatvalue'] = lambda value: '=' + value - - sig = inspect_formatargspec(self.args, - self.varargs, - self.varkw, - [], - kwonly_pairs, - kwonly_pairs, - {}, - **formatters) - sig = self._KWONLY_MARKER.sub('', sig) - return sig[1:-1] - - @classmethod - def from_func(cls, func): - """Create a new FunctionBuilder instance based on an existing - function. The original function will not be stored or - modified. - """ - # TODO: copy_body? gonna need a good signature regex. - # TODO: might worry about __closure__? - if not callable(func): - raise TypeError('expected callable object, not %r' % (func,)) - - if isinstance(func, functools.partial): - if _IS_PY2: - raise ValueError('Cannot build FunctionBuilder instances from partials in python 2.') - kwargs = {'name': func.func.__name__, - 'doc': func.func.__doc__, - 'module': getattr(func.func, '__module__', None), # e.g., method_descriptor - 'annotations': getattr(func.func, "__annotations__", {}), - 'dict': getattr(func.func, '__dict__', {})} - else: - kwargs = {'name': func.__name__, - 'doc': func.__doc__, - 'module': getattr(func, '__module__', None), # e.g., method_descriptor - 'annotations': getattr(func, "__annotations__", {}), - 'dict': getattr(func, '__dict__', {})} - - kwargs.update(cls._argspec_to_dict(func)) - - if _inspect_iscoroutinefunction(func): - kwargs['is_async'] = True - - return cls(**kwargs) - - def get_func(self, execdict=None, add_source=True, with_dict=True): - """Compile and return a new function based on the current values of - the FunctionBuilder. - - Args: - execdict (dict): The dictionary representing the scope in - which the compilation should take place. Defaults to an empty - dict. - add_source (bool): Whether to add the source used to a - special ``__source__`` attribute on the resulting - function. Defaults to True. - with_dict (bool): Add any custom attributes, if - applicable. Defaults to True. - - To see an example of usage, see the implementation of - :func:`~boltons.funcutils.wraps`. - """ - execdict = execdict or {} - body = self.body or self._default_body - - tmpl = 'def {name}{sig_str}:' - tmpl += '\n{body}' - - if self.is_async: - tmpl = 'async ' + tmpl - - body = _indent(self.body, ' ' * self.indent) - - name = self.name.replace('<', '_').replace('>', '_') # lambdas - src = tmpl.format(name=name, sig_str=self.get_sig_str(with_annotations=False), - doc=self.doc, body=body) - self._compile(src, execdict) - func = execdict[name] - - func.__name__ = self.name - func.__doc__ = self.doc - func.__defaults__ = self.defaults - if not _IS_PY2: - func.__kwdefaults__ = self.kwonlydefaults - func.__annotations__ = self.annotations - - if with_dict: - func.__dict__.update(self.dict) - func.__module__ = self.module - # TODO: caller module fallback? - - if add_source: - func.__source__ = src - - return func - - def get_defaults_dict(self): - """Get a dictionary of function arguments with defaults and the - respective values. - """ - ret = dict(reversed(list(zip(reversed(self.args), - reversed(self.defaults or []))))) - kwonlydefaults = getattr(self, 'kwonlydefaults', None) - if kwonlydefaults: - ret.update(kwonlydefaults) - return ret - - def get_arg_names(self, only_required=False): - arg_names = tuple(self.args) + tuple(getattr(self, 'kwonlyargs', ())) - if only_required: - defaults_dict = self.get_defaults_dict() - arg_names = tuple([an for an in arg_names if an not in defaults_dict]) - return arg_names - - if _IS_PY2: - def add_arg(self, arg_name, default=NO_DEFAULT): - "Add an argument with optional *default* (defaults to ``funcutils.NO_DEFAULT``)." - if arg_name in self.args: - raise ExistingArgument('arg %r already in func %s arg list' % (arg_name, self.name)) - self.args.append(arg_name) - if default is not NO_DEFAULT: - self.defaults = (self.defaults or ()) + (default,) - return - else: - def add_arg(self, arg_name, default=NO_DEFAULT, kwonly=False): - """Add an argument with optional *default* (defaults to - ``funcutils.NO_DEFAULT``). Pass *kwonly=True* to add a - keyword-only argument - """ - if arg_name in self.args: - raise ExistingArgument('arg %r already in func %s arg list' % (arg_name, self.name)) - if arg_name in self.kwonlyargs: - raise ExistingArgument('arg %r already in func %s kwonly arg list' % (arg_name, self.name)) - if not kwonly: - self.args.append(arg_name) - if default is not NO_DEFAULT: - self.defaults = (self.defaults or ()) + (default,) - else: - self.kwonlyargs.append(arg_name) - if default is not NO_DEFAULT: - self.kwonlydefaults[arg_name] = default - return - - def remove_arg(self, arg_name): - """Remove an argument from this FunctionBuilder's argument list. The - resulting function will have one less argument per call to - this function. - - Args: - arg_name (str): The name of the argument to remove. - - Raises a :exc:`ValueError` if the argument is not present. - - """ - args = self.args - d_dict = self.get_defaults_dict() - try: - args.remove(arg_name) - except ValueError: - try: - self.kwonlyargs.remove(arg_name) - except (AttributeError, ValueError): - # py2, or py3 and missing from both - exc = MissingArgument('arg %r not found in %s argument list:' - ' %r' % (arg_name, self.name, args)) - exc.arg_name = arg_name - raise exc - else: - self.kwonlydefaults.pop(arg_name, None) - else: - d_dict.pop(arg_name, None) - self.defaults = tuple([d_dict[a] for a in args if a in d_dict]) - return - - def _compile(self, src, execdict): - - filename = ('<%s-%d>' - % (self.filename, next(self._compile_count),)) - try: - code = compile(src, filename, 'single') - exec(code, execdict) - except Exception: - raise - return execdict - - -class MissingArgument(ValueError): - pass - - -class ExistingArgument(ValueError): - pass - - -def _indent(text, margin, newline='\n', key=bool): - "based on boltons.strutils.indent" - indented_lines = [(margin + line if key(line) else line) - for line in text.splitlines()] - return newline.join(indented_lines) - - -try: - from functools import total_ordering # 2.7+ -except ImportError: - # python 2.6 - def total_ordering(cls): - """Class decorator that fills in missing comparators/ordering - methods. Backport of :func:`functools.total_ordering` to work - with Python 2.6. - - Code from http://code.activestate.com/recipes/576685/ - """ - convert = { - '__lt__': [ - ('__gt__', - lambda self, other: not (self < other or self == other)), - ('__le__', - lambda self, other: self < other or self == other), - ('__ge__', - lambda self, other: not self < other)], - '__le__': [ - ('__ge__', - lambda self, other: not self <= other or self == other), - ('__lt__', - lambda self, other: self <= other and not self == other), - ('__gt__', - lambda self, other: not self <= other)], - '__gt__': [ - ('__lt__', - lambda self, other: not (self > other or self == other)), - ('__ge__', - lambda self, other: self > other or self == other), - ('__le__', - lambda self, other: not self > other)], - '__ge__': [ - ('__le__', - lambda self, other: (not self >= other) or self == other), - ('__gt__', - lambda self, other: self >= other and not self == other), - ('__lt__', - lambda self, other: not self >= other)] - } - roots = set(dir(cls)) & set(convert) - if not roots: - raise ValueError('must define at least one ordering operation:' - ' < > <= >=') - root = max(roots) # prefer __lt__ to __le__ to __gt__ to __ge__ - for opname, opfunc in convert[root]: - if opname not in roots: - opfunc.__name__ = opname - opfunc.__doc__ = getattr(int, opname).__doc__ - setattr(cls, opname, opfunc) - return cls - -# end funcutils.py