From ae5ff89c7f8a1ab408c6aaf2717520dcd0a52b78 Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Fri, 13 Feb 2015 21:22:01 -0600 Subject: [PATCH] Refactor `app` module to promote code sharing. Hopefully this is a good approach, we'll see. --- tailbone/app.py | 102 +++++++++++++++++++++++++++++++++++------------- 1 file changed, 74 insertions(+), 28 deletions(-) diff --git a/tailbone/app.py b/tailbone/app.py index d9c791e4..20ce4b80 100644 --- a/tailbone/app.py +++ b/tailbone/app.py @@ -27,64 +27,110 @@ Application Entry Point from __future__ import unicode_literals import os - -from sqlalchemy import engine_from_config +import logging import edbob from edbob.pyramid.forms.formalchemy import TemplateEngine +import rattail.db +from rattail.config import RattailConfig +from rattail.exceptions import ConfigurationError +from rattail.db.util import get_engines +from rattail.db.continuum import configure_versioning from rattail.db.types import GPCType import formalchemy from pyramid.config import Configurator from pyramid.authentication import SessionAuthenticationPolicy -from zope.sqlalchemy import ZopeTransactionExtension -from tailbone.db import Session +import tailbone.db from tailbone.auth import TailboneAuthorizationPolicy from tailbone.forms import GPCFieldRenderer -def main(global_config, **settings): - """ - This function returns a Pyramid WSGI application. - """ +log = logging.getLogger(__name__) - # Use Tailbone templates by default. - settings.setdefault('mako.directories', ['tailbone:templates']) - # Make two attempts when "retryable" errors happen during transactions. - # This is intended to gracefully handle database restarts. +def make_rattail_config(settings): + """ + Make a Rattail config object from the given settings. + """ + # Initialize rattail config and embed it in the settings dict, to make it + # available to web requests later. + path = settings.get('edbob.config') + if not path or not os.path.exists(path): + raise ConfigurationError("Please set 'edbob.config' in [app:main] section of config " + "to the path of your config file. Lame, but necessary.") + edbob.init('rattail', path) + log.info("using rattail config file: {0}".format(path)) + rattail_config = RattailConfig(edbob.config) + settings['rattail_config'] = rattail_config + + # Load all Rattail database engines from config, and store in settings + # dict. This is necessary e.g. in the case of a host server, to have + # access to its subordinate store servers. + rattail_engines = get_engines(rattail_config) + settings['rattail_engines'] = rattail_engines + + # Configure the database session classes. Note that most of the time we'll + # be using the Tailbone Session, but occasionally (e.g. within batch + # processing threads) we want the Rattail Session. The reason is that + # during normal request processing, the Tailbone Session is preferable as + # it includes Zope Transaction magic. Within an explicitly-spawned thread + # however, this is *not* desirable. + rattail.db.Session.configure(bind=rattail_engines['default']) + tailbone.db.Session.configure(bind=rattail_engines['default']) + + # Configure (or not) Continuum versioning. + configure_versioning(rattail_config) + + return rattail_config + + +def provide_postgresql_settings(settings): + """ + Add some PostgreSQL-specific settings to the app config. Specifically, + this enables retrying transactions a second time, in an attempt to + gracefully handle database restarts. + """ settings.setdefault('tm.attempts', 2) + +def make_pyramid_config(settings): + """ + Make a Pyramid config object from the given settings. + """ config = Configurator(settings=settings) - # Initialize edbob, dammit. - edbob.init('rattail', os.path.abspath(settings['edbob.config'])) - edbob.init_modules(['edbob.time']) - - # Configure the primary database session. - engine = engine_from_config(settings) - Session.configure(bind=engine) - Session.configure(extension=ZopeTransactionExtension()) - # Configure user authentication / authorization. config.set_authentication_policy(SessionAuthenticationPolicy()) config.set_authorization_policy(TailboneAuthorizationPolicy()) # Bring in some Pyramid goodies. config.include('pyramid_beaker') + config.include('pyramid_mako') config.include('pyramid_tm') - # Bring in the rest of Tailbone. - config.include('tailbone') - # Configure FormAlchemy. formalchemy.config.engine = TemplateEngine() formalchemy.FieldSet.default_renderers[GPCType] = GPCFieldRenderer - # Consider PostgreSQL server restart errors to be "retryable." - config.add_tween('edbob.pyramid.tweens.sqlerror_tween_factory', - under='pyramid_tm.tm_tween_factory') + return config - return config.make_wsgi_app() + +def configure_postgresql(pyramid_config): + """ + Add some PostgreSQL-specific tweaks to the final app config. Specifically, + adds the tween necessary for graceful handling of database restarts. + """ + pyramid_config.add_tween('edbob.pyramid.tweens.sqlerror_tween_factory', + under='pyramid_tm.tm_tween_factory') + + +def main(global_config, **settings): + """ + This function returns a Pyramid WSGI application. + """ + rattail_config = make_rattail_config(settings) + pyramid_config = make_pyramid_config(settings) + return pyramid_config.make_wsgi_app()