Last active
December 12, 2015 04:38
-
-
Save yaniv-aknin/4716162 to your computer and use it in GitHub Desktop.
When you read something like this, you go: "yuck!". When you write it, you're totally "dude, I'm so amazing!!1!".
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| def parse_with(parser): | |
| def decor(func): | |
| def __get__(self, instance, owner=None): | |
| return self.__class__(instance, owner) | |
| def __call__(self, *args, **kwargs): | |
| params = parser.parse_args() | |
| if self.instance: | |
| return func(self.instance, params, *args, **kwargs) | |
| return func(params, *args, **kwargs) | |
| def __init__(self, instance=None, owner=None): | |
| self.instance = instance | |
| self.owner = owner | |
| __dict__ = {"__get__": __get__, "__call__": __call__, "__init__": __init__, "__doc__": | |
| func.__doc__, "__module__": func.__module__, "__name__": func.__name__} | |
| return type(func.__name__, (object,), __dict__)() | |
| return decor |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| from omnidecorator import parse_with | |
| class MockParser(object): | |
| def parse_args(self): | |
| return 'RESULTS' | |
| @parse_with(MockParser()) | |
| def foo(result): | |
| assert result == 'RESULTS' | |
| @parse_with(MockParser()) | |
| def bar(result, argument): | |
| assert result == 'RESULTS' | |
| assert argument == 'ARGUMENT' | |
| foo() | |
| bar('ARGUMENT') | |
| class Spam(object): | |
| @parse_with(MockParser()) | |
| def foo(self, result): | |
| assert result == 'RESULTS' | |
| return self | |
| @parse_with(MockParser()) | |
| def bar(self, result, argument): | |
| assert result == 'RESULTS' | |
| assert argument == 'ARGUMENT' | |
| return self | |
| @parse_with(MockParser()) | |
| @classmethod | |
| def qux(cls, result): | |
| assert result == 'RESULTS' | |
| return cls | |
| instance = Spam() | |
| assert instance.foo() is instance | |
| assert instance.bar('ARGUMENT') is instance | |
| assert Spam.qux() is Spam |
Author
Author
Limitations of this implementation:
- when decorating
classmethodsandstaticmethods,@parse_withmust appear immediately above the@classmethod/@staticmethoddecorator - in a chain of several decorators on a method (any kind),
@parse_withmust be the first decorator at the top, or it won't be getattributed and its__get__method won't be called.
Solutions to these issues will be highly appreciated.
Neat hack, but as you imply, simply applying decorators after class method/staticmethod and having a way to explicitly tell the decorator to ignore the first parameter is rather more comprehensible than automagically ignoring it :)
Putting init last instead of first is a bit hard to read, and you also want a functools.wrap(func)() at the end of your decor implementation.
If you could explain how to use this to solve the problem of using functools.partial to define a method implementation (where the instance is still added on the left, but all new positional call args are appended on the right), that may make it's coolness more apparent.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
(p.s.: the idea is to make a decorator which alters the arguments of the decorated function and can run on a function, classmethod or instancemethod)