Re: Class-based view decorators

2012-02-21 Thread Tobia
Thank you all for your replies.  My search on the topic had missed
most
of the past discussion, which I'm now catching up on.

Carl Meyer wrote:
> > @method_decorator(permission_required, 'thing.change_thing')
>
> Is this really any better than
> @method_decorator(permission_required("thing.change_thing"))?
> The latter seems more explicit/clearer, and allows method_decorator to
> stay simpler.

No, of course you're right.  The nature of decorators with arguments,
which are relly decorator factories, had somehow eluded me.

Łukasz Rekucki wrote:
> The super() problem actually makes a great Python puzzle so it's easy
> to remember the dangers after solving it ;)

A funny puzzle indeed.  IMHO the root problem is Python 2's super()
requiring an explicit mention of the surrounding class, something I
never liked to begin with.  It would seem the only correct way to
augment classes are mixins.

So until a newer Django release provides mixins (or dstufft's
universal
mixin-decorator classes) for all standard functionality, which seemed
to
be the consensus in the latest big thread, I will use a small utility
to
convert decorators to mixins as needed.  I have attached it to the
issue
https://code.djangoproject.com/ticket/14512 in case someone wants to
use
it or augment the current documentation with it.

-Tobia

-- 
You received this message because you are subscribed to the Google Groups 
"Django developers" group.
To post to this group, send email to django-developers@googlegroups.com.
To unsubscribe from this group, send email to 
django-developers+unsubscr...@googlegroups.com.
For more options, visit this group at 
http://groups.google.com/group/django-developers?hl=en.



Newline stripping in templates: the dnl way

2012-02-24 Thread Tobia
Hi all,
regarding issue #2594 "Template system should handle whitespace
better" explaining the need for some kind of (optional) whitespace
stripping from the output of templates, I've been looking at the
proposed solutions and compared them to other template and macro
engines.

In particular, a historical but still widely used and general-purpose
macro engine is m4. Its own feature for controlled newline stripping
is the "dnl" reserved word, "Discard to Next Line." It works by
"chomping" all input until and including the next newline.

For example, to call a macro foo() without copying over the newline
that appears after the macro call in the template, a m4 programmer
would write:

foo('bar`, `whatever')dnl

An equivalent feature in Django templates would enable template
developers to strip newlines from specific lines, while keeping
backwards compatibility with existing templates.

So if the general idea is well-accepted, I propose the "{#" token. The
example from the issue would become:


{% for item in items %}{#
{{ item }}
{% endfor %}{#


It is already a reserved token in the template system, so it's not
going to break anything. The existing comment syntax {# ... #} is
already restricted to single-line comments, so there are no multi-line
comments that would break. Plus, I'd wager it could be implemented
quite efficiently.

What do you think?

Tobia

-- 
You received this message because you are subscribed to the Google Groups 
"Django developers" group.
To post to this group, send email to django-developers@googlegroups.com.
To unsubscribe from this group, send email to 
django-developers+unsubscr...@googlegroups.com.
For more options, visit this group at 
http://groups.google.com/group/django-developers?hl=en.



Re: Newline stripping in templates: the dnl way

2012-02-24 Thread Tobia
Tai Lee wrote:
> I don't think adding {# to the end of lines is easy to understand at a
> glance, it doesn't even convey a hint of meaning like "dnl" does

I beg to differ.  {# is already recognized by template authors as
meaning "start of comment", and they know (or should know) that it
cannot extend through more than one line.  Therefore I'd think it
intuitive that it will "eat" till the end of the line and not beyond.

Look:

Here are your subscriptions:
{% for thing in things %}{#
 - {{ thing.name }}
   You added it on {{ thing.date }}
{% endfor %}{#
you can manage your subscriptions...

Tom Evans wrote:
> I'd be strongly -1 on anything that makes template language look more
> like m4!

I'll tell you, m4 can be quite addictive once you grasp the basics! :)

> This could be addressed by having a different open/close tag for tags
> which chomp the preceeding/next character if it is a newline. Eg:
> {^ for item in folder ^}

I don't think adding new reserved characters would make the language
simpler for template authors, nor for the the template parser, nor for
the sake of backwards compatibility.  {# is already part of it.

But I can see the need to chomp whitespace before a template tag, as
well as the newline after it.

Martin J. Laubach wrote:
> For this (avoiding newlines) the currently discussed multiline tags
> would work pretty well too without adding more cruft to the template
>language:
>
> foo bar {#
> #}baz

If this can be accomplished without massive performance penalties, I
agree with you.

Maybe {# #} could be made multiline at first, with special code, while
waiting for a proper implementation of generic multiline tags.  This
would certainly be more forward-compatible than my proposal above
and it would solve more whitespace problems, if not all.

Tobia

-- 
You received this message because you are subscribed to the Google Groups 
"Django developers" group.
To post to this group, send email to django-developers@googlegroups.com.
To unsubscribe from this group, send email to 
django-developers+unsubscr...@googlegroups.com.
For more options, visit this group at 
http://groups.google.com/group/django-developers?hl=en.



Re: Newline stripping in templates: the dnl way

2012-02-27 Thread Tobia
Jonathan French wrote:
> Let me make sure I've got this right --- The situation being discussed is
> not where whitespace is insignificant and can be stripped, but where
> whitespace is important and you want to control the exact amount of it,
> e.g. plain text emails. In this case, just using stripwhitespace is not
> enough. Right?

Yes, that is the main problem.

Django templates are useful (and used) for all sorts of text-based
formats. For some of these, an exact control over whitespace is needed
throughout the entire file (eg. text/plain), while for others, control
is needed in some sensitive places (eg. element attributes in html and
xml, which according to standars are not allowed to contain literal
newlines.)

The only option currently available to control whitespace is *not to
use any* for the template syntax itself, in effect writing long one-
liners. The admin site contains several examples of such:



Yes, in this case whitespace could be added between the class="" and
id="" attributes, but the main issue remains there.

Multiline comments {# #} and/or generic multiline tags would offer a
more workable alternative than the present situation, allowing for
syntax-only whitespace in templates. But they admittedly suffer from
some serious ugliness and "denaturation" of Django templates.

After reading all the above, I can see the arguments against this:

http://groups.google.com/group/django-developers?hl=en.



Class-based view decorators

2012-02-17 Thread Tobia Conforto
Hello

As a happy user of the class-based views, I wrote a couple of higher-level 
decorators to make it easy to apply all the standard decorators to them.

The first is an updated version of method_decorator, made to accept additional 
arguments and pass them to the original decorator:

class MyView(View):
@method_decorator(never_cache)
@method_decorator(login_required, login_url='/my-login')
@method_decorator(permission_required, 'thing.change_thing')
def dispatch(self, request, *args, **kwargs):
...

The second is class_view_decorator, to apply classic view decorators to the 
newer class-based views:

@class_view_decorator(never_cache)
@class_view_decorator(login_required, login_url='/my-login')
@class_view_decorator(permission_required, 'thing.change_thing')
class MyView(View):
...

In the application I'm developing they seem to be working ok--and saved me 
quite a bit of code clutter!

If you approve of this addition, I will develop proper unit tests and 
documentation as required.

-Tobia

-- 
You received this message because you are subscribed to the Google Groups 
"Django developers" group.
To post to this group, send email to django-developers@googlegroups.com.
To unsubscribe from this group, send email to 
django-developers+unsubscr...@googlegroups.com.
For more options, visit this group at 
http://groups.google.com/group/django-developers?hl=en.

from functools import update_wrapper

def method_decorator(decorator, *dec_args, **dec_kwargs):
"""
Converts a function decorator into a method decorator, with or without
decorator arguments.

Does not support decorators with call but without arguments: @my_dec()

Example:

@method_decorator(never_cache)
@method_decorator(login_required, login_url='/my-login')
@method_decorator(permission_required, 'thing.change_thing')
def dispatch(self, request, *args, **kwargs):
...
"""
# 'func' is a function at the time it is passed to _dec, but will eventually
# be a method of the class it is defined it.
def _dec(func):
def _wrapper(self, *args, **kwargs):
if dec_args or dec_kwargs:
@decorator(*dec_args, **dec_kwargs)
def bound_func(*args2, **kwargs2):
return func(self, *args2, **kwargs2)
else:
@decorator
def bound_func(*args2, **kwargs2):
return func(self, *args2, **kwargs2)
# bound_func has the signature that 'decorator' expects i.e.  no
# 'self' argument, but it is a closure over self so it can call
# 'func' correctly.
return bound_func(*args, **kwargs)
# In case 'decorator' adds attributes to the function it decorates, we
# want to copy those. We don't have access to bound_func in this scope,
# but we can cheat by using it on a dummy function.
if dec_args or dec_kwargs:
@decorator(*dec_args, **dec_kwargs)
def dummy(*args, **kwargs):
pass
else:
@decorator
def dummy(*args, **kwargs):
pass
update_wrapper(_wrapper, dummy)
# Need to preserve any existing attributes of 'func', including the name.
update_wrapper(_wrapper, func)
return _wrapper
update_wrapper(_dec, decorator)
# Change the name to aid debugging.
_dec.__name__ = 'method_decorator(%s)' % decorator.__name__
return _dec

def class_view_decorator(view_decorator, *args, **kwargs):
"""
Converts a function decorator for regular Django views, with or without
decorator arguments, into a class decorator for class-based views.

Example:

@class_view_decorator(never_cache)
@class_view_decorator(login_required, login_url='/my-login')
@class_view_decorator(permission_required, 'thing.change_thing')
class MyView(View):
...
"""
def apply_decorator(cls):
# extend the original class and decorate its dispatch method
class Decorated(cls):
@method_decorator(view_decorator, *args, **kwargs)
def dispatch(self, *args, **kwargs):
return super(Decorated, self).dispatch(*args, **kwargs)
# preserve name and module of the class, for debugging purposes
Decorated.__module__, Decorated.__name__ = cls.__module__, cls.__name__
return Decorated
return apply_decorator