~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
907.1.45 by John Arbash Meinel
Switch to registering protocol handlers, rather than just updating a dictionary.
22
from bzrlib.trace import mutter
1185.50.11 by John Arbash Meinel
[merge] Refactor NoSuchFile style exceptions.
23
import bzrlib.errors as errors
24
import errno
907.1.45 by John Arbash Meinel
Switch to registering protocol handlers, rather than just updating a dictionary.
25
26
_protocol_handlers = {
907.1.1 by John Arbash Meinel
Reworking the Branch and Store code to support an abstracted filesystem layer.
27
}
28
907.1.45 by John Arbash Meinel
Switch to registering protocol handlers, rather than just updating a dictionary.
29
def register_transport(prefix, klass, override=True):
30
    global _protocol_handlers
1185.16.17 by Martin Pool
- avoid warning about log not being registered during startup
31
    # trace messages commented out because they're typically 
32
    # 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.
33
    if _protocol_handlers.has_key(prefix):
34
        if override:
1185.16.17 by Martin Pool
- avoid warning about log not being registered during startup
35
            ## 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.
36
            _protocol_handlers[prefix] = klass
37
    else:
1185.16.17 by Martin Pool
- avoid warning about log not being registered during startup
38
        ## 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.
39
        _protocol_handlers[prefix] = klass
40
1442.1.24 by Robert Collins
Pull up _check_id and _relpath from Text and CompressedText stores into TransportStore
41
907.1.1 by John Arbash Meinel
Reworking the Branch and Store code to support an abstracted filesystem layer.
42
class Transport(object):
43
    """This class encapsulates methods for retrieving or putting a file
44
    from/to a storage location.
45
46
    Most functions have a _multi variant, which allows you to queue up
47
    multiple requests. They generally have a dumb base implementation 
48
    which just iterates over the arguments, but smart Transport
49
    implementations can do pipelining.
907.1.2 by John Arbash Meinel
Working on making Branch() do all of it's work over a Transport.
50
    In general implementations should support having a generator or a list
51
    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.
52
    """
53
54
    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.
55
        super(Transport, self).__init__()
907.1.1 by John Arbash Meinel
Reworking the Branch and Store code to support an abstracted filesystem layer.
56
        self.base = base
57
1185.50.11 by John Arbash Meinel
[merge] Refactor NoSuchFile style exceptions.
58
    def _translate_error(self, e, path, raise_generic=True):
59
        """Translate an IOError or OSError into an appropriate bzr error.
60
61
        This handles things like ENOENT, ENOTDIR, EEXIST, and EACCESS
62
        """
63
        if hasattr(e, 'errno'):
64
            if e.errno in (errno.ENOENT, errno.ENOTDIR):
65
                raise errors.NoSuchFile(path, extra=e)
66
            if e.errno == errno.EEXIST:
67
                raise errors.FileExists(path, extra=e)
68
            if e.errno == errno.EACCES:
69
                raise errors.PermissionDenied(path, extra=e)
70
        if raise_generic:
71
            raise errors.TransportError(orig_error=e)
72
907.1.2 by John Arbash Meinel
Working on making Branch() do all of it's work over a Transport.
73
    def clone(self, offset=None):
74
        """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.
75
        using a subdirectory or parent directory. This allows connections 
76
        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.
77
        """
78
        raise NotImplementedError
79
907.1.32 by John Arbash Meinel
Renaming is_remote to should_cache as it is more appropriate.
80
    def should_cache(self):
81
        """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.
82
        """
907.1.32 by John Arbash Meinel
Renaming is_remote to should_cache as it is more appropriate.
83
        return False
907.1.22 by John Arbash Meinel
Fixed some encoding issues, added is_remote function for Transport objects.
84
907.1.1 by John Arbash Meinel
Reworking the Branch and Store code to support an abstracted filesystem layer.
85
    def _pump(self, from_file, to_file):
86
        """Most children will need to copy from one file-like 
87
        object or string to another one.
88
        This just gives them something easy to call.
89
        """
90
        if isinstance(from_file, basestring):
907.1.8 by John Arbash Meinel
Changed the format for abspath. Updated branch to use a hidden _transport
91
            to_file.write(from_file)
907.1.1 by John Arbash Meinel
Reworking the Branch and Store code to support an abstracted filesystem layer.
92
        else:
93
            from bzrlib.osutils import pumpfile
94
            pumpfile(from_file, to_file)
95
907.1.2 by John Arbash Meinel
Working on making Branch() do all of it's work over a Transport.
96
    def _get_total(self, multi):
97
        """Try to figure out how many entries are in multi,
98
        but if not possible, return None.
99
        """
100
        try:
101
            return len(multi)
102
        except TypeError: # We can't tell how many, because relpaths is a generator
103
            return None
104
105
    def _update_pb(self, pb, msg, count, total):
106
        """Update the progress bar based on the current count
107
        and total available, total may be None if it was
108
        not possible to determine.
109
        """
907.1.14 by John Arbash Meinel
Handling some transport functions which take only a single argument.
110
        if pb is None:
111
            return
907.1.2 by John Arbash Meinel
Working on making Branch() do all of it's work over a Transport.
112
        if total is None:
113
            pb.update(msg, count, count+1)
114
        else:
115
            pb.update(msg, count, total)
116
907.1.14 by John Arbash Meinel
Handling some transport functions which take only a single argument.
117
    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.
118
        """Iterate over all entries in multi, passing them to func,
119
        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.
120
121
        :param expand:  If True, the entries will be passed to the function
122
                        by expanding the tuple. If False, it will be passed
123
                        as a single parameter.
907.1.2 by John Arbash Meinel
Working on making Branch() do all of it's work over a Transport.
124
        """
125
        total = self._get_total(multi)
126
        count = 0
127
        for entry in multi:
128
            self._update_pb(pb, msg, count, total)
907.1.14 by John Arbash Meinel
Handling some transport functions which take only a single argument.
129
            if expand:
130
                func(*entry)
131
            else:
132
                func(entry)
907.1.2 by John Arbash Meinel
Working on making Branch() do all of it's work over a Transport.
133
            count += 1
134
        return count
135
907.1.34 by John Arbash Meinel
Fixing append(), cleaning up function locations.
136
    def abspath(self, relpath):
137
        """Return the full url to the given relative path.
138
        This can be supplied with a string or a list
1442.1.44 by Robert Collins
Many transport related tweaks:
139
140
        XXX: Robert Collins 20051016 - is this really needed in the public
141
             interface ?
907.1.34 by John Arbash Meinel
Fixing append(), cleaning up function locations.
142
        """
143
        raise NotImplementedError
144
145
    def relpath(self, abspath):
146
        """Return the local path portion from a given absolute path.
1442.1.44 by Robert Collins
Many transport related tweaks:
147
148
        This default implementation is not suitable for filesystems with
149
        aliasing, such as that given by symlinks, where a path may not 
150
        start with our base, but still be a relpath once aliasing is 
151
        resolved.
907.1.34 by John Arbash Meinel
Fixing append(), cleaning up function locations.
152
        """
1185.50.11 by John Arbash Meinel
[merge] Refactor NoSuchFile style exceptions.
153
        # TODO: This might want to use bzrlib.osutils.relpath
154
        #       but we have to watch out because of the prefix issues
1442.1.44 by Robert Collins
Many transport related tweaks:
155
        if not abspath.startswith(self.base):
1185.50.11 by John Arbash Meinel
[merge] Refactor NoSuchFile style exceptions.
156
            raise errors.PathNotChild(abspath, self.base)
1442.1.44 by Robert Collins
Many transport related tweaks:
157
        pl = len(self.base)
158
        return abspath[pl:].lstrip('/')
159
907.1.34 by John Arbash Meinel
Fixing append(), cleaning up function locations.
160
907.1.1 by John Arbash Meinel
Reworking the Branch and Store code to support an abstracted filesystem layer.
161
    def has(self, relpath):
1442.1.44 by Robert Collins
Many transport related tweaks:
162
        """Does the file relpath exist?
163
        
164
        Note that some transports MAY allow querying on directories, but this
165
        is not part of the protocol.
166
        """
907.1.1 by John Arbash Meinel
Reworking the Branch and Store code to support an abstracted filesystem layer.
167
        raise NotImplementedError
168
907.1.36 by John Arbash Meinel
Moving the multi-get functionality higher up into the Branch class.
169
    def has_multi(self, relpaths, pb=None):
907.1.34 by John Arbash Meinel
Fixing append(), cleaning up function locations.
170
        """Return True/False for each entry in relpaths"""
171
        total = self._get_total(relpaths)
172
        count = 0
173
        for relpath in relpaths:
907.1.36 by John Arbash Meinel
Moving the multi-get functionality higher up into the Branch class.
174
            self._update_pb(pb, 'has', count, total)
907.1.34 by John Arbash Meinel
Fixing append(), cleaning up function locations.
175
            yield self.has(relpath)
176
            count += 1
177
1185.16.155 by John Arbash Meinel
Added a has_any function to the Transport API
178
    def has_any(self, relpaths):
179
        """Return True if any of the paths exist."""
180
        for relpath in relpaths:
181
            if self.has(relpath):
182
                return True
183
        return False
184
1442.1.44 by Robert Collins
Many transport related tweaks:
185
    def iter_files_recursive(self):
186
        """Iter the relative paths of files in the transports sub-tree.
187
        
188
        As with other listing functions, only some transports implement this,.
189
        you may check via is_listable to determine if it will.
190
        """
191
        raise NotImplementedError
192
907.1.50 by John Arbash Meinel
Removed encode/decode from Transport.put/get, added more exceptions that can be thrown.
193
    def get(self, relpath):
907.1.1 by John Arbash Meinel
Reworking the Branch and Store code to support an abstracted filesystem layer.
194
        """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
195
196
        :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.
197
        """
198
        raise NotImplementedError
199
907.1.50 by John Arbash Meinel
Removed encode/decode from Transport.put/get, added more exceptions that can be thrown.
200
    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.
201
        """Get a list of file-like objects, one for each entry in relpaths.
202
203
        :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.
204
        :param pb:  An optional ProgressBar for indicating percent done.
205
        :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.
206
        """
907.1.2 by John Arbash Meinel
Working on making Branch() do all of it's work over a Transport.
207
        # TODO: Consider having this actually buffer the requests,
208
        # in the default mode, it probably won't give worse performance,
209
        # and all children wouldn't have to implement buffering
907.1.16 by John Arbash Meinel
Fixing a few cut&paste typos.
210
        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.
211
        count = 0
907.1.1 by John Arbash Meinel
Reworking the Branch and Store code to support an abstracted filesystem layer.
212
        for relpath in relpaths:
907.1.16 by John Arbash Meinel
Fixing a few cut&paste typos.
213
            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.
214
            yield self.get(relpath)
907.1.2 by John Arbash Meinel
Working on making Branch() do all of it's work over a Transport.
215
            count += 1
907.1.1 by John Arbash Meinel
Reworking the Branch and Store code to support an abstracted filesystem layer.
216
907.1.50 by John Arbash Meinel
Removed encode/decode from Transport.put/get, added more exceptions that can be thrown.
217
    def put(self, relpath, f):
907.1.1 by John Arbash Meinel
Reworking the Branch and Store code to support an abstracted filesystem layer.
218
        """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
219
220
        :param relpath: Location to put the contents, relative to base.
221
        :param f:       File-like or string object.
907.1.1 by John Arbash Meinel
Reworking the Branch and Store code to support an abstracted filesystem layer.
222
        """
223
        raise NotImplementedError
224
907.1.50 by John Arbash Meinel
Removed encode/decode from Transport.put/get, added more exceptions that can be thrown.
225
    def put_multi(self, files, pb=None):
907.1.1 by John Arbash Meinel
Reworking the Branch and Store code to support an abstracted filesystem layer.
226
        """Put a set of files or strings into the location.
227
228
        :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.
229
        :param pb:  An optional ProgressBar for indicating percent done.
230
        :return: The number of files copied.
231
        """
1185.11.20 by John Arbash Meinel
Added some more test cases to verify generators work.
232
        return self._iterate_over(files, self.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.
233
234
    def mkdir(self, relpath):
235
        """Create a directory at the given path."""
236
        raise NotImplementedError
237
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
238
    def mkdir_multi(self, relpaths, pb=None):
239
        """Create a group of directories"""
240
        return self._iterate_over(relpaths, self.mkdir, pb, 'mkdir', expand=False)
241
907.1.50 by John Arbash Meinel
Removed encode/decode from Transport.put/get, added more exceptions that can be thrown.
242
    def append(self, relpath, f):
907.1.1 by John Arbash Meinel
Reworking the Branch and Store code to support an abstracted filesystem layer.
243
        """Append the text in the file-like or string object to 
244
        the supplied location.
245
        """
246
        raise NotImplementedError
247
1185.11.19 by John Arbash Meinel
Testing put and append, also testing agaist file-like objects as well as strings.
248
    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.
249
        """Append the text in each file-like or string object to
250
        the supplied location.
907.1.2 by John Arbash Meinel
Working on making Branch() do all of it's work over a Transport.
251
252
        :param files: A set of (path, f) entries
253
        :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.
254
        """
907.1.14 by John Arbash Meinel
Handling some transport functions which take only a single argument.
255
        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.
256
257
    def copy(self, rel_from, rel_to):
258
        """Copy the item at rel_from to the location at rel_to"""
259
        raise NotImplementedError
260
907.1.28 by John Arbash Meinel
Added pb to function that were missing, implemented a basic double-dispatch copy_to function.
261
    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.
262
        """Copy a bunch of entries.
263
        
264
        :param relpaths: A list of tuples of the form [(from, to), (from, to),...]
265
        """
266
        # This is the non-pipelined implementation, so that
267
        # implementors don't have to implement everything.
907.1.14 by John Arbash Meinel
Handling some transport functions which take only a single argument.
268
        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.
269
907.1.28 by John Arbash Meinel
Added pb to function that were missing, implemented a basic double-dispatch copy_to function.
270
    def copy_to(self, relpaths, other, pb=None):
271
        """Copy a set of entries from self into another Transport.
272
273
        :param relpaths: A list/generator of entries to be copied.
1185.16.156 by John Arbash Meinel
Adding a note about changing copy_to's interface
274
        TODO: This interface needs to be updated so that the target location
275
              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.
276
        """
277
        # The dummy implementation just does a simple get + put
278
        def copy_entry(path):
907.1.50 by John Arbash Meinel
Removed encode/decode from Transport.put/get, added more exceptions that can be thrown.
279
            other.put(path, self.get(path))
907.1.28 by John Arbash Meinel
Added pb to function that were missing, implemented a basic double-dispatch copy_to function.
280
281
        return self._iterate_over(relpaths, copy_entry, pb, 'copy_to', expand=False)
282
907.1.57 by John Arbash Meinel
Trying to get pipelined http library working + tests.
283
907.1.1 by John Arbash Meinel
Reworking the Branch and Store code to support an abstracted filesystem layer.
284
    def move(self, rel_from, rel_to):
285
        """Move the item at rel_from to the location at rel_to"""
286
        raise NotImplementedError
287
907.1.28 by John Arbash Meinel
Added pb to function that were missing, implemented a basic double-dispatch copy_to function.
288
    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.
289
        """Move a bunch of entries.
290
        
291
        :param relpaths: A list of tuples of the form [(from1, to1), (from2, to2),...]
292
        """
907.1.14 by John Arbash Meinel
Handling some transport functions which take only a single argument.
293
        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.
294
295
    def move_multi_to(self, relpaths, rel_to):
296
        """Move a bunch of entries to a single location.
297
        This differs from move_multi in that you give a list of from, and
298
        a single destination, rather than multiple destinations.
299
300
        :param relpaths: A list of relative paths [from1, from2, from3, ...]
301
        :param rel_to: A directory where each entry should be placed.
302
        """
303
        # This is not implemented, because you need to do special tricks to
304
        # extract the basename, and add it to rel_to
305
        raise NotImplementedError
306
307
    def delete(self, relpath):
308
        """Delete the item at relpath"""
309
        raise NotImplementedError
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 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.
312
        """Queue up a bunch of deletes to be done.
313
        """
907.1.14 by John Arbash Meinel
Handling some transport functions which take only a single argument.
314
        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.
315
316
    def stat(self, relpath):
317
        """Return the stat information for a file.
318
        WARNING: This may not be implementable for all protocols, so use
319
        sparingly.
1442.1.44 by Robert Collins
Many transport related tweaks:
320
        NOTE: This returns an object with fields such as 'st_size'. It MAY
321
        or MAY NOT return the literal result of an os.stat() call, so all
322
        access should be via named fields.
323
        ALSO NOTE: Stats of directories may not be supported on some 
324
        transports.
907.1.2 by John Arbash Meinel
Working on making Branch() do all of it's work over a Transport.
325
        """
326
        raise NotImplementedError
327
328
    def stat_multi(self, relpaths, pb=None):
329
        """Stat multiple files and return the information.
330
        """
331
        #TODO:  Is it worth making this a generator instead of a
332
        #       returning a list?
333
        stats = []
334
        def gather(path):
335
            stats.append(self.stat(path))
336
907.1.14 by John Arbash Meinel
Handling some transport functions which take only a single argument.
337
        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.
338
        return stats
339
1400.1.1 by Robert Collins
implement a basic test for the ui branch command from http servers
340
    def listable(self):
341
        """Return True if this store supports listing."""
342
        raise NotImplementedError
907.1.1 by John Arbash Meinel
Reworking the Branch and Store code to support an abstracted filesystem layer.
343
344
    def list_dir(self, relpath):
345
        """Return a list of all files at the given location.
346
        WARNING: many transports do not support this, so trying avoid using
347
        it if at all possible.
348
        """
1185.50.11 by John Arbash Meinel
[merge] Refactor NoSuchFile style exceptions.
349
        raise errors.TransportNotPossible("This transport has not "
350
                                          "implemented list_dir.")
907.1.1 by John Arbash Meinel
Reworking the Branch and Store code to support an abstracted filesystem layer.
351
907.1.24 by John Arbash Meinel
Remote functionality work.
352
    def lock_read(self, relpath):
353
        """Lock the given file for shared (read) access.
354
        WARNING: many transports do not support this, so trying avoid using it
355
356
        :return: A lock object, which should contain an unlock() function.
357
        """
358
        raise NotImplementedError
359
360
    def lock_write(self, relpath):
361
        """Lock the given file for exclusive (write) access.
362
        WARNING: many transports do not support this, so trying avoid using it
363
364
        :return: A lock object, which should contain an unlock() function.
365
        """
366
        raise NotImplementedError
367
368
1393.2.4 by John Arbash Meinel
All tests pass.
369
def get_transport(base):
907.1.45 by John Arbash Meinel
Switch to registering protocol handlers, rather than just updating a dictionary.
370
    global _protocol_handlers
907.1.13 by John Arbash Meinel
Fixed bzr root.
371
    if base is None:
1185.33.66 by Martin Pool
[patch] use unicode literals for all hardcoded paths (Alexander Belchenko)
372
        base = u'.'
907.1.45 by John Arbash Meinel
Switch to registering protocol handlers, rather than just updating a dictionary.
373
    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.
374
        if proto is not None and base.startswith(proto):
375
            return klass(base)
376
    # The default handler is the filesystem handler
377
    # which has a lookup of None
907.1.45 by John Arbash Meinel
Switch to registering protocol handlers, rather than just updating a dictionary.
378
    return _protocol_handlers[None](base)
907.1.1 by John Arbash Meinel
Reworking the Branch and Store code to support an abstracted filesystem layer.
379
1185.16.79 by Martin Pool
Load transports when they're first used.
380
381
def register_lazy_transport(scheme, module, classname):
382
    """Register lazy-loaded transport class.
383
384
    When opening a URL with the given scheme, load the module and then
385
    instantiate the particular class.  
386
    """
387
    def _loader(base):
388
        mod = __import__(module, globals(), locals(), [classname])
389
        klass = getattr(mod, classname)
390
        return klass(base)
391
    register_transport(scheme, _loader)
1185.16.81 by mbp at sourcefrog
[merge] robert
392
1185.16.78 by Martin Pool
- load paramiko sftp transport by default
393
1469 by Robert Collins
Change Transport.* to work with URL's.
394
def urlescape(relpath):
395
    """Escape relpath to be a valid url."""
396
    # TODO utf8 it first. utf8relpath = relpath.encode('utf8')
397
    import urllib
398
    return urllib.quote(relpath)
1470 by Robert Collins
merge from Martin, via newformat, and teach sftp about urlescaped paths
399
1185.16.78 by Martin Pool
- load paramiko sftp transport by default
400
1185.16.81 by mbp at sourcefrog
[merge] robert
401
# None is the default transport, for things with no url scheme
1185.16.79 by Martin Pool
Load transports when they're first used.
402
register_lazy_transport(None, 'bzrlib.transport.local', 'LocalTransport')
403
register_lazy_transport('file://', 'bzrlib.transport.local', 'LocalTransport')
404
register_lazy_transport('sftp://', 'bzrlib.transport.sftp', 'SFTPTransport')
405
register_lazy_transport('http://', 'bzrlib.transport.http', 'HttpTransport')
406
register_lazy_transport('https://', 'bzrlib.transport.http', 'HttpTransport')
1185.36.4 by Daniel Silverstone
Add FTP transport
407
register_lazy_transport('ftp://', 'bzrlib.transport.ftp', 'FtpTransport')
408
register_lazy_transport('aftp://', 'bzrlib.transport.ftp', 'FtpTransport')