##// END OF EJS Templates
core: code refactor and cleanups for easier pylons porting.
marcink -
r2321:a29db630 default
parent child Browse files
Show More
@@ -1,182 +1,177 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2010-2017 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 """
22 22 Pylons environment configuration
23 23 """
24 24
25 25 import os
26 26 import logging
27 27 import rhodecode
28 import platform
29 import re
30 import io
31 28
32 29 from mako.lookup import TemplateLookup
33 from pylons.configuration import PylonsConfig
34 from pylons.error import handle_mako_error
35 30 from pyramid.settings import asbool
36 31
37 32 # ------------------------------------------------------------------------------
38 33 # CELERY magic until refactor - issue #4163 - import order matters here:
39 34 from rhodecode.lib import celerypylons # this must be first, celerypylons
40 35 # sets config settings upon import
41 36
42 37 import rhodecode.integrations # any modules using celery task
43 38 # decorators should be added afterwards:
44 39 # ------------------------------------------------------------------------------
45 40
46 41 from rhodecode.lib import app_globals
47 42 from rhodecode.config import utils
48 43 from rhodecode.config.routing import make_map
49 from rhodecode.config.jsroutes import generate_jsroutes_content
50 44
51 45 from rhodecode.lib import helpers
52 from rhodecode.lib.auth import set_available_permissions
53 46 from rhodecode.lib.utils import (
54 repo2db_mapper, make_db_config, set_rhodecode_config,
55 load_rcextensions)
47 make_db_config, set_rhodecode_config, load_rcextensions)
56 48 from rhodecode.lib.utils2 import str2bool, aslist
57 49 from rhodecode.lib.vcs import connect_vcs, start_vcs_server
58 from rhodecode.model.scm import ScmModel
59 50
60 51 log = logging.getLogger(__name__)
61 52
53
62 54 def load_environment(global_conf, app_conf, initial=False,
63 55 test_env=None, test_index=None):
64 56 """
65 57 Configure the Pylons environment via the ``pylons.config``
66 58 object
67 59 """
60 from pylons.configuration import PylonsConfig
61 from pylons.error import handle_mako_error
62
68 63 config = PylonsConfig()
69 64
70 65
71 66 # Pylons paths
72 67 root = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
73 68 paths = {
74 69 'root': root,
75 70 'controllers': os.path.join(root, 'controllers'),
76 71 'static_files': os.path.join(root, 'public'),
77 72 'templates': [os.path.join(root, 'templates')],
78 73 }
79 74
80 75 # Initialize config with the basic options
81 76 config.init_app(global_conf, app_conf, package='rhodecode', paths=paths)
82 77
83 78 # store some globals into rhodecode
84 79 rhodecode.CELERY_ENABLED = str2bool(config['app_conf'].get('use_celery'))
85 80 rhodecode.CELERY_EAGER = str2bool(
86 81 config['app_conf'].get('celery.always.eager'))
87 82
88 83 config['routes.map'] = make_map(config)
89 84
90 85 config['pylons.app_globals'] = app_globals.Globals(config)
91 86 config['pylons.h'] = helpers
92 87 rhodecode.CONFIG = config
93 88
94 89 load_rcextensions(root_path=config['here'])
95 90
96 91 # Setup cache object as early as possible
97 92 import pylons
98 93 pylons.cache._push_object(config['pylons.app_globals'].cache)
99 94
100 95 # Create the Mako TemplateLookup, with the default auto-escaping
101 96 config['pylons.app_globals'].mako_lookup = TemplateLookup(
102 97 directories=paths['templates'],
103 98 error_handler=handle_mako_error,
104 99 module_directory=os.path.join(app_conf['cache_dir'], 'templates'),
105 100 input_encoding='utf-8', default_filters=['escape'],
106 101 imports=['from webhelpers.html import escape'])
107 102
108 103 # sets the c attribute access when don't existing attribute are accessed
109 104 config['pylons.strict_tmpl_context'] = True
110 105
111 106 # configure channelstream
112 107 config['channelstream_config'] = {
113 108 'enabled': asbool(config.get('channelstream.enabled', False)),
114 109 'server': config.get('channelstream.server'),
115 110 'secret': config.get('channelstream.secret')
116 111 }
117 112
118 113 db_cfg = make_db_config(clear_session=True)
119 114
120 115 repos_path = list(db_cfg.items('paths'))[0][1]
121 116 config['base_path'] = repos_path
122 117
123 118 # store db config also in main global CONFIG
124 119 set_rhodecode_config(config)
125 120
126 121 # configure instance id
127 122 utils.set_instance_id(config)
128 123
129 124 # CONFIGURATION OPTIONS HERE (note: all config options will override
130 125 # any Pylons config options)
131 126
132 127 # store config reference into our module to skip import magic of pylons
133 128 rhodecode.CONFIG.update(config)
134 129
135 130 return config
136 131
137 132
138 133 def load_pyramid_environment(global_config, settings):
139 134 # Some parts of the code expect a merge of global and app settings.
140 135 settings_merged = global_config.copy()
141 136 settings_merged.update(settings)
142 137
143 138 # Store the settings to make them available to other modules.
144 139 rhodecode.PYRAMID_SETTINGS = settings_merged
145 140 # NOTE(marcink): needs to be enabled after full port to pyramid
146 141 # rhodecode.CONFIG = config
147 142
148 143 # If this is a test run we prepare the test environment like
149 144 # creating a test database, test search index and test repositories.
150 145 # This has to be done before the database connection is initialized.
151 146 if settings['is_test']:
152 147 rhodecode.is_test = True
153 148 rhodecode.disable_error_handler = True
154 149
155 150 utils.initialize_test_environment(settings_merged)
156 151
157 152 # Initialize the database connection.
158 153 utils.initialize_database(settings_merged)
159 154
160 155 # Limit backends to `vcs.backends` from configuration
161 156 for alias in rhodecode.BACKENDS.keys():
162 157 if alias not in settings['vcs.backends']:
163 158 del rhodecode.BACKENDS[alias]
164 159 log.info('Enabled VCS backends: %s', rhodecode.BACKENDS.keys())
165 160
166 161 # initialize vcs client and optionally run the server if enabled
167 162 vcs_server_uri = settings['vcs.server']
168 163 vcs_server_enabled = settings['vcs.server.enable']
169 164 start_server = (
170 165 settings['vcs.start_server'] and
171 166 not int(os.environ.get('RC_VCSSERVER_TEST_DISABLE', '0')))
172 167
173 168 if vcs_server_enabled and start_server:
174 169 log.info("Starting vcsserver")
175 170 start_vcs_server(server_and_port=vcs_server_uri,
176 171 protocol=utils.get_vcs_server_protocol(settings),
177 172 log_level=settings['vcs.server.log_level'])
178 173
179 174 utils.configure_vcs(settings)
180 175
181 176 if vcs_server_enabled:
182 177 connect_vcs(vcs_server_uri, utils.get_vcs_server_protocol(settings))
@@ -1,542 +1,543 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2010-2017 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 """
22 22 Pylons middleware initialization
23 23 """
24 24 import logging
25 25 import traceback
26 from collections import OrderedDict
26 import collections
27 27
28 28 from paste.registry import RegistryManager
29 29 from paste.gzipper import make_gzip_middleware
30 from pylons.wsgiapp import PylonsApp
31 30 from pyramid.authorization import ACLAuthorizationPolicy
32 31 from pyramid.config import Configurator
33 32 from pyramid.settings import asbool, aslist
34 33 from pyramid.wsgi import wsgiapp
35 34 from pyramid.httpexceptions import (
36 35 HTTPException, HTTPError, HTTPInternalServerError, HTTPFound)
37 36 from pyramid.events import ApplicationCreated
38 37 from pyramid.renderers import render_to_response
39 38 from routes.middleware import RoutesMiddleware
40 39 import rhodecode
41 40
42 41 from rhodecode.model import meta
43 42 from rhodecode.config import patches
44 43 from rhodecode.config import utils as config_utils
45 44 from rhodecode.config.routing import STATIC_FILE_PREFIX
46 45 from rhodecode.config.environment import (
47 46 load_environment, load_pyramid_environment)
48 47
49 48 from rhodecode.lib.vcs import VCSCommunicationError
50 49 from rhodecode.lib.exceptions import VCSServerUnavailable
51 50 from rhodecode.lib.middleware.appenlight import wrap_in_appenlight_if_enabled
52 51 from rhodecode.lib.middleware.error_handling import (
53 52 PylonsErrorHandlingMiddleware)
54 53 from rhodecode.lib.middleware.https_fixup import HttpsFixup
55 54 from rhodecode.lib.middleware.vcs import VCSMiddleware
56 55 from rhodecode.lib.plugins.utils import register_rhodecode_plugin
57 56 from rhodecode.lib.utils2 import aslist as rhodecode_aslist, AttributeDict
58 57 from rhodecode.subscribers import (
59 58 scan_repositories_if_enabled, write_js_routes_if_enabled,
60 59 write_metadata_if_needed, inject_app_settings)
61 60
62 61
63 62 log = logging.getLogger(__name__)
64 63
65 64
66 65 # this is used to avoid avoid the route lookup overhead in routesmiddleware
67 66 # for certain routes which won't go to pylons to - eg. static files, debugger
68 67 # it is only needed for the pylons migration and can be removed once complete
69 68 class SkippableRoutesMiddleware(RoutesMiddleware):
70 69 """ Routes middleware that allows you to skip prefixes """
71 70
72 71 def __init__(self, *args, **kw):
73 72 self.skip_prefixes = kw.pop('skip_prefixes', [])
74 73 super(SkippableRoutesMiddleware, self).__init__(*args, **kw)
75 74
76 75 def __call__(self, environ, start_response):
77 76 for prefix in self.skip_prefixes:
78 77 if environ['PATH_INFO'].startswith(prefix):
79 78 # added to avoid the case when a missing /_static route falls
80 79 # through to pylons and causes an exception as pylons is
81 80 # expecting wsgiorg.routingargs to be set in the environ
82 81 # by RoutesMiddleware.
83 82 if 'wsgiorg.routing_args' not in environ:
84 83 environ['wsgiorg.routing_args'] = (None, {})
85 84 return self.app(environ, start_response)
86 85
87 86 return super(SkippableRoutesMiddleware, self).__call__(
88 87 environ, start_response)
89 88
90 89
91 90 def make_app(global_conf, static_files=True, **app_conf):
92 91 """Create a Pylons WSGI application and return it
93 92
94 93 ``global_conf``
95 94 The inherited configuration for this application. Normally from
96 95 the [DEFAULT] section of the Paste ini file.
97 96
98 97 ``app_conf``
99 98 The application's local configuration. Normally specified in
100 99 the [app:<name>] section of the Paste ini file (where <name>
101 100 defaults to main).
102 101
103 102 """
103 from pylons.wsgiapp import PylonsApp
104
104 105 # Apply compatibility patches
105 106 patches.kombu_1_5_1_python_2_7_11()
106 107 patches.inspect_getargspec()
107 108
108 109 # Configure the Pylons environment
109 110 config = load_environment(global_conf, app_conf)
110 111
111 112 # The Pylons WSGI app
112 113 app = PylonsApp(config=config)
113 114
114 115 # Establish the Registry for this application
115 116 app = RegistryManager(app)
116 117
117 118 app.config = config
118 119
119 120 return app
120 121
121 122
122 123 def make_pyramid_app(global_config, **settings):
123 124 """
124 125 Constructs the WSGI application based on Pyramid and wraps the Pylons based
125 126 application.
126 127
127 128 Specials:
128 129
129 130 * We migrate from Pylons to Pyramid. While doing this, we keep both
130 131 frameworks functional. This involves moving some WSGI middlewares around
131 132 and providing access to some data internals, so that the old code is
132 133 still functional.
133 134
134 135 * The application can also be integrated like a plugin via the call to
135 136 `includeme`. This is accompanied with the other utility functions which
136 137 are called. Changing this should be done with great care to not break
137 138 cases when these fragments are assembled from another place.
138 139
139 140 """
140 141 sanitize_settings_and_apply_defaults(settings)
141 142
142 143 config = Configurator(settings=settings)
143 144 load_pyramid_environment(global_config, settings)
144 145
145 146 add_pylons_compat_data(config.registry, global_config, settings.copy())
146 147
147 148 includeme_first(config)
148 149 includeme(config)
149 150
150 151 pyramid_app = config.make_wsgi_app()
151 152 pyramid_app = wrap_app_in_wsgi_middlewares(pyramid_app, config)
152 153 pyramid_app.config = config
153 154
154 155 # creating the app uses a connection - return it after we are done
155 156 meta.Session.remove()
156 157
157 158 return pyramid_app
158 159
159 160
160 161 def make_not_found_view(config):
161 162 """
162 163 This creates the view which should be registered as not-found-view to
163 164 pyramid. Basically it contains of the old pylons app, converted to a view.
164 165 Additionally it is wrapped by some other middlewares.
165 166 """
166 167 settings = config.registry.settings
167 168 vcs_server_enabled = settings['vcs.server.enable']
168 169
169 170 # Make pylons app from unprepared settings.
170 171 pylons_app = make_app(
171 172 config.registry._pylons_compat_global_config,
172 173 **config.registry._pylons_compat_settings)
173 174 config.registry._pylons_compat_config = pylons_app.config
174 175
175 176 # Appenlight monitoring.
176 177 pylons_app, appenlight_client = wrap_in_appenlight_if_enabled(
177 178 pylons_app, settings)
178 179
179 180 # The pylons app is executed inside of the pyramid 404 exception handler.
180 181 # Exceptions which are raised inside of it are not handled by pyramid
181 182 # again. Therefore we add a middleware that invokes the error handler in
182 183 # case of an exception or error response. This way we return proper error
183 184 # HTML pages in case of an error.
184 185 reraise = (settings.get('debugtoolbar.enabled', False) or
185 186 rhodecode.disable_error_handler)
186 187 pylons_app = PylonsErrorHandlingMiddleware(
187 188 pylons_app, error_handler, reraise)
188 189
189 190 # The VCSMiddleware shall operate like a fallback if pyramid doesn't find a
190 191 # view to handle the request. Therefore it is wrapped around the pylons
191 192 # app. It has to be outside of the error handling otherwise error responses
192 193 # from the vcsserver are converted to HTML error pages. This confuses the
193 194 # command line tools and the user won't get a meaningful error message.
194 195 if vcs_server_enabled:
195 196 pylons_app = VCSMiddleware(
196 197 pylons_app, settings, appenlight_client, registry=config.registry)
197 198
198 199 # Convert WSGI app to pyramid view and return it.
199 200 return wsgiapp(pylons_app)
200 201
201 202
202 203 def add_pylons_compat_data(registry, global_config, settings):
203 204 """
204 205 Attach data to the registry to support the Pylons integration.
205 206 """
206 207 registry._pylons_compat_global_config = global_config
207 208 registry._pylons_compat_settings = settings
208 209
209 210
210 211 def error_handler(exception, request):
211 212 import rhodecode
212 213 from rhodecode.lib import helpers
213 214
214 215 rhodecode_title = rhodecode.CONFIG.get('rhodecode_title') or 'RhodeCode'
215 216
216 217 base_response = HTTPInternalServerError()
217 218 # prefer original exception for the response since it may have headers set
218 219 if isinstance(exception, HTTPException):
219 220 base_response = exception
220 221 elif isinstance(exception, VCSCommunicationError):
221 222 base_response = VCSServerUnavailable()
222 223
223 224 def is_http_error(response):
224 225 # error which should have traceback
225 226 return response.status_code > 499
226 227
227 228 if is_http_error(base_response):
228 229 log.exception(
229 230 'error occurred handling this request for path: %s', request.path)
230 231
231 232 error_explanation = base_response.explanation or str(base_response)
232 233 if base_response.status_code == 404:
233 234 error_explanation += " Or you don't have permission to access it."
234 235 c = AttributeDict()
235 236 c.error_message = base_response.status
236 237 c.error_explanation = error_explanation
237 238 c.visual = AttributeDict()
238 239
239 240 c.visual.rhodecode_support_url = (
240 241 request.registry.settings.get('rhodecode_support_url') or
241 242 request.route_url('rhodecode_support')
242 243 )
243 244 c.redirect_time = 0
244 245 c.rhodecode_name = rhodecode_title
245 246 if not c.rhodecode_name:
246 247 c.rhodecode_name = 'Rhodecode'
247 248
248 249 c.causes = []
249 250 if is_http_error(base_response):
250 251 c.causes.append('Server is overloaded.')
251 252 c.causes.append('Server database connection is lost.')
252 253 c.causes.append('Server expected unhandled error.')
253 254
254 255 if hasattr(base_response, 'causes'):
255 256 c.causes = base_response.causes
256 257
257 258 c.messages = helpers.flash.pop_messages(request=request)
258 259 c.traceback = traceback.format_exc()
259 260 response = render_to_response(
260 261 '/errors/error_document.mako', {'c': c, 'h': helpers}, request=request,
261 262 response=base_response)
262 263
263 264 return response
264 265
265 266
266 267 def includeme(config):
267 268 settings = config.registry.settings
268 269
269 270 # plugin information
270 config.registry.rhodecode_plugins = OrderedDict()
271 config.registry.rhodecode_plugins = collections.OrderedDict()
271 272
272 273 config.add_directive(
273 274 'register_rhodecode_plugin', register_rhodecode_plugin)
274 275
275 276 if asbool(settings.get('appenlight', 'false')):
276 277 config.include('appenlight_client.ext.pyramid_tween')
277 278
278 279 # Includes which are required. The application would fail without them.
279 280 config.include('pyramid_mako')
280 281 config.include('pyramid_beaker')
281 282
282 283 config.include('rhodecode.authentication')
283 284 config.include('rhodecode.integrations')
284 285
285 286 # apps
286 287 config.include('rhodecode.apps._base')
287 288 config.include('rhodecode.apps.ops')
288 289
289 290 config.include('rhodecode.apps.admin')
290 291 config.include('rhodecode.apps.channelstream')
291 292 config.include('rhodecode.apps.login')
292 293 config.include('rhodecode.apps.home')
293 294 config.include('rhodecode.apps.journal')
294 295 config.include('rhodecode.apps.repository')
295 296 config.include('rhodecode.apps.repo_group')
296 297 config.include('rhodecode.apps.user_group')
297 298 config.include('rhodecode.apps.search')
298 299 config.include('rhodecode.apps.user_profile')
299 300 config.include('rhodecode.apps.my_account')
300 301 config.include('rhodecode.apps.svn_support')
301 302 config.include('rhodecode.apps.ssh_support')
302 303 config.include('rhodecode.apps.gist')
303 304
304 305 config.include('rhodecode.apps.debug_style')
305 306 config.include('rhodecode.tweens')
306 307 config.include('rhodecode.api')
307 308
308 309 config.add_route(
309 310 'rhodecode_support', 'https://rhodecode.com/help/', static=True)
310 311
311 312 config.add_translation_dirs('rhodecode:i18n/')
312 313 settings['default_locale_name'] = settings.get('lang', 'en')
313 314
314 315 # Add subscribers.
315 316 config.add_subscriber(inject_app_settings, ApplicationCreated)
316 317 config.add_subscriber(scan_repositories_if_enabled, ApplicationCreated)
317 318 config.add_subscriber(write_metadata_if_needed, ApplicationCreated)
318 319 config.add_subscriber(write_js_routes_if_enabled, ApplicationCreated)
319 320
320 321 config.add_request_method(
321 322 'rhodecode.lib.partial_renderer.get_partial_renderer',
322 323 'get_partial_renderer')
323 324
324 325 # events
325 326 # TODO(marcink): this should be done when pyramid migration is finished
326 327 # config.add_subscriber(
327 328 # 'rhodecode.integrations.integrations_event_handler',
328 329 # 'rhodecode.events.RhodecodeEvent')
329 330
330 331 # Set the authorization policy.
331 332 authz_policy = ACLAuthorizationPolicy()
332 333 config.set_authorization_policy(authz_policy)
333 334
334 335 # Set the default renderer for HTML templates to mako.
335 336 config.add_mako_renderer('.html')
336 337
337 338 config.add_renderer(
338 339 name='json_ext',
339 340 factory='rhodecode.lib.ext_json_renderer.pyramid_ext_json')
340 341
341 342 # include RhodeCode plugins
342 343 includes = aslist(settings.get('rhodecode.includes', []))
343 344 for inc in includes:
344 345 config.include(inc)
345 346
346 347 # This is the glue which allows us to migrate in chunks. By registering the
347 348 # pylons based application as the "Not Found" view in Pyramid, we will
348 349 # fallback to the old application each time the new one does not yet know
349 350 # how to handle a request.
350 351 config.add_notfound_view(make_not_found_view(config))
351 352
352 353 if not settings.get('debugtoolbar.enabled', False):
353 354 # disabled debugtoolbar handle all exceptions via the error_handlers
354 355 config.add_view(error_handler, context=Exception)
355 356
356 357 config.add_view(error_handler, context=HTTPError)
357 358
358 359
359 360 def includeme_first(config):
360 361 # redirect automatic browser favicon.ico requests to correct place
361 362 def favicon_redirect(context, request):
362 363 return HTTPFound(
363 364 request.static_path('rhodecode:public/images/favicon.ico'))
364 365
365 366 config.add_view(favicon_redirect, route_name='favicon')
366 367 config.add_route('favicon', '/favicon.ico')
367 368
368 369 def robots_redirect(context, request):
369 370 return HTTPFound(
370 371 request.static_path('rhodecode:public/robots.txt'))
371 372
372 373 config.add_view(robots_redirect, route_name='robots')
373 374 config.add_route('robots', '/robots.txt')
374 375
375 376 config.add_static_view(
376 377 '_static/deform', 'deform:static')
377 378 config.add_static_view(
378 379 '_static/rhodecode', path='rhodecode:public', cache_max_age=3600 * 24)
379 380
380 381
381 382 def wrap_app_in_wsgi_middlewares(pyramid_app, config):
382 383 """
383 384 Apply outer WSGI middlewares around the application.
384 385
385 386 Part of this has been moved up from the Pylons layer, so that the
386 387 data is also available if old Pylons code is hit through an already ported
387 388 view.
388 389 """
389 390 settings = config.registry.settings
390 391
391 392 # enable https redirects based on HTTP_X_URL_SCHEME set by proxy
392 393 pyramid_app = HttpsFixup(pyramid_app, settings)
393 394
394 395 # Add RoutesMiddleware to support the pylons compatibility tween during
395 396 # migration to pyramid.
396 397
397 398 # TODO(marcink): remove after migration to pyramid
398 399 if hasattr(config.registry, '_pylons_compat_config'):
399 400 routes_map = config.registry._pylons_compat_config['routes.map']
400 401 pyramid_app = SkippableRoutesMiddleware(
401 402 pyramid_app, routes_map,
402 403 skip_prefixes=(STATIC_FILE_PREFIX, '/_debug_toolbar'))
403 404
404 405 pyramid_app, _ = wrap_in_appenlight_if_enabled(pyramid_app, settings)
405 406
406 407 if settings['gzip_responses']:
407 408 pyramid_app = make_gzip_middleware(
408 409 pyramid_app, settings, compress_level=1)
409 410
410 411 # this should be the outer most middleware in the wsgi stack since
411 412 # middleware like Routes make database calls
412 413 def pyramid_app_with_cleanup(environ, start_response):
413 414 try:
414 415 return pyramid_app(environ, start_response)
415 416 finally:
416 417 # Dispose current database session and rollback uncommitted
417 418 # transactions.
418 419 meta.Session.remove()
419 420
420 421 # In a single threaded mode server, on non sqlite db we should have
421 422 # '0 Current Checked out connections' at the end of a request,
422 423 # if not, then something, somewhere is leaving a connection open
423 424 pool = meta.Base.metadata.bind.engine.pool
424 425 log.debug('sa pool status: %s', pool.status())
425 426
426 427 return pyramid_app_with_cleanup
427 428
428 429
429 430 def sanitize_settings_and_apply_defaults(settings):
430 431 """
431 432 Applies settings defaults and does all type conversion.
432 433
433 434 We would move all settings parsing and preparation into this place, so that
434 435 we have only one place left which deals with this part. The remaining parts
435 436 of the application would start to rely fully on well prepared settings.
436 437
437 438 This piece would later be split up per topic to avoid a big fat monster
438 439 function.
439 440 """
440 441
441 442 settings.setdefault('rhodecode.edition', 'Community Edition')
442 443
443 444 if 'mako.default_filters' not in settings:
444 445 # set custom default filters if we don't have it defined
445 446 settings['mako.imports'] = 'from rhodecode.lib.base import h_filter'
446 447 settings['mako.default_filters'] = 'h_filter'
447 448
448 449 if 'mako.directories' not in settings:
449 450 mako_directories = settings.setdefault('mako.directories', [
450 451 # Base templates of the original application
451 452 'rhodecode:templates',
452 453 ])
453 454 log.debug(
454 455 "Using the following Mako template directories: %s",
455 456 mako_directories)
456 457
457 458 # Default includes, possible to change as a user
458 459 pyramid_includes = settings.setdefault('pyramid.includes', [
459 460 'rhodecode.lib.middleware.request_wrapper',
460 461 ])
461 462 log.debug(
462 463 "Using the following pyramid.includes: %s",
463 464 pyramid_includes)
464 465
465 466 # TODO: johbo: Re-think this, usually the call to config.include
466 467 # should allow to pass in a prefix.
467 468 settings.setdefault('rhodecode.api.url', '/_admin/api')
468 469
469 470 # Sanitize generic settings.
470 471 _list_setting(settings, 'default_encoding', 'UTF-8')
471 472 _bool_setting(settings, 'is_test', 'false')
472 473 _bool_setting(settings, 'gzip_responses', 'false')
473 474
474 475 # Call split out functions that sanitize settings for each topic.
475 476 _sanitize_appenlight_settings(settings)
476 477 _sanitize_vcs_settings(settings)
477 478
478 479 # configure instance id
479 480 config_utils.set_instance_id(settings)
480 481
481 482 return settings
482 483
483 484
484 485 def _sanitize_appenlight_settings(settings):
485 486 _bool_setting(settings, 'appenlight', 'false')
486 487
487 488
488 489 def _sanitize_vcs_settings(settings):
489 490 """
490 491 Applies settings defaults and does type conversion for all VCS related
491 492 settings.
492 493 """
493 494 _string_setting(settings, 'vcs.svn.compatible_version', '')
494 495 _string_setting(settings, 'git_rev_filter', '--all')
495 496 _string_setting(settings, 'vcs.hooks.protocol', 'http')
496 497 _string_setting(settings, 'vcs.scm_app_implementation', 'http')
497 498 _string_setting(settings, 'vcs.server', '')
498 499 _string_setting(settings, 'vcs.server.log_level', 'debug')
499 500 _string_setting(settings, 'vcs.server.protocol', 'http')
500 501 _bool_setting(settings, 'startup.import_repos', 'false')
501 502 _bool_setting(settings, 'vcs.hooks.direct_calls', 'false')
502 503 _bool_setting(settings, 'vcs.server.enable', 'true')
503 504 _bool_setting(settings, 'vcs.start_server', 'false')
504 505 _list_setting(settings, 'vcs.backends', 'hg, git, svn')
505 506 _int_setting(settings, 'vcs.connection_timeout', 3600)
506 507
507 508 # Support legacy values of vcs.scm_app_implementation. Legacy
508 509 # configurations may use 'rhodecode.lib.middleware.utils.scm_app_http'
509 510 # which is now mapped to 'http'.
510 511 scm_app_impl = settings['vcs.scm_app_implementation']
511 512 if scm_app_impl == 'rhodecode.lib.middleware.utils.scm_app_http':
512 513 settings['vcs.scm_app_implementation'] = 'http'
513 514
514 515
515 516 def _int_setting(settings, name, default):
516 517 settings[name] = int(settings.get(name, default))
517 518
518 519
519 520 def _bool_setting(settings, name, default):
520 521 input = settings.get(name, default)
521 522 if isinstance(input, unicode):
522 523 input = input.encode('utf8')
523 524 settings[name] = asbool(input)
524 525
525 526
526 527 def _list_setting(settings, name, default):
527 528 raw_value = settings.get(name, default)
528 529
529 530 old_separator = ','
530 531 if old_separator in raw_value:
531 532 # If we get a comma separated list, pass it to our own function.
532 533 settings[name] = rhodecode_aslist(raw_value, sep=old_separator)
533 534 else:
534 535 # Otherwise we assume it uses pyramids space/newline separation.
535 536 settings[name] = aslist(raw_value)
536 537
537 538
538 539 def _string_setting(settings, name, default, lower=True):
539 540 value = settings.get(name, default)
540 541 if lower:
541 542 value = value.lower()
542 543 settings[name] = value
General Comments 0
You need to be logged in to leave comments. Login now