Creating your own Paster templates

Description

How to create Paster code skeleton templates to easily add your own add-on product types or code inside your add-on porduct.

Introduction

Plone CMS and Python extensively use paster code templating system to aid add-on product development.

Paster allows you to create code from code skeleton templates, automatically filling in your company name etc.

Default Plone templates are in ZopeSkel package.

  • This document tells how to create your own paster templates
  • For information how to use paster please refer to paster section in tutorials

More information

Extending ZopeSkel

First you need to create a Python egg where your templates will be contained. We use ZopeSkel's plone template, but generic Python template should do as well.

paster create -t plone gomobile.templates

Note

You do not need tests.py or configure.zcml files in the template package itself.

setup.py entries

Then we edit setup.py and add paster template entry points:

install_requires=[
    'setuptools',
    'PasteScript',
    'ZopeSkel',
    # -*- Extra requirements: -*-
],

entry_points="""
        # These will declare what templates paster create command can find
        # -*- Entry points: -*-
        [paste.paster_create_template]
        dexterity = gomobile.templates.theme:Theme
        """,

You could also have "subtemplates" with local paster commands which add more code into existing code skeletons:

[zopeskel.zopeskel_sub_template]
dexterity_content = collective.dexteritypaste.localcommands.dexterity:DexterityContent
dexterity_behavior = collective.dexteritypaste.localcommands.dexterity:DexterityBehavior
dexterity_view = collective.dexteritypaste.localcommands.dexterity:DexterityView

Entry points

Entrypoints allow different plug-in systems through using the standard Python eggs and setup.py file. Plone 3.3+ picks Plone add-ons through this way and paster command pick available templates from all available eggs this way.

More information

Template class

Paster template is defined with a class referred from the entry point. Here is an example how we extend the existing Plone template class

Variables and asking for the user input

ZopeSkel contains facilities how to ask template input from the user who is running Paster. It provides some sane way to give defaults and validate the input.

Examples

Note

ZopeSkel input definitions should work both on command line and on the web based generator.

Pre- and postcondition triggers

If you want to run special code before the templates are run and after they have successfully complete, ZopeSkel provides some logic for this.

More information

Template folder structure

All templates should go to templates folder in your ZopeSkel extension namespace.

Filenames and folder names can contain variable substitues as:

templates/yourtemplatename/+namespace_package+/

will be mapped to:

yourcompany.package/yourcompany/

Template files

Files having special _tmpl extension will have string substitution performed on then. Paster supports Cheetah templates (default) and Python string templates

Example:

The best way to get the initial template files and folders for your add-on template is to checkout some existing ZopeSkel package, like collective.dexteritypaste and export its templates folder to your own add-on template.

Note

As writing of this I am not aware of any meta-template to create paster templates. But should thing would be greatly beneficial.

Variable substitution

Simple string variable substitution is like:

from ${dotted_name} import ${portlet_filename}
from ${dotted_name}.tests.base_${portlet_filename} import TestCase

More information

Default variables

Defaulte template variables are inherited from various base classes of ZopeSkel templates. One good place to look them is basic_namespace.py template declaration.

Useful snippets:

${namespace_package}.${package}

More information

Variable preparation

You can also prepare template variables in Python code in your Paster template class's pre() method:

class Portlet(PloneSubTemplate):
    """
    A plone 3 portlet skeleton
    """
    _template_dir = 'templates/plone/portlet'
    summary = "A Plone 3 portlet"

    vars = [
      var('portlet_name', 'Portlet name (human readable)',  default="Example portlet"),
      var('portlet_type_name', 'Portlet type name (should not contain spaces)', default="ExamplePortlet"),
      var('description', 'Portlet description', default=""),
           ]

    def pre(self, command, output_dir, vars):
        """
        you can use package_namespace, package_namespace2, package
        and package_dotted_name of the parent package here. you get them
        for free in the vars argument
        """
        vars['portlet_filename'] = vars['portlet_type_name'].lower()

More information

Escaping strings

If you have any page template (*.pt) files you need to templatetize you will encounter problem that both Cheetah and Zope Page Templates use the similar string expansion syntax causing a conflict.

You can use (backslash) before dollar sign to escape it.

Example:

<script tal:attributes="src string:\${viewlet/portal_url}/++resource++${namespace_package}.${package}/theme.js" type="text/javascript"></script>

Conditions and branching

If you need to have if, for and buddies in the templates see Cheetah manual.

Example

Local commands

Local commands define insert snippets which will be injected to the existing files.

The marker for snippet injects is:

-*- extra stuff goes here -*-

You need to put it to the comment format of the file type. Example for XML would be (configure.zcml_tmpl):

<!-- -*- extra stuff goes here -*- -->

Local command injection templates have _insert in their filename extension. Then the local command injection snippet configure.zcml_insert look like:

<plone:behavior
    title="${behavior_name}"
    description="${behavior_description}"
    provides="${behavior_short_dottedinterface}"
    factory="${behavior_short_dottedadapter}"
    for="plone.dexterity.interfaces.IDexterityContent"
    />

More information

Some examples

Testing the templates

ZopeSkel provides some doctest based testing facilities to hook your templates to automatic testing facilities, mainly for the regression testing.

Examples

Developing template egg with paster and buildout.cfg

The preferred method to run paster with Plone is to have it automatically pulled in and configured for you by buildout.

develop-eggs

You need to specially mention to buildout which Python eggs are in source code form.

  • You can use develop-eggs directive
  • You can use buildout extensions designed for source code and version control management, like mr.developer.

Then you need to declare [paster] part and section in buildout.cfg:

parts =
    ...
    paster

develop-eggs =
        src/yourcompany.templates

[paster]
recipe = zc.recipe.egg
# Include your own template egg here.
# Note that ${instance} section name should be the section name
# for plone.recipe.zope2instance from your buildout.cfg
eggs =
        PasteScript
        ZopeSkel
        yourcompany.templates
        ${instance:eggs}

Rerun buildout.

Now when you run paster command it should show your custom template:

bin/paster create --list-templates
...
Available templates:
  archetype:          A Plone project that uses Archetypes content types
  basic_namespace:    A basic Python project with a namespace package
  basic_package:      A basic setuptools-enabled package
  basic_zope:         A Zope project
  gomobile_theme:     A theme for Go Mobile for Plone <---- you should see yours somewhere here
  kss_plugin:         A project for a KSS plugin

Testing the generated product

This checks that your template generates viable code. We use package called gomobiletheme.yourcompany in this examples.

Steps

Generate a product skeleton using paster in non-interactive mode

rm -rf src/gomobiletheme.yourcompany ; bin/paster create --no-interactive -v -f -o src -t gomobile_theme gomobiletheme.yourcompany

Note

Use -f switch or you might encounter problems with template inheritance.

See paster bug regarding template inheritance and -f switch.

Put the newly created add-on skeleton to buildout.cfg in develop eggs and eggs:

eggs =
        gomobiletheme.yourcompany

develop-eggs =
        src/gomobiletheme.yourcompany

Run buildout

bin/buildout

Run testrunner for the created add-on

bin/test -s gomobiletheme.yourcompany

See bin/paste create --help for other useful debug switches.