funtools.py 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115
  1. """Functional utilities."""
  2. from __future__ import absolute_import, unicode_literals
  3. from .abstract import Thenable
  4. from .promises import promise
  5. __all__ = [
  6. 'maybe_promise', 'ensure_promise',
  7. 'ppartial', 'preplace', 'ready_promise',
  8. 'starpromise', 'transform', 'wrap',
  9. ]
  10. def maybe_promise(p):
  11. """Return None if p is unefined, otherwise make sure it's a promise."""
  12. if p:
  13. if not isinstance(p, Thenable):
  14. return promise(p)
  15. return p
  16. def ensure_promise(p):
  17. """Ensure p is a promise.
  18. If p is not a promise, a new promise is created with p' as callback.
  19. """
  20. if p is None:
  21. return promise()
  22. return maybe_promise(p)
  23. def ppartial(p, *args, **kwargs):
  24. """Create/modify promise with partial arguments."""
  25. p = ensure_promise(p)
  26. if args:
  27. p.args = args + p.args
  28. if kwargs:
  29. p.kwargs.update(kwargs)
  30. return p
  31. def preplace(p, *args, **kwargs):
  32. """Replace promise arguments.
  33. This will force the promise to disregard any arguments
  34. the promise is fulfilled with, and to be called with the
  35. provided arguments instead.
  36. """
  37. def _replacer(*_, **__):
  38. return p(*args, **kwargs)
  39. return promise(_replacer)
  40. def ready_promise(callback=None, *args):
  41. """Create promise that is already fulfilled."""
  42. p = ensure_promise(callback)
  43. p(*args)
  44. return p
  45. def starpromise(fun, *args, **kwargs):
  46. """Create promise, using star arguments."""
  47. return promise(fun, args, kwargs)
  48. def transform(filter_, callback, *filter_args, **filter_kwargs):
  49. """Filter final argument to a promise.
  50. E.g. to coerce callback argument to :class:`int`::
  51. transform(int, callback)
  52. or a more complex example extracting something from a dict
  53. and coercing the value to :class:`float`:
  54. .. code-block:: python
  55. def filter_key_value(key, filter_, mapping):
  56. return filter_(mapping[key])
  57. def get_page_expires(self, url, callback=None):
  58. return self.request(
  59. 'GET', url,
  60. callback=transform(get_key, callback, 'PageExpireValue', int),
  61. )
  62. """
  63. callback = ensure_promise(callback)
  64. P = promise(_transback, (filter_, callback, filter_args, filter_kwargs))
  65. P.then(promise(), callback.throw)
  66. return P
  67. def _transback(filter_, callback, args, kwargs, ret):
  68. try:
  69. ret = filter_(*args + (ret,), **kwargs)
  70. except Exception:
  71. callback.throw()
  72. else:
  73. return callback(ret)
  74. def wrap(p):
  75. """Wrap promise.
  76. This wraps the promise such that if the promise is called with a promise as
  77. argument, we attach ourselves to that promise instead.
  78. """
  79. def on_call(*args, **kwargs):
  80. if len(args) == 1 and isinstance(args[0], promise):
  81. return args[0].then(p)
  82. else:
  83. return p(*args, **kwargs)
  84. return on_call