Commits

Anonymous committed 3b26f40

Get documentation ready for release.

Comments (0)

Files changed (13)

-Bugs and todo
--------------
+Bugs
+
+
+* None. :)
+
+Todo
+
 
 * Add a production.ini and test.ini.
 * Add a database demo such as a wiki.
+* Allow keyword args to supplement settings in ``add_engine()``.
+* Add an appendix explaining the different ways to initialize the database
+  and why we chose an "-m" script over a bin script, Paster plugin, or
+  websetup.
+* Add an article explaining the exact differences in the application 
+  template compared to the standard Pyramid templates, and why it was
+  made that way.
       carry prominent notices stating that you changed the files and
       the date of any change.
 
-  Disclaimer
-
-    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND
-    ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
-    TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
-    PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-    HOLDERS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
-    EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
-    TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-    DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
-    ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
-    TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
-    THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-    SUCH DAMAGE.
-
-pyramid_beaker
-==============
+pyramid_sqla
+============
 
-Provides a session factory for the `Pyramid <http://docs.pylonshq.com>`_ web
-framework backed by the `Beaker <http://beaker.groovie.org/>`_ sessioning
-system.
+**pyramid_sqla** is a library for Pyramid applications using SQLAlchemy, and
+an application template that configures the model and other things similar but
+not identical to Pylons 1.
 
-See `the Pylons Project documentation <http://docs.pylonshq.com>`_ for more
-information.
+Version 0.1 is an initial proof-of-concept release; the API is subject to
+change depending on user feedback. The goal is a 1.0 release before Pyramid
+1.0.
+
+See the 'docs' directory for documentation.
+
+(c) 2010 Mike Orr

demos/SimpleDemo/simpledemo/__init__.py

     config = Configurator(settings=settings)
 
     # Initialize database
-    settings["sqlalchemy.convert_unicode"] = True
-    pyramid_sqla.add_engine(settings, prefix='sqlalchemy.')
+    pyramid_sqla.add_engine(settings, prefix='sqlalchemy.', 
+        convert_unicode=True)
 
     # Configure Beaker sessions
     session_factory = pyramid_beaker.session_factory_from_settings(settings)

docs/application_templates.rst

-Application Template Details
-============================
-
-For a simple Pyramid application with one database engine, follow these steps:
-
-1. Add it to your list of 'requires' dependencies in *setup.py*.
-
-2. In your *development.ini* file add:
-
-   .. code-block:: ini
-
-        sqlalchemy.url = sqlite:///%(here)s/db.sqlite
-
-   You can also add any SQLALchemy engine options such as:
-
-    .. code-block:: ini
-
-        sqlalchemy.pool_recycle = 3600
-        sqlalchemy.convert_unicode = true
-
-3. Add the repoze.tm2 middleware to the pipeline:
-
-   .. code-block:: ini
-
-        [pipeline:main]
-        pipeline =
-            egg:WebError#evalerror
-            egg:repoze.tm2#tm
-            MyApp
-
-    (Replace "myapp" with your application name, corresponding to the
-    "[app:myapp]" section.)
-
-4.  To log SQL queries, modify the "[logger_sqlalchemy]" section in
-    *development.ini*. Set ``level = INFO`` to log all queries, ``level =
-    DEBUG`` to log queries and results (very verbose!), or ``level = WARN`` to
-    log neither. If your *development.ini* does not have a
-    "[logger_sqlalchemy]" section, create a new Pyramid application and copy
-    all the logging sections from its *development.ini*.
-
-5. In *myapp/__init__.py*, add the following at the top::
-
-        import pyramid_sqla
-
-   Then inside the ``main()`` function, add this like::
-
-        pyramid_sqla.init_dbsession(settings, prefix="sqlalchemy.")
-
-6. In models or views or anywhere else you need them::
-
-        import pyramid_sqla
-
-        Session = pyramid_sqla.get_dbsession()
-        engine = pyramid_sqla.get_dbengine()
-
-Note that ``get_dbsession()`` returns a SQLAlchemy scoped session
-not a plain SQLAlchemy session.
 pyramid_sqla
 ============
+:Version: 0.1 released XXXX-XX-XXX
+:Docs:
+:Download: 
+:PyPI:
+:Source: http://bitbucket.org/sluggo/pyramid_sqla (Mercurial)
+
 
 **pyramid_sqla** is a library for Pyramid_ applications using SQLAlchemy_, and
-an application template that configures the model and other things similar (but
-not identical) to Pylons 1.  If follows the philosophy of "make the
+an application template that configures the model and other things similar but
+not identical to Pylons 1.  If follows the philosophy of "make the
 simple things simple and the complex things possible".
 
-The current version is 0.1. This is a proof-of-concept release; the API is
-subject to change depending on user feedback. The goal is a 1.0 release before
-Pyramid 1.0.
+Version 0.1 is an initial proof-of-concept release; the API is subject to
+change depending on user feedback. The goal is a 1.0 release before Pyramid
+1.0.
 
 .. _SQLAlchemy: http://sqlalchemy.org/
 .. _Pyramid: http://docs.pylonshq.com/pyramid/dev/
 .. toctree::
    :maxdepth: 1
 
-   install
    usage
+   non_database_features
    model_examples
-   application_templates
    bugs
    changes
 

docs/install.rst

-Installation and Demos
-%%%%%%%%%%%%%%%%%%%%%%
-
-Install pyramid_sqla like any Python package, using either "pip install
-pyramid_sqla" or "easy_install pyramid_sqla". To check out the development
-repository: "hg clone http://bitbucket.org/sluggo/pyramid_sqla". 
-
-There's one demo application in the source distribution but it doesn't do much
-yet. It displays your database URL. The create_db script puts a sample record
-in a table. It has a pony and a unicorn (using Paste Pony). To run it, first
-install pyramid_sqla, then do:
-
-.. code-block:: sh
-   :linenos:
-
-    $ cd demos/SimpleDemo
-    $ python setup.py egg_info
-    $ python -m simpledemo.scripts.create_db development.ini
-    $ paster serve development.ini
-
-Line 2 generates the package's metadata. (You only have to do this once, and
-whenever you modify setup.py.) Line 3 creates the database (db.sqlite) and
-inserts initial data. Line 4 serves the website.
-
-Note: If you get an"AssertionError: Unexpected files/directories
-in PATH/pyramid_sqla" when trying to install or upgrade pyramid_sqlalchemy,
-it's becuase pip gets confused if egg_info files are present in a directory
-it's not expecting them. Delete all *\*.egg.info* directories in all
-demos, and then try the installation again.
-

docs/model_examples.rst

 
     import pyramid_sqla as psa
     import sqlalchemy as sa
-    import sqlalchemy.ext.declarative as declarative
     import sqlalchemy.orm as orm
 
-    Base = declarative.declarative_base()
+    Base = psa.get_base()
+    Session = psa.get_session()
 
     class User(Base):
         __tablename__ = "users"
 
     import pyramid_sqla as psa
     import sqlalchemy as sa
-    import sqlalchemy.ext.declarative as declarative
     import sqlalchemy.orm as orm
 
-    Base = declarative.declarative_base()
+    Base = psa.get_base()
+    Session = psa.get_session()
 
     class User(Base):
         __tablename__ = "users"
         def by_name(class_):
             """Return a query of users sorted by name."""
             User = class_
-            q = psa.get_dbsession().query(User)
+            q = Session.query(User)
             q = q.order_by(User.name)
             return q
         
+
     class Address(Base):
         __tablename__ = "addresses"
 
         activity = sa.Column(sa.Unicode(100), nullable=False)
 
 
-    assoc_users_activities = sa.Table("assoc_users_activities", psa.Base.metadata,
+    assoc_users_activities = sa.Table("assoc_users_activities", Base.metadata,
         foreign_key_column("user_id", sa.Integer, "users.id"),
         foreign_key_column("activities_id", sa.Unicode(100), "activities.id"))
             
     q = models.User.query()
 
 Whether this is a good thing or not depends on your perspective.
-
-Initializing the database
--------------------------

docs/non_database_features.rst

+Non-database Features
+%%%%%%%%%%%%%%%%%%%%%
+
+This chapter discusses the non-database features in pyramid_sqla's application
+template.
+
+Static files
+============
+
+This template takes a different approach to static files. The standard
+templates use ``config.add_static_view`` to mount the static directory onto
+the "/static" URL. This means all static files have to have the "/static"
+prefix, so the top-level static assets -- "/favicon.ico", "/robots.txt", and
+"/w3c_" (a machine-readable privacy policy) -- have to be served another way.
+
+pyramid_sqla does some routing maneuvers to mount the static directory onto
+"/", overlaying your dynamic URLs. This lets you serve all your static files
+the same way. It's enabled by the following lines in *myapp/__init__.py*::
+
+    from pyramid_sqla.static import add_static_route
+
+    # Set up routes and views
+    config.add_handler('home', '/', 'pyramidapp.handlers:MainHandler',
+                       action='index')
+    config.add_handler('main', '/{action}', 'pyramidapp.handlers:MainHandler',
+        path_info=r'/(?!favicon\.ico|robots\.txt|w3c)')
+    add_static_route(config, 'pyramidapp', 'static', cache_max_age=3600)
+
+The first ``config.add_handler()`` call is an ordinary home page route, nothing
+special about it. 
+
+The second is a catchall route for "/{action}"; i.e., any one-component URL.
+The ``path_info`` regex prevents the route from matching URLs referring to
+top-level static assets. You'll have to do this with any routes that could
+accidentally match your static URLs, which are generally routes with a variable
+first component.
+
+The ``add_static_route()`` call is a convenience function that mounts the
+static directory onto "/". Then it just serves the files. If you look at the
+wrapper's source code, it uses a custom route predicate so that if the implied
+file doesn't exist, the route won't match the URL. This gives later routes or
+traversal a chance to work, otherwise they would be blocked. 
+
+
+.. _w3c: http://www.w3.org/P3P/ 
+
+Helpers and the ``h`` variable
+==============================
+
+The *myapp/helpers.py* module is automatically available in templates as the
+``h`` variable. This is borrowed from Pylons 1 and makes a convenient place to
+put generic formatting functions or other convenience functions you use
+throughout your templates.
+
+You can also import the helpers module into your view handlers or other
+code, but for that you'll have to do the import yourself.
+
+The WebHelpers_ library contains a variety of helpers including an HTML tag
+builder, form input tag builders, text and number formatting, etc. WebHelpers
+is available separately in PyPI.
+
+*Note:* ``webhelpers.paginate`` is not compatible with Pyramid unless you
+provide a custom URL generation callback. A Pyramid-compatible alternative is
+under development. The helpers in ``webhelpers.pylonslib`` are not compatible
+with Pyramid due to their dependency on Pylons 1's magic globals. An
+alternative system for flash messages and secure forms is built into Pyramid's
+session object.
+
+You can change which variables automatically appear in all templates. The code
+is in *myapp/subscribers.py*.
+
+Templates ending in .html
+=========================
+
+The app template arranges for all template files ending in .html to be served
+by Mako. This is done by the following lines in *myapp/__init__.py*::
+
+    config.add_renderer('.html', 'pyramid.mako_templating.renderer_factory')
+
+You can change this to associate .html with another templating engine like
+Jinja2, or disable it by commenting out the line.
+
+
+.. _WebHelpers:  http://webhelpers.groovie.org/
 Usage and API
-=============
+%%%%%%%%%%%%%
 
-Installation and demos
-----------------------
+Installation
+============
 
 Install pyramid_sqla like any Python package, using either "pip install
 pyramid_sqla" or "easy_install pyramid_sqla". To check out the development
 repository: "hg clone http://bitbucket.org/sluggo/pyramid_sqla". 
 
-There's one demo application in the source distribution but it doesn't do much
-yet. It displays your database URL. The create_db script puts a sample record
-in a table. It has a pony and a unicorn (using Paste Pony). To run it, first
-install pyramid_sqla, then do:
-
-.. code-block:: sh
-   :linenos:
-
-    $ cd demos/SimpleDemo
-    $ python setup.py egg_info
-    $ python -m simpledemo.scripts.create_db development.ini
-    $ paster serve development.ini
-
-Line 2 generates the package's metadata. (You only have to do this once, and
-whenever you modify setup.py.) Line 3 creates the database (db.sqlite) and
-inserts initial data. Line 4 serves the website.
-
-Note: If you get an"AssertionError: Unexpected files/directories
-in PATH/pyramid_sqla" when trying to install or upgrade pyramid_sqlalchemy,
-it's becuase pip gets confused if egg_info files are present in a directory
-it's not expecting them. Delete all *\*.egg.info* directories in all
-demos, and then try the installation again.
+A wiki demo is in preparation but is not available yet.
 
 Usage
------
+=====
 
 1. Create an application:
    
    The default application doesn't define any tables or models so it doesn't
    actually do anything except display some help links.
 
-2. In *MyApp/development.ini* change the default database URL to your database:
+2. In *development.ini* change the default database URL to your database:
 
    .. code-block:: ini
 
    manual, and in the Dialects_ section for particular databases.
 
 3. If you want your engine to always convert String and Text columns to unicode
-   regardless of what the INI file says, edit *myapp/__init__.py* and uncomment
+   regardless of what the INI file says, edit *myapp/__init__.py* and change
    the line::
 
-        settings["sqlalchemy.convert_unicode"] = True
+        pyramid_sqla.add_engine(settings, prefix="sqlalchemy.")
+
+   to::
+
+        pyramid_sqla.add_engine(settings, prefix="sqlalchemy.", 
+            convert_unicode=True)
 
 3. In models or views or wherever you need them, access the database session,
    engine, and declarative base this way::
 description of the differences between this application template and a basic
 Pyramid template.
 
+
+API
+===
+
+.. currentmodule:: pyramid_sqla
+
+.. automodule:: pyramid_sqla
+
+.. autofunction:: add_engine
+
+.. autofunction:: get_session
+
+.. autofunction:: get_engine
+
+.. autofunction:: get_base
+
+.. autofunction:: reset
+
+
 Managed transactions
---------------------
+====================
 
 ``pyramid_sqla`` has managed transactions. After the view is called, it will
 automatically commit all database changes unless an uncaught exception occurs,
 of the request processing.
 
 Disabling the transaction manager
-+++++++++++++++++++++++++++++++++
+---------------------------------
 
 If you don't want managed transactions, reconfigure the Session to not have the
 extension::
 
-    pyramid_sqla.get_session().config(extension=None)
+    Session.config(extension=None)
 
 and also delete the "egg:repoze.tm2#tm" line in the "[pipeline:main]" section
 in *development.ini*.  If you disable the manager, you'll have to call
 also have to configure the application to remove the session at the end of the
 request. This would be in an event subscriber but I'm not sure which one.
 
+Caveat: adding your own session extensions
+------------------------------------------
+
+If you modify the ``extension`` session option in any way you'll lose the
+transaction extension unless you re-add it. The extension lives in the
+semi-private ``_zte`` variable in the library. Here's how to add your own
+extension while keeping the transaction extension::
+
+    Session.configure(extension=[MyWonderfulExtension(), pyramid_sqla._zte])
+ 
+
 Multiple databases
-------------------
+==================
 
-If you need to connect to multiple databases, list them all in
+The default configuration in *myapp/__init__.py* configures one database::
+
+    import pyramid_sqla as psa
+    psa.add_engine(settings, prefix="sqlalchemy.")
+
+To connect to multiple databases, list them all in
 *development.ini* under distinct prefixes. For instance:
 
 .. code-block: ini
     data.url = postgresql://me:PASSWORD@localhost/mydb
     sessions.url = sqlite:///%(here)s/scratch.sqlite
 
-Then modify *myapp/__init__.py* and replace the ``pyramid_sqla.init_dbsession``
-call. What to replace it with depends on what you want to do. Let's assume that
-if the application has a default engine, it's called "default" and corresponds
-to an INI prefix "sqlalchemy.". If there are two other (non-default) engines,
-they will be called "engine1" and "engine2" as both their engine name and INI
-prefix.
+Then modify *myapp/__init__.py* and put an ``add_engine()`` call for each
+database. The examples below elaborate on the API docs.
 
 A default engine plus other engines
-+++++++++++++++++++++++++++++++++++
+-----------------------------------
 
 In this scenario, the default engine is used for most operations, but two other
 engines are also used occasionally::
 
     # Initialize the default engine.
-    pyramid_sqla.init_dbsession(settings, prefix="sqlalchemy.")
+    pyramid_sqla.add_engine(settings, prefix="sqlalchemy.")
 
     # Initialize the other engines.
     pyramid_sqla.add_engine(settings, name="engine1", prefix="engine1.")
     pyramid_sqla.add_engine(settings, name="engine2", prefix="engine2.")
 
-Queries will choose the default engine by default. To choose a different engine
-you have to use the ``bind=`` argument on some methods, or 
-``engine.execute(sql)`` to run a SQL SELECT or command on a particular engine.
+Queries will use the default engine by default. To use a different engine
+you have to use the ``bind=`` argument the method that executes the query, 
+``engine.execute(sql)`` to run a SQL SELECT or command in a particular engine.
 
 Two engines, but no default engine
-++++++++++++++++++++++++++++++++++
+----------------------------------
 
 In this scenario, two engines are equally important, and neither is predominent
 enough to deserve being the default engine. This is useful in applications
 engine every time using the ``bind=`` argument or ``engine.execute(sql)``.
 
 Different tables bound to different engines
-+++++++++++++++++++++++++++++++++++++++++++
+-------------------------------------------
 
 It's possible to bind different ORM classes to different engines in the same
-database session, but ``init_dbsession()`` has no built-in support for it. 
-Instead you should configure your application with no default engine, and
-then call the scoped session's ``.configure`` method with the ``binds=``
-argument to specify which classes go to which engines. For instance::
+database session.  Configure your application with no default engine, and then
+call the Session's ``.configure`` method with the ``binds=`` argument to
+specify which classes go to which engines. For instance::
 
-    dbsession = pyramid_sqla.init_dbsession()
     pyramid_sqla.add_engine(settings, name="engine1", prefix="engine1.")
     pyramid_sqla.add_engine(settings, name="engine2", prefix="engine2.")
+    Session = pyramid_sqla.get_dbsession()
     import myapp.models as models
     binds = {models.Person: engine1, models.Score: engine2}
-    dbsession.configure(binds=binds)
+    Session.configure(binds=binds)
 
 The keys in the ``binds`` dict can be SQLAlchemy ORM classes, table objects, or
 mapper objects.
 
-API
----
-
-.. currentmodule:: pyramid_sqla
-
-.. automodule:: pyramid_sqla
-
-.. autofunction:: add_engine
-
-.. autofunction:: get_session
-
-.. autofunction:: get_engine
-
-.. autofunction:: get_base
-
-.. autofunction:: reset
 
 Logging
--------
+=======
 
-The default application template is configured to log SQL queries.
-3. The logging is configured to log SQL queries. To change
-   this, adjust the "level" line in the "[logger_sqlalchemy]" section. ::
+The default application template is configured to log SQL queries.  To change
+this, adjust the "level" line in the "[logger_sqlalchemy]" section. ::
 
-        [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.)
+     [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.)
 
-   SQLAlchemy has many other loggers; e.g., to show connection pool activity or
-   ORM operations. For details see `Configuring Logging`_ in the SQLAlchemy
-   manual.
+SQLAlchemy has many other loggers; e.g., to show connection pool activity or
+ORM operations. For details see `Configuring Logging`_ in the SQLAlchemy
+manual.
 
-   *Caution:* Don't set the 'echo' engine option (i.e., don't do
-   "sqlalchemy.echo = true"). This sets up a duplicate logger which may cause
-   double logging.
+*Caution:* Don't set the 'echo' engine option (i.e., don't do
+"sqlalchemy.echo = true"). This sets up a duplicate logger which may cause
+double logging.
 
 
 Declarative base
-----------------
+================
+
+The library includes a declarative base for convenience, but some people may
+choose to define their own declarative base in their model instead. And there's
+one case where you *have* to create your own declarative base; namely, if you
+want to modify its constructor args. The ``cls`` argument is the most common:
+it specifies a superclass which all ORM object should inherit. This allows you
+to define class methods and other methods which are available in all your ORM
+classes.
 
 Reflected tables
-----------------
+================
 
-Reflected tables pose a bit of a dilemma because it depends on a live database
-engine but that may not available when the module is imported. There are two
-ways around this. One way is to assume that when the module is imported,
-``pyrmaid_sqla.init_dbsession()`` has already been initialized, and thus that
-``get_dbengine()``, ``get_dbsession()``, and ``Base`` have all been bound to
-the appropriate engine. The other way is to put an ``init_model()`` function in
-your model, and call it after the engine has been configured. The function
-would then do everything that depends on a live engine. You can either pass the
-engine to the function or have the function call ``get_dbengine()`` to fetch
-it.
+Reflected tables pose a dilemma because it depends on a live database
+connection in order to be initialized. But the engine may not be configured yet
+when the model is imported. ``pyramid_sqla`` does not address this issue
+directly. Pylons 1 models traditionally have an ``init_model(engine)`` function
+which performs any initialization that requires a live connection. Pyramid
+applications typically do not need this function because the Session, engines,
+and base are initialized in the ``pyramid_sqla`` library before the model is
+imported. But in the case of reflection, you may need an ``init_model``
+function.
 
-When using ``init_model()`` with declarative, we think you'd have to put the
-entire declarative class inside the function and use a ``global`` statement to
-assign it to the module scope. When not using declarative, we think you can put
-the ORM class at the module level, but the table definition and mapper would
-have to be inside the function, again using ``global`` to put the table at the
-module level.
+When not using declarative, the ORM classes can be defined at module level in
+the model, but the table definitions and mappers will have to be set up inside
+the ``init_model`` function using a ``global`` statement to set the module
+globals.
 
-If you choose not to use ``init_model()``, remember to initialize
-``pyramid_sqla`` before importing the models. The application template
-initializes the database in *myapp/__init__.py*, and does not import the models
-except in views and in *websetup.py*. (Actually, it doesn't import the models
-at all, but this is where you most likely would.)
-
+When using declarative, we *think* the entire ORM class must be defined inside
+the function, again using a ``global`` statement to project the values into
+the module scope. That's unfortunate but we can't think of a way around it.
+If you can, please tell us.
 
 
 .. _Engine Configuration: http://www.sqlalchemy.org/docs/core/engines.html

pyramid_sqla/__init__.py

 _base = _session = _engines = _zte = None
 
 def reset():
-    """Restore initial module state.
+    """Delete all engines and restore the initial module state.
     
-    This function is mainly for unit tests and debugging. It deletes all
-    engines and recreates the dbsession and other global objects.
+    This function is mainly for unit tests and debugging. It undoes all
+    customizations and reverts to the initial module state.
     """
     global _base, _session, _engines, _zte
     _zte = ZopeTransactionExtension()
     """Configure a SQLAlchemy database engine and return it.
 
     I configure an engine in different ways depending on the combination of
-    arguments. If ``name`` is not specified or the value is "default", I also
+    arguments. If ``name`` is not specified or its value is "default", I also
     bind the scoped session and the declarative base's metadata to it.
 
     Arguments:
       The default value is "sqlalchemy.", which tells SQLAlchemy to use the
       keys starting with "sqlalchemy." for this engine.
 
-    * ``engine``: An existing SQLAlchemy engine. 
+    * ``engine``: An existing SQLAlchemy engine. If you pass this you can't
+      pass ``settings``, ``prefix``, or ``**engine_args``.
 
-    * ``**engine_args``: Engine args supplied as keyword arguments. If these
-      arguments are passed, I call ``sqlalchemy.create_engine(**engine_args)``.
-
-    You must pass exactly one of ``settings``, ``engine``, and
-    ``**engine_args``. Raise ``RuntimeError`` if you pass none of them or too
-    many.
+    * ``**engine_args``: Engine args supplied as keyword arguments. If
+      ``settings`` is also passed, the keyword args override their
+      corresponding settings. If ``settings`` is not passed, I call
+      ``sqlalchemy.create_engine(**engine_args)`` directly.
 
     SQLAlchemy will raise a ``KeyError`` if the database URL is not specified.
     This may indicate the settings dict has no "PREFIX.url" key or that the
         engine1 = add_engine(settings, name="engine1", prefix="db1.")
         engine2 = add_engine(settings, name="engine2", prefix="db2.")
     """
-    if _count_true(settings, engine, engine_args) != 1:
-        m = "only one of 'settings', 'engine', or '**engine_args' allowed"
+    if engine and (settings or engine_args):
+        m = "can't specify settings or engine args when ``engine`` is present"
         raise TypeError(m)
-    if engine:
+    elif engine:
         e = engine
     elif settings:
         if not prefix:
             raise ValueError("empty prefix ('') is not allowed")
         url_key = prefix + "url"
-        if url_key not in settings:
+        if url_key not in settings and "url" not in engine_args:
             msg = """\
 no database URL specified
 settings key '%surl' is required when using prefix='%s'"""
             if prefix and not prefix.endswith("."):
                 msg += "\nHint: did you mean prefix='%s.'?" % prefix
             raise ValueError(msg)
-        e = sa.engine_from_config(settings, prefix)
+        e = sa.engine_from_config(settings, prefix, **engine_args)
     else:
         try:
             url = engine_args.pop("url")
     return e
 
 def get_session():
-    """Return the central SQLAlchemy scoped session.
-
-    If you call ``.configure`` to change the extension option, you'll lose the
-    ZopeTransactionExtension unless you re-add it. Here's how to do that::
-
-        pyramid_sqla.get_session().configure(
-            extension=[YOUR_EXTENSION, pyramid_sqla._zte])
-    """
+    """Return the central SQLAlchemy scoped session."""
     return _session
 
 def get_engine(name="default"):
     """Return the central declarative base.
     """
     return _base
-
-#### Private functions
-
-def _count_true(*items):
-    """Return a count of true items in ``*items``.
-    """
-    true_items = filter(None, items)
-    return len(true_items)
-

pyramid_sqla/paster_templates/pyramid_sqla/+package+/__init__.py_tmpl

     config = Configurator(settings=settings)
 
     # Initialize database
-    #settings["sqlalchemy.convert_unicode"] = True
     pyramid_sqla.add_engine(settings, prefix='sqlalchemy.')
 
     # Configure Beaker sessions
 
 setup(name='pyramid_sqla',
       version='0.1',
-      description='SQLAlchemy helpers for Pyramid',
+      description='A SQLAlchemy library and Pylons-like application template for Pyramid',
       long_description=README + '\n\n' +  CHANGES,
       classifiers=[
         "Intended Audience :: Developers",