73
73
multiple requests. They generally have a dumb base implementation
74
74
which just iterates over the arguments, but smart Transport
75
75
implementations can do pipelining.
76
In general implementations should support having a generator or a list
77
as an argument (ie always iterate, never index)
79
TODO: Worry about file encodings. For instance bzr control files should
80
all be encoded in utf-8, but read as local encoding.
78
83
def __init__(self, base):
86
def clone(self, offset=None):
87
"""Return a new Transport object, cloned from the current location,
88
using a subdirectory. This allows connections to be pooled,
89
rather than a new one needed for each subdir.
91
raise NotImplementedError
81
93
def _pump(self, from_file, to_file):
82
94
"""Most children will need to copy from one file-like
83
95
object or string to another one.
89
101
from bzrlib.osutils import pumpfile
90
102
pumpfile(from_file, to_file)
104
def _get_total(self, multi):
105
"""Try to figure out how many entries are in multi,
106
but if not possible, return None.
110
except TypeError: # We can't tell how many, because relpaths is a generator
113
def _update_pb(self, pb, msg, count, total):
114
"""Update the progress bar based on the current count
115
and total available, total may be None if it was
116
not possible to determine.
119
pb.update(msg, count, count+1)
121
pb.update(msg, count, total)
123
def _iterate_over(self, multi, func, pb, msg):
124
"""Iterate over all entries in multi, passing them to func,
125
and update the progress bar as you go along.
127
total = self._get_total(multi)
130
self._update_pb(pb, msg, count, total)
92
135
def has(self, relpath):
93
136
"""Does the target location exist?"""
94
137
raise NotImplementedError
99
142
raise NotImplementedError
101
def get_multi(self, relpaths):
144
def abspath(self, *args):
145
"""Return the full url to the given relative path.
146
This can be supplied with multiple arguments
148
raise NotImplementedError
150
def get_multi(self, relpaths, pb=None):
102
151
"""Get a list of file-like objects, one for each entry in relpaths.
104
153
:param relpaths: A list of relative paths.
105
:return: A list of file-like objects.
154
:param pb: An optional ProgressBar for indicating percent done.
155
:return: A list or generator of file-like objects
157
# TODO: Consider having this actually buffer the requests,
158
# in the default mode, it probably won't give worse performance,
159
# and all children wouldn't have to implement buffering
160
total = self._get_total(multi)
108
162
for relpath in relpaths:
109
files.append(self.get(relpath))
163
self._update_pb(pb, msg, count, total)
164
yield self.get(relpath)
112
168
def put(self, relpath, f):
115
171
raise NotImplementedError
117
def put_multi(self, files):
173
def put_multi(self, files, pb=None):
118
174
"""Put a set of files or strings into the location.
120
176
:param files: A list of tuples of relpath, file object [(path1, file1), (path2, file2),...]
122
for path, f in files:
177
:param pb: An optional ProgressBar for indicating percent done.
178
:return: The number of files copied.
180
return self._iterate_over(files, self.put, pb, 'put')
182
def mkdir(self, relpath):
183
"""Create a directory at the given path."""
184
raise NotImplementedError
186
def open(self, relpath, mode='wb'):
187
"""Open a remote file for writing.
188
This may return a proxy object, which is written to locally, and
189
then when the file is closed, it is uploaded using put()
191
raise NotImplementedError
125
193
def append(self, relpath, f):
126
194
"""Append the text in the file-like or string object to
131
199
def append_multi(self, files):
132
200
"""Append the text in each file-like or string object to
133
201
the supplied location.
203
:param files: A set of (path, f) entries
204
:param pb: An optional ProgressBar for indicating percent done.
135
for path, f in files:
206
return self._iterate_over(files, self.append, pb, 'append')
138
208
def copy(self, rel_from, rel_to):
139
209
"""Copy the item at rel_from to the location at rel_to"""
147
217
# This is the non-pipelined implementation, so that
148
218
# implementors don't have to implement everything.
149
for rel_from, rel_to in relpaths:
150
self.copy(rel_from, rel_to)
219
return self._iterate_over(relpaths, self.copy, pb, 'copy')
152
221
def move(self, rel_from, rel_to):
153
222
"""Move the item at rel_from to the location at rel_to"""
159
228
:param relpaths: A list of tuples of the form [(from1, to1), (from2, to2),...]
161
for rel_from, rel_to in relpaths:
162
self.move(rel_from, rel_to)
230
return self._iterate_over(relpaths, self.move, pb, 'move')
164
232
def move_multi_to(self, relpaths, rel_to):
165
233
"""Move a bunch of entries to a single location.
180
248
def delete_multi(self, relpaths):
181
249
"""Queue up a bunch of deletes to be done.
183
for relpath in relpaths:
251
return self._iterate_over(relpaths, self.delete, pb, 'delete')
253
def stat(self, relpath):
254
"""Return the stat information for a file.
255
WARNING: This may not be implementable for all protocols, so use
258
raise NotImplementedError
260
def stat_multi(self, relpaths, pb=None):
261
"""Stat multiple files and return the information.
263
#TODO: Is it worth making this a generator instead of a
267
stats.append(self.stat(path))
269
count = self._iterate_over(relpaths, gather, pb, 'stat')
186
273
def async_get(self, relpath):
187
274
"""Make a request for an file at the given location, but