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