statichttprepo.py
134 lines
| 4.3 KiB
| text/x-python
|
PythonLexer
/ mercurial / statichttprepo.py
mpm@selenic.com
|
r1101 | # statichttprepo.py - simple http repository class for mercurial | ||
# | ||||
# This provides read-only repo access to repositories exported via static http | ||||
# | ||||
Thomas Arendsen Hein
|
r4635 | # Copyright 2005-2007 Matt Mackall <mpm@selenic.com> | ||
mpm@selenic.com
|
r1101 | # | ||
Martin Geisler
|
r8225 | # This software may be used and distributed according to the terms of the | ||
# GNU General Public License version 2, incorporated herein by reference. | ||||
mpm@selenic.com
|
r1101 | |||
Matt Mackall
|
r3891 | from i18n import _ | ||
Matt Mackall
|
r7637 | import changelog, byterange, url, error | ||
Peter Arrenbrecht
|
r7873 | import localrepo, manifest, util, store | ||
Dirkjan Ochtman
|
r6028 | import urllib, urllib2, errno | ||
Bryan O'Sullivan
|
r1325 | |||
Benoit Boissinot
|
r7274 | class httprangereader(object): | ||
def __init__(self, url, opener): | ||||
# we assume opener has HTTPRangeHandler | ||||
self.url = url | ||||
self.pos = 0 | ||||
self.opener = opener | ||||
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)) | ||||
Bryan O'Sullivan
|
r1325 | try: | ||
Benoit Boissinot
|
r7274 | f = self.opener.open(req) | ||
data = f.read() | ||||
Patrick Mezard
|
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 | ||||
Thomas Arendsen Hein
|
r1821 | except urllib2.HTTPError, inst: | ||
Dirkjan Ochtman
|
r6028 | num = inst.code == 404 and errno.ENOENT or None | ||
raise IOError(num, inst) | ||||
Bryan O'Sullivan
|
r1325 | except urllib2.URLError, inst: | ||
Thomas Arendsen Hein
|
r1821 | raise IOError(None, inst.reason[1]) | ||
mpm@selenic.com
|
r1101 | |||
Patrick Mezard
|
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: | ||||
Benoit Boissinot
|
r7274 | data = data[:bytes] | ||
Patrick Mezard
|
r8612 | self.pos += len(data) | ||
Benoit Boissinot
|
r7274 | return data | ||
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 | ||||
def o(path, mode="r"): | ||||
f = "/".join((p, urllib.quote(path))) | ||||
return httprangereader(f, urlopener) | ||||
return o | ||||
return opener | ||||
mpm@selenic.com
|
r1101 | |||
class statichttprepository(localrepo.localrepository): | ||||
def __init__(self, ui, path): | ||||
Vadim Gelfer
|
r2673 | self._url = path | ||
mpm@selenic.com
|
r1101 | self.ui = ui | ||
Benoit Boissinot
|
r3853 | |||
Benoit Boissinot
|
r7274 | self.path, authinfo = url.getauthinfo(path.rstrip('/') + "/.hg") | ||
opener = build_opener(ui, authinfo) | ||||
mpm@selenic.com
|
r1101 | self.opener = opener(self.path) | ||
Dirkjan Ochtman
|
r6028 | |||
Benoit Boissinot
|
r3851 | # find requirements | ||
try: | ||||
requirements = self.opener("requires").read().splitlines() | ||||
Dirkjan Ochtman
|
r6028 | except IOError, inst: | ||
Thomas Arendsen Hein
|
r7178 | if inst.errno != errno.ENOENT: | ||
raise | ||||
# check if it is a non-empty old-style repository | ||||
try: | ||||
self.opener("00changelog.i").read(1) | ||||
except IOError, inst: | ||||
if inst.errno != errno.ENOENT: | ||||
raise | ||||
# we do not care about empty old-style repositories here | ||||
Dirkjan Ochtman
|
r6028 | msg = _("'%s' does not appear to be an hg repository") % path | ||
Matt Mackall
|
r7637 | raise error.RepoError(msg) | ||
Thomas Arendsen Hein
|
r7178 | requirements = [] | ||
Dirkjan Ochtman
|
r6028 | |||
Benoit Boissinot
|
r3851 | # check them | ||
for r in requirements: | ||||
if r not in self.supported: | ||||
Matt Mackall
|
r7637 | raise error.RepoError(_("requirement '%s' not supported") % r) | ||
Benoit Boissinot
|
r3851 | |||
# setup store | ||||
Adrian Buehlmann
|
r6988 | def pjoin(a, b): | ||
return a + '/' + b | ||||
self.store = store.store(requirements, self.path, opener, pjoin) | ||||
Matt Mackall
|
r6897 | self.spath = self.store.path | ||
self.sopener = self.store.opener | ||||
self.sjoin = self.store.join | ||||
Benoit Boissinot
|
r3851 | |||
Benoit Boissinot
|
r3791 | self.manifest = manifest.manifest(self.sopener) | ||
self.changelog = changelog.changelog(self.sopener) | ||||
Greg Ward
|
r9146 | self._tags = None | ||
mpm@selenic.com
|
r1101 | self.nodetagscache = None | ||
Benoit Boissinot
|
r1598 | self.encodepats = None | ||
self.decodepats = None | ||||
mpm@selenic.com
|
r1101 | |||
Vadim Gelfer
|
r2673 | def url(self): | ||
Matt Mackall
|
r7211 | return self._url | ||
Vadim Gelfer
|
r2673 | |||
mpm@selenic.com
|
r1101 | def local(self): | ||
return False | ||||
Vadim Gelfer
|
r2740 | |||
Martin Geisler
|
r7005 | def lock(self, wait=True): | ||
raise util.Abort(_('cannot lock static-http repository')) | ||||
Vadim Gelfer
|
r2740 | def instance(ui, path, create): | ||
if create: | ||||
raise util.Abort(_('cannot create new static-http repository')) | ||||
Thomas Arendsen Hein
|
r4853 | return statichttprepository(ui, path[7:]) | ||