funcutils.py 41 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131
  1. # -*- coding: utf-8 -*-
  2. # Copyright (c) 2013, Mahmoud Hashemi
  3. #
  4. # Redistribution and use in source and binary forms, with or without
  5. # modification, are permitted provided that the following conditions are
  6. # met:
  7. #
  8. # * Redistributions of source code must retain the above copyright
  9. # notice, this list of conditions and the following disclaimer.
  10. #
  11. # * Redistributions in binary form must reproduce the above
  12. # copyright notice, this list of conditions and the following
  13. # disclaimer in the documentation and/or other materials provided
  14. # with the distribution.
  15. #
  16. # * The names of the contributors may not be used to endorse or
  17. # promote products derived from this software without specific
  18. # prior written permission.
  19. #
  20. # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  21. # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  22. # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  23. # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  24. # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  25. # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  26. # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  27. # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  28. # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  29. # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  30. # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  31. """Python's built-in :mod:`functools` module builds several useful
  32. utilities on top of Python's first-class function
  33. support. ``funcutils`` generally stays in the same vein, adding to and
  34. correcting Python's standard metaprogramming facilities.
  35. """
  36. from __future__ import print_function
  37. import sys
  38. import re
  39. import inspect
  40. import functools
  41. import itertools
  42. from types import MethodType, FunctionType
  43. try:
  44. xrange
  45. make_method = MethodType
  46. except NameError:
  47. # Python 3
  48. make_method = lambda desc, obj, obj_type: MethodType(desc, obj)
  49. basestring = (str, bytes) # Python 3 compat
  50. _IS_PY2 = False
  51. else:
  52. _IS_PY2 = True
  53. try:
  54. _inspect_iscoroutinefunction = inspect.iscoroutinefunction
  55. except AttributeError:
  56. # Python 3.4
  57. _inspect_iscoroutinefunction = lambda func: False
  58. try:
  59. from .typeutils import make_sentinel
  60. NO_DEFAULT = make_sentinel(var_name='NO_DEFAULT')
  61. except ImportError:
  62. NO_DEFAULT = object()
  63. try:
  64. from functools import partialmethod
  65. except ImportError:
  66. partialmethod = None
  67. _IS_PY35 = sys.version_info >= (3, 5)
  68. if not _IS_PY35:
  69. # py35+ wants you to use signature instead, but
  70. # inspect_formatargspec is way simpler for what it is. Copied the
  71. # vendoring approach from alembic:
  72. # https://github.com/sqlalchemy/alembic/blob/4cdad6aec32b4b5573a2009cc356cb4b144bd359/alembic/util/compat.py#L92
  73. from inspect import formatargspec as inspect_formatargspec
  74. else:
  75. from inspect import formatannotation
  76. def inspect_formatargspec(
  77. args, varargs=None, varkw=None, defaults=None,
  78. kwonlyargs=(), kwonlydefaults={}, annotations={},
  79. formatarg=str,
  80. formatvarargs=lambda name: '*' + name,
  81. formatvarkw=lambda name: '**' + name,
  82. formatvalue=lambda value: '=' + repr(value),
  83. formatreturns=lambda text: ' -> ' + text,
  84. formatannotation=formatannotation):
  85. """Copy formatargspec from python 3.7 standard library.
  86. Python 3 has deprecated formatargspec and requested that Signature
  87. be used instead, however this requires a full reimplementation
  88. of formatargspec() in terms of creating Parameter objects and such.
  89. Instead of introducing all the object-creation overhead and having
  90. to reinvent from scratch, just copy their compatibility routine.
  91. """
  92. def formatargandannotation(arg):
  93. result = formatarg(arg)
  94. if arg in annotations:
  95. result += ': ' + formatannotation(annotations[arg])
  96. return result
  97. specs = []
  98. if defaults:
  99. firstdefault = len(args) - len(defaults)
  100. for i, arg in enumerate(args):
  101. spec = formatargandannotation(arg)
  102. if defaults and i >= firstdefault:
  103. spec = spec + formatvalue(defaults[i - firstdefault])
  104. specs.append(spec)
  105. if varargs is not None:
  106. specs.append(formatvarargs(formatargandannotation(varargs)))
  107. else:
  108. if kwonlyargs:
  109. specs.append('*')
  110. if kwonlyargs:
  111. for kwonlyarg in kwonlyargs:
  112. spec = formatargandannotation(kwonlyarg)
  113. if kwonlydefaults and kwonlyarg in kwonlydefaults:
  114. spec += formatvalue(kwonlydefaults[kwonlyarg])
  115. specs.append(spec)
  116. if varkw is not None:
  117. specs.append(formatvarkw(formatargandannotation(varkw)))
  118. result = '(' + ', '.join(specs) + ')'
  119. if 'return' in annotations:
  120. result += formatreturns(formatannotation(annotations['return']))
  121. return result
  122. def get_module_callables(mod, ignore=None):
  123. """Returns two maps of (*types*, *funcs*) from *mod*, optionally
  124. ignoring based on the :class:`bool` return value of the *ignore*
  125. callable. *mod* can be a string name of a module in
  126. :data:`sys.modules` or the module instance itself.
  127. """
  128. if isinstance(mod, basestring):
  129. mod = sys.modules[mod]
  130. types, funcs = {}, {}
  131. for attr_name in dir(mod):
  132. if ignore and ignore(attr_name):
  133. continue
  134. try:
  135. attr = getattr(mod, attr_name)
  136. except Exception:
  137. continue
  138. try:
  139. attr_mod_name = attr.__module__
  140. except AttributeError:
  141. continue
  142. if attr_mod_name != mod.__name__:
  143. continue
  144. if isinstance(attr, type):
  145. types[attr_name] = attr
  146. elif callable(attr):
  147. funcs[attr_name] = attr
  148. return types, funcs
  149. def mro_items(type_obj):
  150. """Takes a type and returns an iterator over all class variables
  151. throughout the type hierarchy (respecting the MRO).
  152. >>> sorted(set([k for k, v in mro_items(int) if not k.startswith('__') and 'bytes' not in k and not callable(v)]))
  153. ['denominator', 'imag', 'numerator', 'real']
  154. """
  155. # TODO: handle slots?
  156. return itertools.chain.from_iterable(ct.__dict__.items()
  157. for ct in type_obj.__mro__)
  158. def dir_dict(obj, raise_exc=False):
  159. """Return a dictionary of attribute names to values for a given
  160. object. Unlike ``obj.__dict__``, this function returns all
  161. attributes on the object, including ones on parent classes.
  162. """
  163. # TODO: separate function for handling descriptors on types?
  164. ret = {}
  165. for k in dir(obj):
  166. try:
  167. ret[k] = getattr(obj, k)
  168. except Exception:
  169. if raise_exc:
  170. raise
  171. return ret
  172. def copy_function(orig, copy_dict=True):
  173. """Returns a shallow copy of the function, including code object,
  174. globals, closure, etc.
  175. >>> func = lambda: func
  176. >>> func() is func
  177. True
  178. >>> func_copy = copy_function(func)
  179. >>> func_copy() is func
  180. True
  181. >>> func_copy is not func
  182. True
  183. Args:
  184. orig (function): The function to be copied. Must be a
  185. function, not just any method or callable.
  186. copy_dict (bool): Also copy any attributes set on the function
  187. instance. Defaults to ``True``.
  188. """
  189. ret = FunctionType(orig.__code__,
  190. orig.__globals__,
  191. name=orig.__name__,
  192. argdefs=getattr(orig, "__defaults__", None),
  193. closure=getattr(orig, "__closure__", None))
  194. if copy_dict:
  195. ret.__dict__.update(orig.__dict__)
  196. return ret
  197. def partial_ordering(cls):
  198. """Class decorator, similar to :func:`functools.total_ordering`,
  199. except it is used to define `partial orderings`_ (i.e., it is
  200. possible that *x* is neither greater than, equal to, or less than
  201. *y*). It assumes the presence of the ``__le__()`` and ``__ge__()``
  202. method, but nothing else. It will not override any existing
  203. additional comparison methods.
  204. .. _partial orderings: https://en.wikipedia.org/wiki/Partially_ordered_set
  205. >>> @partial_ordering
  206. ... class MySet(set):
  207. ... def __le__(self, other):
  208. ... return self.issubset(other)
  209. ... def __ge__(self, other):
  210. ... return self.issuperset(other)
  211. ...
  212. >>> a = MySet([1,2,3])
  213. >>> b = MySet([1,2])
  214. >>> c = MySet([1,2,4])
  215. >>> b < a
  216. True
  217. >>> b > a
  218. False
  219. >>> b < c
  220. True
  221. >>> a < c
  222. False
  223. >>> c > a
  224. False
  225. """
  226. def __lt__(self, other): return self <= other and not self >= other
  227. def __gt__(self, other): return self >= other and not self <= other
  228. def __eq__(self, other): return self >= other and self <= other
  229. if not hasattr(cls, '__lt__'): cls.__lt__ = __lt__
  230. if not hasattr(cls, '__gt__'): cls.__gt__ = __gt__
  231. if not hasattr(cls, '__eq__'): cls.__eq__ = __eq__
  232. return cls
  233. class InstancePartial(functools.partial):
  234. """:class:`functools.partial` is a huge convenience for anyone
  235. working with Python's great first-class functions. It allows
  236. developers to curry arguments and incrementally create simpler
  237. callables for a variety of use cases.
  238. Unfortunately there's one big gap in its usefulness:
  239. methods. Partials just don't get bound as methods and
  240. automatically handed a reference to ``self``. The
  241. ``InstancePartial`` type remedies this by inheriting from
  242. :class:`functools.partial` and implementing the necessary
  243. descriptor protocol. There are no other differences in
  244. implementation or usage. :class:`CachedInstancePartial`, below,
  245. has the same ability, but is slightly more efficient.
  246. """
  247. if partialmethod is not None: # NB: See https://github.com/mahmoud/boltons/pull/244
  248. @property
  249. def _partialmethod(self):
  250. return partialmethod(self.func, *self.args, **self.keywords)
  251. def __get__(self, obj, obj_type):
  252. return make_method(self, obj, obj_type)
  253. class CachedInstancePartial(functools.partial):
  254. """The ``CachedInstancePartial`` is virtually the same as
  255. :class:`InstancePartial`, adding support for method-usage to
  256. :class:`functools.partial`, except that upon first access, it
  257. caches the bound method on the associated object, speeding it up
  258. for future accesses, and bringing the method call overhead to
  259. about the same as non-``partial`` methods.
  260. See the :class:`InstancePartial` docstring for more details.
  261. """
  262. if partialmethod is not None: # NB: See https://github.com/mahmoud/boltons/pull/244
  263. @property
  264. def _partialmethod(self):
  265. return partialmethod(self.func, *self.args, **self.keywords)
  266. if sys.version_info >= (3, 6):
  267. def __set_name__(self, obj_type, name):
  268. self.__name__ = name
  269. def __get__(self, obj, obj_type):
  270. # These assignments could've been in __init__, but there was
  271. # no simple way to do it without breaking one of PyPy or Py3.
  272. self.__name__ = getattr(self, "__name__", None)
  273. self.__doc__ = self.func.__doc__
  274. self.__module__ = self.func.__module__
  275. name = self.__name__
  276. # if you're on python 3.6+, name will never be `None` bc `__set_name__` sets it when descriptor getting assigned
  277. if name is None:
  278. for k, v in mro_items(obj_type):
  279. if v is self:
  280. self.__name__ = name = k
  281. if obj is None:
  282. return make_method(self, obj, obj_type)
  283. try:
  284. # since this is a data descriptor, this block
  285. # is probably only hit once (per object)
  286. return obj.__dict__[name]
  287. except KeyError:
  288. obj.__dict__[name] = ret = make_method(self, obj, obj_type)
  289. return ret
  290. partial = CachedInstancePartial
  291. def format_invocation(name='', args=(), kwargs=None, **kw):
  292. """Given a name, positional arguments, and keyword arguments, format
  293. a basic Python-style function call.
  294. >>> print(format_invocation('func', args=(1, 2), kwargs={'c': 3}))
  295. func(1, 2, c=3)
  296. >>> print(format_invocation('a_func', args=(1,)))
  297. a_func(1)
  298. >>> print(format_invocation('kw_func', kwargs=[('a', 1), ('b', 2)]))
  299. kw_func(a=1, b=2)
  300. """
  301. _repr = kw.pop('repr', repr)
  302. if kw:
  303. raise TypeError('unexpected keyword args: %r' % ', '.join(kw.keys()))
  304. kwargs = kwargs or {}
  305. a_text = ', '.join([_repr(a) for a in args])
  306. if isinstance(kwargs, dict):
  307. kwarg_items = [(k, kwargs[k]) for k in sorted(kwargs)]
  308. else:
  309. kwarg_items = kwargs
  310. kw_text = ', '.join(['%s=%s' % (k, _repr(v)) for k, v in kwarg_items])
  311. all_args_text = a_text
  312. if all_args_text and kw_text:
  313. all_args_text += ', '
  314. all_args_text += kw_text
  315. return '%s(%s)' % (name, all_args_text)
  316. def format_exp_repr(obj, pos_names, req_names=None, opt_names=None, opt_key=None):
  317. """Render an expression-style repr of an object, based on attribute
  318. names, which are assumed to line up with arguments to an initializer.
  319. >>> class Flag(object):
  320. ... def __init__(self, length, width, depth=None):
  321. ... self.length = length
  322. ... self.width = width
  323. ... self.depth = depth
  324. ...
  325. That's our Flag object, here are some example reprs for it:
  326. >>> flag = Flag(5, 10)
  327. >>> print(format_exp_repr(flag, ['length', 'width'], [], ['depth']))
  328. Flag(5, 10)
  329. >>> flag2 = Flag(5, 15, 2)
  330. >>> print(format_exp_repr(flag2, ['length'], ['width', 'depth']))
  331. Flag(5, width=15, depth=2)
  332. By picking the pos_names, req_names, opt_names, and opt_key, you
  333. can fine-tune how you want the repr to look.
  334. Args:
  335. obj (object): The object whose type name will be used and
  336. attributes will be checked
  337. pos_names (list): Required list of attribute names which will be
  338. rendered as positional arguments in the output repr.
  339. req_names (list): List of attribute names which will always
  340. appear in the keyword arguments in the output repr. Defaults to None.
  341. opt_names (list): List of attribute names which may appear in
  342. the keyword arguments in the output repr, provided they pass
  343. the *opt_key* check. Defaults to None.
  344. opt_key (callable): A function or callable which checks whether
  345. an opt_name should be in the repr. Defaults to a
  346. ``None``-check.
  347. """
  348. cn = type(obj).__name__
  349. req_names = req_names or []
  350. opt_names = opt_names or []
  351. uniq_names, all_names = set(), []
  352. for name in req_names + opt_names:
  353. if name in uniq_names:
  354. continue
  355. uniq_names.add(name)
  356. all_names.append(name)
  357. if opt_key is None:
  358. opt_key = lambda v: v is None
  359. assert callable(opt_key)
  360. args = [getattr(obj, name, None) for name in pos_names]
  361. kw_items = [(name, getattr(obj, name, None)) for name in all_names]
  362. kw_items = [(name, val) for name, val in kw_items
  363. if not (name in opt_names and opt_key(val))]
  364. return format_invocation(cn, args, kw_items)
  365. def format_nonexp_repr(obj, req_names=None, opt_names=None, opt_key=None):
  366. """Format a non-expression-style repr
  367. Some object reprs look like object instantiation, e.g., App(r=[], mw=[]).
  368. This makes sense for smaller, lower-level objects whose state
  369. roundtrips. But a lot of objects contain values that don't
  370. roundtrip, like types and functions.
  371. For those objects, there is the non-expression style repr, which
  372. mimic's Python's default style to make a repr like so:
  373. >>> class Flag(object):
  374. ... def __init__(self, length, width, depth=None):
  375. ... self.length = length
  376. ... self.width = width
  377. ... self.depth = depth
  378. ...
  379. >>> flag = Flag(5, 10)
  380. >>> print(format_nonexp_repr(flag, ['length', 'width'], ['depth']))
  381. <Flag length=5 width=10>
  382. If no attributes are specified or set, utilizes the id, not unlike Python's
  383. built-in behavior.
  384. >>> print(format_nonexp_repr(flag))
  385. <Flag id=...>
  386. """
  387. cn = obj.__class__.__name__
  388. req_names = req_names or []
  389. opt_names = opt_names or []
  390. uniq_names, all_names = set(), []
  391. for name in req_names + opt_names:
  392. if name in uniq_names:
  393. continue
  394. uniq_names.add(name)
  395. all_names.append(name)
  396. if opt_key is None:
  397. opt_key = lambda v: v is None
  398. assert callable(opt_key)
  399. items = [(name, getattr(obj, name, None)) for name in all_names]
  400. labels = ['%s=%r' % (name, val) for name, val in items
  401. if not (name in opt_names and opt_key(val))]
  402. if not labels:
  403. labels = ['id=%s' % id(obj)]
  404. ret = '<%s %s>' % (cn, ' '.join(labels))
  405. return ret
  406. # # #
  407. # # # Function builder
  408. # # #
  409. def wraps(func, injected=None, expected=None, **kw):
  410. """Decorator factory to apply update_wrapper() to a wrapper function.
  411. Modeled after built-in :func:`functools.wraps`. Returns a decorator
  412. that invokes update_wrapper() with the decorated function as the wrapper
  413. argument and the arguments to wraps() as the remaining arguments.
  414. Default arguments are as for update_wrapper(). This is a convenience
  415. function to simplify applying partial() to update_wrapper().
  416. Same example as in update_wrapper's doc but with wraps:
  417. >>> from boltons.funcutils import wraps
  418. >>>
  419. >>> def print_return(func):
  420. ... @wraps(func)
  421. ... def wrapper(*args, **kwargs):
  422. ... ret = func(*args, **kwargs)
  423. ... print(ret)
  424. ... return ret
  425. ... return wrapper
  426. ...
  427. >>> @print_return
  428. ... def example():
  429. ... '''docstring'''
  430. ... return 'example return value'
  431. >>>
  432. >>> val = example()
  433. example return value
  434. >>> example.__name__
  435. 'example'
  436. >>> example.__doc__
  437. 'docstring'
  438. """
  439. return partial(update_wrapper, func=func, build_from=None,
  440. injected=injected, expected=expected, **kw)
  441. def update_wrapper(wrapper, func, injected=None, expected=None, build_from=None, **kw):
  442. """Modeled after the built-in :func:`functools.update_wrapper`,
  443. this function is used to make your wrapper function reflect the
  444. wrapped function's:
  445. * Name
  446. * Documentation
  447. * Module
  448. * Signature
  449. The built-in :func:`functools.update_wrapper` copies the first three, but
  450. does not copy the signature. This version of ``update_wrapper`` can copy
  451. the inner function's signature exactly, allowing seamless usage
  452. and :mod:`introspection <inspect>`. Usage is identical to the
  453. built-in version::
  454. >>> from boltons.funcutils import update_wrapper
  455. >>>
  456. >>> def print_return(func):
  457. ... def wrapper(*args, **kwargs):
  458. ... ret = func(*args, **kwargs)
  459. ... print(ret)
  460. ... return ret
  461. ... return update_wrapper(wrapper, func)
  462. ...
  463. >>> @print_return
  464. ... def example():
  465. ... '''docstring'''
  466. ... return 'example return value'
  467. >>>
  468. >>> val = example()
  469. example return value
  470. >>> example.__name__
  471. 'example'
  472. >>> example.__doc__
  473. 'docstring'
  474. In addition, the boltons version of update_wrapper supports
  475. modifying the outer signature. By passing a list of
  476. *injected* argument names, those arguments will be removed from
  477. the outer wrapper's signature, allowing your decorator to provide
  478. arguments that aren't passed in.
  479. Args:
  480. wrapper (function) : The callable to which the attributes of
  481. *func* are to be copied.
  482. func (function): The callable whose attributes are to be copied.
  483. injected (list): An optional list of argument names which
  484. should not appear in the new wrapper's signature.
  485. expected (list): An optional list of argument names (or (name,
  486. default) pairs) representing new arguments introduced by
  487. the wrapper (the opposite of *injected*). See
  488. :meth:`FunctionBuilder.add_arg()` for more details.
  489. build_from (function): The callable from which the new wrapper
  490. is built. Defaults to *func*, unless *wrapper* is partial object
  491. built from *func*, in which case it defaults to *wrapper*.
  492. Useful in some specific cases where *wrapper* and *func* have the
  493. same arguments but differ on which are keyword-only and positional-only.
  494. update_dict (bool): Whether to copy other, non-standard
  495. attributes of *func* over to the wrapper. Defaults to True.
  496. inject_to_varkw (bool): Ignore missing arguments when a
  497. ``**kwargs``-type catch-all is present. Defaults to True.
  498. hide_wrapped (bool): Remove reference to the wrapped function(s)
  499. in the updated function.
  500. In opposition to the built-in :func:`functools.update_wrapper` bolton's
  501. version returns a copy of the function and does not modify anything in place.
  502. For more in-depth wrapping of functions, see the
  503. :class:`FunctionBuilder` type, on which update_wrapper was built.
  504. """
  505. if injected is None:
  506. injected = []
  507. elif isinstance(injected, basestring):
  508. injected = [injected]
  509. else:
  510. injected = list(injected)
  511. expected_items = _parse_wraps_expected(expected)
  512. if isinstance(func, (classmethod, staticmethod)):
  513. raise TypeError('wraps does not support wrapping classmethods and'
  514. ' staticmethods, change the order of wrapping to'
  515. ' wrap the underlying function: %r'
  516. % (getattr(func, '__func__', None),))
  517. update_dict = kw.pop('update_dict', True)
  518. inject_to_varkw = kw.pop('inject_to_varkw', True)
  519. hide_wrapped = kw.pop('hide_wrapped', False)
  520. if kw:
  521. raise TypeError('unexpected kwargs: %r' % kw.keys())
  522. if isinstance(wrapper, functools.partial) and func is wrapper.func:
  523. build_from = build_from or wrapper
  524. fb = FunctionBuilder.from_func(build_from or func)
  525. for arg in injected:
  526. try:
  527. fb.remove_arg(arg)
  528. except MissingArgument:
  529. if inject_to_varkw and fb.varkw is not None:
  530. continue # keyword arg will be caught by the varkw
  531. raise
  532. for arg, default in expected_items:
  533. fb.add_arg(arg, default) # may raise ExistingArgument
  534. if fb.is_async:
  535. fb.body = 'return await _call(%s)' % fb.get_invocation_str()
  536. else:
  537. fb.body = 'return _call(%s)' % fb.get_invocation_str()
  538. execdict = dict(_call=wrapper, _func=func)
  539. fully_wrapped = fb.get_func(execdict, with_dict=update_dict)
  540. if hide_wrapped and hasattr(fully_wrapped, '__wrapped__'):
  541. del fully_wrapped.__dict__['__wrapped__']
  542. elif not hide_wrapped:
  543. fully_wrapped.__wrapped__ = func # ref to the original function (#115)
  544. return fully_wrapped
  545. def _parse_wraps_expected(expected):
  546. # expected takes a pretty powerful argument, it's processed
  547. # here. admittedly this would be less trouble if I relied on
  548. # OrderedDict (there's an impl of that in the commit history if
  549. # you look
  550. if expected is None:
  551. expected = []
  552. elif isinstance(expected, basestring):
  553. expected = [(expected, NO_DEFAULT)]
  554. expected_items = []
  555. try:
  556. expected_iter = iter(expected)
  557. except TypeError as e:
  558. raise ValueError('"expected" takes string name, sequence of string names,'
  559. ' iterable of (name, default) pairs, or a mapping of '
  560. ' {name: default}, not %r (got: %r)' % (expected, e))
  561. for argname in expected_iter:
  562. if isinstance(argname, basestring):
  563. # dict keys and bare strings
  564. try:
  565. default = expected[argname]
  566. except TypeError:
  567. default = NO_DEFAULT
  568. else:
  569. # pairs
  570. try:
  571. argname, default = argname
  572. except (TypeError, ValueError):
  573. raise ValueError('"expected" takes string name, sequence of string names,'
  574. ' iterable of (name, default) pairs, or a mapping of '
  575. ' {name: default}, not %r')
  576. if not isinstance(argname, basestring):
  577. raise ValueError('all "expected" argnames must be strings, not %r' % (argname,))
  578. expected_items.append((argname, default))
  579. return expected_items
  580. class FunctionBuilder(object):
  581. """The FunctionBuilder type provides an interface for programmatically
  582. creating new functions, either based on existing functions or from
  583. scratch.
  584. Values are passed in at construction or set as attributes on the
  585. instance. For creating a new function based of an existing one,
  586. see the :meth:`~FunctionBuilder.from_func` classmethod. At any
  587. point, :meth:`~FunctionBuilder.get_func` can be called to get a
  588. newly compiled function, based on the values configured.
  589. >>> fb = FunctionBuilder('return_five', doc='returns the integer 5',
  590. ... body='return 5')
  591. >>> f = fb.get_func()
  592. >>> f()
  593. 5
  594. >>> fb.varkw = 'kw'
  595. >>> f_kw = fb.get_func()
  596. >>> f_kw(ignored_arg='ignored_val')
  597. 5
  598. Note that function signatures themselves changed quite a bit in
  599. Python 3, so several arguments are only applicable to
  600. FunctionBuilder in Python 3. Except for *name*, all arguments to
  601. the constructor are keyword arguments.
  602. Args:
  603. name (str): Name of the function.
  604. doc (str): `Docstring`_ for the function, defaults to empty.
  605. module (str): Name of the module from which this function was
  606. imported. Defaults to None.
  607. body (str): String version of the code representing the body
  608. of the function. Defaults to ``'pass'``, which will result
  609. in a function which does nothing and returns ``None``.
  610. args (list): List of argument names, defaults to empty list,
  611. denoting no arguments.
  612. varargs (str): Name of the catch-all variable for positional
  613. arguments. E.g., "args" if the resultant function is to have
  614. ``*args`` in the signature. Defaults to None.
  615. varkw (str): Name of the catch-all variable for keyword
  616. arguments. E.g., "kwargs" if the resultant function is to have
  617. ``**kwargs`` in the signature. Defaults to None.
  618. defaults (tuple): A tuple containing default argument values for
  619. those arguments that have defaults.
  620. kwonlyargs (list): Argument names which are only valid as
  621. keyword arguments. **Python 3 only.**
  622. kwonlydefaults (dict): A mapping, same as normal *defaults*,
  623. but only for the *kwonlyargs*. **Python 3 only.**
  624. annotations (dict): Mapping of type hints and so
  625. forth. **Python 3 only.**
  626. filename (str): The filename that will appear in
  627. tracebacks. Defaults to "boltons.funcutils.FunctionBuilder".
  628. indent (int): Number of spaces with which to indent the
  629. function *body*. Values less than 1 will result in an error.
  630. dict (dict): Any other attributes which should be added to the
  631. functions compiled with this FunctionBuilder.
  632. All of these arguments are also made available as attributes which
  633. can be mutated as necessary.
  634. .. _Docstring: https://en.wikipedia.org/wiki/Docstring#Python
  635. """
  636. if _IS_PY2:
  637. _argspec_defaults = {'args': list,
  638. 'varargs': lambda: None,
  639. 'varkw': lambda: None,
  640. 'defaults': lambda: None}
  641. @classmethod
  642. def _argspec_to_dict(cls, f):
  643. args, varargs, varkw, defaults = inspect.getargspec(f)
  644. return {'args': args,
  645. 'varargs': varargs,
  646. 'varkw': varkw,
  647. 'defaults': defaults}
  648. else:
  649. _argspec_defaults = {'args': list,
  650. 'varargs': lambda: None,
  651. 'varkw': lambda: None,
  652. 'defaults': lambda: None,
  653. 'kwonlyargs': list,
  654. 'kwonlydefaults': dict,
  655. 'annotations': dict}
  656. @classmethod
  657. def _argspec_to_dict(cls, f):
  658. argspec = inspect.getfullargspec(f)
  659. return dict((attr, getattr(argspec, attr))
  660. for attr in cls._argspec_defaults)
  661. _defaults = {'doc': str,
  662. 'dict': dict,
  663. 'is_async': lambda: False,
  664. 'module': lambda: None,
  665. 'body': lambda: 'pass',
  666. 'indent': lambda: 4,
  667. "annotations": dict,
  668. 'filename': lambda: 'boltons.funcutils.FunctionBuilder'}
  669. _defaults.update(_argspec_defaults)
  670. _compile_count = itertools.count()
  671. def __init__(self, name, **kw):
  672. self.name = name
  673. for a, default_factory in self._defaults.items():
  674. val = kw.pop(a, None)
  675. if val is None:
  676. val = default_factory()
  677. setattr(self, a, val)
  678. if kw:
  679. raise TypeError('unexpected kwargs: %r' % kw.keys())
  680. return
  681. # def get_argspec(self): # TODO
  682. if _IS_PY2:
  683. def get_sig_str(self, with_annotations=True):
  684. """Return function signature as a string.
  685. with_annotations is ignored on Python 2. On Python 3 signature
  686. will omit annotations if it is set to False.
  687. """
  688. return inspect_formatargspec(self.args, self.varargs,
  689. self.varkw, [])
  690. def get_invocation_str(self):
  691. return inspect_formatargspec(self.args, self.varargs,
  692. self.varkw, [])[1:-1]
  693. else:
  694. def get_sig_str(self, with_annotations=True):
  695. """Return function signature as a string.
  696. with_annotations is ignored on Python 2. On Python 3 signature
  697. will omit annotations if it is set to False.
  698. """
  699. if with_annotations:
  700. annotations = self.annotations
  701. else:
  702. annotations = {}
  703. return inspect_formatargspec(self.args,
  704. self.varargs,
  705. self.varkw,
  706. [],
  707. self.kwonlyargs,
  708. {},
  709. annotations)
  710. _KWONLY_MARKER = re.compile(r"""
  711. \* # a star
  712. \s* # followed by any amount of whitespace
  713. , # followed by a comma
  714. \s* # followed by any amount of whitespace
  715. """, re.VERBOSE)
  716. def get_invocation_str(self):
  717. kwonly_pairs = None
  718. formatters = {}
  719. if self.kwonlyargs:
  720. kwonly_pairs = dict((arg, arg)
  721. for arg in self.kwonlyargs)
  722. formatters['formatvalue'] = lambda value: '=' + value
  723. sig = inspect_formatargspec(self.args,
  724. self.varargs,
  725. self.varkw,
  726. [],
  727. kwonly_pairs,
  728. kwonly_pairs,
  729. {},
  730. **formatters)
  731. sig = self._KWONLY_MARKER.sub('', sig)
  732. return sig[1:-1]
  733. @classmethod
  734. def from_func(cls, func):
  735. """Create a new FunctionBuilder instance based on an existing
  736. function. The original function will not be stored or
  737. modified.
  738. """
  739. # TODO: copy_body? gonna need a good signature regex.
  740. # TODO: might worry about __closure__?
  741. if not callable(func):
  742. raise TypeError('expected callable object, not %r' % (func,))
  743. if isinstance(func, functools.partial):
  744. if _IS_PY2:
  745. raise ValueError('Cannot build FunctionBuilder instances from partials in python 2.')
  746. kwargs = {'name': func.func.__name__,
  747. 'doc': func.func.__doc__,
  748. 'module': getattr(func.func, '__module__', None), # e.g., method_descriptor
  749. 'annotations': getattr(func.func, "__annotations__", {}),
  750. 'dict': getattr(func.func, '__dict__', {})}
  751. else:
  752. kwargs = {'name': func.__name__,
  753. 'doc': func.__doc__,
  754. 'module': getattr(func, '__module__', None), # e.g., method_descriptor
  755. 'annotations': getattr(func, "__annotations__", {}),
  756. 'dict': getattr(func, '__dict__', {})}
  757. kwargs.update(cls._argspec_to_dict(func))
  758. if _inspect_iscoroutinefunction(func):
  759. kwargs['is_async'] = True
  760. return cls(**kwargs)
  761. def get_func(self, execdict=None, add_source=True, with_dict=True):
  762. """Compile and return a new function based on the current values of
  763. the FunctionBuilder.
  764. Args:
  765. execdict (dict): The dictionary representing the scope in
  766. which the compilation should take place. Defaults to an empty
  767. dict.
  768. add_source (bool): Whether to add the source used to a
  769. special ``__source__`` attribute on the resulting
  770. function. Defaults to True.
  771. with_dict (bool): Add any custom attributes, if
  772. applicable. Defaults to True.
  773. To see an example of usage, see the implementation of
  774. :func:`~boltons.funcutils.wraps`.
  775. """
  776. execdict = execdict or {}
  777. body = self.body or self._default_body
  778. tmpl = 'def {name}{sig_str}:'
  779. tmpl += '\n{body}'
  780. if self.is_async:
  781. tmpl = 'async ' + tmpl
  782. body = _indent(self.body, ' ' * self.indent)
  783. name = self.name.replace('<', '_').replace('>', '_') # lambdas
  784. src = tmpl.format(name=name, sig_str=self.get_sig_str(with_annotations=False),
  785. doc=self.doc, body=body)
  786. self._compile(src, execdict)
  787. func = execdict[name]
  788. func.__name__ = self.name
  789. func.__doc__ = self.doc
  790. func.__defaults__ = self.defaults
  791. if not _IS_PY2:
  792. func.__kwdefaults__ = self.kwonlydefaults
  793. func.__annotations__ = self.annotations
  794. if with_dict:
  795. func.__dict__.update(self.dict)
  796. func.__module__ = self.module
  797. # TODO: caller module fallback?
  798. if add_source:
  799. func.__source__ = src
  800. return func
  801. def get_defaults_dict(self):
  802. """Get a dictionary of function arguments with defaults and the
  803. respective values.
  804. """
  805. ret = dict(reversed(list(zip(reversed(self.args),
  806. reversed(self.defaults or [])))))
  807. kwonlydefaults = getattr(self, 'kwonlydefaults', None)
  808. if kwonlydefaults:
  809. ret.update(kwonlydefaults)
  810. return ret
  811. def get_arg_names(self, only_required=False):
  812. arg_names = tuple(self.args) + tuple(getattr(self, 'kwonlyargs', ()))
  813. if only_required:
  814. defaults_dict = self.get_defaults_dict()
  815. arg_names = tuple([an for an in arg_names if an not in defaults_dict])
  816. return arg_names
  817. if _IS_PY2:
  818. def add_arg(self, arg_name, default=NO_DEFAULT):
  819. "Add an argument with optional *default* (defaults to ``funcutils.NO_DEFAULT``)."
  820. if arg_name in self.args:
  821. raise ExistingArgument('arg %r already in func %s arg list' % (arg_name, self.name))
  822. self.args.append(arg_name)
  823. if default is not NO_DEFAULT:
  824. self.defaults = (self.defaults or ()) + (default,)
  825. return
  826. else:
  827. def add_arg(self, arg_name, default=NO_DEFAULT, kwonly=False):
  828. """Add an argument with optional *default* (defaults to
  829. ``funcutils.NO_DEFAULT``). Pass *kwonly=True* to add a
  830. keyword-only argument
  831. """
  832. if arg_name in self.args:
  833. raise ExistingArgument('arg %r already in func %s arg list' % (arg_name, self.name))
  834. if arg_name in self.kwonlyargs:
  835. raise ExistingArgument('arg %r already in func %s kwonly arg list' % (arg_name, self.name))
  836. if not kwonly:
  837. self.args.append(arg_name)
  838. if default is not NO_DEFAULT:
  839. self.defaults = (self.defaults or ()) + (default,)
  840. else:
  841. self.kwonlyargs.append(arg_name)
  842. if default is not NO_DEFAULT:
  843. self.kwonlydefaults[arg_name] = default
  844. return
  845. def remove_arg(self, arg_name):
  846. """Remove an argument from this FunctionBuilder's argument list. The
  847. resulting function will have one less argument per call to
  848. this function.
  849. Args:
  850. arg_name (str): The name of the argument to remove.
  851. Raises a :exc:`ValueError` if the argument is not present.
  852. """
  853. args = self.args
  854. d_dict = self.get_defaults_dict()
  855. try:
  856. args.remove(arg_name)
  857. except ValueError:
  858. try:
  859. self.kwonlyargs.remove(arg_name)
  860. except (AttributeError, ValueError):
  861. # py2, or py3 and missing from both
  862. exc = MissingArgument('arg %r not found in %s argument list:'
  863. ' %r' % (arg_name, self.name, args))
  864. exc.arg_name = arg_name
  865. raise exc
  866. else:
  867. self.kwonlydefaults.pop(arg_name, None)
  868. else:
  869. d_dict.pop(arg_name, None)
  870. self.defaults = tuple([d_dict[a] for a in args if a in d_dict])
  871. return
  872. def _compile(self, src, execdict):
  873. filename = ('<%s-%d>'
  874. % (self.filename, next(self._compile_count),))
  875. try:
  876. code = compile(src, filename, 'single')
  877. exec(code, execdict)
  878. except Exception:
  879. raise
  880. return execdict
  881. class MissingArgument(ValueError):
  882. pass
  883. class ExistingArgument(ValueError):
  884. pass
  885. def _indent(text, margin, newline='\n', key=bool):
  886. "based on boltons.strutils.indent"
  887. indented_lines = [(margin + line if key(line) else line)
  888. for line in text.splitlines()]
  889. return newline.join(indented_lines)
  890. try:
  891. from functools import total_ordering # 2.7+
  892. except ImportError:
  893. # python 2.6
  894. def total_ordering(cls):
  895. """Class decorator that fills in missing comparators/ordering
  896. methods. Backport of :func:`functools.total_ordering` to work
  897. with Python 2.6.
  898. Code from http://code.activestate.com/recipes/576685/
  899. """
  900. convert = {
  901. '__lt__': [
  902. ('__gt__',
  903. lambda self, other: not (self < other or self == other)),
  904. ('__le__',
  905. lambda self, other: self < other or self == other),
  906. ('__ge__',
  907. lambda self, other: not self < other)],
  908. '__le__': [
  909. ('__ge__',
  910. lambda self, other: not self <= other or self == other),
  911. ('__lt__',
  912. lambda self, other: self <= other and not self == other),
  913. ('__gt__',
  914. lambda self, other: not self <= other)],
  915. '__gt__': [
  916. ('__lt__',
  917. lambda self, other: not (self > other or self == other)),
  918. ('__ge__',
  919. lambda self, other: self > other or self == other),
  920. ('__le__',
  921. lambda self, other: not self > other)],
  922. '__ge__': [
  923. ('__le__',
  924. lambda self, other: (not self >= other) or self == other),
  925. ('__gt__',
  926. lambda self, other: self >= other and not self == other),
  927. ('__lt__',
  928. lambda self, other: not self >= other)]
  929. }
  930. roots = set(dir(cls)) & set(convert)
  931. if not roots:
  932. raise ValueError('must define at least one ordering operation:'
  933. ' < > <= >=')
  934. root = max(roots) # prefer __lt__ to __le__ to __gt__ to __ge__
  935. for opname, opfunc in convert[root]:
  936. if opname not in roots:
  937. opfunc.__name__ = opname
  938. opfunc.__doc__ = getattr(int, opname).__doc__
  939. setattr(cls, opname, opfunc)
  940. return cls
  941. def noop(*args, **kwargs):
  942. """
  943. Simple function that should be used when no effect is desired.
  944. An alternative to checking for an optional function type parameter.
  945. e.g.
  946. def decorate(func, pre_func=None, post_func=None):
  947. if pre_func:
  948. pre_func()
  949. func()
  950. if post_func:
  951. post_func()
  952. vs
  953. def decorate(func, pre_func=noop, post_func=noop):
  954. pre_func()
  955. func()
  956. post_func()
  957. """
  958. return None
  959. # end funcutils.py