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