Commits

Mike Orr  committed c279702

Singficant doc update.

  • Participants
  • Parent commits e9dff9f

Comments (0)

Files changed (8)

 2.0b1 (unreleased)
-----------------
+------------------
 - Revamp documentation to focus on Pyramid's 'alchemy' scaffold.
+- Delete 'akhet' scaffold.
 
 1.0.2 (2011-07-20)
 ------------------
 Does Akhet without scaffolds require 'pyramid' dependency? If so, which
 versions, and how will it handle future Pyramid version changes? Is it still
 depending on Paste or PasteScript?
+
+Subclass the Pygments 'console' lexer to recognize virtualenv prompt prefix.

File docs/architecture.rst

 Pyramid Architecture
 %%%%%%%%%%%%%%%%%%%%
 
-Introduction
-============
+The Zzz application you created has several aspects similar to Pylons: INI
+files, a startup function, views (called Controllers in Pylons), templates, and
+models. However, the filenames are different and the API syntax is
+different.  Pyramid is more flexible than Pylons, so you can create a `minimal
+application`_ in a single Python module, and run it in the same module without
+an INI file or "pserve".  However, we'll stick to the 'alchemy' scaffold which
+creates a directory structure scalable to large applications.
 
-Here we'll go through the code in the application created in the previous
-chapter. (I.e., an application created from the 'alchemy' scaffold in Pyramid
-1.3 or the 'routesalchemy' scaffold in Pyramid 1.2 and earlier.) We'll look at
-what each part is, how it differs from Pylons, how to add the features Akhet 1
-had, and some alternative enhancements and their tradeoffs.
+Directory layout
+----------------
 
-INI files
-=========
+The default application contains the following files after you install
+it:
+
+.. code-block::  text
+
+    Zzz
+    ├── CHANGES.txt
+    ├── MANIFEST.in
+    ├── README.txt
+    ├── development.ini
+    ├── production.ini
+    ├── setup.cfg
+    ├── setup.py
+    ├── zzz
+    │   ├── __init__.py
+    │   ├── models.py
+    │   ├── scripts
+    │   │   ├── __init__.py
+    │   │   └── populate.py
+    │   ├── static
+    │   │   ├── favicon.ico
+    │   │   ├── footerbg.png
+    │   │   ├── headerbg.png
+    │   │   ├── ie6.css
+    │   │   ├── middlebg.png
+    │   │   ├── pylons.css
+    │   │   ├── pyramid.png
+    │   │   ├── pyramid-small.png
+    │   │   └── transparent.gif
+    │   ├── templates
+    │   │   └── mytemplate.pt
+    │   ├── tests.py
+    │   └── views.py
+    └── Zzz.egg-info
+        ├── dependency_links.txt
+        ├── entry_points.txt
+        ├── not-zip-safe
+        ├── PKG-INFO
+        ├── requires.txt
+        ├── SOURCES.txt
+        └── top_level.txt
+
+..
+   Generated via this command and manually resorted and some entries removed.
+   tree --noreport -n -I '*.pyc' Zzz  >/tmp/files
 
 development.ini
 ---------------
 
-*development.ini* is similar to Pylons but some of the options are different:
+*development.ini* has the same structure as Pylons, and it's actually simpler
+than earlier versions of Pyramid. The application and server sections look like
+this:
 
 .. code-block:: ini
 
     host = 0.0.0.0
     port = 6543
 
-The structure is the same as Pylons, but simpler than earlier versions of
-Pyramid. The "[app:main]" section is the runtime configuration for our
-application. The "[server:main]" section tells which WSGI server to run it
-under.  (We've omitted the logging sections, which are the bottom 2/3 of the
-file. We'll cover logging below.) 
+The logging sections will be covered in the logging chapter. Differences
+between development.ini and production.ini will be covered in the deployment
+chapter.
 
-The "use = egg:Zzz" tells which Python distribution to load, in this case our
-"Zzz" application. The "pyramid.\*" options are mostly debugging variables.
-Set any of them to "true" to enable various kinds of debug logging.
-"pyramid.reload_templates" tells whether to recheck the timestamp of template
-source files whenever it renders a template, in case the file has been updated
-since startup. (Mako and Chameleon respect this value, but not all template
-engines understand it.)  "pyramid.default_locale_name" sets the predominent
-region/language for the application.
+When you run "pserve development.ini", it does the following:
 
-"pyramid_includes" lists the "tweens" to wrap around the application. Tweens
-are like WSGI middleware but are specific to Pyramid. "pyramid_debugtoolbar" is
-the debug toolbar at the right margin of browser screens, and shows an
-interactive traceback screen if an exception occurs. "pyramid_tm" is the
-transaction manager which is covered in a later chapter.
+1. Activate logging based on the logging sections.
+2. Read the "[app:main]" section and instantiate the specified application.
+3. Read the "[server:main]" section and instantiate the specified server.
+4. Launch the server with the application, and let it process requests forever.
 
-To see the interactive traceback in action, edit *zzz/views.py* and add a line
-"raise RuntimeError" before the "return" line in my_view().
+In the app section, the "use = egg:Zzz" tells which Python distribution to
+load, in this case our "Zzz" application. The "pyramid.\*" options are mostly
+debugging variables.  Set any of them to "true" to enable various kinds of
+debug logging.  "pyramid.default_locale_name" sets the predominent
+region/language for the application.  "pyramid.reload_templates" tells whether
+to recheck the timestamp of template source files whenever it renders a
+template, in case the file has been updated since startup. (Mako and Chameleon
+respect this value, but not all template engines understand it.)
+
+"pyramid_includes" specifies optional "tweens" to wrap around the application.
+Tweens are like WSGI middleware but are specific to Pyramid. In other words,
+they're generic services that can be wrapped around a variety of applications.
+"pyramid_debugtoolbar" is the debug toolbar at the right margin of browser
+screens, and shows an interactive traceback screen if an exception occurs.
+"pyramid_tm" is the transaction manager, which is covered in a later chapter.
+
+(To see the interactive traceback in action, skip the "populate_Zzz" step or
+delete the "Zzz.db" file, and run pserve. It will error out because a
+database table doesn't exist. If it doesn't give an error, add a ``raise
+RuntimeError`` line in the ``my_view`` function in *zzz/views.py*.)
 
 "sqlalchemy.url" tells which database the application should use. "%(here)s"
-expands to the path of the directory containing this file.
-
-Your application can define its own additional options here, and they will be
-visible in the application code as Pyramid "settings".
-
-Some Pyramid add-ons also look here for configuration settings. For instance,
-Beaker looks for "cache.\*" and "session.\*" settings, and Mako looks for
-"mako.\*" settings.  You can use these to configure what kind of persistent
-session store to use, how long until idle sessions are discarded, and what
-character encoding to use for template output (the default is "utf-8").
-possible options are listed in the Pyramid manual or the add-on's manual.
+expands to the path of the directory containing the INI file.
 
 The "[server:main]" section is the same as in Pylons. It tells which WSGI
 server to run. Pyramid 1.3 defaults to the wsgiref HTTP server in the
 Python standard library. It's single-threaded so it can only handle one request
 at a time, but that's good enough for development or debugging. 
 
-I normally set "host = 127.0.0.1" and "port = 5000" after creating an
-application. That way it serves only request coming from the same computer
-rather than from any computer on the network. That enhances security when the
-debug toolbar is enabled. Port 5000 is the Pylons tradition, and it's easier to
-remember and type than 6543. 
-
 Pyramid no longer uses WSGI middleware by default. If you want to add your own
 middleware, see the `PasteDeploy manual`_ for the syntax. But first consider
 whether making a Pyramid tween would be more convenient.
 
-You can of course create multiple INI files if you want to try the application
-out under different configuration scenarios, for instance to compare a
-database and a PostgresSQL database. You can even run them simultaneously as
-long as each configuration specifies a different port.
 
-production.ini
---------------
-
-The default production file is just slightly different from the development
-file:
-
-.. code-block:: ini
-
-    [app:main]
-    use = egg:Zzz
-
-    pyramid.reload_templates = false
-    pyramid.debug_authorization = false
-    pyramid.debug_notfound = false
-    pyramid.debug_routematch = false
-    pyramid.debug_templates = false
-    pyramid.default_locale_name = en
-    pyramid.includes = pyramid_tm
-
-    sqlalchemy.url = sqlite:///%(here)s/Zzz.db
-
-    [server:main]
-    use = egg:pyramid#wsgiref
-    host = 0.0.0.0
-    port = 6543
-
-The most important difference here is that "pyramid_debugtoolbar" is NOT
-enabled. **This is vital for security!**  Otherwise miscreants can type
-arbitrary Python commands in the interactive traceback if an exception occurs,
-and potentially read password files or damage the system.
-
-If an exception occurs during a production request, the user will get a plain
-white error screen, "A server error occurred.  Please contact the
-administrator." To customize that, see "Exception Views" in the Pyramid manual.
-The traceback will be dumped to the console, and will not be shown to the user.
-To customize how tracebacks are reported to the administrator, install the
-pyramid_exclog_ tween, which is covered below in Logging. (This replaces the
-WebError#error_catcher middleware which was used in Pylons and earlier versions
-of Pyramid.)
-
-The debug settings are all set to false in production. This saves a few CPU
-cycles while it's processing requests.
-
-The server section is unchanged from development.ini.  The correct settings
-here depend on what webserver you're running the application with, so you'll
-have to configure this part yourself. 
-
-If you're using Apache's mod_proxy to proxy to a Python HTTP server, you might
-want to change this to "use = egg:pyramid#cherrypy". This uses the CherryPy
-server, which is multithreaded like paste.httpserver (which Pylons and older
-versions of Pyramid used), but is more robust under high loads. (You'll have to
-install CherryPy to use this.)
-
-The Pyramid manual and Cookbook discuss other deployment scenarios like
-mod_wsgi and FastCGI.
-
-Logging
--------
-
-The bottom 2/3 of both INI files contain several sections to configure
-Python's logging system.  This is the same as in Pylons.  We can't explain the
-entire logging syntax here, but these are the sections most often customized by
-users:
-
-.. code-block:: ini
-
-    [logger_root]
-    level = INFO
-    handlers = console
-
-    [logger_zzz]
-    level = DEBUG
-    handlers =
-    qualname = zzz
-
-    [logger_sqlalchemy]
-    level = INFO
-    handlers =
-    qualname = sqlalchemy.engine
-    # "level = INFO" logs SQL queries.
-    # "level = DEBUG" logs SQL queries and results.
-    # "level = WARN" logs neither.  (Recommended for production systems.)
-
-These define a logger "root", "zzz" (the application's package name), and
-"sqlalchemy.engine" (specified in the qualname). Each has a 'level' variable
-which can be DEBUG, INFO, WARN, ERROR, or CRITICAL. Each level also logs the
-levels on its right, so WARN logs warnings and errors. Logger names are in a
-dotted hierarchy, so that "sqlalchemy.engine" affects all loggers below it
-("sqlalchemy.engine.ENGINE1", etc).  "root" affects all loggers that aren't
-otherwise specified.
-
-Generally, DEBUG is debugging information, INFO is chatty success messages,
-WARN means something might be wrong, ERROR means something is
-definitely wrong, and CRITICAL means you'd better fix it now or else. 
-But there's nothing to enforce this, so each library chooses how to log things.
-So SQLAlchemy logs SQL queries at the INFO level on
-"sqlalchemy.engine.ENGINE_NAME", even though some people would consider this
-debugging information. 
-
-Logger names do NOT automatically correspond to Python module names, although
-it's customary to do so if there's no better name for the logger. You can do
-this by putting the following at the top of each module:
-
-    import logging
-    log = logging.getLogger(__name__)
-
-This creates a variable ``log`` which is a logger named after the module. So if
-the module is ``zzz.views``, the logger is "zzz.views".
-
-The default *development.ini* displays all messages from the application
-modules (logger_zzz = DEBUG). It displays SQL queries (logger_sqlalchemy =
-INFO). It displays other messages only if they're warnings or above
-(logger_root = WARN).  The default *production.ini* sets all these to WARN, so
-it will not log anything except warnings or errors.
-
-By default, *development.ini* sets the root logger to WARN, the application
-logger to DEBUG, and the SQLAlchemy engine logger to INFO. This displays all
-application logging and SQL queries, but suppresses all other messages unless
-they're warnings or errors. *production.ini* sets all of these to WARN, to
-avoid filling up your log files with trivial success messages. You can adjust
-the log levels as you wish. You can also set other loggers to different levels
-by creating a section for them and listing them in the "[loggers]" section.
-they're warnings or errors. 
-
-"paster serve" activates logging when it starts up. If you're not using "paster
-serve", you can activate logging yourself this way::
-
-    import logging.config
-    logging.config.fileConfig(INI_FILENAME)
 
 Init module and main function
 =============================
 
 A Pyramid application revolves around a top-level ``main()`` function in the
-application package. "paster serve" does the equivalent of this::
+application package. "pserve" does the equivalent of this::
 
     # Instantiate your WSGI application
     import zzz
    :linenos:
 
     from pyramid.config import Configurator
-    import akhet
-    import pyramid_beaker
-    import sqlahelper
-    import sqlalchemy
+    from sqlalchemy import engine_from_config
+
+    from .models import DBSession
 
     def main(global_config, XXsettings):
         """ This function returns a Pyramid WSGI application.
         """
-
-        # Here you can insert any code to modify the ``settings`` dict.
-        # You can:
-        # * Add additional keys to serve as constants or "global variables" in the
-        #   application.
-        # * Set default values for settings that may have been omitted.
-        # * Override settings that you don't want the user to change.
-        # * Raise an exception if a setting is missing or invalid.
-        # * Convert values from strings to their intended type.
-
-        # Create the Pyramid Configurator.
+        engine = engine_from_config(settings, 'sqlalchemy.')
+        DBSession.configure(bind=engine)
         config = Configurator(settings=settings)
-        config.include("pyramid_handlers")
-        config.include("akhet")
-
-        # Initialize database
-        engine = sqlalchemy.engine_from_config(settings, prefix="sqlalchemy.")
-        sqlahelper.add_engine(engine)
-        config.include("pyramid_tm")
-
-        # Configure Beaker sessions
-        session_factory = pyramid_beaker.session_factory_from_settings(settings)
-        config.set_session_factory(session_factory)
-
-        # Configure renderers and event subscribers
-        config.add_renderer(".html", "pyramid.mako_templating.renderer_factory")
-        config.add_subscriber("zzz.subscribers.create_url_generator",
-            "pyramid.events.ContextFound")
-        config.add_subscriber("zzz.subscribers.add_renderer_globals",
-                              "pyramid.events.BeforeRender")
-
-        # Set up view handlers
-        config.include("zzz.handlers")
-
-        # Set up other routes and views
-        # ** If you have non-handler views, create create a ``zzz.views``
-        # ** module for them and uncomment the next line.
-        #
-        #config.scan("zzz.views")
-
-        # Mount a static view overlay onto "/". This will serve, e.g.:
-        # ** "/robots.txt" from "zzz/static/robots.txt" and
-        # ** "/images/logo.png" from "zzz/static/images/logo.png".
-        #
-        config.add_static_route("zzz", "static", cache_max_age=3600)
-
-        # Mount a static subdirectory onto a URL path segment.
-        # ** This not necessary when using add_static_route above, but it's the
-        # ** standard Pyramid way to serve static files under a URL prefix (but
-        # ** not top-level URLs such as "/robots.txt"). It can also serve files from
-        # ** third-party packages, or point to an external HTTP server (a static
-        # ** media server).
-        # ** The first commented example serves URLs under "/static" from the
-        # ** "zzz/static" directory. The second serves URLs under 
-        # ** "/deform" from the third-party ``deform`` distribution.
-        #
-        #config.add_static_view("static", "zzz:static")
-        #config.add_static_view("deform", "deform:static")
-
+        config.add_static_view('static', 'static', cache_max_age=3600)
+        config.add_route('home', '/')
+        config.scan()
         return config.make_wsgi_app()
 
-(Note: ``**settings`` in line 7 is displayed as ``XXsettings`` due to a
-limitation in our documentation generator: "``*``" in code blocks
-outside comments make Vim's syntax highlighting go bezerk.)
+(*Doc limitation*: ``XXsettings`` in line 6 is actually ``**settings``.
+We had to alter it in the docs to prevent Vim's syntax highlighting from going
+bezerk.)
 
-Lines 11-18 are a long comment explaining how you can modify the ``settings``
-dict. If you have any code to set "global variables" for the application, or to
-validate the settings or convert the values from strings to other types, 
-put the code here. (We're considering a default routine to validate the
-settings but haven't decided whether to use homegrown code, Colander,
-FormEncode, or another validation library.)
+This main function is short and sweet. Later we'll discuss lots of things you
+can add here to add features.
 
-Line 21 instantiates a ``Configurator`` which will create the application.
-(It's not the application itself.) Lines 22-23 add plug-in functionality to
-the configurator. The argument is the name of a module that contains an
-``includeme()`` function. Line 22 ultimately creates the
-``config.add_handler()`` method; line 23 creates the
-``config.add_static_route()`` method. 
+"pserve" parses the "[app:main]" options into a dict called "settings". It
+calls the ``main()`` function with the settings as keyword args. (The
+``global_config`` arg is not used much; it's covered later.)
 
-Line 26 creates a SQLAlchemy engine based on the "sqlalchemy.url" setting in
-*development.ini*. The default setting is
-"sqlite:///%(here)s/db.sqlite", which creates or opens a database "db.sqlite"
-in the same directory as the INI file. You can also pass other engine arguments
-to SQLAlchemy, either by putting them in the INI file with the "sqlalchemy."
-prefix, or by passing them as keyword args. Line 27 adds the engine to the
-``sqlahelper`` library so that the model can use it; it also updates the
-library's contextual session.  Line 28 initializes the "pyramid_tm" transaction
-manager. SQLAHelper is further explained in the Models section below; the
-transaction manager is explained in the "Transaction Manager" chapter.
+Lines 9-10 instantiate a database engine based on the "sqlalchemy." settings in
+the INI file. ``DBSession`` is a global object used to access SQLAlchemy's
+object-relational mapper (ORM). 
 
-(Note: if you answered 'n' to the SQLAlchemy question when creating the
-application, lines 4-5 and 25-28 will not be present in your module.)
+Line 11 instantiates a ``Configurator`` which will instantiate the application.
+(It's not the application itself.)  
 
-Lines 31-32 configure the session factory. 
+Line 12 tells the configurator to serve the static directory (*zzz/static*)
+under URL "/static". The arguments are more than they appear, as we'll see in
+the customization section.
 
-Line 35 tells Pyramid to render *\*.html* templates using Mako. Pyramid out of
-the box renders Mako templates with the *\*.mako* or *\*.mak* extensions, and
-Chameleon templates with the *\*.pt* extension, but you have to tell it if you
-want to use a different extension or another template engine. Third-party
-packages are available for using Jinja2 (``pyramid_jinja2``), and
-a Genshi emulator using Chameleon (``pyramid_genshi_chameleon``),
+Line 13 creates a route for the home page. This is more or less like a Pylons
+route, except it doesn't specify a controller and action. 
 
-Lines 36-39 registers event subscribers, which are callback functions called at
-specific points during request processing. Lines 36-37 register a callback that
-instantiates a URL generator (described in the Templates section below and in
-the API_ chapter). Lines 38-39 register a callback which adds several
-Pylons-like variables to the template namespace whenever a template is
-rendered. The callbacks are defined in the ``zzz.subscribers`` module, which
-you can modify.
+Line 14 scans the application's Python modules looking for views to register.
+This imports all the modules under ``zzz``.
 
-Lines 42 configures routing. Actually it calls an include function in the
-handlers package. We'll explore routing more fully later.
-
-Lines 44-48 and 56-67 are commented code; they show how to enable certain
-advanced features.
-
-Line 54 is equivalent to the *public* directory in Pylons applications. It's
-not a standard part of Pyramid, which handles static files a different way, but
-this method is closer to the Pylons tradition. Any URLs which did not match a
-dynamic route will be compared to the contents of the *zzz/static* directory,
-and if a file exists for the URL, it is served. Unlike Pylons, this happens
-after the dynamic routes are tried rather than before. This means that any
-dynamic route that might accidentally match a static resource must explicitly
-exclude that URL. 
-
-This is just one of several ways to serve static files in Pyramid, each with
-its own advantages and disadvantages. These are all discussed below in the
-Static Files section.
-
-Line 69 creates and returns a Pyramid WSGI application based on the
-configuration.
-
-This short main function -- compared to Pylons' three functions in three
-modules -- allows an entire small application to be defined in a single module.
-Half the lines are comments so they can be deleted.  A short main function is
-useful for small demos, but the principle also leads to a different developer
-culture. Pylons' application skeleton is complex enough that most people don't
-stray from it, and Pylons' documentation emphasizes using "paster serve" rather
-than other invocation methods. Pyramid's docs encourage users to structure
-everything outside ``main()`` as they wish, and they describe "paster serve" as
-just one way to invoke the application. The INI files and "paster serve" are
-just for your convenience; you don't have to use them.
-
-A bit more about Paster
------------------------
-
-"paster serve" does several other things besides calling the main function.
-It interpolates "%(here)s" placeholders in the INI file, as well as
-variables in the "[DEFAULT]" section (which we aren't using here). It
-configures logging, and finds the application by looking up the entry point
-specified in the 'use' variable. All this can be done by the following code
-in both Pyramid and Pylons::
-
-    import logging.config
-    import os
-    import paste.deploy.loadwsgi as loadwsgi
-    ini_path = "/path/to/development.ini"
-    logging.config.fileConfig(ini_path)
-    app_dir, ini_file = os.path.split(ini_path)
-    app = loadwsgi.loadapp("config:" + ini_file, relative_to=app_dir)
+Line 15 instantiates a Pyramid WSGI application based on the configuration, and
+returns it.
 
 Models
 ======
 
-The default *zzz/models/__init__.py* looks like this::
+This is where you define your domain model; i.e., what makes this application
+different from other Pyramid applications. A good application structure
+separates domain logic (not Pyramid-specific or UI-related) from view logic
+(Pyramid-specific or UI-related). This makes it easy to use the domain code
+outside of the web application (in standalone utilities) or to port it to
+another framework (if you ever decide to do so).
 
-    import logging
-    import sqlahelper
-    import sqlalchemy as sa
-    import sqlalchemy.orm as orm
-    import transaction
+*Note:* the term "model" is used in two different ways. Collectively it means
+all your ORM classes and domain logic together. (One model per application.)
+Individually it means a single ORM class. (Several models in one application.)
+Either way is fine, but beware that the word "model" (singular) can mean one or
+the other. This led to a controversy in both Pylons and Pyramid on whether to
+put "model" or "models" in the scaffolds. Pylons chose "model"; Pyramid chose
+"models". But it doesn't matter, and you can rename models.py to model.py if
+you wish. Just be aware that the word "model" can mean either one class or all
+classes together.
 
-    log = logging.getLogger(__name__)
+At minimum you should define your database tables and ORM classes here. Some
+people also put other business logic here, either as methods in the ORM classes
+or as functions. Other people put miscellaneous domain logic into a 'lib'
+package (*zzz/lib*). Others put the entire models and domain logic in a
+separate Python distribution, which they import into the Pyramid application.
+Others put domain logic directly into the views, but this is not recommended
+unless it's a small amount of code because it mixes framework-independent and
+framework-dependent code. 
 
-    Base = sqlahelper.get_base()
-    Session = sqlahelper.get_session()
 
+The default *zzz/models.py* looks like this::
 
-    #class MyModel(Base):
-    #    __tablename__ = "models"
-    #
-    #    id = sa.Column(sa.Integer, primary_key=True)
-    #    name = sa.Column(sa.Unicode(255), nullable=False)
+    from sqlalchemy import (
+        Column,
+        Integer,
+        Text,
+        )
+
+    from sqlalchemy.ext.declarative import declarative_base
+
+    from sqlalchemy.orm import (
+        scoped_session,
+        sessionmaker,
+        )
+
+    from zope.sqlalchemy import ZopeTransactionExtension
+
+    DBSession = scoped_session(sessionmaker(extension=ZopeTransactionExtension()))
+    Base = declarative_base()
+
+    class MyModel(Base):
+        __tablename__ = 'models'
+        id = Column(Integer, primary_key=True)
+        name = Column(Text, unique=True)
+        value = Column(Integer)
+
+        def __init__(self, name, value):
+            self.name = name
+            self.value = value
+
+
+        import logging
+        import sqlahelper
+        import sqlalchemy as sa
+        import sqlalchemy.orm as orm
+        import transaction
+
+        log = logging.getLogger(__name__)
+
+        Base = sqlahelper.get_base()
+        Session = sqlahelper.get_session()
+
+
+        #class MyModel(Base):
+        #    __tablename__ = "models"
+        #
+        #    id = sa.Column(sa.Integer, primary_key=True)
+        #    name = sa.Column(sa.Unicode(255), nullable=False)
+
+This represents one way to organize a SQLAlchemy model.
+
 
 Pylons applications have a "zzz.model.meta" model to hold SQLAlchemy's
 housekeeping objects, but Akhet uses the SQLAHelper library which holds them
 Two functions in ``pyramid.renderers`` are occasionally useful in views:
 
 .. function:: pyramid.renderers.render(renderer_name, value, request=None, package=None)
+    :noindex:
 
     Render a template and return a string. 'renderer_name' is a template
     filename or renderer name. 'value' is a dict of template variables.
     as the ``package`` arg.
 
 .. function:: pyramid.renderers.render_to_response(renderer_name, value, request=None, package=None)
+    :noindex:
 
     Render a template, instantiate a Response, set the Response's body to
     the result of the rendering, and return the Response. The arguments are the
 from the ``pyramid_handlers`` package, are:
 
 .. function:: @action(\*\*kw)
+   :noindex:
 
    I make a method in a class into a *view* method, which
    ``config.add_handler`` can connect to a URL pattern. By definition, any class
    they presumably will have different renderers too.
 
 .. method:: config.add_handler(name, pattern, handler, action=None, \*\*kw)
+   :noindex:
 
    I create a route connecting the URL pattern to the handler class. If
    'action' is specified, I connect the route to that specific action (a method
 directly:
 
 .. method:: config.add_route(name, pattern, \*\*kw)
+   :noindex:
 
    Create a route connecting a URL pattern directly to a view callable outside
    a handler.  The view is specified with a 'view' arg. If the view is a
    default) is called with no arguments.
 
 .. method:: config.add_view(\*\*kw)
+   :noindex:
 
    I register a view (specified with a 'view' arg). In URL dispatch, you
    normally don't call this directly but let ``config.add_handler`` or
 Two others you should know about:
 
 .. function:: config.scan(package=None)
+   :noindex:
 
    I scan the specified package (which may be an asset spec) and import all its
    modules recursively, looking for functions decorated with ``@view_config``.
    modules even if none of them contain ``@view_config``.
 
 .. function:: @view_config(\*\*kw)
+   :noindex:
 
    I decorate a function so that ``config.scan`` will recognize it as a view
    callable, and I also hold ``add_view`` arguments that ``config.scan`` will
 variables in the view's return dict, plus the following:
 
 .. attribute:: request
+   :noindex:
 
    The current request.
 
 .. attribute:: context
+   :noindex:
 
    The context (same as ``request.context``).
 
 .. attribute:: renderer_name
+   :noindex:
 
    The fully-qualified renderer name; e.g., "zzz:templates/foo.mako".
 
 .. attribute:: renderer_info
+   :noindex:
 
    An object with attributes ``name``, ``package``, and ``type``.
 
 The subscriber in your application adds the following additional variables:
 
 .. attribute:: c, tmpl_context
+   :noindex:
 
    ``request.tmpl_context``
 
 .. attribute:: h
+   :noindex:
 
    The helpers module, defined as "zzz.helpers". This is set by a subscriber
    callback in your application; it is not built into Pyramid. 
 
 .. attribute:: session
+   :noindex:
 
    ``request.session``.
 
 .. attribute:: url
+   :noindex:
 
    In Akhet, a URLGenerator object. In Pyramid's built-in application templates
    that use URL dispatch, an alias to the ``route_url`` *function*, which
 mod_proxy, or mod_wsgi, or whatever else you prefer. 
 
 
+.. _PasteDeploy manual: http://pythonpaste.org/deploy/
 .. _MultiDict API: api.html#multidict
 .. _API: api.html
-.. _PasteDeploy manual: http://pythonpaste.org/deploy/
-.. _pyramid_exclog: http://docs.pylonsproject.org/projects/pyramid_exclog/en/latest/
+.. _minimal application: http://docs.pylonsproject.org/projects/pyramid/en/1.2-branch/narr/firstapp.html
+.. _asset syntax:
+http://docs.pylonsproject.org/projects/pyramid/en/1.2-branch/narr/assets.html#asset-specifications

File docs/conf.py

 # Add any paths that contain custom static files (such as style sheets) here,
 # relative to this directory. They are copied after the builtin static files,
 # so a file named "default.css" will overwrite the builtin "default.css".
-#html_static_path = ['_static']
+html_static_path = ['_static']
 
 # If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
 # using the given strftime format.

File docs/index.rst

 Pyramid_. Version 2 focuses more heavily on documentation, and does not contain
 an application scaffold [#]_. Instead, the documentation shows how to customize
 Pyramid's built-in scaffolds to give a Pylons-like environment. The
-documentation focuses on things that either aren't in the Pyramid manual or
-deserve highlighting, and it also discusses the tradeoffs between various
-alternative APIs.  The Akhet library (the convenience classes) are unchanged in
-this release.
+documentation gives a walk-through of the default application's structure,
+highlighting Useful Bits of Information that are buried in the Pyramid manual
+or are not in the manual.  It also discusses some alternative APIs and the
+tradeoffs between them. The Akhet library (the convenience classes) are
+unchanged in this release.
 
 Akhet 2.0 runs on Python 2.5 - 2.7.   The next version will probably add
-Python 3 support and drop Python 2.5, as Pyramid 1.3 is doing.
+Python 3 and drop Python 2.5, as Pyramid 1.3 is doing.
 
 
 
 .. toctree::
-   :maxdepth: 1
+   :maxdepth: 2
 
    intro
    vocabulary
    api
    other_pyramid_features
    changes
+   unfinished
 
 * :ref:`genindex`
 * :ref:`modindex`

File docs/intro.rst

 Introduction to Akhet 2
 %%%%%%%%%%%%%%%%%%%%%%%
 
+This chapter discusses Akhet's history and current status. The second chapter
+introduces some vocabulary terms. The third chapter summarizes how to create a
+Pyramid application using the recommended skeleton, and how to use the Pyramid
+and Akhet development versions. The next several chapters analyze different
+aspects of the default application, and discuss various enhancements you can
+add to it. The final chapters discuss other Pyramid topics.
+
 Akhet evolved out of a Pyramid/SQLAlchemy application scaffold. It then grew
 more Pylons-like features, a small library, and documentation that
 expanded to become a general introduction to Pyramid.  In Akhet 2, the
 * 'starter': traversal only, no database [#]_
 
 Thus, rather than having scaffolds for all combinations of libraries, Pyramid
-will have scaffolds for just the most widely-used application styles. Most
-people use URL dispatch with SQLAlchemy, and traversal with ZODB, so these are
-the combinations offered.
+1.3 just has scaffold for the most widely-used application styles. Most people
+use URL dispatch with SQLAlchemy, and traversal with ZODB, so these are the
+combinations offered.
 
 If you're coming from Akhet 1, or from Pylons/Django/Rails/PHP, use the
 'alchemy' scaffold in Pyramid 1.3 (or in Pyramid 1.2, the 'routesalchemy'

File docs/unfinished.rst

+Unfinished: Fragments
+%%%%%%%%%%%%%%%%%%%%%
+
+INI files, logging, and pserve
+===============================
+
+development.ini
+---------------
+
+Some Pyramid add-ons also look here for configuration settings. For instance,
+Beaker looks for "cache.\*" and "session.\*" settings, and Mako looks for
+"mako.\*" settings.  You can use these to configure what kind of persistent
+session store to use, how long until idle sessions are discarded, and what
+character encoding to use for template output (the default is "utf-8").
+possible options are listed in the Pyramid manual or the add-on's manual.
+
+You can of course create multiple INI files if you want to try the application
+out under different configuration scenarios, for instance to compare a
+database and a PostgresSQL database. You can even run them simultaneously as
+long as each configuration specifies a different port.
+
+If you're using Apache's mod_proxy to proxy to a Python HTTP server, you might
+want to change this to "use = egg:pyramid#cherrypy". This uses the CherryPy
+server, which is multithreaded like paste.httpserver (which Pylons and older
+versions of Pyramid used), but is more robust under high loads. (You'll have to
+install CherryPy to use this.)
+
+The Pyramid manual and Cookbook discuss other deployment scenarios like
+mod_wsgi and FastCGI.
+
+I normally set "host = 127.0.0.1" and "port = 5000" after creating an
+application. That way it serves only request coming from the same computer
+rather than from any computer on the network. That enhances security when the
+debug toolbar is enabled. Port 5000 is the Pylons tradition, and it's easier to
+remember and type than 6543. 
+
+"%(here)s" expands to the path of the directory containing the INI file.
+
+
+production.ini
+--------------
+
+The default production file is just slightly different from the development
+file:
+
+.. code-block:: ini
+
+    [app:main]
+    use = egg:Zzz
+
+    pyramid.reload_templates = false
+    pyramid.debug_authorization = false
+    pyramid.debug_notfound = false
+    pyramid.debug_routematch = false
+    pyramid.debug_templates = false
+    pyramid.default_locale_name = en
+    pyramid.includes = pyramid_tm
+
+    sqlalchemy.url = sqlite:///%(here)s/Zzz.db
+
+    [server:main]
+    use = egg:pyramid#wsgiref
+    host = 0.0.0.0
+    port = 6543
+
+The most important difference here is that "pyramid_debugtoolbar" is NOT
+enabled. **This is vital for security!**  Otherwise miscreants can type
+arbitrary Python commands in the interactive traceback if an exception occurs,
+and potentially read password files or damage the system.
+
+If an exception occurs during a production request, the user will get a plain
+white error screen, "A server error occurred.  Please contact the
+administrator." To customize that, see "Exception Views" in the Pyramid manual.
+The traceback will be dumped to the console, and will not be shown to the user.
+To customize how tracebacks are reported to the administrator, install the
+pyramid_exclog_ tween, which is covered below in Logging. (This replaces the
+WebError#error_catcher middleware which was used in Pylons and earlier versions
+of Pyramid.)
+
+The debug settings are all set to false in production. This saves a few CPU
+cycles while it's processing requests.
+
+The server section is unchanged from development.ini.  The correct settings
+here depend on what webserver you're running the application with, so you'll
+have to configure this part yourself. 
+
+
+Logging
+-------
+
+The bottom 2/3 of both INI files contain several sections to configure
+Python's logging system.  This is the same as in Pylons.  We can't explain the
+entire logging syntax here, but these are the sections most often customized by
+users:
+
+.. code-block:: ini
+
+    [logger_root]
+    level = INFO
+    handlers = console
+
+    [logger_zzz]
+    level = DEBUG
+    handlers =
+    qualname = zzz
+
+    [logger_sqlalchemy]
+    level = INFO
+    handlers =
+    qualname = sqlalchemy.engine
+    # "level = INFO" logs SQL queries.
+    # "level = DEBUG" logs SQL queries and results.
+    # "level = WARN" logs neither.  (Recommended for production systems.)
+
+These define a logger "root", "zzz" (the application's package name), and
+"sqlalchemy.engine" (specified in the qualname). Each has a 'level' variable
+which can be DEBUG, INFO, WARN, ERROR, or CRITICAL. Each level also logs the
+levels on its right, so WARN logs warnings and errors. Logger names are in a
+dotted hierarchy, so that "sqlalchemy.engine" affects all loggers below it
+("sqlalchemy.engine.ENGINE1", etc).  "root" affects all loggers that aren't
+otherwise specified.
+
+Generally, DEBUG is debugging information, INFO is chatty success messages,
+WARN means something might be wrong, ERROR means something is
+definitely wrong, and CRITICAL means you'd better fix it now or else. 
+But there's nothing to enforce this, so each library chooses how to log things.
+So SQLAlchemy logs SQL queries at the INFO level on
+"sqlalchemy.engine.ENGINE_NAME", even though some people would consider this
+debugging information. 
+
+Logger names do NOT automatically correspond to Python module names, although
+it's customary to do so if there's no better name for the logger. You can do
+this by putting the following at the top of each module:
+
+    import logging
+    log = logging.getLogger(__name__)
+
+This creates a variable ``log`` which is a logger named after the module. So if
+the module is ``zzz.views``, the logger is "zzz.views".
+
+The default *development.ini* displays all messages from the application
+modules (logger_zzz = DEBUG). It displays SQL queries (logger_sqlalchemy =
+INFO). It displays other messages only if they're warnings or above
+(logger_root = WARN).  The default *production.ini* sets all these to WARN, so
+it will not log anything except warnings or errors.
+
+You can create additional loggers by adding a "[logger_yourname]" section,
+listing it in the "[loggers]" section, and calling
+``logging.getLogger("yourname")`` in the Python module. 
+
+To activate logging in a utility script the way "pserve" does, do the
+following::
+
+    import logging.config
+    logging.config.fileConfig(INI_FILENAME)
+
+
+
+Init module and main function
+=============================
+
+a dict based on the "[GLOBAL]" section of the INI
+file.
+
+
+.. code-block:: python
+   :linenos:
+
+   # AKHET 1
+
+    from pyramid.config import Configurator
+    import akhet
+    import pyramid_beaker
+    import sqlahelper
+    import sqlalchemy
+
+    def main(global_config, XXsettings):
+        """ This function returns a Pyramid WSGI application.
+        """
+
+        # Here you can insert any code to modify the ``settings`` dict.
+        # You can:
+        # * Add additional keys to serve as constants or "global variables" in the
+        #   application.
+        # * Set default values for settings that may have been omitted.
+        # * Override settings that you don't want the user to change.
+        # * Raise an exception if a setting is missing or invalid.
+        # * Convert values from strings to their intended type.
+
+        # Create the Pyramid Configurator.
+        config = Configurator(settings=settings)
+        config.include("pyramid_handlers")
+        config.include("akhet")
+
+        # Initialize database
+        engine = sqlalchemy.engine_from_config(settings, prefix="sqlalchemy.")
+        sqlahelper.add_engine(engine)
+        config.include("pyramid_tm")
+
+        # Configure Beaker sessions
+        session_factory = pyramid_beaker.session_factory_from_settings(settings)
+        config.set_session_factory(session_factory)
+
+        # Configure renderers and event subscribers
+        config.add_renderer(".html", "pyramid.mako_templating.renderer_factory")
+        config.add_subscriber("zzz.subscribers.create_url_generator",
+            "pyramid.events.ContextFound")
+        config.add_subscriber("zzz.subscribers.add_renderer_globals",
+                              "pyramid.events.BeforeRender")
+
+        # Set up view handlers
+        config.include("zzz.handlers")
+
+        # Set up other routes and views
+        # ** If you have non-handler views, create create a ``zzz.views``
+        # ** module for them and uncomment the next line.
+        #
+        #config.scan("zzz.views")
+
+        # Mount a static view overlay onto "/". This will serve, e.g.:
+        # ** "/robots.txt" from "zzz/static/robots.txt" and
+        # ** "/images/logo.png" from "zzz/static/images/logo.png".
+        #
+        config.add_static_route("zzz", "static", cache_max_age=3600)
+
+        # Mount a static subdirectory onto a URL path segment.
+        # ** This not necessary when using add_static_route above, but it's the
+        # ** standard Pyramid way to serve static files under a URL prefix (but
+        # ** not top-level URLs such as "/robots.txt"). It can also serve files from
+        # ** third-party packages, or point to an external HTTP server (a static
+        # ** media server).
+        # ** The first commented example serves URLs under "/static" from the
+        # ** "zzz/static" directory. The second serves URLs under 
+        # ** "/deform" from the third-party ``deform`` distribution.
+        #
+        #config.add_static_view("static", "zzz:static")
+        #config.add_static_view("deform", "deform:static")
+
+        return config.make_wsgi_app()
+
+(Note: ``**settings`` in line 7 is displayed as ``XXsettings`` due to a
+limitation in our documentation generator: "``*``" in code blocks
+outside comments make Vim's syntax highlighting go bezerk.)
+
+Lines 11-18 are a long comment explaining how you can modify the ``settings``
+dict. If you have any code to set "global variables" for the application, or to
+validate the settings or convert the values from strings to other types, 
+put the code here. (We're considering a default routine to validate the
+settings but haven't decided whether to use homegrown code, Colander,
+FormEncode, or another validation library.)
+
+Line 21 instantiates a ``Configurator`` which will create the application.
+(It's not the application itself.) Lines 22-23 add plug-in functionality to
+the configurator. The argument is the name of a module that contains an
+``includeme()`` function. Line 22 ultimately creates the
+``config.add_handler()`` method; line 23 creates the
+``config.add_static_route()`` method. 
+
+Line 26 creates a SQLAlchemy engine based on the "sqlalchemy.url" setting in
+*development.ini*. The default setting is
+"sqlite:///%(here)s/db.sqlite", which creates or opens a database "db.sqlite"
+in the same directory as the INI file. You can also pass other engine arguments
+to SQLAlchemy, either by putting them in the INI file with the "sqlalchemy."
+prefix, or by passing them as keyword args. Line 27 adds the engine to the
+``sqlahelper`` library so that the model can use it; it also updates the
+library's contextual session.  Line 28 initializes the "pyramid_tm" transaction
+manager. SQLAHelper is further explained in the Models section below; the
+transaction manager is explained in the "Transaction Manager" chapter.
+
+(Note: if you answered 'n' to the SQLAlchemy question when creating the
+application, lines 4-5 and 25-28 will not be present in your module.)
+
+Lines 31-32 configure the session factory. 
+
+Line 35 tells Pyramid to render *\*.html* templates using Mako. Pyramid out of
+the box renders Mako templates with the *\*.mako* or *\*.mak* extensions, and
+Chameleon templates with the *\*.pt* extension, but you have to tell it if you
+want to use a different extension or another template engine. Third-party
+packages are available for using Jinja2 (``pyramid_jinja2``), and
+a Genshi emulator using Chameleon (``pyramid_genshi_chameleon``),
+
+Lines 36-39 registers event subscribers, which are callback functions called at
+specific points during request processing. Lines 36-37 register a callback that
+instantiates a URL generator (described in the Templates section below and in
+the API_ chapter). Lines 38-39 register a callback which adds several
+Pylons-like variables to the template namespace whenever a template is
+rendered. The callbacks are defined in the ``zzz.subscribers`` module, which
+you can modify.
+
+Lines 42 configures routing. Actually it calls an include function in the
+handlers package. We'll explore routing more fully later.
+
+Lines 44-48 and 56-67 are commented code; they show how to enable certain
+advanced features.
+
+Line 54 is equivalent to the *public* directory in Pylons applications. It's
+not a standard part of Pyramid, which handles static files a different way, but
+this method is closer to the Pylons tradition. Any URLs which did not match a
+dynamic route will be compared to the contents of the *zzz/static* directory,
+and if a file exists for the URL, it is served. Unlike Pylons, this happens
+after the dynamic routes are tried rather than before. This means that any
+dynamic route that might accidentally match a static resource must explicitly
+exclude that URL. 
+
+This is just one of several ways to serve static files in Pyramid, each with
+its own advantages and disadvantages. These are all discussed below in the
+Static Files section.
+
+Line 69 creates and returns a Pyramid WSGI application based on the
+configuration.
+
+This short main function -- compared to Pylons' three functions in three
+modules -- allows an entire small application to be defined in a single module.
+Half the lines are comments so they can be deleted.  A short main function is
+useful for small demos, but the principle also leads to a different developer
+culture. Pylons' application skeleton is complex enough that most people don't
+stray from it, and Pylons' documentation emphasizes using "paster serve" rather
+than other invocation methods. Pyramid's docs encourage users to structure
+everything outside ``main()`` as they wish, and they describe "paster serve" as
+just one way to invoke the application. The INI files and "paster serve" are
+just for your convenience; you don't have to use them.
+
+A bit more about Paster
+-----------------------
+
+"paster serve" does several other things besides calling the main function.
+It interpolates "%(here)s" placeholders in the INI file, as well as
+variables in the "[DEFAULT]" section (which we aren't using here). It
+configures logging, and finds the application by looking up the entry point
+specified in the 'use' variable. All this can be done by the following code
+in both Pyramid and Pylons::
+
+    import logging.config
+    import os
+    import paste.deploy.loadwsgi as loadwsgi
+    ini_path = "/path/to/development.ini"
+    logging.config.fileConfig(ini_path)
+    app_dir, ini_file = os.path.split(ini_path)
+    app = loadwsgi.loadapp("config:" + ini_file, relative_to=app_dir)
+
+Models
+======
+
+The default *zzz/models/__init__.py* looks like this::
+
+    import logging
+    import sqlahelper
+    import sqlalchemy as sa
+    import sqlalchemy.orm as orm
+    import transaction
+
+    log = logging.getLogger(__name__)
+
+    Base = sqlahelper.get_base()
+    Session = sqlahelper.get_session()
+
+
+    #class MyModel(Base):
+    #    __tablename__ = "models"
+    #
+    #    id = sa.Column(sa.Integer, primary_key=True)
+    #    name = sa.Column(sa.Unicode(255), nullable=False)
+
+Pylons applications have a "zzz.model.meta" model to hold SQLAlchemy's
+housekeeping objects, but Akhet uses the SQLAHelper library which holds them
+instead. This gives you more freedom to structure your models as you wish,
+while still avoiding circular imports (which would happen if you defined
+Session in the main module and then import the other modules into it; the
+other modules would import the main module to get the Session, and voilà
+circular imports).
+
+A real application would replace the commented ``MyModel`` class with
+one or more ORM classes. The example uses SQLAlchemy's "declarative" syntax,
+although of course you don't have to. 
+
+SQLAHelper
+----------
+
+The SQLAHelper library is a holding place for the application's contextual
+session (normally assigned to a ``Session`` variable with a capital S, to
+distinguish it from a regular SQLAlchemy session), all engines used by the
+application, and an optional declarative base. We initialized it via the
+``sqlahelper.add_engine`` line in the main function. Because we did not specify
+an engine name, the library set the engine name to "default", and also bound the
+contextual session and the base's metadata to it. 
+
+There's not much else to know about SQLAHelper. You can call ``get_session()``
+at any time to get the contextual session. You can call ``get_engine()`` or
+``get_engine(name)`` to retrieve an engine that was previously added. You can
+call ``get_base()`` to get the declarative base.  
+
+If you need to modify the session-creation parameters, you can call
+``get_session().config(...)``. But if you modify the session extensions, see
+the "Transaction Manager" chapter to avoid losing the extension that powers the
+transaction manager.
+
+View handlers
+=============
+
+The default *zzz.handlers* package contains a *main* module which looks like
+this::
+
+    import logging
+
+    from pyramid_handlers import action
+
+    import zzz.handlers.base as base
+    import zzz.models as model
+
+    log = logging.getLogger(__name__)
+
+    class Main(base.Handler):
+        @action(renderer="index.html")
+        def index(self):
+            log.debug("testing logging; entered Main.index()")
+            return {"project":"Zzz"}
+
+This is clearly different from Pylons, and the ``@action`` decorator looks a
+bit like TurboGears. The decorator has three optional arguments:
+
+name
+    
+    The action name, which is the target of the route. Normally this is the
+    same as the view method name but you can override it, and you must override
+    it when stacking multiple actions on the same view method.
+
+renderer
+
+    A renderer name or template filename (whose extension indicates the
+    renderer). A renderer converts the view's return value into a Response
+    object. Template renderers expect the view to return a dict; other
+    renderers may allow other types. Two non-template renderers are built into
+    Pyramid: "json" serializes the return value to JSON, and "string" calls
+    ``str()`` on the return value unless it's already a Unicode object. If you
+    don't specify a renderer, the view must return a Response object (or any
+    object having three particular attributes described in Pyramid's Response
+    documentation). In all cases the view can return a Response object to
+    bypass the renderer. HTTP errors such as HTTPNotFound also bypass the
+    renderer.
+
+permission
+
+    A string permission name. This is discussed in the Authorization section
+    below.
+
+The Pyramid developers decided to go with the
+return-a-dict approach because it helps in two use cases: 
+
+1.  Unit testing, where you want to test the data calculated rather than
+parsing the HTML output. This works by default because ``@action`` itself does
+not modify the return value or arguments; it merely sets function attributes or
+interacts with the configurator.
+
+2. Situations where several URLs render the same data using different templates
+or different renderers (like "json"). In that case, you can put multiple
+``@action`` decorators on the same method, each with a different name and
+renderer argument.
+
+Two functions in ``pyramid.renderers`` are occasionally useful in views:
+
+.. function:: pyramid.renderers.render(renderer_name, value, request=None, package=None)
+
+    Render a template and return a string. 'renderer_name' is a template
+    filename or renderer name. 'value' is a dict of template variables.
+    'request' is the request, which is needed only if the template cares
+    about it.
+
+    If the function can't find the template, try passing "zzz:templates/"
+    as the ``package`` arg.
+
+.. function:: pyramid.renderers.render_to_response(renderer_name, value, request=None, package=None)
+
+    Render a template, instantiate a Response, set the Response's body to
+    the result of the rendering, and return the Response. The arguments are the
+    same as for ``render()``, except that 'request' is more important.
+    
+
+The handler class inherits from a base class defined in *zzz.handlers.base*::
+
+    """Base classes for view handlers.
+    """
+
+    class Handler(object):
+        def __init__(self, request):
+            self.request = request
+
+            #c = self.request.tmpl_context
+            #c.something_for_site_template = "Some value."
+
+Pyramid does not require a base class but Akhet defines one for convenience. 
+All handlers should set ``self.request`` in their ``.__init__`` method, and the
+base handler does this. It also provides a place to put common methods used by
+several handler classes, or to set ``tmpl_context`` (``c``) variables which are
+used by your site template (common to all views or several views). (You
+can use ``c`` in view methods the same way as in Pylons, although this is not
+recommended.)
+
+Note that non-template renders such as "json" ignore ``c`` variables, so it's
+really only useful for HTML-only data like which stylesheet to use.
+
+The routes are defined in *zzz/handlers/__init__.py*::
+
+    """View handlers package.
+    """
+
+    def includeme(config):
+        """Add the application's view handlers.
+        """
+        config.add_handler("home", "/", "zzz.handlers.main:Main",
+                           action="index")
+        config.add_handler("main", "/{action}", "zzz.handlers.main:Main",
+            path_info=r"/(?!favicon\.ico|robots\.txt|w3c)")
+
+``includeme`` is a configurator "include" function, which we've already seen.
+This function calls ``config.add_handler`` twice to create two routes. The
+first route connects URL "/" to the ``index`` view in the ``Main`` handler.
+
+The second route connects all other one-segment URLs (such as "/hello" or
+"/help") to a same-name method in the ``Main`` handler. "{action}" is a path
+variable which will be set the corresponding substring in the URL. Pyramid will
+look for a method in the handler with the same action name, which can either be
+the method's own name or another name specified in the 'name' argument to
+``@action``. Of course, these other methods ("hello" and "help") don't exist in
+the example, so Pyramid will return 400 Not Found status. 
+
+The 'path_info' argument is a regex which excludes certain URLs from matching
+("/favicon.ico", "/robots.txt", "/w3c"). These are static files or directories
+that would syntactically match "/{action}", but we want these to go to a later
+route instead (the static route). So we set a 'path_info' regex that doesn't
+match them.
+
+Redirecting and HTTP errors
+---------------------------
+
+To issue a redirect inside a view, return an HTTPFound::
+
+    from pyramid.httpexceptions import HTTPFound
+
+    def myview(self):
+        return HTTPFound(location=request.route_url("foo"))
+        # Or to redirect to an external site
+        return HTTPFound(location="http://example.com/")
+
+You can return other HTTP errors the same way: ``HTTPNotFound``, ``HTTPGone``,
+``HTTPForbidden``, ``HTTPUnauthorized``, ``HTTPInternalServerError``, etc.
+These are all subclasses of both ``Response`` and ``Exception``.  Although you
+can raise them, Pyramid prefers that you return them instead. If you intend to
+raise them, you have to define an exception view that receives the exception
+argument and returns it, as shown in the Views chapter in the Pyramid manual.
+(On Python 2.4, you also have to call the instance's ``.exception()`` method
+and raise that, because you can't raise instances of new-style classes in 2.4.) 
+A future version of Pyramid may have an exception view built-in; this would
+conflict with your exception view so you'd need to delete it, but there's no
+need to worry about that until/if it actually happens.
+
+Pyramid catches two non-HTTP exceptions by default,
+``pyramid.exceptions.NotFound`` and ``pyramid.exceptions.Forbidden``, which
+it sends to the Not Found View and the Forbidden View respectively. You can
+override these views to display custom HTML pages.
+
+More on routing and traversal
+=============================
+
+Routing methods and view decorators
+-----------------------------------
+
+Pyramid has several routing methods and view decorators. The ones we've seen,
+from the ``pyramid_handlers`` package, are:
+
+.. function:: @action(\*\*kw)
+
+   I make a method in a class into a *view* method, which
+   ``config.add_handler`` can connect to a URL pattern. By definition, any class
+   that contains view methods is a view handler. My most interesting args are 
+   'name' and 'renderer'. If 'name' is NOT specified, the action name is the
+   same as the method name. If 'name' IS specified, the action name can be
+   different. If 'renderer' is specified, it indicates a renderer or template
+   (and the template's extension indicates a renderer). If multiple ``@action``
+   decorators are put on a single method, each must have a different name, and
+   they presumably will have different renderers too.
+
+.. method:: config.add_handler(name, pattern, handler, action=None, \*\*kw)
+
+   I create a route connecting the URL pattern to the handler class. If
+   'action' is specified, I connect the route to that specific action (a method
+   decorated with the ``@action`` decorator). If 'action' is not specified, the
+   pattern must contain a "{action}" placeholder. In that case I scan the
+   handler class for all possible actions. It is an error to specify both "{action}"
+   and an ``action`` arg. I pass extra keyword args to ``config.add_route``,
+   and keyword args in the ``@action`` decorator to ``config.add_view``.
+
+``config.add_handler`` calls two lower-level methods which you can also call
+directly:
+
+.. method:: config.add_route(name, pattern, \*\*kw)
+
+   Create a route connecting a URL pattern directly to a view callable outside
+   a handler.  The view is specified with a 'view' arg. If the view is a
+   function, it must take a Request argument and return a Response (or any
+   object with the three required attributes). If it's a class, the constructor
+   takes the Request argument and the specified method (``.__call__`` by
+   default) is called with no arguments.
+
+.. method:: config.add_view(\*\*kw)
+
+   I register a view (specified with a 'view' arg). In URL dispatch, you
+   normally don't call this directly but let ``config.add_handler`` or
+   ``config.add_route`` call it for you. In traversal, you call this to
+   register a view. The 'name' argument is the view name, which is used by
+   traversal to choose which view to invoke.
+
+Two others you should know about:
+
+.. function:: config.scan(package=None)
+
+   I scan the specified package (which may be an asset spec) and import all its
+   modules recursively, looking for functions decorated with ``@view_config``.
+   For each such function, I call ``add_view`` passing the decorator's args to
+   it. I can also scan a package, in which case all submodules in the package
+   are recursively scanned. If no package is specified, I scan the caller's
+   package (i.e., the entire application). 
+   
+   I can also be called for my side effect of importing all of a package's
+   modules even if none of them contain ``@view_config``.
+
+.. function:: @view_config(\*\*kw)
+
+   I decorate a function so that ``config.scan`` will recognize it as a view
+   callable, and I also hold ``add_view`` arguments that ``config.scan`` will
+   pick up and apply.  I can also decorate a class or a method in a class.
+
+
+Route arguments and predicates
+------------------------------
+
+``config.add_handler`` accepts a large number of keyword
+arguments. We'll list the ones most commonly used with Pylons-like applications
+here. For full documentation see the `add_route
+<http://docs.pylonsproject.org/projects/pyramid/1.0/api/config.html#pyramid.config.Configurator.add_route>`_
+API. Most of these arguments can also be used with ``config.add_route``.
+
+The arguments are divided into *predicate arguments* and *non-predicate
+arguments*.  Predicate arguments determine whether the route matches the
+current request: all predicates must pass in order for the route to be chosen.
+Non-predicate arguments do not affect whether the route matches.
+
+name
+
+    [Non-predicate] The first positional arg; required. This must be a unique
+    name for the route, and is used in views and templates to generate the URL.
+
+pattern
+
+    [Predicate] The second positional arg; required. This is the URL path with
+    optional "{variable}" placeholders; e.g., "/articles/{id}" or
+    "/abc/{filename}.html". The leading slash is optional. By default the
+    placeholder matches all characters up to a slash, but you can specify a
+    regex to make it match less (e.g., "{variable:\d+}" for a numeric variable)
+    or more ("{variable:.*}" to match the entire rest of the URL including
+    slashes). The substrings matched by the placeholders will be available as
+    *request.matchdict* in the view.
+
+    A wildcard syntax "\*varname" matches the rest of the URL and puts it into
+    the matchdict as a tuple of segments instead of a single string.  So a
+    pattern "/foo/{action}/\*fizzle" would match a URL "/foo/edit/a/1" and
+    produce a matchdict ``{'action': u'edit', 'fizzle': (u'a', u'1')}``.
+
+    Two special wildcards exist, "\*traverse" and "\*subpath". These are used
+    in advanced cases to do traversal on the right side of the URL, and should
+    be avoided otherwise.
+
+factory
+
+    [Non-predicate] A callable (or asset spec). In URL dispatch, this returns a
+    *root resource* which is also used as the *context*. If you don't specify
+    this, a default root will be used. In traversal, the root contains one
+    or more resources, and one of them will be chosen as the context.
+
+xhr
+
+    [Predicate] True if the request must have an "X-Reqested-With" header. Some
+    Javascript libraries (JQuery, Prototype, etc) set this header in AJAX
+    requests.
+
+request_method
+
+    [Predicate] An HTTP method: "GET", "POST", "HEAD", "DELETE", "PUT". Only
+    requests of this type will match the route.
+
+path_info
+
+    [Predicate] A regex compared to the URL path (the part of the URL after the
+    application prefix but before the query string). The URL must match this
+    regex in order for the route to match the request.
+
+request_param
+
+    [Predicate] If the value doesn't contain "=" (e.g., "q"), the request must
+    have the specified parameter (a GET or POST variable). If it does contain
+    "=" (e.g., "name=value"), the parameter must have the specified value.
+
+    This is especially useful when tunnelling other HTTP methods via
+    POST. Web browsers can't submit a PUT or DELETE method via a form, so it's
+    customary to use POST and to set a parameter ``_method="PUT"``. The
+    framework or application sees the "_method" parameter and pretends the
+    other HTTP method was requested. In Pyramid you can do this with
+    ``request_param="_method=PUT``.
+
+header
+
+    [Predicate] If the value doesn't contain ":"; it  specifies an HTTP header
+    which must be present in the request (e.g., "If-Modified-Since"). If it
+    does contain ":", the right side is a regex which the header value must
+    match; e.g., "User-Agent:Mozilla/.\*". The header name is case insensitive.
+
+accept
+
+    [Predicate] A MIME type such as "text/plain", or a wildcard MIME type with
+    a star on the right side ("text/\*") or two stars ("\*/\*"). The request
+    must have an "Accept:" header containing a matching MIME type.
+
+custom_predicates
+
+    [Predicate] A sequence of callables which will be called in order to
+    determine whether the route matches the request. The callables should
+    return ``True`` or ``False``. If any callable returns ``False``, the route
+    will not match the request. The callables are called with two arguments,
+    ``info`` and ``request``. ``request`` is the current request. ``info`` is a
+    dict which contains the following::
+    
+        info["match"]  =>  the match dict for the current route
+        info["route"].name  =>  the name of the current route
+        info["route"].pattern  =>  the URL pattern of the current route
+
+    Use custom predicates argument when none of the other predicate args fit
+    your situation.  See
+    <http://docs.pylonsproject.org/projects/pyramid/1.0/narr/urldispatch.html#custom-route-predicates>`
+    in the Pyramid manual for examples.
+
+    You can modify the match dict to affect how the view will see it. For
+    instance, you can look up a model object based on its ID and put the object
+    in the match dict under another key. If the record is not found in the
+    model, you can return False to prevent the route from matching the request;
+    this will ultimately case HTTPNotFound if no other route or traversal
+    matches the URL.  The difference between doing this and returning
+    HTTPNotFound in the view is that in the latter case the following routes
+    and traversal will never be consulted. That may or may not be an advantage
+    depending on your application.
+
+View arguments
+--------------
+
+The 'name', 'renderer' and 'permission' arguments described for ``@action`` can
+also be used with ``@view_config`` and ``config.add_view``.
+
+``config.add_route`` has counterparts to some of these such as
+'view_permission'.
+
+``config.add_view`` also accepts a 'view' arg which is a view callable or asset
+spec. This arg is not useful for the decorators which already know the view.
+
+The 'wrapper' arg can specify another view, which will be called when this view
+returns. (XXX Is this compatible with view handlers?)
+
+
+The request object
+==================
+
+The Request object contains all information about the current request state and
+application state. It's available as ``self.request`` in handler views, the
+``request`` arg in view functions, and the ``request`` variable in templates.
+In pshell or unit tests you can get it via 
+``pyramid.threadlocal.get_current_request()``. (You shouldn't use the
+threadlocal back door in most other cases. If something you call from the view
+requires it, pass it as an argument.)
+
+Pyramid's ``Request`` object is a subclass of WebOb Request just like
+'pylons.request' is, so it contains all the same attributes in methods like
+``params``, ``GET``, ``POST``, ``headers``, ``method``, ``charset``, ``date``,
+``environ``, ``body``, and ``body_file``. The most commonly-used attribute is
+``request.params``, which is the query parameters and POST variables.
+
+Pyramid adds several attributes and methods. ``context``, ``matchdict``,
+``matched_route``, ``registry``, ``registry.settings``, ``session``, and
+``tmpl_context`` access the request's state data and global application data. 
+``route_path``, ``route_url``, ``resource_url``, and ``static_url`` generate
+URLs, shadowing the functions in ``pyramid.url``. (One function,
+``current_route_url``, is available only as a function.)
+
+Rather than repeating the existing documentation for these attributes and
+methods, we'll just refer you to the original docs:
+
+* `Pyramd Request, Response, HTTP Exceptions, and MultiDict <http://docs.pylonsproject.org/projects/pyramid/1.0/narr/webob.html>`_
+* `Pyramid Request API <http://docs.pylonsproject.org/projects/pyramid/1.0/api/request.html#request-module>`_
+* `WebOb Request API <http://pythonpaste.org/webob/reference.html#id1>`_
+* `Pyramid Response API <http://docs.pylonsproject.org/projects/pyramid/1.0/api/response.html>`_
+* `WebOb Response API <http://pythonpaste.org/webob/reference.html#id2>`_
+
+MultiDict is not well documented so we've written our own `MultiDict API`_
+page. In short, it's a dict-like object that can have multiple values for each
+key.  ``request.params``, ``request.GET``, and ``request.POST`` are MultiDicts.
+
+Pyramid has no pre-existing Response object when your view starts executing. To
+change the response status type or headers, you can either instantiate your own
+``pyramid.response.Response`` object and return it, or use these special
+Request attributes defined by Pyramid::
+
+    request.response_status = "200 OK"
+    request.response_content_type = "text/html"
+    request.response_charset = "utf-8"
+    request.response_headerlist = [
+        ('Set-Cookie', 'abc=123'), ('X-My-Header', 'foo')]
+    request.response_cache_for = 3600    # Seconds
+
+Akhet adds one Request attribute. ``request.url_generator``, which is used to
+implement the ``url`` template global described below.
+
+
+Templates
+=========
+
+Pyramid has built-in support for Mako and Chameleon templates. Chameleon runs
+only on CPython and Google App Engine, not on Jython or other platforms. Jinja2
+support is available via the ``pyramid_jinja2`` package on PyPI, and a Genshi
+emulator using Chameleon is in the ``pyramid_chameleon_genshi`` package.
+
+Whenever a renderer invokes a template, the template namespace includes all the
+variables in the view's return dict, plus the following:
+
+.. attribute:: request
+
+   The current request.
+
+.. attribute:: context
+
+   The context (same as ``request.context``).
+
+.. attribute:: renderer_name
+
+   The fully-qualified renderer name; e.g., "zzz:templates/foo.mako".
+
+.. attribute:: renderer_info
+
+   An object with attributes ``name``, ``package``, and ``type``.
+
+The subscriber in your application adds the following additional variables:
+
+.. attribute:: c, tmpl_context
+
+   ``request.tmpl_context``
+
+.. attribute:: h
+
+   The helpers module, defined as "zzz.helpers". This is set by a subscriber
+   callback in your application; it is not built into Pyramid. 
+
+.. attribute:: session
+
+   ``request.session``.
+
+.. attribute:: url
+
+   In Akhet, a URLGenerator object. In Pyramid's built-in application templates
+   that use URL dispatch, an alias to the ``route_url`` *function*, which
+   requires you to pass the route name as the first arg and the request as the
+   second arg.
+
+   The URLGenerator object has convenience methods for generating URLs based on
+   your application's routes. See the complete list on the API_ page.
+
+   By default the generator creates unqualified URLs (i.e., without the
+   "scheme://hostname" prefix) if the underlying Pyramid functions allow it.
+   To get absolute URLs throughout the application, edit *zzz/subscribers.py*,
+   go to the line where the URLGenerator is instantiated, and change the
+   'qualified' argument to True. Pylons traditionally uses unqualified URLs,
+   while Pyramid traditionally uses qualified URLs. Note that qualified URLs
+   may be wrong if the application is running behind a reverse proxy! (E.g.,
+   Apache's mod_proxy.) The generated URL may be "http://localhost:5000" which
+   is correct for the application but invalid to the end user (who needs the
+   proxy's URL, "https://example.com").  
+
+Advanced template usage
+-----------------------
+
+If you need to fill a template within view code or elsewhere, do this::
+
+    from pyramid.renderers import render
+    variables = {"foo": "bar"}
+    html = render("mytemplate.mako", variables, request=request)
+
+There's also a ``render_to_response`` function which invokes the template and
+returns a Response, but usually it's easier to let ``@action`` or
+``@view_config`` do this. However, if your view has an if-stanza that needs to
+override the template specified in the decorator, ``render_to_response`` is
+the way to do it. ::
+
+    @action(renderer="index.html")
+    def index(self):
+        records = models.MyModel.all()
+        if not records:
+            return render_to_response("no_records.html")
+        return {"records": records}
+
+For further information on templating see the Templates section in the Pyramid
+manual, the Mako manual, and the Chameleon manual.  You can customize Mako's
+TemplateLookup by setting "mako.*" variables in the INI file.
+
+Site template
+-------------
+
+Most applications using Mako will define a site template something like this:
+
+.. code-block:: mako
+
+   <!DOCTYPE html>
+   <html>
+     <head>
+       <title>${self.title()}</title>
+       <link rel="stylesheet" href="${application_url}/default.css"
+           type="text/css" />
+     </head>
+     <body>
+
+   <!-- *** BEGIN page content *** -->
+   ${self.body()}
+   <!-- *** END page content *** -->
+     </body>
+   </html>
+   <%def name="title()" />
+
+Then the page templates can inherit it like so:
+
+.. code-block:: mako
+
+   <%inherit file="/site.html" />
+   <%def name="title()">My Title</def>
+   ... rest of page content goes here ...
+
+Static files
+============
+
+Pyramid has five ways to serve static files. Each way has different
+advantages and limitations, and requires a different way to generate static
+URLs.
+
+``config.add_static_route``
+
+    This is the Akhet default,
+    and is closest to Pylons. It serves the static directory as an overlay on
+    "/", so that URL "/robots.txt" serves "zzz/static/robots.txt", and URL
+    "/images/logo.png" serves "zzz/static/images/logo.png". If the file does
+    not exist, the route will not match the URL and Pyramid will try the next
+    route or traversal. You cannot use any of the URL generation methods with
+    this; instead you can put a literal URL like
+    "${application_url}/images/logo.png" in your template. 
+
+    Usage::
+
+        config.include('akhet')
+        config.add_static_route('zzz', 'static', cache_max_age=3600)
+        # Arg 1 is the Python package containing the static files.
+        # Arg 2 is the subdirectory in the package containing the files.
+
+``config.add_static_view``
+
+    This is Pyramid's default algorithm. It mounts a static directory under a
+    URL prefix such as "/static". It is not an overlay; it takes over the URL
+    prefix completely. So URL "/static/images/logo.png" serves file
+    "zzz/static/images/logo.png". You cannot serve top-level static files like
+    "/robots.txt" and "/favicon.ico" using this method; you'll have to serve
+    them another way. 
+
+    Usage::
+
+        config.add_static_view("static", "zzz:static")
+        # Arg 1 is the view name which is also the URL prefix.
+        # It can also be the URL of an external static webserver.
+        # Arg 2 is an asset spec referring to the static directory/
+
+    To generate "/static/images/logo.png" in a Mako template, which will serve
+    "zzz/static/images/logo.png":
+
+    .. code-block:: mako
+
+       href="${request.static_url('zzz:static/images/logo.png')}
+
+    One advantage of add_static_view is that you can copy the static directory
+    to an external static webserver in production, and static_url will
+    automatically generate the external URL:
+
+    .. code-block:: ini
+
+        # In INI file
+        static_assets = "static"
+        # -OR-
+        static_assets = "http://staticserver.com/"
+
+    ..  code-block:: python
+
+        config.add_static_view(settings["static_assets"], "zzz:static")
+
+    .. code-block:: mako
+
+        href="${request.static_url('zzz:static/images/logo.png')}"
+        ## Generates URL "http://staticserver.com/static/images/logo.png"
+
+Other ways
+
+    There are three other ways to serve static files. One is to write a custom
+    view callable to serve the file; an example is in the Static Assets section
+    of the Pyramid manual. Another is to use ``paste.fileapp.FileApp`` or
+    ``paste.fileapp.DirectoryApp`` in a view. (More recent versions are in the
+    "PasteOb" distribution.) These three ways can be used with
+    ``request.route_url()`` because the route is an ordinary route. The
+    advantage of these three ways is that they can serve a static file or
+    directory from a normal view callable, and the view can be protected
+    separately using the usual authorization mechanism.
+
+Session, flash messages, and secure forms
+=========================================
+
+Pyramid's session object is ``request.session``. It has its own interface but
+uses Beaker on the back end, and is configured in the INI file the same way as
+Pylons' session. It's a dict-like object and can store any pickleable value.
+It's pulled from persistent storage only if it's accessed during the request
+processing, and it's (re)saved only if the data changes. 
+
+Unlike Pylons' sesions, you don't have to call ``session.save()`` after adding
+or replacing keys because Pyramid does that for you. But you do have to call
+``session.changed()`` if you modify a mutable value in place (e.g., a session
+value that's a list or dict) because Pyramid can't tell that child objects have
+been modified.
+
+You can call ``session.invalidate()`` to discard the session data at the end of
+the request.  ``session.created`` is an integer timestamp in Unix ticks telling
+when the session was created, and ``session.new`` is true if it was created
+during this request (as opposed to being loaded from persistent storage).
+
+Pyramid sessions have two extra features: flash messages and a secure form
+token. These replace ``webhelpers.pylonslib.flash`` and
+``webhelpers.pylonslib.secure_form``, which are incompatible with Pyramid.
+
+Flash messages are a session-based queue. You can push a message to be
+displayed on the next request, such as before redirecting. This is often used 
+after form submissions, to push a success or failure message before redirecting
+to the record's main screen. (This is different from form validation, which
+normally redisplays the form with error messages if the data is rejected.)
+
+To push a message, call ``request.session.flash("My message.")`` The message is
+normally text but it can be any object. Your site template will then have to
+call ``request.session.pop_flash()`` to retrieve the list of messages, and
+display then as it wishes, perhaps in <div>'s or a <ul>. The queue is
+automatically cleared when the messages are popped, to ensure they are
+displayed only once.
+
+The full signature for the flash method is::
+
+    session.flash(message, queue='', allow_duplicate=True)
+
+You can have as many message queues as you wish, each with a different string
+name. You can use this to display warnings differently from errors, or to show
+different kinds of messages at different places on the page. If
+``allow_duplicate`` is false, the message will not be inserted if an identical
+message already exists in that queue. The ``session.pop_flash`` method also takes a
+queue argument to specify a queue. You can also use ``session.peek_flash`` to
+look at the messages without deleting them from the queue.
+
+The secure form token prevents cross-site request forgery (CSRF)
+attacts. Call ``session.get_csrf_token()`` to get the session's token, which is
+a random string. (The first time it's called, it will create a new random token and
+store it in the session. Thereafter it will return the same token.) Put the
+token in a hidden form field. When the form submission comes back in the next
+request, call ``session.get_csrf_token()`` again and compare it to the hidden
+field's value; they should be the same. If the form data is missing the field
+or the value is different, reject the request, perhaps by returning a forbidden
+status. ``session.new_csrf_token()`` always returns a new token, overwriting
+the previous one if it exists.
+
+WebHelpers and forms
+====================
+
+Most of WebHelpers works with Pyramid, including the popular
+``webhelpers.html`` subpackage, ``webhelpers.text``, and ``webhelpers.number``.
+Pyramid does not depend on WebHelpers so you'll have to add the dependency to
+your application if you want to use it.  The only part that doesn't work with
+Pyramid is the ``webhelpers.pylonslib`` subpackage, which depends on Pylons'
+special globals.
+
+We are working on a form demo that compares various form libraries: Deform,
+Formish, FormEncode/htmlfill. 
+
+To organize the form display-validate-action route, we recommend the
+``pyramid_simpleform`` package. It replaces ``@validate`` in Pylons. It's not a
+decorator because too many people found the decorator too inflexible: they
+ended up copying part of the code into their action method.
+
+WebHelpers 1.3 has some new URL generator classes to make it easier to use
+with Pyramid. See the ``webhelpers.paginate`` documentation for details. (Note:
+this is *not* the same as Akhet's URL generator; it's a different kind of class
+specifically for the paginator's needs.)
+
+
+Shell
+=====
+
+**paster pshell** is similar to Pylons' "paster shell". It gives you an
+interactive shell in the application's namespace with a dummy request. Unlike
+Pylons, you have to specify the application section on the command line because
+it's not "main". Akhet, for convenience, names the section "myapp" regardless
+of the actual application name. 
+
+.. code-block:: sh
+
+    $ paster pshell development.ini myapp
+    Python 2.6.6 (r266:84292, Sep 15 2010, 15:52:39) 
+    [GCC 4.4.5] on linux2
+    Type "help" for more information. "root" is the Pyramid app root object, "registry" is the Pyramid registry object.
+    >>> registry.settings["sqlalchemy.url"]
+    'sqlite:////home/sluggo/exp/pyramid-docs/main/workspace/Zzz/db.sqlite'
+    >>> import pyramid.threadlocal
+    >>> request = pyramid.threadlocal.get_current_request()
+    >>> 
+
+As the example above shows, the interactice namespace contains two objects
+initially: ``root`` which is the root object, and ``registry`` from which you
+can access the settings. To get the request, you have to use Pyramid's
+threadlocal library to fetch it. This is one of the few places where it's
+recommended to use the threadlocal library.
+
+Deployment
+==========
+
+Deployment is the same for Pyramid as for Pylons. Use "paster serve" with
+mod_proxy, or mod_wsgi, or whatever else you prefer. 
+
+
+.. _MultiDict API: api.html#multidict
+.. _API: api.html
+.. _pyramid_exclog: http://docs.pylonsproject.org/projects/pyramid_exclog/en/latest/

File docs/usage.rst

-Usage
-%%%%%
+Creating an Application and Using Development Versions
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
-Creating a Pyramid/Akhet application
-====================================
+Creating a Pyramid application
+==============================
 
 Here are the basic steps to install Pyramid and Akhet, create a virtualenv and
-activate it, create an application, and run it so you can see it in the
-browser. The sample application is called "Zzz"; it contains a Python package
-``zzz``.
+activate it, create an application using the recommended scaffold, initialize
+the database, and run the application so you can see it in your web browser.
+Our sample application is called "Zzz"; it contains a Python package ``zzz``.
+A prebuilt tarball is available: Zzz.tar.gz_ [#]_.  The chapters after this
+will walk through this default application.
 
 For Pyramid 1.3 (unreleased; this won't work until it's released):
 
-.. code-block:: sh
+.. code-block:: console
 
     $ virtualenv --no-site-packages ~/directory/myvenv
     $ source ~/directory/myvenv/bin/activate
 
 For Pyramid 1.2 and earlier:
 
-.. code-block:: sh
+.. code-block:: console
 
     $ virtualenv --no-site-packages ~/directory/myvenv
     $ source ~/directory/myvenv/bin/activate
     (myenv)$ populate_Zzz development.ini
     (myenv)$ paster serve development.ini
 
-Throughout this manual we'll use "Zzz" for your application's name, and ``zzz``
-for the top-level Python module in the application.
-
 The "--no-site-packages" option is recommended for Pyramid; it isolates the
 virtualenv from packages installed globally on the computer, which may be
 incompatible or have conflicting versions. If you have trouble installing a
 Installing Akhet from its source repository works like most Python
 repositories. Pyramid, however, requires additional steps.
 
-.. code-block::  sh
+.. code-block::  console
 
     $ virtualenv --no-site-packages ~/directory/myvenv
     $ source ~/directory/myvenv/bin/activate
 To uninstall an application or package that was installed with pip, use "pip
 uninstall":
 
-.. code-block:: sh
+.. code-block:: console
 
    (myvenv)$ pip uninstall Zzz
 
 if present.
 
 
+.. [#] The tarball was built with Pyramid 1.3-dev (2011-12-02, rev.
+   d5666e630a08c943a22682540aa51174cee6851f), Python 2.7.2, on Ubuntu 11.10
+   (Linux). 
+
+
 .. _Pyramid documentation: http://docs.pylonsproject.org/en/latest/docs/pyramid.html
 .. _Pyramid tutorials: http://docs.pylonsproject.org/projects/pyramid_tutorials/dev/
 .. _virtualenv: http://pypi.python.org/pypi/virtualenv
 .. _Installing Pyramid: http://docs.pylonsproject.org/projects/pyramid/1.0/narr/install.html
 .. _submodules: http://schacon.github.com/git/git-submodule.html
+.. _Zzz.tar.gz: _static/Zzz.tar.gz