summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGES.txt24
-rw-r--r--docs/api.rst1
-rw-r--r--docs/api/scaffolds.rst13
-rw-r--r--docs/conf.py7
-rw-r--r--docs/index.rst1
-rw-r--r--docs/narr/scaffolding.rst171
-rw-r--r--docs/whatsnew-1.3.rst14
-rw-r--r--pyramid/scaffolds/__init__.py15
-rw-r--r--pyramid/scaffolds/copydir.py2
-rw-r--r--pyramid/scaffolds/template.py35
-rw-r--r--pyramid/scripts/pcreate.py5
-rw-r--r--pyramid/tests/test_scaffolds/test_template.py30
-rw-r--r--setup.py2
13 files changed, 292 insertions, 28 deletions
diff --git a/CHANGES.txt b/CHANGES.txt
index c979c4dc1..74f79e5ae 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,5 +1,5 @@
-Next release
-============
+1.3a2 (2011-12-14)
+==================
Features
--------
@@ -10,6 +10,14 @@ Features
``@view_config`` decorator that decorates a method of that class. It also
works against view configurations involving a class made imperatively.
+- Added a backwards compatibility knob to ``pcreate`` to emulate ``paster
+ create`` handling for the ``--list-templates`` option.
+
+- Changed scaffolding machinery around a bit to make it easier for people who
+ want to have extension scaffolds that can work across Pyramid 1.0.X, 1.1.X,
+ 1.2.X and 1.3.X. See the new "Creating Pyramid Scaffolds" chapter in the
+ narrative documentation for more info.
+
Documentation
-------------
@@ -18,6 +26,18 @@ Documentation
- Added API docs for ``view_defaults`` class decorator.
+- Added an API docs chapter for ``pyramid.scaffolds``.
+
+- Added a narrative docs chapter named "Creating Pyramid Scaffolds".
+
+Backwards Incompatibilities
+---------------------------
+
+- The ``template_renderer`` method of ``pyramid.scaffolds.PyramidScaffold``
+ was renamed to ``render_template``. If you were overriding it, you're a
+ bad person, because it wasn't an API before now. But we're nice so we're
+ letting you know.
+
1.3a1 (2011-12-09)
==================
diff --git a/docs/api.rst b/docs/api.rst
index 979e8f490..d510c0d27 100644
--- a/docs/api.rst
+++ b/docs/api.rst
@@ -26,6 +26,7 @@ documentation is organized alphabetically by module name.
api/renderers
api/request
api/response
+ api/scaffolds
api/scripting
api/security
api/session
diff --git a/docs/api/scaffolds.rst b/docs/api/scaffolds.rst
new file mode 100644
index 000000000..827962e19
--- /dev/null
+++ b/docs/api/scaffolds.rst
@@ -0,0 +1,13 @@
+.. _scaffolds_module:
+
+:mod:`pyramid.scaffolds`
+------------------------
+
+.. automodule:: pyramid.scaffolds
+
+ .. autoclass:: pyramid.scaffolds.Template
+ :members:
+
+ .. autoclass:: pyramid.scaffolds.PyramidTemplate
+ :members:
+
diff --git a/docs/conf.py b/docs/conf.py
index 9be5db325..5281017e7 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -80,7 +80,8 @@ copyright = '%s, Agendaless Consulting' % datetime.datetime.now().year
# other places throughout the built documents.
#
# The short X.Y version.
-version = '1.4dev'
+version = '1.3a2'
+
# The full version, including alpha/beta/rc tags.
release = version
@@ -475,7 +476,7 @@ def resig(app, what, name, obj, options, signature, return_annotation):
# -- Options for Epub output ---------------------------------------------------
# Bibliographic Dublin Core info.
-epub_title = 'The Pyramid Web Application Development Framework, Version 1.4dev'
+epub_title = 'The Pyramid Web Application Development Framework, Version 1.3'
epub_author = 'Chris McDonough'
epub_publisher = 'Agendaless Consulting'
epub_copyright = '2008-2011'
@@ -492,7 +493,7 @@ epub_scheme = 'ISBN'
epub_identifier = '0615445675'
# A unique identification for the text.
-epub_uid = 'The Pyramid Web Application Development Framework, Version 1.4dev'
+epub_uid = 'The Pyramid Web Application Development Framework, Version 1.3'
# HTML files that should be inserted before the pages created by sphinx.
# The format is a list of tuples containing the path and title.
diff --git a/docs/index.rst b/docs/index.rst
index df7a422d4..ca5f4aa30 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -93,6 +93,7 @@ Narrative documentation in chapter form explaining how to use
narr/extending
narr/advconfig
narr/extconfig
+ narr/scaffolding
narr/threadlocals
narr/zca
diff --git a/docs/narr/scaffolding.rst b/docs/narr/scaffolding.rst
new file mode 100644
index 000000000..3e7b102fd
--- /dev/null
+++ b/docs/narr/scaffolding.rst
@@ -0,0 +1,171 @@
+.. _scaffolding_chapter:
+
+Creating Pyramid Scaffolds
+==========================
+
+You can extend Pyramid by creating a :term:`scaffold` template. A scaffold
+template is useful if you'd like to distribute a customizable configuration
+of Pyramid to other users. Once you've created a scaffold, and someone has
+installed the distribution that houses the scaffold, they can use the
+``pcreate`` script to create a custom version of your scaffold's template.
+Pyramid itself uses scaffolds to allow people to bootstrap new projects. For
+example, ``pcreate -s alchemy MyStuff`` causes Pyramid to render the
+``alchemy`` scaffold template to the ``MyStuff`` directory.
+
+Basics
+------
+
+A scaffold template is just a bunch of source files and directories on disk.
+A small definition class points at this directory; it is in turn pointed at
+by a :term:`setuptools` "entry point" which registers the scaffold so it can
+be found by the ``pcreate`` command.
+
+To create a scaffold template, create a Python :term:`distribution` to house
+the scaffold which includes a ``setup.py`` that relies on the ``setuptools``
+package. See `Creating a Package
+<http://guide.python-distribute.org/creation.html>`_ for more information
+about how to do this. For the sake of example, we'll pretend the
+distribution you create is named ``CoolExtension``, and it has a package
+directory within it named ``coolextension``
+
+Once you've created the distribution put a "scaffolds" directory within your
+distribution's package directory, and create a file within that directory
+named ``__init__.py`` with something like the following:
+
+.. code-block:: python
+ :linenos:
+
+ # CoolExtension/coolextension/scaffolds/__init__.py
+
+ from pyramid.scaffolds import PyramidTemplate
+
+ class CoolExtensionTemplate(PyramidTemplate):
+ _template_dir = 'coolextension_scaffold'
+ summary = 'My cool extension'
+
+Once this is done, within the ``scaffolds`` directory, create a template
+directory. Our example used a template directory named
+``coolextension_scaffold``.
+
+As you create files and directories within the template directory, note that:
+
+- Files which have a name which are suffixed with the value ``_tmpl`` will be
+ rendered, and replacing any instance of the literal string ``{{var}}`` with
+ the string value of the variable named ``var`` provided to the scaffold.
+
+- Files and directories with filenames that contain the string ``+var+`` will
+ have that string replaced with the value of the ``var`` variable provided
+ to the scaffold.
+
+Otherwise, files and directories which live in the template directory will be
+copied directly without modification to the ``pcreate`` output location.
+
+The variables provided by the default ``PyramidTemplate`` include ``project``
+(the project name provided by the user as an argument to ``pcreate``),
+``package`` (a lowercasing and normalizing of the project name provided by
+the user), ``random_string`` (a long random string), and ``package_logger``
+(the name of the package's logger).
+
+See Pyramid's "scaffolds" package
+(https://github.com/Pylons/pyramid/tree/master/pyramid/scaffolds) for
+concrete examples of scaffold directories (``zodb``, ``alchemy``, and
+``starter``, for example).
+
+After you've created the template directory, add the following to the
+``entry_points`` value of your distribution's ``setup.py``:
+
+ [pyramid.scaffold]
+ coolextension=coolextension.scaffolds:CoolExtensionTemplate
+
+For example::
+
+ def setup(
+ ...,
+ entry_points = """\
+ [pyramid.scaffold]
+ coolextension=coolextension.scaffolds:CoolExtensionTemplate
+ """
+ )
+
+Run your distribution's ``setup.py develop`` or ``setup.py install``
+command. After that, you should be able to see your scaffolding template
+listed when you run ``pcreate -l``. It will be named ``coolextension``
+because that's the name we gave it in the entry point setup. Running
+``pcreate -s coolextension MyStuff`` will then render your scaffold to an
+output directory named ``MyStuff``.
+
+See the module documentation for :mod:`pyramid.scaffolds` for information
+about the API of the :class:`pyramid.scaffolds.PyramidScaffold` class and
+related classes. You can override methods of this class to get special
+behavior.
+
+Supporting Older Pyramid Versions
+---------------------------------
+
+Because different versions of Pyramid handled scaffolding differently, if you
+want to have extension scaffolds that can work across Pyramid 1.0.X, 1.1.X,
+1.2.X and 1.3.X, you'll need to use something like this bit of horror while
+defining your scaffold template:
+
+.. code-block:: python
+ :linenos:
+
+ try: # pyramid 1.0.X
+ # "pyramid.paster.paste_script..." doesn't exist past 1.0.X
+ from pyramid.paster import paste_script_template_renderer
+ from pyramid.paster import PyramidTemplate
+ except ImportError:
+ try: # pyramid 1.1.X, 1.2.X
+ # trying to import "paste_script_template_renderer" fails on 1.3.X
+ from pyramid.scaffolds import paste_script_template_renderer
+ from pyramid.scaffolds import PyramidTemplate
+ except ImportError: # pyramid >=1.3a2
+ paste_script_template_renderer = None
+ from pyramid.scaffolds import PyramidTemplate
+
+ class CoolExtensionTemplateTemplate(PyramidTemplate):
+ _template_dir = 'coolextension_scaffold'
+ summary = 'My cool extension'
+ template_renderer = staticmethod(paste_script_template_renderer)
+
+And then in the setup.py of the package that contains your scaffold, define
+the template as a target of both ``paste.paster_create_template`` (for
+``paster create``) and ``pyramid.scaffold`` (for ``pcreate``)::
+
+ [paste.paster_create_template]
+ coolextension=coolextension.scaffolds:CoolExtensionTemplate
+ [pyramid.scaffold]
+ coolextension=coolextension.scaffolds:CoolExtensionTemplate
+
+Doing this hideousness will allow your scaffold to work as a ``paster
+create`` target (under 1.0, 1.1, or 1.2) or as a ``pcreate`` target (under
+1.3). If an invoker tries to run ``paster create`` against a scaffold
+defined this way under 1.3, an error is raised instructing them to use
+``pcreate`` instead.
+
+If you want only to support Pyramid 1.3 only, it's much cleaner, and the API
+is stable:
+
+.. code-block:: python
+ :linenos:
+
+ from pyramid.scaffolds import PyramidTemplate
+
+ class CoolExtensionTemplate(PyramidTemplate):
+ _template_dir = 'coolextension_scaffold'
+ summary = 'My cool_extension'
+
+You only need to specify a ``paste.paster_create_template`` entry point
+target in your ``setup.py`` if you want your scaffold to be consumable by
+users of Pyramid 1.0, 1.1, or 1.2. To support only 1.3, specifying only the
+``pyramid.scaffold`` entry point is good enough. If you want to support both
+``paster create`` and ``pcreate`` (meaning you want to support Pyramid 1.2
+and some older version), you'll need to define both.
+
+Examples
+--------
+
+Existing third-party distributions which house scaffolding are available via
+:term:`PyPI`. The ``pyramid_jqm``, ``pyramid_zcml`` and ``pyramid_jinja2``
+packages house scaffolds. You can install and examine these packages to see
+how they work in the quest to develop your own scaffolding.
diff --git a/docs/whatsnew-1.3.rst b/docs/whatsnew-1.3.rst
index 28c161ad0..b0afacfe6 100644
--- a/docs/whatsnew-1.3.rst
+++ b/docs/whatsnew-1.3.rst
@@ -76,8 +76,10 @@ command::
The ``ini`` configuration file format supported by Pyramid has not changed.
As a result, Python 2-only users can install PasteScript manually and use
-``paster serve`` and ``paster create`` instead if they like. However, using
-``pserve`` and ``pcreate`` will work under both Python 2 and Python 3.
+``paster serve`` instead if they like. However, using ``pserve`` will work
+under both Python 2 and Python 3. ``pcreate`` is required to be used for
+internal Pyramid scaffolding; externally distributed scaffolding may allow
+for both ``pcreate`` and/or ``paster create``.
Analogues of ``paster pshell``, ``paster pviews`` and ``paster ptweens`` also
exist under the respective console script names ``pshell``, ``pviews``, and
@@ -101,6 +103,10 @@ actually recommended if you rely on proxying from Apache or Nginx to a
``pserve`` -invoked application. **The wsgiref server is not a production
quality server.** See :ref:`alternate_wsgi_server` for more information.
+New releases in every older major Pyramid series (1.0.2, 1.1.3, 1.2.5) also
+have the ``egg:pyramid#wsgiref`` entry point, so scaffold-writers can depend
+on it being there even in older major Pyramid versions.
+
.. warning::
Previously, paste.httpserver "helped" by converting header values that weren't
@@ -286,6 +292,10 @@ Documentation Enhancements
- A narrative documentation chapter named :ref:`using_introspection` was
added. It describes how to query the introspection system.
+- Added an API docs chapter for :mod:`pyramid.scaffolds`.
+
+- Added a narrative docs chapter named :ref:`scaffolding_chapter`.
+
Dependency Changes
------------------
diff --git a/pyramid/scaffolds/__init__.py b/pyramid/scaffolds/__init__.py
index 1906e0f51..ab2b3034a 100644
--- a/pyramid/scaffolds/__init__.py
+++ b/pyramid/scaffolds/__init__.py
@@ -3,10 +3,20 @@ import os
from pyramid.compat import native_
-from pyramid.scaffolds.template import Template
+from pyramid.scaffolds.template import Template # API
class PyramidTemplate(Template):
+ """
+ A class that can be used as a base class for Pyramid scaffolding
+ templates.
+ """
def pre(self, command, output_dir, vars):
+ """ Overrides :meth:`pyramid.scaffold.template.Template.pre`, adding
+ several variables to the default variables list (including
+ ``random_string``, and ``package_logger``). It also prevents common
+ misnamings (such as naming a package "site" or naming a package
+ logger "root".
+ """
if vars['package'] == 'site':
raise ValueError('Sorry, you may not name your package "site". '
'The package name "site" has a special meaning in '
@@ -20,6 +30,9 @@ class PyramidTemplate(Template):
return Template.pre(self, command, output_dir, vars)
def post(self, command, output_dir, vars): # pragma: no cover
+ """ Overrides :meth:`pyramid.scaffold.template.Template.post`, to
+ print "Welcome to Pyramid. Sorry for the convenience." after a
+ successful scaffolding rendering."""
self.out('Welcome to Pyramid. Sorry for the convenience.')
return Template.post(self, command, output_dir, vars)
diff --git a/pyramid/scaffolds/copydir.py b/pyramid/scaffolds/copydir.py
index c99238d33..d55ea165a 100644
--- a/pyramid/scaffolds/copydir.py
+++ b/pyramid/scaffolds/copydir.py
@@ -5,8 +5,6 @@
import os
import sys
import pkg_resources
-import cgi
-import urllib
from pyramid.compat import (
input_,
diff --git a/pyramid/scaffolds/template.py b/pyramid/scaffolds/template.py
index c7797cc1d..39d0e4b3f 100644
--- a/pyramid/scaffolds/template.py
+++ b/pyramid/scaffolds/template.py
@@ -16,13 +16,22 @@ from pyramid.scaffolds import copydir
fsenc = sys.getfilesystemencoding()
class Template(object):
+ """ Inherit from this base class and override methods to use the Pyramid
+ scaffolding system."""
copydir = copydir # for testing
_template_dir = None
def __init__(self, name):
self.name = name
- def template_renderer(self, content, vars, filename=None):
+ def render_template(self, content, vars, filename=None):
+ """ Return a bytestring representing a templated file based on the
+ input (content) and the variable names defined (vars). ``filename``
+ is used for exception reporting."""
+ # this method must not be named "template_renderer" fbo of extension
+ # scaffolds that need to work under pyramid 1.2 and 1.3, and which
+ # need to do "template_renderer =
+ # staticmethod(paste_script_template_renderer)"
content = native_(content, fsenc)
try:
return bytes_(
@@ -32,14 +41,20 @@ class Template(object):
raise
def module_dir(self):
- """Returns the module directory of this template."""
mod = sys.modules[self.__class__.__module__]
return os.path.dirname(mod.__file__)
def template_dir(self):
+ """ Return the template directory of the scaffold. By default, it
+ returns the value of ``os.path.join(self.module_dir(),
+ self._template_dir)`` (``self.module_dir()`` returns the module in
+ which your subclass has been defined). If ``self._template_dir`` is
+ a tuple this method just returns the value instead of trying to
+ construct a path. If _template_dir is a tuple, it should be a
+ 2-element tuple: ``(package_name, package_relative_path)``."""
assert self._template_dir is not None, (
"Template %r didn't set _template_dir" % self)
- if isinstance( self._template_dir, tuple):
+ if isinstance(self._template_dir, tuple):
return self._template_dir
else:
return os.path.join(self.module_dir(), self._template_dir)
@@ -78,7 +93,7 @@ class Template(object):
interactive=command.options.interactive,
overwrite=command.options.overwrite,
indent=1,
- template_renderer=self.template_renderer
+ template_renderer=self.render_template,
)
def makedirs(self, dir): # pragma: no cover
@@ -90,6 +105,18 @@ class Template(object):
def out(self, msg): # pragma: no cover
print(msg)
+ # hair for exit with usage when paster create is used under 1.3 instead
+ # of pcreate for extension scaffolds which need to support multiple
+ # versions of pyramid; the check_vars method is called by pastescript
+ # only as the result of "paster create"; pyramid doesn't use it. the
+ # required_templates tuple is required to allow it to get as far as
+ # calling check_vars.
+ required_templates = ()
+ def check_vars(self, vars, other):
+ raise RuntimeError(
+ 'Under Pyramid 1.3, you should use the "pcreate" command rather '
+ 'than "paster create"')
+
class TypeMapper(dict):
def __getitem__(self, item):
diff --git a/pyramid/scripts/pcreate.py b/pyramid/scripts/pcreate.py
index dacebd6ea..0057402b3 100644
--- a/pyramid/scripts/pcreate.py
+++ b/pyramid/scripts/pcreate.py
@@ -34,6 +34,11 @@ class PCreateCommand(object):
dest='list',
action='store_true',
help="List all available scaffold names")
+ parser.add_option('--list-templates',
+ dest='list',
+ action='store_true',
+ help=("A backwards compatibility alias for -l/--list. "
+ "List all available scaffold names."))
parser.add_option('--simulate',
dest='simulate',
action='store_true',
diff --git a/pyramid/tests/test_scaffolds/test_template.py b/pyramid/tests/test_scaffolds/test_template.py
index e674a338d..d7cf638b6 100644
--- a/pyramid/tests/test_scaffolds/test_template.py
+++ b/pyramid/tests/test_scaffolds/test_template.py
@@ -7,34 +7,34 @@ class TestTemplate(unittest.TestCase):
from pyramid.scaffolds.template import Template
return Template(name)
- def test_template_renderer_success(self):
+ def test_render_template_success(self):
inst = self._makeOne()
- result = inst.template_renderer('{{a}} {{b}}', {'a':'1', 'b':'2'})
+ result = inst.render_template('{{a}} {{b}}', {'a':'1', 'b':'2'})
self.assertEqual(result, bytes_('1 2'))
- def test_template_renderer_expr_failure(self):
+ def test_render_template_expr_failure(self):
inst = self._makeOne()
- self.assertRaises(AttributeError, inst.template_renderer,
+ self.assertRaises(AttributeError, inst.render_template,
'{{a.foo}}', {'a':'1', 'b':'2'})
- def test_template_renderer_expr_success(self):
+ def test_render_template_expr_success(self):
inst = self._makeOne()
- result = inst.template_renderer('{{a.lower()}}', {'a':'A'})
+ result = inst.render_template('{{a.lower()}}', {'a':'A'})
self.assertEqual(result, b'a')
- def test_template_renderer_expr_success_via_pipe(self):
+ def test_render_template_expr_success_via_pipe(self):
inst = self._makeOne()
- result = inst.template_renderer('{{b|c|a.lower()}}', {'a':'A'})
+ result = inst.render_template('{{b|c|a.lower()}}', {'a':'A'})
self.assertEqual(result, b'a')
- def test_template_renderer_expr_success_via_pipe2(self):
+ def test_render_template_expr_success_via_pipe2(self):
inst = self._makeOne()
- result = inst.template_renderer('{{b|a.lower()|c}}', {'a':'A'})
+ result = inst.render_template('{{b|a.lower()|c}}', {'a':'A'})
self.assertEqual(result, b'a')
- def test_template_renderer_expr_value_is_None(self):
+ def test_render_template_expr_value_is_None(self):
inst = self._makeOne()
- result = inst.template_renderer('{{a}}', {'a':None})
+ result = inst.render_template('{{a}}', {'a':None})
self.assertEqual(result, b'')
def test_module_dir(self):
@@ -83,7 +83,7 @@ class TestTemplate(unittest.TestCase):
self.assertEqual(copydir.output_dir, 'output dir')
self.assertEqual(copydir.vars, {'a':1})
self.assertEqual(copydir.kw,
- {'template_renderer':inst.template_renderer,
+ {'template_renderer':inst.render_template,
'indent':1,
'verbosity':1,
'simulate':False,
@@ -117,6 +117,10 @@ class TestTemplate(unittest.TestCase):
inst.run(command, 'output dir', {'a':1})
self.assertEqual(L, ['output dir'])
+ def test_check_vars(self):
+ inst = self._makeOne()
+ self.assertRaises(RuntimeError, inst.check_vars, 'one', 'two')
+
class DummyCopydir(object):
def copy_dir(self, template_dir, output_dir, vars, **kw):
self.template_dir = template_dir
diff --git a/setup.py b/setup.py
index de1c4ae34..3d0cfd6fd 100644
--- a/setup.py
+++ b/setup.py
@@ -56,7 +56,7 @@ if not PY3:
])
setup(name='pyramid',
- version='1.4dev',
+ version='1.3a2',
description=('The Pyramid web application development framework, a '
'Pylons project'),
long_description=README + '\n\n' + CHANGES,