##// END OF EJS Templates
vcs: Fix vcsserver startup with http backend.
Martin Bornhold -
r964:eac204f1 default
parent child Browse files
Show More
@@ -1,296 +1,300 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2014-2016 RhodeCode GmbH
3 # Copyright (C) 2014-2016 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 """
21 """
22 Various version Control System version lib (vcs) management abstraction layer
22 Various version Control System version lib (vcs) management abstraction layer
23 for Python. Build with server client architecture.
23 for Python. Build with server client architecture.
24 """
24 """
25
25
26
26
27 VERSION = (0, 5, 0, 'dev')
27 VERSION = (0, 5, 0, 'dev')
28
28
29 __version__ = '.'.join((str(each) for each in VERSION[:4]))
29 __version__ = '.'.join((str(each) for each in VERSION[:4]))
30
30
31 __all__ = [
31 __all__ = [
32 'get_version', 'get_vcs_instance', 'get_backend',
32 'get_version', 'get_vcs_instance', 'get_backend',
33 'VCSError', 'RepositoryError', 'CommitError'
33 'VCSError', 'RepositoryError', 'CommitError'
34 ]
34 ]
35
35
36 import atexit
36 import atexit
37 import logging
37 import logging
38 import subprocess
38 import subprocess
39 import time
39 import time
40 import urlparse
40 import urlparse
41 from cStringIO import StringIO
41 from cStringIO import StringIO
42
42
43 import Pyro4
43 import Pyro4
44 from Pyro4.errors import CommunicationError
44 from Pyro4.errors import CommunicationError
45
45
46 from rhodecode.lib.vcs.conf import settings
46 from rhodecode.lib.vcs.conf import settings
47 from rhodecode.lib.vcs.backends import get_vcs_instance, get_backend
47 from rhodecode.lib.vcs.backends import get_vcs_instance, get_backend
48 from rhodecode.lib.vcs.exceptions import (
48 from rhodecode.lib.vcs.exceptions import (
49 VCSError, RepositoryError, CommitError)
49 VCSError, RepositoryError, CommitError, VCSCommunicationError)
50
50
51 log = logging.getLogger(__name__)
51 log = logging.getLogger(__name__)
52
52
53 # The pycurl library directly accesses C API functions and is not patched by
53 # The pycurl library directly accesses C API functions and is not patched by
54 # gevent. This will potentially lead to deadlocks due to incompatibility to
54 # gevent. This will potentially lead to deadlocks due to incompatibility to
55 # gevent. Therefore we check if gevent is active and import a gevent compatible
55 # gevent. Therefore we check if gevent is active and import a gevent compatible
56 # wrapper in that case.
56 # wrapper in that case.
57 try:
57 try:
58 from gevent import monkey
58 from gevent import monkey
59 if monkey.is_module_patched('__builtin__'):
59 if monkey.is_module_patched('__builtin__'):
60 import geventcurl as pycurl
60 import geventcurl as pycurl
61 log.debug('Using gevent comapatible pycurl: %s', pycurl)
61 log.debug('Using gevent comapatible pycurl: %s', pycurl)
62 else:
62 else:
63 import pycurl
63 import pycurl
64 except ImportError:
64 except ImportError:
65 import pycurl
65 import pycurl
66
66
67
67
68 def get_version():
68 def get_version():
69 """
69 """
70 Returns shorter version (digit parts only) as string.
70 Returns shorter version (digit parts only) as string.
71 """
71 """
72 return '.'.join((str(each) for each in VERSION[:3]))
72 return '.'.join((str(each) for each in VERSION[:3]))
73
73
74
74
75 def connect_pyro4(server_and_port):
75 def connect_pyro4(server_and_port):
76 from rhodecode.lib.vcs import connection, client
76 from rhodecode.lib.vcs import connection, client
77 from rhodecode.lib.middleware.utils import scm_app
77 from rhodecode.lib.middleware.utils import scm_app
78
78
79 git_remote = client.RequestScopeProxyFactory(
79 git_remote = client.RequestScopeProxyFactory(
80 settings.pyro_remote(settings.PYRO_GIT, server_and_port))
80 settings.pyro_remote(settings.PYRO_GIT, server_and_port))
81 hg_remote = client.RequestScopeProxyFactory(
81 hg_remote = client.RequestScopeProxyFactory(
82 settings.pyro_remote(settings.PYRO_HG, server_and_port))
82 settings.pyro_remote(settings.PYRO_HG, server_and_port))
83 svn_remote = client.RequestScopeProxyFactory(
83 svn_remote = client.RequestScopeProxyFactory(
84 settings.pyro_remote(settings.PYRO_SVN, server_and_port))
84 settings.pyro_remote(settings.PYRO_SVN, server_and_port))
85
85
86 connection.Git = client.RepoMaker(proxy_factory=git_remote)
86 connection.Git = client.RepoMaker(proxy_factory=git_remote)
87 connection.Hg = client.RepoMaker(proxy_factory=hg_remote)
87 connection.Hg = client.RepoMaker(proxy_factory=hg_remote)
88 connection.Svn = client.RepoMaker(proxy_factory=svn_remote)
88 connection.Svn = client.RepoMaker(proxy_factory=svn_remote)
89
89
90 scm_app.GIT_REMOTE_WSGI = Pyro4.Proxy(
90 scm_app.GIT_REMOTE_WSGI = Pyro4.Proxy(
91 settings.pyro_remote(
91 settings.pyro_remote(
92 settings.PYRO_GIT_REMOTE_WSGI, server_and_port))
92 settings.PYRO_GIT_REMOTE_WSGI, server_and_port))
93 scm_app.HG_REMOTE_WSGI = Pyro4.Proxy(
93 scm_app.HG_REMOTE_WSGI = Pyro4.Proxy(
94 settings.pyro_remote(
94 settings.pyro_remote(
95 settings.PYRO_HG_REMOTE_WSGI, server_and_port))
95 settings.PYRO_HG_REMOTE_WSGI, server_and_port))
96
96
97 @atexit.register
97 @atexit.register
98 def free_connection_resources():
98 def free_connection_resources():
99 connection.Git = None
99 connection.Git = None
100 connection.Hg = None
100 connection.Hg = None
101 connection.Svn = None
101 connection.Svn = None
102
102
103
103
104 def connect_http(server_and_port):
104 def connect_http(server_and_port):
105 from rhodecode.lib.vcs import connection, client_http
105 from rhodecode.lib.vcs import connection, client_http
106 from rhodecode.lib.middleware.utils import scm_app
106 from rhodecode.lib.middleware.utils import scm_app
107
107
108 session_factory = client_http.ThreadlocalSessionFactory()
108 session_factory = client_http.ThreadlocalSessionFactory()
109
109
110 connection.Git = client_http.RepoMaker(
110 connection.Git = client_http.RepoMaker(
111 server_and_port, '/git', session_factory)
111 server_and_port, '/git', session_factory)
112 connection.Hg = client_http.RepoMaker(
112 connection.Hg = client_http.RepoMaker(
113 server_and_port, '/hg', session_factory)
113 server_and_port, '/hg', session_factory)
114 connection.Svn = client_http.RepoMaker(
114 connection.Svn = client_http.RepoMaker(
115 server_and_port, '/svn', session_factory)
115 server_and_port, '/svn', session_factory)
116
116
117 scm_app.HG_REMOTE_WSGI = client_http.VcsHttpProxy(
117 scm_app.HG_REMOTE_WSGI = client_http.VcsHttpProxy(
118 server_and_port, '/proxy/hg')
118 server_and_port, '/proxy/hg')
119 scm_app.GIT_REMOTE_WSGI = client_http.VcsHttpProxy(
119 scm_app.GIT_REMOTE_WSGI = client_http.VcsHttpProxy(
120 server_and_port, '/proxy/git')
120 server_and_port, '/proxy/git')
121
121
122 @atexit.register
122 @atexit.register
123 def free_connection_resources():
123 def free_connection_resources():
124 connection.Git = None
124 connection.Git = None
125 connection.Hg = None
125 connection.Hg = None
126 connection.Svn = None
126 connection.Svn = None
127
127
128
128
129 def connect_vcs(server_and_port, protocol):
129 def connect_vcs(server_and_port, protocol):
130 """
130 """
131 Initializes the connection to the vcs server.
131 Initializes the connection to the vcs server.
132
132
133 :param server_and_port: str, e.g. "localhost:9900"
133 :param server_and_port: str, e.g. "localhost:9900"
134 :param protocol: str, "pyro4" or "http"
134 :param protocol: str, "pyro4" or "http"
135 """
135 """
136 if protocol == 'pyro4':
136 if protocol == 'pyro4':
137 connect_pyro4(server_and_port)
137 connect_pyro4(server_and_port)
138 elif protocol == 'http':
138 elif protocol == 'http':
139 connect_http(server_and_port)
139 connect_http(server_and_port)
140 else:
140 else:
141 raise Exception('Invalid vcs server protocol "{}"'.format(protocol))
141 raise Exception('Invalid vcs server protocol "{}"'.format(protocol))
142
142
143
143
144 # TODO: johbo: This function should be moved into our test suite, there is
144 # TODO: johbo: This function should be moved into our test suite, there is
145 # no reason to support starting the vcsserver in Enterprise itself.
145 # no reason to support starting the vcsserver in Enterprise itself.
146 def start_vcs_server(server_and_port, protocol, log_level=None):
146 def start_vcs_server(server_and_port, protocol, log_level=None):
147 """
147 """
148 Starts the vcs server in a subprocess.
148 Starts the vcs server in a subprocess.
149 """
149 """
150 log.info('Starting VCSServer as a sub process with %s protocol', protocol)
150 log.info('Starting VCSServer as a sub process with %s protocol', protocol)
151 if protocol == 'http':
151 if protocol == 'http':
152 return _start_http_vcs_server(server_and_port, log_level)
152 return _start_http_vcs_server(server_and_port, log_level)
153 elif protocol == 'pyro4':
153 elif protocol == 'pyro4':
154 return _start_pyro4_vcs_server(server_and_port, log_level)
154 return _start_pyro4_vcs_server(server_and_port, log_level)
155 else:
155 else:
156 raise Exception('Invalid vcs server protocol "{}"'.format(protocol))
156 raise Exception('Invalid vcs server protocol "{}"'.format(protocol))
157
157
158
158
159 def _start_pyro4_vcs_server(server_and_port, log_level=None):
159 def _start_pyro4_vcs_server(server_and_port, log_level=None):
160 _try_to_shutdown_running_server(server_and_port, protocol='pyro4')
160 _try_to_shutdown_running_server(server_and_port, protocol='pyro4')
161 host, port = server_and_port.rsplit(":", 1)
161 host, port = server_and_port.rsplit(":", 1)
162 host = host.strip('[]')
162 host = host.strip('[]')
163 args = [
163 args = [
164 'vcsserver', '--port', port, '--host', host, '--locale', 'en_US.UTF-8',
164 'vcsserver', '--port', port, '--host', host, '--locale', 'en_US.UTF-8',
165 '--threadpool', '32']
165 '--threadpool', '32']
166 if log_level:
166 if log_level:
167 args += ['--log-level', log_level]
167 args += ['--log-level', log_level]
168 proc = subprocess.Popen(args)
168 proc = subprocess.Popen(args)
169
169
170 def cleanup_server_process():
170 def cleanup_server_process():
171 proc.kill()
171 proc.kill()
172 atexit.register(cleanup_server_process)
172 atexit.register(cleanup_server_process)
173
173
174 server = create_vcsserver_proxy(server_and_port, protocol='pyro4')
174 server = create_vcsserver_proxy(server_and_port, protocol='pyro4')
175 _wait_until_vcs_server_is_reachable(server)
175 _wait_until_vcs_server_is_reachable(server)
176
176
177
177
178 def _start_http_vcs_server(server_and_port, log_level=None):
178 def _start_http_vcs_server(server_and_port, log_level=None):
179 # TODO: mikhail: shutdown if an http server already runs
179 # TODO: mikhail: shutdown if an http server already runs
180
180
181 host, port = server_and_port.rsplit(":", 1)
181 host, port = server_and_port.rsplit(":", 1)
182 args = [
182 args = [
183 'pserve', 'vcsserver/development_pyramid.ini',
183 'pserve', 'rhodecode/tests/vcsserver_http.ini',
184 'http_port=%s' % (port, ), 'http_host=%s' % (host, )]
184 'http_port=%s' % (port, ), 'http_host=%s' % (host, )]
185 proc = subprocess.Popen(args)
185 proc = subprocess.Popen(args)
186
186
187 def cleanup_server_process():
187 def cleanup_server_process():
188 proc.kill()
188 proc.kill()
189 atexit.register(cleanup_server_process)
189 atexit.register(cleanup_server_process)
190
190
191 server = create_vcsserver_proxy(server_and_port, protocol='http')
191 server = create_vcsserver_proxy(server_and_port, protocol='http')
192 _wait_until_vcs_server_is_reachable(server)
192 _wait_until_vcs_server_is_reachable(server)
193
193
194
194
195 def _wait_until_vcs_server_is_reachable(server):
195 def _wait_until_vcs_server_is_reachable(server, timeout=40):
196 while xrange(80): # max 40s of sleep
196 begin = time.time()
197 while (time.time() - begin) < timeout:
197 try:
198 try:
198 server.ping()
199 server.ping()
199 break
200 return
200 except (CommunicationError, pycurl.error):
201 except (VCSCommunicationError, CommunicationError, pycurl.error):
201 pass
202 log.debug('VCSServer not started yet, retry to connect.')
202 time.sleep(0.5)
203 time.sleep(0.5)
204 raise Exception(
205 'Starting the VCSServer failed or took more than {} '
206 'seconds.'.format(timeout))
203
207
204
208
205 def _try_to_shutdown_running_server(server_and_port, protocol):
209 def _try_to_shutdown_running_server(server_and_port, protocol):
206 server = create_vcsserver_proxy(server_and_port, protocol)
210 server = create_vcsserver_proxy(server_and_port, protocol)
207 try:
211 try:
208 server.shutdown()
212 server.shutdown()
209 except (CommunicationError, pycurl.error):
213 except (CommunicationError, pycurl.error):
210 return
214 return
211
215
212 # TODO: Not sure why this is important, but without it the following start
216 # TODO: Not sure why this is important, but without it the following start
213 # of the server fails.
217 # of the server fails.
214 server = create_vcsserver_proxy(server_and_port, protocol)
218 server = create_vcsserver_proxy(server_and_port, protocol)
215 server.ping()
219 server.ping()
216
220
217
221
218 def create_vcsserver_proxy(server_and_port, protocol):
222 def create_vcsserver_proxy(server_and_port, protocol):
219 if protocol == 'pyro4':
223 if protocol == 'pyro4':
220 return _create_vcsserver_proxy_pyro4(server_and_port)
224 return _create_vcsserver_proxy_pyro4(server_and_port)
221 elif protocol == 'http':
225 elif protocol == 'http':
222 return _create_vcsserver_proxy_http(server_and_port)
226 return _create_vcsserver_proxy_http(server_and_port)
223 else:
227 else:
224 raise Exception('Invalid vcs server protocol "{}"'.format(protocol))
228 raise Exception('Invalid vcs server protocol "{}"'.format(protocol))
225
229
226
230
227 def _create_vcsserver_proxy_pyro4(server_and_port):
231 def _create_vcsserver_proxy_pyro4(server_and_port):
228 server = Pyro4.Proxy(
232 server = Pyro4.Proxy(
229 settings.pyro_remote(settings.PYRO_VCSSERVER, server_and_port))
233 settings.pyro_remote(settings.PYRO_VCSSERVER, server_and_port))
230 return server
234 return server
231
235
232
236
233 def _create_vcsserver_proxy_http(server_and_port):
237 def _create_vcsserver_proxy_http(server_and_port):
234 from rhodecode.lib.vcs import client_http
238 from rhodecode.lib.vcs import client_http
235
239
236 session = _create_http_rpc_session()
240 session = _create_http_rpc_session()
237 url = urlparse.urljoin('http://%s' % server_and_port, '/server')
241 url = urlparse.urljoin('http://%s' % server_and_port, '/server')
238 return client_http.RemoteObject(url, session)
242 return client_http.RemoteObject(url, session)
239
243
240
244
241 class CurlSession(object):
245 class CurlSession(object):
242 """
246 """
243 Modeled so that it provides a subset of the requests interface.
247 Modeled so that it provides a subset of the requests interface.
244
248
245 This has been created so that it does only provide a minimal API for our
249 This has been created so that it does only provide a minimal API for our
246 needs. The parts which it provides are based on the API of the library
250 needs. The parts which it provides are based on the API of the library
247 `requests` which allows us to easily benchmark against it.
251 `requests` which allows us to easily benchmark against it.
248
252
249 Please have a look at the class :class:`requests.Session` when you extend
253 Please have a look at the class :class:`requests.Session` when you extend
250 it.
254 it.
251 """
255 """
252
256
253 def __init__(self):
257 def __init__(self):
254 curl = pycurl.Curl()
258 curl = pycurl.Curl()
255 # TODO: johbo: I did test with 7.19 of libcurl. This version has
259 # TODO: johbo: I did test with 7.19 of libcurl. This version has
256 # trouble with 100 - continue being set in the expect header. This
260 # trouble with 100 - continue being set in the expect header. This
257 # can lead to massive performance drops, switching it off here.
261 # can lead to massive performance drops, switching it off here.
258 curl.setopt(curl.HTTPHEADER, ["Expect:"])
262 curl.setopt(curl.HTTPHEADER, ["Expect:"])
259 curl.setopt(curl.TCP_NODELAY, True)
263 curl.setopt(curl.TCP_NODELAY, True)
260 curl.setopt(curl.PROTOCOLS, curl.PROTO_HTTP)
264 curl.setopt(curl.PROTOCOLS, curl.PROTO_HTTP)
261 self._curl = curl
265 self._curl = curl
262
266
263 def post(self, url, data, allow_redirects=False):
267 def post(self, url, data, allow_redirects=False):
264 response_buffer = StringIO()
268 response_buffer = StringIO()
265
269
266 curl = self._curl
270 curl = self._curl
267 curl.setopt(curl.URL, url)
271 curl.setopt(curl.URL, url)
268 curl.setopt(curl.POST, True)
272 curl.setopt(curl.POST, True)
269 curl.setopt(curl.POSTFIELDS, data)
273 curl.setopt(curl.POSTFIELDS, data)
270 curl.setopt(curl.FOLLOWLOCATION, allow_redirects)
274 curl.setopt(curl.FOLLOWLOCATION, allow_redirects)
271 curl.setopt(curl.WRITEDATA, response_buffer)
275 curl.setopt(curl.WRITEDATA, response_buffer)
272 curl.perform()
276 curl.perform()
273
277
274 return CurlResponse(response_buffer)
278 return CurlResponse(response_buffer)
275
279
276
280
277 class CurlResponse(object):
281 class CurlResponse(object):
278 """
282 """
279 The response of a request, modeled after the requests API.
283 The response of a request, modeled after the requests API.
280
284
281 This class provides a subset of the response interface known from the
285 This class provides a subset of the response interface known from the
282 library `requests`. It is intentionally kept similar, so that we can use
286 library `requests`. It is intentionally kept similar, so that we can use
283 `requests` as a drop in replacement for benchmarking purposes.
287 `requests` as a drop in replacement for benchmarking purposes.
284 """
288 """
285
289
286 def __init__(self, response_buffer):
290 def __init__(self, response_buffer):
287 self._response_buffer = response_buffer
291 self._response_buffer = response_buffer
288
292
289 @property
293 @property
290 def content(self):
294 def content(self):
291 return self._response_buffer.getvalue()
295 return self._response_buffer.getvalue()
292
296
293
297
294 def _create_http_rpc_session():
298 def _create_http_rpc_session():
295 session = CurlSession()
299 session = CurlSession()
296 return session
300 return session
@@ -1,197 +1,196 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2014-2016 RhodeCode GmbH
3 # Copyright (C) 2014-2016 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 """
21 """
22 Custom vcs exceptions module.
22 Custom vcs exceptions module.
23 """
23 """
24
24
25 import functools
25 import functools
26 import urllib2
26 import urllib2
27 import pycurl
27
28 from Pyro4.errors import CommunicationError
29
28
30 class VCSCommunicationError(Exception):
29 class VCSCommunicationError(Exception):
31 pass
30 pass
32
31
33
32
34 class PyroVCSCommunicationError(VCSCommunicationError):
33 class PyroVCSCommunicationError(VCSCommunicationError):
35 pass
34 pass
36
35
37
36
38 class HttpVCSCommunicationError(VCSCommunicationError):
37 class HttpVCSCommunicationError(VCSCommunicationError):
39 pass
38 pass
40
39
41
40
42 class VCSError(Exception):
41 class VCSError(Exception):
43 pass
42 pass
44
43
45
44
46 class RepositoryError(VCSError):
45 class RepositoryError(VCSError):
47 pass
46 pass
48
47
49
48
50 class RepositoryRequirementError(RepositoryError):
49 class RepositoryRequirementError(RepositoryError):
51 pass
50 pass
52
51
53
52
54 class VCSBackendNotSupportedError(VCSError):
53 class VCSBackendNotSupportedError(VCSError):
55 """
54 """
56 Exception raised when VCSServer does not support requested backend
55 Exception raised when VCSServer does not support requested backend
57 """
56 """
58
57
59
58
60 class EmptyRepositoryError(RepositoryError):
59 class EmptyRepositoryError(RepositoryError):
61 pass
60 pass
62
61
63
62
64 class TagAlreadyExistError(RepositoryError):
63 class TagAlreadyExistError(RepositoryError):
65 pass
64 pass
66
65
67
66
68 class TagDoesNotExistError(RepositoryError):
67 class TagDoesNotExistError(RepositoryError):
69 pass
68 pass
70
69
71
70
72 class BranchAlreadyExistError(RepositoryError):
71 class BranchAlreadyExistError(RepositoryError):
73 pass
72 pass
74
73
75
74
76 class BranchDoesNotExistError(RepositoryError):
75 class BranchDoesNotExistError(RepositoryError):
77 pass
76 pass
78
77
79
78
80 class CommitError(RepositoryError):
79 class CommitError(RepositoryError):
81 """
80 """
82 Exceptions related to an existing commit
81 Exceptions related to an existing commit
83 """
82 """
84
83
85
84
86 class CommitDoesNotExistError(CommitError):
85 class CommitDoesNotExistError(CommitError):
87 pass
86 pass
88
87
89
88
90 class CommittingError(RepositoryError):
89 class CommittingError(RepositoryError):
91 """
90 """
92 Exceptions happening while creating a new commit
91 Exceptions happening while creating a new commit
93 """
92 """
94
93
95
94
96 class NothingChangedError(CommittingError):
95 class NothingChangedError(CommittingError):
97 pass
96 pass
98
97
99
98
100 class NodeError(VCSError):
99 class NodeError(VCSError):
101 pass
100 pass
102
101
103
102
104 class RemovedFileNodeError(NodeError):
103 class RemovedFileNodeError(NodeError):
105 pass
104 pass
106
105
107
106
108 class NodeAlreadyExistsError(CommittingError):
107 class NodeAlreadyExistsError(CommittingError):
109 pass
108 pass
110
109
111
110
112 class NodeAlreadyChangedError(CommittingError):
111 class NodeAlreadyChangedError(CommittingError):
113 pass
112 pass
114
113
115
114
116 class NodeDoesNotExistError(CommittingError):
115 class NodeDoesNotExistError(CommittingError):
117 pass
116 pass
118
117
119
118
120 class NodeNotChangedError(CommittingError):
119 class NodeNotChangedError(CommittingError):
121 pass
120 pass
122
121
123
122
124 class NodeAlreadyAddedError(CommittingError):
123 class NodeAlreadyAddedError(CommittingError):
125 pass
124 pass
126
125
127
126
128 class NodeAlreadyRemovedError(CommittingError):
127 class NodeAlreadyRemovedError(CommittingError):
129 pass
128 pass
130
129
131
130
132 class ImproperArchiveTypeError(VCSError):
131 class ImproperArchiveTypeError(VCSError):
133 pass
132 pass
134
133
135
134
136 class CommandError(VCSError):
135 class CommandError(VCSError):
137 pass
136 pass
138
137
139
138
140 class UnhandledException(VCSError):
139 class UnhandledException(VCSError):
141 """
140 """
142 Signals that something unexpected went wrong.
141 Signals that something unexpected went wrong.
143
142
144 This usually means we have a programming error on the side of the VCSServer
143 This usually means we have a programming error on the side of the VCSServer
145 and should inspect the logfile of the VCSServer to find more details.
144 and should inspect the logfile of the VCSServer to find more details.
146 """
145 """
147
146
148
147
149 _EXCEPTION_MAP = {
148 _EXCEPTION_MAP = {
150 'abort': RepositoryError,
149 'abort': RepositoryError,
151 'archive': ImproperArchiveTypeError,
150 'archive': ImproperArchiveTypeError,
152 'error': RepositoryError,
151 'error': RepositoryError,
153 'lookup': CommitDoesNotExistError,
152 'lookup': CommitDoesNotExistError,
154 'repo_locked': RepositoryError,
153 'repo_locked': RepositoryError,
155 'requirement': RepositoryRequirementError,
154 'requirement': RepositoryRequirementError,
156 'unhandled': UnhandledException,
155 'unhandled': UnhandledException,
157 # TODO: johbo: Define our own exception for this and stop abusing
156 # TODO: johbo: Define our own exception for this and stop abusing
158 # urllib's exception class.
157 # urllib's exception class.
159 'url_error': urllib2.URLError,
158 'url_error': urllib2.URLError,
160 }
159 }
161
160
162
161
163 def map_vcs_exceptions(func):
162 def map_vcs_exceptions(func):
164 """
163 """
165 Utility to decorate functions so that plain exceptions are translated.
164 Utility to decorate functions so that plain exceptions are translated.
166
165
167 The translation is based on `exc_map` which maps a `str` indicating
166 The translation is based on `exc_map` which maps a `str` indicating
168 the error type into an exception class representing this error inside
167 the error type into an exception class representing this error inside
169 of the vcs layer.
168 of the vcs layer.
170 """
169 """
171
170
172 @functools.wraps(func)
171 @functools.wraps(func)
173 def wrapper(*args, **kwargs):
172 def wrapper(*args, **kwargs):
174 try:
173 try:
175 return func(*args, **kwargs)
174 return func(*args, **kwargs)
176 except Exception as e:
175 except Exception as e:
177 # The error middleware adds information if it finds
176 # The error middleware adds information if it finds
178 # __traceback_info__ in a frame object. This way the remote
177 # __traceback_info__ in a frame object. This way the remote
179 # traceback information is made available in error reports.
178 # traceback information is made available in error reports.
180 remote_tb = getattr(e, '_pyroTraceback', None)
179 remote_tb = getattr(e, '_pyroTraceback', None)
181 if remote_tb:
180 if remote_tb:
182 __traceback_info__ = (
181 __traceback_info__ = (
183 'Found Pyro4 remote traceback information:\n\n' +
182 'Found Pyro4 remote traceback information:\n\n' +
184 '\n'.join(remote_tb))
183 '\n'.join(remote_tb))
185
184
186 # Avoid that remote_tb also appears in the frame
185 # Avoid that remote_tb also appears in the frame
187 del remote_tb
186 del remote_tb
188
187
189 # Special vcs errors had an attribute "_vcs_kind" which is used
188 # Special vcs errors had an attribute "_vcs_kind" which is used
190 # to translate them to the proper exception class in the vcs
189 # to translate them to the proper exception class in the vcs
191 # client layer.
190 # client layer.
192 kind = getattr(e, '_vcs_kind', None)
191 kind = getattr(e, '_vcs_kind', None)
193 if kind:
192 if kind:
194 raise _EXCEPTION_MAP[kind](*e.args)
193 raise _EXCEPTION_MAP[kind](*e.args)
195 else:
194 else:
196 raise
195 raise
197 return wrapper
196 return wrapper
General Comments 0
You need to be logged in to leave comments. Login now