Ben Bangert avatar Ben Bangert committed 4512631 Merge

Merge of Grahams doc sprint

Comments (0)

Files changed (33)

pylons/docs/_templates/layout.html

+{% extends "!layout.html" %}
+{% block sidebarlogo %}
+    <img style="height:42px;width:158px;padding:1em" src="{{pathto("_static/akhet_rev.png", 1)}}" alt="" />
+    {{ super() }}
+{% endblock %}

pylons/docs/about.rst

+.. about:
+
+============
+About Pylons
+============
+
+What, who and why
+-----------------
+
+.. image:: _static/pylon2.jpg
+   :height: 480
+   :width: 365
+   :alt: The First Pylon of the Temple of Medinet-Habu.
+   :align: left
+
+
+Pylons brings a very broad but explicit :term:`Model-View-Controller` (:term:`MVC`) approach to the construction of web applications. It is specifically designed to give the application developer a high degree of flexibility when choosing ways of expressing business logic and application data; of managing the business logic and data; and of constructing a user interface (:term:`UI`) and/or an application programmer interface (:term:`API`) to afford and manage access to the model.
+
+Pylons was written by Ben Bangert, James Gardner and Phil Jenvey, they explain here why they chose this particular approach:
+
+    "The Pylons team are obsessed with reuse. Our vision is that in the near future Python web frameworks will all adopt the :term:`Web Server Gateway Interface` (or :term:`WSGI`) throughout their stack, making integration and reuse between frameworks extremely easy. Other frameworks don't necessarily share this vision to the same extent, preferring instead to keep their existing code base rather than opening it up to be replaced with standardized code. 
+
+    There have been many other excellent developments in the Python web world recently such as :mod:`Paste` and :mod:`Myghty` and so, rather than duplicating the efforts of these teams by writing similar code for existing frameworks, we decided instead to put a new framework together using these existing projects with an emphasis on WSGI. We did this by working with the existing project teams and adding features to their code when possible rather than putting it all into the Pylons package.
+
+    While we designed Pylons to have all the functionality of :term:`Rails` (porting Rails components to Python as necessary) we also designed it to be much more flexible, thanks mainly to :term:`WSGI`. This allowed us to design Pylons so that your projects are highly reusable and easy to integrate with existing projects, or integrate existing projects into Pylons. You can use Pylons in the way that feels most natural to you, and in smaller or larger chunks."
+
+In summary, Pylons provides the connectivity for a wide set of relevant and useful components from which the web application developer can select those components that are considered best suited for the kind of application that is to be constructed.
+
+The Model-View-Controller pattern
+---------------------------------
+
+:term:`Model-View-Controller` is an architectural pattern used in software engineering. Its defining characteristic is that the modeling of the external world, the handling of user input and the feedback to the user are explicitly separated and handled by three types of object, each specialized for its task:
+
+The **model** 
+    is a representation of the application data and the business rules used to manipulate the data. It manages the representation of the application domain, responding to requests for information about its state and responding to instructions to change state (c.f. Pylons :ref:`models`)
+
+The **controller** 
+    interprets the inputs from users or applications, commanding the model and/or the view to change as appropriate. Web applications often have several distinct controllers, each handling a different aspect of the application's behavior; a separate URL *dispatcher* maps incoming requests to the appropriate controller, as directed by a URL mapping scheme defined by the developer (c.f. Pylons :ref:`controllers`)
+
+The **view** 
+    manages the presentation of the output and renders the model into a form suitable for interaction via a UI element (for human use) or an API function (for machine use). Multiple views can exist for a single model for different purposes (c.f. Pylons :ref:`views`)
+
+The explicit separation of these three tasks isolates the business logic and data from user interface considerations. By decoupling models and views, MVC helps to reduce the complexity in architectural design and to increase flexibility and reuse.
+
+Developer's choice
+------------------
+
+It is becoming increasingly important for web app developers to be able to operate at an elevated level of abstraction. The choice of data store implementation is driven by the dictates of expressing the business model. Similarly, the selection of a template engine to render views of the data has to acknowledge both operational concerns and development requirements.
+
+Making an *informed* choice entails becoming informed about the candidates: understanding their different capabilities, their respective strengths and weaknesses. In this way, Pylons encourages the development of more sophisticated attitudes towards component elements. The old saying "If all you have is a hammer, then everything looks like a nail" simply doesn't hold true for Pylons developers with their well-equipped toolboxes.
+
+The benefits of adopting a MVC architecture for application development typically appear in the form of superior applications: they are more capable (more can be done),  they are more robust (fewer errors are made), they are easier to maintain (the separation *really* helps), they are more readily scalable and extensible (again, the separation is a key factor) ... the list goes on.
+
+Information
+-----------
+
+Pylons is similar in many ways to Ruby on Rails since it uses similar methodologies and includes direct ports of useful Rails components such as Routes and AJAX Helpers. By combining ideas from Mason, TurboGears and various Python frameworks with a highly extensible API, Pylons provides a framework that is:
+
+**Fast and Stable**
+    Built on Mako, Routes and Paste, Pylons harnesses the full power of Mako for maximum performance. Although Pylons is still in development the core packages it uses are mature and Pylons is already being used in production systems.
+
+**Easy to Use**
+    Pylons implements the very best web development methodologies in straightforward pure-Python code. People new to Pylons are often surprised by how natural it is to use. If you already know Python you will feel right at home with Pylons, if not the Python Tutorial is an excellent starting point. Pylons plays nicely with other frameworks; you can even run TurboGears, Django and other WSGI applications from within a Pylons application making transition that bit easier. The web based interactive debugging makes even tough problems easy to resolve.
+
+**Compatible**
+    Pylons and its underlying components have been designed with great care to run on operating systems from Windows to MacOS to Linux on all types of computer from embedded devices to dedicated servers, in fact anywhere with a full Python 2.3 installation or above. Pylons can be deployed with its own server or integrated with other servers through WSGI / FastCGI / SCGI / CGI / mod_python and more. For example you can run Pylons applications on an Apache shared hosting account with ease.
+
+**Component-Based**
+    Most Pylons functionality is implemented through extension packages distributed as eggs rather than as part of Pylons Core. This enables you to only include functionality you need in your application and avoid slowdown caused by unnecessary code. Even the Pylons Core APIs have been designed so that they can be tweaked, customized, extended or replaced with the minimum of effort, often just by modifying files in your application's config directory.
+
+**Extensible**
+    If your application needs extra functionality you can install a Pylons Extension of your own. Pylons Extensions already exist to add Internationalization, Auth facilities and Rails helpers. You can easily write and distribute your own Pylons Extensions using the Extension API. Pylons Extensions are also designed to be integrated with other web frameworks and this integration is actively encouraged.
+
+**Growing Rapidly**
+    Pylons' ease of use and sophisticated features mean that it is rapidly gaining support. Its extensible architecture is enabling more people to write new packages to extend the core functionality and its careful and flexible design mean that Pylons can rapidly evolve and grow as new methodologies and ideas take root.
+
+**Knowing Python makes Pylons easy**
+    Pylons makes it easy to expand on your knowledge of Python to master Pylons for web development. Using a MVC style dispatch, Python knowledge is used at various levels:
+        The Controller is just a basic Python class, called for each request. Customizing the response is as easy as overriding __call__ to make your webapp work how you want.
+
+        Mako templating compiles directly to Python byte-code for speed and utilizes Python for template control rather than creating its own template syntax for "for, while, etc"

pylons/docs/advanced_pylons.rst

+.. _advanced_pylons:
+
+===============
+Advanced Pylons
+===============
+
+To be added
+===========
+
+.. _paster:
+
+WSGI, CLI scripts
+=================
+
+Working with :class:`wsgiwrappers.WSGIRequest`
+----------------------------------------------
+
+Pylons uses a specialised *WSGIRequest* class that is accessible via the
+``paste.wsgiwrappers`` module.
+
+The ``wsgiwrappers.WSGIRequest`` object represents a WSGI request that has 
+a more programmer-friendly interface. This interface does not expose every 
+detail of the WSGI environment *(why?)* and does not attempt to express 
+anything beyond what is available in the environment dictionary.
+
+The only state maintained in this object is the desired ``charset``, an 
+associated errors handler and a ``decode_param_names`` option.
+
+.. _note:
+
+    *Unicode notes*
+
+    When ``charset`` is set, the incoming parameter values will be 
+    automatically coerced to unicode objects of the charset encoding.
+
+    When unicode is expected, ``charset`` will be overridden by the the value 
+    of the charset parameter set in the Content-Type header, if one was 
+    specified by the client.
+
+    The incoming parameter names are not decoded to unicode unless the 
+    decode_param_names option is enabled.
+
+The class variable ``defaults`` specifies default values for charset, errors,
+and language. These default values can be overridden for the current request 
+via the registry *(what's a registry?)*.
+
+The language default value is considered the fallback during i18n 
+translations to ensure in odd cases that mixed languages don't occur should 
+the language file contain the string but not another language in the accepted 
+languages list. The language value only applies when getting a list of 
+accepted languages from the HTTP Accept header.
+
+This behavior is duplicated from Aquarium, and may seem strange but is very 
+useful. Normally, everything in the code is in "en-us". However, the "en-us" 
+translation catalog is usually empty. If the user requests ["en-us", "zh-cn"] 
+and a translation isn't found for a string in "en-us", you don't want gettext 
+to fallback to "zh-cn". You want it to just use the string itself. Hence, if 
+a string isn't found in the language catalog, the string in the source code 
+will be used.
+
+All other state is kept in the environment dictionary; this is essential for 
+interoperability.
+
+You are free to subclass this object.
+
+Attributes
+----------
+
+GET
+^^^
+
+A dictionary-like object representing the QUERY_STRING parameters. Always present, possibly empty.
+
+If the same key is present in the query string multiple times, a list of its 
+values can be retrieved from the :class:`MultiDict` via the :meth:``getall`` 
+method.
+
+Returns a :class:`MultiDict` container or, when charset is set, a :class:`UnicodeMultiDict`.
+
+POST
+^^^^
+
+A dictionary-like object representing the ``POST`` body.
+
+Most values are encoded strings, or unicode strings when charset is set. 
+There may also be FieldStorage objects representing file uploads. If this is 
+not a POST request, or the body is not encoded fields (e.g., an XMLRPC 
+request) then this will be empty.
+
+This will consume wsgi.input when first accessed if applicable, but the raw 
+version will be put in environ['paste.parsed_formvars'].
+
+Returns a MultiDict container or a UnicodeMultiDict when charset is set.
+
+cookies
+^^^^^^^
+
+A dictionary of cookies, keyed by cookie name.
+
+Just a plain dictionary, may be empty but not None.
+
+defaults
+^^^^^^^^
+
+.. code-block:: python
+
+    {'errors': 'replace', 
+     'decode_param_names': False, 
+     'charset': None, 
+     'language': 'en-us'}
+
+host
+^^^^
+
+The host name, as provided in ``HTTP_HOST`` with a fall-back to :envvar:`SERVER_NAME`
+
+is_xhr
+^^^^^^
+
+Returns a boolean if ``X-Requested-With`` is present and is a ``XMLHttpRequest``
+
+languages
+^^^^^^^^^
+
+Returns a (possibly empty) list of preferred languages, most preferred first.
+
+
+params
+^^^^^^
+
+A dictionary-like object of keys from ``POST``, ``GET``, ``URL`` dicts
+
+Return a key value from the parameters, they are checked in the following order: POST, GET, URL
+
+
+Additional methods supported:
+-----------------------------
+
+getlist(key)
+^^^^^^^^^^^^
+
+Returns a list of all the values by that key, collected from POST, GET, URL dicts
+
+Returns a :class:`MultiDict` container or a :class:`UnicodeMultiDict` when :data:`charset` is set.
+
+urlvars
+^^^^^^^
+
+Return any variables matched in the URL (e.g. wsgiorg.routing_args).
+
+Methods
+-------
+
+__init__(self, environ)
+^^^^^^^^^^^^^^^^^^^^^^^
+
+determine_browser_charset(self)
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Determine the encoding as specified by the browser via the Content-Type's ``charset parameter``, if one is set
+
+match_accept(self, mimetypes)
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Return a list of specified mime-types that the browser's HTTP Accept header allows in the order provided.
+

pylons/docs/beaker.rst

+.. _beaker_caching:
+
+===============================================
+Sessions & Caching in controllers and templates
+===============================================
+
+Inevitably, there will be occasions during your applications development or deployment where some task takes a significant amount of time to complete. When this occurs, the best way to speed things up is with :term:`caching`. 
+
+Pylons comes with caching middleware enabled that is part of the same package that provides the session handling, `Beaker <http://beaker.groovie.org>`_. It supports backends in memory, the filesystem, and memcached. 
+
+There are several ways to cache data under Pylons, depending on where the slowdown is occurring:
+
+* Browser-side Caching - HTTP/1.1 supports the :term:`ETag` caching system that allows the browser to use its own cache instead of requiring the re-generation the entire page. ETag-based caching avoids repeated generation of content but if the browser has never seen the page it will still be generated. Therefore using ETag caching in conjunction with one of the other types of caching listed here will achieve optimal throughput and avoid resource-intensive operations.
+
+    .. Note:: the latter only helps if the entire page can be cached.
+
+* Controllers - The `cache` object is available in controllers and templates for use in caching anything that can be pickled. 
+
+* Templates - You can cache the results of an entire rendered template using the `3 cache keyword arguments to the render calls <http://pylonshq.com/docs/class-pylons.templating.Buffet.html#render>`_. These render commands can also be used inside templates. 
+
+* Mako/Myghty Templates - Built-in caching options are available for both `Mako <http://www.makotemplates.org/docs/caching.html>`_ and `Myghty <http://www.myghty.org/docs/cache.myt>`_ template engines. They allow fine grained caching of only certain sections of the template, as well as caching of the entire template. 
+
+The two primary concepts to keep in mind when caching is that caches have a *namespace* and can have *keys* under that namespace. Consider that for a single template, there might be multiple versions of the template you wish to keep a cache for. The keys in the namespace are the "version" and the name of the template is the "namespace". **Both of these values must be Python strings.** 
+
+In templates, the cache "namespace" will be set to the name of the template being rendered automatically. Nothing else is required for caching, unless you wish to control how long the template is cached for, and have multiple versions of the template cached. 
+
+see also Stephen Pierzchala's `Caching for Performance <http://web.archive.org/web/20060424171425/http://www.webperformance.org/caching/caching_for_performance.pdf>`_ (stephen@pierzchala.com)
+
+Using the Cache object 
+---------------------- 
+
+Inside a controller, the `cache` object will be available. If you have an action 
+or block of code that is resource or time intensive, it can be handy to cache 
+the result. The `cache` object can cache any Python structure that can be 
+`pickled <http://docs.python.org/lib/module-pickle.html>`_. 
+
+Consider an action where you'd like to cache some code that does a 
+time-consuming or resource intensive lookup and returns an object that can be 
+pickled (list, dict, tuple, etc.): 
+
+.. code-block:: python 
+
+    def some_action(self, day): 
+        # hypothetical action that uses a 'day' variable as its key 
+
+        def expensive_function(): 
+            # do something that takes a lot of cpu/resources 
+
+        # Get a cache for a specific namespace, you can name it whatever 
+        # you want, in this case its 'my_function' 
+        mycache = cache.get_cache('my_function') 
+
+        # Get the value, this will create the cache copy the first time 
+        # and any time it expires (in seconds, so 3600 = one hour) 
+        c.myvalue = mycache.get_value(key=day, createfunc=expensive_function, 
+        type="memory", expiretime=3600) 
+
+        return render('/some/template.myt') 
+
+The `createfunc` option requires a callable object (function) which is then called by the cache anytime a value for the provided is not in the cache, or has expired in the cache. Since it is called with no arguments, the expensive function must not require any as well. 
+
+Other Cache Options 
+^^^^^^^^^^^^^^^^^^^
+
+The cache also supports removing values from the cache by their key, and clearing the cache should you wish to reset it. 
+
+.. code-block:: python 
+
+    # Clear the cache 
+    mycache.clear() 
+
+    # Remove a specific key 
+    mycache.remove_value('some_key') 
+
+
+Using Cache keywords to `render` 
+-------------------------------- 
+
+All `render` commmands have caching functionality built in. To use it, merely
+add the appropriate cache keyword to your `render` call. 
+
+.. code-block:: python 
+
+    class SampleController(BaseController): 
+
+        def index(self): 
+            # Cache the template for 10 mins 
+            return render('/index.myt', cache_expire=600) 
+
+        def show(self, id): 
+            # Cache this version of the template for 3 mins 
+            return render('/show.myt', cache_key=id, cache_expire=180) 
+
+        def feed(self): 
+            # Cache for 20 mins to memory 
+            return render('/feed.myt', cache_type='memory', cache_expire=1200) 
+
+        def home(self, user): 
+            # Cache this version of a page forever (until the cache dir is cleaned) 
+            return render('/home.myt', cache_key=user, cache_expire='never') 
+
+
+Using the Cache Decorator 
+-------------------------
+
+Pylons also provides the `beaker_cache 
+<http://pylonshq.com/docs/module-pylons.decorators.cache.html#beaker_cache>`_ 
+decorator for caching the results of an entire function call (memoizing) to 
+`pylons.cache`. 
+
+It takes the same cache arguments (minus their `cache_` prefix) as does the 
+`render` function. 
+
+.. code-block:: python 
+
+    from pylons.decorators.cache import beaker_cache 
+
+    class SampleController(BaseController): 
+
+        # Cache this controller action forever (until the cache dir is cleaned) 
+        @beaker_cache() 
+        def home(self): 
+            c.data = expensive_call() 
+            return render('/home.myt') 
+
+        # Cache this controller action by its GET args for 10 mins to memory 
+        @beaker_cache(expire=600, type='memory', query_args=True) 
+        def show(self, id): 
+            c.data = expensive_call(id) 
+            return render('/show.myt') 
+
+By default it uses all of the decorated function's arguments as the cache 
+key. It can alternatively use the `request.GET` query args as the cache key 
+when the `query_args` option is enabled. The cache key can be further 
+customized via the `key` argument. 
+
+ETag Caching 
+------------
+
+Caching via ETag involves sending the browser an ETag header so that it knows 
+to save and possibly use a cached copy of the page from its own cache, instead 
+of your application sending it another. 
+
+Since the ETag cache relies on sending headers to the browser, it works in a 
+slightly different manner. The `etag_cache` function will return a `Response` 
+object with the proper HTTP headers set if the browser doesn't yet have a copy 
+of the page. Otherwise a 304 HTTP Exception will be thrown that is caught by 
+Paste middlware and turned into a proper 304 response to the browser. This will
+cause the browser to use its own copy. 
+
+ETag based caching requires a single key, which is sent in the ETag HTTP header
+back to the browser. The `RFC specification for HTTP headers <http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html>`_ indicates that your 
+ETag header merely needs to be a string. This value does not need to be unique 
+for every URL, as the browser determines whether to use its own copy based on 
+the URL and the ETag key. 
+
+.. code-block:: python 
+
+    def my_action(self): 
+        etag_cache('somekey') 
+        return render('/show.myt', cache_expire=3600) 
+
+Or to change other aspects of the response: 
+
+.. code-block:: python 
+
+    def my_action(self): 
+        etag_cache('somekey') 
+        response.headers['content-type'] = 'text/plain' 
+        return render('/show.myt', cache_expire=3600) 
+
+.. note:: 
+    In this example that we're using template caching in addition to ETag
+    caching. If a new visitor comes to the site, we avoid re-rendering the
+    template if a cached copy exists, and repeat hits to the page by that user
+    will then trigger the ETag cache. This example also will never change the
+    ETag key, so the browsers cache will always be used if it has one.
+
+Your ETag cache key will likely change depending on how often you want to have 
+the browser fetch a fresh copy of the page. 

pylons/docs/configuration.rst

+:Date: 2008-05-01
+:Version: 1
+:Review: 
+:Authors: - Ben Bangert
+
+.. sectionauthor:: Ben Bangert
+
 .. _configuration:
 
 =============
 Configuration
 =============
 
+Introduction 
+============ 
+
+Pylons comes with two main ways to configure an application:
+
+* Through the application's ``config`` directory 
+* Through a user configuration file 
+
+Typically as a developer writing a Pylons application you will change the files in the ``config`` directory to change certain aspects of how your application behaves but any options you want the webmaster using your configuration file to be able to configure will be specified by the in a configuration file. This document describes how to use the webmaster configurable configuration file support in Pylons. 
+
 XXX: Write overview of configuration choices, difference between run-time/deplyment configuration, and application configuration (whats in the config/ dir)
 
 
 Runtime Configuration
 =====================
 
+Default Configuration File Setup 
+-------------------------------- 
+
+When you create a project using ``paster create -t pylons`` a sample configuration file called ``development.ini`` is automatically produced as one of the project files. This is an example of a configuration file and contains sensible options for development use in your project. For example when you are developing a Pylons application it is very useful to be able to see a debug report every time an error occurs so the development.ini file includes options to enable debug mode so these errors are shown. 
+
+You can serve your application using this development configuration like this: 
+
+.. code-block :: bash 
+
+    $ paster serve --reload development.ini 
+
+The ``--reload`` option is useful during development as the server will automatically reload each time a file is changed so that you can test the changes without having to manually restart the server. 
+
+Say your application is called wiki, when a user comes to want to setup an application they will run the command: 
+
+.. code-block :: bash 
+
+    $ paster make-config wiki production.ini 
+
+This command produces a different configuration file with sensible options for production use. In particular, the debug display is disables as the report might contain your usernames, passwords or other information you wouldn't want end users to see. 
+
+You can find out how to control what is produced by the ``paster make-config`` by reading `Application Setup <Packaging+and+Deployment>`_. 
+
+It is your responsibility as a developer to ensure that a sensible set of default configuration values exist when the webmaster uses the ``paster make-config`` command. 
+
+You can create as many different configuration files as you like. Typically a developer might have a ``development.ini`` configuration file for testing, a ``production.ini`` file produced by the ``paster make-config`` command for testing the command produces sensible production output. You might also want a ``testing.ini`` configuration for doing some specific testing or any other number of configurations appropriate for your application. You can deploy your configuration in a variety of ways supported by paste deploy but typically you would serve your configuration like this: 
+
+.. code-block :: bash 
+
+    $ paster serve production.ini 
+
+Configuration File Format 
+------------------------- 
+
+Configuration file format is described in great detail in the `Paste Deploy documentation <http://www.pythonpaste.org>`_. 
+
+
+Error Handling Options 
+====================== 
+
+A number of error handling options can be specified in the config file. These are described in the `Error Handler <interactive_debugger.txt>`_ documentation but the important point to remember is that debug should always be set to ``false`` in production environments otherwise if an error occurs the visitor will be presented with the developer's interactive traceback which they could use to execute malicious code.
+
+.. _interactive_debugging:
+
+Interactive debugging
+---------------------
+
+Things break, and when they do, quickly pinpointing what went wrong and why makes a huge difference. By default, Pylons uses a customized version of `Ian Bicking's <http://blog.ianbicking.org/>`_ EvalException middleware that also includes full Mako/Myghty Traceback information. 
+
+See the Enabling Debugging section of the `Getting Started <Getting+Started>`_ guide to enable the interactive debugging. 
+
+The Debugging Screen 
+-------------------- 
+
+The debugging screen has three tabs at the top: 
+
+``Traceback`` 
+Provides the raw exception trace with the interactive debugger 
+
+``Extra Data`` 
+Displays CGI, WSGI variables at the time of the exception, in addition to configuration information 
+
+``Template`` 
+Human friendly traceback for Mako or Myghty templates 
+
+Since Mako and Myghty compile their templates to Python modules, it can be difficult to accurately figure out what line of the template resulted in the error. The `Template` tab provides the full Mako or Myghty traceback which contains accurate line numbers for your templates, and where the error originated from. If your exception was triggered before a template was rendered, no Template information will be available in this section. 
+
+Example: Exploring the Traceback 
+-------------------------------- 
+
+Using the interactive debugger can also be useful to gain a deeper insight into objects present only during the web request like the ``session`` and ``request`` objects. 
+
+To trigger an error so that we can explore what's happening just raise an exception inside an action you're curious about. In this example, we'll raise an error in the action that's used to display the page you're reading this on. Here's what the docs controller looks like: 
+
+.. code-block:: python 
+
+    class DocsController(BaseController): 
+        def view(self, url): 
+            if request.path_info.endswith('docs'): 
+                redirect_to('/docs/') 
+            return render('/docs/' + url) 
+
+Since we want to explore the ``session`` and ``request``, we'll need to bind them first. Here's what our action now looks like with the binding and raising an exception: 
+
+.. code-block:: python 
+
+    def view(self, url): 
+        raise "hi" 
+        if request.path_info.endswith('docs'): 
+            redirect_to('/docs/') 
+        return render('/docs/' + url) 
+
+Here's what exploring the Traceback from the above example looks like (Excerpt of the relevant portion): 
+
+.. image:: _static/doctraceback.gif 
+
+Email Options 
+-------------
+
+You can make all sorts of changes to how the debugging works. For example if you disable the ``debug`` variable in the config file Pylons will email you an error report instead of displaying it as long as you provide your email address at the top of the config file: 
+
+.. code-block:: ini 
+
+    error_email_from = you@example.com 
+
+This is very useful for a production site. Emails are sent via SMTP so you need to specify a valid SMTP server too. 
+
+Changing the Debugger Theme 
+--------------------------- 
+
+If you are using Pylons in a commercial company it is useful to be able to change the theme of the debugger so that if an error occurs, a page with your company logo appears. You might also decide to remove the Pylons logo if you use the debugger a lot so that there is more space to view the traceback. 
+
+You can change the theme by creating a new template. For example, a very simple template might look like this: 
+
+.. code-block:: python 
+
+    my_error_template = ''' 
+    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" 
+    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> 
+    <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> 
+    <head> 
+    <title>Server Error</title> 
+    %(head)s 
+    <body id="documentation"> 
+    %(extra_data)s 
+    %(template_data)s 
+    %(traceback_data)s 
+    </body> 
+    </html> 
+    ''' 
+
+The values are automatically substituted by the error middleware. You can also add ``%(prefix)s`` which is replaced by the path to your application so you can include CSS files or images. For example if your application had a file called ``style.css`` in a directory called ``css`` within your ``public`` directory, you could add the following line to your template to ensure that the CSS file was always correctly found: 
+
+.. code-block:: html 
+
+    <link rel="stylesheet" href="%(prefix)s/css/style.css" type="text/css" media="screen" /> 
+
+If you want to retain the ability to switch between the different error displays you need a slightly more complicated example: 
+
+.. code-block:: python 
+
+    my_error_template = ''' 
+    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" 
+    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> 
+    <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> 
+    <head> 
+    <title>Server Error</title> 
+    %(head)s 
+    <body id="documentation" onload="switch_display('%(set_tab)s')"> 
+    <ul id="navlist"> 
+    <li id='traceback_data_tab' class="active"> 
+    <a href="javascript:switch_display('traceback_data')" id='traceback_data_link'>Traceback</a> 
+    </li> 
+    <li id='extra_data_tab' class=""> 
+    <a href="javascript:switch_display('extra_data')" id='extra_data_link'>Extra Data</a> 
+    </li> 
+    <li id='template_data_tab'> 
+    <a href="javascript:switch_display('template_data')" id='template_data_link'>Template</a> 
+    </li> 
+    </ul> 
+    <div id="extra_data" class="hidden-data"> 
+    %(extra_data)s 
+    </div> 
+    <div id="template_data" class="hidden-data"> 
+    %(template_data)s 
+    </div> 
+    <div id="traceback_data"> 
+    %(traceback_data)s 
+    </div> 
+    </body> 
+    </html> 
+    ''' 
+
+In this case when you click on a link the relevant tab is displayed. As long as you keep the same IDs and class names, you can specify your own styles and create a theme like the one used by Pylons by default. 
+
+Now that you have a template you need to use it in your application. In ``config/middleware.py`` change the following lines: 
+
+.. code-block:: python 
+
+    # Error Handling 
+    app = ErrorHandler(app, 
+            global_conf, error_template=error_template, **config.errorware) 
+
+to use your template: 
+
+.. code-block:: python 
+
+    my_error_template = ''' 
+    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" 
+    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> 
+    <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> 
+        <head> 
+            <title>Server Error</title> 
+            %(head)s 
+        <body id="documentation"> 
+            %(extra_data)s 
+            %(template_data)s 
+            %(traceback_data)s 
+        </body> 
+    </html> 
+    ''' 
+    app = ErrorHandler(app, global_conf, 
+            error_template=my_error_template, **config.errorware) 
+
+Your interactive debugger will now be themed with the new template. 
+ 
+
+Getting Information From Configuration Files 
+============================================ 
+
+All information from your configuration file is available in the ``pylons.config`` object. ``pylons.config`` also contains runtime configuration as defined in your project's ``config.environment`` module. 
+
+.. code-block :: python 
+
+    from pylons import config 
+
+``pylons.config`` behaves like a dictionary. For example you can obtain the location of the cache directory like this: 
+
+.. code-block :: python 
+
+    cache_dir = config.get('cache_dir') 
+
+Or the debug status like this: 
+
+.. code-block :: python 
+
+    debug = config.get('debug') 
+
+
 XXX: Explain run-time config, the ini format used by development.ini and the
 other ini files and how that affects the run-time configuration
 
 URL Configuration
 =================
 
-XXX: Explanation of how the default route can map to any controller, how to add routes, link to Routes manual
+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` (found in :file:`YOURPROJ/config/routing.py`):
 
+.. code-block:: python
+
+	map.connect(':controller/:action/:id')
+
+A part of the path beginning with a ``:`` means that it is a variable 
+that will match that part of the URL. The default mapping can match to 
+any of your controllers, and any of their actions, which means the 
+following URLs will match like so:
+
+.. code-block:: text
+
+	/entry/view/4      >>    controller: entry, action: view, id:4
+	/comment/edit/2    >>    controller: comment, action: edit, id:2
+
+
+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 you want to match.
+
+Since the first ``/`` doesn't need to be in the route, adding the 
+``/`` match looks like this:
+
+.. code-block:: python
+
+	map.connect('', controller='main', action='index')
+
+Generating URLs
+---------------
+
+URLs can be generated using the helper method :meth:`url_for`, which by 
+default in a Pylons project will be under the :data:`h` global variable.
+
+Keyword arguments indicating the controller and action to use can be 
+passed directly in:
+
+.. code-block:: python
+	
+	# generates /content/view/2
+	h.url_for(controller='content', action='view', id=2)  
+
+Inside your templates, you might notice that other parts seem to creep into the
+URLs generated. This is due to 
+`Routes memory <http://routes.groovie.org/manual.html#route-memory>`_ and can be 
+disabled by specifying the controller with a ``/`` in front like so:
+
+.. code-block:: python
+
+	# generates /content/view/2
+	h.url_for(controller='/content', action='view', id=2)   
+
+Keeping methods private
+-----------------------
+
+Since the default route will map any controller and action, you will probably 
+want to prevent some methods in a controller from being callable from a URL.
+
+Routes uses the default Python convention of private methods beginning with
+``_``. To hide a method ``edit_generic`` in this class, just changing its name
+to begin with ``_`` will be sufficient:
+
+.. code-block:: python
+
+	class UserController(BaseController):
+		def index(self):
+			return Response("This is the index.")
+	
+		def _edit_generic(self):
+			"I can't be called from the web!"
+			return True
+
+.. seealso::
+
+    `Routes manual <http://routes.groovie.org/manual.html>`_
+    Full details and source code.
 
 .. _middleware-config:
 
 Middleware
 ==========
 
+PrefixMiddleware 
+---------------- 
+
+``PrefixMiddleware`` provides a way to manually override the root prefix (``SCRIPT_NAME``) of your application for certain situations. 
+
+When running an application under a prefix (such as '``/james``') in FastCGI/apache, the ``SCRIPT_NAME`` environment variable is automatically set to to the appropriate value: '``/james``'. Pylons' URL generating functions such as ``url_for`` always take the ``SCRIPT_NAME`` value into account. 
+
+One situation where ``PrefixMiddleware`` is required is when an application is accessed via a reverse proxy with a prefix. The application is accessed through the reverse proxy via the the URL prefix '``/james``', whereas the reverse proxy forwards those requests to the application at the prefix '``/``'. 
+
+The reverse proxy, being an entirely separate web server, has no way of specifying the ``SCRIPT_NAME`` variable; it must be manually set by a ``PrefixMiddleware`` instance. Without setting ``SCRIPT_NAME``, ``url_for`` will generate URLs such as: '``/purchase_orders/1``', when it should be generating: '``/james/purchase_orders/1``'. 
+
+To filter your application through a ``PrefixMiddleware`` instance, add the following to the '``[app:main]``' section of your .ini file: 
+
+.. code-block :: ini 
+
+    filter-with = proxy-prefix 
+
+    [filter:proxy-prefix] 
+    use = egg:PasteDeploy#prefix 
+    prefix = /james 
+
+The name ``proxy-prefix`` simply acts as an identifier of the filter section; feel free to rename it. 
+
+These .ini settings are equivalent to adding the following to the end of your application's ``config/middleware.py``, right before the ``return app`` line: 
+
+.. code-block :: python 
+
+    # This app is served behind a proxy via the following prefix (SCRIPT_NAME) 
+    app = PrefixMiddleware(app, global_conf, prefix='/james') 
+
+This requires the additional import line: 
+
+.. code-block :: python 
+
+    from paste.deploy.config import PrefixMiddleware 
+
+Whereas the modification to ``config/middleware.py`` will setup an instance of ``PrefixMiddleware`` under every environment (.ini). 
+
+
 XXX: How to change the middleware, the purpose of full_stack, changing when
 middleware is used in the stack
 
 
 XXX: Explain how to setup app dependencies in the setup.py file to ensure
 the appropriate libraries are required, explain what setup.py needs, etc.
+
+.. _logging:
+
+Logging
+=======
+
+Logging messages 
+----------------
+ 
+As of Pylons 0.9.6, Pylons controllers (created via ``paster 
+controller/restcontroller``) and ``websetup.py`` create their own Logger objects 
+via `Python's logging module <http://docs.python.org/lib/module-logging.html>`_. 
+
+For example, in the helloworld project's hello controller 
+(``helloworld/controllers/hello.py``): 
+
+.. code-block:: python 
+
+    import logging 
+
+    from helloworld.lib.base import * 
+
+    log = logging.getLogger(__name__) 
+
+    class HelloController(BaseController): 
+
+        def index(self): 
+            # Return a... 
+
+Python's special ``__name__`` variable refers to the current module's fully 
+qualified name; in this case, ``helloworld.controllers.hello``. 
+
+To log messages, simply use methods available on that Logger object: 
+
+.. code-block:: python 
+
+    import logging 
+
+    from helloworld.lib.base import * 
+
+    log = logging.getLogger(__name__) 
+
+    class HelloController(BaseController): 
+
+        def index(self): 
+            content_type = 'text/plain' 
+            content = 'Hello World!' 
+
+            log.debug('Returning: %s (content-type: %s)', content, content_type) 
+            response.content_type = content_type 
+            return content 
+
+Which will result in the following printed to the console, on stderr: 
+
+.. code-block:: text 
+
+    16:20:20,440 DEBUG [helloworld.controllers.hello] Returning: Hello World! (content-type: text/plain) 
+
+
+
+Basic Logging configuration 
+---------------------------
+ 
+As of Pylons 0.9.6, the default ini files include a basic configuration for the 
+logging module. Paste ini files use the Python standard `ConfigParser format 
+<http://docs.python.org/lib/module-ConfigParser.html>`_; the same format used 
+for the Python `logging module's Configuration file format 
+<http://docs.python.org/lib/logging-config-fileformat.html>`_. 
+
+``paster``, when loading an application via the ``paster`` ``serve``, ``shell`` 
+or ``setup-app`` commands, calls the `logging.fileConfig function 
+<http://docs.python.org/lib/logging-config-api.html>`_ on that specified ini 
+file if it contains a 'loggers' entry. ``logging.fileConfig`` reads the logging 
+configuration from a ``ConfigParser`` file. 
+
+Logging configuration is provided in both the default ``development.ini`` and 
+the production ini file (created via ``paster make-config <package_name> 
+<ini_file>``). The production ini's logging setup is a little simpler than the 
+``development.ini``'s, and is as follows: 
+
+.. code-block:: ini 
+
+    # Logging configuration 
+    [loggers] 
+    keys = root 
+
+    [handlers] 
+    keys = console 
+
+    [formatters] 
+    keys = generic 
+
+    [logger_root] 
+    level = INFO 
+    handlers = console 
+
+    [handler_console] 
+    class = StreamHandler 
+    args = (sys.stderr,) 
+    level = NOTSET 
+    formatter = generic 
+
+    [formatter_generic] 
+    format = %(asctime)s %(levelname)-5.5s [%(name)s] %(message)s 
+
+One root Logger is created that logs only messages at a level above or equal to 
+the ``INFO`` level to stderr, with the following format: 
+
+.. code-block:: text 
+
+    2007-08-17 15:04:08,704 INFO [helloworld.controllers.hello] Loading resource, id: 86 
+
+For those familiar with the ``logging.basicConfig`` function, this configuration 
+is equivalent to the code: 
+
+.. code-block:: python 
+
+    logging.basicConfig(level=logging.INFO, 
+    format='%(asctime)s %(levelname)-5.5s [%(name)s] %(message)s') 
+
+
+The default ``development.ini``'s logging section has a couple of differences: 
+it uses a less verbose timestamp, and defaults your application's log messages 
+to the ``DEBUG`` level (described in the next section). 
+
+Pylons and many other libraries (such as Beaker, SQLAlchemy, Paste) log a number 
+of messages for debugging purposes. Switching the root Logger level to ``DEBUG`` 
+reveals them: 
+
+.. code-block:: ini 
+
+    [logger_root] 
+    #level = INFO 
+    level = DEBUG 
+    handlers = console 
+
+Filtering log messages 
+^^^^^^^^^^^^^^^^^^^^^^ 
+
+Often there's too much log output to sift through, such as when switching 
+the root Logger's level to ``DEBUG``. 
+
+An example: you're diagnosing database connection issues in your application and 
+only want to see SQLAlchemy's ``DEBUG`` messages in relation to database 
+connection pooling. You can leave the root Logger's level at the less verbose 
+``INFO`` level and set that particular SQLAlchemy Logger to ``DEBUG`` on its 
+own, apart from the root Logger: 
+
+.. code-block:: ini 
+
+    [logger_sqlalchemy.pool] 
+    level = DEBUG 
+    handlers = 
+    qualname = sqlalchemy.pool 
+
+then add it to the list of Loggers: 
+
+.. code-block:: ini 
+
+    [loggers] 
+    keys = root, sqlalchemy.pool 
+
+No Handlers need to be configured for this Logger as by default non root Loggers 
+will propagate their log records up to their parent Logger's Handlers. The root 
+Logger is the top level parent of all Loggers. 
+
+This technique is used in the default ``development.ini``. The root Logger's 
+level is set to ``INFO``, whereas the application's log level is set to 
+``DEBUG``: 
+
+.. code-block:: ini 
+
+    # Logging configuration 
+    [loggers] 
+    keys = root, helloworld 
+
+.. code-block:: ini 
+
+    [logger_helloworld] 
+    level = DEBUG 
+    handlers = 
+    qualname = helloworld 
+
+All of the child Loggers of the helloworld Logger will inherit the ``DEBUG`` 
+level unless they're explicitly set differently. Meaning the 
+``helloworld.controllers.hello``, ``helloworld.websetup`` (and all your app's 
+modules') Loggers by default have an effective level of ``DEBUG`` too. 
+
+For more advanced filtering, the logging module provides a `Filter 
+<http://docs.python.org/lib/node423.html>`_ object; however it cannot be used 
+directly from the configuration file. 
+
+Advanced Configuration 
+---------------------- 
+To capture log output to a separate file, use a `FileHandler 
+<http://docs.python.org/lib/node412.html>`_ (or a `RotatingFileHandler 
+<http://docs.python.org/lib/node413.html>`_): 
+
+.. code-block:: ini 
+
+    [handler_accesslog] 
+    class = FileHandler 
+    args = ('access.log','a') 
+    level = INFO 
+    formatter = generic 
+
+Before it's recognized, it needs to be added to the list of Handlers: 
+
+.. code-block:: ini 
+
+    [handlers] 
+    keys = console, accesslog 
+
+and finally utilized by a Logger. 
+
+.. code-block:: ini 
+
+    [logger_root] 
+    level = INFO 
+    handlers = console, accesslog 
+
+These final 3 lines of configuration directs all of the root Logger's output to 
+the access.log as well as the console; we'll want to disable this for the next 
+section. 
+
+Request logging with Paste's TransLogger 
+---------------------------------------- 
+Paste provides the `TransLogger 
+<http://pythonpaste.org/module-paste.translogger.html>`_ middleware for logging 
+requests using the `Apache Combined Log Format 
+<http://httpd.apache.org/docs/2.2/logs.html#combined>`_. TransLogger combined 
+with a FileHandler can be used to create an ``access.log`` file similar to 
+Apache's. 
+
+Like any standard middleware with a Paste entry point, TransLogger can be 
+configured to wrap your application in the ``[app:main]`` section of the ini 
+file: 
+
+.. code-block:: ini 
+
+    filter-with = translogger 
+
+    [filter:translogger] 
+    use = egg:Paste#translogger 
+    setup_console_handler = False 
+
+This is equivalent to wrapping your app in a TransLogger instance via the bottom 
+of your project's ``config/middleware.py`` file: 
+
+.. code-block:: python 
+
+    from paste.translogger import TransLogger 
+    app = TransLogger(app, setup_console_handler=False) 
+    return app 
+
+TransLogger will automatically setup a logging Handler to the console when 
+called with no arguments, so it 'just works' in environments that don't 
+configure logging. Since we've configured our own logging Handlers, we need to 
+disable that option via ``setup_console_handler = False``. 
+
+With the filter in place, TransLogger's Logger (named the 'wsgi' Logger) will 
+propagate its log messages to the parent Logger (the root Logger), sending its 
+output to the console when we request a page: 
+
+.. code-block:: text 
+
+    00:50:53,694 INFO [helloworld.controllers.hello] Returning: Hello World! (content-type: text/plain) 
+    00:50:53,695 INFO [wsgi] 192.168.1.111 - - [11/Aug/2007:20:09:33 -0700] "GET /hello HTTP/1.1" 404 - "-" 
+    "Mozilla/5.0 (Macintosh; U; Intel Mac OS X; en-US; rv:1.8.1.6) Gecko/20070725 Firefox/2.0.0.6" 
+
+To direct TransLogger to the ``access.log`` FileHandler defined above, we need 
+to add that FileHandler to the wsgi Logger's list of Handlers: 
+
+.. code-block:: ini 
+
+    # Logging configuration 
+    [loggers] 
+    keys = root, wsgi 
+
+.. code-block:: ini 
+
+    [logger_wsgi] 
+    level = INFO 
+    handlers = handler_accesslog 
+    qualname = wsgi 
+    propagate = 0 
+
+As mentioned above, non-root Loggers by default propagate their log Records to 
+the root Logger's Handlers (currently the console Handler). Setting 
+``propagate`` to 0 (false) here disables this; so the ``wsgi`` Logger directs 
+its records only to the ``accesslog`` Handler. 
+
+Finally, there's no need to use the ``generic`` Formatter with TransLogger as 
+TransLogger itself provides all the information we need. We'll use a Formatter 
+that passes-through the log messages as is: 
+
+.. code-block:: ini 
+
+    [formatters] 
+    keys = generic, accesslog 
+
+.. code-block:: ini 
+
+    [formatter_accesslog] 
+    format = %(message)s 
+
+Then wire this new ``accesslog`` Formatter into the FileHandler: 
+
+.. code-block:: ini 
+
+    [handler_accesslog] 
+    class = FileHandler 
+    args = ('access.log','a') 
+    level = INFO 
+    formatter = accesslog 
+
+Logging to wsgi.errors 
+---------------------- 
+Pylons provides a custom logging Handler class, `pylons.log.WSGIErrorsHandler 
+<http://pylonshq.com/docs/class-pylons.log.WSGIErrorsHandler.html>`_, for 
+logging output to ``environ['wsgi.errors']``: the WSGI server's error stream 
+(see the `WSGI Spefification, PEP 333 
+<http://www.python.org/dev/peps/pep-0333/>`_ for more 
+information). ``wsgi.errors`` can be useful to log to in certain situations, 
+such as when deployed under Apache mod_wsgi/mod_python, where the 
+``wsgi.errors`` stream is the Apache error log. 
+
+To configure logging of only ``ERROR`` (and ``CRITICAL``) messages to 
+``wsgi.errors``, add the following to the ini file: 
+
+.. code-block:: ini 
+
+    [handlers] 
+    keys = console, wsgierrors 
+
+.. code-block:: ini 
+
+    [handler_wsgierrors] 
+    class = pylons.log.WSGIErrorsHandler 
+    args = () 
+    level = ERROR 
+    format = generic 
+
+then add the new Handler name to the list of Handlers used by the root Logger: 
+
+.. code-block:: ini 
+
+    [logger_root] 
+    level = INFO 
+    handlers = console, wsgierrors 
+
+.. warning :: 
+
+    ``WSGIErrorsHandler`` does not receive log messages created during
+    application startup. This is due to the ``wsgi.errors`` stream only being
+    available through the ``environ`` dictionary; which isn't available until a
+    request is made. 
+
+Lumberjacking with log4j's Chainsaw 
+=================================== 
+Java's ``log4j`` project provides the Java GUI application `Chainsaw 
+<http://logging.apache.org/log4j/docs/chainsaw.html>`_ for viewing and managing 
+log messages. Among its features are the ability to filter log messages on the 
+fly, and customizable color highlighting of log messages. 
+
+We can configure Python's logging module to output to a format parsable by 
+Chainsaw, ``log4j``'s `XMLLayout 
+<http://logging.apache.org/log4j/docs/api/org/apache/log4j/xml/XMLLayout.html>`_ 
+format. 
+
+To do so, we first need to install the `Python XMLLayout package 
+<http://pypi.python.org/pypi/XMLLayout>`_: 
+
+.. code-block:: bash 
+
+    $ easy_install XMLLayout 
+
+It provides a log Formatter that generates ``XMLLayout`` XML. It also provides 
+``RawSocketHandler``; like the logging module's ``SocketHandler``, it sends log 
+messages across the network, but does not pickle them. 
+
+The following is an example configuration for sending ``XMLLayout`` log messages 
+across the network to Chainsaw, if it were listening on `localhost` port `4448`: 
+
+.. code-block:: ini 
+
+    [handlers] 
+    keys = console, chainsaw 
+
+    [formatters] 
+    keys = generic, xmllayout 
+
+    [logger_root] 
+    level = INFO 
+    handlers = console, chainsaw 
+
+.. code-block:: ini 
+
+    [handler_chainsaw] 
+    class = xmllayout.RawSocketHandler 
+    args = ('localhost', 4448) 
+    level = NOTSET 
+    formatter = xmllayout 
+
+.. code-block:: ini 
+
+    [formatter_xmllayout] 
+    class = xmllayout.XMLLayout 
+
+This configures any log messages handled by the root Logger to also be sent to 
+Chainsaw. The default ``development.ini`` configures the root Logger to the 
+``INFO`` level, however in the case of using Chainsaw, it is preferable to 
+configure the root Logger to ``NOTSET`` so *all* log messages are sent to 
+Chainsaw. Instead, we can restrict the console handler to the ``INFO`` level: 
+
+.. code-block:: ini 
+
+    [logger_root] 
+    level = NOTSET 
+    handlers = console 
+
+    [handler_console] 
+    class = StreamHandler 
+    args = (sys.stderr,) 
+    level = INFO 
+    formatter = generic 
+
+Chainsaw can be downloaded from its `home page 
+<http://logging.apache.org/log4j/docs/chainsaw.html>`_, but can also be launched 
+directly from a Java-enabled browser via the link: `Chainsaw web start 
+<http://logging.apache.org/log4j/docs/webstart/chainsaw/chainsawWebStart.jnlp>`_.
+
+It can be configured from the GUI, but it also supports reading its 
+configuration from a ``log4j.xml`` file. 
+
+The following ``log4j.xml`` file configures Chainsaw to listen on port `4448` 
+for ``XMLLayout`` style log messages. It also hides Chainsaw's own logging 
+messages under the ``WARN`` level, so only your app's log messages are 
+displayed: 
+
+.. code-block:: xml 
+
+    <?xml version="1.0" encoding="UTF-8" ?> 
+    <!DOCTYPE configuration> 
+    <configuration xmlns="http://logging.apache.org/"> 
+
+    <plugin name="XMLSocketReceiver" class="org.apache.log4j.net.XMLSocketReceiver"> 
+        <param name="decoder" value="org.apache.log4j.xml.XMLDecoder"/> 
+        <param name="port" value="4448"/> 
+    </plugin> 
+
+    <logger name="org.apache.log4j"> 
+        <level value="warn"/> 
+    </logger> 
+
+    <root> 
+        <level value="debug"/> 
+    </root> 
+
+    </configuration> 
+
+Chainsaw will prompt for a configuration file upon startup. The configuration 
+can also be loaded later by clicking `File`/`Load Log4J File...`. You should see 
+an XMLSocketReceiver instance loaded in Chainsaw's Receiver list, configured at 
+port `4448`, ready to receive log messages. 
+
+Here's how the Pylons stack's log messages can look with colors defined (using 
+Chainsaw on OS X): 
+
+.. image:: _static/Pylons_Stack-Chainsaw-OSX.png 
+    :width: 900 
+    :height: 563 
+    :target: _/static/Pylons_Stack-Chainsaw-OSX.png 
+
+Alternate Logging Configuration style
+=====================================
+
+Pylons' default ini files include a basic configuration for Python's logging
+module. Its format matches the standard Python :mod:`logging` module's `config file format <http://docs.python.org/lib/logging-config-fileformat.html>`_ . If a 
+more concise format is preferred, here is Max Ischenko's demonstration of 
+an alternative style to setup logging.
+
+The following function is called at the application start up (e.g. Global ctor):
+
+.. code-block:: python
+
+    def setup_logging():
+        logfile = config['logfile']
+        if logfile == 'STDOUT': # special value, used for unit testing
+            logging.basicConfig(stream=sys.stdout, level=logging.DEBUG,
+                   #format='%(name)s %(levelname)s %(message)s',
+                   #format='%(asctime)s,%(msecs)d %(levelname)s %(message)s',
+                   format='%(asctime)s,%(msecs)d %(name)s %(levelname)s %(message)s',
+                   datefmt='%H:%M:%S')
+        else:
+            logdir = os.path.dirname(os.path.abspath(logfile))
+            if not os.path.exists(logdir):
+                os.makedirs(logdir)
+            logging.basicConfig(filename=logfile, mode='at+',
+                 level=logging.DEBUG,
+                 format='%(asctime)s,%(msecs)d %(name)s %(levelname)s %(message)s',
+                 datefmt='%Y-%b-%d %H:%M:%S')
+        setup_thirdparty_logging()
+
+The setup_thirdparty_logging function searches through the certain keys of the
+application ``.ini`` file which specify logging level for a particular logger
+(module).
+
+.. code-block:: python
+
+    def setup_thirdparty_logging():
+        for key in config:
+            if not key.endswith('logging'):
+                continue
+            value = config.get(key)
+            key = key.rstrip('.logging')
+            loglevel = logging.getLevelName(value)
+            log.info('Set %s logging for %s', logging.getLevelName(loglevel), key)
+            logging.getLogger(key).setLevel(loglevel)
+
+Relevant section of the .ini file (example):
+
+.. code-block:: ini
+
+	sqlalchemy.logging = WARNING
+	sqlalchemy.orm.unitofwork.logging = INFO
+	sqlalchemy.engine.logging = DEBUG
+	sqlalchemy.orm.logging = INFO
+	routes.logging = WARNING
+
+This means that routes logger (and all sub-loggers such as routes.mapper) only
+passes through messages of at least WARNING level; sqlalachemy defaults to
+WARNING level but some loggers are configured with more verbose level to aid
+debugging.
+
+

pylons/docs/controllers.rst

 .. _controllers:
 
+===========
 Controllers
 ===========
 
+About the controller
+--------------------
+
+.. image:: _static/pylon2.jpg
+   :alt: 
+   :align: left
+   :height: 450
+   :width: 368
+
+In the MVC paradigm the *controller* interprets the inputs, commanding the model and/or the view to change as appropriate.
+
+The controller is used to stitch together different pieces of the model and the view to fulfill a request. This places significant power into the developer’s hands. 
+
+Presented with a number of reusable building blocks in the model and the view, the controller picks and chooses which blocks are needed to handle specific processing and display requirements.
+
+The controller interprets requests from the user and calls portions of the model and view as necessary to fulfill the request. So when the user clicks a Web link or submits an HTML form, the controller itself doesn’t output anything or perform any real processing. It takes the request and determines which model components to invoke and which formatting to apply to the resulting data.
+
+Pylons uses a class, where the superclass provides the WSGI interface and the subclass implements the application-specific controller logic. 
+
+The Pylons WSGI Controller handles incoming web requests that are dispatched from the PylonsBaseWSGIApp. 
+
+These requests result in a new instance of the WSGIController being created, which is then called with the dict options from the Routes match. The standard WSGI response is then returned with start_response called as per the WSGI spec.
+
+Since Pylons controllers are actually called with the WSGI interface, normal WSGI applications can also be Pylons ‘controllers’.
+
+Standard Controllers
+====================
+
+Standard Controllers intended for subclassing by web developers
+
+
+Adding Controllers dynamically
+------------------------------
+
+It is possible for an application to add controllers without restarting the application. This requires telling Routes to re-scan the controllers directory.
+
+New controllers may be added from the command line with the paster command (recommended as that also creates the test harness file), or any other means of creating the controller file.
+
+For Routes to become aware of new controllers present in the controller directory, an internal flag is toggled to indicate that Routes should rescan the directory:
+
+.. code-block:: python
+
+    from routes import request_config
+
+    mapper = request_config().mapper
+    mapper._created_regs = False
+
+
+On the next request, Routes will rescan the controllers directory and those routes that use the ``:controller`` dynamic part of the path will be able to match the new controller.
+
+
+Attaching WSGI apps
+-------------------
+
+.. note::
+
+    This recipe assumes a basic level of familiarity with the WSGI Specification (PEP 333)
+
+WSGI runs deep through Pylons, and is present in many parts of the architecture. Since Pylons controllers are actually called with the WSGI interface, normal WSGI applications can also be Pylons 'controllers'. 
+
+Optionally, if a full WSGI app should be mounted and handle the remainder of the URL, Routes can automatically move the right part of the URL into the :envvar:`SCRIPT_NAME`, so that the WSGI application can properly handle its :envvar:`PATH_INFO` part.
+
+This recipe will demonstrate adding a basic WSGI app as a Pylons controller. 
+
+Create a new controller file in your Pylons project directory:
+
+.. code-block:: python
+
+    paster controller wsgiapp
+
+This sets up the basic imports that you may want available when using other WSGI applications.
+
+Edit your controller so it looks like this:
+
+.. code-block:: python
+
+    import logging
+
+    from YOURPROJ.lib.base import *
+
+    log = logging.getLogger(__name__)
+
+    def WsgiappController(environ, start_response):
+        start_response('200 OK', [('Content-type', 'text/plain')])
+        return ["Hello World"]
+
+When hooking up other WSGI applications, they will expect the part of the URL that was used to get to this controller to have been moved into :envvar:`SCRIPT_NAME`. :mod:`Routes` can properly adjust the environ if a :term:`map route` for this controller is added to the :file:`config/routing.py` file:
+
+.. code-block:: python
+
+    # CUSTOM ROUTES HERE
+
+    # Map the WSGI application
+    map.connect('wsgiapp/*path_info', controller='wsgiapp')
+
+
+By specifying the ``path_info`` dynamic path, Routes will put everything leading up to the ``path_info`` in the :envvar:`SCRIPT_NAME` and the rest will go in the :envvar:`PATH_INFO`.
+
+.. warning::
+
+    Is this still true of Routes 2?
+
+
+Adding trailing slashes
+-----------------------
+
+From a search engine point of view, it is important that every URL in your application has one canonical location. It's not true by default in your Pylons app but it is easy to fix.
+
+.. warning::
+
+    Will it be true by default in Routes 2?
+
+1. Routes has undocumented :data:`append_slash` option which adds a trailing '/' to every URL that it is asked to generate for you. So update :file:`routing.py` like this:
+
+    .. code-block:: python
+
+        map = Mapper(...)
+        map.append_slash = True
+
+2. In addition to generating correct URLs, you also want to redirect the user to the canonical page location if the '/' wasn't specified. One possible solution is to add the following code to :file:`YOURPROJ/lib/base.py`:
+
+    .. code-block:: python
+
+        from paste.request import construct_url
+        from paste.httpexceptions import HTTPMovedPermanently
+
+        class BaseController(WSGIController):
+            # ...
+            def __call__(self, environ, start_response):
+                """Invoke the Controller"""
+                if not environ['PATH_INFO'].endswith('/'):
+                    environ['PATH_INFO'] += '/'
+                    url = construct_url(environ)
+                    raise HTTPMovedPermanently(url)
+                return WSGIController.__call__(self, environ, start_response)
+
+3. Adjust :file:`YOURPROJ/config/middleware.py`
+
+    In the imports:
+
+    .. code-block:: python
+
+        from paste import httpexceptions
+
+    In :meth:`make_app` after instantiating the PylonsApp
+
+    .. code-block:: python
+
+        app = httpexceptions.make_middleware(app, global_conf)
+
+.. seealso::
+
+     `Trailing spaces <http://jtauber.com/blog/2007/08/22/trailing_slashes/>`_
+     
+     James' Tauber's blog post - *The biggest mistake I made in 
+     Leonardo was making "foo" and "foo/" mean the same thing.*
+
+Using the WSGI Controller to provide a WSGI service
+===================================================
+
+The Pylons WSGI Controller
+--------------------------
+
+Pylons' own WSGI Controller follows the WSGI spec for calling and return
+values
+
+The Pylons WSGI Controller handles incoming web requests that are 
+dispatched from the ``PylonsBaseWSGIApp``. These requests result in a
+new instance of the ``WSGIController`` being created, which is then
+called with the dict options from the Routes match. The standard
+WSGI response is then returned with :meth:`start_response` called as per
+the WSGI spec.
+
+WSGIController methods
+----------------------
+
+
+Special WSGIController methods you may define:
+
+``__before__``
+    This method will be run before your action is, and should be
+    used for setting up variables/objects, restricting access to
+    other actions, or other tasks which should be executed before
+    the action is called.
+``__after__``
+    Method to run after the action is run. This method will
+    *always* be run after your method, even if it raises an
+    Exception or redirects.
+    
+Each action to be called is inspected with :meth:`_inspect_call` so
+that it is only passed the arguments in the Routes match dict that
+it asks for. The arguments passed into the action can be customized
+by overriding the :meth:`_get_method_args` function which is
+expected to return a dict.
+
+In the event that an action is not found to handle the request, the
+Controller will raise an "Action Not Found" error if in debug mode,
+otherwise a ``404 Not Found`` error will be returned.
+
+.. _rest_controller:
+
+Using the REST Controller with a RESTful API
+============================================
+
+Using the paster restcontroller temlate
+---------------------------------------
+
+.. code-block:: bash
+
+    $ paster restcontroller --help
+
+Create a REST Controller and accompanying functional test
+
+The RestController command will create a REST-based Controller file
+for use with the :meth:`~routes.base.Mapper.resource`
+REST-based dispatching. This template includes the methods that
+:meth:`~routes.base.Mapper.resource` dispatches to in
+addition to doc strings for clarification on when the methods will
+be called.
+
+The first argument should be the singular form of the REST
+resource. The second argument is the plural form of the word. If
+its a nested controller, put the directory information in front as
+shown in the second example below.
+
+Example usage:
+
+.. code-block:: bash
+
+    yourproj% paster restcontroller comment comments
+    Creating yourproj/yourproj/controllers/comments.py
+    Creating yourproj/yourproj/tests/functional/test_comments.py
+
+If you'd like to have controllers underneath a directory, just
+include the path as the controller name and the necessary
+directories will be created for you:
+
+.. code-block:: bash
+
+    yourproj% paster restcontroller admin/tracback admin/trackbacks
+    Creating yourproj/controllers/admin
+    Creating yourproj/yourproj/controllers/admin/trackbacks.py
+    Creating yourproj/yourproj/tests/functional/test_admin_trackbacks.py
+
+An Atom-Style REST Controller for Users
+---------------------------------------
+
+.. code-block:: python
+
+    # From http://pylonshq.com/pasties/503
+    import logging
+
+    from simplejson import dumps
+    from formencode.api import Invalid
+    from restmarks.lib.base import *
+
+    log = logging.getLogger(__name__)
+
+    class UsersController(BaseController):
+        """REST Controller styled on the Atom Publishing Protocol"""
+        # To properly map this controller, ensure your 
+        # config/routing.py file has a resource setup:
+        #     map.resource('user', 'users')
+
+        def index(self, format='html'):
+            """GET /users: All items in the collection.<br>
+                @param format the format passed from the URI.
+            """
+            #url_for('users')
+            users = model.User.select()
+            if format=='json':
+                data = []
+                for user in users:
+                    d = user._state['original'].data
+                    del d['password']
+                    d['link'] = h.url_for('user', id=user.name)
+                    data.append(d)
+                response.headers['content-type'] = 'text/javascript'
+                return dumps(data)
+            else:
+                c.users = users
+                return render('/users/index_user.mako')
+
+        def create(self):
+            """POST /users: Create a new item."""
+            # url_for('users')
+            user = model.User.get_by(name=request.params['name'])
+            if user:
+                # The client tried to create a user that already exists
+                abort(409, '409 Conflict', 
+                      headers=[('location', 
+                                 h.url_for('user', id=user.name)), ])
+            else:
+                try:
+                    # Validate the data that was sent to us
+                    params = model.forms.UserForm.to_python(request.params)
+                except Invalid, e:
+                    # Something didn't validate correctly
+                    abort(400, '400 Bad Request -- '+str(e))
+                user = model.User(**params)
+                model.objectstore.flush()
+                response.headers['location'] = \
+                    h.url_for('user', id=user.name)
+                response.status_code = 201
+                c.user_name = user.name
+                return render('/users/created_user.mako')
+
+        def new(self, format='html'):
+            """GET /users/new: Form to create a new item.
+                @param format the format passed from the URI.
+            """
+            # url_for('new_user')
+            return render('/users/new_user.mako')
+
+        def update(self, id):
+            """PUT /users/id: Update an existing item.
+                @param id the id (name) of the user to be updated
+            """
+            # Forms posted to this method should contain a hidden field:
+            #    <input type="hidden" name="_method" value="PUT" />
+            # Or using helpers:
+            #    h.form(h.url_for('user', id=ID),
+            #           method='put')
+            # url_for('user', id=ID)
+            old_name = id
+            new_name = request.params['name']
+            user = model.User.get_by(name=id)
+
+            if user:
+                if (old_name != new_name) and \
+                        model.User.get_by(name=new_name):
+                    abort(409, '409 Conflict')
+                else:
+                    params = model.forms.UserForm.to_python(request.params)
+                    user.name = params['name']
+                    user.full_name = params['full_name']
+                    user.email = params['email']
+                    user.password = params['password']
+                    model.objectstore.flush()
+                    if user.name != old_name:
+                        abort(301, '301 Moved Permanently',
+                              [('Location', 
+                                h.url_for('users', id=user.name)),])
+                    else:
+                        return ''
+
+        def delete(self, id):
+            """DELETE /users/id: Delete an existing item.
+                @param id the id (name) of the user to be updated
+            """
+            # Forms posted to this method should contain a hidden field:
+            #    <input type="hidden" name="_method" value="DELETE" />
+            # Or using helpers:
+            #    h.form(h.url_for('user', id=ID),
+            #           method='delete')
+            # url_for('user', id=ID)
+            user = model.User.get_by(name=id)
+            user.delete()
+            model.objectstore.flush()
+            return ''
+
+        def show(self, id, format='html'):
+            """GET /users/id: Show a specific item.
+                @param id the id (name) of the user to be updated.
+                @param format the format of the URI requested.
+            """
+            # url_for('user', id=ID)
+            user = model.User.get_by(name=id)
+            if user:
+                if format=='json':
+                    data = user._state['original'].data
+                    del data['password']
+                    data['link'] = h.url_for('user', id=user.name)
+                    response.headers['content-type'] = 'text/javascript'
+                    return dumps(data)
+                else:
+                    c.data = user
+                    return render('/users/show_user.mako')
+            else:
+                abort(404, '404 Not Found')
+
+        def edit(self, id, format='html'):
+            """GET /users/id;edit: Form to edit an existing item.
+                @param id the id (name) of the user to be updated.
+                @param format the format of the URI requested.
+            """
+            # url_for('edit_user', id=ID)
+            user = model.User.get_by(name=id)
+            if not user:
+                abort(404, '404 Not Found')
+            # Get the form values from the table
+            c.values = model.forms.UserForm.from_python(user.__dict__)
+            return render('/users/edit_user.mako')
+
+.. _xmlrpc_controller:
+
+Using the XML-RPC Controller for XML-RPC requests
+================================================= 
+
+In order to deploy this controller you will need at least a passing familiarity with XML-RPC itself. We will first review the basics of XML-RPC and then describe the workings of the ``Pylons XMLRPCController``. Finally, we will show an example of how to use the controller to implement a simple web service. 
+
+After you've read this document, you may be interested in reading the companion document: "A blog publishing web service in XML-RPC" which takes the subject further, covering details of the MetaWeblog API (a popular XML-RPC service) and demonstrating how to construct some basic service methods to act as the core of a MetaWeblog blog publishing service. 
+
+A brief introduction to XML-RPC
+------------------------------- 
+
+XML-RPC is a specification that describes a Remote Procedure Call (RPC) interface by which an application can use the Internet to execute a specified procedure call on a remote XML-RPC server. The name of the procedure to be called and any required parameter values are "marshalled" into XML. The XML forms the body of a POST request which is despatched via HTTP to the XML-RPC server. At the server, the procedure is executed, the returned value(s) is/are marshalled into XML and despatched back to the application. XML-RPC is designed to be as simple as possible, while allowing complex data structures to be transmitted, processed and returned. 
+
+XML-RPC Controller that speaks WSGI 
+-----------------------------------
+
+Pylons uses Python's xmlrpclib library to provide a specialised :class:`XMLRPCController` class that gives you the full range of these XML-RPC Introspection facilities for use in your service methods and provides the foundation for constructing a set of specialised service methods that provide a useful web service --- such as a blog publishing interface. 
+
+This controller handles XML-RPC responses and complies with the `XML-RPC Specification <http://www.xmlrpc.com/spec>`_ as well as the `XML-RPC Introspection <http://scripts.incutio.com/xmlrpc/introspection.html>`_ specification. 
+
+As part of its basic functionality an XML-RPC server provides three standard introspection procedures or "service methods" as they are called. The Pylons :class:`XMLRPCController` class provides these standard service methods ready-made for you: 
+
+* :meth:`system.listMethods` Returns a list of XML-RPC methods for this XML-RPC resource 
+* :meth:`system.methodSignature` Returns an array of arrays for the valid signatures for a method. The first value of each array is the return value of the method. The result is an array to indicate multiple signatures a method may be capable of. 
+* :meth:`system.methodHelp` Returns the documentation for a method 
+
+By default, methods with names containing a dot are translated to use an underscore. For example, the ``system.methodHelp`` is handled by the method :meth:`system_methodHelp`. 
+
+Methods in the XML-RPC controller will be called with the method given in the XML-RPC body. Methods may be annotated with a signature attribute to declare the valid arguments and return types. 
+
+For example:
+
+.. code-block:: python
+
+    class MyXML(XMLRPCController): 
+        def userstatus(self): 
+            return 'basic string' 
+        userstatus.signature = [ [docmeta:'string'] ] 
+
+        def userinfo(self, username, age=None): 
+            user = LookUpUser(username) 
+            response = {'username':user.name} 
+            if age and age > 10: 
+                response[docmeta:'age'] = age 
+            return response 
+        userinfo.signature = [ [docmeta:'struct', 'string'], 
+                               [docmeta:'struct', 'string', 'int'] ] 
+
+
+Since XML-RPC methods can take different sets of data, each set of valid arguments is its own list. The first value in the list is the type of the return argument. The rest of the arguments are the types of the data that must be passed in. 
+
+In the last method in the example above, since the method can optionally take an integer value, both sets of valid parameter lists should be provided. 
+
+Valid types that can be checked in the signature and their corresponding Python types: 
+
++--------------------+--------------------+
+| XMLRPC             | Python             |
++====================+====================+
+| string             | str                |
++--------------------+--------------------+
+| array              | list               |
++--------------------+--------------------+
+| boolean            | bool               |
++--------------------+--------------------+
+| int                | int                |
++--------------------+--------------------+
+| double             | float              |
++--------------------+--------------------+
+| struct             | dict               |
++--------------------+--------------------+
+| dateTime.iso8601   | xmlrpclib.DateTime |
++--------------------+--------------------+
+| base64             | xmlrpclib.Binary   |
++--------------------+--------------------+
+
+Note, requiring a signature is optional. 
+
+Also note that a convenient fault handler function is provided. 
+
+.. code-block:: python 
+
+    def xmlrpc_fault(code, message): 
+        """Convenience method to return a Pylons response XMLRPC Fault""" 
+
+(The `XML-RPC Home page <http://www.xmlrpc.com/>`_ and the `XML-RPC HOW-TO <http://www.faqs.org/docs/Linux-HOWTO/XML-RPC-HOWTO.html>`_ both provide further detail on the XML-RPC specification.) 
+
+A simple XML-RPC service  
+------------------------
+
+This simple service ``test.battingOrder`` accepts a positive integer < 51 as the parameter ``posn`` and returns a string containing the name of the US state occupying that ranking in the order of ratifying the constitution / joining the union. 
+
+.. code-block:: python
+ 
+    import xmlrpclib 
+    import pylons 
+    from pylons import request 
+    from pylons.controllers import XMLRPCController 
+    from myapp.lib.base import * 
+
+    states = [docmeta:'Delaware', 'Pennsylvania', 'New Jersey', 
+             'Georgia', 'Connecticut', 'Massachusetts', 'Maryland', 
+             'South Carolina', 'New Hampshire', 'Virginia', 'New York', 
+             'North Carolina', 'Rhode Island', 'Vermont', 'Kentucky',
+             'Tennessee', 'Ohio', 'Louisiana', 'Indiana', 'Mississippi', 
+             'Illinois', 'Alabama', 'Maine', 'Missouri', 'Arkansas',
+             'Michigan', 'Florida', 'Texas', 'Iowa', 'Wisconsin',
+             'California', 'Minnesota', 'Oregon', 'Kansas', 'West Virginia',
+             'Nevada', 'Nebraska', 'Colorado', 'North Dakota', 'South Dakota',
+             'Montana', 'Washington', 'Idaho', 'Wyoming', 'Utah', 'Oklahoma',
+             'New Mexico', 'Arizona', 'Alaska', 'Hawaii'] 
+
+    class RpctestController(XMLRPCController): 
+
+        def test_battingOrder(self, posn): 
+            """This docstring becomes the content of the 
+            returned value for system.methodHelp called with 
+            the parameter "test.battingOrder"). The method 
+            signature will be appended below ... 
+            """ 
+            # XML-RPC checks agreement for arity and parameter datatype, so 
+            # by the time we get called, we know we have an int. 
+            if posn > 0 and posn < 51: 
+                return states[docmeta:posn-1] 
+            else: 
+                # Technically, the param value is correct: it is an int. 
+                # Raising an error is inappropriate, so instead we 
+                # return a facetious message as a string. 
+                return 'Out of cheese error.' 
+        test_battingOrder.signature = [ [docmeta:'string', 'int'] ] 
+
+
+Testing the service
+-------------------
+
+For developers using OS X, there's an `XML/RPC client <http://www.ditchnet.org/xmlrpc/>`_ that is an extremely useful diagnostic tool when developing XML-RPC (it's free ... but not entirely bug-free). Or, you can just use the Python interpreter: 
+
+.. code-block:: pycon
+
+    >>> from pprint import pprint 
+    >>> import xmlrpclib 
+    >>> srvr = xmlrpclib.Server("http://example.com/rpctest/") 
+    >>> pprint(srvr.system.listMethods()) 
+    [docmeta:'system.listMethods', 
+    'system.methodHelp', 
+    'system.methodSignature', 
+    'test.battingOrder'] 
+    >>> print srvr.system.methodHelp('test.battingOrder') 
+    This docstring becomes the content of the 
+    returned value for system.methodHelp called with 
+    the parameter "test.battingOrder"). The method 
+    signature will be appended below ... 
+
+    Method signature: [docmeta:['string', 'int']] 
+    >>> pprint(srvr.system.methodSignature('test.battingOrder')) 
+    [docmeta:['string', 'int']] 
+    >>> pprint(srvr.test.battingOrder(12)) 
+    'North Carolina' 
+
+To debug XML-RPC servers from Python, create the client object using the optional verbose=1 parameter. You can then use the client as normal and watch as the XML-RPC request and response is displayed in the console. 
+

pylons/docs/deployment.rst

 ========================
 
 XXX: how to package an app, deployment in package format, etc.
+
+Running Pylons apps with webservers
+===================================
+
+This document assumes that you have already installed a Pylons web application, and `created a configuration <Configuration+Files>`_ for it.  Pylons applications use `PasteDeploy <http://pythonpaste.org/deploy/>`_ to  start up your Pylons WSGI application, and can use the flup package to provide a Fast-CGI, SCGI, or AJP connection to it. 
+
+Using Fast-CGI 
+============== 
+
+`Fast-CGI <http://fastcgi.com/>`_ is a gateway to connect web severs like `Apache <http://httpd.apache.org/>`_ and `lighttpd <http://lighttpd.net/>`_ to a CGI-style application. Out of the box, Pylons applications can run with Fast-CGI in either a threaded or forking mode. (Threaded is the recommended choice) 
+
+Setting a Pylons application to use Fast-CGI is very easy, and merely requires you to change the config line like so: 
+
+.. code-block:: ini 
+
+    # default 
+    [server:main] 
+    use = egg:Paste#http 
+
+    # Use Fastcgi threaded 
+    [server:main] 
+    use = egg:PasteScript#flup_fcgi_thread 
+    host = 0.0.0.0 
+    port = 6500 
+
+Note that you will need to install the `flup <http://www.saddi.com/software/flup/dist/>`_ package, which can be 
+installed via easy_install: 
+
+.. code-block:: bash 
+
+    $ easy_install -U flup 
+
+The options in the config file are passed onto flup. The two common ways to run Fast CGI is either using a socket to listen for requests, or listening on a port/host which allows a webserver to send your requests to web applications on a different machine. 
+
+To configure for a socket, your ``server:main`` section should look like this: 
+
+.. code-block:: ini 
+
+    [server:main] 
+    use = egg:PasteScript#flup_fcgi_thread 
+    socket = /location/to/app.socket 
+
+If you want to listen on a host/port, the configuration cited in the first example will do the trick. 
+
+Apache Configuration 
+==================== 
+
+For this example, we will assume you're using Apache 2, though Apache 1 configuration will be very similar. First, make sure that you have the Apache `mod_fastcgi <http://fastcgi.com/mod_fastcgi/docs/mod_fastcgi.html>`_ module installed in 
+your Apache. 
+
+There will most likely be a section where you declare your FastCGI servers, and whether they're external: 
+
+.. code-block:: apacheconf 
+
+    <IfModule mod_fastcgi.c> 
+    FastCgiIpcDir /tmp 
+    FastCgiExternalServer /some/path/to/app/myapp.fcgi -host some.host.com:6200 
+    </IfModule> 
+
+In our example we'll assume you're going to run a Pylons web application listening on a host/port. Changing ``-host`` to ``-socket`` will let you use a Pylons web application listening on a socket. 
+
+The filename you give in the second option does not need to physically exist on the webserver, URIs that Apache resolve to this filename will be handled by the FastCGI application. 
+
+The other important line to ensure that your Apache webserver has is to indicate that fcgi scripts should be handled with Fast-CGI: 
+
+.. code-block:: apacheconf 
+
+    AddHandler fastcgi-script .fcgi 
+
+Finally, to configure your website to use the Fast CGI application you will need to indicate the script to be used: 
+
+.. code-block:: apacheconf 
+
+    <VirtualHost *:80> 
+        ServerAdmin george@monkey.com 
+        ServerName monkey.com 
+        ServerAlias www.monkey.com 
+        DocumentRoot /some/path/to/app 
+
+        ScriptAliasMatch ^(/.*)$ /some/path/to/app/myapp.fcgi$1 
+    </VirtualHost> 
+
+Other useful directives should be added as needed, for example, the ErrorLog directive, etc. This configuration will result in all requests being sent to your FastCGI application. 
+
+
+Distributing your project
+=========================
+
+As mentioned earlier eggs are a convenient format for packaging applications. You can create an egg for your project like this:
+
+.. code-block:: bash
+
+    $ cd helloworld
+    $ python setup.py bdist_egg
+
+Your egg will be in the ``dist`` directory and will be called ``helloworld-0.0.0dev-py2.4.egg``.
+
+You can change options in ``setup.py`` to change information about your project. For example change version to ``version="0.1.0",`` and run ``python setup.py bdist_egg`` again to produce a new egg with an updated version number.
+
+You can then register your application with the python CheeseShop at http://cheeseshop.python.org/pypi with the following command:
+
+.. code-block:: bash
+
+    $ setup.py register
+
+Note: You should not do this unless you actually want to register a package.
+
+If users want to install your software and have installed easy install they can install your new egg as follows:
+
+.. code-block:: bash
+
+    $ easy_install helloworld==0.1.0
+
+This will retrieve the package from the CheeseShop and install it. Alternatively you can install the egg locally:
+
+.. code-block:: bash
+
+    $ easy_install -f C:\path\with\the\egg\files\in helloworld==0.1.0
+
+In order to use the egg in a website you need to use Paste. You have already used Paste to create your Pylons template and to run a test server to test the tutorial application.
+
+Paste is a set of tools available at http://www.pythonpaste.org for providing a uniform way in which all compatible Python web frameworks can work together. To run a paste application such as any Pylons application you need to create a Paste configuration file. The idea is that the your paste configuration file will contain all the configuration for all the different Paste applications you run. A configuration file suitable for development is in the ``helloworld/development.ini`` file of the tutorial but the idea is that the person using your egg will add relevant configuration options to their own Paste configuration file so that your egg behaves they way they want.
+
+Paste configuration files can be run in many different ways, from CGI scripts, as standalone servers, with FastCGI, SCGI, mod_python and more. This flexibility means that your Pylons application can be run in virtually any environment and also take advantage of the speed benefits that the deployment option offers.
+
+Running your application
+------------------------
+
+In order to run your application your users will need to install it as described above but then generate a config file and setup your application before deploying it. This is described in `Application Configuration <Configuration+Files>`_.
+
+Packaging and deployment
+========================
+
+This document describes how a developer can take advantage of Pylons' application setup functionality to allow webmasters to easily setup their application. 
+
+Installation refers to the process of downloading and installing the application with easy_install whereas setup refers to the process of setting up an instance of an installed application so it is ready to be deployed. 
+
+For example, a wiki application might need to create database tables to use. The webmaster would only install the wiki ``.egg`` file once using easy_install but might want to run 5 wikis on the site so would setup the wiki 5 times, each time specifying a different database to use so that 5 wikis can run from the same code, but store their data in different databases. 
+
+Egg Files 
+========= 
+
+Before you can understand how a user configures an application you have to understand how Pylons applications are distributed. All Pylons applications are distributed in ``.egg`` format. An egg is simply a Python executable package that has been put together into a single file. 
+
+You create an egg from your project by going into the project root directory and running the command: 
+
+.. code-block:: bash 
+
+    $ python setup.py bdist_egg 
+
+If everything goes smoothly a ``.egg`` file with the correct name and version number appears in a newly created ``dist`` directory. 
+
+When a webmaster wants to install a Pylons application he will do so by downloading the egg and then installing it. 
+
+Installing as a Non-root User 
+============================= 
+
+It's quite possible when using shared hosting accounts that you do not have root access to install packages. In this 
+case you can install setuptools based packages like Pylons and Pylons web applications in your home directory using 
+the `virtual Python <http://peak.telecommunity.com/DevCenter/EasyInstall#creating-a-virtual-python>`_ setup. This way 
+you can install all the packages you want to use without super-user access. 
+
+Understanding the Setup Process 
+================================= 
+
+Say you have written a Pylons wiki application called ``wiki``. When a webmaster wants to install your wiki application he will run the following command to generate a config file: 
+
+.. code-block:: bash 
+
+$ paster make-config wiki wiki_production.ini 
+
+He will then edit the config file for his production environment with the settings he wants and then run this command to setup the application: 
+
+.. code-block:: bash 
+
+    $ paster setup-app wiki_production.ini 
+
+Finally he might choose to deploy the wiki application through the paste server like this (although he could have chosen CGI/FastCGI/SCGI etc): 
+
+.. code-block:: bash 
+
+    $ paster serve wiki_production.ini 
+
+The idea is that an application only needs to be installed once but if necessary can be setup multiple times, each with a different configuration. 
+
+All Pylons applications are installed in the same way, so you as the developer need to know what to make the above commands work. 
+
+Make Config 
+----------- 
+
+The ``paster make-config`` command looks for the file ``paste_deploy_config.ini_tmpl`` and uses it as a basis for generating a new ``.ini`` file. 
+
+Using our new wiki example again the ``wiki