~bzr-pqm/bzr/bzr.dev

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')