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