.. _configuration: ============= Configuration ============= Pylons comes with two main ways to configure an application: * The configuration file (:ref:`run-config`) * 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 :file:`config/` directory file. If the option should be changed depending on deployment, it belongs in the :ref:`run-config`. The applications :file:`config/` directory includes: * :file:`config/environment.py` described in :ref:`environment-config` * :file:`config/middleware.py` described in :ref:`middleware-config` * :file:`config/deployment.ini_tmpl` described in :ref:`production-config` * :file:`config/routing.py` described in :ref:`url-config` Each of these files allows developers to change key aspects of how the application behaves. .. _run-config: ********************* Runtime Configuration ********************* When a new project is created a sample configuration file called :file:`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 :file:`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 :command:`paster make-config` command for testing the command produces sensible production output. A :file:`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 :command:`paster serve` to include the desired config file: .. code-block :: bash $ paster serve production.ini .. seealso:: 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 :file:`config.environment` module. .. code-block :: python from pylons import config ``pylons.config`` behaves like a dictionary. For example, if the configuration file has an entry under the ``[app:main]`` block: .. code-block :: ini cache_dir = %(here)s/data That can then be read in the projects code: .. code-block :: python from pylons import config cache_dir = config['cache_dir'] Or the current debug status like this: .. code-block :: python 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: .. code-block :: python from paste.deploy.converters import asbool debug = asbool(config['debug']) This is used already in the default projects' :ref:`middleware-config` to toggle middleware that should only be used in development mode (with ``debug``) set to true. .. _production-config: Production Configuration Files ============================== To change the defaults of the configuration INI file that should be used when deploying the application, edit the :file:`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' :file:`config/deployment.ini_tmpl` it must first be installed either as an :term:`egg` or under development mode. Assuming the name of the Pylons application is ``helloworld``, run: .. code-block :: bash $ 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-config: *********** Environment *********** The :file:`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 :file:`lib/app_globals` :meth:`__init__` method. It also calls the :ref:`url-config` function to setup how the URL's will be matched up to :ref:`controllers`, creates the :term:`app_globals` object, configures which module will be referred to as :term:`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 :file:`model/__init__.py`. .. _url-config: ***************** URL Configuration ***************** A Python library called Routes handles mapping URLs to controllers and their methods, or their :term:`action` as Routes refers to them. By default, Pylons sets up the following :term:`route`\s (found in :file:`config/routing.py`): .. code-block:: python map.connect('/{controller}/{action}') map.connect('/{controller}/{action}/{id}') .. versionchanged:: 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 :term:`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: .. code-block :: python 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: .. code-block :: python 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: .. code-block :: python # 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: .. code-block :: python 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: .. code-block:: text /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: .. code-block:: bash $ paster controller admin/comments Will create the ``admin`` directory along with the appropriate ``comments`` controller under it. To get to the comments controller: .. code-block:: text /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 :meth:`map.connect` statement, as well as the raw URL should be matched. .. code-block:: python map.connect('/', controller='main', action='index') will result in ``/`` being handled by the ``index`` method of the ``main`` controller. Generating URLs =============== URLs are generated via the callable :class:`routes.util.URLGenerator` object. Pylons provides an instance of this special object at :data:`pylons.url`. It accepts keyword arguments indicating the desired controller, action and additional variables defined in a route. .. code-block:: python # generates /content/view/2 url(controller='content', action='view', id=2) To generate the URL of the matched route of the current request, call :meth:`routes.util.URLGenerator.current`: .. code-block:: python # Generates /content/view/3 during a request for /content/view/3 url.current() :meth:`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: .. code-block:: python # Generates /content/view/2 during a request for /content/view/3 url.current(id=2) .. seealso:: `Routes manual `_ Full details and source code. .. _middleware-config: ********** Middleware ********** A projects WSGI stack should be setup in the :file:`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 :ref:`wsgi-middleware`. Default middleware stack: .. code-block :: python # 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 :term:`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 :file:`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 :file:`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 {:file:`development.ini` or :file:`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. .. _setup-config: ***************** 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' :file:`websetup.py` file. The default script loads the projects configuration to make it easier to write application setup steps: .. code-block :: python 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: .. code-block :: bash $ 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 :file:`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: .. code-block :: python 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 :file:`setup.py` line for new dependencies: .. code-block :: bash $ 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 :command:`python setup.py install`). .. seealso:: `Declaring Dependencies `_