statichttprepo.py
166 lines
| 5.3 KiB
| text/x-python
|
PythonLexer
/ mercurial / statichttprepo.py
|
r1101 | # statichttprepo.py - simple http repository class for mercurial | ||
# | ||||
# This provides read-only repo access to repositories exported via static http | ||||
# | ||||
|
r4635 | # Copyright 2005-2007 Matt Mackall <mpm@selenic.com> | ||
|
r1101 | # | ||
|
r8225 | # This software may be used and distributed according to the terms of the | ||
|
r10263 | # GNU General Public License version 2 or any later version. | ||
|
r1101 | |||
|
r3891 | from i18n import _ | ||
|
r23558 | import changelog, byterange, url, error, namespaces | ||
|
r14091 | import localrepo, manifest, util, scmutil, store | ||
|
r18915 | import urllib, urllib2, errno, os | ||
|
r1325 | |||
|
r7274 | class httprangereader(object): | ||
def __init__(self, url, opener): | ||||
# we assume opener has HTTPRangeHandler | ||||
self.url = url | ||||
self.pos = 0 | ||||
self.opener = opener | ||||
|
r11066 | self.name = url | ||
|
r7274 | def seek(self, pos): | ||
self.pos = pos | ||||
def read(self, bytes=None): | ||||
req = urllib2.Request(self.url) | ||||
end = '' | ||||
if bytes: | ||||
end = self.pos + bytes - 1 | ||||
|
r16882 | if self.pos or end: | ||
req.add_header('Range', 'bytes=%d-%s' % (self.pos, end)) | ||||
|
r7274 | |||
|
r1325 | try: | ||
|
r7274 | f = self.opener.open(req) | ||
data = f.read() | ||||
|
r14962 | # Python 2.6+ defines a getcode() function, and 2.4 and | ||
# 2.5 appear to always have an undocumented code attribute | ||||
# set. If we can't read either of those, fall back to 206 | ||||
# and hope for the best. | ||||
code = getattr(f, 'getcode', lambda : getattr(f, 'code', 206))() | ||||
|
r1821 | except urllib2.HTTPError, inst: | ||
|
r6028 | num = inst.code == 404 and errno.ENOENT or None | ||
raise IOError(num, inst) | ||||
|
r1325 | except urllib2.URLError, inst: | ||
|
r1821 | raise IOError(None, inst.reason[1]) | ||
|
r1101 | |||
|
r8612 | if code == 200: | ||
# HTTPRangeHandler does nothing if remote does not support | ||||
# Range headers and returns the full entity. Let's slice it. | ||||
if bytes: | ||||
data = data[self.pos:self.pos + bytes] | ||||
else: | ||||
data = data[self.pos:] | ||||
elif bytes: | ||||
|
r7274 | data = data[:bytes] | ||
|
r8612 | self.pos += len(data) | ||
|
r7274 | return data | ||
|
r20055 | def readlines(self): | ||
return self.read().splitlines(True) | ||||
|
r11066 | def __iter__(self): | ||
|
r20055 | return iter(self.readlines()) | ||
|
r11066 | def close(self): | ||
pass | ||||
|
r7274 | |||
def build_opener(ui, authinfo): | ||||
# urllib cannot handle URLs with embedded user or passwd | ||||
urlopener = url.opener(ui, authinfo) | ||||
urlopener.add_handler(byterange.HTTPRangeHandler()) | ||||
|
r17649 | class statichttpvfs(scmutil.abstractvfs): | ||
|
r14091 | def __init__(self, base): | ||
self.base = base | ||||
|
r23552 | def __call__(self, path, mode='r', *args, **kw): | ||
|
r13533 | if mode not in ('r', 'rb'): | ||
|
r11066 | raise IOError('Permission denied') | ||
|
r14091 | f = "/".join((self.base, urllib.quote(path))) | ||
|
r7274 | return httprangereader(f, urlopener) | ||
|
r17725 | def join(self, path): | ||
if path: | ||||
return os.path.join(self.base, path) | ||||
else: | ||||
return self.base | ||||
|
r17649 | return statichttpvfs | ||
|
r1101 | |||
|
r17192 | class statichttppeer(localrepo.localpeer): | ||
def local(self): | ||||
return None | ||||
|
r17193 | def canpush(self): | ||
return False | ||||
|
r17192 | |||
|
r1101 | class statichttprepository(localrepo.localrepository): | ||
|
r19778 | supported = localrepo.localrepository._basesupported | ||
|
r1101 | def __init__(self, ui, path): | ||
|
r2673 | self._url = path | ||
|
r1101 | self.ui = ui | ||
|
r3853 | |||
|
r11066 | self.root = path | ||
|
r14076 | u = util.url(path.rstrip('/') + "/.hg") | ||
|
r13819 | self.path, authinfo = u.authinfo() | ||
|
r7274 | |||
opener = build_opener(ui, authinfo) | ||||
|
r1101 | self.opener = opener(self.path) | ||
|
r17156 | self.vfs = self.opener | ||
|
r15922 | self._phasedefaults = [] | ||
|
r6028 | |||
|
r23561 | self.names = namespaces.namespaces() | ||
|
r23558 | |||
|
r3851 | try: | ||
|
r14482 | requirements = scmutil.readrequires(self.opener, self.supported) | ||
|
r6028 | except IOError, inst: | ||
|
r7178 | if inst.errno != errno.ENOENT: | ||
raise | ||||
|
r14482 | requirements = set() | ||
|
r7178 | # check if it is a non-empty old-style repository | ||
try: | ||||
|
r13400 | fp = self.opener("00changelog.i") | ||
fp.read(1) | ||||
fp.close() | ||||
|
r7178 | except IOError, inst: | ||
if inst.errno != errno.ENOENT: | ||||
raise | ||||
# we do not care about empty old-style repositories here | ||||
|
r6028 | msg = _("'%s' does not appear to be an hg repository") % path | ||
|
r7637 | raise error.RepoError(msg) | ||
|
r3851 | |||
# setup store | ||||
|
r13426 | self.store = store.store(requirements, self.path, opener) | ||
|
r6897 | self.spath = self.store.path | ||
self.sopener = self.store.opener | ||||
|
r17156 | self.svfs = self.sopener | ||
|
r6897 | self.sjoin = self.store.join | ||
|
r16115 | self._filecache = {} | ||
|
r17192 | self.requirements = requirements | ||
|
r3851 | |||
|
r3791 | self.manifest = manifest.manifest(self.sopener) | ||
self.changelog = changelog.changelog(self.sopener) | ||||
|
r9146 | self._tags = None | ||
|
r1101 | self.nodetagscache = None | ||
|
r18189 | self._branchcaches = {} | ||
|
r1598 | self.encodepats = None | ||
self.decodepats = None | ||||
|
r17192 | |||
def _restrictcapabilities(self, caps): | ||||
|
r20962 | caps = super(statichttprepository, self)._restrictcapabilities(caps) | ||
|
r17192 | return caps.difference(["pushkey"]) | ||
|
r1101 | |||
|
r2673 | def url(self): | ||
|
r7211 | return self._url | ||
|
r2673 | |||
|
r1101 | def local(self): | ||
return False | ||||
|
r2740 | |||
|
r17192 | def peer(self): | ||
return statichttppeer(self) | ||||
|
r7005 | def lock(self, wait=True): | ||
raise util.Abort(_('cannot lock static-http repository')) | ||||
|
r2740 | def instance(ui, path, create): | ||
if create: | ||||
raise util.Abort(_('cannot create new static-http repository')) | ||||
|
r4853 | return statichttprepository(ui, path[7:]) | ||