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