Decorator with optional argument to set a template

# -*- coding: utf-8 -*-

"""
Template Decorator
~~~~~~~~~~~~~~~~~~

The decorator makes the callable return a 2-tuple of the template name and the
return value (inspired by Buffet_).

The template name is an optional argument.  If not specified, the callable's
name will be used.  In both cases, the name of the callable's module will be
prepended.  This makes sense if a module represents the controller in a MVC
(model/view/controller) application structure and contains multiple callables
that represent its actions.  E. g., CherryPy_ applications are usually
structured this way.

:Copyright: 2007-2008 Jochen Kupperschmidt
:Date: 09-Jul-2008
:License: MIT

.. _Buffet:     http://projects.dowski.com/projects/buffet
.. _CherryPy:   http://cherrypy.org/
"""

def use_template(arg):
    """Decorator to specify a template."""
    if hasattr(arg, '__call__'):
        return set_template(arg)
    def wrapper(func):
        return set_template(func, arg)
    return wrapper

def set_template(func, name=None):
    """Set a template for a controller action."""
    ctrl = func.__module__.split('.')[-1]
    tpl = '%s_%s' % (ctrl, name or func.__name__)
    # Alternative: Set the template name as attribute of the callable and
    # don't touch the return value:
    #     func.template = tpl
    #     return func
    def inner(*args, **kwargs):
        return tpl, func(*args, **kwargs)
    return inner


# Usage example follows.
# It is assumed that the class is defined in a module 'blog.py'.  However, it
# is also possible to use plain functions instead of the class structure

class Root(object):

    # Returns template name 'blog_index'.
    @use_template
    def index(self):
        """Show a list of items."""

    # Returns template name 'blog_details'.
    @use_template('details')
    def view(self, id):
        """Show a single item."""