##// END OF EJS Templates
configuration: expose the config file location to the pyramid settings.
marcink -
r3431:85be2e5f default
parent child Browse files
Show More
@@ -1,738 +1,739 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2019 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import os
21 import os
22 import sys
22 import sys
23 import logging
23 import logging
24 import collections
24 import collections
25 import tempfile
25 import tempfile
26 import time
26 import time
27
27
28 from paste.gzipper import make_gzip_middleware
28 from paste.gzipper import make_gzip_middleware
29 import pyramid.events
29 import pyramid.events
30 from pyramid.wsgi import wsgiapp
30 from pyramid.wsgi import wsgiapp
31 from pyramid.authorization import ACLAuthorizationPolicy
31 from pyramid.authorization import ACLAuthorizationPolicy
32 from pyramid.config import Configurator
32 from pyramid.config import Configurator
33 from pyramid.settings import asbool, aslist
33 from pyramid.settings import asbool, aslist
34 from pyramid.httpexceptions import (
34 from pyramid.httpexceptions import (
35 HTTPException, HTTPError, HTTPInternalServerError, HTTPFound, HTTPNotFound)
35 HTTPException, HTTPError, HTTPInternalServerError, HTTPFound, HTTPNotFound)
36 from pyramid.renderers import render_to_response
36 from pyramid.renderers import render_to_response
37
37
38 from rhodecode.model import meta
38 from rhodecode.model import meta
39 from rhodecode.config import patches
39 from rhodecode.config import patches
40 from rhodecode.config import utils as config_utils
40 from rhodecode.config import utils as config_utils
41 from rhodecode.config.environment import load_pyramid_environment
41 from rhodecode.config.environment import load_pyramid_environment
42
42
43 import rhodecode.events
43 import rhodecode.events
44 from rhodecode.lib.middleware.vcs import VCSMiddleware
44 from rhodecode.lib.middleware.vcs import VCSMiddleware
45 from rhodecode.lib.request import Request
45 from rhodecode.lib.request import Request
46 from rhodecode.lib.vcs import VCSCommunicationError
46 from rhodecode.lib.vcs import VCSCommunicationError
47 from rhodecode.lib.exceptions import VCSServerUnavailable
47 from rhodecode.lib.exceptions import VCSServerUnavailable
48 from rhodecode.lib.middleware.appenlight import wrap_in_appenlight_if_enabled
48 from rhodecode.lib.middleware.appenlight import wrap_in_appenlight_if_enabled
49 from rhodecode.lib.middleware.https_fixup import HttpsFixup
49 from rhodecode.lib.middleware.https_fixup import HttpsFixup
50 from rhodecode.lib.celerylib.loader import configure_celery
50 from rhodecode.lib.celerylib.loader import configure_celery
51 from rhodecode.lib.plugins.utils import register_rhodecode_plugin
51 from rhodecode.lib.plugins.utils import register_rhodecode_plugin
52 from rhodecode.lib.utils2 import aslist as rhodecode_aslist, AttributeDict
52 from rhodecode.lib.utils2 import aslist as rhodecode_aslist, AttributeDict
53 from rhodecode.lib.exc_tracking import store_exception
53 from rhodecode.lib.exc_tracking import store_exception
54 from rhodecode.subscribers import (
54 from rhodecode.subscribers import (
55 scan_repositories_if_enabled, write_js_routes_if_enabled,
55 scan_repositories_if_enabled, write_js_routes_if_enabled,
56 write_metadata_if_needed, inject_app_settings)
56 write_metadata_if_needed, inject_app_settings)
57
57
58
58
59 log = logging.getLogger(__name__)
59 log = logging.getLogger(__name__)
60
60
61
61
62 def is_http_error(response):
62 def is_http_error(response):
63 # error which should have traceback
63 # error which should have traceback
64 return response.status_code > 499
64 return response.status_code > 499
65
65
66
66
67 def should_load_all():
67 def should_load_all():
68 """
68 """
69 Returns if all application components should be loaded. In some cases it's
69 Returns if all application components should be loaded. In some cases it's
70 desired to skip apps loading for faster shell script execution
70 desired to skip apps loading for faster shell script execution
71 """
71 """
72 return True
72 return True
73
73
74
74
75 def make_pyramid_app(global_config, **settings):
75 def make_pyramid_app(global_config, **settings):
76 """
76 """
77 Constructs the WSGI application based on Pyramid.
77 Constructs the WSGI application based on Pyramid.
78
78
79 Specials:
79 Specials:
80
80
81 * The application can also be integrated like a plugin via the call to
81 * The application can also be integrated like a plugin via the call to
82 `includeme`. This is accompanied with the other utility functions which
82 `includeme`. This is accompanied with the other utility functions which
83 are called. Changing this should be done with great care to not break
83 are called. Changing this should be done with great care to not break
84 cases when these fragments are assembled from another place.
84 cases when these fragments are assembled from another place.
85
85
86 """
86 """
87
87
88 # Allows to use format style "{ENV_NAME}" placeholders in the configuration. It
88 # Allows to use format style "{ENV_NAME}" placeholders in the configuration. It
89 # will be replaced by the value of the environment variable "NAME" in this case.
89 # will be replaced by the value of the environment variable "NAME" in this case.
90 start_time = time.time()
90 start_time = time.time()
91
91
92 debug = asbool(global_config.get('debug'))
92 debug = asbool(global_config.get('debug'))
93 if debug:
93 if debug:
94 enable_debug()
94 enable_debug()
95
95
96 environ = {'ENV_{}'.format(key): value for key, value in os.environ.items()}
96 environ = {'ENV_{}'.format(key): value for key, value in os.environ.items()}
97
97
98 global_config = _substitute_values(global_config, environ)
98 global_config = _substitute_values(global_config, environ)
99 settings = _substitute_values(settings, environ)
99 settings = _substitute_values(settings, environ)
100
100
101 sanitize_settings_and_apply_defaults(settings)
101 sanitize_settings_and_apply_defaults(global_config, settings)
102
102
103 config = Configurator(settings=settings)
103 config = Configurator(settings=settings)
104
104
105 # Apply compatibility patches
105 # Apply compatibility patches
106 patches.inspect_getargspec()
106 patches.inspect_getargspec()
107
107
108 load_pyramid_environment(global_config, settings)
108 load_pyramid_environment(global_config, settings)
109
109
110 # Static file view comes first
110 # Static file view comes first
111 includeme_first(config)
111 includeme_first(config)
112
112
113 includeme(config)
113 includeme(config)
114
114
115 pyramid_app = config.make_wsgi_app()
115 pyramid_app = config.make_wsgi_app()
116 pyramid_app = wrap_app_in_wsgi_middlewares(pyramid_app, config)
116 pyramid_app = wrap_app_in_wsgi_middlewares(pyramid_app, config)
117 pyramid_app.config = config
117 pyramid_app.config = config
118
118
119 config.configure_celery(global_config['__file__'])
119 config.configure_celery(global_config['__file__'])
120 # creating the app uses a connection - return it after we are done
120 # creating the app uses a connection - return it after we are done
121 meta.Session.remove()
121 meta.Session.remove()
122 total_time = time.time() - start_time
122 total_time = time.time() - start_time
123 log.info('Pyramid app `%s` created and configured in %.2fs',
123 log.info('Pyramid app `%s` created and configured in %.2fs',
124 pyramid_app.func_name, total_time)
124 pyramid_app.func_name, total_time)
125
125
126 return pyramid_app
126 return pyramid_app
127
127
128
128
129 def not_found_view(request):
129 def not_found_view(request):
130 """
130 """
131 This creates the view which should be registered as not-found-view to
131 This creates the view which should be registered as not-found-view to
132 pyramid.
132 pyramid.
133 """
133 """
134
134
135 if not getattr(request, 'vcs_call', None):
135 if not getattr(request, 'vcs_call', None):
136 # handle like regular case with our error_handler
136 # handle like regular case with our error_handler
137 return error_handler(HTTPNotFound(), request)
137 return error_handler(HTTPNotFound(), request)
138
138
139 # handle not found view as a vcs call
139 # handle not found view as a vcs call
140 settings = request.registry.settings
140 settings = request.registry.settings
141 ae_client = getattr(request, 'ae_client', None)
141 ae_client = getattr(request, 'ae_client', None)
142 vcs_app = VCSMiddleware(
142 vcs_app = VCSMiddleware(
143 HTTPNotFound(), request.registry, settings,
143 HTTPNotFound(), request.registry, settings,
144 appenlight_client=ae_client)
144 appenlight_client=ae_client)
145
145
146 return wsgiapp(vcs_app)(None, request)
146 return wsgiapp(vcs_app)(None, request)
147
147
148
148
149 def error_handler(exception, request):
149 def error_handler(exception, request):
150 import rhodecode
150 import rhodecode
151 from rhodecode.lib import helpers
151 from rhodecode.lib import helpers
152
152
153 rhodecode_title = rhodecode.CONFIG.get('rhodecode_title') or 'RhodeCode'
153 rhodecode_title = rhodecode.CONFIG.get('rhodecode_title') or 'RhodeCode'
154
154
155 base_response = HTTPInternalServerError()
155 base_response = HTTPInternalServerError()
156 # prefer original exception for the response since it may have headers set
156 # prefer original exception for the response since it may have headers set
157 if isinstance(exception, HTTPException):
157 if isinstance(exception, HTTPException):
158 base_response = exception
158 base_response = exception
159 elif isinstance(exception, VCSCommunicationError):
159 elif isinstance(exception, VCSCommunicationError):
160 base_response = VCSServerUnavailable()
160 base_response = VCSServerUnavailable()
161
161
162 if is_http_error(base_response):
162 if is_http_error(base_response):
163 log.exception(
163 log.exception(
164 'error occurred handling this request for path: %s', request.path)
164 'error occurred handling this request for path: %s', request.path)
165
165
166 error_explanation = base_response.explanation or str(base_response)
166 error_explanation = base_response.explanation or str(base_response)
167 if base_response.status_code == 404:
167 if base_response.status_code == 404:
168 error_explanation += " Optionally you don't have permission to access this page."
168 error_explanation += " Optionally you don't have permission to access this page."
169 c = AttributeDict()
169 c = AttributeDict()
170 c.error_message = base_response.status
170 c.error_message = base_response.status
171 c.error_explanation = error_explanation
171 c.error_explanation = error_explanation
172 c.visual = AttributeDict()
172 c.visual = AttributeDict()
173
173
174 c.visual.rhodecode_support_url = (
174 c.visual.rhodecode_support_url = (
175 request.registry.settings.get('rhodecode_support_url') or
175 request.registry.settings.get('rhodecode_support_url') or
176 request.route_url('rhodecode_support')
176 request.route_url('rhodecode_support')
177 )
177 )
178 c.redirect_time = 0
178 c.redirect_time = 0
179 c.rhodecode_name = rhodecode_title
179 c.rhodecode_name = rhodecode_title
180 if not c.rhodecode_name:
180 if not c.rhodecode_name:
181 c.rhodecode_name = 'Rhodecode'
181 c.rhodecode_name = 'Rhodecode'
182
182
183 c.causes = []
183 c.causes = []
184 if is_http_error(base_response):
184 if is_http_error(base_response):
185 c.causes.append('Server is overloaded.')
185 c.causes.append('Server is overloaded.')
186 c.causes.append('Server database connection is lost.')
186 c.causes.append('Server database connection is lost.')
187 c.causes.append('Server expected unhandled error.')
187 c.causes.append('Server expected unhandled error.')
188
188
189 if hasattr(base_response, 'causes'):
189 if hasattr(base_response, 'causes'):
190 c.causes = base_response.causes
190 c.causes = base_response.causes
191
191
192 c.messages = helpers.flash.pop_messages(request=request)
192 c.messages = helpers.flash.pop_messages(request=request)
193
193
194 exc_info = sys.exc_info()
194 exc_info = sys.exc_info()
195 c.exception_id = id(exc_info)
195 c.exception_id = id(exc_info)
196 c.show_exception_id = isinstance(base_response, VCSServerUnavailable) \
196 c.show_exception_id = isinstance(base_response, VCSServerUnavailable) \
197 or base_response.status_code > 499
197 or base_response.status_code > 499
198 c.exception_id_url = request.route_url(
198 c.exception_id_url = request.route_url(
199 'admin_settings_exception_tracker_show', exception_id=c.exception_id)
199 'admin_settings_exception_tracker_show', exception_id=c.exception_id)
200
200
201 if c.show_exception_id:
201 if c.show_exception_id:
202 store_exception(c.exception_id, exc_info)
202 store_exception(c.exception_id, exc_info)
203
203
204 response = render_to_response(
204 response = render_to_response(
205 '/errors/error_document.mako', {'c': c, 'h': helpers}, request=request,
205 '/errors/error_document.mako', {'c': c, 'h': helpers}, request=request,
206 response=base_response)
206 response=base_response)
207
207
208 return response
208 return response
209
209
210
210
211 def includeme_first(config):
211 def includeme_first(config):
212 # redirect automatic browser favicon.ico requests to correct place
212 # redirect automatic browser favicon.ico requests to correct place
213 def favicon_redirect(context, request):
213 def favicon_redirect(context, request):
214 return HTTPFound(
214 return HTTPFound(
215 request.static_path('rhodecode:public/images/favicon.ico'))
215 request.static_path('rhodecode:public/images/favicon.ico'))
216
216
217 config.add_view(favicon_redirect, route_name='favicon')
217 config.add_view(favicon_redirect, route_name='favicon')
218 config.add_route('favicon', '/favicon.ico')
218 config.add_route('favicon', '/favicon.ico')
219
219
220 def robots_redirect(context, request):
220 def robots_redirect(context, request):
221 return HTTPFound(
221 return HTTPFound(
222 request.static_path('rhodecode:public/robots.txt'))
222 request.static_path('rhodecode:public/robots.txt'))
223
223
224 config.add_view(robots_redirect, route_name='robots')
224 config.add_view(robots_redirect, route_name='robots')
225 config.add_route('robots', '/robots.txt')
225 config.add_route('robots', '/robots.txt')
226
226
227 config.add_static_view(
227 config.add_static_view(
228 '_static/deform', 'deform:static')
228 '_static/deform', 'deform:static')
229 config.add_static_view(
229 config.add_static_view(
230 '_static/rhodecode', path='rhodecode:public', cache_max_age=3600 * 24)
230 '_static/rhodecode', path='rhodecode:public', cache_max_age=3600 * 24)
231
231
232
232
233 def includeme(config):
233 def includeme(config):
234 log.debug('Initializing main includeme from %s', os.path.basename(__file__))
234 log.debug('Initializing main includeme from %s', os.path.basename(__file__))
235 settings = config.registry.settings
235 settings = config.registry.settings
236 config.set_request_factory(Request)
236 config.set_request_factory(Request)
237
237
238 # plugin information
238 # plugin information
239 config.registry.rhodecode_plugins = collections.OrderedDict()
239 config.registry.rhodecode_plugins = collections.OrderedDict()
240
240
241 config.add_directive(
241 config.add_directive(
242 'register_rhodecode_plugin', register_rhodecode_plugin)
242 'register_rhodecode_plugin', register_rhodecode_plugin)
243
243
244 config.add_directive('configure_celery', configure_celery)
244 config.add_directive('configure_celery', configure_celery)
245
245
246 if asbool(settings.get('appenlight', 'false')):
246 if asbool(settings.get('appenlight', 'false')):
247 config.include('appenlight_client.ext.pyramid_tween')
247 config.include('appenlight_client.ext.pyramid_tween')
248
248
249 load_all = should_load_all()
249 load_all = should_load_all()
250
250
251 # Includes which are required. The application would fail without them.
251 # Includes which are required. The application would fail without them.
252 config.include('pyramid_mako')
252 config.include('pyramid_mako')
253 config.include('pyramid_beaker')
253 config.include('pyramid_beaker')
254 config.include('rhodecode.lib.rc_cache')
254 config.include('rhodecode.lib.rc_cache')
255
255
256 config.include('rhodecode.apps._base.navigation')
256 config.include('rhodecode.apps._base.navigation')
257 config.include('rhodecode.apps._base.subscribers')
257 config.include('rhodecode.apps._base.subscribers')
258 config.include('rhodecode.tweens')
258 config.include('rhodecode.tweens')
259
259
260 config.include('rhodecode.integrations')
260 config.include('rhodecode.integrations')
261 config.include('rhodecode.authentication')
261 config.include('rhodecode.authentication')
262
262
263 if load_all:
263 if load_all:
264 from rhodecode.authentication import discover_legacy_plugins
264 from rhodecode.authentication import discover_legacy_plugins
265 # load CE authentication plugins
265 # load CE authentication plugins
266 config.include('rhodecode.authentication.plugins.auth_crowd')
266 config.include('rhodecode.authentication.plugins.auth_crowd')
267 config.include('rhodecode.authentication.plugins.auth_headers')
267 config.include('rhodecode.authentication.plugins.auth_headers')
268 config.include('rhodecode.authentication.plugins.auth_jasig_cas')
268 config.include('rhodecode.authentication.plugins.auth_jasig_cas')
269 config.include('rhodecode.authentication.plugins.auth_ldap')
269 config.include('rhodecode.authentication.plugins.auth_ldap')
270 config.include('rhodecode.authentication.plugins.auth_pam')
270 config.include('rhodecode.authentication.plugins.auth_pam')
271 config.include('rhodecode.authentication.plugins.auth_rhodecode')
271 config.include('rhodecode.authentication.plugins.auth_rhodecode')
272 config.include('rhodecode.authentication.plugins.auth_token')
272 config.include('rhodecode.authentication.plugins.auth_token')
273
273
274 # Auto discover authentication plugins and include their configuration.
274 # Auto discover authentication plugins and include their configuration.
275 discover_legacy_plugins(config)
275 discover_legacy_plugins(config)
276
276
277 # apps
277 # apps
278 config.include('rhodecode.apps._base')
278 config.include('rhodecode.apps._base')
279
279
280 if load_all:
280 if load_all:
281 config.include('rhodecode.apps.ops')
281 config.include('rhodecode.apps.ops')
282 config.include('rhodecode.apps.admin')
282 config.include('rhodecode.apps.admin')
283 config.include('rhodecode.apps.channelstream')
283 config.include('rhodecode.apps.channelstream')
284 config.include('rhodecode.apps.login')
284 config.include('rhodecode.apps.login')
285 config.include('rhodecode.apps.home')
285 config.include('rhodecode.apps.home')
286 config.include('rhodecode.apps.journal')
286 config.include('rhodecode.apps.journal')
287 config.include('rhodecode.apps.repository')
287 config.include('rhodecode.apps.repository')
288 config.include('rhodecode.apps.repo_group')
288 config.include('rhodecode.apps.repo_group')
289 config.include('rhodecode.apps.user_group')
289 config.include('rhodecode.apps.user_group')
290 config.include('rhodecode.apps.search')
290 config.include('rhodecode.apps.search')
291 config.include('rhodecode.apps.user_profile')
291 config.include('rhodecode.apps.user_profile')
292 config.include('rhodecode.apps.user_group_profile')
292 config.include('rhodecode.apps.user_group_profile')
293 config.include('rhodecode.apps.my_account')
293 config.include('rhodecode.apps.my_account')
294 config.include('rhodecode.apps.svn_support')
294 config.include('rhodecode.apps.svn_support')
295 config.include('rhodecode.apps.ssh_support')
295 config.include('rhodecode.apps.ssh_support')
296 config.include('rhodecode.apps.gist')
296 config.include('rhodecode.apps.gist')
297 config.include('rhodecode.apps.debug_style')
297 config.include('rhodecode.apps.debug_style')
298 config.include('rhodecode.api')
298 config.include('rhodecode.api')
299
299
300 config.add_route('rhodecode_support', 'https://rhodecode.com/help/', static=True)
300 config.add_route('rhodecode_support', 'https://rhodecode.com/help/', static=True)
301 config.add_translation_dirs('rhodecode:i18n/')
301 config.add_translation_dirs('rhodecode:i18n/')
302 settings['default_locale_name'] = settings.get('lang', 'en')
302 settings['default_locale_name'] = settings.get('lang', 'en')
303
303
304 # Add subscribers.
304 # Add subscribers.
305 config.add_subscriber(inject_app_settings,
305 config.add_subscriber(inject_app_settings,
306 pyramid.events.ApplicationCreated)
306 pyramid.events.ApplicationCreated)
307 config.add_subscriber(scan_repositories_if_enabled,
307 config.add_subscriber(scan_repositories_if_enabled,
308 pyramid.events.ApplicationCreated)
308 pyramid.events.ApplicationCreated)
309 config.add_subscriber(write_metadata_if_needed,
309 config.add_subscriber(write_metadata_if_needed,
310 pyramid.events.ApplicationCreated)
310 pyramid.events.ApplicationCreated)
311 config.add_subscriber(write_js_routes_if_enabled,
311 config.add_subscriber(write_js_routes_if_enabled,
312 pyramid.events.ApplicationCreated)
312 pyramid.events.ApplicationCreated)
313
313
314 # request custom methods
314 # request custom methods
315 config.add_request_method(
315 config.add_request_method(
316 'rhodecode.lib.partial_renderer.get_partial_renderer',
316 'rhodecode.lib.partial_renderer.get_partial_renderer',
317 'get_partial_renderer')
317 'get_partial_renderer')
318
318
319 # Set the authorization policy.
319 # Set the authorization policy.
320 authz_policy = ACLAuthorizationPolicy()
320 authz_policy = ACLAuthorizationPolicy()
321 config.set_authorization_policy(authz_policy)
321 config.set_authorization_policy(authz_policy)
322
322
323 # Set the default renderer for HTML templates to mako.
323 # Set the default renderer for HTML templates to mako.
324 config.add_mako_renderer('.html')
324 config.add_mako_renderer('.html')
325
325
326 config.add_renderer(
326 config.add_renderer(
327 name='json_ext',
327 name='json_ext',
328 factory='rhodecode.lib.ext_json_renderer.pyramid_ext_json')
328 factory='rhodecode.lib.ext_json_renderer.pyramid_ext_json')
329
329
330 # include RhodeCode plugins
330 # include RhodeCode plugins
331 includes = aslist(settings.get('rhodecode.includes', []))
331 includes = aslist(settings.get('rhodecode.includes', []))
332 for inc in includes:
332 for inc in includes:
333 config.include(inc)
333 config.include(inc)
334
334
335 # custom not found view, if our pyramid app doesn't know how to handle
335 # custom not found view, if our pyramid app doesn't know how to handle
336 # the request pass it to potential VCS handling ap
336 # the request pass it to potential VCS handling ap
337 config.add_notfound_view(not_found_view)
337 config.add_notfound_view(not_found_view)
338 if not settings.get('debugtoolbar.enabled', False):
338 if not settings.get('debugtoolbar.enabled', False):
339 # disabled debugtoolbar handle all exceptions via the error_handlers
339 # disabled debugtoolbar handle all exceptions via the error_handlers
340 config.add_view(error_handler, context=Exception)
340 config.add_view(error_handler, context=Exception)
341
341
342 # all errors including 403/404/50X
342 # all errors including 403/404/50X
343 config.add_view(error_handler, context=HTTPError)
343 config.add_view(error_handler, context=HTTPError)
344
344
345
345
346 def wrap_app_in_wsgi_middlewares(pyramid_app, config):
346 def wrap_app_in_wsgi_middlewares(pyramid_app, config):
347 """
347 """
348 Apply outer WSGI middlewares around the application.
348 Apply outer WSGI middlewares around the application.
349 """
349 """
350 registry = config.registry
350 registry = config.registry
351 settings = registry.settings
351 settings = registry.settings
352
352
353 # enable https redirects based on HTTP_X_URL_SCHEME set by proxy
353 # enable https redirects based on HTTP_X_URL_SCHEME set by proxy
354 pyramid_app = HttpsFixup(pyramid_app, settings)
354 pyramid_app = HttpsFixup(pyramid_app, settings)
355
355
356 pyramid_app, _ae_client = wrap_in_appenlight_if_enabled(
356 pyramid_app, _ae_client = wrap_in_appenlight_if_enabled(
357 pyramid_app, settings)
357 pyramid_app, settings)
358 registry.ae_client = _ae_client
358 registry.ae_client = _ae_client
359
359
360 if settings['gzip_responses']:
360 if settings['gzip_responses']:
361 pyramid_app = make_gzip_middleware(
361 pyramid_app = make_gzip_middleware(
362 pyramid_app, settings, compress_level=1)
362 pyramid_app, settings, compress_level=1)
363
363
364 # this should be the outer most middleware in the wsgi stack since
364 # this should be the outer most middleware in the wsgi stack since
365 # middleware like Routes make database calls
365 # middleware like Routes make database calls
366 def pyramid_app_with_cleanup(environ, start_response):
366 def pyramid_app_with_cleanup(environ, start_response):
367 try:
367 try:
368 return pyramid_app(environ, start_response)
368 return pyramid_app(environ, start_response)
369 finally:
369 finally:
370 # Dispose current database session and rollback uncommitted
370 # Dispose current database session and rollback uncommitted
371 # transactions.
371 # transactions.
372 meta.Session.remove()
372 meta.Session.remove()
373
373
374 # In a single threaded mode server, on non sqlite db we should have
374 # In a single threaded mode server, on non sqlite db we should have
375 # '0 Current Checked out connections' at the end of a request,
375 # '0 Current Checked out connections' at the end of a request,
376 # if not, then something, somewhere is leaving a connection open
376 # if not, then something, somewhere is leaving a connection open
377 pool = meta.Base.metadata.bind.engine.pool
377 pool = meta.Base.metadata.bind.engine.pool
378 log.debug('sa pool status: %s', pool.status())
378 log.debug('sa pool status: %s', pool.status())
379 log.debug('Request processing finalized')
379 log.debug('Request processing finalized')
380
380
381 return pyramid_app_with_cleanup
381 return pyramid_app_with_cleanup
382
382
383
383
384 def sanitize_settings_and_apply_defaults(settings):
384 def sanitize_settings_and_apply_defaults(global_config, settings):
385 """
385 """
386 Applies settings defaults and does all type conversion.
386 Applies settings defaults and does all type conversion.
387
387
388 We would move all settings parsing and preparation into this place, so that
388 We would move all settings parsing and preparation into this place, so that
389 we have only one place left which deals with this part. The remaining parts
389 we have only one place left which deals with this part. The remaining parts
390 of the application would start to rely fully on well prepared settings.
390 of the application would start to rely fully on well prepared settings.
391
391
392 This piece would later be split up per topic to avoid a big fat monster
392 This piece would later be split up per topic to avoid a big fat monster
393 function.
393 function.
394 """
394 """
395
395
396 settings.setdefault('rhodecode.edition', 'Community Edition')
396 settings.setdefault('rhodecode.edition', 'Community Edition')
397
397
398 if 'mako.default_filters' not in settings:
398 if 'mako.default_filters' not in settings:
399 # set custom default filters if we don't have it defined
399 # set custom default filters if we don't have it defined
400 settings['mako.imports'] = 'from rhodecode.lib.base import h_filter'
400 settings['mako.imports'] = 'from rhodecode.lib.base import h_filter'
401 settings['mako.default_filters'] = 'h_filter'
401 settings['mako.default_filters'] = 'h_filter'
402
402
403 if 'mako.directories' not in settings:
403 if 'mako.directories' not in settings:
404 mako_directories = settings.setdefault('mako.directories', [
404 mako_directories = settings.setdefault('mako.directories', [
405 # Base templates of the original application
405 # Base templates of the original application
406 'rhodecode:templates',
406 'rhodecode:templates',
407 ])
407 ])
408 log.debug(
408 log.debug(
409 "Using the following Mako template directories: %s",
409 "Using the following Mako template directories: %s",
410 mako_directories)
410 mako_directories)
411
411
412 # Default includes, possible to change as a user
412 # Default includes, possible to change as a user
413 pyramid_includes = settings.setdefault('pyramid.includes', [
413 pyramid_includes = settings.setdefault('pyramid.includes', [
414 'rhodecode.lib.middleware.request_wrapper',
414 'rhodecode.lib.middleware.request_wrapper',
415 ])
415 ])
416 log.debug(
416 log.debug(
417 "Using the following pyramid.includes: %s",
417 "Using the following pyramid.includes: %s",
418 pyramid_includes)
418 pyramid_includes)
419
419
420 # TODO: johbo: Re-think this, usually the call to config.include
420 # TODO: johbo: Re-think this, usually the call to config.include
421 # should allow to pass in a prefix.
421 # should allow to pass in a prefix.
422 settings.setdefault('rhodecode.api.url', '/_admin/api')
422 settings.setdefault('rhodecode.api.url', '/_admin/api')
423 settings.setdefault('__file__', global_config.get('__file__'))
423
424
424 # Sanitize generic settings.
425 # Sanitize generic settings.
425 _list_setting(settings, 'default_encoding', 'UTF-8')
426 _list_setting(settings, 'default_encoding', 'UTF-8')
426 _bool_setting(settings, 'is_test', 'false')
427 _bool_setting(settings, 'is_test', 'false')
427 _bool_setting(settings, 'gzip_responses', 'false')
428 _bool_setting(settings, 'gzip_responses', 'false')
428
429
429 # Call split out functions that sanitize settings for each topic.
430 # Call split out functions that sanitize settings for each topic.
430 _sanitize_appenlight_settings(settings)
431 _sanitize_appenlight_settings(settings)
431 _sanitize_vcs_settings(settings)
432 _sanitize_vcs_settings(settings)
432 _sanitize_cache_settings(settings)
433 _sanitize_cache_settings(settings)
433
434
434 # configure instance id
435 # configure instance id
435 config_utils.set_instance_id(settings)
436 config_utils.set_instance_id(settings)
436
437
437 return settings
438 return settings
438
439
439
440
440 def enable_debug():
441 def enable_debug():
441 """
442 """
442 Helper to enable debug on running instance
443 Helper to enable debug on running instance
443 :return:
444 :return:
444 """
445 """
445 import tempfile
446 import tempfile
446 import textwrap
447 import textwrap
447 import logging.config
448 import logging.config
448
449
449 ini_template = textwrap.dedent("""
450 ini_template = textwrap.dedent("""
450 #####################################
451 #####################################
451 ### DEBUG LOGGING CONFIGURATION ####
452 ### DEBUG LOGGING CONFIGURATION ####
452 #####################################
453 #####################################
453 [loggers]
454 [loggers]
454 keys = root, sqlalchemy, beaker, celery, rhodecode, ssh_wrapper
455 keys = root, sqlalchemy, beaker, celery, rhodecode, ssh_wrapper
455
456
456 [handlers]
457 [handlers]
457 keys = console, console_sql
458 keys = console, console_sql
458
459
459 [formatters]
460 [formatters]
460 keys = generic, color_formatter, color_formatter_sql
461 keys = generic, color_formatter, color_formatter_sql
461
462
462 #############
463 #############
463 ## LOGGERS ##
464 ## LOGGERS ##
464 #############
465 #############
465 [logger_root]
466 [logger_root]
466 level = NOTSET
467 level = NOTSET
467 handlers = console
468 handlers = console
468
469
469 [logger_sqlalchemy]
470 [logger_sqlalchemy]
470 level = INFO
471 level = INFO
471 handlers = console_sql
472 handlers = console_sql
472 qualname = sqlalchemy.engine
473 qualname = sqlalchemy.engine
473 propagate = 0
474 propagate = 0
474
475
475 [logger_beaker]
476 [logger_beaker]
476 level = DEBUG
477 level = DEBUG
477 handlers =
478 handlers =
478 qualname = beaker.container
479 qualname = beaker.container
479 propagate = 1
480 propagate = 1
480
481
481 [logger_rhodecode]
482 [logger_rhodecode]
482 level = DEBUG
483 level = DEBUG
483 handlers =
484 handlers =
484 qualname = rhodecode
485 qualname = rhodecode
485 propagate = 1
486 propagate = 1
486
487
487 [logger_ssh_wrapper]
488 [logger_ssh_wrapper]
488 level = DEBUG
489 level = DEBUG
489 handlers =
490 handlers =
490 qualname = ssh_wrapper
491 qualname = ssh_wrapper
491 propagate = 1
492 propagate = 1
492
493
493 [logger_celery]
494 [logger_celery]
494 level = DEBUG
495 level = DEBUG
495 handlers =
496 handlers =
496 qualname = celery
497 qualname = celery
497
498
498
499
499 ##############
500 ##############
500 ## HANDLERS ##
501 ## HANDLERS ##
501 ##############
502 ##############
502
503
503 [handler_console]
504 [handler_console]
504 class = StreamHandler
505 class = StreamHandler
505 args = (sys.stderr, )
506 args = (sys.stderr, )
506 level = DEBUG
507 level = DEBUG
507 formatter = color_formatter
508 formatter = color_formatter
508
509
509 [handler_console_sql]
510 [handler_console_sql]
510 # "level = DEBUG" logs SQL queries and results.
511 # "level = DEBUG" logs SQL queries and results.
511 # "level = INFO" logs SQL queries.
512 # "level = INFO" logs SQL queries.
512 # "level = WARN" logs neither. (Recommended for production systems.)
513 # "level = WARN" logs neither. (Recommended for production systems.)
513 class = StreamHandler
514 class = StreamHandler
514 args = (sys.stderr, )
515 args = (sys.stderr, )
515 level = WARN
516 level = WARN
516 formatter = color_formatter_sql
517 formatter = color_formatter_sql
517
518
518 ################
519 ################
519 ## FORMATTERS ##
520 ## FORMATTERS ##
520 ################
521 ################
521
522
522 [formatter_generic]
523 [formatter_generic]
523 class = rhodecode.lib.logging_formatter.ExceptionAwareFormatter
524 class = rhodecode.lib.logging_formatter.ExceptionAwareFormatter
524 format = %(asctime)s.%(msecs)03d [%(process)d] %(levelname)-5.5s [%(name)s] %(message)s | %(req_id)s
525 format = %(asctime)s.%(msecs)03d [%(process)d] %(levelname)-5.5s [%(name)s] %(message)s | %(req_id)s
525 datefmt = %Y-%m-%d %H:%M:%S
526 datefmt = %Y-%m-%d %H:%M:%S
526
527
527 [formatter_color_formatter]
528 [formatter_color_formatter]
528 class = rhodecode.lib.logging_formatter.ColorRequestTrackingFormatter
529 class = rhodecode.lib.logging_formatter.ColorRequestTrackingFormatter
529 format = %(asctime)s.%(msecs)03d [%(process)d] %(levelname)-5.5s [%(name)s] %(message)s | %(req_id)s
530 format = %(asctime)s.%(msecs)03d [%(process)d] %(levelname)-5.5s [%(name)s] %(message)s | %(req_id)s
530 datefmt = %Y-%m-%d %H:%M:%S
531 datefmt = %Y-%m-%d %H:%M:%S
531
532
532 [formatter_color_formatter_sql]
533 [formatter_color_formatter_sql]
533 class = rhodecode.lib.logging_formatter.ColorFormatterSql
534 class = rhodecode.lib.logging_formatter.ColorFormatterSql
534 format = %(asctime)s.%(msecs)03d [%(process)d] %(levelname)-5.5s [%(name)s] %(message)s
535 format = %(asctime)s.%(msecs)03d [%(process)d] %(levelname)-5.5s [%(name)s] %(message)s
535 datefmt = %Y-%m-%d %H:%M:%S
536 datefmt = %Y-%m-%d %H:%M:%S
536 """)
537 """)
537
538
538 with tempfile.NamedTemporaryFile(prefix='rc_debug_logging_', suffix='.ini',
539 with tempfile.NamedTemporaryFile(prefix='rc_debug_logging_', suffix='.ini',
539 delete=False) as f:
540 delete=False) as f:
540 log.info('Saved Temporary DEBUG config at %s', f.name)
541 log.info('Saved Temporary DEBUG config at %s', f.name)
541 f.write(ini_template)
542 f.write(ini_template)
542
543
543 logging.config.fileConfig(f.name)
544 logging.config.fileConfig(f.name)
544 log.debug('DEBUG MODE ON')
545 log.debug('DEBUG MODE ON')
545 os.remove(f.name)
546 os.remove(f.name)
546
547
547
548
548 def _sanitize_appenlight_settings(settings):
549 def _sanitize_appenlight_settings(settings):
549 _bool_setting(settings, 'appenlight', 'false')
550 _bool_setting(settings, 'appenlight', 'false')
550
551
551
552
552 def _sanitize_vcs_settings(settings):
553 def _sanitize_vcs_settings(settings):
553 """
554 """
554 Applies settings defaults and does type conversion for all VCS related
555 Applies settings defaults and does type conversion for all VCS related
555 settings.
556 settings.
556 """
557 """
557 _string_setting(settings, 'vcs.svn.compatible_version', '')
558 _string_setting(settings, 'vcs.svn.compatible_version', '')
558 _string_setting(settings, 'git_rev_filter', '--all')
559 _string_setting(settings, 'git_rev_filter', '--all')
559 _string_setting(settings, 'vcs.hooks.protocol', 'http')
560 _string_setting(settings, 'vcs.hooks.protocol', 'http')
560 _string_setting(settings, 'vcs.hooks.host', '127.0.0.1')
561 _string_setting(settings, 'vcs.hooks.host', '127.0.0.1')
561 _string_setting(settings, 'vcs.scm_app_implementation', 'http')
562 _string_setting(settings, 'vcs.scm_app_implementation', 'http')
562 _string_setting(settings, 'vcs.server', '')
563 _string_setting(settings, 'vcs.server', '')
563 _string_setting(settings, 'vcs.server.log_level', 'debug')
564 _string_setting(settings, 'vcs.server.log_level', 'debug')
564 _string_setting(settings, 'vcs.server.protocol', 'http')
565 _string_setting(settings, 'vcs.server.protocol', 'http')
565 _bool_setting(settings, 'startup.import_repos', 'false')
566 _bool_setting(settings, 'startup.import_repos', 'false')
566 _bool_setting(settings, 'vcs.hooks.direct_calls', 'false')
567 _bool_setting(settings, 'vcs.hooks.direct_calls', 'false')
567 _bool_setting(settings, 'vcs.server.enable', 'true')
568 _bool_setting(settings, 'vcs.server.enable', 'true')
568 _bool_setting(settings, 'vcs.start_server', 'false')
569 _bool_setting(settings, 'vcs.start_server', 'false')
569 _list_setting(settings, 'vcs.backends', 'hg, git, svn')
570 _list_setting(settings, 'vcs.backends', 'hg, git, svn')
570 _int_setting(settings, 'vcs.connection_timeout', 3600)
571 _int_setting(settings, 'vcs.connection_timeout', 3600)
571
572
572 # Support legacy values of vcs.scm_app_implementation. Legacy
573 # Support legacy values of vcs.scm_app_implementation. Legacy
573 # configurations may use 'rhodecode.lib.middleware.utils.scm_app_http', or
574 # configurations may use 'rhodecode.lib.middleware.utils.scm_app_http', or
574 # disabled since 4.13 'vcsserver.scm_app' which is now mapped to 'http'.
575 # disabled since 4.13 'vcsserver.scm_app' which is now mapped to 'http'.
575 scm_app_impl = settings['vcs.scm_app_implementation']
576 scm_app_impl = settings['vcs.scm_app_implementation']
576 if scm_app_impl in ['rhodecode.lib.middleware.utils.scm_app_http', 'vcsserver.scm_app']:
577 if scm_app_impl in ['rhodecode.lib.middleware.utils.scm_app_http', 'vcsserver.scm_app']:
577 settings['vcs.scm_app_implementation'] = 'http'
578 settings['vcs.scm_app_implementation'] = 'http'
578
579
579
580
580 def _sanitize_cache_settings(settings):
581 def _sanitize_cache_settings(settings):
581 temp_store = tempfile.gettempdir()
582 temp_store = tempfile.gettempdir()
582 default_cache_dir = os.path.join(temp_store, 'rc_cache')
583 default_cache_dir = os.path.join(temp_store, 'rc_cache')
583
584
584 # save default, cache dir, and use it for all backends later.
585 # save default, cache dir, and use it for all backends later.
585 default_cache_dir = _string_setting(
586 default_cache_dir = _string_setting(
586 settings,
587 settings,
587 'cache_dir',
588 'cache_dir',
588 default_cache_dir, lower=False, default_when_empty=True)
589 default_cache_dir, lower=False, default_when_empty=True)
589
590
590 # ensure we have our dir created
591 # ensure we have our dir created
591 if not os.path.isdir(default_cache_dir):
592 if not os.path.isdir(default_cache_dir):
592 os.makedirs(default_cache_dir, mode=0o755)
593 os.makedirs(default_cache_dir, mode=0o755)
593
594
594 # exception store cache
595 # exception store cache
595 _string_setting(
596 _string_setting(
596 settings,
597 settings,
597 'exception_tracker.store_path',
598 'exception_tracker.store_path',
598 temp_store, lower=False, default_when_empty=True)
599 temp_store, lower=False, default_when_empty=True)
599
600
600 # cache_perms
601 # cache_perms
601 _string_setting(
602 _string_setting(
602 settings,
603 settings,
603 'rc_cache.cache_perms.backend',
604 'rc_cache.cache_perms.backend',
604 'dogpile.cache.rc.file_namespace', lower=False)
605 'dogpile.cache.rc.file_namespace', lower=False)
605 _int_setting(
606 _int_setting(
606 settings,
607 settings,
607 'rc_cache.cache_perms.expiration_time',
608 'rc_cache.cache_perms.expiration_time',
608 60)
609 60)
609 _string_setting(
610 _string_setting(
610 settings,
611 settings,
611 'rc_cache.cache_perms.arguments.filename',
612 'rc_cache.cache_perms.arguments.filename',
612 os.path.join(default_cache_dir, 'rc_cache_1'), lower=False)
613 os.path.join(default_cache_dir, 'rc_cache_1'), lower=False)
613
614
614 # cache_repo
615 # cache_repo
615 _string_setting(
616 _string_setting(
616 settings,
617 settings,
617 'rc_cache.cache_repo.backend',
618 'rc_cache.cache_repo.backend',
618 'dogpile.cache.rc.file_namespace', lower=False)
619 'dogpile.cache.rc.file_namespace', lower=False)
619 _int_setting(
620 _int_setting(
620 settings,
621 settings,
621 'rc_cache.cache_repo.expiration_time',
622 'rc_cache.cache_repo.expiration_time',
622 60)
623 60)
623 _string_setting(
624 _string_setting(
624 settings,
625 settings,
625 'rc_cache.cache_repo.arguments.filename',
626 'rc_cache.cache_repo.arguments.filename',
626 os.path.join(default_cache_dir, 'rc_cache_2'), lower=False)
627 os.path.join(default_cache_dir, 'rc_cache_2'), lower=False)
627
628
628 # cache_license
629 # cache_license
629 _string_setting(
630 _string_setting(
630 settings,
631 settings,
631 'rc_cache.cache_license.backend',
632 'rc_cache.cache_license.backend',
632 'dogpile.cache.rc.file_namespace', lower=False)
633 'dogpile.cache.rc.file_namespace', lower=False)
633 _int_setting(
634 _int_setting(
634 settings,
635 settings,
635 'rc_cache.cache_license.expiration_time',
636 'rc_cache.cache_license.expiration_time',
636 5*60)
637 5*60)
637 _string_setting(
638 _string_setting(
638 settings,
639 settings,
639 'rc_cache.cache_license.arguments.filename',
640 'rc_cache.cache_license.arguments.filename',
640 os.path.join(default_cache_dir, 'rc_cache_3'), lower=False)
641 os.path.join(default_cache_dir, 'rc_cache_3'), lower=False)
641
642
642 # cache_repo_longterm memory, 96H
643 # cache_repo_longterm memory, 96H
643 _string_setting(
644 _string_setting(
644 settings,
645 settings,
645 'rc_cache.cache_repo_longterm.backend',
646 'rc_cache.cache_repo_longterm.backend',
646 'dogpile.cache.rc.memory_lru', lower=False)
647 'dogpile.cache.rc.memory_lru', lower=False)
647 _int_setting(
648 _int_setting(
648 settings,
649 settings,
649 'rc_cache.cache_repo_longterm.expiration_time',
650 'rc_cache.cache_repo_longterm.expiration_time',
650 345600)
651 345600)
651 _int_setting(
652 _int_setting(
652 settings,
653 settings,
653 'rc_cache.cache_repo_longterm.max_size',
654 'rc_cache.cache_repo_longterm.max_size',
654 10000)
655 10000)
655
656
656 # sql_cache_short
657 # sql_cache_short
657 _string_setting(
658 _string_setting(
658 settings,
659 settings,
659 'rc_cache.sql_cache_short.backend',
660 'rc_cache.sql_cache_short.backend',
660 'dogpile.cache.rc.memory_lru', lower=False)
661 'dogpile.cache.rc.memory_lru', lower=False)
661 _int_setting(
662 _int_setting(
662 settings,
663 settings,
663 'rc_cache.sql_cache_short.expiration_time',
664 'rc_cache.sql_cache_short.expiration_time',
664 30)
665 30)
665 _int_setting(
666 _int_setting(
666 settings,
667 settings,
667 'rc_cache.sql_cache_short.max_size',
668 'rc_cache.sql_cache_short.max_size',
668 10000)
669 10000)
669
670
670
671
671 def _int_setting(settings, name, default):
672 def _int_setting(settings, name, default):
672 settings[name] = int(settings.get(name, default))
673 settings[name] = int(settings.get(name, default))
673 return settings[name]
674 return settings[name]
674
675
675
676
676 def _bool_setting(settings, name, default):
677 def _bool_setting(settings, name, default):
677 input_val = settings.get(name, default)
678 input_val = settings.get(name, default)
678 if isinstance(input_val, unicode):
679 if isinstance(input_val, unicode):
679 input_val = input_val.encode('utf8')
680 input_val = input_val.encode('utf8')
680 settings[name] = asbool(input_val)
681 settings[name] = asbool(input_val)
681 return settings[name]
682 return settings[name]
682
683
683
684
684 def _list_setting(settings, name, default):
685 def _list_setting(settings, name, default):
685 raw_value = settings.get(name, default)
686 raw_value = settings.get(name, default)
686
687
687 old_separator = ','
688 old_separator = ','
688 if old_separator in raw_value:
689 if old_separator in raw_value:
689 # If we get a comma separated list, pass it to our own function.
690 # If we get a comma separated list, pass it to our own function.
690 settings[name] = rhodecode_aslist(raw_value, sep=old_separator)
691 settings[name] = rhodecode_aslist(raw_value, sep=old_separator)
691 else:
692 else:
692 # Otherwise we assume it uses pyramids space/newline separation.
693 # Otherwise we assume it uses pyramids space/newline separation.
693 settings[name] = aslist(raw_value)
694 settings[name] = aslist(raw_value)
694 return settings[name]
695 return settings[name]
695
696
696
697
697 def _string_setting(settings, name, default, lower=True, default_when_empty=False):
698 def _string_setting(settings, name, default, lower=True, default_when_empty=False):
698 value = settings.get(name, default)
699 value = settings.get(name, default)
699
700
700 if default_when_empty and not value:
701 if default_when_empty and not value:
701 # use default value when value is empty
702 # use default value when value is empty
702 value = default
703 value = default
703
704
704 if lower:
705 if lower:
705 value = value.lower()
706 value = value.lower()
706 settings[name] = value
707 settings[name] = value
707 return settings[name]
708 return settings[name]
708
709
709
710
710 def _substitute_values(mapping, substitutions):
711 def _substitute_values(mapping, substitutions):
711 result = {}
712 result = {}
712
713
713 try:
714 try:
714 for key, value in mapping.items():
715 for key, value in mapping.items():
715 # initialize without substitution first
716 # initialize without substitution first
716 result[key] = value
717 result[key] = value
717
718
718 # Note: Cannot use regular replacements, since they would clash
719 # Note: Cannot use regular replacements, since they would clash
719 # with the implementation of ConfigParser. Using "format" instead.
720 # with the implementation of ConfigParser. Using "format" instead.
720 try:
721 try:
721 result[key] = value.format(**substitutions)
722 result[key] = value.format(**substitutions)
722 except KeyError as e:
723 except KeyError as e:
723 env_var = '{}'.format(e.args[0])
724 env_var = '{}'.format(e.args[0])
724
725
725 msg = 'Failed to substitute: `{key}={{{var}}}` with environment entry. ' \
726 msg = 'Failed to substitute: `{key}={{{var}}}` with environment entry. ' \
726 'Make sure your environment has {var} set, or remove this ' \
727 'Make sure your environment has {var} set, or remove this ' \
727 'variable from config file'.format(key=key, var=env_var)
728 'variable from config file'.format(key=key, var=env_var)
728
729
729 if env_var.startswith('ENV_'):
730 if env_var.startswith('ENV_'):
730 raise ValueError(msg)
731 raise ValueError(msg)
731 else:
732 else:
732 log.warning(msg)
733 log.warning(msg)
733
734
734 except ValueError as e:
735 except ValueError as e:
735 log.warning('Failed to substitute ENV variable: %s', e)
736 log.warning('Failed to substitute ENV variable: %s', e)
736 result = mapping
737 result = mapping
737
738
738 return result
739 return result
General Comments 0
You need to be logged in to leave comments. Login now