Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Nicer syntax for creating/comparing messages #39

Closed
drozzy opened this issue Jul 16, 2014 · 3 comments
Closed

Nicer syntax for creating/comparing messages #39

drozzy opened this issue Jul 16, 2014 · 3 comments
Assignees
Milestone

Comments

@drozzy
Copy link

drozzy commented Jul 16, 2014

Is there any way we can create messages with some kind of class-inheriance, and have some tools for comparing them?

E.g.:

class Tick(Message):
   def __init__(self, param1, param2):
       self.param1 = param1
       self.param2 = param2

self.actor_ref.tell(Tick('1', '2'))

def on_receive(self, message):
      if message |match| Tick:
          pass

I tried doing this on my own, but doing "name" on a class does not seem to be thread safe! I got really weird errors of actors sending messages of the wrong type!

@jodal jodal added the wishlist label Aug 10, 2014
@drozzy
Copy link
Author

drozzy commented Oct 9, 2014

Issue #45 alludes to this approach. I am not sure inheritance is the right way to go.

@drozzy
Copy link
Author

drozzy commented Mar 10, 2015

Here is what I came up with, works quite nicely (same solution for issue #45):

Here is how you can patch the ThreadingActor. Of course, you would inherit from this class instead for creating all your actors:

from pykka import ThreadingActor, ActorRef


class Actor(ThreadingActor):
    """
    Actor class from which classes should inherit to create an actor.

    Adapter to the pykka actors that treats messages as instances
    of some class. If pykka's system message is encountered,
    this adapter simply passes the message on.
    """
    PAYLOAD_MARKER = '__actor.message.payload__'

    def __init__(self, *args, **kwargs):
        super(Actor, self).__init__(*args, **kwargs)
        # Override actor ref with our custom actor ref,
        # that creates a dict each time it sends a message
        self.actor_ref = ActorRefAdaptor(self)

    def _handle_receive(self, message_or_dict):
        if Actor.PAYLOAD_MARKER in message_or_dict:
            # This is our message with an instance of the object in it - pass it
            # straight to on receive function
            self.on_receive(message_or_dict[Actor.PAYLOAD_MARKER])
        else:
            # hand the message over to pykka
            super(Actor, self)._handle_receive(message_or_dict)


class ActorRefAdaptor(ActorRef):
    def tell(self, message):
        # If this is a dict message, it might be pykka's system message,
        # and we leave it as is.
        if isinstance(message, dict):
            super(ActorRefAdaptor, self).tell(message)
        else:
            # Our message is an instance of some class, but we need
            #to pass pykka a dict object - so we wrap it in one!
            super(ActorRefAdaptor, self).tell({Actor.PAYLOAD_MARKER: message})

Now use it simply as:

class MyActor(Actor):
    def on_receive(self, message):
       if isinstance(message, SomeCommand):
           print("Got command with x=%s, and y=%s!!!" % (message.x, message.y))

class SomeCommand(object):
    def __init__(self, x, y):
        self.x = x
        self.y = y

actor = MyActor.start()
actor.tell(SomeCommand(x=1, y=2))

Enjoy!

@jodal jodal added this to the v2.0 milestone Mar 3, 2019
@jodal jodal self-assigned this Mar 3, 2019
@jodal jodal closed this as completed in 97b74fb Mar 3, 2019
@jodal
Copy link
Owner

jodal commented Mar 3, 2019

With the merge of #79 that will be part of the upcoming Pykka 2.0, the example here now works without having to wrap the actor and ref in adapters:

In [1]: import pykka

In [2]: class SomeCommand(object):
   ...:     def __init__(self, x, y):
   ...:         self.x = x
   ...:         self.y = y
   ...: 

In [3]: class MyActor(pykka.ThreadingActor):
   ...:     def on_receive(self, message):
   ...:         if isinstance(message, SomeCommand):
   ...:             print('Got command with x={} and y={}'.format(message.x, message.y))
   ...: 

In [4]: actor = MyActor.start()

In [5]: actor.tell(SomeCommand(x=1, y=2))

Got command with x=1 and y=2
In [6]: 

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants