Source code for pykka.proxy

from pykka import ActorDeadError as _ActorDeadError


[docs]class ActorProxy(object): """ An :class:`ActorProxy` wraps an :class:`ActorRef <pykka.actor.ActorRef>` instance. The proxy allows the referenced actor to be used through regular method calls and field access. You can create an :class:`ActorProxy` from any :class:`ActorRef <pykka.actor.ActorRef>`:: actor_ref = MyActor.start() actor_proxy = ActorProxy(actor_ref) You can also get an :class:`ActorProxy` by using :meth:`proxy() <pykka.actor.ActorRef.proxy>`:: actor_proxy = MyActor.start().proxy() When reading an attribute or getting a return value from a method, you get a :class:`Future <pykka.future.Future>` object back. To get the enclosed value from the future, you must call :meth:`get() <pykka.future.Future.get>` on the returned future:: print actor_proxy.string_attribute.get() print actor_proxy.count().get() + 1 If you call a method just for it's side effects and do not care about the return value, you do not need to accept the returned future or call :meth:`get() <pykka.future.Future.get>` on the future. Simply call the method, and it will be executed concurrently with your own code:: actor_proxy.method_with_side_effect() If you want to block your own code from continuing while the other method is processing, you can use :meth:`get() <pykka.future.Future.get>` to block until it completes:: actor_proxy.method_with_side_effect().get() An example of :class:`ActorProxy` usage: .. literalinclude:: ../examples/counter.py :param actor_ref: reference to the actor to proxy :type actor_ref: :class:`pykka.actor.ActorRef` :raise: :exc:`pykka.ActorDeadError` if actor is not available """ #: The actor's :class:`pykka.actor.ActorRef` instance. actor_ref = None def __init__(self, actor_ref, attr_path=None): if not actor_ref.is_alive(): raise _ActorDeadError('%s not found' % actor_ref) self.actor_ref = actor_ref self._attr_path = attr_path or tuple() self._known_attrs = None self._actor_proxies = {} self._callable_proxies = {} def _update_attrs(self): self._known_attrs = self.actor_ref.ask( {'command': 'pykka_get_attributes'}) def __repr__(self): return '<ActorProxy for %s, attr_path=%s>' % (self.actor_ref, self._attr_path) def __dir__(self): if self._known_attrs is None: self._update_attrs() result = ['__class__'] result += list(self.__class__.__dict__.keys()) result += list(self.__dict__.keys()) result += [attr_path[0] for attr_path in list(self._known_attrs.keys())] return sorted(result) def __getattr__(self, name): """Get a field or callable from the actor.""" attr_path = self._attr_path + (name,) if self._known_attrs is None or attr_path not in self._known_attrs: self._update_attrs() attr_info = self._known_attrs.get(attr_path) if attr_info is None: raise AttributeError('%s has no attribute "%s"' % (self, name)) if attr_info['callable']: if attr_path not in self._callable_proxies: self._callable_proxies[attr_path] = _CallableProxy( self.actor_ref, attr_path) return self._callable_proxies[attr_path] elif attr_info['traversable']: if attr_path not in self._actor_proxies: self._actor_proxies[attr_path] = ActorProxy( self.actor_ref, attr_path) return self._actor_proxies[attr_path] else: message = { 'command': 'pykka_getattr', 'attr_path': attr_path, } return self.actor_ref.ask(message, block=False) def __setattr__(self, name, value): """ Set a field on the actor. Blocks until the field is set to check if any exceptions was raised. """ if name == 'actor_ref' or name.startswith('_'): return super(ActorProxy, self).__setattr__(name, value) attr_path = self._attr_path + (name,) message = { 'command': 'pykka_setattr', 'attr_path': attr_path, 'value': value, } return self.actor_ref.ask(message)
class _CallableProxy(object): """Internal helper class for proxying callables.""" def __init__(self, ref, attr_path): self.actor_ref = ref self._attr_path = attr_path def __call__(self, *args, **kwargs): message = { 'command': 'pykka_call', 'attr_path': self._attr_path, 'args': args, 'kwargs': kwargs, } return self.actor_ref.ask(message, block=False)