##// END OF EJS Templates
statichttprepo: explicitly convert error message to str (issue6247)...
Joerg Sonnenberger -
r46818:5135b393 default
parent child Browse files
Show More
@@ -1,255 +1,258
1 # statichttprepo.py - simple http repository class for mercurial
1 # statichttprepo.py - simple http repository class for mercurial
2 #
2 #
3 # This provides read-only repo access to repositories exported via static http
3 # This provides read-only repo access to repositories exported via static http
4 #
4 #
5 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
5 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
6 #
6 #
7 # This software may be used and distributed according to the terms of the
7 # This software may be used and distributed according to the terms of the
8 # GNU General Public License version 2 or any later version.
8 # GNU General Public License version 2 or any later version.
9
9
10 from __future__ import absolute_import
10 from __future__ import absolute_import
11
11
12 import errno
12 import errno
13
13
14 from .i18n import _
14 from .i18n import _
15 from . import (
15 from . import (
16 branchmap,
16 branchmap,
17 changelog,
17 changelog,
18 error,
18 error,
19 localrepo,
19 localrepo,
20 manifest,
20 manifest,
21 namespaces,
21 namespaces,
22 pathutil,
22 pathutil,
23 pycompat,
23 pycompat,
24 url,
24 url,
25 util,
25 util,
26 vfs as vfsmod,
26 vfs as vfsmod,
27 )
27 )
28
28
29 urlerr = util.urlerr
29 urlerr = util.urlerr
30 urlreq = util.urlreq
30 urlreq = util.urlreq
31
31
32
32
33 class httprangereader(object):
33 class httprangereader(object):
34 def __init__(self, url, opener):
34 def __init__(self, url, opener):
35 # we assume opener has HTTPRangeHandler
35 # we assume opener has HTTPRangeHandler
36 self.url = url
36 self.url = url
37 self.pos = 0
37 self.pos = 0
38 self.opener = opener
38 self.opener = opener
39 self.name = url
39 self.name = url
40
40
41 def __enter__(self):
41 def __enter__(self):
42 return self
42 return self
43
43
44 def __exit__(self, exc_type, exc_value, traceback):
44 def __exit__(self, exc_type, exc_value, traceback):
45 self.close()
45 self.close()
46
46
47 def seek(self, pos):
47 def seek(self, pos):
48 self.pos = pos
48 self.pos = pos
49
49
50 def read(self, bytes=None):
50 def read(self, bytes=None):
51 req = urlreq.request(pycompat.strurl(self.url))
51 req = urlreq.request(pycompat.strurl(self.url))
52 end = b''
52 end = b''
53 if bytes:
53 if bytes:
54 end = self.pos + bytes - 1
54 end = self.pos + bytes - 1
55 if self.pos or end:
55 if self.pos or end:
56 req.add_header('Range', 'bytes=%d-%s' % (self.pos, end))
56 req.add_header('Range', 'bytes=%d-%s' % (self.pos, end))
57
57
58 try:
58 try:
59 f = self.opener.open(req)
59 f = self.opener.open(req)
60 data = f.read()
60 data = f.read()
61 code = f.code
61 code = f.code
62 except urlerr.httperror as inst:
62 except urlerr.httperror as inst:
63 num = inst.code == 404 and errno.ENOENT or None
63 num = inst.code == 404 and errno.ENOENT or None
64 raise IOError(num, inst)
64 # Explicitly convert the exception to str as Py3 will try
65 # convert it to local encoding and with as the HTTPResponse
66 # instance doesn't support encode.
67 raise IOError(num, str(inst))
65 except urlerr.urlerror as inst:
68 except urlerr.urlerror as inst:
66 raise IOError(None, inst.reason)
69 raise IOError(None, inst.reason)
67
70
68 if code == 200:
71 if code == 200:
69 # HTTPRangeHandler does nothing if remote does not support
72 # HTTPRangeHandler does nothing if remote does not support
70 # Range headers and returns the full entity. Let's slice it.
73 # Range headers and returns the full entity. Let's slice it.
71 if bytes:
74 if bytes:
72 data = data[self.pos : self.pos + bytes]
75 data = data[self.pos : self.pos + bytes]
73 else:
76 else:
74 data = data[self.pos :]
77 data = data[self.pos :]
75 elif bytes:
78 elif bytes:
76 data = data[:bytes]
79 data = data[:bytes]
77 self.pos += len(data)
80 self.pos += len(data)
78 return data
81 return data
79
82
80 def readlines(self):
83 def readlines(self):
81 return self.read().splitlines(True)
84 return self.read().splitlines(True)
82
85
83 def __iter__(self):
86 def __iter__(self):
84 return iter(self.readlines())
87 return iter(self.readlines())
85
88
86 def close(self):
89 def close(self):
87 pass
90 pass
88
91
89
92
90 # _RangeError and _HTTPRangeHandler were originally in byterange.py,
93 # _RangeError and _HTTPRangeHandler were originally in byterange.py,
91 # which was itself extracted from urlgrabber. See the last version of
94 # which was itself extracted from urlgrabber. See the last version of
92 # byterange.py from history if you need more information.
95 # byterange.py from history if you need more information.
93 class _RangeError(IOError):
96 class _RangeError(IOError):
94 """Error raised when an unsatisfiable range is requested."""
97 """Error raised when an unsatisfiable range is requested."""
95
98
96
99
97 class _HTTPRangeHandler(urlreq.basehandler):
100 class _HTTPRangeHandler(urlreq.basehandler):
98 """Handler that enables HTTP Range headers.
101 """Handler that enables HTTP Range headers.
99
102
100 This was extremely simple. The Range header is a HTTP feature to
103 This was extremely simple. The Range header is a HTTP feature to
101 begin with so all this class does is tell urllib2 that the
104 begin with so all this class does is tell urllib2 that the
102 "206 Partial Content" response from the HTTP server is what we
105 "206 Partial Content" response from the HTTP server is what we
103 expected.
106 expected.
104 """
107 """
105
108
106 def http_error_206(self, req, fp, code, msg, hdrs):
109 def http_error_206(self, req, fp, code, msg, hdrs):
107 # 206 Partial Content Response
110 # 206 Partial Content Response
108 r = urlreq.addinfourl(fp, hdrs, req.get_full_url())
111 r = urlreq.addinfourl(fp, hdrs, req.get_full_url())
109 r.code = code
112 r.code = code
110 r.msg = msg
113 r.msg = msg
111 return r
114 return r
112
115
113 def http_error_416(self, req, fp, code, msg, hdrs):
116 def http_error_416(self, req, fp, code, msg, hdrs):
114 # HTTP's Range Not Satisfiable error
117 # HTTP's Range Not Satisfiable error
115 raise _RangeError(b'Requested Range Not Satisfiable')
118 raise _RangeError(b'Requested Range Not Satisfiable')
116
119
117
120
118 def build_opener(ui, authinfo):
121 def build_opener(ui, authinfo):
119 # urllib cannot handle URLs with embedded user or passwd
122 # urllib cannot handle URLs with embedded user or passwd
120 urlopener = url.opener(ui, authinfo)
123 urlopener = url.opener(ui, authinfo)
121 urlopener.add_handler(_HTTPRangeHandler())
124 urlopener.add_handler(_HTTPRangeHandler())
122
125
123 class statichttpvfs(vfsmod.abstractvfs):
126 class statichttpvfs(vfsmod.abstractvfs):
124 def __init__(self, base):
127 def __init__(self, base):
125 self.base = base
128 self.base = base
126 self.options = {}
129 self.options = {}
127
130
128 def __call__(self, path, mode=b'r', *args, **kw):
131 def __call__(self, path, mode=b'r', *args, **kw):
129 if mode not in (b'r', b'rb'):
132 if mode not in (b'r', b'rb'):
130 raise IOError(b'Permission denied')
133 raise IOError(b'Permission denied')
131 f = b"/".join((self.base, urlreq.quote(path)))
134 f = b"/".join((self.base, urlreq.quote(path)))
132 return httprangereader(f, urlopener)
135 return httprangereader(f, urlopener)
133
136
134 def join(self, path):
137 def join(self, path):
135 if path:
138 if path:
136 return pathutil.join(self.base, path)
139 return pathutil.join(self.base, path)
137 else:
140 else:
138 return self.base
141 return self.base
139
142
140 return statichttpvfs
143 return statichttpvfs
141
144
142
145
143 class statichttppeer(localrepo.localpeer):
146 class statichttppeer(localrepo.localpeer):
144 def local(self):
147 def local(self):
145 return None
148 return None
146
149
147 def canpush(self):
150 def canpush(self):
148 return False
151 return False
149
152
150
153
151 class statichttprepository(
154 class statichttprepository(
152 localrepo.localrepository, localrepo.revlogfilestorage
155 localrepo.localrepository, localrepo.revlogfilestorage
153 ):
156 ):
154 supported = localrepo.localrepository._basesupported
157 supported = localrepo.localrepository._basesupported
155
158
156 def __init__(self, ui, path):
159 def __init__(self, ui, path):
157 self._url = path
160 self._url = path
158 self.ui = ui
161 self.ui = ui
159
162
160 self.root = path
163 self.root = path
161 u = util.url(path.rstrip(b'/') + b"/.hg")
164 u = util.url(path.rstrip(b'/') + b"/.hg")
162 self.path, authinfo = u.authinfo()
165 self.path, authinfo = u.authinfo()
163
166
164 vfsclass = build_opener(ui, authinfo)
167 vfsclass = build_opener(ui, authinfo)
165 self.vfs = vfsclass(self.path)
168 self.vfs = vfsclass(self.path)
166 self.cachevfs = vfsclass(self.vfs.join(b'cache'))
169 self.cachevfs = vfsclass(self.vfs.join(b'cache'))
167 self._phasedefaults = []
170 self._phasedefaults = []
168
171
169 self.names = namespaces.namespaces()
172 self.names = namespaces.namespaces()
170 self.filtername = None
173 self.filtername = None
171 self._extrafilterid = None
174 self._extrafilterid = None
172
175
173 try:
176 try:
174 requirements = set(self.vfs.read(b'requires').splitlines())
177 requirements = set(self.vfs.read(b'requires').splitlines())
175 except IOError as inst:
178 except IOError as inst:
176 if inst.errno != errno.ENOENT:
179 if inst.errno != errno.ENOENT:
177 raise
180 raise
178 requirements = set()
181 requirements = set()
179
182
180 # check if it is a non-empty old-style repository
183 # check if it is a non-empty old-style repository
181 try:
184 try:
182 fp = self.vfs(b"00changelog.i")
185 fp = self.vfs(b"00changelog.i")
183 fp.read(1)
186 fp.read(1)
184 fp.close()
187 fp.close()
185 except IOError as inst:
188 except IOError as inst:
186 if inst.errno != errno.ENOENT:
189 if inst.errno != errno.ENOENT:
187 raise
190 raise
188 # we do not care about empty old-style repositories here
191 # we do not care about empty old-style repositories here
189 msg = _(b"'%s' does not appear to be an hg repository") % path
192 msg = _(b"'%s' does not appear to be an hg repository") % path
190 raise error.RepoError(msg)
193 raise error.RepoError(msg)
191
194
192 supportedrequirements = localrepo.gathersupportedrequirements(ui)
195 supportedrequirements = localrepo.gathersupportedrequirements(ui)
193 localrepo.ensurerequirementsrecognized(
196 localrepo.ensurerequirementsrecognized(
194 requirements, supportedrequirements
197 requirements, supportedrequirements
195 )
198 )
196 localrepo.ensurerequirementscompatible(ui, requirements)
199 localrepo.ensurerequirementscompatible(ui, requirements)
197
200
198 # setup store
201 # setup store
199 self.store = localrepo.makestore(requirements, self.path, vfsclass)
202 self.store = localrepo.makestore(requirements, self.path, vfsclass)
200 self.spath = self.store.path
203 self.spath = self.store.path
201 self.svfs = self.store.opener
204 self.svfs = self.store.opener
202 self.sjoin = self.store.join
205 self.sjoin = self.store.join
203 self._filecache = {}
206 self._filecache = {}
204 self.requirements = requirements
207 self.requirements = requirements
205
208
206 rootmanifest = manifest.manifestrevlog(self.svfs)
209 rootmanifest = manifest.manifestrevlog(self.svfs)
207 self.manifestlog = manifest.manifestlog(
210 self.manifestlog = manifest.manifestlog(
208 self.svfs, self, rootmanifest, self.narrowmatch()
211 self.svfs, self, rootmanifest, self.narrowmatch()
209 )
212 )
210 self.changelog = changelog.changelog(self.svfs)
213 self.changelog = changelog.changelog(self.svfs)
211 self._tags = None
214 self._tags = None
212 self.nodetagscache = None
215 self.nodetagscache = None
213 self._branchcaches = branchmap.BranchMapCache()
216 self._branchcaches = branchmap.BranchMapCache()
214 self._revbranchcache = None
217 self._revbranchcache = None
215 self.encodepats = None
218 self.encodepats = None
216 self.decodepats = None
219 self.decodepats = None
217 self._transref = None
220 self._transref = None
218
221
219 def _restrictcapabilities(self, caps):
222 def _restrictcapabilities(self, caps):
220 caps = super(statichttprepository, self)._restrictcapabilities(caps)
223 caps = super(statichttprepository, self)._restrictcapabilities(caps)
221 return caps.difference([b"pushkey"])
224 return caps.difference([b"pushkey"])
222
225
223 def url(self):
226 def url(self):
224 return self._url
227 return self._url
225
228
226 def local(self):
229 def local(self):
227 return False
230 return False
228
231
229 def peer(self):
232 def peer(self):
230 return statichttppeer(self)
233 return statichttppeer(self)
231
234
232 def wlock(self, wait=True):
235 def wlock(self, wait=True):
233 raise error.LockUnavailable(
236 raise error.LockUnavailable(
234 0,
237 0,
235 _(b'lock not available'),
238 _(b'lock not available'),
236 b'lock',
239 b'lock',
237 _(b'cannot lock static-http repository'),
240 _(b'cannot lock static-http repository'),
238 )
241 )
239
242
240 def lock(self, wait=True):
243 def lock(self, wait=True):
241 raise error.LockUnavailable(
244 raise error.LockUnavailable(
242 0,
245 0,
243 _(b'lock not available'),
246 _(b'lock not available'),
244 b'lock',
247 b'lock',
245 _(b'cannot lock static-http repository'),
248 _(b'cannot lock static-http repository'),
246 )
249 )
247
250
248 def _writecaches(self):
251 def _writecaches(self):
249 pass # statichttprepository are read only
252 pass # statichttprepository are read only
250
253
251
254
252 def instance(ui, path, create, intents=None, createopts=None):
255 def instance(ui, path, create, intents=None, createopts=None):
253 if create:
256 if create:
254 raise error.Abort(_(b'cannot create new static-http repository'))
257 raise error.Abort(_(b'cannot create new static-http repository'))
255 return statichttprepository(ui, path[7:])
258 return statichttprepository(ui, path[7:])
General Comments 0
You need to be logged in to leave comments. Login now