##// END OF EJS Templates
locales: fetch proper locale before defaulting to default.
marcink -
r148:11cb569c default
parent child Browse files
Show More
@@ -1,380 +1,380 b''
1 # RhodeCode VCSServer provides access to different vcs backends via network.
1 # RhodeCode VCSServer provides access to different vcs backends via network.
2 # Copyright (C) 2014-2016 RodeCode GmbH
2 # Copyright (C) 2014-2016 RodeCode GmbH
3 #
3 #
4 # This program is free software; you can redistribute it and/or modify
4 # This program is free software; you can redistribute it and/or modify
5 # it under the terms of the GNU General Public License as published by
5 # it under the terms of the GNU General Public License as published by
6 # the Free Software Foundation; either version 3 of the License, or
6 # the Free Software Foundation; either version 3 of the License, or
7 # (at your option) any later version.
7 # (at your option) any later version.
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 General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software Foundation,
15 # along with this program; if not, write to the Free Software Foundation,
16 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17
17
18 import base64
18 import base64
19 import locale
19 import locale
20 import logging
20 import logging
21 import uuid
21 import uuid
22 import wsgiref.util
22 import wsgiref.util
23 import traceback
23 import traceback
24 from itertools import chain
24 from itertools import chain
25
25
26 import msgpack
26 import msgpack
27 from beaker.cache import CacheManager
27 from beaker.cache import CacheManager
28 from beaker.util import parse_cache_config_options
28 from beaker.util import parse_cache_config_options
29 from pyramid.config import Configurator
29 from pyramid.config import Configurator
30 from pyramid.wsgi import wsgiapp
30 from pyramid.wsgi import wsgiapp
31
31
32 from vcsserver import remote_wsgi, scm_app, settings, hgpatches
32 from vcsserver import remote_wsgi, scm_app, settings, hgpatches
33 from vcsserver.echo_stub import remote_wsgi as remote_wsgi_stub
33 from vcsserver.echo_stub import remote_wsgi as remote_wsgi_stub
34 from vcsserver.echo_stub.echo_app import EchoApp
34 from vcsserver.echo_stub.echo_app import EchoApp
35 from vcsserver.exceptions import HTTPRepoLocked
35 from vcsserver.exceptions import HTTPRepoLocked
36 from vcsserver.server import VcsServer
36 from vcsserver.server import VcsServer
37
37
38 try:
38 try:
39 from vcsserver.git import GitFactory, GitRemote
39 from vcsserver.git import GitFactory, GitRemote
40 except ImportError:
40 except ImportError:
41 GitFactory = None
41 GitFactory = None
42 GitRemote = None
42 GitRemote = None
43 try:
43 try:
44 from vcsserver.hg import MercurialFactory, HgRemote
44 from vcsserver.hg import MercurialFactory, HgRemote
45 except ImportError:
45 except ImportError:
46 MercurialFactory = None
46 MercurialFactory = None
47 HgRemote = None
47 HgRemote = None
48 try:
48 try:
49 from vcsserver.svn import SubversionFactory, SvnRemote
49 from vcsserver.svn import SubversionFactory, SvnRemote
50 except ImportError:
50 except ImportError:
51 SubversionFactory = None
51 SubversionFactory = None
52 SvnRemote = None
52 SvnRemote = None
53
53
54 log = logging.getLogger(__name__)
54 log = logging.getLogger(__name__)
55
55
56
56
57 class VCS(object):
57 class VCS(object):
58 def __init__(self, locale=None, cache_config=None):
58 def __init__(self, locale=None, cache_config=None):
59 self.locale = locale
59 self.locale = locale
60 self.cache_config = cache_config
60 self.cache_config = cache_config
61 self._configure_locale()
61 self._configure_locale()
62 self._initialize_cache()
62 self._initialize_cache()
63
63
64 if GitFactory and GitRemote:
64 if GitFactory and GitRemote:
65 git_repo_cache = self.cache.get_cache_region(
65 git_repo_cache = self.cache.get_cache_region(
66 'git', region='repo_object')
66 'git', region='repo_object')
67 git_factory = GitFactory(git_repo_cache)
67 git_factory = GitFactory(git_repo_cache)
68 self._git_remote = GitRemote(git_factory)
68 self._git_remote = GitRemote(git_factory)
69 else:
69 else:
70 log.info("Git client import failed")
70 log.info("Git client import failed")
71
71
72 if MercurialFactory and HgRemote:
72 if MercurialFactory and HgRemote:
73 hg_repo_cache = self.cache.get_cache_region(
73 hg_repo_cache = self.cache.get_cache_region(
74 'hg', region='repo_object')
74 'hg', region='repo_object')
75 hg_factory = MercurialFactory(hg_repo_cache)
75 hg_factory = MercurialFactory(hg_repo_cache)
76 self._hg_remote = HgRemote(hg_factory)
76 self._hg_remote = HgRemote(hg_factory)
77 else:
77 else:
78 log.info("Mercurial client import failed")
78 log.info("Mercurial client import failed")
79
79
80 if SubversionFactory and SvnRemote:
80 if SubversionFactory and SvnRemote:
81 svn_repo_cache = self.cache.get_cache_region(
81 svn_repo_cache = self.cache.get_cache_region(
82 'svn', region='repo_object')
82 'svn', region='repo_object')
83 svn_factory = SubversionFactory(svn_repo_cache)
83 svn_factory = SubversionFactory(svn_repo_cache)
84 self._svn_remote = SvnRemote(svn_factory, hg_factory=hg_factory)
84 self._svn_remote = SvnRemote(svn_factory, hg_factory=hg_factory)
85 else:
85 else:
86 log.info("Subversion client import failed")
86 log.info("Subversion client import failed")
87
87
88 self._vcsserver = VcsServer()
88 self._vcsserver = VcsServer()
89
89
90 def _initialize_cache(self):
90 def _initialize_cache(self):
91 cache_config = parse_cache_config_options(self.cache_config)
91 cache_config = parse_cache_config_options(self.cache_config)
92 log.info('Initializing beaker cache: %s' % cache_config)
92 log.info('Initializing beaker cache: %s' % cache_config)
93 self.cache = CacheManager(**cache_config)
93 self.cache = CacheManager(**cache_config)
94
94
95 def _configure_locale(self):
95 def _configure_locale(self):
96 if self.locale:
96 if self.locale:
97 log.info('Settings locale: `LC_ALL` to %s' % self.locale)
97 log.info('Settings locale: `LC_ALL` to %s' % self.locale)
98 else:
98 else:
99 log.info(
99 log.info(
100 'Configuring locale subsystem based on environment variables')
100 'Configuring locale subsystem based on environment variables')
101 try:
101 try:
102 # If self.locale is the empty string, then the locale
102 # If self.locale is the empty string, then the locale
103 # module will use the environment variables. See the
103 # module will use the environment variables. See the
104 # documentation of the package `locale`.
104 # documentation of the package `locale`.
105 locale.setlocale(locale.LC_ALL, self.locale)
105 locale.setlocale(locale.LC_ALL, self.locale)
106
106
107 language_code, encoding = locale.getlocale()
107 language_code, encoding = locale.getlocale()
108 log.info(
108 log.info(
109 'Locale set to language code "%s" with encoding "%s".',
109 'Locale set to language code "%s" with encoding "%s".',
110 language_code, encoding)
110 language_code, encoding)
111 except locale.Error:
111 except locale.Error:
112 log.exception(
112 log.exception(
113 'Cannot set locale, not configuring the locale system')
113 'Cannot set locale, not configuring the locale system')
114
114
115
115
116 class WsgiProxy(object):
116 class WsgiProxy(object):
117 def __init__(self, wsgi):
117 def __init__(self, wsgi):
118 self.wsgi = wsgi
118 self.wsgi = wsgi
119
119
120 def __call__(self, environ, start_response):
120 def __call__(self, environ, start_response):
121 input_data = environ['wsgi.input'].read()
121 input_data = environ['wsgi.input'].read()
122 input_data = msgpack.unpackb(input_data)
122 input_data = msgpack.unpackb(input_data)
123
123
124 error = None
124 error = None
125 try:
125 try:
126 data, status, headers = self.wsgi.handle(
126 data, status, headers = self.wsgi.handle(
127 input_data['environment'], input_data['input_data'],
127 input_data['environment'], input_data['input_data'],
128 *input_data['args'], **input_data['kwargs'])
128 *input_data['args'], **input_data['kwargs'])
129 except Exception as e:
129 except Exception as e:
130 data, status, headers = [], None, None
130 data, status, headers = [], None, None
131 error = {
131 error = {
132 'message': str(e),
132 'message': str(e),
133 '_vcs_kind': getattr(e, '_vcs_kind', None)
133 '_vcs_kind': getattr(e, '_vcs_kind', None)
134 }
134 }
135
135
136 start_response(200, {})
136 start_response(200, {})
137 return self._iterator(error, status, headers, data)
137 return self._iterator(error, status, headers, data)
138
138
139 def _iterator(self, error, status, headers, data):
139 def _iterator(self, error, status, headers, data):
140 initial_data = [
140 initial_data = [
141 error,
141 error,
142 status,
142 status,
143 headers,
143 headers,
144 ]
144 ]
145
145
146 for d in chain(initial_data, data):
146 for d in chain(initial_data, data):
147 yield msgpack.packb(d)
147 yield msgpack.packb(d)
148
148
149
149
150 class HTTPApplication(object):
150 class HTTPApplication(object):
151 ALLOWED_EXCEPTIONS = ('KeyError', 'URLError')
151 ALLOWED_EXCEPTIONS = ('KeyError', 'URLError')
152
152
153 remote_wsgi = remote_wsgi
153 remote_wsgi = remote_wsgi
154 _use_echo_app = False
154 _use_echo_app = False
155
155
156 def __init__(self, settings=None):
156 def __init__(self, settings=None):
157 self.config = Configurator(settings=settings)
157 self.config = Configurator(settings=settings)
158 locale = settings.get('', 'en_US.UTF-8')
158 locale = settings.get('locale', '') or 'en_US.UTF-8'
159 vcs = VCS(locale=locale, cache_config=settings)
159 vcs = VCS(locale=locale, cache_config=settings)
160 self._remotes = {
160 self._remotes = {
161 'hg': vcs._hg_remote,
161 'hg': vcs._hg_remote,
162 'git': vcs._git_remote,
162 'git': vcs._git_remote,
163 'svn': vcs._svn_remote,
163 'svn': vcs._svn_remote,
164 'server': vcs._vcsserver,
164 'server': vcs._vcsserver,
165 }
165 }
166 if settings.get('dev.use_echo_app', 'false').lower() == 'true':
166 if settings.get('dev.use_echo_app', 'false').lower() == 'true':
167 self._use_echo_app = True
167 self._use_echo_app = True
168 log.warning("Using EchoApp for VCS operations.")
168 log.warning("Using EchoApp for VCS operations.")
169 self.remote_wsgi = remote_wsgi_stub
169 self.remote_wsgi = remote_wsgi_stub
170 self._configure_settings(settings)
170 self._configure_settings(settings)
171 self._configure()
171 self._configure()
172
172
173 def _configure_settings(self, app_settings):
173 def _configure_settings(self, app_settings):
174 """
174 """
175 Configure the settings module.
175 Configure the settings module.
176 """
176 """
177 git_path = app_settings.get('git_path', None)
177 git_path = app_settings.get('git_path', None)
178 if git_path:
178 if git_path:
179 settings.GIT_EXECUTABLE = git_path
179 settings.GIT_EXECUTABLE = git_path
180
180
181 def _configure(self):
181 def _configure(self):
182 self.config.add_renderer(
182 self.config.add_renderer(
183 name='msgpack',
183 name='msgpack',
184 factory=self._msgpack_renderer_factory)
184 factory=self._msgpack_renderer_factory)
185
185
186 self.config.add_route('service', '/_service')
186 self.config.add_route('service', '/_service')
187 self.config.add_route('status', '/status')
187 self.config.add_route('status', '/status')
188 self.config.add_route('hg_proxy', '/proxy/hg')
188 self.config.add_route('hg_proxy', '/proxy/hg')
189 self.config.add_route('git_proxy', '/proxy/git')
189 self.config.add_route('git_proxy', '/proxy/git')
190 self.config.add_route('vcs', '/{backend}')
190 self.config.add_route('vcs', '/{backend}')
191 self.config.add_route('stream_git', '/stream/git/*repo_name')
191 self.config.add_route('stream_git', '/stream/git/*repo_name')
192 self.config.add_route('stream_hg', '/stream/hg/*repo_name')
192 self.config.add_route('stream_hg', '/stream/hg/*repo_name')
193
193
194 self.config.add_view(
194 self.config.add_view(
195 self.status_view, route_name='status', renderer='json')
195 self.status_view, route_name='status', renderer='json')
196 self.config.add_view(
196 self.config.add_view(
197 self.service_view, route_name='service', renderer='msgpack')
197 self.service_view, route_name='service', renderer='msgpack')
198
198
199 self.config.add_view(self.hg_proxy(), route_name='hg_proxy')
199 self.config.add_view(self.hg_proxy(), route_name='hg_proxy')
200 self.config.add_view(self.git_proxy(), route_name='git_proxy')
200 self.config.add_view(self.git_proxy(), route_name='git_proxy')
201 self.config.add_view(
201 self.config.add_view(
202 self.vcs_view, route_name='vcs', renderer='msgpack')
202 self.vcs_view, route_name='vcs', renderer='msgpack')
203
203
204 self.config.add_view(self.hg_stream(), route_name='stream_hg')
204 self.config.add_view(self.hg_stream(), route_name='stream_hg')
205 self.config.add_view(self.git_stream(), route_name='stream_git')
205 self.config.add_view(self.git_stream(), route_name='stream_git')
206 self.config.add_view(
206 self.config.add_view(
207 self.handle_vcs_exception, context=Exception,
207 self.handle_vcs_exception, context=Exception,
208 custom_predicates=[self.is_vcs_exception])
208 custom_predicates=[self.is_vcs_exception])
209
209
210 def wsgi_app(self):
210 def wsgi_app(self):
211 return self.config.make_wsgi_app()
211 return self.config.make_wsgi_app()
212
212
213 def vcs_view(self, request):
213 def vcs_view(self, request):
214 remote = self._remotes[request.matchdict['backend']]
214 remote = self._remotes[request.matchdict['backend']]
215 payload = msgpack.unpackb(request.body, use_list=True)
215 payload = msgpack.unpackb(request.body, use_list=True)
216 method = payload.get('method')
216 method = payload.get('method')
217 params = payload.get('params')
217 params = payload.get('params')
218 wire = params.get('wire')
218 wire = params.get('wire')
219 args = params.get('args')
219 args = params.get('args')
220 kwargs = params.get('kwargs')
220 kwargs = params.get('kwargs')
221 if wire:
221 if wire:
222 try:
222 try:
223 wire['context'] = uuid.UUID(wire['context'])
223 wire['context'] = uuid.UUID(wire['context'])
224 except KeyError:
224 except KeyError:
225 pass
225 pass
226 args.insert(0, wire)
226 args.insert(0, wire)
227
227
228 try:
228 try:
229 resp = getattr(remote, method)(*args, **kwargs)
229 resp = getattr(remote, method)(*args, **kwargs)
230 except Exception as e:
230 except Exception as e:
231 tb_info = traceback.format_exc()
231 tb_info = traceback.format_exc()
232
232
233 type_ = e.__class__.__name__
233 type_ = e.__class__.__name__
234 if type_ not in self.ALLOWED_EXCEPTIONS:
234 if type_ not in self.ALLOWED_EXCEPTIONS:
235 type_ = None
235 type_ = None
236
236
237 resp = {
237 resp = {
238 'id': payload.get('id'),
238 'id': payload.get('id'),
239 'error': {
239 'error': {
240 'message': e.message,
240 'message': e.message,
241 'traceback': tb_info,
241 'traceback': tb_info,
242 'type': type_
242 'type': type_
243 }
243 }
244 }
244 }
245 try:
245 try:
246 resp['error']['_vcs_kind'] = e._vcs_kind
246 resp['error']['_vcs_kind'] = e._vcs_kind
247 except AttributeError:
247 except AttributeError:
248 pass
248 pass
249 else:
249 else:
250 resp = {
250 resp = {
251 'id': payload.get('id'),
251 'id': payload.get('id'),
252 'result': resp
252 'result': resp
253 }
253 }
254
254
255 return resp
255 return resp
256
256
257 def status_view(self, request):
257 def status_view(self, request):
258 return {'status': 'OK'}
258 return {'status': 'OK'}
259
259
260 def service_view(self, request):
260 def service_view(self, request):
261 import vcsserver
261 import vcsserver
262 payload = msgpack.unpackb(request.body, use_list=True)
262 payload = msgpack.unpackb(request.body, use_list=True)
263 resp = {
263 resp = {
264 'id': payload.get('id'),
264 'id': payload.get('id'),
265 'result': dict(
265 'result': dict(
266 version=vcsserver.__version__,
266 version=vcsserver.__version__,
267 config={},
267 config={},
268 payload=payload,
268 payload=payload,
269 )
269 )
270 }
270 }
271 return resp
271 return resp
272
272
273 def _msgpack_renderer_factory(self, info):
273 def _msgpack_renderer_factory(self, info):
274 def _render(value, system):
274 def _render(value, system):
275 value = msgpack.packb(value)
275 value = msgpack.packb(value)
276 request = system.get('request')
276 request = system.get('request')
277 if request is not None:
277 if request is not None:
278 response = request.response
278 response = request.response
279 ct = response.content_type
279 ct = response.content_type
280 if ct == response.default_content_type:
280 if ct == response.default_content_type:
281 response.content_type = 'application/x-msgpack'
281 response.content_type = 'application/x-msgpack'
282 return value
282 return value
283 return _render
283 return _render
284
284
285 def hg_proxy(self):
285 def hg_proxy(self):
286 @wsgiapp
286 @wsgiapp
287 def _hg_proxy(environ, start_response):
287 def _hg_proxy(environ, start_response):
288 app = WsgiProxy(self.remote_wsgi.HgRemoteWsgi())
288 app = WsgiProxy(self.remote_wsgi.HgRemoteWsgi())
289 return app(environ, start_response)
289 return app(environ, start_response)
290 return _hg_proxy
290 return _hg_proxy
291
291
292 def git_proxy(self):
292 def git_proxy(self):
293 @wsgiapp
293 @wsgiapp
294 def _git_proxy(environ, start_response):
294 def _git_proxy(environ, start_response):
295 app = WsgiProxy(self.remote_wsgi.GitRemoteWsgi())
295 app = WsgiProxy(self.remote_wsgi.GitRemoteWsgi())
296 return app(environ, start_response)
296 return app(environ, start_response)
297 return _git_proxy
297 return _git_proxy
298
298
299 def hg_stream(self):
299 def hg_stream(self):
300 if self._use_echo_app:
300 if self._use_echo_app:
301 @wsgiapp
301 @wsgiapp
302 def _hg_stream(environ, start_response):
302 def _hg_stream(environ, start_response):
303 app = EchoApp('fake_path', 'fake_name', None)
303 app = EchoApp('fake_path', 'fake_name', None)
304 return app(environ, start_response)
304 return app(environ, start_response)
305 return _hg_stream
305 return _hg_stream
306 else:
306 else:
307 @wsgiapp
307 @wsgiapp
308 def _hg_stream(environ, start_response):
308 def _hg_stream(environ, start_response):
309 repo_path = environ['HTTP_X_RC_REPO_PATH']
309 repo_path = environ['HTTP_X_RC_REPO_PATH']
310 repo_name = environ['HTTP_X_RC_REPO_NAME']
310 repo_name = environ['HTTP_X_RC_REPO_NAME']
311 packed_config = base64.b64decode(
311 packed_config = base64.b64decode(
312 environ['HTTP_X_RC_REPO_CONFIG'])
312 environ['HTTP_X_RC_REPO_CONFIG'])
313 config = msgpack.unpackb(packed_config)
313 config = msgpack.unpackb(packed_config)
314 app = scm_app.create_hg_wsgi_app(
314 app = scm_app.create_hg_wsgi_app(
315 repo_path, repo_name, config)
315 repo_path, repo_name, config)
316
316
317 # Consitent path information for hgweb
317 # Consitent path information for hgweb
318 environ['PATH_INFO'] = environ['HTTP_X_RC_PATH_INFO']
318 environ['PATH_INFO'] = environ['HTTP_X_RC_PATH_INFO']
319 environ['REPO_NAME'] = repo_name
319 environ['REPO_NAME'] = repo_name
320 return app(environ, ResponseFilter(start_response))
320 return app(environ, ResponseFilter(start_response))
321 return _hg_stream
321 return _hg_stream
322
322
323 def git_stream(self):
323 def git_stream(self):
324 if self._use_echo_app:
324 if self._use_echo_app:
325 @wsgiapp
325 @wsgiapp
326 def _git_stream(environ, start_response):
326 def _git_stream(environ, start_response):
327 app = EchoApp('fake_path', 'fake_name', None)
327 app = EchoApp('fake_path', 'fake_name', None)
328 return app(environ, start_response)
328 return app(environ, start_response)
329 return _git_stream
329 return _git_stream
330 else:
330 else:
331 @wsgiapp
331 @wsgiapp
332 def _git_stream(environ, start_response):
332 def _git_stream(environ, start_response):
333 repo_path = environ['HTTP_X_RC_REPO_PATH']
333 repo_path = environ['HTTP_X_RC_REPO_PATH']
334 repo_name = environ['HTTP_X_RC_REPO_NAME']
334 repo_name = environ['HTTP_X_RC_REPO_NAME']
335 packed_config = base64.b64decode(
335 packed_config = base64.b64decode(
336 environ['HTTP_X_RC_REPO_CONFIG'])
336 environ['HTTP_X_RC_REPO_CONFIG'])
337 config = msgpack.unpackb(packed_config)
337 config = msgpack.unpackb(packed_config)
338
338
339 environ['PATH_INFO'] = environ['HTTP_X_RC_PATH_INFO']
339 environ['PATH_INFO'] = environ['HTTP_X_RC_PATH_INFO']
340 app = scm_app.create_git_wsgi_app(
340 app = scm_app.create_git_wsgi_app(
341 repo_path, repo_name, config)
341 repo_path, repo_name, config)
342 return app(environ, start_response)
342 return app(environ, start_response)
343 return _git_stream
343 return _git_stream
344
344
345 def is_vcs_exception(self, context, request):
345 def is_vcs_exception(self, context, request):
346 """
346 """
347 View predicate that returns true if the context object is a VCS
347 View predicate that returns true if the context object is a VCS
348 exception.
348 exception.
349 """
349 """
350 return hasattr(context, '_vcs_kind')
350 return hasattr(context, '_vcs_kind')
351
351
352 def handle_vcs_exception(self, exception, request):
352 def handle_vcs_exception(self, exception, request):
353 if exception._vcs_kind == 'repo_locked':
353 if exception._vcs_kind == 'repo_locked':
354 # Get custom repo-locked status code if present.
354 # Get custom repo-locked status code if present.
355 status_code = request.headers.get('X-RC-Locked-Status-Code')
355 status_code = request.headers.get('X-RC-Locked-Status-Code')
356 return HTTPRepoLocked(
356 return HTTPRepoLocked(
357 title=exception.message, status_code=status_code)
357 title=exception.message, status_code=status_code)
358
358
359 # Re-raise exception if we can not handle it.
359 # Re-raise exception if we can not handle it.
360 raise exception
360 raise exception
361
361
362
362
363 class ResponseFilter(object):
363 class ResponseFilter(object):
364
364
365 def __init__(self, start_response):
365 def __init__(self, start_response):
366 self._start_response = start_response
366 self._start_response = start_response
367
367
368 def __call__(self, status, response_headers, exc_info=None):
368 def __call__(self, status, response_headers, exc_info=None):
369 headers = tuple(
369 headers = tuple(
370 (h, v) for h, v in response_headers
370 (h, v) for h, v in response_headers
371 if not wsgiref.util.is_hop_by_hop(h))
371 if not wsgiref.util.is_hop_by_hop(h))
372 return self._start_response(status, headers, exc_info)
372 return self._start_response(status, headers, exc_info)
373
373
374
374
375 def main(global_config, **settings):
375 def main(global_config, **settings):
376 if MercurialFactory:
376 if MercurialFactory:
377 hgpatches.patch_largefiles_capabilities()
377 hgpatches.patch_largefiles_capabilities()
378 hgpatches.patch_subrepo_type_mapping()
378 hgpatches.patch_subrepo_type_mapping()
379 app = HTTPApplication(settings=settings)
379 app = HTTPApplication(settings=settings)
380 return app.wsgi_app()
380 return app.wsgi_app()
General Comments 0
You need to be logged in to leave comments. Login now