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