summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRyan Petrello <lists@ryanpetrello.com>2014-01-10 10:20:20 -0500
committerRyan Petrello <lists@ryanpetrello.com>2014-01-19 17:49:11 -0500
commit087ea4f6993c7b2f5f66fff52d46fd318e20b330 (patch)
tree2900c5c20265602b385a8257b29c5345f002a622
parent49315401929385f462c73947d7ea899688bd396e (diff)
downloadpecan-087ea4f6993c7b2f5f66fff52d46fd318e20b330.tar.gz
Improve pecan documentation and correct intersphinx references.
Change-Id: Iac6229a2727a3c662d3fe9e83e1aa02ef648f025
-rw-r--r--docs/source/commands.rst33
-rw-r--r--docs/source/conf.py10
-rw-r--r--docs/source/configuration.rst21
-rw-r--r--docs/source/databases.rst24
-rw-r--r--docs/source/deployment.rst11
-rw-r--r--docs/source/errors.rst10
-rw-r--r--docs/source/hooks.rst72
-rw-r--r--docs/source/installation.rst7
-rw-r--r--docs/source/logging.rst30
-rw-r--r--docs/source/pecan_secure.rst10
-rw-r--r--docs/source/quick_start.rst15
-rw-r--r--docs/source/reload.rst7
-rw-r--r--docs/source/rest.rst34
-rw-r--r--docs/source/routing.rst32
-rw-r--r--docs/source/secure_controller.rst56
-rw-r--r--docs/source/sessions.rst2
-rw-r--r--docs/source/templates.rst28
-rw-r--r--docs/source/testing.rst11
-rw-r--r--pecan/commands/base.py5
-rw-r--r--pecan/core.py2
-rw-r--r--pecan/hooks.py10
-rw-r--r--pecan/secure.py6
22 files changed, 247 insertions, 189 deletions
diff --git a/docs/source/commands.rst b/docs/source/commands.rst
index 0e75583..fd9836f 100644
--- a/docs/source/commands.rst
+++ b/docs/source/commands.rst
@@ -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, you’ll need to tell your
distribution’s ``setup.py`` about its existence and reinstall. Within your
-distribution’s ``setup.py`` file, you'll find a call to :func:`setuptools.setup`.
+distribution’s ``setup.py`` file, you'll find a call to :func:`~setuptools.setup`.
::
@@ -225,7 +222,7 @@ distribution’s ``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::
diff --git a/docs/source/conf.py b/docs/source/conf.py
index 4b89f31..9d71241 100644
--- a/docs/source/conf.py
+++ b/docs/source/conf.py
@@ -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']
diff --git a/docs/source/configuration.rst b/docs/source/configuration.rst
index ea4d29d..1118c72 100644
--- a/docs/source/configuration.rst
+++ b/docs/source/configuration.rst
@@ -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', [...]
diff --git a/docs/source/databases.rst b/docs/source/databases.rst
index 8e92c4c..d4d23dc 100644
--- a/docs/source/databases.rst
+++ b/docs/source/databases.rst
@@ -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.
diff --git a/docs/source/deployment.rst b/docs/source/deployment.rst
index fceb32d..b6ab953 100644
--- a/docs/source/deployment.rst
+++ b/docs/source/deployment.rst
@@ -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')
diff --git a/docs/source/errors.rst b/docs/source/errors.rst
index 2b78b02..6e38a88 100644
--- a/docs/source/errors.rst
+++ b/docs/source/errors.rst
@@ -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.
diff --git a/docs/source/hooks.rst b/docs/source/hooks.rst
index b7c01ce..7796189 100644
--- a/docs/source/hooks.rst
+++ b/docs/source/hooks.rst
@@ -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.
::
diff --git a/docs/source/installation.rst b/docs/source/installation.rst
index eae40e8..fb43491 100644
--- a/docs/source/installation.rst
+++ b/docs/source/installation.rst
@@ -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
diff --git a/docs/source/logging.rst b/docs/source/logging.rst
index 2b6985a..7494a2e 100644
--- a/docs/source/logging.rst
+++ b/docs/source/logging.rst
@@ -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
diff --git a/docs/source/pecan_secure.rst b/docs/source/pecan_secure.rst
index 903845e..f2c288a 100644
--- a/docs/source/pecan_secure.rst
+++ b/docs/source/pecan_secure.rst
@@ -8,4 +8,12 @@ security into your applications.
.. automodule:: pecan.secure
:members:
- :show-inheritance: \ No newline at end of file
+ :show-inheritance:
+
+.. autoclass:: pecan.secure.SecureControllerBase
+ :members:
+ :show-inheritance:
+
+.. autoclass:: pecan.secure.SecureController
+ :members:
+ :show-inheritance:
diff --git a/docs/source/quick_start.rst b/docs/source/quick_start.rst
index 28b14f3..45915fa 100644
--- a/docs/source/quick_start.rst
+++ b/docs/source/quick_start.rst
@@ -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 it’s
-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
diff --git a/docs/source/reload.rst b/docs/source/reload.rst
index 923043e..dfae883 100644
--- a/docs/source/reload.rst
+++ b/docs/source/reload.rst
@@ -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
diff --git a/docs/source/rest.rst b/docs/source/rest.rst
index c0f31ee..8432a86 100644
--- a/docs/source/rest.rst
+++ b/docs/source/rest.rst
@@ -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::
diff --git a/docs/source/routing.rst b/docs/source/routing.rst
index 3a40241..da8c785 100644
--- a/docs/source/routing.rst
+++ b/docs/source/routing.rst
@@ -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::
diff --git a/docs/source/secure_controller.rst b/docs/source/secure_controller.rst
index 1bc94c6..baa6cbe 100644
--- a/docs/source/secure_controller.rst
+++ b/docs/source/secure_controller.rst
@@ -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`.
::
diff --git a/docs/source/sessions.rst b/docs/source/sessions.rst
index cefba7d..e6be814 100644
--- a/docs/source/sessions.rst
+++ b/docs/source/sessions.rst
@@ -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``.
::
diff --git a/docs/source/templates.rst b/docs/source/templates.rst
index 991bf6f..ca06b4a 100644
--- a/docs/source/templates.rst
+++ b/docs/source/templates.rst
@@ -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.
::
diff --git a/docs/source/testing.rst b/docs/source/testing.rst
index 481166e..5253d6b 100644
--- a/docs/source/testing.rst
+++ b/docs/source/testing.rst
@@ -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.
diff --git a/pecan/commands/base.py b/pecan/commands/base.py
index 2a79ca5..8f7d3dc 100644
--- a/pecan/commands/base.py
+++ b/pecan/commands/base.py
@@ -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__
+})
diff --git a/pecan/core.py b/pecan/core.py
index 9c133ce..4037a32 100644
--- a/pecan/core.py
+++ b/pecan/core.py
@@ -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
diff --git a/pecan/hooks.py b/pecan/hooks.py
index 86ba60c..4ceeb42 100644
--- a/pecan/hooks.py
+++ b/pecan/hooks.py
@@ -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.")}
)
diff --git a/pecan/secure.py b/pecan/secure.py
index 62b82cb..cca81f0 100644
--- a/pecan/secure.py
+++ b/pecan/secure.py
@@ -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__}
)