~bzr-pqm/bzr/bzr.dev

1553.5.10 by Martin Pool
New DirectoryNotEmpty exception, and raise this from local and memory
1
# Copyright (C) 2005, 2006 Canonical Ltd
1887.1.1 by Adeodato Simó
Do not separate paragraphs in the copyright statement with blank lines,
2
#
1185.11.19 by John Arbash Meinel
Testing put and append, also testing agaist file-like objects as well as strings.
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.
1887.1.1 by Adeodato Simó
Do not separate paragraphs in the copyright statement with blank lines,
7
#
1185.11.19 by John Arbash Meinel
Testing put and append, also testing agaist file-like objects as well as strings.
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.
1887.1.1 by Adeodato Simó
Do not separate paragraphs in the copyright statement with blank lines,
12
#
1185.11.19 by John Arbash Meinel
Testing put and append, also testing agaist file-like objects as well as strings.
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
1540.3.1 by Martin Pool
First-cut implementation of pycurl. Substantially faster than using urllib.
16
1185.11.19 by John Arbash Meinel
Testing put and append, also testing agaist file-like objects as well as strings.
17
"""Transport is an abstraction layer to handle file access.
18
19
The abstraction is to allow access from the local filesystem, as well
20
as remote (such as http or sftp).
1540.3.12 by Martin Pool
Multiple transports can be registered for any protocol, and they are
21
22
Transports are constructed from a string, being a URL or (as a degenerate
23
case) a local filesystem path.  This is typically the top directory of
24
a bzrdir, repository, or similar object we are interested in working with.
25
The Transport returned has methods to read, write and manipulate files within
26
it.
907.1.1 by John Arbash Meinel
Reworking the Branch and Store code to support an abstracted filesystem layer.
27
"""
28
1185.31.44 by John Arbash Meinel
Cleaned up Exceptions for all transports.
29
import errno
1594.2.17 by Robert Collins
Better readv coalescing, now with test, and progress during knit index reading.
30
from collections import deque
1530.1.1 by Robert Collins
Minimal infrastructure to test TransportTestProviderAdapter.
31
from copy import deepcopy
1685.1.9 by John Arbash Meinel
Updated LocalTransport so that it's base is now a URL rather than a local path. This helps consistency with all other functions. To do so, I added local_abspath() which returns the local path, and local_path_to/from_url
32
import re
1773.4.1 by Martin Pool
Add pyflakes makefile target; fix many warnings
33
from stat import S_ISDIR
1530.1.7 by Robert Collins
merge integration.
34
import sys
1530.1.1 by Robert Collins
Minimal infrastructure to test TransportTestProviderAdapter.
35
from unittest import TestSuite
1685.1.70 by Wouter van Heyst
working on get_parent, set_parent and relative urls, broken
36
import urllib
1636.1.1 by Robert Collins
Fix calling relpath() and abspath() on transports at their root.
37
import urlparse
1773.4.1 by Martin Pool
Add pyflakes makefile target; fix many warnings
38
import warnings
1530.1.1 by Robert Collins
Minimal infrastructure to test TransportTestProviderAdapter.
39
1725.2.3 by Robert Collins
Remove import of pumpfile from inner loop of commit.
40
import bzrlib
1185.31.44 by John Arbash Meinel
Cleaned up Exceptions for all transports.
41
import bzrlib.errors as errors
1540.3.8 by Martin Pool
Some support for falling back between transport implementations.
42
from bzrlib.errors import DependencyNotPresent
1685.1.9 by John Arbash Meinel
Updated LocalTransport so that it's base is now a URL rather than a local path. This helps consistency with all other functions. To do so, I added local_abspath() which returns the local path, and local_path_to/from_url
43
import bzrlib.osutils as osutils
1725.2.3 by Robert Collins
Remove import of pumpfile from inner loop of commit.
44
from bzrlib.osutils import pumpfile
1773.4.1 by Martin Pool
Add pyflakes makefile target; fix many warnings
45
from bzrlib.symbol_versioning import (deprecated_passed, deprecated_method, deprecated_function, 
46
        DEPRECATED_PARAMETER,
47
        zero_eight)
1685.1.9 by John Arbash Meinel
Updated LocalTransport so that it's base is now a URL rather than a local path. This helps consistency with all other functions. To do so, I added local_abspath() which returns the local path, and local_path_to/from_url
48
from bzrlib.trace import mutter, warning
1685.1.45 by John Arbash Meinel
Moved url functions into bzrlib.urlutils
49
import bzrlib.urlutils as urlutils
907.1.45 by John Arbash Meinel
Switch to registering protocol handlers, rather than just updating a dictionary.
50
1540.3.12 by Martin Pool
Multiple transports can be registered for any protocol, and they are
51
# {prefix: [transport_classes]}
52
# Transports are inserted onto the list LIFO and tried in order; as a result
53
# transports provided by plugins are tried first, which is usually what we
54
# want.
907.1.45 by John Arbash Meinel
Switch to registering protocol handlers, rather than just updating a dictionary.
55
_protocol_handlers = {
907.1.1 by John Arbash Meinel
Reworking the Branch and Store code to support an abstracted filesystem layer.
56
}
57
1540.3.12 by Martin Pool
Multiple transports can be registered for any protocol, and they are
58
def register_transport(prefix, klass, override=DEPRECATED_PARAMETER):
1540.3.8 by Martin Pool
Some support for falling back between transport implementations.
59
    """Register a transport that can be used to open URLs
60
61
    Normally you should use register_lazy_transport, which defers loading the
62
    implementation until it's actually used, and so avoids pulling in possibly
63
    large implementation libraries.
64
    """
1540.3.12 by Martin Pool
Multiple transports can be registered for any protocol, and they are
65
    # Note that this code runs very early in library setup -- trace may not be
66
    # working, etc.
907.1.45 by John Arbash Meinel
Switch to registering protocol handlers, rather than just updating a dictionary.
67
    global _protocol_handlers
1540.3.12 by Martin Pool
Multiple transports can be registered for any protocol, and they are
68
    if deprecated_passed(override):
1773.4.1 by Martin Pool
Add pyflakes makefile target; fix many warnings
69
        warnings.warn("register_transport(override) is deprecated")
1540.3.12 by Martin Pool
Multiple transports can be registered for any protocol, and they are
70
    _protocol_handlers.setdefault(prefix, []).insert(0, klass)
71
72
73
def register_lazy_transport(scheme, module, classname):
74
    """Register lazy-loaded transport class.
75
76
    When opening a URL with the given scheme, load the module and then
77
    instantiate the particular class.  
78
79
    If the module raises DependencyNotPresent when it's imported, it is
80
    skipped and another implementation of the protocol is tried.  This is
81
    intended to be used when the implementation depends on an external
82
    implementation that may not be present.  If any other error is raised, it
83
    propagates up and the attempt to open the url fails.
84
    """
85
    # TODO: If no implementation of a protocol is available because of missing
86
    # dependencies, we should perhaps show the message about what dependency
87
    # was missing.
88
    def _loader(base):
89
        mod = __import__(module, globals(), locals(), [classname])
90
        klass = getattr(mod, classname)
91
        return klass(base)
92
    _loader.module = module
93
    register_transport(scheme, _loader)
907.1.45 by John Arbash Meinel
Switch to registering protocol handlers, rather than just updating a dictionary.
94
1442.1.24 by Robert Collins
Pull up _check_id and _relpath from Text and CompressedText stores into TransportStore
95
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.
96
def _get_protocol_handlers():
1540.3.12 by Martin Pool
Multiple transports can be registered for any protocol, and they are
97
    """Return a dictionary of {urlprefix: [factory]}"""
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.
98
    return _protocol_handlers
99
100
101
def _set_protocol_handlers(new_handlers):
102
    """Replace the current protocol handlers dictionary.
103
104
    WARNING this will remove all build in protocols. Use with care.
105
    """
106
    global _protocol_handlers
107
    _protocol_handlers = new_handlers
108
109
1540.3.12 by Martin Pool
Multiple transports can be registered for any protocol, and they are
110
def _clear_protocol_handlers():
111
    global _protocol_handlers
112
    _protocol_handlers = {}
113
114
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.
115
def _get_transport_modules():
116
    """Return a list of the modules providing transports."""
117
    modules = set()
1540.3.12 by Martin Pool
Multiple transports can be registered for any protocol, and they are
118
    for prefix, factory_list in _protocol_handlers.items():
119
        for factory in factory_list:
120
            if factory.__module__ == "bzrlib.transport":
121
                # this is a lazy load transport, because no real ones
122
                # are directlry in bzrlib.transport
123
                modules.add(factory.module)
124
            else:
125
                modules.add(factory.__module__)
1530.1.19 by Robert Collins
Make transport test adapter tests reliable.
126
    result = list(modules)
127
    result.sort()
128
    return result
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.
129
130
1636.1.2 by Robert Collins
More review fixen to the relpath at '/' fixes.
131
def register_urlparse_netloc_protocol(protocol):
1636.1.1 by Robert Collins
Fix calling relpath() and abspath() on transports at their root.
132
    """Ensure that protocol is setup to be used with urlparse netloc parsing."""
133
    if protocol not in urlparse.uses_netloc:
134
        urlparse.uses_netloc.append(protocol)
135
136
1707.3.4 by John Arbash Meinel
Moved most of sftp.split_url into a Transport function.
137
def split_url(url):
1685.1.69 by Wouter van Heyst
merge bzr.dev 1740
138
    # TODO: jam 20060606 urls should only be ascii, or they should raise InvalidURL
1707.3.4 by John Arbash Meinel
Moved most of sftp.split_url into a Transport function.
139
    if isinstance(url, unicode):
140
        url = url.encode('utf-8')
141
    (scheme, netloc, path, params,
142
     query, fragment) = urlparse.urlparse(url, allow_fragments=False)
143
    username = password = host = port = None
144
    if '@' in netloc:
145
        username, host = netloc.split('@', 1)
146
        if ':' in username:
147
            username, password = username.split(':', 1)
148
            password = urllib.unquote(password)
149
        username = urllib.unquote(username)
150
    else:
151
        host = netloc
152
153
    if ':' in host:
154
        host, port = host.rsplit(':', 1)
155
        try:
156
            port = int(port)
157
        except ValueError:
158
            # TODO: Should this be ConnectionError?
159
            raise errors.TransportError('%s: invalid port number' % port)
160
    host = urllib.unquote(host)
161
162
    path = urllib.unquote(path)
163
164
    return (scheme, username, password, host, port, path)
165
166
1864.5.9 by John Arbash Meinel
Switch to returning an object to make the api more understandable.
167
class _CoalescedOffset(object):
168
    """A data container for keeping track of coalesced offsets."""
169
170
    __slots__ = ['start', 'length', 'ranges']
171
172
    def __init__(self, start, length, ranges):
173
        self.start = start
174
        self.length = length
175
        self.ranges = ranges
176
177
    def __cmp__(self, other):
178
        return cmp((self.start, self.length, self.ranges),
179
                   (other.start, other.length, other.ranges))
180
181
907.1.1 by John Arbash Meinel
Reworking the Branch and Store code to support an abstracted filesystem layer.
182
class Transport(object):
183
    """This class encapsulates methods for retrieving or putting a file
184
    from/to a storage location.
185
186
    Most functions have a _multi variant, which allows you to queue up
187
    multiple requests. They generally have a dumb base implementation 
188
    which just iterates over the arguments, but smart Transport
189
    implementations can do pipelining.
907.1.2 by John Arbash Meinel
Working on making Branch() do all of it's work over a Transport.
190
    In general implementations should support having a generator or a list
191
    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.
192
    """
193
1864.5.1 by John Arbash Meinel
Change the readv combining algorithm for one that is easier to test.
194
    # implementations can override this if it is more efficient
195
    # for them to combine larger read chunks together
1864.5.4 by John Arbash Meinel
play around with tuning the partial reads.
196
    _max_readv_combine = 50
1864.5.3 by John Arbash Meinel
Allow collapsing ranges even if they are just 'close'
197
    # It is better to read this much more data in order, rather
198
    # than doing another seek. Even for the local filesystem,
199
    # there is a benefit in just reading.
200
    # TODO: jam 20060714 Do some real benchmarking to figure out
201
    #       where the biggest benefit between combining reads and
1864.5.8 by John Arbash Meinel
Cleanup and NEWS
202
    #       and seeking is. Consider a runtime auto-tune.
1864.5.4 by John Arbash Meinel
play around with tuning the partial reads.
203
    _bytes_to_read_before_seek = 0
1864.5.1 by John Arbash Meinel
Change the readv combining algorithm for one that is easier to test.
204
907.1.1 by John Arbash Meinel
Reworking the Branch and Store code to support an abstracted filesystem layer.
205
    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.
206
        super(Transport, self).__init__()
907.1.1 by John Arbash Meinel
Reworking the Branch and Store code to support an abstracted filesystem layer.
207
        self.base = base
208
1185.31.44 by John Arbash Meinel
Cleaned up Exceptions for all transports.
209
    def _translate_error(self, e, path, raise_generic=True):
210
        """Translate an IOError or OSError into an appropriate bzr error.
211
212
        This handles things like ENOENT, ENOTDIR, EEXIST, and EACCESS
213
        """
214
        if hasattr(e, 'errno'):
215
            if e.errno in (errno.ENOENT, errno.ENOTDIR):
216
                raise errors.NoSuchFile(path, extra=e)
1185.31.58 by John Arbash Meinel
Updating for new transport tests so that they pass on win32
217
            # I would rather use errno.EFOO, but there doesn't seem to be
218
            # any matching for 267
219
            # This is the error when doing a listdir on a file:
220
            # WindowsError: [Errno 267] The directory name is invalid
221
            if sys.platform == 'win32' and e.errno in (errno.ESRCH, 267):
222
                raise errors.NoSuchFile(path, extra=e)
1185.31.44 by John Arbash Meinel
Cleaned up Exceptions for all transports.
223
            if e.errno == errno.EEXIST:
224
                raise errors.FileExists(path, extra=e)
225
            if e.errno == errno.EACCES:
226
                raise errors.PermissionDenied(path, extra=e)
1553.5.10 by Martin Pool
New DirectoryNotEmpty exception, and raise this from local and memory
227
            if e.errno == errno.ENOTEMPTY:
228
                raise errors.DirectoryNotEmpty(path, extra=e)
1558.10.1 by Aaron Bentley
Handle lockdirs over NFS properly
229
            if e.errno == errno.EBUSY:
230
                raise errors.ResourceBusy(path, extra=e)
1185.31.44 by John Arbash Meinel
Cleaned up Exceptions for all transports.
231
        if raise_generic:
232
            raise errors.TransportError(orig_error=e)
233
907.1.2 by John Arbash Meinel
Working on making Branch() do all of it's work over a Transport.
234
    def clone(self, offset=None):
235
        """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.
236
        using a subdirectory or parent directory. This allows connections 
237
        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.
238
        """
1540.3.1 by Martin Pool
First-cut implementation of pycurl. Substantially faster than using urllib.
239
        raise NotImplementedError(self.clone)
907.1.2 by John Arbash Meinel
Working on making Branch() do all of it's work over a Transport.
240
907.1.32 by John Arbash Meinel
Renaming is_remote to should_cache as it is more appropriate.
241
    def should_cache(self):
242
        """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.
243
        """
907.1.32 by John Arbash Meinel
Renaming is_remote to should_cache as it is more appropriate.
244
        return False
907.1.22 by John Arbash Meinel
Fixed some encoding issues, added is_remote function for Transport objects.
245
907.1.1 by John Arbash Meinel
Reworking the Branch and Store code to support an abstracted filesystem layer.
246
    def _pump(self, from_file, to_file):
247
        """Most children will need to copy from one file-like 
248
        object or string to another one.
249
        This just gives them something easy to call.
250
        """
251
        if isinstance(from_file, basestring):
907.1.8 by John Arbash Meinel
Changed the format for abspath. Updated branch to use a hidden _transport
252
            to_file.write(from_file)
907.1.1 by John Arbash Meinel
Reworking the Branch and Store code to support an abstracted filesystem layer.
253
        else:
254
            pumpfile(from_file, to_file)
255
907.1.2 by John Arbash Meinel
Working on making Branch() do all of it's work over a Transport.
256
    def _get_total(self, multi):
257
        """Try to figure out how many entries are in multi,
258
        but if not possible, return None.
259
        """
260
        try:
261
            return len(multi)
262
        except TypeError: # We can't tell how many, because relpaths is a generator
263
            return None
264
265
    def _update_pb(self, pb, msg, count, total):
266
        """Update the progress bar based on the current count
267
        and total available, total may be None if it was
268
        not possible to determine.
269
        """
907.1.14 by John Arbash Meinel
Handling some transport functions which take only a single argument.
270
        if pb is None:
271
            return
907.1.2 by John Arbash Meinel
Working on making Branch() do all of it's work over a Transport.
272
        if total is None:
273
            pb.update(msg, count, count+1)
274
        else:
275
            pb.update(msg, count, total)
276
907.1.14 by John Arbash Meinel
Handling some transport functions which take only a single argument.
277
    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.
278
        """Iterate over all entries in multi, passing them to func,
279
        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.
280
281
        :param expand:  If True, the entries will be passed to the function
282
                        by expanding the tuple. If False, it will be passed
283
                        as a single parameter.
907.1.2 by John Arbash Meinel
Working on making Branch() do all of it's work over a Transport.
284
        """
285
        total = self._get_total(multi)
1563.2.3 by Robert Collins
Change the return signature of transport.append and append_multi to return the length of the pre-append content.
286
        result = []
907.1.2 by John Arbash Meinel
Working on making Branch() do all of it's work over a Transport.
287
        count = 0
288
        for entry in multi:
289
            self._update_pb(pb, msg, count, total)
907.1.14 by John Arbash Meinel
Handling some transport functions which take only a single argument.
290
            if expand:
1563.2.3 by Robert Collins
Change the return signature of transport.append and append_multi to return the length of the pre-append content.
291
                result.append(func(*entry))
907.1.14 by John Arbash Meinel
Handling some transport functions which take only a single argument.
292
            else:
1563.2.3 by Robert Collins
Change the return signature of transport.append and append_multi to return the length of the pre-append content.
293
                result.append(func(entry))
907.1.2 by John Arbash Meinel
Working on making Branch() do all of it's work over a Transport.
294
            count += 1
1563.2.3 by Robert Collins
Change the return signature of transport.append and append_multi to return the length of the pre-append content.
295
        return tuple(result)
907.1.2 by John Arbash Meinel
Working on making Branch() do all of it's work over a Transport.
296
907.1.34 by John Arbash Meinel
Fixing append(), cleaning up function locations.
297
    def abspath(self, relpath):
298
        """Return the full url to the given relative path.
299
        This can be supplied with a string or a list
1442.1.44 by Robert Collins
Many transport related tweaks:
300
301
        XXX: Robert Collins 20051016 - is this really needed in the public
302
             interface ?
907.1.34 by John Arbash Meinel
Fixing append(), cleaning up function locations.
303
        """
1540.3.1 by Martin Pool
First-cut implementation of pycurl. Substantially faster than using urllib.
304
        raise NotImplementedError(self.abspath)
907.1.34 by John Arbash Meinel
Fixing append(), cleaning up function locations.
305
306
    def relpath(self, abspath):
307
        """Return the local path portion from a given absolute path.
1442.1.44 by Robert Collins
Many transport related tweaks:
308
309
        This default implementation is not suitable for filesystems with
310
        aliasing, such as that given by symlinks, where a path may not 
311
        start with our base, but still be a relpath once aliasing is 
312
        resolved.
907.1.34 by John Arbash Meinel
Fixing append(), cleaning up function locations.
313
        """
1185.31.44 by John Arbash Meinel
Cleaned up Exceptions for all transports.
314
        # TODO: This might want to use bzrlib.osutils.relpath
315
        #       but we have to watch out because of the prefix issues
1530.1.3 by Robert Collins
transport implementations now tested consistently.
316
        if not (abspath == self.base[:-1] or abspath.startswith(self.base)):
1185.31.44 by John Arbash Meinel
Cleaned up Exceptions for all transports.
317
            raise errors.PathNotChild(abspath, self.base)
1442.1.44 by Robert Collins
Many transport related tweaks:
318
        pl = len(self.base)
1530.1.3 by Robert Collins
transport implementations now tested consistently.
319
        return abspath[pl:].strip('/')
907.1.34 by John Arbash Meinel
Fixing append(), cleaning up function locations.
320
1685.1.9 by John Arbash Meinel
Updated LocalTransport so that it's base is now a URL rather than a local path. This helps consistency with all other functions. To do so, I added local_abspath() which returns the local path, and local_path_to/from_url
321
    def local_abspath(self, relpath):
322
        """Return the absolute path on the local filesystem.
323
324
        This function will only be defined for Transports which have a
325
        physical local filesystem representation.
326
        """
1685.1.11 by John Arbash Meinel
Not all of the registered transports use :// as part of their path, specifically memory:/
327
        # TODO: jam 20060426 Should this raise NotLocalUrl instead?
1685.1.9 by John Arbash Meinel
Updated LocalTransport so that it's base is now a URL rather than a local path. This helps consistency with all other functions. To do so, I added local_abspath() which returns the local path, and local_path_to/from_url
328
        raise errors.TransportNotPossible('This is not a LocalTransport,'
329
            ' so there is no local representation for a path')
330
907.1.1 by John Arbash Meinel
Reworking the Branch and Store code to support an abstracted filesystem layer.
331
    def has(self, relpath):
1442.1.44 by Robert Collins
Many transport related tweaks:
332
        """Does the file relpath exist?
333
        
334
        Note that some transports MAY allow querying on directories, but this
1553.5.66 by Martin Pool
doc
335
        is not part of the protocol.  In other words, the results of 
1786.1.8 by John Arbash Meinel
[merge] Johan Rydberg test updates
336
        t.has("a_directory_name") are undefined.
1442.1.44 by Robert Collins
Many transport related tweaks:
337
        """
1540.3.1 by Martin Pool
First-cut implementation of pycurl. Substantially faster than using urllib.
338
        raise NotImplementedError(self.has)
907.1.1 by John Arbash Meinel
Reworking the Branch and Store code to support an abstracted filesystem layer.
339
907.1.36 by John Arbash Meinel
Moving the multi-get functionality higher up into the Branch class.
340
    def has_multi(self, relpaths, pb=None):
907.1.34 by John Arbash Meinel
Fixing append(), cleaning up function locations.
341
        """Return True/False for each entry in relpaths"""
342
        total = self._get_total(relpaths)
343
        count = 0
344
        for relpath in relpaths:
907.1.36 by John Arbash Meinel
Moving the multi-get functionality higher up into the Branch class.
345
            self._update_pb(pb, 'has', count, total)
907.1.34 by John Arbash Meinel
Fixing append(), cleaning up function locations.
346
            yield self.has(relpath)
347
            count += 1
348
1185.16.155 by John Arbash Meinel
Added a has_any function to the Transport API
349
    def has_any(self, relpaths):
350
        """Return True if any of the paths exist."""
351
        for relpath in relpaths:
352
            if self.has(relpath):
353
                return True
354
        return False
355
1442.1.44 by Robert Collins
Many transport related tweaks:
356
    def iter_files_recursive(self):
357
        """Iter the relative paths of files in the transports sub-tree.
1553.5.13 by Martin Pool
New Transport.rename that mustn't overwrite
358
359
        *NOTE*: This only lists *files*, not subdirectories!
1442.1.44 by Robert Collins
Many transport related tweaks:
360
        
361
        As with other listing functions, only some transports implement this,.
362
        you may check via is_listable to determine if it will.
363
        """
1530.1.4 by Robert Collins
integrate Memory tests into transport interface tests.
364
        raise errors.TransportNotPossible("This transport has not "
1530.1.21 by Robert Collins
Review feedback fixes.
365
                                          "implemented iter_files_recursive "
1530.1.4 by Robert Collins
integrate Memory tests into transport interface tests.
366
                                          "(but must claim to be listable "
367
                                          "to trigger this error).")
1442.1.44 by Robert Collins
Many transport related tweaks:
368
907.1.50 by John Arbash Meinel
Removed encode/decode from Transport.put/get, added more exceptions that can be thrown.
369
    def get(self, relpath):
907.1.1 by John Arbash Meinel
Reworking the Branch and Store code to support an abstracted filesystem layer.
370
        """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
371
372
        :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.
373
        """
1540.3.1 by Martin Pool
First-cut implementation of pycurl. Substantially faster than using urllib.
374
        raise NotImplementedError(self.get)
907.1.1 by John Arbash Meinel
Reworking the Branch and Store code to support an abstracted filesystem layer.
375
1594.2.5 by Robert Collins
Readv patch from Johan Rydberg giving knits partial download support.
376
    def readv(self, relpath, offsets):
377
        """Get parts of the file at the given relative path.
378
379
        :offsets: A list of (offset, size) tuples.
380
        :return: A list or generator of (offset, data) tuples
381
        """
1864.5.1 by John Arbash Meinel
Change the readv combining algorithm for one that is easier to test.
382
        if not offsets:
1594.2.16 by Robert Collins
Coalesce readv requests on file based transports.
383
            return
1864.5.1 by John Arbash Meinel
Change the readv combining algorithm for one that is easier to test.
384
1864.5.7 by John Arbash Meinel
remove disable prefetch support
385
        fp = self.get(relpath)
1864.5.11 by John Arbash Meinel
Factor out the workhorse of Transport.readv() into a helper function, and re-use that function in sftp but with a non-prefetch file.
386
        return self._seek_and_read(fp, offsets)
387
388
    def _seek_and_read(self, fp, offsets):
389
        """An implementation of readv that uses fp.seek and fp.read.
390
391
        This uses _coalesce_offsets to issue larger reads and fewer seeks.
392
393
        :param fp: A file-like object that supports seek() and read(size)
394
        :param offsets: A list of offsets to be read from the given file.
395
        :return: yield (pos, data) tuples for each request
396
        """
1864.5.2 by John Arbash Meinel
always read in sorted order, and return in requested order, but only cache what is currently out of order
397
        # We are going to iterate multiple times, we need a list
398
        offsets = list(offsets)
399
        sorted_offsets = sorted(offsets)
400
401
        # turn the list of offsets into a stack
1864.5.9 by John Arbash Meinel
Switch to returning an object to make the api more understandable.
402
        offset_stack = iter(offsets)
403
        cur_offset_and_size = offset_stack.next()
1864.5.2 by John Arbash Meinel
always read in sorted order, and return in requested order, but only cache what is currently out of order
404
        coalesced = self._coalesce_offsets(sorted_offsets,
1864.5.3 by John Arbash Meinel
Allow collapsing ranges even if they are just 'close'
405
                               limit=self._max_readv_combine,
406
                               fudge_factor=self._bytes_to_read_before_seek)
1864.5.2 by John Arbash Meinel
always read in sorted order, and return in requested order, but only cache what is currently out of order
407
408
        # Cache the results, but only until they have been fulfilled
409
        data_map = {}
1864.5.9 by John Arbash Meinel
Switch to returning an object to make the api more understandable.
410
        for c_offset in coalesced:
1864.5.11 by John Arbash Meinel
Factor out the workhorse of Transport.readv() into a helper function, and re-use that function in sftp but with a non-prefetch file.
411
            # TODO: jam 20060724 it might be faster to not issue seek if 
412
            #       we are already at the right location. This should be
413
            #       benchmarked.
1864.5.9 by John Arbash Meinel
Switch to returning an object to make the api more understandable.
414
            fp.seek(c_offset.start)
415
            data = fp.read(c_offset.length)
416
            for suboffset, subsize in c_offset.ranges:
417
                key = (c_offset.start+suboffset, subsize)
418
                data_map[key] = data[suboffset:suboffset+subsize]
1864.5.1 by John Arbash Meinel
Change the readv combining algorithm for one that is easier to test.
419
1864.5.8 by John Arbash Meinel
Cleanup and NEWS
420
            # Now that we've read some data, see if we can yield anything back
1864.5.9 by John Arbash Meinel
Switch to returning an object to make the api more understandable.
421
            while cur_offset_and_size in data_map:
422
                this_data = data_map.pop(cur_offset_and_size)
423
                yield cur_offset_and_size[0], this_data
424
                cur_offset_and_size = offset_stack.next()
1864.5.1 by John Arbash Meinel
Change the readv combining algorithm for one that is easier to test.
425
426
    @staticmethod
1864.5.3 by John Arbash Meinel
Allow collapsing ranges even if they are just 'close'
427
    def _coalesce_offsets(offsets, limit, fudge_factor):
1864.5.1 by John Arbash Meinel
Change the readv combining algorithm for one that is easier to test.
428
        """Yield coalesced offsets.
429
430
        With a long list of neighboring requests, combine them
431
        into a single large request, while retaining the original
432
        offsets.
433
        Turns  [(15, 10), (25, 10)] => [(15, 20, [(0, 10), (10, 10)])]
434
435
        :param offsets: A list of (start, length) pairs
436
        :param limit: Only combine a maximum of this many pairs
437
                      Some transports penalize multiple reads more than
438
                      others, and sometimes it is better to return early.
439
                      0 means no limit
1864.5.3 by John Arbash Meinel
Allow collapsing ranges even if they are just 'close'
440
        :param fudge_factor: All transports have some level of 'it is
441
                better to read some more data and throw it away rather 
442
                than seek', so collapse if we are 'close enough'
1864.5.9 by John Arbash Meinel
Switch to returning an object to make the api more understandable.
443
        :return: yield _CoalescedOffset objects, which have members for wher
444
                to start, how much to read, and how to split those 
445
                chunks back up
1864.5.1 by John Arbash Meinel
Change the readv combining algorithm for one that is easier to test.
446
        """
447
        last_end = None
1864.5.9 by John Arbash Meinel
Switch to returning an object to make the api more understandable.
448
        cur = _CoalescedOffset(None, None, [])
1864.5.1 by John Arbash Meinel
Change the readv combining algorithm for one that is easier to test.
449
450
        for start, size in offsets:
451
            end = start + size
452
            if (last_end is not None 
1864.5.3 by John Arbash Meinel
Allow collapsing ranges even if they are just 'close'
453
                and start <= last_end + fudge_factor
1864.5.9 by John Arbash Meinel
Switch to returning an object to make the api more understandable.
454
                and start >= cur.start
455
                and (limit <= 0 or len(cur.ranges) < limit)):
456
                cur.length = end - cur.start
457
                cur.ranges.append((start-cur.start, size))
1594.2.16 by Robert Collins
Coalesce readv requests on file based transports.
458
            else:
1864.5.9 by John Arbash Meinel
Switch to returning an object to make the api more understandable.
459
                if cur.start is not None:
460
                    yield cur
461
                cur = _CoalescedOffset(start, size, [(0, size)])
1864.5.1 by John Arbash Meinel
Change the readv combining algorithm for one that is easier to test.
462
            last_end = end
463
1864.5.9 by John Arbash Meinel
Switch to returning an object to make the api more understandable.
464
        if cur.start is not None:
465
            yield cur
1864.5.1 by John Arbash Meinel
Change the readv combining algorithm for one that is easier to test.
466
467
        return
1594.2.5 by Robert Collins
Readv patch from Johan Rydberg giving knits partial download support.
468
907.1.50 by John Arbash Meinel
Removed encode/decode from Transport.put/get, added more exceptions that can be thrown.
469
    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.
470
        """Get a list of file-like objects, one for each entry in relpaths.
471
472
        :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.
473
        :param pb:  An optional ProgressBar for indicating percent done.
474
        :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.
475
        """
907.1.2 by John Arbash Meinel
Working on making Branch() do all of it's work over a Transport.
476
        # TODO: Consider having this actually buffer the requests,
477
        # in the default mode, it probably won't give worse performance,
478
        # and all children wouldn't have to implement buffering
907.1.16 by John Arbash Meinel
Fixing a few cut&paste typos.
479
        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.
480
        count = 0
907.1.1 by John Arbash Meinel
Reworking the Branch and Store code to support an abstracted filesystem layer.
481
        for relpath in relpaths:
907.1.16 by John Arbash Meinel
Fixing a few cut&paste typos.
482
            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.
483
            yield self.get(relpath)
907.1.2 by John Arbash Meinel
Working on making Branch() do all of it's work over a Transport.
484
            count += 1
907.1.1 by John Arbash Meinel
Reworking the Branch and Store code to support an abstracted filesystem layer.
485
1185.58.2 by John Arbash Meinel
Added mode to the appropriate transport functions, and tests to make sure they work.
486
    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.
487
        """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
488
489
        :param relpath: Location to put the contents, relative to base.
490
        :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.
491
        :param mode: The mode for the newly created file, 
492
                     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.
493
        """
1540.3.1 by Martin Pool
First-cut implementation of pycurl. Substantially faster than using urllib.
494
        raise NotImplementedError(self.put)
907.1.1 by John Arbash Meinel
Reworking the Branch and Store code to support an abstracted filesystem layer.
495
1185.58.2 by John Arbash Meinel
Added mode to the appropriate transport functions, and tests to make sure they work.
496
    def put_multi(self, files, mode=None, pb=None):
1530.1.3 by Robert Collins
transport implementations now tested consistently.
497
        """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.
498
499
        :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.
500
        :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.
501
        :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.
502
        :return: The number of files copied.
503
        """
1185.58.2 by John Arbash Meinel
Added mode to the appropriate transport functions, and tests to make sure they work.
504
        def put(path, f):
505
            self.put(path, f, mode=mode)
1563.2.3 by Robert Collins
Change the return signature of transport.append and append_multi to return the length of the pre-append content.
506
        return len(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.
507
1185.58.2 by John Arbash Meinel
Added mode to the appropriate transport functions, and tests to make sure they work.
508
    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.
509
        """Create a directory at the given path."""
1540.3.1 by Martin Pool
First-cut implementation of pycurl. Substantially faster than using urllib.
510
        raise NotImplementedError(self.mkdir)
907.1.2 by John Arbash Meinel
Working on making Branch() do all of it's work over a Transport.
511
1185.58.2 by John Arbash Meinel
Added mode to the appropriate transport functions, and tests to make sure they work.
512
    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
513
        """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.
514
        def mkdir(path):
515
            self.mkdir(path, mode=mode)
1563.2.3 by Robert Collins
Change the return signature of transport.append and append_multi to return the length of the pre-append content.
516
        return len(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
517
907.1.50 by John Arbash Meinel
Removed encode/decode from Transport.put/get, added more exceptions that can be thrown.
518
    def append(self, relpath, f):
907.1.1 by John Arbash Meinel
Reworking the Branch and Store code to support an abstracted filesystem layer.
519
        """Append the text in the file-like or string object to 
520
        the supplied location.
1563.2.3 by Robert Collins
Change the return signature of transport.append and append_multi to return the length of the pre-append content.
521
522
        returns the length of f before the content was written to it.
907.1.1 by John Arbash Meinel
Reworking the Branch and Store code to support an abstracted filesystem layer.
523
        """
1540.3.1 by Martin Pool
First-cut implementation of pycurl. Substantially faster than using urllib.
524
        raise NotImplementedError(self.append)
907.1.1 by John Arbash Meinel
Reworking the Branch and Store code to support an abstracted filesystem layer.
525
1185.11.19 by John Arbash Meinel
Testing put and append, also testing agaist file-like objects as well as strings.
526
    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.
527
        """Append the text in each file-like or string object to
528
        the supplied location.
907.1.2 by John Arbash Meinel
Working on making Branch() do all of it's work over a Transport.
529
530
        :param files: A set of (path, f) entries
531
        :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.
532
        """
907.1.14 by John Arbash Meinel
Handling some transport functions which take only a single argument.
533
        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.
534
535
    def copy(self, rel_from, rel_to):
1530.1.3 by Robert Collins
transport implementations now tested consistently.
536
        """Copy the item at rel_from to the location at rel_to.
537
        
538
        Override this for efficiency if a specific transport can do it 
539
        faster than this default implementation.
540
        """
541
        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.
542
907.1.28 by John Arbash Meinel
Added pb to function that were missing, implemented a basic double-dispatch copy_to function.
543
    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.
544
        """Copy a bunch of entries.
545
        
546
        :param relpaths: A list of tuples of the form [(from, to), (from, to),...]
547
        """
548
        # This is the non-pipelined implementation, so that
549
        # implementors don't have to implement everything.
907.1.14 by John Arbash Meinel
Handling some transport functions which take only a single argument.
550
        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.
551
1185.58.2 by John Arbash Meinel
Added mode to the appropriate transport functions, and tests to make sure they work.
552
    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.
553
        """Copy a set of entries from self into another Transport.
554
555
        :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.
556
        :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
557
        TODO: This interface needs to be updated so that the target location
558
              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.
559
        """
560
        # The dummy implementation just does a simple get + put
561
        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.
562
            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.
563
1563.2.3 by Robert Collins
Change the return signature of transport.append and append_multi to return the length of the pre-append content.
564
        return len(self._iterate_over(relpaths, copy_entry, pb, 'copy_to', expand=False))
907.1.28 by John Arbash Meinel
Added pb to function that were missing, implemented a basic double-dispatch copy_to function.
565
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
566
    def copy_tree(self, from_relpath, to_relpath):
567
        """Copy a subtree from one relpath to another.
568
569
        If a faster implementation is available, specific transports should 
570
        implement it.
571
        """
572
        source = self.clone(from_relpath)
573
        self.mkdir(to_relpath)
574
        target = self.clone(to_relpath)
575
        files = []
576
        directories = ['.']
577
        while directories:
578
            dir = directories.pop()
579
            if dir != '.':
580
                target.mkdir(dir)
581
            for path in source.list_dir(dir):
582
                path = dir + '/' + path
583
                stat = source.stat(path)
584
                if S_ISDIR(stat.st_mode):
585
                    directories.append(path)
586
                else:
587
                    files.append(path)
588
        source.copy_to(files, target)
589
1553.5.13 by Martin Pool
New Transport.rename that mustn't overwrite
590
    def rename(self, rel_from, rel_to):
591
        """Rename a file or directory.
592
593
        This *must* fail if the destination is a nonempty directory - it must
594
        not automatically remove it.  It should raise DirectoryNotEmpty, or
595
        some other PathError if the case can't be specifically detected.
596
597
        If the destination is an empty directory or a file this function may
598
        either fail or succeed, depending on the underlying transport.  It
599
        should not attempt to remove the destination if overwriting is not the
600
        native transport behaviour.  If at all possible the transport should
601
        ensure that the rename either completes or not, without leaving the
602
        destination deleted and the new file not moved in place.
603
604
        This is intended mainly for use in implementing LockDir.
605
        """
606
        # transports may need to override this
1553.5.17 by Martin Pool
Transport.rename should be unimplemented in base class
607
        raise NotImplementedError(self.rename)
1553.5.13 by Martin Pool
New Transport.rename that mustn't overwrite
608
907.1.1 by John Arbash Meinel
Reworking the Branch and Store code to support an abstracted filesystem layer.
609
    def move(self, rel_from, rel_to):
1530.1.3 by Robert Collins
transport implementations now tested consistently.
610
        """Move the item at rel_from to the location at rel_to.
1553.5.13 by Martin Pool
New Transport.rename that mustn't overwrite
611
612
        The destination is deleted if possible, even if it's a non-empty
613
        directory tree.
1530.1.3 by Robert Collins
transport implementations now tested consistently.
614
        
615
        If a transport can directly implement this it is suggested that
616
        it do so for efficiency.
617
        """
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
618
        if S_ISDIR(self.stat(rel_from).st_mode):
619
            self.copy_tree(rel_from, rel_to)
620
            self.delete_tree(rel_from)
621
        else:
622
            self.copy(rel_from, rel_to)
623
            self.delete(rel_from)
907.1.1 by John Arbash Meinel
Reworking the Branch and Store code to support an abstracted filesystem layer.
624
907.1.28 by John Arbash Meinel
Added pb to function that were missing, implemented a basic double-dispatch copy_to function.
625
    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.
626
        """Move a bunch of entries.
627
        
628
        :param relpaths: A list of tuples of the form [(from1, to1), (from2, to2),...]
629
        """
907.1.14 by John Arbash Meinel
Handling some transport functions which take only a single argument.
630
        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.
631
632
    def move_multi_to(self, relpaths, rel_to):
633
        """Move a bunch of entries to a single location.
634
        This differs from move_multi in that you give a list of from, and
635
        a single destination, rather than multiple destinations.
636
637
        :param relpaths: A list of relative paths [from1, from2, from3, ...]
638
        :param rel_to: A directory where each entry should be placed.
639
        """
640
        # This is not implemented, because you need to do special tricks to
641
        # extract the basename, and add it to rel_to
1540.3.1 by Martin Pool
First-cut implementation of pycurl. Substantially faster than using urllib.
642
        raise NotImplementedError(self.move_multi_to)
907.1.1 by John Arbash Meinel
Reworking the Branch and Store code to support an abstracted filesystem layer.
643
644
    def delete(self, relpath):
645
        """Delete the item at relpath"""
1540.3.1 by Martin Pool
First-cut implementation of pycurl. Substantially faster than using urllib.
646
        raise NotImplementedError(self.delete)
907.1.1 by John Arbash Meinel
Reworking the Branch and Store code to support an abstracted filesystem layer.
647
907.1.28 by John Arbash Meinel
Added pb to function that were missing, implemented a basic double-dispatch copy_to function.
648
    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.
649
        """Queue up a bunch of deletes to be done.
650
        """
907.1.14 by John Arbash Meinel
Handling some transport functions which take only a single argument.
651
        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.
652
1534.4.15 by Robert Collins
Remove shutil dependency in upgrade - create a delete_tree method for transports.
653
    def delete_tree(self, relpath):
654
        """Delete an entire tree. This may require a listable transport."""
655
        subtree = self.clone(relpath)
656
        files = []
657
        directories = ['.']
658
        pending_rmdirs = []
659
        while directories:
660
            dir = directories.pop()
661
            if dir != '.':
662
                pending_rmdirs.append(dir)
663
            for path in subtree.list_dir(dir):
664
                path = dir + '/' + path
665
                stat = subtree.stat(path)
666
                if S_ISDIR(stat.st_mode):
667
                    directories.append(path)
668
                else:
669
                    files.append(path)
670
        subtree.delete_multi(files)
671
        pending_rmdirs.reverse()
672
        for dir in pending_rmdirs:
673
            subtree.rmdir(dir)
674
        self.rmdir(relpath)
675
1534.5.7 by Robert Collins
Start factoring out the upgrade policy logic.
676
    def __repr__(self):
677
        return "<%s.%s url=%s>" % (self.__module__, self.__class__.__name__, self.base)
678
907.1.2 by John Arbash Meinel
Working on making Branch() do all of it's work over a Transport.
679
    def stat(self, relpath):
680
        """Return the stat information for a file.
681
        WARNING: This may not be implementable for all protocols, so use
682
        sparingly.
1442.1.44 by Robert Collins
Many transport related tweaks:
683
        NOTE: This returns an object with fields such as 'st_size'. It MAY
684
        or MAY NOT return the literal result of an os.stat() call, so all
685
        access should be via named fields.
686
        ALSO NOTE: Stats of directories may not be supported on some 
687
        transports.
907.1.2 by John Arbash Meinel
Working on making Branch() do all of it's work over a Transport.
688
        """
1540.3.1 by Martin Pool
First-cut implementation of pycurl. Substantially faster than using urllib.
689
        raise NotImplementedError(self.stat)
907.1.2 by John Arbash Meinel
Working on making Branch() do all of it's work over a Transport.
690
1534.4.15 by Robert Collins
Remove shutil dependency in upgrade - create a delete_tree method for transports.
691
    def rmdir(self, relpath):
692
        """Remove a directory at the given path."""
693
        raise NotImplementedError
694
907.1.2 by John Arbash Meinel
Working on making Branch() do all of it's work over a Transport.
695
    def stat_multi(self, relpaths, pb=None):
696
        """Stat multiple files and return the information.
697
        """
698
        #TODO:  Is it worth making this a generator instead of a
699
        #       returning a list?
700
        stats = []
701
        def gather(path):
702
            stats.append(self.stat(path))
703
907.1.14 by John Arbash Meinel
Handling some transport functions which take only a single argument.
704
        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.
705
        return stats
706
1400.1.1 by Robert Collins
implement a basic test for the ui branch command from http servers
707
    def listable(self):
708
        """Return True if this store supports listing."""
1540.3.1 by Martin Pool
First-cut implementation of pycurl. Substantially faster than using urllib.
709
        raise NotImplementedError(self.listable)
907.1.1 by John Arbash Meinel
Reworking the Branch and Store code to support an abstracted filesystem layer.
710
711
    def list_dir(self, relpath):
712
        """Return a list of all files at the given location.
713
        WARNING: many transports do not support this, so trying avoid using
714
        it if at all possible.
715
        """
1185.31.44 by John Arbash Meinel
Cleaned up Exceptions for all transports.
716
        raise errors.TransportNotPossible("This transport has not "
1530.1.21 by Robert Collins
Review feedback fixes.
717
                                          "implemented list_dir "
1530.1.3 by Robert Collins
transport implementations now tested consistently.
718
                                          "(but must claim to be listable "
719
                                          "to trigger this error).")
907.1.1 by John Arbash Meinel
Reworking the Branch and Store code to support an abstracted filesystem layer.
720
907.1.24 by John Arbash Meinel
Remote functionality work.
721
    def lock_read(self, relpath):
722
        """Lock the given file for shared (read) access.
723
        WARNING: many transports do not support this, so trying avoid using it
724
725
        :return: A lock object, which should contain an unlock() function.
726
        """
1540.3.1 by Martin Pool
First-cut implementation of pycurl. Substantially faster than using urllib.
727
        raise NotImplementedError(self.lock_read)
907.1.24 by John Arbash Meinel
Remote functionality work.
728
729
    def lock_write(self, relpath):
730
        """Lock the given file for exclusive (write) access.
731
        WARNING: many transports do not support this, so trying avoid using it
732
733
        :return: A lock object, which should contain an unlock() function.
734
        """
1540.3.1 by Martin Pool
First-cut implementation of pycurl. Substantially faster than using urllib.
735
        raise NotImplementedError(self.lock_write)
907.1.24 by John Arbash Meinel
Remote functionality work.
736
1530.1.3 by Robert Collins
transport implementations now tested consistently.
737
    def is_readonly(self):
738
        """Return true if this connection cannot be written to."""
739
        return False
740
1608.2.7 by Martin Pool
Rename supports_unix_modebits to _can_roundtrip_unix_modebits for clarity
741
    def _can_roundtrip_unix_modebits(self):
1608.2.5 by Martin Pool
Add Transport.supports_unix_modebits, so tests can
742
        """Return true if this transport can store and retrieve unix modebits.
743
744
        (For example, 0700 to make a directory owner-private.)
745
        
746
        Note: most callers will not want to switch on this, but should rather 
747
        just try and set permissions and let them be either stored or not.
748
        This is intended mainly for the use of the test suite.
749
        
750
        Warning: this is not guaranteed to be accurate as sometimes we can't 
751
        be sure: for example with vfat mounted on unix, or a windows sftp
752
        server."""
753
        # TODO: Perhaps return a e.g. TransportCharacteristics that can answer
754
        # several questions about the transport.
755
        return False
756
907.1.24 by John Arbash Meinel
Remote functionality work.
757
1685.1.9 by John Arbash Meinel
Updated LocalTransport so that it's base is now a URL rather than a local path. This helps consistency with all other functions. To do so, I added local_abspath() which returns the local path, and local_path_to/from_url
758
# jam 20060426 For compatibility we copy the functions here
1685.1.45 by John Arbash Meinel
Moved url functions into bzrlib.urlutils
759
# TODO: The should be marked as deprecated
760
urlescape = urlutils.escape
761
urlunescape = urlutils.unescape
1685.1.9 by John Arbash Meinel
Updated LocalTransport so that it's base is now a URL rather than a local path. This helps consistency with all other functions. To do so, I added local_abspath() which returns the local path, and local_path_to/from_url
762
_urlRE = re.compile(r'^(?P<proto>[^:/\\]+)://(?P<path>.*)$')
763
764
1393.2.4 by John Arbash Meinel
All tests pass.
765
def get_transport(base):
1185.70.3 by Martin Pool
Various updates to make storage branch mergeable:
766
    """Open a transport to access a URL or directory.
767
768
    base is either a URL or a directory name.  
769
    """
1540.3.8 by Martin Pool
Some support for falling back between transport implementations.
770
    # TODO: give a better error if base looks like a url but there's no
771
    # handler for the scheme?
907.1.45 by John Arbash Meinel
Switch to registering protocol handlers, rather than just updating a dictionary.
772
    global _protocol_handlers
907.1.13 by John Arbash Meinel
Fixed bzr root.
773
    if base is None:
1685.1.11 by John Arbash Meinel
Not all of the registered transports use :// as part of their path, specifically memory:/
774
        base = '.'
1685.1.33 by John Arbash Meinel
Be more of a Nazi about URLs not being unicode
775
1843.1.1 by John Arbash Meinel
Update get_transport to raise a nicer error which includes dependency info
776
    last_err = None
777
1685.1.33 by John Arbash Meinel
Be more of a Nazi about URLs not being unicode
778
    def convert_path_to_url(base, error_str):
779
        m = _urlRE.match(base)
780
        if m:
781
            # This looks like a URL, but we weren't able to 
782
            # instantiate it as such raise an appropriate error
1843.1.1 by John Arbash Meinel
Update get_transport to raise a nicer error which includes dependency info
783
            raise errors.UnsupportedProtocol(base, last_err)
1685.1.33 by John Arbash Meinel
Be more of a Nazi about URLs not being unicode
784
        # This doesn't look like a protocol, consider it a local path
1685.1.45 by John Arbash Meinel
Moved url functions into bzrlib.urlutils
785
        new_base = urlutils.local_path_to_url(base)
1711.4.6 by John Arbash Meinel
Removing hacks for _win32_abspath, on real win32 abspath handles unicode just fine, it doesn't handle encoding into 'mbcs'
786
        mutter('converting os path %r => url %s', base, new_base)
1685.1.33 by John Arbash Meinel
Be more of a Nazi about URLs not being unicode
787
        return new_base
788
789
    # Catch any URLs which are passing Unicode rather than ASCII
790
    try:
791
        base = base.encode('ascii')
792
    except UnicodeError:
793
        # Only local paths can be Unicode
794
        base = convert_path_to_url(base,
795
            'URLs must be properly escaped (protocol: %s)')
1685.1.11 by John Arbash Meinel
Not all of the registered transports use :// as part of their path, specifically memory:/
796
    
797
    for proto, factory_list in _protocol_handlers.iteritems():
798
        if proto is not None and base.startswith(proto):
1843.1.1 by John Arbash Meinel
Update get_transport to raise a nicer error which includes dependency info
799
            t, last_err = _try_transport_factories(base, factory_list)
1685.1.11 by John Arbash Meinel
Not all of the registered transports use :// as part of their path, specifically memory:/
800
            if t:
801
                return t
1685.1.9 by John Arbash Meinel
Updated LocalTransport so that it's base is now a URL rather than a local path. This helps consistency with all other functions. To do so, I added local_abspath() which returns the local path, and local_path_to/from_url
802
1685.1.33 by John Arbash Meinel
Be more of a Nazi about URLs not being unicode
803
    # We tried all the different protocols, now try one last time
804
    # as a local protocol
805
    base = convert_path_to_url(base, 'Unsupported protocol: %s')
1685.1.11 by John Arbash Meinel
Not all of the registered transports use :// as part of their path, specifically memory:/
806
1540.3.12 by Martin Pool
Multiple transports can be registered for any protocol, and they are
807
    # The default handler is the filesystem handler, stored as protocol None
1843.1.1 by John Arbash Meinel
Update get_transport to raise a nicer error which includes dependency info
808
    return _try_transport_factories(base, _protocol_handlers[None])[0]
1540.3.12 by Martin Pool
Multiple transports can be registered for any protocol, and they are
809
810
811
def _try_transport_factories(base, factory_list):
1843.1.1 by John Arbash Meinel
Update get_transport to raise a nicer error which includes dependency info
812
    last_err = None
1540.3.12 by Martin Pool
Multiple transports can be registered for any protocol, and they are
813
    for factory in factory_list:
814
        try:
1843.1.1 by John Arbash Meinel
Update get_transport to raise a nicer error which includes dependency info
815
            return factory(base), None
1540.3.12 by Martin Pool
Multiple transports can be registered for any protocol, and they are
816
        except DependencyNotPresent, e:
817
            mutter("failed to instantiate transport %r for %r: %r" %
818
                    (factory, base, e))
1843.1.1 by John Arbash Meinel
Update get_transport to raise a nicer error which includes dependency info
819
            last_err = e
1540.3.12 by Martin Pool
Multiple transports can be registered for any protocol, and they are
820
            continue
1843.1.1 by John Arbash Meinel
Update get_transport to raise a nicer error which includes dependency info
821
    return None, last_err
1185.16.81 by mbp at sourcefrog
[merge] robert
822
1185.16.78 by Martin Pool
- load paramiko sftp transport by default
823
1530.1.3 by Robert Collins
transport implementations now tested consistently.
824
class Server(object):
1530.1.21 by Robert Collins
Review feedback fixes.
825
    """A Transport Server.
826
    
827
    The Server interface provides a server for a given transport. We use
828
    these servers as loopback testing tools. For any given transport the
829
    Servers it provides must either allow writing, or serve the contents
830
    of os.getcwdu() at the time setUp is called.
831
    
832
    Note that these are real servers - they must implement all the things
833
    that we want bzr transports to take advantage of.
834
    """
1530.1.3 by Robert Collins
transport implementations now tested consistently.
835
836
    def setUp(self):
837
        """Setup the server to service requests."""
838
839
    def tearDown(self):
840
        """Remove the server and cleanup any resources it owns."""
841
842
    def get_url(self):
1530.1.21 by Robert Collins
Review feedback fixes.
843
        """Return a url for this server.
1530.1.3 by Robert Collins
transport implementations now tested consistently.
844
        
845
        If the transport does not represent a disk directory (i.e. it is 
1530.1.21 by Robert Collins
Review feedback fixes.
846
        a database like svn, or a memory only transport, it should return
1530.1.3 by Robert Collins
transport implementations now tested consistently.
847
        a connection to a newly established resource for this Server.
1530.1.21 by Robert Collins
Review feedback fixes.
848
        Otherwise it should return a url that will provide access to the path
849
        that was os.getcwdu() when setUp() was called.
1530.1.3 by Robert Collins
transport implementations now tested consistently.
850
        
851
        Subsequent calls will return the same resource.
852
        """
853
        raise NotImplementedError
854
1530.1.9 by Robert Collins
Test bogus urls with http in the new infrastructure.
855
    def get_bogus_url(self):
856
        """Return a url for this protocol, that will fail to connect."""
857
        raise NotImplementedError
858
1530.1.3 by Robert Collins
transport implementations now tested consistently.
859
1530.1.1 by Robert Collins
Minimal infrastructure to test TransportTestProviderAdapter.
860
class TransportTestProviderAdapter(object):
1530.1.21 by Robert Collins
Review feedback fixes.
861
    """A tool to generate a suite testing all transports for a single test.
862
863
    This is done by copying the test once for each transport and injecting
864
    the transport_class and transport_server classes into each copy. Each copy
865
    is also given a new id() to make it easy to identify.
866
    """
1530.1.1 by Robert Collins
Minimal infrastructure to test TransportTestProviderAdapter.
867
868
    def adapt(self, test):
869
        result = TestSuite()
870
        for klass, server_factory in self._test_permutations():
871
            new_test = deepcopy(test)
872
            new_test.transport_class = klass
873
            new_test.transport_server = server_factory
1530.1.3 by Robert Collins
transport implementations now tested consistently.
874
            def make_new_test_id():
875
                new_id = "%s(%s)" % (new_test.id(), server_factory.__name__)
876
                return lambda: new_id
877
            new_test.id = make_new_test_id()
1530.1.1 by Robert Collins
Minimal infrastructure to test TransportTestProviderAdapter.
878
            result.addTest(new_test)
879
        return result
880
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.
881
    def get_transport_test_permutations(self, module):
882
        """Get the permutations module wants to have tested."""
1540.3.6 by Martin Pool
[merge] update from bzr.dev
883
        if not hasattr(module, 'get_test_permutations'):
884
            warning("transport module %s doesn't provide get_test_permutations()"
885
                    % module.__name__)
886
            return []
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.
887
        return module.get_test_permutations()
888
1530.1.1 by Robert Collins
Minimal infrastructure to test TransportTestProviderAdapter.
889
    def _test_permutations(self):
890
        """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.
891
        result = []
892
        for module in _get_transport_modules():
1534.7.70 by abentley
Fix bug when no paramiko
893
            try:
894
                result.extend(self.get_transport_test_permutations(reduce(getattr, 
895
                    (module).split('.')[1:],
1185.71.1 by John Arbash Meinel
Allow selftest to run, even if we can't load a transport.
896
                     __import__(module))))
1185.62.24 by John Arbash Meinel
Changing the exception that sftp.py throws when it can't find paramiko, so that the test suite can handle it.
897
            except errors.DependencyNotPresent, e:
898
                # Continue even if a dependency prevents us 
899
                # from running this test
1534.7.70 by abentley
Fix bug when no paramiko
900
                pass
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.
901
        return result
1594.2.24 by Robert Collins
Make use of the transaction finalisation warning support to implement in-knit caching.
902
903
904
class TransportLogger(object):
905
    """Adapt a transport to get clear logging data on api calls.
906
    
907
    Feel free to extend to log whatever calls are of interest.
908
    """
909
910
    def __init__(self, adapted):
911
        self._adapted = adapted
912
        self._calls = []
913
914
    def get(self, name):
915
        self._calls.append((name,))
916
        return self._adapted.get(name)
917
918
    def __getattr__(self, name):
919
        """Thunk all undefined access through to self._adapted."""
920
        # raise AttributeError, name 
921
        return getattr(self._adapted, name)
922
923
    def readv(self, name, offsets):
924
        self._calls.append((name, offsets))
925
        return self._adapted.readv(name, offsets)
1530.1.1 by Robert Collins
Minimal infrastructure to test TransportTestProviderAdapter.
926
        
927
1185.16.81 by mbp at sourcefrog
[merge] robert
928
# None is the default transport, for things with no url scheme
1185.16.79 by Martin Pool
Load transports when they're first used.
929
register_lazy_transport(None, 'bzrlib.transport.local', 'LocalTransport')
930
register_lazy_transport('file://', 'bzrlib.transport.local', 'LocalTransport')
931
register_lazy_transport('sftp://', 'bzrlib.transport.sftp', 'SFTPTransport')
1540.3.23 by Martin Pool
Allow urls like http+pycurl://host/ to use a particular impl
932
register_lazy_transport('http+urllib://', 'bzrlib.transport.http._urllib',
1540.3.26 by Martin Pool
[merge] bzr.dev; pycurl not updated for readv yet
933
                        'HttpTransport_urllib')
1540.3.23 by Martin Pool
Allow urls like http+pycurl://host/ to use a particular impl
934
register_lazy_transport('https+urllib://', 'bzrlib.transport.http._urllib',
1540.3.26 by Martin Pool
[merge] bzr.dev; pycurl not updated for readv yet
935
                        'HttpTransport_urllib')
936
register_lazy_transport('http+pycurl://', 'bzrlib.transport.http._pycurl',
937
                        'PyCurlTransport')
938
register_lazy_transport('https+pycurl://', 'bzrlib.transport.http._pycurl',
939
                        'PyCurlTransport')
940
register_lazy_transport('http://', 'bzrlib.transport.http._urllib',
941
                        'HttpTransport_urllib')
942
register_lazy_transport('https://', 'bzrlib.transport.http._urllib',
943
                        'HttpTransport_urllib')
1540.3.7 by Martin Pool
Prepare to select a transport depending on what dependencies can be satisfied.
944
register_lazy_transport('http://', 'bzrlib.transport.http._pycurl', 'PyCurlTransport')
945
register_lazy_transport('https://', 'bzrlib.transport.http._pycurl', 'PyCurlTransport')
1185.36.4 by Daniel Silverstone
Add FTP transport
946
register_lazy_transport('ftp://', 'bzrlib.transport.ftp', 'FtpTransport')
947
register_lazy_transport('aftp://', 'bzrlib.transport.ftp', 'FtpTransport')
1685.1.41 by John Arbash Meinel
memory is now memory://, need to fix the test cases.
948
register_lazy_transport('memory://', 'bzrlib.transport.memory', 'MemoryTransport')
1534.4.9 by Robert Collins
Add a readonly decorator for transports.
949
register_lazy_transport('readonly+', 'bzrlib.transport.readonly', 'ReadonlyTransportDecorator')
1558.10.2 by Robert Collins
Refactor the FakeNFS support into a TransportDecorator.
950
register_lazy_transport('fakenfs+', 'bzrlib.transport.fakenfs', 'FakeNFSTransportDecorator')
1608.2.4 by Martin Pool
[broken] Add FakeFVATTransport
951
register_lazy_transport('vfat+', 
952
                        'bzrlib.transport.fakevfat',
953
                        'FakeVFATTransportDecorator')