statichttprepo.py
147 lines
| 4.8 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 _ | ||
|
r7637 | import changelog, byterange, url, error | ||
|
r7873 | import localrepo, manifest, util, store | ||
|
r6028 | import urllib, urllib2, errno | ||
|
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 | ||||
req.add_header('Range', 'bytes=%d-%s' % (self.pos, end)) | ||||
|
r1325 | try: | ||
|
r7274 | f = self.opener.open(req) | ||
data = f.read() | ||||
|
r8612 | if hasattr(f, 'getcode'): | ||
# python 2.6+ | ||||
code = f.getcode() | ||||
elif hasattr(f, 'code'): | ||||
# undocumented attribute, seems to be set in 2.4 and 2.5 | ||||
code = f.code | ||||
else: | ||||
# Don't know how to check, hope for the best. | ||||
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 | ||
|
r11066 | def __iter__(self): | ||
return iter(self.read().splitlines(1)) | ||||
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()) | ||||
def opener(base): | ||||
"""return a function that opens files over http""" | ||||
p = base | ||||
|
r11066 | def o(path, mode="r", atomictemp=None): | ||
|
r13533 | if mode not in ('r', 'rb'): | ||
|
r11066 | raise IOError('Permission denied') | ||
|
r7274 | f = "/".join((p, urllib.quote(path))) | ||
return httprangereader(f, urlopener) | ||||
return o | ||||
return opener | ||||
|
r1101 | |||
class statichttprepository(localrepo.localrepository): | ||||
def __init__(self, ui, path): | ||||
|
r2673 | self._url = path | ||
|
r1101 | self.ui = ui | ||
|
r3853 | |||
|
r11066 | self.root = path | ||
|
r13819 | u = url.url(path.rstrip('/') + "/.hg") | ||
self.path, authinfo = u.authinfo() | ||||
|
r7274 | |||
opener = build_opener(ui, authinfo) | ||||
|
r1101 | self.opener = opener(self.path) | ||
|
r6028 | |||
|
r3851 | # find requirements | ||
try: | ||||
requirements = self.opener("requires").read().splitlines() | ||||
|
r6028 | except IOError, inst: | ||
|
r7178 | if inst.errno != errno.ENOENT: | ||
raise | ||||
# 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) | ||
|
r7178 | requirements = [] | ||
|
r6028 | |||
|
r3851 | # check them | ||
for r in requirements: | ||||
if r not in self.supported: | ||||
|
r13447 | raise error.RequirementError( | ||
_("requirement '%s' not supported") % r) | ||||
|
r3851 | |||
# setup store | ||||
|
r13426 | self.store = store.store(requirements, self.path, opener) | ||
|
r6897 | self.spath = self.store.path | ||
self.sopener = self.store.opener | ||||
self.sjoin = self.store.join | ||||
|
r3851 | |||
|
r3791 | self.manifest = manifest.manifest(self.sopener) | ||
self.changelog = changelog.changelog(self.sopener) | ||||
|
r9146 | self._tags = None | ||
|
r1101 | self.nodetagscache = None | ||
|
r11066 | self._branchcache = None | ||
self._branchcachetip = None | ||||
|
r1598 | self.encodepats = None | ||
self.decodepats = None | ||||
|
r13440 | self.capabilities = self.capabilities.difference(["pushkey"]) | ||
|
r1101 | |||
|
r2673 | def url(self): | ||
|
r7211 | return self._url | ||
|
r2673 | |||
|
r1101 | def local(self): | ||
return False | ||||
|
r2740 | |||
|
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:]) | ||