Creating Paste templates

Introduction

Python Paste is an extremely powerful package that isn’t just about WSGI middleware. The related document Using Entry Points to Write Plugins demonstrates how to use entry_points to create simple plugins. This document describes how to write just such a plugin for use Paste’s project template creation facility and how to add a command to Paste’s paster script.

The example task is to create a template for an imaginary content management system. The template is going to produce a project directory structure for a Python package, so we need to be able to specify a package name.

Creating The Directory Structure and Templates

The directory structure for the new project needs to look like this:

- default_project
    - +package+
        - __init__.py
        - static
            - layout
            - region
            - renderer
        - service
            - layout
                - __init__.py
            - region
                - __init__.py
            - renderer
                - __init__.py
    - setup.py_tmpl
    - setup.cfg_tmpl
    - development.ini_tmpl
    - README.txt_tmpl
    - ez_setup.py

Of course, the actual project’s directory structure might look very different. In fact the paster create command can even be used to generate directory structures which aren’t project templates — although this wasn’t what it was designed for.

When the paster create command is run, any directories with +package+ in their name will have that portion of the name replaced by a simplified package name and likewise any directories with +egg+ in their name will have that portion replaced by the name of the egg directory, although we don’t make use of that feature in this example.

All of the files with _tmpl at the end of their filenames are treated as templates and will have the variables they contain replaced automatically. All other files will remain unchanged.

Note

The small templating language used with paster create in files ending in _tmpl is described in detail in the Paste util module documentation

When specifying a package name it can include capitalisation and _ characters but it should be borne in mind that the actual name of the package will be the lowercase package name with the _ characters removed. If the package name contains an _, the egg name will contain a _ character so occasionally the +egg+ name is different to the +package+ name.

To avoid difficulty always recommend to users that they stick with package names that contain no _ characters so that the names remain unique when made lowercase.

Implementing the Code

Now that the directory structure has been defined, the next step is to implement the commands that will convert this to a ready-to-run project. The template creation commands are implemented by a class derived from paste.script.templates.Template. This is how our example appears:

from paste.script.templates import Template, var

vars = [
    var('version', 'Version (like 0.1)'),
    var('description', 'One-line description of the package'),
    var('long_description', 'Multi-line description (in reST)'),
    var('keywords', 'Space-separated keywords/tags'),
    var('author', 'Author name'),
    var('author_email', 'Author email'),
    var('url', 'URL of homepage'),
    var('license_name', 'License name'),
    var('zip_safe', 'True/False: if the package can be distributed as a .zip file',
        default=False),
]

class ArtProjectTemplate(Template):
    _template_dir = 'templates/default_project'
    summary = 'Art project template'
    vars = vars

The vars arguments can all be set at run time and will be available to be used as (in this instance) Cheetah template variables in the files which end _tmpl. For example the setup.py_tmpl file for the default_project might look like this:

from setuptools import setup, find_packages

version = ${repr(version)|"0.0"}

setup(name=${repr(project)},
    version=version,
    description="${description|nothing}",
    long_description="""\
    ${long_description|nothing}""",
    classifiers=[],
    keywords=${repr(keywords)|empty},
    author=${repr(author)|empty},
    author_email=${repr(author_email)|empty},
    url=${repr(url)|empty},
    license=${repr(license_name)|empty},
    packages=find_packages(exclude=['ez_setup']),
    include_package_data=True,
    zip_safe=${repr(bool(zip_safe))|False},
    install_requires=[
      # Extra requirements go here #
    ],
    entry_points="""
        [paste.app_factory]
        main=${package}:make_app
    """,
)

Note how the variables specified in vars earlier are used to generate the actual setup.py file.

In order to use the new templates they must be hooked up to the paster create command by means of an entry point. In the setup.py file of the project (in which created the project template is going to be stored) we need to add the following:

entry_points="""
    [paste.paster_create_template]
    art_project=art.entry.template:ArtProjectTemplate
""",

We also need to add PasteScript>=1.3 to the install_requires line.

install_requires=["PasteScript>=1.3"],

We just need to install the entry points now by running:

python setup.py develop

We should now be able to see a list of available templates with this command:

$ paster create --list-templates

Note

Windows users will need to add their Python scripts directory to their path or enter the full version of the command, similar to this:

C:\Python24\Scripts\paster.exe create --list-templates

You should see the following:

Available templates:
art_project:              Art project template
basic_package:            A basic setuptools-enabled package

There may be other projects too.

Troubleshooting

If the Art entries don’t show up, check whether it is possible to import the template.py file because any errors are simply ignored by the paster create command rather than output as a warning.

If the code is correct, the issue might be that the entry points data hasn’t been updated. Examine the Python site-packages directory and delete the Art.egg-link files, any Art*.egg files or directories and remove any entries for art from easy_install.pth (replacing Art with the name chosen for the project of course). Then re-run python setup.py develop to install the correct information.

If problems are still evident, then running the following code will print out a list of all entry points. It might help track the problem down:

import pkg_resources
    for x in pkg_resources.iter_group_name(None, None):
        print x

Using the Template

Now that the entry point is working, a new project can be created:

$ paster create --template=art TestProject

Paster will ask lots of questions based on the variables set up in vars earlier. Pressing return will cause the default to be used. The final result is a nice project template ready for people to start coding with.

Implementing Pylons Templates

If the development context is subject to a frequent need to create lots of Pylons projects, each with a slightly different setup from the standard Pylons defaults then it is probably desirable to create a customised Pylons template to use when generating projects. This can be done in exactly the way described in this document.

First, set up a new Python package, perhaps called something like CustomPylons (obviously, don’t use the Pylons name because Pylons itself is already using it). Then check out the Pylons source code and copy the pylons/templates/default_project directory into the new project as a starting point. The next stage is to add the custom vars and Template class and set up the entry points in the CustomPylons setup.py file.

After those tasks have been completed, it is then possible to create customised templates (ultimately based on the Pylons one) by using the CustomPylons package.