A Python module providing container and proxy promises, supporting delayed linear and multi-processing delivery.
This is dissimilar to PEP-3148, where the focus is on a robust asynchronous delivery framework. We're mostly interested in simple deferral and most of all, transparent proxies.
At this stage this project is just a rough draft. I've set the version to 0.9.0 and am not promising any kind of API stability until 1.0.0 at which point I'll tag it and cut a release. Feel free to play, fork, or experiment.
- python-promises documentation
- python-promises on GitHub
- python-promises not on PyPI until version 1.0.0
This is very much a work in progress. I am still working out how much I want to explain, in what order, etc. It may be best to just expect that everyone knows what a promise is and not explain anything at all... - Chris
So let's start simply, assuming that while everyone is already familiar with the concept of a promise and how it affects them socially, they may not be clear on how the concept relates to programming and computer science.
It's very likely that you're using something very akin to a promise in your code, and just not considering it as such. At the most basic level, one could conceive of a promise as nothing more than say, a memoized nullary function. One may have thought, "this function involves network access, so let's not call it unless we absolutely need to load this data." The placeholder for the value is the promise, as is the code and any data that would be needed to deliver on it.
There's no free computation involved. To get the value from a promise, the work still has to be done and delivered. But perhaps it can happen in another thread or process while you're working on collating ten thousand similar pieces.
Some languages are built on the concept of the promise and lazy evaluation. Others offer it as an option, but at the syntax level. And still others at least provide an OO representation of the concept in some library. Python doesn't, by default, have any of these.
In this library, the promise isn't necessarily that the passed work will be executed. The promise being made is that the answer or result of a piece of computation will be delivered if asked for. As such, if no code ever attempts to retrieve a promised value, it's perfectly acceptable for there to be no attempt to execute the underlying work. Put another way, promises are not the same as tasks.
A lazy container is a simple, object-oriented placeholder. It can be
created by invoking the lazy
function, passing a work function and
any arguments it needs. When delivered, the container will call that
work and collect the result as its answer. Any further invocations of
deliver will return the answer without re-executing the work. However,
if an exception is raised by the work during delivery the container
will not be considered as delivered. In the case of a transient issue
(such as a time-out), delivery can be attempted again until an answer
is finally returned.
Proxies are a way to write promises without looking like you're
writing promises. You treat the proxy as though it were the answer
itself. A proxy is created by invoking the lazy_proxy
function, and
passing a nullary work function as the single argument. If your work
delivers an int, then treat the proxy like an int. If your work
delivers a dictionary, then treat the proxy like it were a dictionary.
A proxy tries fairly hard to act like the delivered value, by passing along almost every conceivable call to the underlying answer.
However, proxies are still their own type. As such, any code that is written which does a type check will potentially misbehave.
An example of this is the builtin set type. Below we show that the proxy will happily pass the richcompare call along to the underlying set and affirm that A and X are equal. However, reverse the operands and X will first check that the arguments to its richcompare call are another set instance. Since A is not a set (A is an instance of promises.Proxy), X's richcompare immediately returns False, indicating that X and A are not equal.
>>> from promises import lazy_proxy, deliver
>>> A = lazy_proxy(set, [1, 2, 3])
>>> A
set([1, 2, 3])
>>> X = set([1, 2, 3])
>>> X
set([1, 2, 3])
>>> A == X
True
>>> X == A
False
>>> X == deliver(A)
True
- Python 2.6 or later (no support for Python 3 unless someone else wants to hack in all the macros for the proxy code)
This module uses setuptools, so simply run
python setup.py install
There are multiple alternative implementations following different wavelengths of this concept. Here are some for your perusal.
- concurrent.futures - Python 3.4 includes PEP-3148
- futureutils - Introduces futures and promises into iterators
- aplus - Promises/A+ specification in Python
- promised - Python "promise" for output of asynchronous operations, and callback chaining.
Christopher O'Brien obriencj@gmail.com
This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along with this library; if not, see http://www.gnu.org/licenses/.