/
factory.py
173 lines (133 loc) · 5.48 KB
/
factory.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
from __future__ import absolute_import
import os
import time
import uuid
from datetime import datetime
import logging
from logging.handlers import SMTPHandler
from flask import (Flask, abort, g, request, session, render_template,
current_app)
from flask.ext.babel import get_locale as babel_locale
from werkzeug.contrib.fixers import ProxyFix
from werkzeug.utils import import_string
from flamaster.account import user_ds, connection_ds
from flamaster.core import http
from flamaster.core.session import RedisSessionInterface
from flamaster.extensions import register_jinja_helpers
class ExtensionLoadError(Exception):
pass
class BlueprintLoadError(Exception):
pass
class AppFactory(object):
""" Application factory for creating flask instance serving this project.
Usage:
app = AppFactory('settings').init_app(__name__)
"""
def __init__(self, config, envvar='PROJECT_SETTINGS', bind_db_object=True):
self.app_config = config
self.app_envvar = os.environ.get(envvar, False)
# self.bind_db_object = bind_db_object
def init_app(self, app_name, **kwargs):
app = Flask(app_name, **kwargs)
app.config.from_object(self.app_config)
app.config.from_envvar(self.app_envvar, silent=True)
self._add_logger(app)
self._bind_extensions(app)
self._register_blueprints(app)
self._register_hooks(app)
app.session_interface = RedisSessionInterface()
app.wsgi_app = ProxyFix(app.wsgi_app)
return app
def _import(self, path):
module_name, object_name = path.rsplit('.', 1)
module = import_string(module_name)
return module, object_name
def _bind_extensions(self, app):
for ext_path in app.config.get('EXTENSIONS', []):
module, ext_name = self._import(ext_path)
try:
ext = getattr(module, ext_name)
except AttributeError:
ExtensionLoadError("Extension '{}'' not found".format(ext))
try:
# TODO: create workaround for special cases
if ext_name == 'security':
ext.init_app(app, datastore=user_ds)
elif ext_name == 'social':
ext.init_app(app, datastore=connection_ds)
else:
ext.init_app(app)
except AttributeError:
ext(app)
def _register_blueprints(self, app):
""" Register all blueprint modules listed under the settings
BLUEPRINTS key """
for blueprint_path in app.config.get('BLUEPRINTS', []):
module, bp_name = self._import(blueprint_path)
if hasattr(module, bp_name):
app.register_blueprint(getattr(module, bp_name))
else:
raise BlueprintLoadError('No {} blueprint '
'found'.format(bp_name))
def _register_hooks(self, app):
register_jinja_helpers(app)
app.before_request(setup_session)
app.errorhandler(http.NOT_FOUND)(show_page_not_found)
app.errorhandler(http.INTERNAL_ERR)(show_internal_error)
app.after_request(modify_headers)
app.after_request(close_session)
app.extensions['babel'].localeselector(get_locale(app))
def _add_logger(self, app):
""" Creates SMTPHandler for logging errors to the specified admins list
"""
kwargs = dict()
username = app.config.get('MAIL_USERNAME')
password = app.config.get('MAIL_PASSWORD')
if username and password:
kwargs['credentials'] = (username, password)
mail_handler = SMTPHandler(app.config['MAIL_SERVER'],
app.config['DEFAULT_MAIL_SENDER'],
app.config['ADMINS'],
'[ERROR] Findevent got error',
**kwargs)
mail_handler.setFormatter(logging.Formatter('''
Message type: %(levelname)s
Location: %(pathname)s:%(lineno)d
Module: %(module)s
Function: %(funcName)s
Time: %(asctime)s
Message:
%(message)s
'''))
mail_handler.setLevel(logging.DEBUG)
if not app.debug:
app.logger.addHandler(mail_handler)
def modify_headers(response):
map(lambda h: response.headers.add(*h), current_app.config['HEADERS'])
return response
def close_session(response):
state = current_app.extensions.get('sqlalchemy')
if state is not None:
state.db.session.commit()
return response
def setup_session():
g.now = time.mktime(datetime.utcnow().timetuple())
g.locale = babel_locale().language
session['id'] = session.get('id', uuid.uuid4().hex)
def show_internal_error(error):
return render_template('50x.html'), http.INTERNAL_ERR
def show_page_not_found(error):
try:
return render_template('base.html'), http.NOT_FOUND
except:
return abort(http.NOT_FOUND)
def get_locale(app):
def closure():
language = request.headers.get('X-Client-Locale', None)
if language is None:
languages = app.config['ACCEPT_LANGUAGES']
matched = request.accept_languages.best_match(languages)
language = session.get(app.config['LOCALE_KEY'], matched)
session[app.config['LOCALE_KEY']] = language
return language
return closure