Reputation: 9521
I have a python class, for example:
class Book(models.Model):
enabled = models.BooleanField(default=False)
full_title = models.CharField(max_length=256)
alias = models.CharField(max_length=64)
author = models.CharField(max_length=64)
status = models.CharField(max_length=64)
@serializable
def pretty_status(self):
return [b for a, b in BOOK_STATUS_CHOICES if a == self.status][0]
The method pretty_status is decorated with @serializable.
What is the simplest and most efficient way to discover the methods in a class that have a certain decoration ? (in the above example giving: pretty_status).
Edit: Please also note that the decorator in question is custom/modifiable.
Upvotes: 3
Views: 162
Reputation: 1123420
Generally speaking, you can't. A decorator is just syntactic sugar for applying a callable. In your case the decorator syntax translates to:
def pretty_status(self):
return [b for a, b in BOOK_STATUS_CHOICES if a == self.status][0]
pretty_status = serializable(pretty_status)
That is, pretty_status
is replaced by whatever serializable()
returns. What it returns could be anything.
Now, if what serializable
returns has itself been decorated with functools.wraps()
and you are using Python 3.2 or newer, then you can see if there is a .__wrapped__
attribute on the new .pretty_status
method; it's a reference to the original wrapped function.
On earlier versions of Python, you can easily do this yourself too:
def serializable(func):
def wrapper(*args, **kw):
# ...
wrapper.__wrapped__ = func
return wrapper
You can add any number of attributes to that wrapper function, including custom attributes of your own choosing:
def serializable(func):
def wrapper(*args, **kw):
# ...
wrapper._serializable = True
return wrapper
and then test for that attribute:
if getattr(method, '_serializable', False):
print "Method decorated with the @serializable decorator"
One last thing you can do is test for that wrapper function; it'll have a .__name__
attribute that you can test against. That name might not be unique, but it is a start.
In the above sample decorator, the wrapper function is called wrapper
, so pretty_status.__name__ == 'wrapper'
will be True.
Upvotes: 2
Reputation: 880289
If you have no control over what the decorator does, then in general, you can not identify decorated methods.
However, since you can modify serializable
, then you could add an attribute to the wrapped function which you could later use to identify serialized methods:
import inspect
def serializable(func):
def wrapper(self):
pass
wrapper.serialized = True
return wrapper
class Book:
@serializable
def pretty_status(self):
pass
def foo(self):
pass
for name, member in inspect.getmembers(Book, inspect.ismethod):
if getattr(member, 'serialized', False):
print(name, member)
yields
('pretty_status', <unbound method Book.wrapper>)
Upvotes: 2
Reputation: 4182
You can't discover them directly but You can mark decorated methods with some flag.
import functools
def serializable(func):
functools.wraps(func)
def wrapper(*args, **kw):
# ...
wrapper._serializable = True
return wrapper
And then You can make metaclass for example analyse presence or absence of _serializable
attribute.
Or You can collect all wrapped methodsin decorator
import functools
DECORATED = {}
def serializable(func):
functools.wraps(func)
def wrapper(*args, **kw):
# ...
DECORATED[func.__name__] = wrapper
return wrapper
Upvotes: 0