Paste Script: Development
=========================
:author: Ian Bicking
:revision: $Rev: 7163 $
:date: $LastChangedDate: 2008-01-02 09:36:05 -0600 (Wed, 02 Jan 2008) $
.. contents::
Introduction
------------
This document is an introduction to how you can extend ``paster`` and
Paste Script for your system -- be it a framework, server setup, or
whatever else you want to do.
What Paste Script Can Do
------------------------
``paster`` is a two-level command, where the second level (e.g.,
``paster help``, ``paster create``, etc) is pluggable.
Commands are attached to `Python Eggs
`_, i.e., to the
package you distribute and someone installs. The commands are
identified using `entry points
`_.
To make your command available do something like this in your
``setup.py`` file::
from setuptools import setup
setup(...
entry_points="""
[paste.paster_command]
mycommand = mypackage.mycommand:MyCommand
[paste.global_paster_command]
myglobal = mypackage.myglobal:MyGlobalCommand
""")
This means that ``paster mycommand`` will run the ``MyCommand``
command located in the ``mypackage.mycommand`` module. Similarly with
``paster myglobal``. The distinction between these two entry points
is that the first will only be usable when ``paster`` is run inside a
project that is identified as using your project, while the second
will be globally available as a command as soon as your package is
installed.
How's the Local Thing Work?
---------------------------
So if you have a local command, how does it get enabled? If the
person is running ``paster`` inside their project directory,
``paster`` will look in ``Project_Name.egg-info/paster_plugins.txt``
which is a list of project names (the name of your package) whose
commands should be made available.
This is for frameworks, so frameworks can add commands to ``paster``
that only apply to projects that use that framework.
What Do Commands Look Like?
---------------------------
The command objects (like ``MyCommand``) are subclasses of
``paste.script.command.Command``. You can look at that class to get
an idea, but a basic outline looks like this::
from paste.script import command
class MyCommand(command.Command):
max_args = 1
min_args = 1
usage = "NAME"
summary = "Say hello!"
group_name = "My Package Name"
parser = command.Command.standard_parser(verbose=True)
parser.add_option('--goodbye',
action='store_true',
dest='goodbye',
help="Say 'Goodbye' instead")
def command(self):
name = self.args[0]
if self.verbose:
print "Got name: %r" % name
if self.options.goodbye:
print "Goodbye", name
else:
print "Hello", name
``max_args`` and ``min_args`` are used to give error messages. You
can also raise ``command.BadCommand(msg)`` if the arguments are
incorrect in some way. (Use ``None`` here to give no restriction)
The ``usage`` variable means ``paster mycommand -h`` will give a usage
of ``paster mycommand [options] NAME``. ``summary`` is used with
``paster help`` (describing your command in a short form).
``group_name`` is used to group commands together for ``paste help``
under that title.
The ``parser`` object is an `optparse
`
``OptionParser`` object. ``Command.standard_parser`` is a class
method that creates normal options, and enables options based on these
keyword (boolean) arguments: ``verbose``, ``interactive``,
``no_interactive`` (if interactive is the default), ``simulate``,
``quiet`` (undoes verbose), and ``overwrite``. You can create the
parser however you want, but using ``standard_parser()`` encourages a
consistent set of shared options across commands.
When your command is run, ``.command()`` is called. As you can see,
the options are in ``self.options`` and the positional arguments are
in ``self.args``. Some options are turned into instance variables --
especially ``self.verbose`` and ``self.simulate`` (even if you haven't
chosen to use those options, many methods expect to find some value
there, which is why they are turned into instance variables).
There are quite a few useful methods you can use in your command. See
the `Command class `_ for a
complete list. Some particulars:
``run_command(cmd, arg1, arg2, ..., cwd=os.getcwd(), capture_stderr=False)``:
Runs the command, respecting verbosity and simulation. Will raise
an error if the command doesn't exit with a 0 code.
``insert_into_file(filename, marker_name, text, indent=False)``:
Inserts a line of text into the file, looking for a marker like
``-*- marker_name -*-`` (and inserting just after it). If
``indent=True``, then the line will be indented at the same level
as the marker line.
``ensure_dir(dir, svn_add=True)``:
Ensures that the directory exists. If ``svn_add`` is true and the
parent directory has an ``.svn`` directory, add the new directory
to Subversion.
``ensure_file(filename, content, svn_add=True)``:
Ensure the file exists with the given content. Will ask the user
before overwriting a file if ``--interactive`` has been given.
Templates
---------
The other pluggable part is "templates". These are used to create new
projects. Paste Script includes one template itself:
``basic_package`` which creates a new setuptools package.
To enable, add to ``setup.py``::
setup(...
entry_points="""
[paste.paster_create_template]
framework = framework.templates:FrameworkTemplate
""")
``FrameworkTemplate`` should be a subclass of
``paste.script.templates.Template``. An easy way to do this is simply
with::
from paste.script import templates
class FrameworkTemplate(templates.Template):
egg_plugins = ['Framework']
summary = 'Template for creating a basic Framework package'
required_templates = ['basic_package']
_template_dir = 'template'
use_cheetah = True
``egg_plugins`` will add ``Framework`` to ``paste_plugins.txt`` in the
package. ``required_template`` means those template will be run
before this one (so in this case you'll have a complete package ready,
and you can just write your framework files to it). ``_template_dir``
is a module-relative directory to find your source files.
The source files are just a directory of files that will be copied
into place, potentially with variable substitutions. Three variables
are expected: ``project`` is the project name (e.g., ``Project-Name``),
``package`` is the Python package in that project (e.g.,
``projectname``) and ``egg`` is the project's egg name as generated by
setuptools (e.g., ``Project_Name``). Users can add other variables by
adding ``foo=bar`` arguments to ``paster create``.
Filenames are substituted with ``+var_name+``, e.g., ``+package+`` is
the package directory.
If a file in the template directory ends in ``_tmpl`` then it will be
substituted. If ``use_cheetah`` is true, then it's treated as a
`Cheetah `_ template. Otherwise
`string.Template `_ is
used, though full expressions are allowed in ``${expr}`` instead of
just variables.
See the `templates module `_ for
more.