Configuration¶
Pylons comes with two main ways to configure an application:
- The configuration file (Runtime Configuration)
- The application’s
config
directory
The files in the config
directory change certain aspects of how the application behaves. Any options that the webmaster should be able to change during deployment should be specified in a configuration file.
Tip
A good indicator of whether an option should be set in the config
directory code vs. the configuration file is whether or not the option is necessary for the functioning of the application. If the application won’t function without the setting, it belongs in the appropriate config/
directory file. If the option should be changed depending on deployment, it belongs in the Runtime Configuration.
The applications config/
directory includes:
config/environment.py
described in Environmentconfig/middleware.py
described in Middlewareconfig/deployment.ini_tmpl
described in Production Configuration Filesconfig/routing.py
described in URL Configuration
Each of these files allows developers to change key aspects of how the application behaves.
Runtime Configuration¶
When a new project is created a sample configuration file called development.ini
is automatically produced as one of the project files. This default configuration file contains sensible options for development use, for example when developing a Pylons application it is very useful to be able to see a debug report every time an error occurs. The development.ini
file includes options to enable debug mode so these errors are shown.
Since the configuration file is used to determine which application is run, multiple configuration files can be used to easily toggle sets of options. Typically a developer might have a development.ini
configuration file for testing and a production.ini
file produced by the paster make-config command for testing the command produces sensible production output. A test.ini
configuration is also included in the project for test-specific options.
To specify a configuration file to use when running the application, change the last part of the paster serve to include the desired config file:
$ paster serve production.ini
See also
Configuration file format and options are described in great detail in the Paste Deploy documentation.
Getting Information From Configuration Files¶
All information from the configuration file is available in the pylons.config
object. pylons.config
also contains application configuration as defined in the project’s config.environment
module.
from pylons import config
pylons.config
behaves like a dictionary. For example, if the configuration file has an entry under the [app:main]
block:
cache_dir = %(here)s/data
That can then be read in the projects code:
from pylons import config
cache_dir = config['cache.dir']
Or the current debug status like this:
debug = config['debug']
Evaluating Non-string Data in Configuration Files¶
By default, all the values in the configuration file are considered strings.
To make it easier to handle boolean values, the Paste library comes with a
function that will convert true
and false
to proper Python boolean
values:
from paste.deploy.converters import asbool
debug = asbool(config['debug'])
This is used already in the default projects’ Middleware to
toggle middleware that should only be used in development mode (with
debug
) set to true.
Production Configuration Files¶
To change the defaults of the configuration INI file that should be used when deploying the application, edit the config/deployment.ini_tmpl
file. This is the file that will be used as a template during deployment, so that the person handling deployment has a starting point of the minimum options the application needs set.
One of the most important options set in the deployment ini is the debug = true
setting. The email options should be setup so that errors can be e-mailed to the appropriate developers or webmaster in the event of an application error.
Generating the Production Configuration¶
To generate the production.ini file from the projects’ config/deployment.ini_tmpl
it must first be installed either as an egg or under development mode. Assuming the name of the Pylons application is helloworld
, run:
$ paster make-config helloworld production.ini
Note
This command will also work from inside the project when its being developed.
It is the responsibility of the developer to ensure that a sensible set of default configuration values exist when the webmaster uses the paster make-config
command.
Warning
Always make sure that the debug
is set to false
when deploying a Pylons application.
Environment¶
The config/environment.py
module sets up the basic Pylons environment
variables needed to run the application. Objects that should be setup once
for the entire application should either be setup here, or in the
lib/app_globals
__init__()
method.
It also calls the URL Configuration function to setup how the URL’s will be matched up to Controllers, creates the app_globals object, configures which module will be referred to as h, and is where the template engine is setup.
When using SQLAlchemy it’s recommended that the SQLAlchemy engine be setup
in this module. The default SQLAlchemy configuration that Pylons comes
with creates the engine here which is then used in model/__init__.py
.
URL Configuration¶
A Python library called Routes handles mapping URLs to controllers and their methods, or their action as Routes refers to them. By default, Pylons sets up the following routes (found in config/routing.py
):
map.connect('/{controller}/{action}')
map.connect('/{controller}/{action}/{id}')
Changed in version 0.9.7: Prior to Routes 1.9, all map.connect statements required variable parts
to begin with a :
like map.connect(':controller/:action')
. This
syntax is now optional, and the new {}
syntax is recommended.
Any part of the path inside the curly braces is a variable (a variable part ) that will match any text in the URL for that ‘part’. A ‘part’ of the URL is the text between two forward slashes. Every part of the URL must be present for the route to match, otherwise a 404 will be returned.
The routes above are translated by the Routes library into regular expressions
for high performance URL matching. By default, all the variable parts (except
for the special case of {controller}
) become a matching regular expression
of [^/]+
to match anything except for a forward slash. This can be
changed easily, for example to have the {id}
only match digits:
map.connect('/{controller}/{action}/{id:\d+}')
If the desired regular expression includes the {}
, then it should be
specified separately for the variable part. To limit the {id}
to only
match at least 2-4 digits:
map.connect('/{controller}/{action}/{id}', requirements=dict(id='\d{2,4}'))
The controller and action can also be specified as keyword arguments so that they don’t need to be included in the URL:
# Archives by 2 digit year -> /archives/08
map.connect('/archives/{year:\d\d}', controller='articles', action='archives')
Any variable part, or keyword argument in the map.connect
statement will
be available for use in the
action used. For the route above, which resolves to the articles
controller:
class ArticlesController(BaseController):
def archives(self, year):
...
The part of the URL that matched as the year is available by name in the function argument.
Note
Routes also includes the ability to attempt to ‘minimize’ the URL. This
behavior is generally not intuitive, and starting in Pylons 0.9.7 is
turned off by default with the map.minimization=False
setting.
The default mapping can match to any controller and any of their actions which means the following URLs will match:
/hello/index >> controller: hello, action: index
/entry/view/4 >> controller: entry, action: view, id:4
/comment/edit/2 >> controller: comment, action: edit, id:2
This simple scheme can be suitable for even large applications when complex URL’s aren’t needed.
Controllers can be organized into directories as well. For example, if the admins should have a separate comments
controller:
$ paster controller admin/comments
Will create the admin
directory along with the appropriate comments
controller under it. To get to the comments controller:
/admin/comments/index >> controller: admin/comments, action: index
Note
The {controller}
match is special, in that it doesn’t always stop
at the next forward slash (/
). As the example above demonstrates,
it is able to match controllers nested under a directory should they
exist.
Adding a route to match /
¶
The controller and action can be specified directly in the map.connect()
statement, as well as the raw URL to be matched:
map.connect('/', controller='main', action='index')
results in /
being handled by the index
method of the main
controller.
Note
By default, projects’ static files (in the public/
directory) are
served in preference to controllers. New Pylons projects include a welcome
page (public/index.html
) that shows up at the /
url. You’ll
want to remove this file before mapping a route there.
Generating URLs¶
URLs are generated via the callable routes.util.URLGenerator
object. Pylons provides an instance of this special object at
pylons.url
. It accepts keyword arguments indicating the desired
controller, action and additional variables defined in a route.
# generates /content/view/2
url(controller='content', action='view', id=2)
To generate the URL of the matched route of the current request, call
routes.util.URLGenerator.current()
:
# Generates /content/view/3 during a request for /content/view/3
url.current()
routes.util.URLGenerator.current()
also accepts the same arguments as
url(). This uses Routes memory to generate a small
change to the current URL without the need to specify all the relevant
arguments:
# Generates /content/view/2 during a request for /content/view/3
url.current(id=2)
See also
Routes manual Full details and source code.
Middleware¶
A projects WSGI stack should be setup in the config/middleware.py
module. Ideally this file should import middleware it needs, and set it up
in the make_app function.
The default stack that is setup for a Pylons application is described in detail in WSGI Middleware.
Default middleware stack:
# The Pylons WSGI app
app = PylonsApp()
# Routing/Session/Cache Middleware
app = RoutesMiddleware(app, config['routes.map'])
app = SessionMiddleware(app, config)
app = CacheMiddleware(app, config)
# CUSTOM MIDDLEWARE HERE (filtered by error handling middlewares)
if asbool(full_stack):
# Handle Python exceptions
app = ErrorHandler(app, global_conf, **config['pylons.errorware'])
# Display error documents for 401, 403, 404 status codes (and
# 500 when debug is disabled)
if asbool(config['debug']):
app = StatusCodeRedirect(app)
else:
app = StatusCodeRedirect(app, [400, 401, 403, 404, 500])
# Establish the Registry for this application
app = RegistryManager(app)
if asbool(static_files):
# Serve static files
static_app = StaticURLParser(config['pylons.paths']['static_files'])
app = Cascade([static_app, app])
return app
Since each piece of middleware wraps the one before it, the stack needs to be assembled in reverse order from the order in which its called. That is, the very last middleware that wraps the WSGI Application, is the very first that will be called by the server.
The last piece of middleware in the stack, called Cascade, is used to
serve static content files during development. For top performance,
consider disabling the Cascade middleware via setting the
static_files = false
in the configuration file. Then have the
webserver or a CDN serve static files.
Warning
When unsure about whether or not to change the middleware, don’t. The order of the middleware is important to the proper functioning of a Pylons application, and shouldn’t be altered unless needed.
Adding custom middleware¶
Custom middleware should be included in the config/middleware.py
at
comment marker:
# CUSTOM MIDDLEWARE HERE (filtered by error handling middlewares)
For example, to add a middleware component named MyMiddleware,
include it in config/middleware.py
:
# The Pylons WSGI app
app = PylonsApp()
# Routing/Session/Cache Middleware
app = RoutesMiddleware(app, config['routes.map'])
app = SessionMiddleware(app, config)
app = CacheMiddleware(app, config)
# CUSTOM MIDDLEWARE HERE (filtered by error handling middlewares)
app = MyMiddleware(app)
The app object is simply passed as a parameter to the MyMiddleware middleware which in turn should return a wrapped WSGI application.
Care should be taken when deciding in which layer to place custom middleware. In most cases middleware should be placed before the Pylons WSGI application and its supporting Routes/Session/Cache middlewares, however if the middleware should run after the CacheMiddleware:
# Routing/Session/Cache Middleware
app = RoutesMiddleware(app, config['routes.map'])
app = SessionMiddleware(app, config)
# MyMiddleware can only see the cache object, nothing *above* here
app = MyMiddleware(app)
app = CacheMiddleware(app, config)
What is full_stack?¶
In the Pylons ini file {development.ini
or production.ini
} this block determines if the flag full_stack is set to true or false:
[app:main]
use = egg:app_name
full_stack = true
The full_stack flag determines if the ErrorHandler and StatusCodeRedirect is included as a layer in the middleware wrapping process. The only condition in which this option would be set to false is if multiple Pylons applications are running and will be wrapped in the appropriate middleware elsewhere.
Application Setup¶
There are two kinds of ‘Application Setup’ that are occasionally referenced with regards to a project using Pylons.
- Setting up a new application
- Configuring project information and package dependencies
Setting Up a New Application¶
To make it easier to setup a new instance of a project, such as setting up the basic database schema, populating necessary defaults, etc. a setup script can be created.
In a Pylons project, the setup script to be run is located in the projects’
websetup.py
file. The default script loads the projects configuration
to make it easier to write application setup steps:
import logging
from helloworld.config.environment import load_environment
log = logging.getLogger(__name__)
def setup_app(command, conf, vars):
"""Place any commands to setup helloworld here"""
load_environment(conf.global_conf, conf.local_conf)
Note
If the project was configured during creation to use SQLAlchemy this file will include some commands to setup the database connection to make it easier to setup database tables.
To run the setup script using the development configuration:
$ paster setup-app development.ini
Configuring the Package¶
A newly created project with Pylons is a standard Python package. As a Python
package, it has a setup.py
file that records meta-information about
the package. Most of the options in it are fairly self-explanatory, the most
important being the ‘install_requires’ option:
install_requires=[
"Pylons>=0.9.7",
],
These lines indicate what packages are required for the proper functioning
of the application, and should be updated as needed. To re-parse the
setup.py
line for new dependencies:
$ python setup.py develop
In addition to updating the packages as needed so that the dependency requirements are made, this command will ensure that this package is active in the system (without requiring the traditional python setup.py install).
See also