Skip to content

Instantly share code, notes, and snippets.

@gcarothers
Created October 28, 2010 16:07
Show Gist options
  • Save gcarothers/651689 to your computer and use it in GitHub Desktop.
Save gcarothers/651689 to your computer and use it in GitHub Desktop.
Template Packages
"""Functions for doing things with template packages.
Template packages are collections of templates and code that they use for
support when rendering."""
import os.path
import os
import urlparse
import sys
from paste.script.command import Command, BadCommand
from paste.script.filemaker import FileOp
from tempita import paste_script_template_renderer
class TemplateNotFoundError(Exception):
"""Raised when a template to be rendered is not found."""
pass
def parent_package(package_name):
"""Returns the module object for the parent package of the package.
If the package is a top-level package, it returns None. "Parent" in this
case is defined as the package above it in the heirarchy.
Derived from the sample my_import in section 2.1 of the Python 2.5 Library
Reference."""
segments = package_name.split('.')
if len(segments) == 1:
return None
del segments[-1]
parent_name = '.'.join(segments)
parent = __import__(parent_name, level=0)
for segment in segments[1:]:
parent = getattr(parent, segment)
return parent
class TemplatePackages(object):
"""Represents a source of template packages."""
def __init__(self, base_package, render, debug):
self.base_package = base_package
self.debug = debug
self.render = render
@property
def template_directory(self):
"""Return the directory to search for templates from this package."""
__import__('.'.join((self.base_package.__name__, 'templates')))
return self.base_package.templates.__path__[0]
@property
def static_directory(self):
"""Return the directory to search for static files from this package."""
return os.path.join(self.base_package.__path__[0],
self.base_package.static_directory)
def template_package(self, family, name):
"""Retreive the template package with the given name from family.
Example: template_package('widgets', 'buy_buttons')"""
try:
templates_module = __import__(
'.'.join((self.base_package.__name__, 'templates', family, name)),
level=0)
templates_module = reload(self.base_package.templates)
family_module = reload(getattr(templates_module, family))
return reload(getattr(family_module, name))
except ImportError:
return None
def execute_prerender(self, template_package, **kwargs):
"""The prerender function is called to perform any necessary work before
a template can be rendered.
If no prerender is found on the template package, the next package up the
heirarchy is checked, and so on. If a template package implements a
prerender and wishes to continue using its parent's functionality, it should
do from . import prerender and call it manually."""
while template_package is not None:
if hasattr(template_package, 'prerender'):
return template_package.prerender(**kwargs)
else:
template_package = parent_package(template_package.__name__)
return {}
def choose_template(self, template_package, **kwargs):
"""Pick a template to render for template_package based on kwargs.
Should return a name relative to the location of template_package."""
raise NotImplementedError('Must subclass TemplatePackages and define a way to select templates.')
def valid_template(self, template_package):
"""Tests whether or not the given template_package is 'valid', IE, can
be chosen."""
raise NotImplementedError('Must subclass TemplatePackages and define a way to select templates.')
def all_valid_templates(self):
for (dirpath, dirnames, filenames) in os.walk(self.template_directory):
if '__init__.py' in filenames:
package_name = dirpath.replace(self.template_directory, 'templates')
package_name = package_name.replace(os.path.sep, '.')
segments = package_name.split('.')
if len(segments) == 3:
template_package = self.template_package(segments[1],
segments[2])
if self.valid_template(template_package):
yield template_package
def render_package(self, template_package, **kwargs):
"""Renders a template package, returning the result."""
kwargs.update(self.execute_prerender(template_package, **kwargs))
relative_template = self.choose_template(template_package, **kwargs)
template_path = template_package.__name__.split('.')[-2:]
template_path.append(relative_template)
return self.render(os.path.join(*template_path),
extra_vars=dict(p=template_package, **kwargs))
def render_template(self, template, **kwargs):
"""Renders a template returning the result.
The template is specified in dot notation, as family.name."""
family, name = template.split('.')
package = self.template_package(family, name)
if package is None:
raise TemplateNotFoundError("Could not find template %s" % template)
return self.render_package(package, **kwargs)
class DefaultTemplateChooser(TemplatePackages):
"""A refinement of TemplatePackages that implements a choose_template that
picks a single default template for each package."""
def choose_template(self, template_package, **kwargs):
return template_package.default_template
def valid_template(self, template_package):
return hasattr(template_package, 'default_template')
class TemplatePackageCommand(Command):
"""Create a template package with all the features needed to render.
Example usage::
your_templates% paster template_package widgets/buy_buttons
Creating your_templates/your_templates/templates/widgets/buy_buttons/__init__.py
Creating your_templates/your_templates/templates/widgets/buy_buttons/buy_buttons.mako
"""
summary = __doc__.splitlines()[0]
usage = '\n' + __doc__
min_args = 1
max_args = 1
group_name = 'template_packages'
default_verbosity = 3
parser = Command.standard_parser(simulate=True)
def command(self):
try:
file_op = FileOp(source_dir=('template_packages', 'paster_templates'))
try:
name, directory = file_op.parse_path_name_args(self.args[0])
except:
raise BadCommand('No egg_info directory was found')
base_package = file_op.find_dir('templates', True)[0]
dest = os.path.join('templates', directory, name)
file_op.template_vars.update({'name': name,});
file_op.copy_file(template='package_init.py_tmpl',
dest=dest, filename='__init__.py',
add_py=False, package=False,
template_renderer=paste_script_template_renderer)
file_op.copy_file(template='base_template.mako_tmpl',
dest=dest, filename='%s.mako' % (name), add_py=False,
template_renderer=paste_script_template_renderer)
except BadCommand, e:
raise BadCommand('An error occurred. %s' % e)
except:
msg = str(sys.exc_info()[1])
raise BadCommand('An unknown error occurred. %s' % msg)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment