changeset 201:ac0a27a72b9e

Reorganize Flask application setup code.
author Daniele Nicolodi <daniele@grinta.net>
date Wed, 16 Nov 2011 16:09:39 +0100
parents 6bcf931c0e59
children 10801d55c5d5
files src/ltpdarepo/__init__.py src/ltpdarepo/admin.py src/ltpdarepo/templates/layout.html
diffstat 3 files changed, 78 insertions(+), 70 deletions(-) [+]
line wrap: on
line diff
--- a/src/ltpdarepo/__init__.py	Tue Nov 15 19:15:58 2011 +0100
+++ b/src/ltpdarepo/__init__.py	Wed Nov 16 16:09:39 2011 +0100
@@ -3,27 +3,30 @@
 # This software may be used and distributed according to the terms of
 # the GNU Affero General Public License version 3 or any later version.
 
-from urlparse import urlparse, urljoin
+
 from datetime import datetime
+from pkg_resources import get_distribution
+from urlparse import urlparse, urljoin
 
-from flask import Flask, g, request, session, render_template, Markup, redirect, flash, url_for
+from flask import Flask, g, request, session, render_template, Markup, redirect, flash, url_for, current_app
 from werkzeug.exceptions import default_exceptions, InternalServerError, HTTPException
 
-from pkg_resources import get_distribution
 import MySQLdb as mysql
-import MySQLdb.converters
+import MySQLdb.converters as converters
 import dateutil.tz
 
-from ltpdarepo.security import secure, require, authenticate
+from .security import secure, require, authenticate
+from .views.browse import module as browse
+from .views.databases import module as databases
+from .views.feed import url_for_atom_feed, module as feed
+from .views.profile import module as profile
+from .views.queries import module as queries
+from .views.users import module as users
 
 
 SCHEMA = 29
 
 
-app = Flask(__name__)
-secure(app)
-
-
 class datetimeutc(datetime):
     # subclass of `datetime.datetime` with default string
     # representation including the timezone name
@@ -34,17 +37,16 @@
 # customize mysql types conversion for datetime fields to return
 # timezone aware objects in the UTC timezone
 def datetime_or_none_utc(s):
-    value = mysql.converters.DateTime_or_None(s)
+    value = converters.DateTime_or_None(s)
     if value is not None:
         value = datetimeutc(value.year, value.month, value.day, value.hour,
                             value.minute, value.second, value.microsecond,
                             tzinfo=dateutil.tz.tzutc())
     return value
-conversions = mysql.converters.conversions.copy()
+conversions = converters.conversions.copy()
 conversions[mysql.constants.FIELD_TYPE.DATETIME] = datetime_or_none_utc
 
 
-@app.before_request
 def before_request():
     # get version information from package
     g.version = get_distribution('ltpdarepo').version
@@ -54,8 +56,9 @@
         return
 
     # open database connection
-    g.db = mysql.connect(host=app.config['HOSTNAME'], db=app.config['DATABASE'],
-                         user=app.config['USERNAME'], passwd=app.config['PASSWORD'],
+    config = current_app.config
+    g.db = mysql.connect(host=config['HOSTNAME'], db=config['DATABASE'],
+                         user=config['USERNAME'], passwd=config['PASSWORD'],
                          charset='utf8', conv=conversions)
 
     # validate schema revision
@@ -69,7 +72,6 @@
             'Required version: %s.</p>' % (g.schema, SCHEMA))
 
 
-@app.teardown_request
 def teardown_request(exception):
     # close database connection
     db = getattr(g, 'db', None)
@@ -77,8 +79,7 @@
         db.close()
 
 
-# register error handlers
-def _error_handler(error):
+def error_handler(error):
     if not isinstance(error, HTTPException):
         # nicely report tracebacks
         import traceback
@@ -86,15 +87,11 @@
         error.description += '<pre>' + traceback.format_exc() + '</pre>'
     return render_template('error.html', error=error), error.code
 
-for exc in default_exceptions:
-    app.error_handler_spec[None][exc] = _error_handler
 
-
-@app.template_filter('breadcrumbs')
-def breadcrumbs(path):
+def breadcrumbs():
     url = []
     parts = []
-    for item in path.split('/')[1:-1]:
+    for item in request.path.split('/')[1:-1]:
         url.append(item)
         if item:
             parts.append((item, urljoin(url_for('index'), '/'.join(url))))
@@ -111,7 +108,6 @@
     args.update(request.args)
     args.update(p=page)
     return url_for(request.endpoint, **args)
-app.jinja_env.globals['url_for_other_page'] = url_for_other_page
 
 
 def url_for_other_order(field):
@@ -123,7 +119,6 @@
     args.update(request.args)
     args.update(o=field, r=int(reverse))
     return url_for(request.endpoint, **args)
-app.jinja_env.globals['url_for_other_order'] = url_for_other_order
 
 
 def url_for_other_size(size):
@@ -131,7 +126,6 @@
     args.update(request.args)
     args.update(n=size)
     return url_for(request.endpoint, **args)
-app.jinja_env.globals['url_for_other_size'] = url_for_other_size
 
 
 def is_safe_url(target):
@@ -140,57 +134,69 @@
     return test.scheme in ('http', 'https') and test.netloc == ref.netloc
 
 
-@app.route('/login', methods=['GET', 'POST'])
-def login():
-    if request.method == 'POST':
-        if authenticate(request.form['username'], request.form['password']):
-            session['username'] = request.form['username']
-            target = request.args.get('next')
-            if not target or not is_safe_url(target):
-                target = url_for('index')
-            return redirect(target)
-        flash('Login failed.', category='error')
+class Application(Flask):
+    def __init__(self, conf=None, **kwargs):
+        super(Application, self).__init__(__name__)
+        secure(self)
 
-    return render_template('login.html')
-
+        # configuration
+        self.config.from_pyfile('config.py')
+        if conf is not None:
+            self.config.from_pyfile(conf)
+        self.config.update(kwargs)
 
-@app.route('/logout')
-def logout():
-    session.pop('username', None)
-    return redirect(url_for('index'))
-
+        @self.route('/')
+        @require('user')
+        def index():
+            curs = g.db.cursor()
+            curs.execute("""SELECT DISTINCT Db FROM mysql.db, available_dbs
+                            WHERE Select_priv='Y' AND User=%s AND Db=db_name
+                            ORDER BY Db""", session['username'])
+            dbs = [row[0] for row in curs.fetchall()]
+            return render_template('index.html', databases=dbs)
 
-@app.route('/')
-@require('user')
-def index():
-    curs = g.db.cursor()
-    curs.execute("""SELECT DISTINCT Db FROM mysql.db, available_dbs
-                    WHERE Select_priv='Y' AND User=%s AND Db=db_name
-                    ORDER BY Db""", session['username'])
-    dbs = [row[0] for row in curs.fetchall()]
-    return render_template('index.html', databases=dbs)
+        @self.route('/login', methods=['GET', 'POST'])
+        def login():
+            if request.method == 'POST':
+                if authenticate(request.form['username'], request.form['password']):
+                    session['username'] = request.form['username']
+                    target = request.args.get('next')
+                    if not target or not is_safe_url(target):
+                        target = url_for('index')
+                    return redirect(target)
+                flash('Login failed.', category='error')
 
+            return render_template('login.html')
 
-from .views.browse import module
-app.register_blueprint(module, url_prefix='/browse')
+        @self.route('/logout')
+        def logout():
+            session.pop('username', None)
+            return redirect(url_for('index'))
 
-from .views.feed import module, url_for_atom_feed
-app.register_blueprint(module, url_prefix='/browse')
-app.jinja_env.globals['url_for_atom_feed'] = url_for_atom_feed
+        # database connection
+        self.before_request(before_request)
+        self.teardown_request(teardown_request)
 
-from .views.profile import module
-app.register_blueprint(module, url_prefix='/user')
+        # template globals
+        self.jinja_env.globals['breadcrumbs'] = breadcrumbs
+        self.jinja_env.globals['url_for_other_page'] = url_for_other_page
+        self.jinja_env.globals['url_for_other_order'] = url_for_other_order
+        self.jinja_env.globals['url_for_other_size'] = url_for_other_size
+        self.jinja_env.globals['url_for_atom_feed'] = url_for_atom_feed
 
-from .views.databases import module
-app.register_blueprint(module, url_prefix='/manage/databases')
+        # error handlers
+        for exc in default_exceptions:
+            self.error_handler_spec[None][exc] = error_handler
 
-from .views.queries import module
-app.register_blueprint(module, url_prefix='/manage/queries')
-
-from .views.users import module
-app.register_blueprint(module, url_prefix='/manage/users')
+        # blueprints
+        self.register_blueprint(browse, url_prefix='/browse')
+        self.register_blueprint(feed, url_prefix='/browse')
+        self.register_blueprint(profile, url_prefix='/user')
+        self.register_blueprint(databases, url_prefix='/manage/databases')
+        self.register_blueprint(queries, url_prefix='/manage/queries')
+        self.register_blueprint(users, url_prefix='/manage/users')
 
 
 def main():
-    app.config.from_pyfile('config.py')
+    app = Application()
     app.run()
--- a/src/ltpdarepo/admin.py	Tue Nov 15 19:15:58 2011 +0100
+++ b/src/ltpdarepo/admin.py	Wed Nov 16 16:09:39 2011 +0100
@@ -19,14 +19,16 @@
 except ImportError:
     HAS_SQL_ALCHEMY = False
 
-from . import app
-app.config.from_pyfile('config.py')
 
 from .user import User
 from .database import Database
 from .config import HOSTNAME, DATABASE, USERNAME, PASSWORD
 
 
+from . import Application
+app = Application()
+
+
 @contextmanager
 def interact(app):
     # fake request
--- a/src/ltpdarepo/templates/layout.html	Tue Nov 15 19:15:58 2011 +0100
+++ b/src/ltpdarepo/templates/layout.html	Wed Nov 16 16:09:39 2011 +0100
@@ -22,7 +22,7 @@
       {% block page %}
 
       <div class="bar wrapper">
-        <div class="breadcrumbs">{{ request.path|breadcrumbs }}</div>
+        <div class="breadcrumbs">{{ breadcrumbs() }}</div>
         <div class="user">
           {% if session.username is defined %}
           <a href="{{ url_for('user.view', username=session.username) }}">{{ session.username }}</a>