##// END OF EJS Templates
vfs: use 'vfs' module directly in 'mercurial.statichttprepo'...
Pierre-Yves David -
r31241:591fda75 default
parent child Browse files
Show More
@@ -1,189 +1,190 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 import os
13 import os
14
14
15 from .i18n import _
15 from .i18n import _
16 from . import (
16 from . import (
17 byterange,
17 byterange,
18 changelog,
18 changelog,
19 error,
19 error,
20 localrepo,
20 localrepo,
21 manifest,
21 manifest,
22 namespaces,
22 namespaces,
23 scmutil,
23 scmutil,
24 store,
24 store,
25 url,
25 url,
26 util,
26 util,
27 vfs as vfsmod,
27 )
28 )
28
29
29 urlerr = util.urlerr
30 urlerr = util.urlerr
30 urlreq = util.urlreq
31 urlreq = util.urlreq
31
32
32 class httprangereader(object):
33 class httprangereader(object):
33 def __init__(self, url, opener):
34 def __init__(self, url, opener):
34 # we assume opener has HTTPRangeHandler
35 # we assume opener has HTTPRangeHandler
35 self.url = url
36 self.url = url
36 self.pos = 0
37 self.pos = 0
37 self.opener = opener
38 self.opener = opener
38 self.name = url
39 self.name = url
39
40
40 def __enter__(self):
41 def __enter__(self):
41 return self
42 return self
42
43
43 def __exit__(self, exc_type, exc_value, traceback):
44 def __exit__(self, exc_type, exc_value, traceback):
44 self.close()
45 self.close()
45
46
46 def seek(self, pos):
47 def seek(self, pos):
47 self.pos = pos
48 self.pos = pos
48 def read(self, bytes=None):
49 def read(self, bytes=None):
49 req = urlreq.request(self.url)
50 req = urlreq.request(self.url)
50 end = ''
51 end = ''
51 if bytes:
52 if bytes:
52 end = self.pos + bytes - 1
53 end = self.pos + bytes - 1
53 if self.pos or end:
54 if self.pos or end:
54 req.add_header('Range', 'bytes=%d-%s' % (self.pos, end))
55 req.add_header('Range', 'bytes=%d-%s' % (self.pos, end))
55
56
56 try:
57 try:
57 f = self.opener.open(req)
58 f = self.opener.open(req)
58 data = f.read()
59 data = f.read()
59 code = f.code
60 code = f.code
60 except urlerr.httperror as inst:
61 except urlerr.httperror as inst:
61 num = inst.code == 404 and errno.ENOENT or None
62 num = inst.code == 404 and errno.ENOENT or None
62 raise IOError(num, inst)
63 raise IOError(num, inst)
63 except urlerr.urlerror as inst:
64 except urlerr.urlerror as inst:
64 raise IOError(None, inst.reason[1])
65 raise IOError(None, inst.reason[1])
65
66
66 if code == 200:
67 if code == 200:
67 # HTTPRangeHandler does nothing if remote does not support
68 # HTTPRangeHandler does nothing if remote does not support
68 # Range headers and returns the full entity. Let's slice it.
69 # Range headers and returns the full entity. Let's slice it.
69 if bytes:
70 if bytes:
70 data = data[self.pos:self.pos + bytes]
71 data = data[self.pos:self.pos + bytes]
71 else:
72 else:
72 data = data[self.pos:]
73 data = data[self.pos:]
73 elif bytes:
74 elif bytes:
74 data = data[:bytes]
75 data = data[:bytes]
75 self.pos += len(data)
76 self.pos += len(data)
76 return data
77 return data
77 def readlines(self):
78 def readlines(self):
78 return self.read().splitlines(True)
79 return self.read().splitlines(True)
79 def __iter__(self):
80 def __iter__(self):
80 return iter(self.readlines())
81 return iter(self.readlines())
81 def close(self):
82 def close(self):
82 pass
83 pass
83
84
84 def build_opener(ui, authinfo):
85 def build_opener(ui, authinfo):
85 # urllib cannot handle URLs with embedded user or passwd
86 # urllib cannot handle URLs with embedded user or passwd
86 urlopener = url.opener(ui, authinfo)
87 urlopener = url.opener(ui, authinfo)
87 urlopener.add_handler(byterange.HTTPRangeHandler())
88 urlopener.add_handler(byterange.HTTPRangeHandler())
88
89
89 class statichttpvfs(scmutil.abstractvfs):
90 class statichttpvfs(vfsmod.abstractvfs):
90 def __init__(self, base):
91 def __init__(self, base):
91 self.base = base
92 self.base = base
92
93
93 def __call__(self, path, mode='r', *args, **kw):
94 def __call__(self, path, mode='r', *args, **kw):
94 if mode not in ('r', 'rb'):
95 if mode not in ('r', 'rb'):
95 raise IOError('Permission denied')
96 raise IOError('Permission denied')
96 f = "/".join((self.base, urlreq.quote(path)))
97 f = "/".join((self.base, urlreq.quote(path)))
97 return httprangereader(f, urlopener)
98 return httprangereader(f, urlopener)
98
99
99 def join(self, path):
100 def join(self, path):
100 if path:
101 if path:
101 return os.path.join(self.base, path)
102 return os.path.join(self.base, path)
102 else:
103 else:
103 return self.base
104 return self.base
104
105
105 return statichttpvfs
106 return statichttpvfs
106
107
107 class statichttppeer(localrepo.localpeer):
108 class statichttppeer(localrepo.localpeer):
108 def local(self):
109 def local(self):
109 return None
110 return None
110 def canpush(self):
111 def canpush(self):
111 return False
112 return False
112
113
113 class statichttprepository(localrepo.localrepository):
114 class statichttprepository(localrepo.localrepository):
114 supported = localrepo.localrepository._basesupported
115 supported = localrepo.localrepository._basesupported
115
116
116 def __init__(self, ui, path):
117 def __init__(self, ui, path):
117 self._url = path
118 self._url = path
118 self.ui = ui
119 self.ui = ui
119
120
120 self.root = path
121 self.root = path
121 u = util.url(path.rstrip('/') + "/.hg")
122 u = util.url(path.rstrip('/') + "/.hg")
122 self.path, authinfo = u.authinfo()
123 self.path, authinfo = u.authinfo()
123
124
124 vfsclass = build_opener(ui, authinfo)
125 vfsclass = build_opener(ui, authinfo)
125 self.vfs = vfsclass(self.path)
126 self.vfs = vfsclass(self.path)
126 self._phasedefaults = []
127 self._phasedefaults = []
127
128
128 self.names = namespaces.namespaces()
129 self.names = namespaces.namespaces()
129
130
130 try:
131 try:
131 requirements = scmutil.readrequires(self.vfs, self.supported)
132 requirements = scmutil.readrequires(self.vfs, self.supported)
132 except IOError as inst:
133 except IOError as inst:
133 if inst.errno != errno.ENOENT:
134 if inst.errno != errno.ENOENT:
134 raise
135 raise
135 requirements = set()
136 requirements = set()
136
137
137 # check if it is a non-empty old-style repository
138 # check if it is a non-empty old-style repository
138 try:
139 try:
139 fp = self.vfs("00changelog.i")
140 fp = self.vfs("00changelog.i")
140 fp.read(1)
141 fp.read(1)
141 fp.close()
142 fp.close()
142 except IOError as inst:
143 except IOError as inst:
143 if inst.errno != errno.ENOENT:
144 if inst.errno != errno.ENOENT:
144 raise
145 raise
145 # we do not care about empty old-style repositories here
146 # we do not care about empty old-style repositories here
146 msg = _("'%s' does not appear to be an hg repository") % path
147 msg = _("'%s' does not appear to be an hg repository") % path
147 raise error.RepoError(msg)
148 raise error.RepoError(msg)
148
149
149 # setup store
150 # setup store
150 self.store = store.store(requirements, self.path, vfsclass)
151 self.store = store.store(requirements, self.path, vfsclass)
151 self.spath = self.store.path
152 self.spath = self.store.path
152 self.svfs = self.store.opener
153 self.svfs = self.store.opener
153 self.sjoin = self.store.join
154 self.sjoin = self.store.join
154 self._filecache = {}
155 self._filecache = {}
155 self.requirements = requirements
156 self.requirements = requirements
156
157
157 self.manifestlog = manifest.manifestlog(self.svfs, self)
158 self.manifestlog = manifest.manifestlog(self.svfs, self)
158 self.changelog = changelog.changelog(self.svfs)
159 self.changelog = changelog.changelog(self.svfs)
159 self._tags = None
160 self._tags = None
160 self.nodetagscache = None
161 self.nodetagscache = None
161 self._branchcaches = {}
162 self._branchcaches = {}
162 self._revbranchcache = None
163 self._revbranchcache = None
163 self.encodepats = None
164 self.encodepats = None
164 self.decodepats = None
165 self.decodepats = None
165 self._transref = None
166 self._transref = None
166
167
167 def _restrictcapabilities(self, caps):
168 def _restrictcapabilities(self, caps):
168 caps = super(statichttprepository, self)._restrictcapabilities(caps)
169 caps = super(statichttprepository, self)._restrictcapabilities(caps)
169 return caps.difference(["pushkey"])
170 return caps.difference(["pushkey"])
170
171
171 def url(self):
172 def url(self):
172 return self._url
173 return self._url
173
174
174 def local(self):
175 def local(self):
175 return False
176 return False
176
177
177 def peer(self):
178 def peer(self):
178 return statichttppeer(self)
179 return statichttppeer(self)
179
180
180 def lock(self, wait=True):
181 def lock(self, wait=True):
181 raise error.Abort(_('cannot lock static-http repository'))
182 raise error.Abort(_('cannot lock static-http repository'))
182
183
183 def _writecaches(self):
184 def _writecaches(self):
184 pass # statichttprepository are read only
185 pass # statichttprepository are read only
185
186
186 def instance(ui, path, create):
187 def instance(ui, path, create):
187 if create:
188 if create:
188 raise error.Abort(_('cannot create new static-http repository'))
189 raise error.Abort(_('cannot create new static-http repository'))
189 return statichttprepository(ui, path[7:])
190 return statichttprepository(ui, path[7:])
General Comments 0
You need to be logged in to leave comments. Login now