Improve pecan documentation and correct intersphinx references.

Change-Id: Iac6229a2727a3c662d3fe9e83e1aa02ef648f025
This commit is contained in:
Ryan Petrello
2014-01-10 10:20:20 -05:00
parent 4931540192
commit 087ea4f699
22 changed files with 247 additions and 189 deletions

View File

@@ -1,8 +1,5 @@
.. _commands:
.. |argparse| replace:: ``argparse``
.. _argparse: http://docs.python.org/dev/library/argparse.html
Command Line Pecan
==================
@@ -15,7 +12,7 @@ Serving a Pecan App For Development
-----------------------------------
Pecan comes bundled with a lightweight WSGI development server based on
Python's :py:mod:`wsgiref.simpleserver` module.
Python's :py:mod:`wsgiref.simple_server` module.
Serving your Pecan app is as simple as invoking the ``pecan serve`` command::
@@ -161,28 +158,28 @@ Let's analyze this piece-by-piece.
Overriding the ``run`` Method
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
First, we're subclassing :class:`pecan.commands.BaseCommand` and extending
the :func:`run` method to:
First, we're subclassing :class:`~pecan.commands.base.BaseCommand` and extending
the :func:`~pecan.commands.base.BaseCommandParent.run` method to:
* Load a Pecan application - ``self.load_app()``
* Wrap it in a fake WGSI environment - ``webtest.TestApp()``
* Issue an HTTP GET request against it - ``app.get(args.path)``
* Load a Pecan application - :func:`~pecan.core.load_app`
* Wrap it in a fake WGSI environment - :class:`~webtest.app.TestApp`
* Issue an HTTP GET request against it - :meth:`~webtest.app.TestApp.get`
Defining Custom Arguments
,,,,,,,,,,,,,,,,,,,,,,,,,
The :attr:`arguments` class attribute is used to define command line arguments
specific to your custom command. You'll notice in this example that we're
*adding* to the arguments list provided by :class:`pecan.commands.BaseCommand`
*adding* to the arguments list provided by :class:`~pecan.commands.base.BaseCommand`
(which already provides an argument for the ``config_file``), rather
than overriding it entirely.
The format of the :attr:`arguments` class attribute is a :class:`tuple` of dictionaries,
with each dictionary representing an argument definition in the
same format accepted by Python's |argparse|_ module (more specifically,
:func:`argparse.ArgumentParser.add_argument`). By providing a list of arguments in
this format, the :command:`pecan` command can include your custom commands in the help
and usage output it provides.
The format of the :attr:`arguments` class attribute is a :class:`tuple` of
dictionaries, with each dictionary representing an argument definition in the
same format accepted by Python's :py:mod:`argparse` module (more specifically,
:meth:`~argparse.ArgumentParser.add_argument`). By providing a list of
arguments in this format, the :command:`pecan` command can include your custom
commands in the help and usage output it provides.
::
@@ -211,7 +208,7 @@ Registering a Custom Command
Now that you've written your custom command, youll need to tell your
distributions ``setup.py`` about its existence and reinstall. Within your
distributions ``setup.py`` file, you'll find a call to :func:`setuptools.setup`.
distributions ``setup.py`` file, you'll find a call to :func:`~setuptools.setup`.
::
@@ -225,7 +222,7 @@ distributions ``setup.py`` file, you'll find a call to :func:`setuptools.setu
)
Assuming it doesn't exist already, we'll add the ``entry_points`` argument
to the :func:`setup` call, and define a ``[pecan.command]`` definition for your custom
to the :func:`~setuptools.setup` call, and define a ``[pecan.command]`` definition for your custom
command::

View File

@@ -28,7 +28,15 @@ import os
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
extensions = ['sphinx.ext.autodoc']
extensions = ['sphinx.ext.autodoc', 'sphinx.ext.intersphinx']
intersphinx_mapping = {
'python': ('http://docs.python.org', None),
'webob': ('http://docs.webob.org/en/latest', None),
'webtest': ('http://webtest.readthedocs.org/en/latest/', None),
'beaker': ('http://beaker.readthedocs.org/en/latest/', None),
'paste': ('http://pythonpaste.org', None),
}
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']

View File

@@ -78,7 +78,8 @@ Let's look at each value and what it means:
the project root).
**debug**
Enables ``WebError`` to display tracebacks in the browser
Enables the ability to display tracebacks in the browser and interactively
debug during development.
.. warning::
@@ -149,33 +150,35 @@ Dictionary Conversion
---------------------
In certain situations you might want to deal with keys and values, but in strict
dictionary form. The :class:`Config` object has a helper method for this purpose
that will return a dictionary representation of the configuration, including nested values.
dictionary form. The :class:`~pecan.configuration.Config` object has a helper
method for this purpose that will return a dictionary representation of the
configuration, including nested values.
Below is a representation of how you can access the :func:`as_dict` method and what
it returns as a result (shortened for brevity):
Below is a representation of how you can access the
:meth:`~pecan.configuration.Config.to_dict` method and what it returns as
a result (shortened for brevity):
::
>>> from pecan import conf
>>> conf
Config({'app': Config({'errors': {}, 'template_path': '', 'static_root': 'public', [...]
>>> conf.as_dict()
>>> conf.to_dict()
{'app': {'errors': {}, 'template_path': '', 'static_root': 'public', [...]
Prefixing Dictionary Keys
-------------------------
:func:`Config.as_dict` allows you to pass an optional string argument
if you need to prefix the keys in the returned dictionary.
:func:`~pecan.configuration.Config.to_dict` allows you to pass an optional
string argument if you need to prefix the keys in the returned dictionary.
::
>>> from pecan import conf
>>> conf
Config({'app': Config({'errors': {}, 'template_path': '', 'static_root': 'public', [...]
>>> conf.as_dict('prefixed_')
>>> conf.to_dict('prefixed_')
{'prefixed_app': {'prefixed_errors': {}, 'prefixed_template_path': '', 'prefixed_static_root': 'prefixed_public', [...]

View File

@@ -117,9 +117,9 @@ Binding Within the Application
There are several approaches to wrapping your application's requests
with calls to appropriate model function calls. One approach is WSGI
middleware. We also recommend Pecan :ref:`hooks`. Pecan comes with
:class:`TransactionHook`, a hook which can be used to wrap requests in
database transactions for you. To use it, simply include it in your
project's ``app.py`` file and pass it a set of functions related to
:class:`~pecan.hooks.TransactionHook`, a hook which can be used to wrap
requests in database transactions for you. To use it, simply include it in
your project's ``app.py`` file and pass it a set of functions related to
database binding.
::
@@ -145,7 +145,7 @@ database binding.
)
In the above example, on HTTP ``POST``, ``PUT``, and ``DELETE``
requests, :class:`TransactionHook` takes care of the transaction
requests, :class:`~pecan.hooks.TransactionHook` takes care of the transaction
automatically by following these rules:
#. Before controller routing has been determined, :func:`model.start`
@@ -164,7 +164,7 @@ automatically by following these rules:
:func:`model.clear` are called.
On idempotent operations (like HTTP ``GET`` and ``HEAD`` requests),
:class:`TransactionHook` handles transactions following different
:class:`~pecan.hooks.TransactionHook` handles transactions following different
rules.
#. ``model.start_read_only()`` is called. This function should bind
@@ -175,17 +175,17 @@ rules.
#. If the controller returns successfully, ``model.clear()`` is
called.
Also note that there is a useful :func:`@after_commit` decorator provided
in :ref:`pecan_decorators`.
Also note that there is a useful :func:`~pecan.decorators.after_commit`
decorator provided in :ref:`pecan_decorators`.
Splitting Reads and Writes
--------------------------
Employing the strategy above with :class:`TransactionHook` makes it very
simple to split database reads and writes based upon HTTP methods
Employing the strategy above with :class:`~pecan.hooks.TransactionHook` makes
it very simple to split database reads and writes based upon HTTP methods
(i.e., GET/HEAD requests are read-only and would potentially be routed
to a read-only database slave, while POST/PUT/DELETE requests require
writing, and would always bind to a master database with read/write
privileges). It's also possible to extend :class:`TransactionHook` or
write your own hook implementation for more refined control over where
and when database bindings are called.
privileges). It's also possible to extend
:class:`~pecan.hooks.TransactionHook` or write your own hook implementation for
more refined control over where and when database bindings are called.

View File

@@ -9,10 +9,10 @@ not explicit instruction; deployment is usually heavily dependent upon
the needs and goals of individual applications, so your mileage will
probably vary.
.. ::
.. note::
While Pecan comes packaged with a simple server *for development use*
(``pecan serve``), using a *production-ready* server similar to the ones
(:command:`pecan serve`), using a *production-ready* server similar to the ones
described in this document is **very highly encouraged**.
Installing Pecan
@@ -21,15 +21,14 @@ Installing Pecan
A few popular options are available for installing Pecan in production
environments:
* Using `setuptools/distribute
<http://packages.python.org/distribute/setuptools.html>`_. Manage
* Using `setuptools <https://pypi.python.org/pypi/setuptools>`_. Manage
Pecan as a dependency in your project's ``setup.py`` file so that it's
installed alongside your project (e.g., ``python
/path/to/project/setup.py install``). The default Pecan project
described in :ref:`quick_start` facilitates this by including Pecan as
a dependency for your project.
* Using `pip <http://www.pip-installer.org/en/latest/requirements.html>`_.
* Using `pip <http://www.pip-installer.org/>`_.
Use ``pip freeze`` and ``pip install`` to create and install from
a ``requirements.txt`` file for your project.
@@ -73,7 +72,7 @@ examples are:
* `CherryPy <http://cherrypy.org/>`__
Generally speaking, the WSGI entry point to any Pecan application can be
generated using ``pecan.deploy``::
generated using :func:`~pecan.deploy.deploy`::
from pecan.deploy import deploy
application = deploy('/path/to/some/app/config.py')

View File

@@ -109,11 +109,11 @@ Notice that the only bit of code we added to our :class:`RootController` was::
def notfound(self):
return dict(status=404, message="test_project does not have this page")
We simply :func:`@expose` the ``notfound`` controller with the ``error.html``
template (which was conveniently generated for us and placed under
``test_project/templates/`` when we created ``test_project``). As with any
Pecan controller, we return a dictionary of variables for interpolation by the
template renderer.
We simply :func:`~pecan.decorators.expose` the ``notfound`` controller with the
``error.html`` template (which was conveniently generated for us and placed
under ``test_project/templates/`` when we created ``test_project``). As with
any Pecan controller, we return a dictionary of variables for interpolation by
the template renderer.
Now we can modify the error template, or write a brand new one to make the 404
error status page of ``test_project`` as pretty or fancy as we want.

View File

@@ -10,13 +10,17 @@ without having to write separate middleware.
Hooks allow you to execute code at key points throughout the life cycle of your request:
* :func:`on_route`: called before Pecan attempts to route a request to a controller
* :func:`~pecan.hooks.PecanHook.on_route`: called before Pecan attempts to
route a request to a controller
* :func:`before`: called after routing, but before controller code is run
* :func:`~pecan.hooks.PecanHook.before`: called after routing, but before
controller code is run
* :func:`after`: called after controller code has been run
* :func:`~pecan.hooks.PecanHook.after`: called after controller code has been
run
* :func:`on_error`: called when a request generates an exception
* :func:`~pecan.hooks.PecanHook.on_error`: called when a request generates an
exception
Implementating a Pecan Hook
---------------------------
@@ -24,9 +28,12 @@ Implementating a Pecan Hook
In the below example, a simple hook will gather some information about
the request and print it to ``stdout``.
Your hook implementation needs to import :class:`PecanHook` so it can be
used as a base class. From there, you'll need to override the
:func:`on_route`, :func:`before`, :func:`after`, or :func:`on_error` methods.
Your hook implementation needs to import :class:`~pecan.hooks.PecanHook` so it
can be used as a base class. From there, you'll want to override the
:func:`~pecan.hooks.PecanHook.on_route`, :func:`~pecan.hooks.PecanHook.before`,
:func:`~pecan.hooks.PecanHook.after`, or
:func:`~pecan.hooks.PecanHook.on_error` methods to
define behavior.
::
@@ -41,14 +48,44 @@ used as a base class. From there, you'll need to override the
print "\nmethod: \t %s" % state.request.method
print "\nresponse: \t %s" % state.response.status
:func:`on_route`, :func:`before`, and :func:`after` are each passed a shared state
object which includes useful information, such as
the request and response objects, and which controller was selected by
Pecan's routing.
:func:`~pecan.hooks.PecanHook.on_route`, :func:`~pecan.hooks.PecanHook.before`,
and :func:`~pecan.hooks.PecanHook.after` are each passed a shared
state object which includes useful information, such as the request and
response objects, and which controller was selected by Pecan's routing::
:func:`on_error` is passed a shared state object **and** the original exception. If
an :func:`on_error` handler returns a Response object, this response will be returned
to the end user and no furthur :func:`on_error` hooks will be executed.
class SimpleHook(PecanHook):
def on_route(self, state):
print "\nabout to map the URL to a Python method (controller)..."
assert state.controller is None # Routing hasn't occurred yet
assert isinstance(state.request, webob.Request)
assert isinstance(state.response, webob.Response)
assert isinstance(state.hooks, list) # A list of hooks to apply
def before(self, state):
print "\nabout to enter the controller..."
if state.request.path == '/':
#
# `state.controller` is a reference to the actual
# `@pecan.expose()`-ed controller that will be routed to
# and used to generate the response body
#
assert state.controller.__func__ is RootController.index.__func__
assert isinstance(state.request, webob.Request)
assert isinstance(state.response, webob.Response)
assert isinstance(state.hooks, list)
:func:`~pecan.hooks.PecanHook.on_error` is passed a shared state object **and**
the original exception. If an :func:`~pecan.hooks.PecanHook.on_error` handler
returns a Response object, this response will be returned to the end user and
no furthur :func:`~pecan.hooks.PecanHook.on_error` hooks will be executed::
class CustomErrorHook(PecanHook):
def on_error(self, state, exc):
if isinstance(exc, SomeExceptionType):
return webob.Response('Custom Error!', status=500)
Attaching Hooks
---------------
@@ -65,7 +102,8 @@ in your project's configuration file.
}
Hooks can also be applied selectively to controllers and their sub-controllers
using the :attr:`__hooks__` attribute on one or more controllers.
using the :attr:`__hooks__` attribute on one or more controllers and
subclassing :class:`~pecan.hooks.HookController`.
::
@@ -82,8 +120,8 @@ using the :attr:`__hooks__` attribute on one or more controllers.
print "DO SOMETHING!"
return dict()
Now that :class:`SimpleHook` is included, let's see what happens when we run
the app and browse the application from our web browser.
Now that :class:`SimpleHook` is included, let's see what happens
when we run the app and browse the application from our web browser.
::

View File

@@ -6,9 +6,10 @@ Installation
Stable Version
--------------
We recommend installing Pecan with ``pip``, but you can also try with
``easy_install``. Creating a spot in your environment where
Pecan can be isolated from other packages is best practice.
We recommend installing Pecan with `pip
<http://www.pip-installer.org/>`_, but you
can also try with :command:`easy_install`. Creating a spot in your environment
where Pecan can be isolated from other packages is best practice.
To get started with an environment for Pecan, we recommend creating a new
`virtual environment <http://www.virtualenv.org>`_ using `virtualenv

View File

@@ -1,15 +1,3 @@
.. |FileHandler| replace:: ``FileHandler``
.. _FileHandler: http://docs.python.org/dev/library/logging.handlers.html#filehandler
.. |RotatingFileHandler| replace:: ``RotatingFileHandler``
.. _RotatingFileHandler: http://docs.python.org/dev/library/logging.handlers.html#rotatingfilehandler
.. |SysLogHandler| replace:: ``SysLogHandler``
.. _SysLogHandler: http://docs.python.org/dev/library/logging.handlers.html#sysloghandler
.. |SMTPHandler| replace:: ``SMTPHandler``
.. _SMTPHandler: http://docs.python.org/dev/library/logging.handlers.html#smtphandler
.. _logging:
Logging
@@ -101,11 +89,13 @@ Logging to Files and Other Locations
Python's :py:mod:`logging` library defines a variety of handlers that assist in
writing logs to file. A few interesting ones are:
* |FileHandler|_ - used to log messages to a file on the filesystem
* |RotatingFileHandler|_ - similar to |FileHandler|_, but also rotates logs
* :class:`~logging.FileHandler` - used to log messages to a file on the filesystem
* :class:`~logging.handlers.RotatingFileHandler` - similar to
:class:`~logging.FileHandler`, but also rotates logs
periodically
* |SysLogHandler|_ - used to log messages to a UNIX syslog
* |SMTPHandler|_ - used to log messages to an email address via SMTP
* :class:`~logging.handlers.SysLogHandler` - used to log messages to a UNIX syslog
* :class:`~logging.handlers.SMTPHandler` - used to log messages to an email
address via SMTP
Using any of them is as simple as defining a new handler in your
application's ``logging`` block and assigning it to one of more loggers.
@@ -114,7 +104,7 @@ Logging Requests with Paste Translogger
---------------------------------------
`Paste <http://pythonpaste.org/>`_ (which is not included with Pecan) includes
the `TransLogger <http://pythonpaste.org/modules/translogger.html>`_ middleware
the :class:`~paste.translogger.TransLogger` middleware
for logging requests in `Apache Combined Log Format
<http://httpd.apache.org/docs/2.2/logs.html#combined>`_. Combined with
file-based logging, TransLogger can be used to create an ``access.log`` file
@@ -136,9 +126,9 @@ project's ``app.py`` as follows::
app = TransLogger(app, setup_console_handler=False)
return app
By default, ``TransLogger`` creates a logger named ``wsgi``, so you'll need to
specify a new (file-based) handler for this logger in our Pecan configuration
file::
By default, :class:`~paste.translogger.TransLogger` creates a logger named
``wsgi``, so you'll need to specify a new (file-based) handler for this logger
in our Pecan configuration file::
# myapp/config.py

View File

@@ -8,4 +8,12 @@ security into your applications.
.. automodule:: pecan.secure
:members:
:show-inheritance:
:show-inheritance:
.. autoclass:: pecan.secure.SecureControllerBase
:members:
:show-inheritance:
.. autoclass:: pecan.secure.SecureController
:members:
:show-inheritance:

View File

@@ -27,7 +27,7 @@ Go ahead and change into your newly created project directory.::
$ cd test_project
You'll want to deploy it in "development mode", such that its
available on ``sys.path``, yet can still be edited directly from its
available on :mod:`sys.path`, yet can still be edited directly from its
source distribution::
$ python setup.py develop
@@ -117,7 +117,7 @@ basic settings you need to run your Pecan application in
on, the location where your controllers and templates are stored on
disk, and the name of the directory containing any static files.
If you just run ``pecan serve``, passing ``config.py`` as the
If you just run :command:`pecan serve`, passing ``config.py`` as the
configuration file, it will bring up the development server and serve
the app::
@@ -126,7 +126,7 @@ the app::
serving on 0.0.0.0:8080, view at http://127.0.0.1:8080
The location for the configuration file and the argument itself are very
flexible--you can pass an absolute or relative path to the file.
flexible - you can pass an absolute or relative path to the file.
.. _python_based_config:
@@ -230,10 +230,11 @@ now, let's examine the sample project, controller by controller::
def index(self):
return dict()
The :func:`index` method is marked as *publicly available* via the :func:`@expose`
decorator (which in turn uses the ``index.html`` template) at the root of the
application (http://127.0.0.1:8080/), so any HTTP ``GET`` that hits the root of
your application (``/``) will be routed to this method.
The :func:`index` method is marked as *publicly available* via the
:func:`~pecan.decorators.expose` decorator (which in turn uses the
``index.html`` template) at the root of the application
(http://127.0.0.1:8080/), so any HTTP ``GET`` that hits the root of your
application (``/``) will be routed to this method.
Notice that the :func:`index` method returns a Python dictionary. This dictionary
is used as a namespace to render the specified template (``index.html``) into

View File

@@ -5,10 +5,11 @@ Reloading Automatically as Files Change
---------------------------------------
Pausing to restart your development server as you work can be interruptive, so
``pecan serve`` provides a ``--reload`` flag to make life easier.
:command:`pecan serve` provides a ``--reload`` flag to make life easier.
To provide this functionality, Pecan makes use of the Python ``watchdog``
library. You'll need to install it for development use before continuing::
To provide this functionality, Pecan makes use of the Python
`watchdog <https://pypi.python.org/pypi/watchdog>`_ library. You'll need to
install it for development use before continuing::
$ pip install watchdog
Downloading/unpacking watchdog

View File

@@ -4,9 +4,10 @@ Writing RESTful Web Services with Pecan
=======================================
If you need to write controllers to interact with objects, using the
:class:`RestController` may help speed things up. It follows the Representational
State Transfer Protocol, also known as REST, by routing the standard HTTP
verbs of ``GET``, ``POST``, ``PUT``, and ``DELETE`` to individual methods.
:class:`~pecan.rest.RestController` may help speed things up. It follows the
Representational State Transfer Protocol, also known as REST, by routing the
standard HTTP verbs of ``GET``, ``POST``, ``PUT``, and ``DELETE`` to individual
methods.
::
@@ -27,7 +28,7 @@ verbs of ``GET``, ``POST``, ``PUT``, and ``DELETE`` to individual methods.
URL Mapping
-----------
By default, the :class:`RestController` routes as follows:
By default, :class:`~pecan.rest.RestController` routes as follows:
+-----------------+--------------------------------------------------------------+--------------------------------------------+
| Method | Description | Example Method(s) / URL(s) |
@@ -57,25 +58,25 @@ By default, the :class:`RestController` routes as follows:
| | | DELETE /books/1 |
+-----------------+--------------------------------------------------------------+--------------------------------------------+
Pecan's :class:`RestController` uses the ``?_method=`` query string to
work around the lack of support for the PUT and DELETE verbs when
Pecan's :class:`~pecan.rest.RestController` uses the ``?_method=`` query string
to work around the lack of support for the PUT and DELETE verbs when
submitting forms in most current browsers.
In addition to handling REST, the :class:`RestController` also
supports the :func:`index`, :func:`_default`, and :func:`_lookup`
In addition to handling REST, the :class:`~pecan.rest.RestController` also
supports the :meth:`index`, :meth:`_default`, and :meth:`_lookup`
routing overrides.
.. warning::
If you need to override :func:`_route`, make sure to call
If you need to override :meth:`_route`, make sure to call
:func:`RestController._route` at the end of your custom method so
that the REST routing described above still occurs.
Nesting ``RestController``
---------------------------
:class:`RestController` instances can be nested so that child resources receive the
parameters necessary to look up parent resources.
:class:`~pecan.rest.RestController` instances can be nested so that child
resources receive the parameters necessary to look up parent resources.
For example::
@@ -115,16 +116,17 @@ Accessing ``/authors/1/books/2`` invokes :func:`BooksController.get` with
``author_id`` set to ``1`` and ``id`` set to ``2``.
To determine which arguments are associated with the parent resource, Pecan
looks at the :func:`get_one` then :func:`get` method signatures, in that order, in the
parent controller. If the parent resource takes a variable number of arguments,
Pecan will pass it everything up to the child resource controller name (e.g.,
``books`` in the above example).
looks at the :func:`get_one` then :func:`get` method signatures, in that order,
in the parent controller. If the parent resource takes a variable number of
arguments, Pecan will pass it everything up to the child resource controller
name (e.g., ``books`` in the above example).
Defining Custom Actions
-----------------------
In addition to the default methods defined above, you can add additional
behaviors to a :class:`RestController` by defining a special :attr:`_custom_actions`
behaviors to a :class:`~pecan.rest.RestController` by defining a special
:attr:`_custom_actions`
dictionary.
For example::

View File

@@ -74,10 +74,10 @@ Exposing Controllers
--------------------
You tell Pecan which methods in a class are publically-visible via
:func:`@expose`. If a method is *not* decorated with :func:`@expose`,
Pecan will never route a request to it. :func:`@expose` accepts three
optional parameters, some of which can impact routing and the content
type of the response body.
:func:`~pecan.decorators.expose`. If a method is *not* decorated with
:func:`~pecan.decorators.expose`, Pecan will never route a request to it.
:func:`~pecan.decorators.expose` accepts three optional parameters, some of
which can impact routing and the content type of the response body.
::
@@ -106,8 +106,8 @@ Let's look at an example using ``template`` and ``content_type``:
def hello(self):
return {'msg': 'Hello!'}
You'll notice that we called :func:`expose` three times, with different
arguments.
You'll notice that we called :func:`~pecan.decoators.expose` three times, with
different arguments.
::
@@ -143,8 +143,8 @@ Pecan's Routing Algorithm
Sometimes, the standard object-dispatch routing isn't adequate to properly
route a URL to a controller. Pecan provides several ways to short-circuit
the object-dispatch system to process URLs with more control, including the
special :func:`_lookup`, :func:`_default`, and :func:`_route` methods. Defining these
methods on your controller objects provides additional flexibility for
special :func:`_lookup`, :func:`_default`, and :func:`_route` methods. Defining
these methods on your controller objects provides additional flexibility for
processing all or part of a URL.
@@ -165,7 +165,7 @@ modifying the ``status`` attribute of the response object.
response.status = 201
return {'foo': 'bar'}
Use the utility function :func:`abort` to raise HTTP errors.
Use the utility function :func:`~pecan.core.abort` to raise HTTP errors.
::
@@ -178,8 +178,8 @@ Use the utility function :func:`abort` to raise HTTP errors.
abort(404)
:func:`abort` raises an instance of
:class:`webob.exc.WSGIHTTPException` which is used by Pecan to render
:func:`~pecan.core.abort` raises an instance of
:class:`~webob.exc.WSGIHTTPException` which is used by Pecan to render
:default response bodies for HTTP errors. This exception is stored in
:the WSGI request environ at ``pecan.original_exception``, where it
:can be accessed later in the request cycle (by, for example, other
@@ -262,9 +262,9 @@ Defining Customized Routing with ``_route``
The :func:`_route` method allows a controller to completely override the routing
mechanism of Pecan. Pecan itself uses the :func:`_route` method to implement its
:class:`RestController`. If you want to design an alternative routing system on
top of Pecan, defining a base controller class that defines a :func:`_route` method
will enable you to have total control.
:class:`~pecan.rest.RestController`. If you want to design an alternative
routing system on top of Pecan, defining a base controller class that defines
a :func:`_route` method will enable you to have total control.
Mapping Controller Arguments
@@ -359,8 +359,8 @@ Helper Functions
----------------
Pecan also provides several useful helper functions for moving between
different routes. The :func:`redirect` function allows you to issue internal or
``HTTP 302`` redirects.
different routes. The :func:`~pecan.core.redirect` function allows you to issue
internal or ``HTTP 302`` redirects.
.. seealso::

View File

@@ -11,7 +11,7 @@ authorization as you see fit.
---------------------------
You can wrap entire controller subtrees *or* individual method calls
with access controls using the :func:`secure` decorator.
with access controls using the :func:`~pecan.secure.secure` decorator.
To decorate a method, use one argument::
@@ -65,10 +65,11 @@ To secure a class, invoke with two arguments::
--------------------
Alternatively, the same functionality can also be accomplished by
subclassing Pecan's :class:`SecureController`. Implementations of
:class:`SecureController` should extend the :func:`check_permissions`
class method to return ``True`` if the user has permissions to the
controller branch and ``False`` if they do not.
subclassing Pecan's :class:`~pecan.secure.SecureController`. Implementations of
:class:`~pecan.secure.SecureController` should extend the
:meth:`~pecan.secure.SecureControllerBase.check_permissions` class method to
return ``True`` if the user has permissions to the controller branch and
``False`` if they do not.
::
@@ -110,31 +111,32 @@ controller branch and ``False`` if they do not.
unclassified = unlocked(UnclassifiedController())
Also note the use of the :func:`@unlocked` decorator in the above example, which
can be used similarly to explicitly unlock a controller for public access
without any security checks.
Also note the use of the :func:`~pecan.secure.unlocked` decorator in the above
example, which can be used similarly to explicitly unlock a controller for
public access without any security checks.
Writing Authentication/Authorization Methods
--------------------------------------------
The :func:`check_permissions` method should be used to determine user
authentication and authorization. The code you implement here could range
from simple session assertions (the existing user is authenticated as an
administrator) to connecting to an LDAP service.
The :meth:`~pecan.secure.SecureControllerBase.check_permissions` method should
be used to determine user authentication and authorization. The code you
implement here could range from simple session assertions (the existing user is
authenticated as an administrator) to connecting to an LDAP service.
More on ``secure``
------------------
The :func:`secure` method has several advanced uses that allow you to create
robust security policies for your application.
The :func:`~pecan.secure.secure` method has several advanced uses that allow
you to create robust security policies for your application.
First, you can pass via a string the name of either a class method or an
instance method of the controller to use as the :func:`check_permission` method.
Instance methods are particularly useful if you wish to authorize access to
attributes of a model instance. Consider the following example
of a basic virtual filesystem.
instance method of the controller to use as the
:meth:`~pecan.secure.SecureControllerBase.check_permissions` method. Instance
methods are particularly useful if you wish to authorize access to attributes
of a model instance. Consider the following example of a basic virtual
filesystem.
::
@@ -170,7 +172,7 @@ of a basic virtual filesystem.
return FileController(name), remainder
The :func:`secure` method also accepts a function argument. When
The :func:`~pecan.secure.secure` method also accepts a function argument. When
passing a function, make sure that the function is imported from another
file or defined in the same file before the class definition, otherwise
you will likely get error during module import.
@@ -189,11 +191,11 @@ you will likely get error during module import.
return 'Logged in'
You can also use the :func:`secure` method to change the behavior of a
:class:`SecureController`. Decorating a method or wrapping a subcontroller tells
Pecan to use another security function other than the default controller
method. This is useful for situations where you want a different level or
type of security.
You can also use the :func:`~pecan.secure.secure` method to change the behavior
of a :class:`~pecan.secure.SecureController`. Decorating a method or wrapping
a subcontroller tells Pecan to use another security function other than the
default controller method. This is useful for situations where you want
a different level or type of security.
::
@@ -230,8 +232,10 @@ Multiple Secure Controllers
Secure controllers can be nested to provide increasing levels of
security on subcontrollers. In the example below, when a request is
made for ``/admin/index/``, Pecan first calls
:func:`check_permissions` on the :class:`RootController` and then
calls :func:`check_permissions` on the :class:`AdminController`.
:func:`~pecan.secure.SecureControllerBase.check_permissions` on the
:class:`RootController` and then
calls :func:`~pecan.secure.SecureControllerBase.check_permissions` on the
:class:`AdminController`.
::

View File

@@ -17,7 +17,7 @@ There are several approaches that can be taken to set up session management.
One approach is WSGI middleware. Another is Pecan :ref:`hooks`.
Here's an example of wrapping your WSGI application with Beaker's
:class:`SessionMiddleware` in your project's ``app.py``.
:class:`~beaker.middleware.SessionMiddleware` in your project's ``app.py``.
::

View File

@@ -35,11 +35,11 @@ configuration::
Using Template Renderers
------------------------
:py:mod:`pecan.decorators` defines a decorator called :func:`@expose`, which
is used to flag a method as a public controller. The :func:`@expose`
decorator takes a ``template`` argument, which can be used to specify
the path to the template file to use for the controller method being
exposed.
:py:mod:`pecan.decorators` defines a decorator called
:func:`~pecan.decorators.expose`, which is used to flag a method as a public
controller. The :func:`~pecan.decorators.expose` decorator takes a ``template``
argument, which can be used to specify the path to the template file to use for
the controller method being exposed.
::
@@ -48,8 +48,8 @@ exposed.
def index(self):
return dict(message='I am a mako template')
:func:`@expose` will use the default template engine unless the path
is prefixed by another renderer name.
:func:`~pecan.decorators.expose` will use the default template engine unless
the path is prefixed by another renderer name.
::
@@ -67,11 +67,11 @@ is prefixed by another renderer name.
Overriding Templates
--------------------
:func:`override_template` allows you to override the template set for
a controller method when it is exposed. When
:func:`override_template` is called within the body of the controller
method, it changes the template that will be used for that invocation
of the method.
:func:`~pecan.core.override_template` allows you to override the template set
for a controller method when it is exposed. When
:func:`~pecan.core.override_template` is called within the body of the
controller method, it changes the template that will be used for that
invocation of the method.
::
@@ -85,9 +85,9 @@ of the method.
Manual Rendering
----------------
:func:`render` allows you to manually render output using the Pecan
:func:`~pecan.core.render` allows you to manually render output using the Pecan
templating framework. Pass the template path and values to go into the
template, and :func:`render` returns the rendered output as text.
template, and :func:`~pecan.core.render` returns the rendered output as text.
::

View File

@@ -26,7 +26,7 @@ source code is tested.
A healthy suite of tests combines **unit tests** with **functional tests**. In
the context of a Pecan application, functional tests can be written with the
help of the ``WebTest`` library. In this way, it is possible to write tests
help of the :mod:`webtest` library. In this way, it is possible to write tests
that verify the behavior of an HTTP request life cycle from the controller
routing down to the HTTP response. The following is an example that is
similar to the one included with Pecan's quickstart project.
@@ -57,7 +57,8 @@ similar to the one included with Pecan's quickstart project.
The testing utility included with Pecan, :func:`pecan.testing.load_test_app`, can
be passed a file path representing a Pecan configuration file, and will return
an instance of the application, wrapped in a :class:`webtest.TestApp` environment.
an instance of the application, wrapped in a :class:`~webtest.app.TestApp`
environment.
From here, it's possible to extend the :class:`FunctionalTest` base class and write
tests that issue simulated HTTP requests.
@@ -73,16 +74,16 @@ tests that issue simulated HTTP requests.
.. seealso::
See the `WebTest <http://pythonpaste.org/webtest/>`_ documentation
See the :mod:`webtest` documentation
for further information about the methods available to a
``webtest.TestApp`` instance.
:class:`~webtest.app.TestApp` instance.
Special Testing Variables
-------------------------
Sometimes it's not enough to make assertions about the response body of certain
requests. To aid in inspection, Pecan applications provide a special set of
"testing variables" to any :class:`webtest.TestResponse` object.
"testing variables" to any :class:`~webtest.response.TestResponse` object.
Let's suppose that your Pecan applicaton had some controller which took a
``name`` as an optional argument in the URL.

View File

@@ -154,10 +154,13 @@ class BaseCommandParent(object):
},)
def run(self, args):
"""To be implemented by subclasses."""
self.args = args
def load_app(self):
from pecan import load_app
return load_app(self.args.config_file)
BaseCommand = BaseCommandMeta('BaseCommand', (BaseCommandParent,), {})
BaseCommand = BaseCommandMeta('BaseCommand', (BaseCommandParent,), {
'__doc__': BaseCommandParent.__doc__
})

View File

@@ -180,7 +180,7 @@ class Pecan(object):
:param template_path: A relative file system path (from the project root)
where template files live. Defaults to 'templates'.
:param hooks: A callable which returns a list of
:class:`pecan.hooks.PecanHook`s
:class:`pecan.hooks.PecanHook`
:param custom_renderers: Custom renderer objects, as a dictionary keyed
by engine name.
:param extra_template_vars: Any variables to inject into the template

View File

@@ -40,15 +40,13 @@ class HookControllerMeta(type):
walk_controller(cls, cls, dict_.get('__hooks__', []))
'''
A base class for controllers that would like to specify hooks on
their controller methods. Simply create a list of hook objects
called ``__hooks__`` as a member of the controller's namespace.
'''
HookController = HookControllerMeta(
'HookController',
(object,),
{}
{'__doc__': ("A base class for controllers that would like to specify "
"hooks on their controller methods. Simply create a list "
"of hook objects called ``__hooks__`` as a class attribute "
"of your controller.")}
)

View File

@@ -178,13 +178,17 @@ class SecureControllerBase(object):
@classmethod
def check_permissions(cls):
"""
Returns `True` or `False` to grant access. Implemented in subclasses
of :class:`SecureController`.
"""
return False
SecureController = SecureControllerMeta(
'SecureController',
(SecureControllerBase,),
{}
{'__doc__': SecureControllerMeta.__doc__}
)