1185.11.19
by John Arbash Meinel
Testing put and append, also testing agaist file-like objects as well as strings. |
1 |
# Copyright (C) 2005 Canonical Ltd
|
2 |
||
3 |
# This program is free software; you can redistribute it and/or modify
|
|
4 |
# it under the terms of the GNU General Public License as published by
|
|
5 |
# the Free Software Foundation; either version 2 of the License, or
|
|
6 |
# (at your option) any later version.
|
|
7 |
||
8 |
# This program is distributed in the hope that it will be useful,
|
|
9 |
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
10 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
11 |
# GNU General Public License for more details.
|
|
12 |
||
13 |
# You should have received a copy of the GNU General Public License
|
|
14 |
# along with this program; if not, write to the Free Software
|
|
15 |
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
16 |
"""Transport is an abstraction layer to handle file access.
|
|
17 |
||
18 |
The abstraction is to allow access from the local filesystem, as well
|
|
19 |
as remote (such as http or sftp).
|
|
907.1.1
by John Arbash Meinel
Reworking the Branch and Store code to support an abstracted filesystem layer. |
20 |
"""
|
21 |
||
1530.1.1
by Robert Collins
Minimal infrastructure to test TransportTestProviderAdapter. |
22 |
import errno |
23 |
from copy import deepcopy |
|
1530.1.7
by Robert Collins
merge integration. |
24 |
import sys |
1530.1.1
by Robert Collins
Minimal infrastructure to test TransportTestProviderAdapter. |
25 |
from unittest import TestSuite |
26 |
||
907.1.45
by John Arbash Meinel
Switch to registering protocol handlers, rather than just updating a dictionary. |
27 |
from bzrlib.trace import mutter |
1185.31.44
by John Arbash Meinel
Cleaned up Exceptions for all transports. |
28 |
import bzrlib.errors as errors |
907.1.45
by John Arbash Meinel
Switch to registering protocol handlers, rather than just updating a dictionary. |
29 |
|
30 |
_protocol_handlers = { |
|
907.1.1
by John Arbash Meinel
Reworking the Branch and Store code to support an abstracted filesystem layer. |
31 |
}
|
32 |
||
907.1.45
by John Arbash Meinel
Switch to registering protocol handlers, rather than just updating a dictionary. |
33 |
def register_transport(prefix, klass, override=True): |
34 |
global _protocol_handlers |
|
1185.16.17
by Martin Pool
- avoid warning about log not being registered during startup |
35 |
# trace messages commented out because they're typically
|
36 |
# run during import before trace is set up
|
|
907.1.45
by John Arbash Meinel
Switch to registering protocol handlers, rather than just updating a dictionary. |
37 |
if _protocol_handlers.has_key(prefix): |
38 |
if override: |
|
1185.16.17
by Martin Pool
- avoid warning about log not being registered during startup |
39 |
## mutter('overriding transport: %s => %s' % (prefix, klass.__name__))
|
907.1.45
by John Arbash Meinel
Switch to registering protocol handlers, rather than just updating a dictionary. |
40 |
_protocol_handlers[prefix] = klass |
41 |
else: |
|
1185.16.17
by Martin Pool
- avoid warning about log not being registered during startup |
42 |
## mutter('registering transport: %s => %s' % (prefix, klass.__name__))
|
907.1.45
by John Arbash Meinel
Switch to registering protocol handlers, rather than just updating a dictionary. |
43 |
_protocol_handlers[prefix] = klass |
44 |
||
1442.1.24
by Robert Collins
Pull up _check_id and _relpath from Text and CompressedText stores into TransportStore |
45 |
|
1530.1.11
by Robert Collins
Push the transport permutations list into each transport module allowing for automatic testing of new modules that are registered as transports. |
46 |
def _get_protocol_handlers(): |
47 |
"""Return a dictionary of prefix:transport-factories."""
|
|
48 |
return _protocol_handlers |
|
49 |
||
50 |
||
51 |
def _set_protocol_handlers(new_handlers): |
|
52 |
"""Replace the current protocol handlers dictionary.
|
|
53 |
||
54 |
WARNING this will remove all build in protocols. Use with care.
|
|
55 |
"""
|
|
56 |
global _protocol_handlers |
|
57 |
_protocol_handlers = new_handlers |
|
58 |
||
59 |
||
60 |
def _get_transport_modules(): |
|
61 |
"""Return a list of the modules providing transports."""
|
|
62 |
modules = set() |
|
63 |
for prefix, factory in _protocol_handlers.items(): |
|
64 |
if factory.__module__ == "bzrlib.transport": |
|
65 |
# this is a lazy load transport, because no real ones
|
|
66 |
# are directlry in bzrlib.transport
|
|
67 |
modules.add(factory.module) |
|
68 |
else: |
|
69 |
modules.add(factory.__module__) |
|
70 |
return list(modules) |
|
71 |
||
72 |
||
907.1.1
by John Arbash Meinel
Reworking the Branch and Store code to support an abstracted filesystem layer. |
73 |
class Transport(object): |
74 |
"""This class encapsulates methods for retrieving or putting a file
|
|
75 |
from/to a storage location.
|
|
76 |
||
77 |
Most functions have a _multi variant, which allows you to queue up
|
|
78 |
multiple requests. They generally have a dumb base implementation
|
|
79 |
which just iterates over the arguments, but smart Transport
|
|
80 |
implementations can do pipelining.
|
|
907.1.2
by John Arbash Meinel
Working on making Branch() do all of it's work over a Transport. |
81 |
In general implementations should support having a generator or a list
|
82 |
as an argument (ie always iterate, never index)
|
|
907.1.1
by John Arbash Meinel
Reworking the Branch and Store code to support an abstracted filesystem layer. |
83 |
"""
|
84 |
||
85 |
def __init__(self, base): |
|
907.1.50
by John Arbash Meinel
Removed encode/decode from Transport.put/get, added more exceptions that can be thrown. |
86 |
super(Transport, self).__init__() |
907.1.1
by John Arbash Meinel
Reworking the Branch and Store code to support an abstracted filesystem layer. |
87 |
self.base = base |
88 |
||
1185.31.44
by John Arbash Meinel
Cleaned up Exceptions for all transports. |
89 |
def _translate_error(self, e, path, raise_generic=True): |
90 |
"""Translate an IOError or OSError into an appropriate bzr error.
|
|
91 |
||
92 |
This handles things like ENOENT, ENOTDIR, EEXIST, and EACCESS
|
|
93 |
"""
|
|
94 |
if hasattr(e, 'errno'): |
|
95 |
if e.errno in (errno.ENOENT, errno.ENOTDIR): |
|
96 |
raise errors.NoSuchFile(path, extra=e) |
|
1185.31.58
by John Arbash Meinel
Updating for new transport tests so that they pass on win32 |
97 |
# I would rather use errno.EFOO, but there doesn't seem to be
|
98 |
# any matching for 267
|
|
99 |
# This is the error when doing a listdir on a file:
|
|
100 |
# WindowsError: [Errno 267] The directory name is invalid
|
|
101 |
if sys.platform == 'win32' and e.errno in (errno.ESRCH, 267): |
|
102 |
raise errors.NoSuchFile(path, extra=e) |
|
1185.31.44
by John Arbash Meinel
Cleaned up Exceptions for all transports. |
103 |
if e.errno == errno.EEXIST: |
104 |
raise errors.FileExists(path, extra=e) |
|
105 |
if e.errno == errno.EACCES: |
|
106 |
raise errors.PermissionDenied(path, extra=e) |
|
107 |
if raise_generic: |
|
108 |
raise errors.TransportError(orig_error=e) |
|
109 |
||
907.1.2
by John Arbash Meinel
Working on making Branch() do all of it's work over a Transport. |
110 |
def clone(self, offset=None): |
111 |
"""Return a new Transport object, cloned from the current location,
|
|
1185.11.6
by John Arbash Meinel
Made HttpTransport handle a request for a parent directory differently. |
112 |
using a subdirectory or parent directory. This allows connections
|
113 |
to be pooled, rather than a new one needed for each subdir.
|
|
907.1.2
by John Arbash Meinel
Working on making Branch() do all of it's work over a Transport. |
114 |
"""
|
115 |
raise NotImplementedError |
|
116 |
||
907.1.32
by John Arbash Meinel
Renaming is_remote to should_cache as it is more appropriate. |
117 |
def should_cache(self): |
118 |
"""Return True if the data pulled across should be cached locally.
|
|
907.1.22
by John Arbash Meinel
Fixed some encoding issues, added is_remote function for Transport objects. |
119 |
"""
|
907.1.32
by John Arbash Meinel
Renaming is_remote to should_cache as it is more appropriate. |
120 |
return False |
907.1.22
by John Arbash Meinel
Fixed some encoding issues, added is_remote function for Transport objects. |
121 |
|
907.1.1
by John Arbash Meinel
Reworking the Branch and Store code to support an abstracted filesystem layer. |
122 |
def _pump(self, from_file, to_file): |
123 |
"""Most children will need to copy from one file-like
|
|
124 |
object or string to another one.
|
|
125 |
This just gives them something easy to call.
|
|
126 |
"""
|
|
127 |
if isinstance(from_file, basestring): |
|
907.1.8
by John Arbash Meinel
Changed the format for abspath. Updated branch to use a hidden _transport |
128 |
to_file.write(from_file) |
907.1.1
by John Arbash Meinel
Reworking the Branch and Store code to support an abstracted filesystem layer. |
129 |
else: |
130 |
from bzrlib.osutils import pumpfile |
|
131 |
pumpfile(from_file, to_file) |
|
132 |
||
907.1.2
by John Arbash Meinel
Working on making Branch() do all of it's work over a Transport. |
133 |
def _get_total(self, multi): |
134 |
"""Try to figure out how many entries are in multi,
|
|
135 |
but if not possible, return None.
|
|
136 |
"""
|
|
137 |
try: |
|
138 |
return len(multi) |
|
139 |
except TypeError: # We can't tell how many, because relpaths is a generator |
|
140 |
return None |
|
141 |
||
142 |
def _update_pb(self, pb, msg, count, total): |
|
143 |
"""Update the progress bar based on the current count
|
|
144 |
and total available, total may be None if it was
|
|
145 |
not possible to determine.
|
|
146 |
"""
|
|
907.1.14
by John Arbash Meinel
Handling some transport functions which take only a single argument. |
147 |
if pb is None: |
148 |
return
|
|
907.1.2
by John Arbash Meinel
Working on making Branch() do all of it's work over a Transport. |
149 |
if total is None: |
150 |
pb.update(msg, count, count+1) |
|
151 |
else: |
|
152 |
pb.update(msg, count, total) |
|
153 |
||
907.1.14
by John Arbash Meinel
Handling some transport functions which take only a single argument. |
154 |
def _iterate_over(self, multi, func, pb, msg, expand=True): |
907.1.2
by John Arbash Meinel
Working on making Branch() do all of it's work over a Transport. |
155 |
"""Iterate over all entries in multi, passing them to func,
|
156 |
and update the progress bar as you go along.
|
|
907.1.14
by John Arbash Meinel
Handling some transport functions which take only a single argument. |
157 |
|
158 |
:param expand: If True, the entries will be passed to the function
|
|
159 |
by expanding the tuple. If False, it will be passed
|
|
160 |
as a single parameter.
|
|
907.1.2
by John Arbash Meinel
Working on making Branch() do all of it's work over a Transport. |
161 |
"""
|
162 |
total = self._get_total(multi) |
|
163 |
count = 0 |
|
164 |
for entry in multi: |
|
165 |
self._update_pb(pb, msg, count, total) |
|
907.1.14
by John Arbash Meinel
Handling some transport functions which take only a single argument. |
166 |
if expand: |
167 |
func(*entry) |
|
168 |
else: |
|
169 |
func(entry) |
|
907.1.2
by John Arbash Meinel
Working on making Branch() do all of it's work over a Transport. |
170 |
count += 1 |
171 |
return count |
|
172 |
||
907.1.34
by John Arbash Meinel
Fixing append(), cleaning up function locations. |
173 |
def abspath(self, relpath): |
174 |
"""Return the full url to the given relative path.
|
|
175 |
This can be supplied with a string or a list
|
|
1442.1.44
by Robert Collins
Many transport related tweaks: |
176 |
|
177 |
XXX: Robert Collins 20051016 - is this really needed in the public
|
|
178 |
interface ?
|
|
907.1.34
by John Arbash Meinel
Fixing append(), cleaning up function locations. |
179 |
"""
|
180 |
raise NotImplementedError |
|
181 |
||
182 |
def relpath(self, abspath): |
|
183 |
"""Return the local path portion from a given absolute path.
|
|
1442.1.44
by Robert Collins
Many transport related tweaks: |
184 |
|
185 |
This default implementation is not suitable for filesystems with
|
|
186 |
aliasing, such as that given by symlinks, where a path may not
|
|
187 |
start with our base, but still be a relpath once aliasing is
|
|
188 |
resolved.
|
|
907.1.34
by John Arbash Meinel
Fixing append(), cleaning up function locations. |
189 |
"""
|
1185.31.44
by John Arbash Meinel
Cleaned up Exceptions for all transports. |
190 |
# TODO: This might want to use bzrlib.osutils.relpath
|
191 |
# but we have to watch out because of the prefix issues
|
|
1530.1.3
by Robert Collins
transport implementations now tested consistently. |
192 |
if not (abspath == self.base[:-1] or abspath.startswith(self.base)): |
1185.31.44
by John Arbash Meinel
Cleaned up Exceptions for all transports. |
193 |
raise errors.PathNotChild(abspath, self.base) |
1442.1.44
by Robert Collins
Many transport related tweaks: |
194 |
pl = len(self.base) |
1530.1.3
by Robert Collins
transport implementations now tested consistently. |
195 |
return abspath[pl:].strip('/') |
907.1.34
by John Arbash Meinel
Fixing append(), cleaning up function locations. |
196 |
|
907.1.1
by John Arbash Meinel
Reworking the Branch and Store code to support an abstracted filesystem layer. |
197 |
def has(self, relpath): |
1442.1.44
by Robert Collins
Many transport related tweaks: |
198 |
"""Does the file relpath exist?
|
199 |
|
|
200 |
Note that some transports MAY allow querying on directories, but this
|
|
201 |
is not part of the protocol.
|
|
202 |
"""
|
|
907.1.1
by John Arbash Meinel
Reworking the Branch and Store code to support an abstracted filesystem layer. |
203 |
raise NotImplementedError |
204 |
||
907.1.36
by John Arbash Meinel
Moving the multi-get functionality higher up into the Branch class. |
205 |
def has_multi(self, relpaths, pb=None): |
907.1.34
by John Arbash Meinel
Fixing append(), cleaning up function locations. |
206 |
"""Return True/False for each entry in relpaths"""
|
207 |
total = self._get_total(relpaths) |
|
208 |
count = 0 |
|
209 |
for relpath in relpaths: |
|
907.1.36
by John Arbash Meinel
Moving the multi-get functionality higher up into the Branch class. |
210 |
self._update_pb(pb, 'has', count, total) |
907.1.34
by John Arbash Meinel
Fixing append(), cleaning up function locations. |
211 |
yield self.has(relpath) |
212 |
count += 1 |
|
213 |
||
1185.16.155
by John Arbash Meinel
Added a has_any function to the Transport API |
214 |
def has_any(self, relpaths): |
215 |
"""Return True if any of the paths exist."""
|
|
216 |
for relpath in relpaths: |
|
217 |
if self.has(relpath): |
|
218 |
return True |
|
219 |
return False |
|
220 |
||
1442.1.44
by Robert Collins
Many transport related tweaks: |
221 |
def iter_files_recursive(self): |
222 |
"""Iter the relative paths of files in the transports sub-tree.
|
|
223 |
|
|
224 |
As with other listing functions, only some transports implement this,.
|
|
225 |
you may check via is_listable to determine if it will.
|
|
226 |
"""
|
|
1530.1.4
by Robert Collins
integrate Memory tests into transport interface tests. |
227 |
raise errors.TransportNotPossible("This transport has not " |
228 |
"implemented iter_files_recursive."
|
|
229 |
"(but must claim to be listable "
|
|
230 |
"to trigger this error).") |
|
1442.1.44
by Robert Collins
Many transport related tweaks: |
231 |
|
907.1.50
by John Arbash Meinel
Removed encode/decode from Transport.put/get, added more exceptions that can be thrown. |
232 |
def get(self, relpath): |
907.1.1
by John Arbash Meinel
Reworking the Branch and Store code to support an abstracted filesystem layer. |
233 |
"""Get the file at the given relative path.
|
907.1.20
by John Arbash Meinel
Removed Transport.open(), making get + put encode/decode to utf-8 |
234 |
|
235 |
:param relpath: The relative path to the file
|
|
907.1.1
by John Arbash Meinel
Reworking the Branch and Store code to support an abstracted filesystem layer. |
236 |
"""
|
237 |
raise NotImplementedError |
|
238 |
||
907.1.50
by John Arbash Meinel
Removed encode/decode from Transport.put/get, added more exceptions that can be thrown. |
239 |
def get_multi(self, relpaths, pb=None): |
907.1.1
by John Arbash Meinel
Reworking the Branch and Store code to support an abstracted filesystem layer. |
240 |
"""Get a list of file-like objects, one for each entry in relpaths.
|
241 |
||
242 |
:param relpaths: A list of relative paths.
|
|
907.1.2
by John Arbash Meinel
Working on making Branch() do all of it's work over a Transport. |
243 |
:param pb: An optional ProgressBar for indicating percent done.
|
244 |
:return: A list or generator of file-like objects
|
|
907.1.1
by John Arbash Meinel
Reworking the Branch and Store code to support an abstracted filesystem layer. |
245 |
"""
|
907.1.2
by John Arbash Meinel
Working on making Branch() do all of it's work over a Transport. |
246 |
# TODO: Consider having this actually buffer the requests,
|
247 |
# in the default mode, it probably won't give worse performance,
|
|
248 |
# and all children wouldn't have to implement buffering
|
|
907.1.16
by John Arbash Meinel
Fixing a few cut&paste typos. |
249 |
total = self._get_total(relpaths) |
907.1.2
by John Arbash Meinel
Working on making Branch() do all of it's work over a Transport. |
250 |
count = 0 |
907.1.1
by John Arbash Meinel
Reworking the Branch and Store code to support an abstracted filesystem layer. |
251 |
for relpath in relpaths: |
907.1.16
by John Arbash Meinel
Fixing a few cut&paste typos. |
252 |
self._update_pb(pb, 'get', count, total) |
907.1.50
by John Arbash Meinel
Removed encode/decode from Transport.put/get, added more exceptions that can be thrown. |
253 |
yield self.get(relpath) |
907.1.2
by John Arbash Meinel
Working on making Branch() do all of it's work over a Transport. |
254 |
count += 1 |
907.1.1
by John Arbash Meinel
Reworking the Branch and Store code to support an abstracted filesystem layer. |
255 |
|
1185.58.2
by John Arbash Meinel
Added mode to the appropriate transport functions, and tests to make sure they work. |
256 |
def put(self, relpath, f, mode=None): |
907.1.1
by John Arbash Meinel
Reworking the Branch and Store code to support an abstracted filesystem layer. |
257 |
"""Copy the file-like or string object into the location.
|
907.1.20
by John Arbash Meinel
Removed Transport.open(), making get + put encode/decode to utf-8 |
258 |
|
259 |
:param relpath: Location to put the contents, relative to base.
|
|
260 |
:param f: File-like or string object.
|
|
1185.58.2
by John Arbash Meinel
Added mode to the appropriate transport functions, and tests to make sure they work. |
261 |
:param mode: The mode for the newly created file,
|
262 |
None means just use the default
|
|
907.1.1
by John Arbash Meinel
Reworking the Branch and Store code to support an abstracted filesystem layer. |
263 |
"""
|
264 |
raise NotImplementedError |
|
265 |
||
1185.58.2
by John Arbash Meinel
Added mode to the appropriate transport functions, and tests to make sure they work. |
266 |
def put_multi(self, files, mode=None, pb=None): |
1530.1.3
by Robert Collins
transport implementations now tested consistently. |
267 |
"""Put a set of files into the location.
|
907.1.1
by John Arbash Meinel
Reworking the Branch and Store code to support an abstracted filesystem layer. |
268 |
|
269 |
:param files: A list of tuples of relpath, file object [(path1, file1), (path2, file2),...]
|
|
907.1.2
by John Arbash Meinel
Working on making Branch() do all of it's work over a Transport. |
270 |
:param pb: An optional ProgressBar for indicating percent done.
|
1185.58.2
by John Arbash Meinel
Added mode to the appropriate transport functions, and tests to make sure they work. |
271 |
:param mode: The mode for the newly created files
|
907.1.2
by John Arbash Meinel
Working on making Branch() do all of it's work over a Transport. |
272 |
:return: The number of files copied.
|
273 |
"""
|
|
1185.58.2
by John Arbash Meinel
Added mode to the appropriate transport functions, and tests to make sure they work. |
274 |
def put(path, f): |
275 |
self.put(path, f, mode=mode) |
|
276 |
return self._iterate_over(files, put, pb, 'put', expand=True) |
|
907.1.2
by John Arbash Meinel
Working on making Branch() do all of it's work over a Transport. |
277 |
|
1185.58.2
by John Arbash Meinel
Added mode to the appropriate transport functions, and tests to make sure they work. |
278 |
def mkdir(self, relpath, mode=None): |
907.1.2
by John Arbash Meinel
Working on making Branch() do all of it's work over a Transport. |
279 |
"""Create a directory at the given path."""
|
280 |
raise NotImplementedError |
|
281 |
||
1185.58.2
by John Arbash Meinel
Added mode to the appropriate transport functions, and tests to make sure they work. |
282 |
def mkdir_multi(self, relpaths, mode=None, pb=None): |
907.1.47
by John Arbash Meinel
Created mkdir_multi, modified branch to use _multi forms when initializing a branch, creating a full test routine for transports |
283 |
"""Create a group of directories"""
|
1185.58.2
by John Arbash Meinel
Added mode to the appropriate transport functions, and tests to make sure they work. |
284 |
def mkdir(path): |
285 |
self.mkdir(path, mode=mode) |
|
286 |
return self._iterate_over(relpaths, mkdir, pb, 'mkdir', expand=False) |
|
907.1.47
by John Arbash Meinel
Created mkdir_multi, modified branch to use _multi forms when initializing a branch, creating a full test routine for transports |
287 |
|
907.1.50
by John Arbash Meinel
Removed encode/decode from Transport.put/get, added more exceptions that can be thrown. |
288 |
def append(self, relpath, f): |
907.1.1
by John Arbash Meinel
Reworking the Branch and Store code to support an abstracted filesystem layer. |
289 |
"""Append the text in the file-like or string object to
|
290 |
the supplied location.
|
|
291 |
"""
|
|
292 |
raise NotImplementedError |
|
293 |
||
1185.11.19
by John Arbash Meinel
Testing put and append, also testing agaist file-like objects as well as strings. |
294 |
def append_multi(self, files, pb=None): |
907.1.1
by John Arbash Meinel
Reworking the Branch and Store code to support an abstracted filesystem layer. |
295 |
"""Append the text in each file-like or string object to
|
296 |
the supplied location.
|
|
907.1.2
by John Arbash Meinel
Working on making Branch() do all of it's work over a Transport. |
297 |
|
298 |
:param files: A set of (path, f) entries
|
|
299 |
:param pb: An optional ProgressBar for indicating percent done.
|
|
907.1.1
by John Arbash Meinel
Reworking the Branch and Store code to support an abstracted filesystem layer. |
300 |
"""
|
907.1.14
by John Arbash Meinel
Handling some transport functions which take only a single argument. |
301 |
return self._iterate_over(files, self.append, pb, 'append', expand=True) |
907.1.1
by John Arbash Meinel
Reworking the Branch and Store code to support an abstracted filesystem layer. |
302 |
|
303 |
def copy(self, rel_from, rel_to): |
|
1530.1.3
by Robert Collins
transport implementations now tested consistently. |
304 |
"""Copy the item at rel_from to the location at rel_to.
|
305 |
|
|
306 |
Override this for efficiency if a specific transport can do it
|
|
307 |
faster than this default implementation.
|
|
308 |
"""
|
|
309 |
self.put(rel_to, self.get(rel_from)) |
|
907.1.1
by John Arbash Meinel
Reworking the Branch and Store code to support an abstracted filesystem layer. |
310 |
|
907.1.28
by John Arbash Meinel
Added pb to function that were missing, implemented a basic double-dispatch copy_to function. |
311 |
def copy_multi(self, relpaths, pb=None): |
907.1.1
by John Arbash Meinel
Reworking the Branch and Store code to support an abstracted filesystem layer. |
312 |
"""Copy a bunch of entries.
|
313 |
|
|
314 |
:param relpaths: A list of tuples of the form [(from, to), (from, to),...]
|
|
315 |
"""
|
|
316 |
# This is the non-pipelined implementation, so that
|
|
317 |
# implementors don't have to implement everything.
|
|
907.1.14
by John Arbash Meinel
Handling some transport functions which take only a single argument. |
318 |
return self._iterate_over(relpaths, self.copy, pb, 'copy', expand=True) |
907.1.1
by John Arbash Meinel
Reworking the Branch and Store code to support an abstracted filesystem layer. |
319 |
|
1185.58.2
by John Arbash Meinel
Added mode to the appropriate transport functions, and tests to make sure they work. |
320 |
def copy_to(self, relpaths, other, mode=None, pb=None): |
907.1.28
by John Arbash Meinel
Added pb to function that were missing, implemented a basic double-dispatch copy_to function. |
321 |
"""Copy a set of entries from self into another Transport.
|
322 |
||
323 |
:param relpaths: A list/generator of entries to be copied.
|
|
1185.58.2
by John Arbash Meinel
Added mode to the appropriate transport functions, and tests to make sure they work. |
324 |
:param mode: This is the target mode for the newly created files
|
1185.16.156
by John Arbash Meinel
Adding a note about changing copy_to's interface |
325 |
TODO: This interface needs to be updated so that the target location
|
326 |
can be different from the source location.
|
|
907.1.28
by John Arbash Meinel
Added pb to function that were missing, implemented a basic double-dispatch copy_to function. |
327 |
"""
|
328 |
# The dummy implementation just does a simple get + put
|
|
329 |
def copy_entry(path): |
|
1185.58.2
by John Arbash Meinel
Added mode to the appropriate transport functions, and tests to make sure they work. |
330 |
other.put(path, self.get(path), mode=mode) |
907.1.28
by John Arbash Meinel
Added pb to function that were missing, implemented a basic double-dispatch copy_to function. |
331 |
|
332 |
return self._iterate_over(relpaths, copy_entry, pb, 'copy_to', expand=False) |
|
333 |
||
907.1.57
by John Arbash Meinel
Trying to get pipelined http library working + tests. |
334 |
|
907.1.1
by John Arbash Meinel
Reworking the Branch and Store code to support an abstracted filesystem layer. |
335 |
def move(self, rel_from, rel_to): |
1530.1.3
by Robert Collins
transport implementations now tested consistently. |
336 |
"""Move the item at rel_from to the location at rel_to.
|
337 |
|
|
338 |
If a transport can directly implement this it is suggested that
|
|
339 |
it do so for efficiency.
|
|
340 |
"""
|
|
341 |
self.copy(rel_from, rel_to) |
|
342 |
self.delete(rel_from) |
|
907.1.1
by John Arbash Meinel
Reworking the Branch and Store code to support an abstracted filesystem layer. |
343 |
|
907.1.28
by John Arbash Meinel
Added pb to function that were missing, implemented a basic double-dispatch copy_to function. |
344 |
def move_multi(self, relpaths, pb=None): |
907.1.1
by John Arbash Meinel
Reworking the Branch and Store code to support an abstracted filesystem layer. |
345 |
"""Move a bunch of entries.
|
346 |
|
|
347 |
:param relpaths: A list of tuples of the form [(from1, to1), (from2, to2),...]
|
|
348 |
"""
|
|
907.1.14
by John Arbash Meinel
Handling some transport functions which take only a single argument. |
349 |
return self._iterate_over(relpaths, self.move, pb, 'move', expand=True) |
907.1.1
by John Arbash Meinel
Reworking the Branch and Store code to support an abstracted filesystem layer. |
350 |
|
351 |
def move_multi_to(self, relpaths, rel_to): |
|
352 |
"""Move a bunch of entries to a single location.
|
|
353 |
This differs from move_multi in that you give a list of from, and
|
|
354 |
a single destination, rather than multiple destinations.
|
|
355 |
||
356 |
:param relpaths: A list of relative paths [from1, from2, from3, ...]
|
|
357 |
:param rel_to: A directory where each entry should be placed.
|
|
358 |
"""
|
|
359 |
# This is not implemented, because you need to do special tricks to
|
|
360 |
# extract the basename, and add it to rel_to
|
|
361 |
raise NotImplementedError |
|
362 |
||
363 |
def delete(self, relpath): |
|
364 |
"""Delete the item at relpath"""
|
|
365 |
raise NotImplementedError |
|
366 |
||
907.1.28
by John Arbash Meinel
Added pb to function that were missing, implemented a basic double-dispatch copy_to function. |
367 |
def delete_multi(self, relpaths, pb=None): |
907.1.1
by John Arbash Meinel
Reworking the Branch and Store code to support an abstracted filesystem layer. |
368 |
"""Queue up a bunch of deletes to be done.
|
369 |
"""
|
|
907.1.14
by John Arbash Meinel
Handling some transport functions which take only a single argument. |
370 |
return self._iterate_over(relpaths, self.delete, pb, 'delete', expand=False) |
907.1.2
by John Arbash Meinel
Working on making Branch() do all of it's work over a Transport. |
371 |
|
372 |
def stat(self, relpath): |
|
373 |
"""Return the stat information for a file.
|
|
374 |
WARNING: This may not be implementable for all protocols, so use
|
|
375 |
sparingly.
|
|
1442.1.44
by Robert Collins
Many transport related tweaks: |
376 |
NOTE: This returns an object with fields such as 'st_size'. It MAY
|
377 |
or MAY NOT return the literal result of an os.stat() call, so all
|
|
378 |
access should be via named fields.
|
|
379 |
ALSO NOTE: Stats of directories may not be supported on some
|
|
380 |
transports.
|
|
907.1.2
by John Arbash Meinel
Working on making Branch() do all of it's work over a Transport. |
381 |
"""
|
382 |
raise NotImplementedError |
|
383 |
||
384 |
def stat_multi(self, relpaths, pb=None): |
|
385 |
"""Stat multiple files and return the information.
|
|
386 |
"""
|
|
387 |
#TODO: Is it worth making this a generator instead of a
|
|
388 |
# returning a list?
|
|
389 |
stats = [] |
|
390 |
def gather(path): |
|
391 |
stats.append(self.stat(path)) |
|
392 |
||
907.1.14
by John Arbash Meinel
Handling some transport functions which take only a single argument. |
393 |
count = self._iterate_over(relpaths, gather, pb, 'stat', expand=False) |
907.1.2
by John Arbash Meinel
Working on making Branch() do all of it's work over a Transport. |
394 |
return stats |
395 |
||
1400.1.1
by Robert Collins
implement a basic test for the ui branch command from http servers |
396 |
def listable(self): |
397 |
"""Return True if this store supports listing."""
|
|
398 |
raise NotImplementedError |
|
907.1.1
by John Arbash Meinel
Reworking the Branch and Store code to support an abstracted filesystem layer. |
399 |
|
400 |
def list_dir(self, relpath): |
|
401 |
"""Return a list of all files at the given location.
|
|
402 |
WARNING: many transports do not support this, so trying avoid using
|
|
403 |
it if at all possible.
|
|
404 |
"""
|
|
1185.31.44
by John Arbash Meinel
Cleaned up Exceptions for all transports. |
405 |
raise errors.TransportNotPossible("This transport has not " |
1530.1.3
by Robert Collins
transport implementations now tested consistently. |
406 |
"implemented list_dir."
|
407 |
"(but must claim to be listable "
|
|
408 |
"to trigger this error).") |
|
907.1.1
by John Arbash Meinel
Reworking the Branch and Store code to support an abstracted filesystem layer. |
409 |
|
907.1.24
by John Arbash Meinel
Remote functionality work. |
410 |
def lock_read(self, relpath): |
411 |
"""Lock the given file for shared (read) access.
|
|
412 |
WARNING: many transports do not support this, so trying avoid using it
|
|
413 |
||
414 |
:return: A lock object, which should contain an unlock() function.
|
|
415 |
"""
|
|
416 |
raise NotImplementedError |
|
417 |
||
418 |
def lock_write(self, relpath): |
|
419 |
"""Lock the given file for exclusive (write) access.
|
|
420 |
WARNING: many transports do not support this, so trying avoid using it
|
|
421 |
||
422 |
:return: A lock object, which should contain an unlock() function.
|
|
423 |
"""
|
|
424 |
raise NotImplementedError |
|
425 |
||
1530.1.3
by Robert Collins
transport implementations now tested consistently. |
426 |
def is_readonly(self): |
427 |
"""Return true if this connection cannot be written to."""
|
|
428 |
return False |
|
429 |
||
907.1.24
by John Arbash Meinel
Remote functionality work. |
430 |
|
1393.2.4
by John Arbash Meinel
All tests pass. |
431 |
def get_transport(base): |
907.1.45
by John Arbash Meinel
Switch to registering protocol handlers, rather than just updating a dictionary. |
432 |
global _protocol_handlers |
907.1.13
by John Arbash Meinel
Fixed bzr root. |
433 |
if base is None: |
1185.33.66
by Martin Pool
[patch] use unicode literals for all hardcoded paths (Alexander Belchenko) |
434 |
base = u'.' |
1185.31.60
by John Arbash Meinel
get_transport() now forces unicode paths, which helps get proper filenames. |
435 |
else: |
436 |
base = unicode(base) |
|
907.1.45
by John Arbash Meinel
Switch to registering protocol handlers, rather than just updating a dictionary. |
437 |
for proto, klass in _protocol_handlers.iteritems(): |
907.1.1
by John Arbash Meinel
Reworking the Branch and Store code to support an abstracted filesystem layer. |
438 |
if proto is not None and base.startswith(proto): |
439 |
return klass(base) |
|
440 |
# The default handler is the filesystem handler
|
|
441 |
# which has a lookup of None
|
|
907.1.45
by John Arbash Meinel
Switch to registering protocol handlers, rather than just updating a dictionary. |
442 |
return _protocol_handlers[None](base) |
907.1.1
by John Arbash Meinel
Reworking the Branch and Store code to support an abstracted filesystem layer. |
443 |
|
1185.16.79
by Martin Pool
Load transports when they're first used. |
444 |
|
445 |
def register_lazy_transport(scheme, module, classname): |
|
446 |
"""Register lazy-loaded transport class.
|
|
447 |
||
448 |
When opening a URL with the given scheme, load the module and then
|
|
449 |
instantiate the particular class.
|
|
450 |
"""
|
|
451 |
def _loader(base): |
|
452 |
mod = __import__(module, globals(), locals(), [classname]) |
|
453 |
klass = getattr(mod, classname) |
|
454 |
return klass(base) |
|
1530.1.11
by Robert Collins
Push the transport permutations list into each transport module allowing for automatic testing of new modules that are registered as transports. |
455 |
_loader.module = module |
1185.16.79
by Martin Pool
Load transports when they're first used. |
456 |
register_transport(scheme, _loader) |
1185.16.81
by mbp at sourcefrog
[merge] robert |
457 |
|
1185.16.78
by Martin Pool
- load paramiko sftp transport by default |
458 |
|
1469
by Robert Collins
Change Transport.* to work with URL's. |
459 |
def urlescape(relpath): |
460 |
"""Escape relpath to be a valid url."""
|
|
461 |
# TODO utf8 it first. utf8relpath = relpath.encode('utf8')
|
|
462 |
import urllib |
|
463 |
return urllib.quote(relpath) |
|
1470
by Robert Collins
merge from Martin, via newformat, and teach sftp about urlescaped paths |
464 |
|
1185.16.78
by Martin Pool
- load paramiko sftp transport by default |
465 |
|
1530.1.3
by Robert Collins
transport implementations now tested consistently. |
466 |
class Server(object): |
467 |
"""Basic server API for paths."""
|
|
468 |
||
469 |
def setUp(self): |
|
470 |
"""Setup the server to service requests."""
|
|
471 |
||
472 |
def tearDown(self): |
|
473 |
"""Remove the server and cleanup any resources it owns."""
|
|
474 |
||
475 |
def get_url(self): |
|
476 |
"""Return a url for this server to os.getcwdu.
|
|
477 |
|
|
478 |
If the transport does not represent a disk directory (i.e. it is
|
|
479 |
a databse like svn, or a memory only transport, it should return
|
|
480 |
a connection to a newly established resource for this Server.
|
|
481 |
|
|
482 |
Subsequent calls will return the same resource.
|
|
483 |
"""
|
|
484 |
raise NotImplementedError |
|
485 |
||
1530.1.9
by Robert Collins
Test bogus urls with http in the new infrastructure. |
486 |
def get_bogus_url(self): |
487 |
"""Return a url for this protocol, that will fail to connect."""
|
|
488 |
raise NotImplementedError |
|
489 |
||
1530.1.3
by Robert Collins
transport implementations now tested consistently. |
490 |
|
1530.1.1
by Robert Collins
Minimal infrastructure to test TransportTestProviderAdapter. |
491 |
class TransportTestProviderAdapter(object): |
492 |
"""A class which can setup transport interface tests."""
|
|
493 |
||
494 |
def adapt(self, test): |
|
495 |
result = TestSuite() |
|
496 |
for klass, server_factory in self._test_permutations(): |
|
497 |
new_test = deepcopy(test) |
|
498 |
new_test.transport_class = klass |
|
499 |
new_test.transport_server = server_factory |
|
1530.1.3
by Robert Collins
transport implementations now tested consistently. |
500 |
def make_new_test_id(): |
501 |
new_id = "%s(%s)" % (new_test.id(), server_factory.__name__) |
|
502 |
return lambda: new_id |
|
503 |
new_test.id = make_new_test_id() |
|
1530.1.1
by Robert Collins
Minimal infrastructure to test TransportTestProviderAdapter. |
504 |
result.addTest(new_test) |
505 |
return result |
|
506 |
||
1530.1.11
by Robert Collins
Push the transport permutations list into each transport module allowing for automatic testing of new modules that are registered as transports. |
507 |
def get_transport_test_permutations(self, module): |
508 |
"""Get the permutations module wants to have tested."""
|
|
509 |
return module.get_test_permutations() |
|
510 |
||
1530.1.1
by Robert Collins
Minimal infrastructure to test TransportTestProviderAdapter. |
511 |
def _test_permutations(self): |
512 |
"""Return a list of the klass, server_factory pairs to test."""
|
|
1530.1.11
by Robert Collins
Push the transport permutations list into each transport module allowing for automatic testing of new modules that are registered as transports. |
513 |
result = [] |
514 |
for module in _get_transport_modules(): |
|
515 |
result.extend(self.get_transport_test_permutations(reduce(getattr, |
|
516 |
(module).split('.')[1:], |
|
517 |
__import__(module)))) |
|
518 |
return result |
|
1530.1.1
by Robert Collins
Minimal infrastructure to test TransportTestProviderAdapter. |
519 |
|
520 |
||
1185.16.81
by mbp at sourcefrog
[merge] robert |
521 |
# None is the default transport, for things with no url scheme
|
1185.16.79
by Martin Pool
Load transports when they're first used. |
522 |
register_lazy_transport(None, 'bzrlib.transport.local', 'LocalTransport') |
523 |
register_lazy_transport('file://', 'bzrlib.transport.local', 'LocalTransport') |
|
524 |
register_lazy_transport('sftp://', 'bzrlib.transport.sftp', 'SFTPTransport') |
|
525 |
register_lazy_transport('http://', 'bzrlib.transport.http', 'HttpTransport') |
|
526 |
register_lazy_transport('https://', 'bzrlib.transport.http', 'HttpTransport') |
|
1185.36.4
by Daniel Silverstone
Add FTP transport |
527 |
register_lazy_transport('ftp://', 'bzrlib.transport.ftp', 'FtpTransport') |
528 |
register_lazy_transport('aftp://', 'bzrlib.transport.ftp', 'FtpTransport') |
|
1530.1.11
by Robert Collins
Push the transport permutations list into each transport module allowing for automatic testing of new modules that are registered as transports. |
529 |
register_lazy_transport('memory://', 'bzrlib.transport.memory', 'MemoryTransport') |