diff options
author | Ryan Petrello <lists@ryanpetrello.com> | 2012-03-13 14:16:08 -0700 |
---|---|---|
committer | Ryan Petrello <lists@ryanpetrello.com> | 2012-03-13 16:21:44 -0700 |
commit | bded674abd5afbf2bb10b30fa820d7002a0b42f4 (patch) | |
tree | 47c912f7b1b1bec1f1696f00b833195481c81743 | |
parent | 02a80731c48feb814dce727dd95db1510a7eb08e (diff) | |
download | pecan-bded674abd5afbf2bb10b30fa820d7002a0b42f4.tar.gz |
Cleaning up cruft, simplifying default config, and improving docs.
-rw-r--r-- | docs/source/configuration.rst | 59 | ||||
-rw-r--r-- | docs/source/index.rst | 7 | ||||
-rw-r--r-- | docs/source/installation.rst | 21 | ||||
-rw-r--r-- | docs/source/jsonify.rst | 1 | ||||
-rw-r--r-- | docs/source/pecan_default_config.rst | 32 | ||||
-rw-r--r-- | docs/source/pecan_deploy.rst | 11 | ||||
-rw-r--r-- | docs/source/pecan_testing.rst | 11 | ||||
-rw-r--r-- | docs/source/quick_start.rst | 45 | ||||
-rw-r--r-- | docs/source/routing.rst | 196 | ||||
-rw-r--r-- | docs/source/templates.rst | 6 | ||||
-rw-r--r-- | docs/source/validation_n_errors.rst | 6 | ||||
-rw-r--r-- | pecan/__init__.py | 18 | ||||
-rw-r--r-- | pecan/configuration.py | 10 | ||||
-rw-r--r-- | pecan/core.py | 41 | ||||
-rw-r--r-- | pecan/deploy.py | 4 | ||||
-rw-r--r-- | pecan/templates/project/+package+/app.py_tmpl | 6 | ||||
-rw-r--r-- | pecan/templates/project/config.py_tmpl | 2 | ||||
-rw-r--r-- | pecan/tests/templates/error_for.html | 1 | ||||
-rw-r--r-- | pecan/tests/test_conf.py | 13 | ||||
-rw-r--r-- | pecan/tests/test_util.py | 12 | ||||
-rw-r--r-- | pecan/util.py | 25 |
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 |