https://github.com/erotemic/futures_actors
Extension of the python futures module to support stateful asynchronous computations
https://github.com/erotemic/futures_actors
Last synced: 2 months ago
JSON representation
Extension of the python futures module to support stateful asynchronous computations
- Host: GitHub
- URL: https://github.com/erotemic/futures_actors
- Owner: Erotemic
- License: apache-2.0
- Created: 2017-07-02T17:43:57.000Z (almost 8 years ago)
- Default Branch: main
- Last Pushed: 2021-09-24T00:58:16.000Z (over 3 years ago)
- Last Synced: 2025-03-28T17:05:49.857Z (3 months ago)
- Language: Python
- Size: 29.3 KB
- Stars: 2
- Watchers: 2
- Forks: 1
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
[](https://travis-ci.org/Erotemic/futures_actors)
[](https://pypi.python.org/pypi/futures_actors)
[](https://codecov.io/github/Erotemic/futures_actors?branch=master)An extension of the concurrent.futures module to support stateful computations using a simplified actor model.
## Purpose
Allows a class to be created in a separate thread/process.
Unlike the simple functions that can be run using the builtin concurrent.futures module, the class instance can
maintain its own private state.
Messages (in the form of arbitrary pickleable objects) can be send to this process allowing communication.
The actor responds in the form of a Future object.## Installation:
You can install the latest stable version through pypi.
```
pip install futures_actors
```Or you can install the latest development version through GitHub.
```
pip install git+https://github.com/Erotemic/futures_actors.git
```## API
There are two main attributes exposed:
`futures_utils.ThreadActor` and `futures_utils.ProcessActor`.
Each of this are abstract base classes that a custom actor class should inherit from.
This custom class should implement a `handles(self, message)` method, which should accept an arbitrary message
object and can return arbitrary responses.
To get asynchronous computation, a new instance should be created using the `executor(*args, **kw)` method, which
will call the constructor of the class in a separate thread/process and return an `executor` object that can be
used to send messages to it.
This `executor` object works very similar to a `concurrent.futures.ThreadExecutor` or
`concurrent.futures.ProcessExecutor`, except instead of having a `submit(func, *args, **kw)` method that takes a
function and arguments, it has a `post(message)` method that sends a message to the asynchronous actor.
However, like `submit`, `post` also returns a `Future` object.### Example
Here is a simple example showing basic usage
```python
from futures_actors import ThreadActor
class MyActor(ThreadActor):
def __init__(self):
self.state = 5
#
def handle(self, message):
self.state += message
return self.state
#
executor = MyActor.executor()
f = executor.post(10)
assert f.result() == 15
```Here is another setting multiple messages at once, cancelling a task, and
adding callbacks.```python
import futures_actorsclass TestActor(futures_actors.ProcessActor):
def __init__(actor, a=None, factor=1):
actor.state = {}
if a is not None:
print('init mixin with args')
print('a = %r' % (a,))
actor.state['a'] = a * factordef handle(actor, message):
print('handling message = {}'.format(message))
if not isinstance(message, dict):
raise ValueError('Commands must be passed in a message dict')
message = message.copy()
action = message.pop('action', None)
if action is None:
raise ValueError('message must have an action item')
if action == 'debug':
return actor
if action == 'wait':
import time
num = message.get('time', 0)
time.sleep(num)
return num
else:
raise ValueError('Unknown action=%r' % (action,))test_state = {'num': False}
def done_callback(f):
""" this will be executed in the main process """
try:
num = f.result()
except futures.CancelledError:
num = 'canceled'
print('Canceled task {}'.format(f))
else:
test_state['num'] += num
print('DONE CALLBACK GOT = {}'.format(num))executor = TestActor.executor()
f1 = executor.post({'action': 'wait', 'time': 1})
f1.add_done_callback(done_callback)f2 = executor.post({'action': 'wait', 'time': 2})
f2.add_done_callback(done_callback)f3 = executor.post({'action': 'wait', 'time': 3})
f3.add_done_callback(done_callback)f4 = executor.post({'action': 'wait', 'time': 4})
f4.add_done_callback(done_callback)can_cancel = f3.cancel()
assert can_cancel, 'we should be able to cancel in time'f4.result()
assert test_state['num'] == 7, 'f3 was not cancelled'
```## Limitations
Currently actors can only communicate with their executor. Simple support for
actors communicating with other actors is not supported.### Implementation details
Most of this code is duplicated from the concurrent.futures.thread and
concurrent.futures.process modules, written by Brian Quinlan. The main
difference is that we expose an `Actor` class which can be inherited from and
provides the `executor` classmethod. This creates an asynchronously maintained
instance of this class in a separate thread/process