19
19
from bzrlib.transport import Transport, register_transport
20
20
from bzrlib.errors import (TransportNotPossible, NoSuchFile,
21
NonRelativePath, TransportError)
21
NonRelativePath, TransportError, ConnectionError)
23
23
from cStringIO import StringIO
77
74
"""Return the full url to the given relative path.
78
75
This can be supplied with a string or a list
77
assert isinstance(relpath, basestring)
80
78
if isinstance(relpath, basestring):
79
relpath_parts = relpath.split('/')
81
# TODO: Don't call this with an array - no magic interfaces
82
relpath_parts = relpath[:]
83
if len(relpath_parts) > 1:
84
if relpath_parts[0] == '':
85
raise ValueError("path %r within branch %r seems to be absolute"
86
% (relpath, self._path))
87
if relpath_parts[-1] == '':
88
raise ValueError("path %r within branch %r seems to be a directory"
89
% (relpath, self._path))
82
90
basepath = self._path.split('/')
83
91
if len(basepath) > 0 and basepath[-1] == '':
84
92
basepath = basepath[:-1]
93
for p in relpath_parts:
95
if len(basepath) == 0:
89
96
# In most filesystems, a request for the parent
90
97
# of root, just returns root.
100
elif p == '.' or p == '':
96
103
basepath.append(p)
98
104
# Possibly, we could use urlparse.urljoin() here, but
99
105
# I'm concerned about when it chooses to strip the last
100
106
# portion of the path, and when it doesn't.
102
108
return urlparse.urlunparse((self._proto,
103
109
self._host, path, '', '', ''))
105
def relpath(self, abspath):
106
if not abspath.startswith(self.base):
107
raise NonRelativePath('path %r is not under base URL %r'
108
% (abspath, self.base))
110
return abspath[pl:].lstrip('/')
112
111
def has(self, relpath):
113
112
"""Does the target location exist?
145
144
return get_url(self.abspath(relpath))
146
except (BzrError, urllib2.URLError, IOError), e:
147
raise NoSuchFile(orig_error=e)
149
raise HttpTransportError(orig_error=e)
151
def get_partial(self, relpath, start, length=None):
152
"""Get just part of a file.
154
:param relpath: Path to the file, relative to base
155
:param start: The starting position to read from
156
:param length: The length to read. A length of None indicates
157
read to the end of the file.
158
:return: A file-like object containing at least the specified bytes.
159
Some implementations may return objects which can be read
160
past this length, but this is not guaranteed.
162
# TODO: You can make specialized http requests for just
163
# a portion of the file. Figure out how to do that.
164
# For now, urllib2 returns files that cannot seek() so
165
# we just read bytes off the beginning, until we
166
# get to the point that we care about.
167
f = self.get(relpath)
168
# TODO: read in smaller chunks, in case things are
169
# buffered internally.
145
except urllib2.HTTPError, e:
147
raise NoSuchFile(msg = "Error retrieving %s: %s"
148
% (self.abspath(relpath), str(e)),
151
except (BzrError, IOError), e:
152
raise ConnectionError(msg = "Error retrieving %s: %s"
153
% (self.abspath(relpath), str(e)),
173
156
def put(self, relpath, f):
174
157
"""Copy the file-like or string object into the location.
216
199
"""Delete the item at relpath"""
217
200
raise TransportNotPossible('http does not support delete()')
219
def list_dir(self, relpath):
220
"""Return a list of all files at the given location.
221
WARNING: many transports do not support this, so trying avoid using
222
it if at all possible.
224
raise TransportNotPossible('http does not support list_dir()')
203
"""See Transport.listable."""
226
206
def stat(self, relpath):
227
207
"""Return the stat information for a file.
248
228
:return: A lock object, which should be passed to Transport.unlock()
250
230
raise TransportNotPossible('http does not support lock_write()')
252
register_transport('http://', HttpTransport)
253
register_transport('https://', HttpTransport)