1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220
|
Promise
=======
This is a implementation of Promises in Python. It is a super set of
Promises/A+ designed to have readable, performant code and to provide
just the extensions that are absolutely necessary for using promises in
Python.
Its fully compatible with the `Promises/A+
spec <http://promises-aplus.github.io/promises-spec/>`__
|travis| |pypi| |coveralls|
Installation
------------
::
$ pip install promise
Usage
-----
The example below shows how you can load the promise library. It then
demonstrates creating a promise from scratch. You simply call
``Promise(fn)``. There is a complete specification for what is returned
by this method in
`Promises/A+ <http://promises-aplus.github.com/promises-spec/>`__.
.. code:: python
from promise import Promise
promise = Promise(
lambda resolve, reject: resolve('RESOLVED!')
)
API
---
Before all examples, you will need:
.. code:: python
from promise import Promise
Promise(resolver)
~~~~~~~~~~~~~~~~~
This creates and returns a new promise. ``resolver`` must be a function.
The ``resolver`` function is passed two arguments:
1. ``resolve`` should be called with a single argument. If it is called
with a non-promise value then the promise is fulfilled with that
value. If it is called with a promise (A) then the returned promise
takes on the state of that new promise (A).
2. ``reject`` should be called with a single argument. The returned
promise will be rejected with that argument.
Class Methods
~~~~~~~~~~~~~
These methods are invoked by calling ``Promise.methodName``.
Promise.resolve(value)
^^^^^^^^^^^^^^^^^^^^^^
Converts values and foreign promises into Promises/A+ promises. If you
pass it a value then it returns a Promise for that value. If you pass it
something that is close to a promise (such as a jQuery attempt at a
promise) it returns a Promise that takes on the state of ``value``
(rejected or fulfilled).
Promise.reject(value)
^^^^^^^^^^^^^^^^^^^^^
Returns a rejected promise with the given value.
Promise.all(list)
^^^^^^^^^^^^^^^^^
Returns a promise for a list. If it is called with a single argument
then this returns a promise for a copy of that list with any promises
replaced by their fulfilled values. e.g.
.. code:: python
p = Promise.all([Promise.resolve('a'), 'b', Promise.resolve('c')]) \
.then(lambda res: res == ['a', 'b', 'c'])
assert p.get() is True
Promise.cast(obj)
^^^^^^^^^^^^^^^^^
This function wraps the ``obj`` act as a ``Promise`` if possible. Python
``Future``\ s are supported, with a callback to ``promise.done`` when
resolved. Have the same effects as ``Promise.resolve(obj)``.
Promise.for\_dict(d)
^^^^^^^^^^^^^^^^^^^^
A special function that takes a dictionary of promises and turns them
into a promise for a dictionary of values. In other words, this turns an
dictionary of promises for values into a promise for a dictionary of
values.
Promise.is\_thenable(obj)
^^^^^^^^^^^^^^^^^^^^^^^^^
This function checks if the ``obj`` is a ``Promise``, or could be
``cast``\ ed.
Promise.promisify(func)
^^^^^^^^^^^^^^^^^^^^^^^
This function wraps the result of calling ``func`` in a ``Promise``
instance.
Instance Methods
~~~~~~~~~~~~~~~~
These methods are invoked on a promise instance by calling
``myPromise.methodName``
promise.then(did\_fulfill, did\_reject)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
This method follows the `Promises/A+
spec <http://promises-aplus.github.io/promises-spec/>`__. It explains
things very clearly so I recommend you read it.
Either ``did_fulfill`` or ``did_reject`` will be called and they will
not be called more than once. They will be passed a single argument and
will always be called asynchronously (in the next turn of the event
loop).
If the promise is fulfilled then ``did_fulfill`` is called. If the
promise is rejected then ``did_reject`` is called.
The call to ``.then`` also returns a promise. If the handler that is
called returns a promise, the promise returned by ``.then`` takes on the
state of that returned promise. If the handler that is called returns a
value that is not a promise, the promise returned by ``.then`` will be
fulfilled with that value. If the handler that is called throws an
exception then the promise returned by ``.then`` is rejected with that
exception.
promise.catch(did\_reject)
^^^^^^^^^^^^^^^^^^^^^^^^^^
Sugar for ``promise.then(None, did_reject)``, to mirror ``catch`` in
synchronous code.
promise.done(did\_fulfill, did\_reject)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The same semantics as ``.then`` except that it does not return a promise
and any exceptions are re-thrown so that they can be logged (crashing
the application in non-browser environments)
Contributing
============
After cloning this repo, ensure dependencies are installed by running:
.. code:: sh
pip install -e ".[test]"
After developing, the full test suite can be evaluated by running:
.. code:: sh
py.test tests --cov=promise --benchmark-skip # Use -v -s for verbose mode
You can also run the benchmarks with:
.. code:: sh
py.test tests --benchmark-only
Static type checking
--------------------
Python type annotations are very useful for making sure we use the
libary the way is intended.
You can run ``mypy`` static type checker:
.. code:: sh
pip install mypy
mypy promise --ignore-missing-imports
Or ``pyre``:
.. code:: sh
pip install pyre-check
pyre --source-directory promise check
Notes
=====
This package is heavily insipired in
`aplus <https://github.com/xogeny/aplus>`__.
License
-------
`MIT
License <https://github.com/syrusakbary/promise/blob/master/LICENSE>`__
.. |travis| image:: https://img.shields.io/travis/syrusakbary/promise.svg?style=flat
:target: https://travis-ci.org/syrusakbary/promise
.. |pypi| image:: https://img.shields.io/pypi/v/promise.svg?style=flat
:target: https://pypi.python.org/pypi/promise
.. |coveralls| image:: https://coveralls.io/repos/syrusakbary/promise/badge.svg?branch=master&service=github
:target: https://coveralls.io/github/syrusakbary/promise?branch=master
|