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

Added EventMetaclass #43

Closed
wants to merge 3 commits into from
Closed

Added EventMetaclass #43

wants to merge 3 commits into from

Conversation

DanielSank
Copy link

Suppose you have a class Foo which has a method .bar to which you'd like to apply the @event decorator, but that class is defined externally to your project. You're stuck subclassing Foo and re-implementing .bar with manual addition of the @event decorator. That's ok for some cases, but it's kind of ugly and can be really tedious.

In this submission I introduce a metaclass so that the @event decorator can be applied to specified methods at class construction time. A full explanation with a working example are given in the docstring of obsub.EventMetaclass, but here's the basic idea

class Foo(object):
    def bar(self, arg):
        print("bar called with argument %s"%(arg,))


class MySubclass(Foo):
    __metaclass__ = obsub.EventMetaclass
    _event_methods = ['bar']


def callback(obj, arg):
    print("callback fired with argument %s"%(arg,))

f = MySubclass()
f.bar += callback
f.bar('baz')
> bar called with argument baz
> callback fired with argument baz

This allows application of the @event decorator to methods defined in a superclass.
@milibopp
Copy link
Owner

This is a very useful feature. You should add proper tests, though. You'll certainly run into Python 2 vs 3 compatibility issues due to the change in metaclass syntax. There is a precedent for that in the test/test_signature.py module.

I am not sure how to handle the docstring here. Conveniently omitting the prompt >>> makes the test suite ignore it, but that's a dangerous thing to do, since it introduces the possibility of having disfunctional code samples in the docstring.

This raises the general issue of how to deal with cross-version compatibility. Thus I'd suggest you to get to some point where it "just works" including tests. We can proceed to a more general policy later.

options

1. Add the @event decorator in Foo. This is a bad option because it
requires us to modify someone elses class. This may not be possible or
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

elses -> else's

@coveralls
Copy link

Coverage Status

Coverage decreased (-12.07%) when pulling 2ee3990 on DanielSank:master into acf8d4f on aepsil0n:master.

@DanielSank
Copy link
Author

I may need a little help here. I don't understand how the tests are run. In my own checkout I added

if __name__ == "__main__":
    unittest.main()

but I'm clearly missing something.

@Moredread
Copy link
Collaborator

If you install the nose package you can run the tests with 'nosetests' from the base directory.

On February 22, 2014 8:43:55 PM CET, Daniel Sank notifications@github.com wrote:

I may need a little help here. I don't understand how the tests are
run. In my own checkout I added

if name == "main":
unittest.main()

but I'm clearly missing something.


Reply to this email directly or view it on GitHub:
#43 (comment)

@coldfix
Copy link
Contributor

coldfix commented Feb 23, 2014

Note: you can do python{2,3} compatible instanciation of classes with metaclass like so:

class Meta(type):
     ....

# use the metaclass to instanciate a new class with no bases () and empty clsdict {}:
Base = Meta('Base', (), {})

# Foo inherits the metaclass from Base:
class Foo(Base):
    def foo(self):
        pass

Of course you could also instanciate Foo directly, but often times this is much less convenient..

@coldfix
Copy link
Contributor

coldfix commented Feb 23, 2014

I'm not sure about the usefulness of the metaclass..

  • when having only a few events, it's hardly more typing to use the decorator and more idiomatic (explicit and simple)
  • metaclasses get messy pretty fast in scenarios with multiple inheritance (metaclass conflicts)

On the other hand that's probably the user's problem. I wouldn't use it :P, but if you want to add it, go ahead.

I have a minor comment regarding the implementation: The try: ...; except KeyError: .. can have a KeyError even if d['_event_methods'] is present (if there is an overload for getattr(base, m).__func__). So better change it to:

try:
    event_methods = clsdict['_event_methods']
except KeyError:
    raise RuntimeError("...")
else:
    for m in event_methods:
        ...

or just let the exception go unhandled (I think the KeyError is probably descriptive enough).

@milibopp
Copy link
Owner

btw, I think that the logic in the docstring is suited for testing. You just have to generalize it for Python 2.x and 3.x, e.g. as @coldfix suggested. The coverage went down, because you only defined test classes in the tests but did no instantiation and testing.

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

Successfully merging this pull request may close these issues.

None yet

5 participants