~bzr-pqm/bzr/bzr.dev

4763.2.4 by John Arbash Meinel
merge bzr.2.1 in preparation for NEWS entry.
1
# Copyright (C) 2005-2010 Canonical Ltd
1887.1.1 by Adeodato Simó
Do not separate paragraphs in the copyright statement with blank lines,
2
#
1442.1.44 by Robert Collins
Many transport related tweaks:
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
#
1442.1.44 by Robert Collins
Many transport related tweaks:
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
#
1442.1.44 by Robert Collins
Many transport related tweaks:
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
4183.7.1 by Sabin Iacob
update FSF mailing address
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
1553.5.14 by Martin Pool
doc
16
17
"""Implementation of Transport that uses memory for its storage.
18
19
The contents of the transport will be lost when the object is discarded,
20
so this is primarily useful for testing.
21
"""
1442.1.44 by Robert Collins
Many transport related tweaks:
22
6379.6.7 by Jelmer Vernooij
Move importing from future until after doc string, otherwise the doc string will disappear.
23
from __future__ import absolute_import
24
1442.1.44 by Robert Collins
Many transport related tweaks:
25
import os
26
import errno
1773.4.1 by Martin Pool
Add pyflakes makefile target; fix many warnings
27
from stat import S_IFREG, S_IFDIR
1442.1.44 by Robert Collins
Many transport related tweaks:
28
from cStringIO import StringIO
29
4634.108.12 by John Arbash Meinel
Have MemoryTransport.tearDown unregister its transport.
30
from bzrlib import (
5017.3.4 by Vincent Ladeuil
Move MemoryServer to bzrlib.tests.test_server
31
    transport,
4634.108.12 by John Arbash Meinel
Have MemoryTransport.tearDown unregister its transport.
32
    urlutils,
33
    )
2634.1.1 by Robert Collins
(robertc) Reinstate the accidentally backed out external_url patch.
34
from bzrlib.errors import (
35
    FileExists,
36
    LockError,
37
    InProcessTransport,
38
    NoSuchFile,
39
    )
2052.6.1 by Robert Collins
``Transport.get`` has had its interface made more clear for ease of use.
40
from bzrlib.transport import (
2671.3.6 by Robert Collins
Review feedback.
41
    AppendBasedFileStream,
2671.3.2 by Robert Collins
Start open_file_stream logic.
42
    _file_streams,
2052.6.1 by Robert Collins
``Transport.get`` has had its interface made more clear for ease of use.
43
    LateReadError,
44
    )
1442.1.44 by Robert Collins
Many transport related tweaks:
45
46
1558.10.2 by Robert Collins
Refactor the FakeNFS support into a TransportDecorator.
47
1442.1.44 by Robert Collins
Many transport related tweaks:
48
class MemoryStat(object):
49
1530.1.15 by Robert Collins
Move put mode tests into test_transport_implementation.
50
    def __init__(self, size, is_dir, perms):
1442.1.44 by Robert Collins
Many transport related tweaks:
51
        self.st_size = size
1530.1.3 by Robert Collins
transport implementations now tested consistently.
52
        if not is_dir:
1553.5.65 by Martin Pool
MemoryTransport: Set better permissions on fake directory inodes
53
            if perms is None:
54
                perms = 0644
1530.1.15 by Robert Collins
Move put mode tests into test_transport_implementation.
55
            self.st_mode = S_IFREG | perms
1530.1.3 by Robert Collins
transport implementations now tested consistently.
56
        else:
1553.5.65 by Martin Pool
MemoryTransport: Set better permissions on fake directory inodes
57
            if perms is None:
58
                perms = 0755
1530.1.15 by Robert Collins
Move put mode tests into test_transport_implementation.
59
            self.st_mode = S_IFDIR | perms
1442.1.44 by Robert Collins
Many transport related tweaks:
60
61
5017.3.4 by Vincent Ladeuil
Move MemoryServer to bzrlib.tests.test_server
62
class MemoryTransport(transport.Transport):
1534.4.9 by Robert Collins
Add a readonly decorator for transports.
63
    """This is an in memory file system for transient data storage."""
1442.1.44 by Robert Collins
Many transport related tweaks:
64
1530.1.3 by Robert Collins
transport implementations now tested consistently.
65
    def __init__(self, url=""):
1442.1.44 by Robert Collins
Many transport related tweaks:
66
        """Set the 'base' path where files will be stored."""
1530.1.3 by Robert Collins
transport implementations now tested consistently.
67
        if url == "":
1685.1.42 by John Arbash Meinel
A couple more fixes to make sure memory:/// works correctly.
68
            url = "memory:///"
1530.1.3 by Robert Collins
transport implementations now tested consistently.
69
        if url[-1] != '/':
70
            url = url + '/'
71
        super(MemoryTransport, self).__init__(url)
1910.16.2 by Andrew Bennetts
Reduce transport code duplication by creating a '_combine_paths' method to Transport.
72
        split = url.find(':') + 3
73
        self._scheme = url[:split]
74
        self._cwd = url[split:]
1553.5.16 by Martin Pool
MemoryTransport.rename() must raise exceptions on collision
75
        # dictionaries from absolute path to file mode
1685.1.44 by John Arbash Meinel
Now all MemoryTransports start with a valid root.
76
        self._dirs = {'/':None}
1442.1.44 by Robert Collins
Many transport related tweaks:
77
        self._files = {}
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
78
        self._locks = {}
1442.1.44 by Robert Collins
Many transport related tweaks:
79
80
    def clone(self, offset=None):
81
        """See Transport.clone()."""
6055.2.16 by Jelmer Vernooij
Move _combine_paths to URL.
82
        path = urlutils.URL._combine_paths(self._cwd, offset)
1910.16.2 by Andrew Bennetts
Reduce transport code duplication by creating a '_combine_paths' method to Transport.
83
        if len(path) == 0 or path[-1] != '/':
84
            path += '/'
85
        url = self._scheme + path
4547.2.2 by Andrew Bennetts
Add test for read_mergeable_from_transport raising NotABundle when TooManyRedirections happens.
86
        result = self.__class__(url)
1530.1.3 by Robert Collins
transport implementations now tested consistently.
87
        result._dirs = self._dirs
88
        result._files = self._files
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
89
        result._locks = self._locks
1530.1.3 by Robert Collins
transport implementations now tested consistently.
90
        return result
1442.1.44 by Robert Collins
Many transport related tweaks:
91
92
    def abspath(self, relpath):
93
        """See Transport.abspath()."""
1636.1.1 by Robert Collins
Fix calling relpath() and abspath() on transports at their root.
94
        # while a little slow, this is sufficiently fast to not matter in our
95
        # current environment - XXX RBC 20060404 move the clone '..' handling
96
        # into here and call abspath from clone
97
        temp_t = self.clone(relpath)
1685.1.42 by John Arbash Meinel
A couple more fixes to make sure memory:/// works correctly.
98
        if temp_t.base.count('/') == 3:
1636.1.1 by Robert Collins
Fix calling relpath() and abspath() on transports at their root.
99
            return temp_t.base
100
        else:
101
            return temp_t.base[:-1]
1442.1.44 by Robert Collins
Many transport related tweaks:
102
1955.3.15 by John Arbash Meinel
Deprecate 'Transport.append' in favor of Transport.append_file or Transport.append_bytes
103
    def append_file(self, relpath, f, mode=None):
1955.3.16 by John Arbash Meinel
Switch over to Transport.append_bytes or append_files
104
        """See Transport.append_file()."""
1530.1.3 by Robert Collins
transport implementations now tested consistently.
105
        _abspath = self._abspath(relpath)
106
        self._check_parent(_abspath)
1530.1.15 by Robert Collins
Move put mode tests into test_transport_implementation.
107
        orig_content, orig_mode = self._files.get(_abspath, ("", None))
1666.1.6 by Robert Collins
Make knit the default format.
108
        if mode is None:
109
            mode = orig_mode
110
        self._files[_abspath] = (orig_content + f.read(), 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.
111
        return len(orig_content)
1442.1.44 by Robert Collins
Many transport related tweaks:
112
1530.1.3 by Robert Collins
transport implementations now tested consistently.
113
    def _check_parent(self, _abspath):
114
        dir = os.path.dirname(_abspath)
115
        if dir != '/':
1442.1.44 by Robert Collins
Many transport related tweaks:
116
            if not dir in self._dirs:
1530.1.3 by Robert Collins
transport implementations now tested consistently.
117
                raise NoSuchFile(_abspath)
1442.1.44 by Robert Collins
Many transport related tweaks:
118
119
    def has(self, relpath):
120
        """See Transport.has()."""
1530.1.3 by Robert Collins
transport implementations now tested consistently.
121
        _abspath = self._abspath(relpath)
1711.9.11 by John Arbash Meinel
change return foo in bar to return (foo in bar)
122
        return (_abspath in self._files) or (_abspath in self._dirs)
1530.1.3 by Robert Collins
transport implementations now tested consistently.
123
124
    def delete(self, relpath):
125
        """See Transport.delete()."""
126
        _abspath = self._abspath(relpath)
127
        if not _abspath in self._files:
128
            raise NoSuchFile(relpath)
129
        del self._files[_abspath]
1442.1.44 by Robert Collins
Many transport related tweaks:
130
2634.1.1 by Robert Collins
(robertc) Reinstate the accidentally backed out external_url patch.
131
    def external_url(self):
132
        """See bzrlib.transport.Transport.external_url."""
133
        # MemoryTransport's are only accessible in-process
134
        # so we raise here
135
        raise InProcessTransport(self)
136
2164.2.15 by Vincent Ladeuil
Http redirections are not followed by default. Do not use hints
137
    def get(self, relpath):
1442.1.44 by Robert Collins
Many transport related tweaks:
138
        """See Transport.get()."""
1530.1.3 by Robert Collins
transport implementations now tested consistently.
139
        _abspath = self._abspath(relpath)
140
        if not _abspath in self._files:
2052.6.1 by Robert Collins
``Transport.get`` has had its interface made more clear for ease of use.
141
            if _abspath in self._dirs:
142
                return LateReadError(relpath)
143
            else:
144
                raise NoSuchFile(relpath)
1530.1.15 by Robert Collins
Move put mode tests into test_transport_implementation.
145
        return StringIO(self._files[_abspath][0])
1442.1.44 by Robert Collins
Many transport related tweaks:
146
1955.3.6 by John Arbash Meinel
Lots of deprecation warnings, but no errors
147
    def put_file(self, relpath, f, mode=None):
148
        """See Transport.put_file()."""
1530.1.3 by Robert Collins
transport implementations now tested consistently.
149
        _abspath = self._abspath(relpath)
150
        self._check_parent(_abspath)
2018.5.131 by Andrew Bennetts
Be strict about unicode passed to transport.put_{bytes,file} and SmartClient.call_with_body_bytes, fixing part of TestLockableFiles_RemoteLockDir.test_read_write.
151
        bytes = f.read()
152
        if type(bytes) is not str:
153
            # Although not strictly correct, we raise UnicodeEncodeError to be
154
            # compatible with other transports.
155
            raise UnicodeEncodeError(
156
                'undefined', bytes, 0, 1,
157
                'put_file must be given a file of bytes, not unicode.')
158
        self._files[_abspath] = (bytes, mode)
2745.5.2 by Robert Collins
* ``bzrlib.transport.Transport.put_file`` now returns the number of bytes
159
        return len(bytes)
1442.1.44 by Robert Collins
Many transport related tweaks:
160
1185.58.4 by John Arbash Meinel
Added permission checking to Branch, and propogated that change into the stores.
161
    def mkdir(self, relpath, mode=None):
1442.1.44 by Robert Collins
Many transport related tweaks:
162
        """See Transport.mkdir()."""
1530.1.3 by Robert Collins
transport implementations now tested consistently.
163
        _abspath = self._abspath(relpath)
164
        self._check_parent(_abspath)
165
        if _abspath in self._dirs:
1442.1.44 by Robert Collins
Many transport related tweaks:
166
            raise FileExists(relpath)
1530.1.16 by Robert Collins
Move mkdir and copy_to permissions tests to test_transport_impleentation.
167
        self._dirs[_abspath]=mode
1442.1.44 by Robert Collins
Many transport related tweaks:
168
2671.3.9 by Robert Collins
Review feedback and fix VFat emulated transports to not claim to have unix permissions.
169
    def open_write_stream(self, relpath, mode=None):
170
        """See Transport.open_write_stream."""
2671.3.6 by Robert Collins
Review feedback.
171
        self.put_bytes(relpath, "", mode)
172
        result = AppendBasedFileStream(self, relpath)
173
        _file_streams[self.abspath(relpath)] = result
174
        return result
2671.3.2 by Robert Collins
Start open_file_stream logic.
175
1442.1.44 by Robert Collins
Many transport related tweaks:
176
    def listable(self):
177
        """See Transport.listable."""
178
        return True
179
180
    def iter_files_recursive(self):
1530.1.3 by Robert Collins
transport implementations now tested consistently.
181
        for file in self._files:
182
            if file.startswith(self._cwd):
1959.2.4 by John Arbash Meinel
MemoryTransport.iter_files_recursive() was not returning escaped paths
183
                yield urlutils.escape(file[len(self._cwd):])
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
184
1530.1.3 by Robert Collins
transport implementations now tested consistently.
185
    def list_dir(self, relpath):
186
        """See Transport.list_dir()."""
187
        _abspath = self._abspath(relpath)
188
        if _abspath != '/' and _abspath not in self._dirs:
189
            raise NoSuchFile(relpath)
190
        result = []
2120.3.1 by John Arbash Meinel
Fix MemoryTransport.list_dir() implementation, and update tests
191
192
        if not _abspath.endswith('/'):
193
            _abspath += '/'
194
195
        for path_group in self._files, self._dirs:
196
            for path in path_group:
197
                if path.startswith(_abspath):
198
                    trailing = path[len(_abspath):]
199
                    if trailing and '/' not in trailing:
200
                        result.append(trailing)
1910.7.1 by Andrew Bennetts
Make sure list_dir always returns url-escaped names.
201
        return map(urlutils.escape, result)
1553.5.16 by Martin Pool
MemoryTransport.rename() must raise exceptions on collision
202
203
    def rename(self, rel_from, rel_to):
204
        """Rename a file or directory; fail if the destination exists"""
205
        abs_from = self._abspath(rel_from)
206
        abs_to = self._abspath(rel_to)
207
        def replace(x):
208
            if x == abs_from:
209
                x = abs_to
210
            elif x.startswith(abs_from + '/'):
211
                x = abs_to + x[len(abs_from):]
212
            return x
213
        def do_renames(container):
214
            for path in container:
215
                new_path = replace(path)
216
                if new_path != path:
217
                    if new_path in container:
218
                        raise FileExists(new_path)
219
                    container[new_path] = container[path]
220
                    del container[path]
221
        do_renames(self._files)
222
        do_renames(self._dirs)
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
223
1534.4.15 by Robert Collins
Remove shutil dependency in upgrade - create a delete_tree method for transports.
224
    def rmdir(self, relpath):
225
        """See Transport.rmdir."""
226
        _abspath = self._abspath(relpath)
227
        if _abspath in self._files:
228
            self._translate_error(IOError(errno.ENOTDIR, relpath), relpath)
229
        for path in self._files:
2338.5.1 by Andrew Bennetts
Fix bug in MemoryTransport.rmdir.
230
            if path.startswith(_abspath + '/'):
1553.5.15 by Martin Pool
MemoryTransport should indicate ENOTEMPTY on rmdir of nonempty, same as unix
231
                self._translate_error(IOError(errno.ENOTEMPTY, relpath),
232
                                      relpath)
1534.4.15 by Robert Collins
Remove shutil dependency in upgrade - create a delete_tree method for transports.
233
        for path in self._dirs:
2338.5.1 by Andrew Bennetts
Fix bug in MemoryTransport.rmdir.
234
            if path.startswith(_abspath + '/') and path != _abspath:
1553.5.10 by Martin Pool
New DirectoryNotEmpty exception, and raise this from local and memory
235
                self._translate_error(IOError(errno.ENOTEMPTY, relpath), relpath)
1534.4.15 by Robert Collins
Remove shutil dependency in upgrade - create a delete_tree method for transports.
236
        if not _abspath in self._dirs:
237
            raise NoSuchFile(relpath)
238
        del self._dirs[_abspath]
239
1442.1.44 by Robert Collins
Many transport related tweaks:
240
    def stat(self, relpath):
241
        """See Transport.stat()."""
1530.1.3 by Robert Collins
transport implementations now tested consistently.
242
        _abspath = self._abspath(relpath)
243
        if _abspath in self._files:
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
244
            return MemoryStat(len(self._files[_abspath][0]), False,
1530.1.15 by Robert Collins
Move put mode tests into test_transport_implementation.
245
                              self._files[_abspath][1])
1530.1.16 by Robert Collins
Move mkdir and copy_to permissions tests to test_transport_impleentation.
246
        elif _abspath in self._dirs:
247
            return MemoryStat(0, True, self._dirs[_abspath])
1530.1.3 by Robert Collins
transport implementations now tested consistently.
248
        else:
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
249
            raise NoSuchFile(_abspath)
250
251
    def lock_read(self, relpath):
252
        """See Transport.lock_read()."""
253
        return _MemoryLock(self._abspath(relpath), self)
254
255
    def lock_write(self, relpath):
256
        """See Transport.lock_write()."""
257
        return _MemoryLock(self._abspath(relpath), self)
1530.1.3 by Robert Collins
transport implementations now tested consistently.
258
259
    def _abspath(self, relpath):
260
        """Generate an internal absolute path."""
1685.1.45 by John Arbash Meinel
Moved url functions into bzrlib.urlutils
261
        relpath = urlutils.unescape(relpath)
3211.6.1 by James Henstridge
* Remove relpath='' special case in MemoryTransport._abspath(), which
262
        if relpath[:1] == '/':
1910.15.1 by Andrew Bennetts
More tests for abspath and clone behaviour
263
            return relpath
2940.3.1 by mbp at sourcefrog
MemoryTransport._abspath: fix handling of '..' and other strangeness
264
        cwd_parts = self._cwd.split('/')
265
        rel_parts = relpath.split('/')
266
        r = []
267
        for i in cwd_parts + rel_parts:
268
            if i == '..':
269
                if not r:
270
                    raise ValueError("illegal relpath %r under %r"
3010.2.1 by Martin Pool
Followon from MemoryTransport._abspath fix: add test_rename_across_subdirs, and fix error construction
271
                        % (relpath, self._cwd))
2940.3.1 by mbp at sourcefrog
MemoryTransport._abspath: fix handling of '..' and other strangeness
272
                r = r[:-1]
273
            elif i == '.' or i == '':
274
                pass
275
            else:
276
                r.append(i)
277
        return '/' + '/'.join(r)
1530.1.3 by Robert Collins
transport implementations now tested consistently.
278
279
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
280
class _MemoryLock(object):
281
    """This makes a lock."""
282
283
    def __init__(self, path, transport):
284
        self.path = path
285
        self.transport = transport
286
        if self.path in self.transport._locks:
287
            raise LockError('File %r already locked' % (self.path,))
288
        self.transport._locks[self.path] = self
289
290
    def unlock(self):
291
        del self.transport._locks[self.path]
292
        self.transport = None
293
294
5017.3.45 by Vincent Ladeuil
Move MemoryServer back into bzrlib.transport.memory as it's needed as soon as a MemoryTransport is used. Add a NEWS entry.
295
class MemoryServer(transport.Server):
296
    """Server for the MemoryTransport for testing with."""
297
298
    def start_server(self):
299
        self._dirs = {'/':None}
300
        self._files = {}
301
        self._locks = {}
302
        self._scheme = "memory+%s:///" % id(self)
303
        def memory_factory(url):
304
            from bzrlib.transport import memory
305
            result = memory.MemoryTransport(url)
306
            result._dirs = self._dirs
307
            result._files = self._files
308
            result._locks = self._locks
309
            return result
310
        self._memory_factory = memory_factory
311
        transport.register_transport(self._scheme, self._memory_factory)
312
313
    def stop_server(self):
314
        # unregister this server
315
        transport.unregister_transport(self._scheme, self._memory_factory)
316
317
    def get_url(self):
318
        """See bzrlib.transport.Server.get_url."""
319
        return self._scheme
320
321
    def get_bogus_url(self):
322
        raise NotImplementedError
323
324
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.
325
def get_test_permutations():
326
    """Return the permutations to be used in testing."""
5017.3.45 by Vincent Ladeuil
Move MemoryServer back into bzrlib.transport.memory as it's needed as soon as a MemoryTransport is used. Add a NEWS entry.
327
    return [(MemoryTransport, MemoryServer),
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.
328
            ]