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