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

Possible to use @ee.on() decorator on class method? #81

Closed
drmikecrowe opened this issue Mar 7, 2021 · 3 comments · Fixed by #84
Closed

Possible to use @ee.on() decorator on class method? #81

drmikecrowe opened this issue Mar 7, 2021 · 3 comments · Fixed by #84

Comments

@drmikecrowe
Copy link

I think the answer to this is No, but wanted to check and see:

I have a class with:

    async init(self):
        ee.on(ActionList.A_DOWNLOAD, self.handle_action_A_DOWNLOAD)
        # ...

    async def handle_action_A_DOWNLOAD(self, event: Event):
        #blah blah

Is there any way to decorate the task directly?

    async init(self):
        # ...

    @ee.on(ActionList.A_DOWNLOAD)
    async def handle_action_A_DOWNLOAD(self, event: Event):
        #blah blah

In my other module, I'm emitting as follows:

msg = DownloadMessage(...)
ee.emit(ActionList.A_DOWNLOAD, msg)

Interestingly, when I use the decorator as above the self argument is my other module, and the msg parameter is missing

@jfhbrook
Copy link
Owner

@ee.on has no way of knowing what the "self" argument should be (nor even that it's in the call signature), so it gets confused when you try to use it on a method. In other words, the short answer is no.

What you're doing seems like the most straightforward way to do this, but we might be able to make it easier if we collect the events and methods we want during class definition and then apply the events later. Maybe we have a helper class like:

class MethodListeners:
    def __init__(self):
        self._events = []

    def on(self, event):
        def decorator(method):
            self._events.append((event, method.__name__))
            return method

        return decorator

    def apply(self, o, ee):
        for event, method in self._events:
            ee.on(event, getattr(o, method))


listeners = MethodListeners()


class Downloader:
    def __init__(self):
        self.ee = EventEmitter()
        listeners.apply(self)

    @listeners.on("download")
    def on_download(self, event):
        ...

No guarantees that this will work out of the box, and I'm sure it's possible to iterate on this into something more helpful. If anyone gives this strategy a shot and they come up with an API they don't hate, go ahead and send it along in a merge request. Otherwise maybe I'll arrive at the right answer someday.

@jfhbrook jfhbrook reopened this Aug 14, 2021
@jfhbrook
Copy link
Owner

Another idea is to do a trick similar to attrs and wrap this in a class decorator:

from pyee.cls import evented, on

@evented
class Downloader:
    @on("download")
    def download_handler(self, download):
        ....

Basically @on would lazily create a listeners object and @evented would add __init__ behavior that instantiates and configures the EE and clean up the listeners object.

@jfhbrook
Copy link
Owner

I have a PR implementing this:

#84

Feel free to take it for a spin.

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

Successfully merging a pull request may close this issue.

2 participants