summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRyan Petrello <lists@ryanpetrello.com>2012-03-13 14:16:08 -0700
committerRyan Petrello <lists@ryanpetrello.com>2012-03-13 16:21:44 -0700
commitbded674abd5afbf2bb10b30fa820d7002a0b42f4 (patch)
tree47c912f7b1b1bec1f1696f00b833195481c81743
parent02a80731c48feb814dce727dd95db1510a7eb08e (diff)
downloadpecan-bded674abd5afbf2bb10b30fa820d7002a0b42f4.tar.gz
Cleaning up cruft, simplifying default config, and improving docs.
-rw-r--r--docs/source/configuration.rst59
-rw-r--r--docs/source/index.rst7
-rw-r--r--docs/source/installation.rst21
-rw-r--r--docs/source/jsonify.rst1
-rw-r--r--docs/source/pecan_default_config.rst32
-rw-r--r--docs/source/pecan_deploy.rst11
-rw-r--r--docs/source/pecan_testing.rst11
-rw-r--r--docs/source/quick_start.rst45
-rw-r--r--docs/source/routing.rst196
-rw-r--r--docs/source/templates.rst6
-rw-r--r--docs/source/validation_n_errors.rst6
-rw-r--r--pecan/__init__.py18
-rw-r--r--pecan/configuration.py10
-rw-r--r--pecan/core.py41
-rw-r--r--pecan/deploy.py4
-rw-r--r--pecan/templates/project/+package+/app.py_tmpl6
-rw-r--r--pecan/templates/project/config.py_tmpl2
-rw-r--r--pecan/tests/templates/error_for.html1
-rw-r--r--pecan/tests/test_conf.py13
-rw-r--r--pecan/tests/test_util.py12
-rw-r--r--pecan/util.py25
21 files changed, 239 insertions, 288 deletions
diff --git a/docs/source/configuration.rst b/docs/source/configuration.rst
index cf005b5..05e6f30 100644
--- a/docs/source/configuration.rst
+++ b/docs/source/configuration.rst
@@ -3,25 +3,13 @@
Configuration
=============
Pecan is very easy to configure. As long as you follow certain conventions,
-using, setting and dealing with configuration should be very intuitive.
+using, setting and dealing with configuration should be very intuitive.
-Pecan configuration files are pure Python. These files need to specify the values in a key/value way (Python
-dictionaries) or if you need simple one-way values you can also specify them as
-direct variables (more on that below).
+Pecan configuration files are pure Python.
-Even if you want custom configuration values, you need to get them in the
-configuration file as dictionaries.
-
-No Configuration
-----------------
-What happens when no configuration is passed? Or if you are missing some values?
-Pecan fills in anything that you might have left behind, like specific values or
-even if you leave them out completely. This includes
-**app** and **server** but will not, however, include any custom configurations.
-
-Defaults
---------
-Below is the complete default values the framework uses::
+Default Values
+---------------
+Below is the complete list of default values the framework uses::
server = {
@@ -31,9 +19,9 @@ Below is the complete default values the framework uses::
app = {
'root' : None,
+ 'modules' : [],
'static_root' : 'public',
- 'template_path' : '',
- 'debug' : False
+ 'template_path' : ''
}
@@ -42,10 +30,9 @@ Below is the complete default values the framework uses::
Application Configuration
-------------------------
-This is the part of the configuration that is specific to your application.
-Things like debug mode, Root Controller and possible Hooks, should be specified
-here. This is what is used when the framework is wrapping your application into
-a valid WSGI app.
+This is the part of the configuration that is specific to your application -
+the framework uses it to wrap your application into a valid
+`WSGI app <http://www.wsgi.org/en/latest/what.html>`_.
A typical application configuration might look like this::
@@ -60,23 +47,24 @@ A typical application configuration might look like this::
Let's look at each value and what it means:
-**app** is a reserved variable name for the configuration, so make sure you are
-not overriding, otherwise you will get default values.
+**app** is a reserved variable name for the configuration, so make sure you
+don't override it.
**root** The root controller of your application. Remember to provide
a string representing a Python path to some callable (e.g.,
-`yourapp.controllers.root.RootController`).
+``"yourapp.controllers.root.RootController"``).
**static_root** Points to the directory where your static files live (relative
-to the project root).
+to the project root). By default, Pecan comes with middleware that can be
+used to serve static files (like CSS and Javascript files) during development.
**template_path** Points to the directory where your template files live
(relative to the project root).
**reload** - When ``True``, ``pecan serve`` will listen for file changes and
-restare your app (especially useful for development).
+restart your app (especially useful for development).
-**debug** Enables ``WebError`` to have display tracebacks in the browser
+**debug** Enables ``WebError`` to display tracebacks in the browser
(**IMPORTANT**: Make sure this is *always* set to ``False`` in production
environments).
@@ -85,7 +73,7 @@ environments).
Server Configuration
--------------------
-Pecan provides some defaults. Change these to alter the host and port your
+Pecan provides some sane defaults. Change these to alter the host and port your
WSGI app is served on::
server = {
@@ -93,6 +81,17 @@ WSGI app is served on::
'host' : '0.0.0.0'
}
+Additional Configuration
+------------------------
+Your application may need access to other configuration values at runtime
+(like third-party API credentials). These types of configuration can be
+defined in their own blocks in your configuration file::
+
+ twitter = {
+ 'api_key' : 'FOO',
+ 'api_secret' : 'SECRET'
+ }
+
.. _accessibility:
Accessing Configuration at Runtime
diff --git a/docs/source/index.rst b/docs/source/index.rst
index 9315e61..f005387 100644
--- a/docs/source/index.rst
+++ b/docs/source/index.rst
@@ -12,10 +12,9 @@ Contents:
installation.rst
quick_start.rst
+ commands.rst
routing.rst
- validation_n_errors.rst
configuration.rst
- commands.rst
templates.rst
rest.rst
secure_controller.rst
@@ -42,7 +41,6 @@ for building HTTP-based applications, including:
* Object-dispatch for easy routing
* Full support for REST-style controllers
- * Validation and error handling
* Extensible security framework
* Extensible template language support
* Extensible JSON support
@@ -75,13 +73,14 @@ docstrings here:
pecan_core.rst
pecan_configuration.rst
pecan_decorators.rst
- pecan_default_config.rst
+ pecan_deploy.rst
pecan_hooks.rst
pecan_jsonify.rst
pecan_rest.rst
pecan_routing.rst
pecan_secure.rst
pecan_templating.rst
+ pecan_testing.rst
pecan_util.rst
diff --git a/docs/source/installation.rst b/docs/source/installation.rst
index 2f43623..a0588b7 100644
--- a/docs/source/installation.rst
+++ b/docs/source/installation.rst
@@ -6,27 +6,26 @@ Installation
Stable Version
------------------------------
-We recommend installing Pecan with ``pip`` but you can also try with
-``easy_install`` and ``virtualenv``. Creating a spot in your environment where
+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.
-To get started with an environment for Pecan, create a new
-`virtual environment <http://www.virtualenv.org>`_::
+To get started with an environment for Pecan, we recommend creating a new
+`virtual environment <http://www.virtualenv.org>`_ using `virtualenv
+<http://www.virtualenv.org>`_::
virtualenv pecan-env
cd pecan-env
source bin/activate
-The above commands create a virtual environment and *activate* it. Those
-actions will encapsulate any dependency installations for the framework,
-making it easier to debug problems if needed.
+The above commands create a virtual environment and *activate* it. This
+will isolate Pecan's dependency installations from your system packages, making
+it easier to debug problems if needed.
Next, let's install Pecan::
pip install pecan
-After a lot of output, you should have Pecan successfully installed.
-
Development (Unstable) Version
------------------------------
@@ -35,8 +34,8 @@ need to install git and clone the repo from GitHub::
git clone https://github.com/dreamhost/pecan.git
-If your virtual environment is still activated, call ``setup.py`` to install
-the development version::
+Assuming your virtual environment is still activated, call ``setup.py`` to
+install the development version::
cd pecan
python setup.py develop
diff --git a/docs/source/jsonify.rst b/docs/source/jsonify.rst
index 31bde69..dc5657b 100644
--- a/docs/source/jsonify.rst
+++ b/docs/source/jsonify.rst
@@ -19,7 +19,6 @@ we want to use to return ``JSON`` output for a ``User`` object::
class UsersController(object):
@expose('json')
- @validate()
def current_user(self):
'''
return an instance of myproject.model.User which represents
diff --git a/docs/source/pecan_default_config.rst b/docs/source/pecan_default_config.rst
deleted file mode 100644
index 656bffa..0000000
--- a/docs/source/pecan_default_config.rst
+++ /dev/null
@@ -1,32 +0,0 @@
-.. _pecan_default_config:
-
-:mod:`pecan.default_config` -- Pecan Default Configuration
-==========================================================
-
-The :mod:`pecan.default_config` module contains the default configuration
-for all Pecan applications.
-
-.. automodule:: pecan.default_config
- :members:
- :show-inheritance:
-
-The default configuration is as follows::
-
- # Server Specific Configurations
- server = {
- 'port' : '8080',
- 'host' : '0.0.0.0'
- }
-
- # Pecan Application Configurations
- app = {
- 'root' : None,
- 'modules' : [],
- 'static_root' : 'public',
- 'template_path' : '',
- 'debug' : False,
- 'force_canonical' : True,
- 'errors' : {
- '__force_dict__' : True
- }
- } \ No newline at end of file
diff --git a/docs/source/pecan_deploy.rst b/docs/source/pecan_deploy.rst
new file mode 100644
index 0000000..bcda855
--- /dev/null
+++ b/docs/source/pecan_deploy.rst
@@ -0,0 +1,11 @@
+.. _pecan_deploy:
+
+:mod:`pecan.deploy` -- Pecan Deploy
+===========================================
+
+The :mod:`pecan.deploy` module includes fixtures to help deploy Pecan
+applications.
+
+.. automodule:: pecan.deploy
+ :members:
+ :show-inheritance:
diff --git a/docs/source/pecan_testing.rst b/docs/source/pecan_testing.rst
new file mode 100644
index 0000000..09d76aa
--- /dev/null
+++ b/docs/source/pecan_testing.rst
@@ -0,0 +1,11 @@
+.. _pecan_testing:
+
+:mod:`pecan.testing` -- Pecan Testing
+===========================================
+
+The :mod:`pecan.testing` module includes fixtures to help test Pecan
+applications.
+
+.. automodule:: pecan.testing
+ :members:
+ :show-inheritance:
diff --git a/docs/source/quick_start.rst b/docs/source/quick_start.rst
index d8c2aa9..f2ff1f0 100644
--- a/docs/source/quick_start.rst
+++ b/docs/source/quick_start.rst
@@ -18,9 +18,9 @@ your shell, type::
$ pecan create
-The above command will prompt you for a project name. I chose *test_project*,
-but you can also provided as an argument at the end of the example command
-above, like::
+The above command will prompt you for a project name. This example uses
+*test_project*, but you can also provide an argument at the end of the
+example command above, like::
$ pecan create test_project
@@ -36,8 +36,7 @@ This is how the layout of your new project should look::
├── public
│   ├── css
│   │   └── style.css
- │   └── javascript
- │   └── shared.js
+ │   └── images
├── setup.cfg
├── setup.py
└── test_project
@@ -51,12 +50,12 @@ This is how the layout of your new project should look::
   ├── templates
   │   ├── error.html
   │   ├── index.html
-    │   ├── layout.html
-    │   └── success.html
+    │   └── layout.html
   └── tests
   ├── __init__.py
-    ├── test_config.py
-    └── test_root.py
+    ├── config.py
+    ├── test_functional.py
+    └── test_units.py
The amount of files and directories may vary, but the above structure should
give you an idea of what you should expect.
@@ -64,7 +63,7 @@ give you an idea of what you should expect.
A few things have been created for you, so let's review them one by one:
* **public**: All your static files (like CSS and Javascript) live here. If you
- have any images (this example app doesn't) they would live here too.
+ have any images they would live here too.
The remaining directories encompass your models, controllers and templates, and
@@ -162,17 +161,14 @@ This is how it looks in the project template
class RootController(object):
- @expose(
- generic = True,
- template = 'index.html'
- )
+ @expose(generic=True, template='index.html')
def index(self):
return dict()
-
+
@index.when(method='POST')
- def index_post(self, name, age):
- return dict(name=name)
-
+ def index_post(self, q):
+ redirect('http://pecan.readthedocs.org/en/latest/search.html?q=%s' % q)
+
@expose('error.html')
def error(self, status):
try:
@@ -194,21 +190,10 @@ this method.
Notice that the index method returns a dictionary - this dictionary is used as
a namespace to render the specified template (``index.html``) into HTML.
-**def index_post**: receives 2 arguments (*name* and *age*) that are validated
-through the *SampleForm* schema.
+**def index_post**: receives one HTTP POST argument (``q``).
``method`` has been set to 'POST', so HTTP POSTs to the application root (in
our example, form submissions) will be routed to this method.
-The ``error_handler`` has been set to index. This means that when errors are
-raised, they will be sent to the index controller and rendered through its
-template.
-
**def error**: Finally, we have the error controller that allows your application to
display custom pages for certain HTTP errors (404, etc...).
-
-Application Interaction
------------------------
-If you still have your application running and you visit it in your browser,
-you should see a page with some information about Pecan and the form so you can
-play a bit.
diff --git a/docs/source/routing.rst b/docs/source/routing.rst
index f936254..498383f 100644
--- a/docs/source/routing.rst
+++ b/docs/source/routing.rst
@@ -1,11 +1,13 @@
.. _routing:
-Routing
-=======
+Controllers and Routing
+=======================
When a user requests a certain URL in your app, how does Pecan know which
-controller to route to? Pecan uses a method known as **object-dispatch** to map an
-HTTP request to a controller. Object-dispatch begins by splitting the
+controller to route to? Pecan uses a routing strategy known as
+**object-dispatch** to map an HTTP request to a controller.
+
+Object-dispatch begins by splitting the
path into a list of components and then walking an object path, starting at
the root controller. You can imagine your application's controllers as a tree
of objects (branches of the object tree map directly to URL paths). Let's look
@@ -49,6 +51,77 @@ controller. Using the ``catalog`` object, Pecan would then lookup
``books``, followed by ``bestsellers``. What if the URL ends in a slash?
Pecan will check for an ``index`` method on the current object.
+To illustrate further, the following paths...
+
+::
+
+    └── /
+    ├── /hours
+    └── /catalog
+    └── /catalog/books
+    └── /catalog/books/bestsellers
+
+Would route to the following controller methods...
+
+::
+
+    └── RootController.index
+    ├── RootController.hours
+    └── CatalogController.index
+    └── BooksController.index
+    └── BooksController.bestsellers
+
+Exposing Controllers
+--------------------
+
+At its core, ``@expose`` is how you tell Pecan which methods in a class
+are publically-visible controllers. If a method is *not* decorated with
+``@expose``, it will not be routed to. ``@expose`` accepts three optional
+parameters, some of which can impact routing.
+
+::
+
+ from pecan import expose
+
+ class RootController(object):
+ @expose(
+ template = None,
+ content_type = 'text/html',
+ generic = False
+ )
+ def hello(self):
+ return 'Hello World'
+
+
+Let's look at an example using ``template`` and ``content_type``:
+
+::
+
+ from pecan import expose
+
+ class RootController(object):
+ @expose('json')
+ @expose('text_template.mako', content_type='text/plain')
+ @expose('html_template.mako')
+ def hello(self):
+ return {'msg': 'Hello!'}
+
+You'll notice that we used three ``expose`` decorators.
+
+The first tells Pecan to serialize the response namespace using JSON
+serialization when the client requests ``/hello.json``.
+
+The second tells Pecan to use the ``text_template.mako`` template file when the
+client requests ``/hello.txt``.
+
+The third tells Pecan to use the html_template.mako template file when the
+client requests ``/hello.html``. If the client requests ``/hello``, Pecan will
+use the ``text/html`` content type by default.
+
+Please see :ref:`pecan_decorators` for more information on ``@expose``.
+
+
+
Routing Algorithm
-----------------
@@ -60,8 +133,8 @@ methods on your controller objects provides additional flexibility for
processing all or part of a URL.
-``_lookup``
------------
+Routing to Subcontrollers with ``_lookup``
+------------------------------------------
The ``_lookup`` special method provides a way to process a portion of a URL,
and then return a new controller object to route to for the remainder.
@@ -75,8 +148,8 @@ resort, when no other controller matches the URL via standard object-dispatch.
::
- from pecan import expose
- from mymodel import get_student_by_name
+ from pecan import expose, abort
+ from somelib import get_student_by_name
class StudentController(object):
def __init__(self, student):
@@ -98,11 +171,11 @@ resort, when no other controller matches the URL via standard object-dispatch.
An HTTP GET request to `/8/name` would return the name of the student
where `primary_key == 8`.
-``_default``
-------------
+Falling Back with ``_default``
+------------------------------
-The ``_default`` controller is called when no other controller methods
-match the URL via standard object-dispatch.
+The ``_default`` controller is called as a last resort when no other controller
+methods match the URL via standard object-dispatch.
::
@@ -110,20 +183,24 @@ match the URL via standard object-dispatch.
class RootController(object):
@expose()
- def hello(self):
+ def english(self):
return 'hello'
@expose()
- def bonjour(self):
+ def french(self):
return 'bonjour'
@expose()
def _default(self):
- return 'I cannot say hi in that language'
+ return 'I cannot say hello in that language'
+
+
+...so in the example above, a request to ``/spanish`` would route to
+``RootController._default``.
-Overriding ``_route``
----------------------
+Defining Customized Routing with ``_route``
+-------------------------------------------
The ``_route`` method allows a controller to completely override the routing
mechanism of Pecan. Pecan itself uses the ``_route`` method to implement its
@@ -135,71 +212,68 @@ will enable you to have total control.
Controller Arguments
--------------------
-A controller can receive arguments in a variety of ways, including ``GET`` and
-``POST`` variables, and even chunks of the URL itself. ``GET`` and ``POST``
-arguments simply map to arguments on the controller method, while unprocessed
-chunks of the URL can be passed as positional arguments to the controller method.
+In Pecan, HTTP ``GET`` and ``POST`` variables that are `not` consumed
+during the routing process can be passed onto the controller as arguments.
+
+Depending on the signature of your controller, these arguments can be mapped
+explicitly to method arguments:
::
from pecan import expose
class RootController(object):
- @expose(generic=True)
- def index(self):
- return 'Default case'
+ @expose()
+ def index(self, arg):
+ return arg
- @index.when(method='POST')
- def index_post(self):
- return 'You POSTed to me!'
+ @expose()
+ def kwargs(self, **kwargs):
+ return str(kwargs)
- @index.when(method='GET')
- def index_get(self):
- return 'You GET me!'
+::
+ $ curl http://localhost:8080/?arg=foo
+ foo
+ $ curl http://localhost:8080/kwargs?a=1&b=2&c=3
+ {u'a': u'1', u'c': u'3', u'b': u'2'}
-Helper Functions
-----------------
-
-Pecan also provides several useful helper functions. The ``redirect``
-function allows you to issue internal or ``HTTP 302`` redirects.
-The ``redirect`` utility, along with several other useful helpers,
-are documented in :ref:`pecan_core`.
+...or can be consumed positionally:
+::
-``@expose``
------------
+ from pecan import expose
-At its core, ``@expose`` is how you tell Pecan which methods in a class
-are publically-visible controllers. ``@expose`` accepts eight optional
-parameters, some of which can impact routing.
+ class RootController(object):
+ @expose()
+ def args(self, *args):
+ return ','.join(args)
::
- expose(template = None,
- content_type = 'text/html',
- generic = False)
-
+ $ curl http://localhost:8080/one/two/three
+ one,two,three
-Let's look at an example using template and content_type
+The same effect can be achieved with HTTP ``POST`` body variables:
::
- from pecan import decorators
+ from pecan import expose
class RootController(object):
- @expose('json')
- @expose('text_template.mako', content_type='text/plain')
- @expose('html_template.mako')
- def hello(self):
- return {'msg': 'Hello!'}
+ @expose()
+ def index(self, arg):
+ return arg
-You'll notice that we used three expose decorators. The first tells
-Pecan to serialize our response namespace using JSON serialization when
-the client requests ``/hello.json``. The second tells the templating
-engine to use ``text_template.mako`` when the client request ``/hello.txt``.
-The third tells Pecan to use the html_template.mako when the client
-requests ``/hello.html``. If the client requests ``/hello``, Pecan will
-use the text/html template.
+::
-Please see :ref:`pecan_decorators` for more information on ``@expose``.
+ $ curl -X POST "http://localhost:8080/" -H "Content-Type: application/x-www-form-urlencoded" -d "arg=foo"
+ foo
+
+Helper Functions
+----------------
+
+Pecan also provides several useful helper functions for moving between
+different routes. The ``redirect`` function allows you to issue internal or
+``HTTP 302`` redirects. The ``redirect`` utility, along with several other
+useful helpers, are documented in :ref:`pecan_core`.
diff --git a/docs/source/templates.rst b/docs/source/templates.rst
index 7ba33e1..8f8357c 100644
--- a/docs/source/templates.rst
+++ b/docs/source/templates.rst
@@ -76,7 +76,7 @@ The ``render`` helper is also quite simple to use::
The JSON Renderer
-----------------
-Pecan provides a `JSON` renderer by simple passing ``@expose('json')``. For
+Pecan also provides a `JSON` renderer, e.g., ``@expose('json')``. For
more information on using `JSON` in Pecan, please refer to :ref:`jsonify` and
:ref:`pecan_jsonify`.
@@ -84,7 +84,7 @@ more information on using `JSON` in Pecan, please refer to :ref:`jsonify` and
Custom Renderers
----------------
-To define a custom renderer, you simply create a class that follows a simple
+To define a custom renderer, you can create a class that follows a simple
protocol::
class MyRenderer(object):
@@ -105,7 +105,7 @@ protocol::
return str(namespace)
-To enable your custom renderer, you can define a ``custom_renderers`` key In
+To enable your custom renderer, you can define a ``custom_renderers`` key in
your application's configuration::
app = {
diff --git a/docs/source/validation_n_errors.rst b/docs/source/validation_n_errors.rst
deleted file mode 100644
index facc4d6..0000000
--- a/docs/source/validation_n_errors.rst
+++ /dev/null
@@ -1,6 +0,0 @@
-.. _validation_n_errors:
-
-Validation and Error Handling
-=============================
-TODO: Rewrite this (potentially as an extension/cookbook) to illustrate the
-new technique (without formencode).
diff --git a/pecan/__init__.py b/pecan/__init__.py
index 8f95f28..2828e1b 100644
--- a/pecan/__init__.py
+++ b/pecan/__init__.py
@@ -1,6 +1,3 @@
-'''
-'''
-
from paste.cascade import Cascade
from paste.errordocument import make_errordocument
from paste.recursive import RecursiveMiddleware
@@ -11,7 +8,7 @@ from weberror.evalexception import EvalException
from core import (
abort, override_template, Pecan, load_app, redirect, render,
- request, response, ValidationException
+ request, response
)
from decorators import expose
from hooks import RequestViewerHook
@@ -29,18 +26,21 @@ __all__ = [
def make_app(root, static_root=None, debug=False, errorcfg={},
wrap_app=None, logging=False, **kw):
- '''
- '''
+ # A shortcut for the RequestViewerHook middleware.
if hasattr(conf, 'requestviewer'):
existing_hooks = kw.get('hooks', [])
existing_hooks.append(RequestViewerHook(conf.requestviewer))
kw['hooks'] = existing_hooks
+ # Instantiate the WSGI app by passing **kw onward
app = Pecan(root, **kw)
+ # Optionally wrap the app in another WSGI app
if wrap_app:
app = wrap_app(app)
+ # Included for internal redirect support
app = RecursiveMiddleware(app)
+ # Support for interactive debugging (and error reporting)
if debug:
app = EvalException(
app,
@@ -49,9 +49,13 @@ def make_app(root, static_root=None, debug=False, errorcfg={},
)
else:
app = ErrorMiddleware(app, **errorcfg)
- app = make_errordocument(app, conf, **dict(conf.app.errors))
+ # Configuration for serving custom error messages
+ if hasattr(conf.app, 'errors'):
+ app = make_errordocument(app, conf, **dict(conf.app.errors))
+ # Support for serving static files (for development convenience)
if static_root:
app = Cascade([StaticURLParser(static_root), app])
+ # Support for simple Apache-style logs
if isinstance(logging, dict) or logging == True:
app = TransLogger(app, **(isinstance(logging, dict) and logging or {}))
return app
diff --git a/pecan/configuration.py b/pecan/configuration.py
index bc4b757..a903ac8 100644
--- a/pecan/configuration.py
+++ b/pecan/configuration.py
@@ -17,13 +17,7 @@ DEFAULT = {
'root': None,
'modules': [],
'static_root': 'public',
- 'template_path': '',
- 'debug': False,
- 'logging': False,
- 'force_canonical': True,
- 'errors': {
- '__force_dict__': True
- }
+ 'template_path': ''
}
}
@@ -190,7 +184,7 @@ def initconf():
def set_config(config, overwrite=False):
'''
- Updates the global configuration a filename.
+ Updates the global configuration.
:param config: Can be a dictionary containing configuration, or a string
which
diff --git a/pecan/core.py b/pecan/core.py
index 261c83d..ce71260 100644
--- a/pecan/core.py
+++ b/pecan/core.py
@@ -1,7 +1,7 @@
from configuration import _runtime_conf, set_config
from templating import RendererFactory
from routing import lookup_controller, NonCanonicalPath
-from util import _cfg, splitext, encode_if_needed
+from util import _cfg, encode_if_needed
from webob import Request, Response, exc
from threading import local
@@ -9,6 +9,7 @@ from itertools import chain
from mimetypes import guess_type, add_type
from paste.recursive import ForwardRequestException
from urlparse import urlsplit, urlunsplit
+from os.path import splitext
try:
from simplejson import loads
@@ -116,17 +117,6 @@ def redirect(location=None, internal=False, code=None, headers={},
raise exc.status_map[code](location=location, headers=headers)
-def error_for(field):
- '''
- A convenience function for fetching the validation error for a
- particular field in a form.
-
- :param field: The name of the field to get the error for.
- '''
-
- return request.pecan['validation_errors'].get(field, '')
-
-
def render(template, namespace):
'''
Render the specified template using the Pecan rendering framework
@@ -142,31 +132,6 @@ def render(template, namespace):
return state.app.render(template, namespace)
-class ValidationException(ForwardRequestException):
- '''
- This exception is raised when a validation error occurs using Pecan's
- built-in validation framework.
- '''
-
- def __init__(self, location=None, errors={}):
- if state.controller is not None:
- cfg = _cfg(state.controller)
- else:
- cfg = {}
- if location is None and 'error_handler' in cfg:
- location = cfg['error_handler']
- if callable(location):
- location = location()
- if 'pecan.params' not in request.environ:
- request.environ['pecan.params'] = dict(request.params)
- request.environ[
- 'pecan.validation_errors'
- ] = request.pecan['validation_errors']
- request.environ['REQUEST_METHOD'] = 'GET'
- request.environ['pecan.validation_redirected'] = True
- ForwardRequestException.__init__(self, location)
-
-
def load_app(config):
'''
Used to load a ``Pecan`` application and its environment based on passed
@@ -383,8 +348,6 @@ class Pecan(object):
)
if template == 'json':
renderer = self.renderers.get('json', self.template_path)
- else:
- namespace['error_for'] = error_for
if ':' in template:
renderer = self.renderers.get(
template.split(':')[0],
diff --git a/pecan/deploy.py b/pecan/deploy.py
index 3559e99..08019f9 100644
--- a/pecan/deploy.py
+++ b/pecan/deploy.py
@@ -2,4 +2,8 @@ from core import load_app
def deploy(config):
+ """
+ Given a config (dictionary of relative filename), returns a configured
+ WSGI app.
+ """
return load_app(config)
diff --git a/pecan/templates/project/+package+/app.py_tmpl b/pecan/templates/project/+package+/app.py_tmpl
index 02b554f..2b2ec7e 100644
--- a/pecan/templates/project/+package+/app.py_tmpl
+++ b/pecan/templates/project/+package+/app.py_tmpl
@@ -8,8 +8,8 @@ def setup_app(config):
return make_app(
config.app.root,
static_root = config.app.static_root,
- debug = config.app.debug,
- logging = config.app.logging,
template_path = config.app.template_path,
- force_canonical = config.app.force_canonical
+ debug = getattr(config.app, 'debug', None),
+ logging = getattr(config.app, 'logging', None),
+ force_canonical = getattr(config.app, 'force_canonical', None)
)
diff --git a/pecan/templates/project/config.py_tmpl b/pecan/templates/project/config.py_tmpl
index 2aa3a65..081060b 100644
--- a/pecan/templates/project/config.py_tmpl
+++ b/pecan/templates/project/config.py_tmpl
@@ -12,8 +12,6 @@ app = {
'template_path' : '%(confdir)s/${package}/templates',
'reload' : True,
'debug' : True,
- 'logging' : False,
- 'force_canonical' : True,
'errors' : {
'404' : '/error/404',
'__force_dict__' : True
diff --git a/pecan/tests/templates/error_for.html b/pecan/tests/templates/error_for.html
deleted file mode 100644
index fcd47b6..0000000
--- a/pecan/tests/templates/error_for.html
+++ /dev/null
@@ -1 +0,0 @@
-${error_for('colors-1')} \ No newline at end of file
diff --git a/pecan/tests/test_conf.py b/pecan/tests/test_conf.py
index 3df8c8f..459bd02 100644
--- a/pecan/tests/test_conf.py
+++ b/pecan/tests/test_conf.py
@@ -23,7 +23,6 @@ class TestConf(TestCase):
'test_config/config.py'
)))
- self.assertTrue(conf.app.debug)
self.assertEqual(conf.app.root, None)
self.assertEqual(conf.app.template_path, 'myproject/templates')
self.assertEqual(conf.app.static_root, 'public')
@@ -41,7 +40,6 @@ class TestConf(TestCase):
'test_config/empty.py'
)))
- self.assertFalse(conf.app.debug)
self.assertEqual(conf.app.root, None)
self.assertEqual(conf.app.template_path, '')
self.assertEqual(conf.app.static_root, 'public')
@@ -58,7 +56,6 @@ class TestConf(TestCase):
'test_config/forcedict.py'
)))
- self.assertFalse(conf.app.debug)
self.assertEqual(conf.app.root, None)
self.assertEqual(conf.app.template_path, '')
self.assertEqual(conf.app.static_root, 'public')
@@ -99,7 +96,6 @@ class TestConf(TestCase):
os.path.dirname(__file__), 'test_config', 'config.py'
)
conf = configuration.conf_from_file(path)
- self.assertTrue(conf.app.debug)
def test_config_illegal_ids(self):
from pecan import configuration
@@ -197,9 +193,6 @@ class TestConf(TestCase):
assert isinstance(as_dict, dict)
assert as_dict['server']['host'] == '0.0.0.0'
assert as_dict['server']['port'] == '8080'
- assert as_dict['app']['debug'] == False
- assert as_dict['app']['errors'] == {}
- assert as_dict['app']['force_canonical'] == True
assert as_dict['app']['modules'] == []
assert as_dict['app']['root'] == None
assert as_dict['app']['static_root'] == 'public'
@@ -217,9 +210,6 @@ class TestConf(TestCase):
assert isinstance(as_dict, dict)
assert as_dict['server']['host'] == '0.0.0.0'
assert as_dict['server']['port'] == '8080'
- assert as_dict['app']['debug'] == False
- assert as_dict['app']['errors'] == {}
- assert as_dict['app']['force_canonical'] == True
assert as_dict['app']['modules'] == []
assert as_dict['app']['root'] == None
assert as_dict['app']['static_root'] == 'public'
@@ -238,9 +228,6 @@ class TestConf(TestCase):
assert isinstance(as_dict, dict)
assert as_dict['prefix_server']['prefix_host'] == '0.0.0.0'
assert as_dict['prefix_server']['prefix_port'] == '8080'
- assert as_dict['prefix_app']['prefix_debug'] == False
- assert as_dict['prefix_app']['prefix_errors'] == {}
- assert as_dict['prefix_app']['prefix_force_canonical'] == True
assert as_dict['prefix_app']['prefix_modules'] == []
assert as_dict['prefix_app']['prefix_root'] == None
assert as_dict['prefix_app']['prefix_static_root'] == 'public'
diff --git a/pecan/tests/test_util.py b/pecan/tests/test_util.py
deleted file mode 100644
index 3294eec..0000000
--- a/pecan/tests/test_util.py
+++ /dev/null
@@ -1,12 +0,0 @@
-from pecan.util import compat_splitext
-
-
-def test_compat_splitext():
- assert ('foo', '.bar') == compat_splitext('foo.bar')
- assert ('/foo/bar', '.txt') == compat_splitext('/foo/bar.txt')
- assert ('/foo/bar', '') == compat_splitext('/foo/bar')
- assert ('.bashrc', '') == compat_splitext('.bashrc')
- assert ('..bashrc', '') == compat_splitext('..bashrc')
- assert ('/.bashrc', '') == compat_splitext('/.bashrc')
- assert ('/foo.bar/.bashrc', '') == compat_splitext('/foo.bar/.bashrc')
- assert ('/foo.js', '.js') == compat_splitext('/foo.js.js')
diff --git a/pecan/util.py b/pecan/util.py
index 7f29a91..697dec8 100644
--- a/pecan/util.py
+++ b/pecan/util.py
@@ -12,31 +12,6 @@ def _cfg(f):
return f._pecan
-def compat_splitext(path):
- """
- This method emulates the behavior os.path.splitext introduced in python 2.6
- """
- basename = os.path.basename(path)
-
- index = basename.rfind('.')
- if index > 0:
- root = basename[:index]
- if root.count('.') != index:
- return (
- os.path.join(os.path.dirname(path), root),
- basename[index:]
- )
-
- return (path, '')
-
-
-# use the builtin splitext unless we're python 2.5
-if sys.version_info >= (2, 6):
- from os.path import splitext
-else: # pragma no cover
- splitext = compat_splitext # noqa
-
-
if sys.version_info >= (2, 6, 5):
def encode_if_needed(s):
return s