Anyone know what is wrong with this code?
def paginated_instance_method(default_page_size=25):
def wrap(func):
@functools.wraps(func)
def inner(self, page=1, page_size=default_page_size, *args, **kwargs):
objects = func(self=self, *args, **kwargs)
return _paginate(objects, page, page_size)
return inner
return wrap
class Event(object):
...
@paginated_instance_method
def get_attending_users(self, *args, **kwargs):
return User.objects.filter(pk__in=self.attending_list)
I get the following error:
Traceback (most recent call last):
File "<console>", line 1, in <module>
File "/Users/zarathustra/Virtual_Envs/hinge/hinge_services/hinge/api/decorators.py", line 108, in wrap
def inner(self, page=1, page_size=default_page_size, *args, **kwargs):
File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/functools.py", line 33, in update_wrapper
setattr(wrapper, attr, getattr(wrapped, attr))
AttributeError: 'Event' object has no attribute '__name__'
The reason why I thought this would work is because, through trial and error, I got the following decorator working like a charm for classmethods:
def paginated_class_method(default_page_size=25):
def wrap(func):
@functools.wraps(func)
def inner(cls, page=1, page_size=default_page_size, *args, **kwargs):
objects = func(cls=cls, *args, **kwargs)
return _paginate(objects, page, page_size)
return inner
return wrap
paginated_instance_method
is not a decorator, it is a function that returns a decorator. So(Note the parentheses.)
This is really easy, but tricky at first view. Look at pep 318.
This is equivalent to:
You have an extra wrapper, which takes a decorator's args to use it in the wrapped functions (closure design pattern). So your decorator will look like this:
Equivalent to:
Your decorator has an extra level of indirection which is throwing things off. When you do this:
You are doing this:
That is what decorators do. Note that
paginated_instance_method
is called withget_attending_users
as its argument. That means that in your decorator, the argumentdefault_page_size
is set to the functionpaginated_instance_method
. Your decorator returns the functionwrap
, soget_attending_users
is set to thatwrap
function.Then when you then call
Event().get_attending_users()
it callswrap(self)
, whereself
is your Event instance.wrap
is expecting the argument to be a function, and tries to return a new function wrapping that function. But the argument isn't a function, it's anEvent
object, sofunctools.wrap
fails when trying to wrap it.I have a hunch that what you're trying to do is this:
That is, you want
paginated_instance_method
to take an argument. But even if you want to use the default value of that argument, you still have to actually callpaginated_instance_method
. Otherwise you just pass the method as the argument, which is not whatpaginated_instance_method
is expecting.The reason it "worked" for a classmethod is that a classmethod takes the class as the first argument, and a class (unlike an instance) does have a
__name__
attribute. However, I suspect that if you test it further you'll find it's not really doing what you want it to do, as it's still wrapping the class rather than the method.