Python decorator syntax limitations

J

Jonathan S

Hi all,
The following is what I want to do, but this results in a syntax
error:


@news_page('template.html').lookup(News, 'news_id', 'news')
def view(request, group, news):
pass


What does work is the equivalent old way of doing decorating:


def view(request, group, news):
pass
view = news_page('template.html').lookup(News, 'news_id', 'news')
(view)



Any suggestions? I have my reasons for doing this, (news_page is a
class, and __call__ is used to wrap the template.)
I'm sure this is a limitation in the syntax, but would parenthesis
somewhere help?

Thanks!
Jonathan
 
S

Steve Howell

Hi all,
The following is what I want to do, but this results in a syntax
error:

@news_page('template.html').lookup(News, 'news_id', 'news')
def view(request, group, news):
    pass

What does work is the equivalent old way of doing decorating:

def view(request, group, news):
    pass
view = news_page('template.html').lookup(News, 'news_id', 'news')
(view)

Any suggestions? I have my reasons for doing this, (news_page is a
class, and __call__ is used to wrap the template.)
I'm sure this is a limitation in the syntax, but would parenthesis
somewhere help?


You might want to consider doing something like this:

def lookup_view(page_class, template, class_, id_, label):
def decorator(func):
return page_class(template).lookup(class_, id_, label)
(func)
return decorator

@lookup_view(news_page, 'template.html', News, 'news_id', 'news')
def view(...):
pass
 
L

Lie Ryan

Any suggestions? I have my reasons for doing this, (news_page is a
class, and __call__ is used to wrap the template.)
I'm sure this is a limitation in the syntax, but would parenthesis
somewhere help?

The restriction[1] is put in there since Guido has a "gut feeling" that
allowing arbitrary expression in decorator syntax severely harms
readability.
http://mail.python.org/pipermail/python-dev/2004-August/046711.html

A workaround is to put an assignment above it:

nplookup = news_page('template.html').lookup
@nplookup(News, 'news_id', 'news')
def view(request, group, news):
pass

If you are sure you can put up a convincing argument for lifting this
restriction, and you are willing to put some time arguing, you are
welcome to start a thread in the python-dev mailing list. Be sure to
read about previous discussions, as repeating arguments wouldn't change
anything.

[1] there is a subtle difference between "restriction" and "limitation"
 
P

Peter Otten

Jonathan said:
Hi all,
The following is what I want to do, but this results in a syntax
error:


@news_page('template.html').lookup(News, 'news_id', 'news')
def view(request, group, news):
pass


What does work is the equivalent old way of doing decorating:


def view(request, group, news):
pass
view = news_page('template.html').lookup(News, 'news_id', 'news')
(view)



Any suggestions? I have my reasons for doing this, (news_page is a
class, and __call__ is used to wrap the template.)
I'm sure this is a limitation in the syntax, but would parenthesis
somewhere help?

@apply(lambda:news_page('template.html').lookup(News, 'news_id', 'news'))
def view(request, group, news):
pass

But I'd go with a dedicated helper function.

Peter
 
A

Aahz

If you are sure you can put up a convincing argument for lifting this
restriction, and you are willing to put some time arguing, you are
welcome to start a thread in the python-dev mailing list. Be sure to
read about previous discussions, as repeating arguments wouldn't change
anything.

While I haven't been reading python-dev in recent months, I'm pretty sure
the moratorium makes the only appropriate place for this python-ideas,
and even there people are not likely to invest much time:

http://www.python.org/dev/peps/pep-3003/
 
J

Jonathan S

Thanks a lot, all of you! This was really helpful. (or at least give
me the inspiration I needed to finish it.)

I'm sure this is a use case where most other options are less readable
than the chain of methods in the decorator.
In this use case, I had a lot of Django views to which access
permissions had to be attached. Chaining the permissions was in my
opinion the easiest way to go but appeared not to work because Guido
had a gut feeling about that.
Using multiple decorators in the usual way (like every following wraps
the previous) does not work, because these decorators need to access
the same authentication class. (little hard to explain, but take from
me that I had a case where it didn't work.)
Placing all possible options in the constructor method of the
decorator like Steve Howell proposed would work but is ugly.

This is what I made. I'll upgrade my news_page decorator to work in
the following way. (Applying the news_page decorator to 'view', will
turn it into a class, but make it callable buy wrapping the actual
view in __call__. So we can still apply the other methods to the view.

@news_page('template.html')
def view(request, group, news):
...
pass
view.lookup(News, 'news_id', 'news')
view.require_administrator()


That is still rather ugly, because the 'lookup' option is appended
behind the view. I kept playing with the code and came to the
following:


@require_administrator
@do_lookup(News, 'news_id', 'news)
@news_page('template.html')
def view(request, group, news):
...
pass


Where 'do_lookup' and 'require_administrator' passes the method, but
sets some parameters. So, 'do_lookup' can access class members of
news_page. It would look like:

def do_lookup(*lookup_options):
def set_options(news_page_view):
news_page_view.lookup(*lookup_options)
return news_page_view
return set_options

def require_administrator(news_page_view):
news_page_view.require_administrator()
return news_page_view


Maybe, I'll join the discussion later on, when I have time to read all
the conversations and write down good arguments.
But, honestly, I'm satisfied with the last result.
Have a nice day!
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
474,177
Messages
2,570,952
Members
47,506
Latest member
tomiy16522

Latest Threads

Top