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

utility for handler decorator #91

Open
spaceone opened this issue Jun 11, 2015 · 12 comments · May be fixed by #311
Open

utility for handler decorator #91

spaceone opened this issue Jun 11, 2015 · 12 comments · May be fixed by #311
Milestone

Comments

@spaceone
Copy link
Contributor

spaceone commented Jun 11, 2015

We should enhance the possibility to write decorators for handler-functions, as @handler copies meta information from the function and does arginspect() to check if the first argument is called event - in that case the event is passed as argument.

If the function gets decorated these meta information doesn't match the real underlying implementation.

We should provide a utility which developers can use so that they don't need to care about these internals when they want to write additional decorators.

Any further ideas on this? I have some ideas in mind which I will implement but I would like to hear what you think.

@spaceone spaceone changed the title decorator for handlers utility for handler decorator Jun 11, 2015
@prologic
Copy link
Member

Hmm; I'm not quite sure what you mean? What ideas do you have in mind?

Do you mean a decorator to make writing custom decoraotrs that create event handlers a bit easier?

@spaceone
Copy link
Contributor Author

The problem is that if you want to write a decorator which is applied after @handler one need to

    1. copy the function metadata of the original function to the decorated function
    1. copy the code which does the arginspect to know if one have to pass the 'event' to the next function.

@prologic
Copy link
Member

Ahh I see; so what are you ideas then?

Turn @handler into a class that can be subclasses?

How else can decorators be extended in this way?

@prologic
Copy link
Member

If I'm not mistaken I think you can do something like this:

def mydecorator(*args, **kwargS):
    @handler(...)
    def wrapper(f):
        return f(...)
    return wrapper

I think :)

@spaceone
Copy link
Contributor Author

Ì think a class is not necessary?!
I would add a function handler_decorator(func) which does the necessary arginspect and use this instead of doing the arginspect in manager.py

@prologic
Copy link
Member

e.g: circuits.web.controller.expose

@spaceone
Copy link
Contributor Author

circuits.web.controller.expose is the best example for the redundant things i want to get rid of:

 if not getattr(f, "event", False):
    return f(self, *args, **kwargs)
else:
    return f(self, event, *args, **kwargs)

@prologic
Copy link
Member

Yeah I agree; this is less to do with function parameter inspection and more to do with:

Do we need to send the event object?

I'm not sure how to solve this per se (yet)

@prologic
Copy link
Member

Got any ideas around this? I haven't really looked much further into this (yet).

@prologic prologic modified the milestone: 3.3 Aug 13, 2015
@spaceone
Copy link
Contributor Author

My description skills weren't that good in 2015.

I explain again with a sample implementation:

class Foo(BaseComponent):
    @handler('foo')
    @my_decorator
    def on_foo(self, event):
       pass

@handler() must always be the outside decorator, as it adds sets attributes at the function which makes them a handler.
on_foo() can in circuits either take event or not.

So my_decorator has to implement the same logic as handler and circuits.core.manager.Manager._dispatcher() - knowing implementation details of circuits (unlikely: and watching on every release if something changed):

from circuits.tools import getargspec
def my_decorator(func):
    @functool.wraps(func)
    def decorated(self, event, *args, **kwargs):
        args = getargspec(func)[0]
        if args and args[0] == "self":
            del args[0]
        with_event = args and args[0] == "event"
        if with_event:
            return func(self, event, *args, **kwargs)
        return func(self, *args, **kwargs)

    return decorated

And my implementation must always take the event argument (OK, probably cannot be prevented).

@spaceone
Copy link
Contributor Author

If I'm not mistaken I think you can do something like this:

def mydecorator(*args, **kwargS):
    @handler(...)
    def wrapper(f):
        return f(...)
    return wrapper

I think :)

  1. I want to chain multiple own decorators
  2. I don't want to turn the function into an handler already - because I don't know the other arguments passed to @handler
  3. If I do this, I still have to evaluate the event.event flag and call the function differently.

@spaceone
Copy link
Contributor Author

Ahh I see; so what are you ideas then?

Turn @handler into a class that can be subclasses?

How else can decorators be extended in this way?

No, turning it into a class is optional. Making a subclass is not my need, see the last comment.
But of course creating a class or extra functions is okay.

in PR #311 I changed the handler decorator into a class, providing .call() and .decorate().

This would simplify my example to:

def my_decorator(func):
    handler.decorate(func)
    @functool.wraps(func)
    def decorated(self, event, *args, **kwargs):
        return handler.call(func, self, event, *args, **kwargs)
    return decorated

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

Successfully merging a pull request may close this issue.

2 participants