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