/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
1553.5.16 by Martin Pool
MemoryTransport.rename() must raise exceptions on collision
1
# Copyright (C) 2005, 2006 Canonical Ltd
1442.1.44 by Robert Collins
Many transport related tweaks:
2
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
7
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
# GNU General Public License for more details.
12
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
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
1530.1.3 by Robert Collins
transport implementations now tested consistently.
23
from copy import copy
1442.1.44 by Robert Collins
Many transport related tweaks:
24
import os
25
import errno
1553.5.16 by Martin Pool
MemoryTransport.rename() must raise exceptions on collision
26
import re
1530.1.3 by Robert Collins
transport implementations now tested consistently.
27
from stat import *
1442.1.44 by Robert Collins
Many transport related tweaks:
28
from cStringIO import StringIO
29
30
from bzrlib.trace import mutter
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
31
from bzrlib.errors import TransportError, NoSuchFile, FileExists, LockError
1530.1.3 by Robert Collins
transport implementations now tested consistently.
32
from bzrlib.transport import Transport, register_transport, Server
1442.1.44 by Robert Collins
Many transport related tweaks:
33
34
class MemoryStat(object):
35
1530.1.15 by Robert Collins
Move put mode tests into test_transport_implementation.
36
    def __init__(self, size, is_dir, perms):
1442.1.44 by Robert Collins
Many transport related tweaks:
37
        self.st_size = size
1530.1.15 by Robert Collins
Move put mode tests into test_transport_implementation.
38
        if perms is None:
39
            perms = 0644
1530.1.3 by Robert Collins
transport implementations now tested consistently.
40
        if not is_dir:
1530.1.15 by Robert Collins
Move put mode tests into test_transport_implementation.
41
            self.st_mode = S_IFREG | perms
1530.1.3 by Robert Collins
transport implementations now tested consistently.
42
        else:
1530.1.15 by Robert Collins
Move put mode tests into test_transport_implementation.
43
            self.st_mode = S_IFDIR | perms
1442.1.44 by Robert Collins
Many transport related tweaks:
44
45
46
class MemoryTransport(Transport):
1534.4.9 by Robert Collins
Add a readonly decorator for transports.
47
    """This is an in memory file system for transient data storage."""
1442.1.44 by Robert Collins
Many transport related tweaks:
48
1530.1.3 by Robert Collins
transport implementations now tested consistently.
49
    def __init__(self, url=""):
1442.1.44 by Robert Collins
Many transport related tweaks:
50
        """Set the 'base' path where files will be stored."""
1530.1.3 by Robert Collins
transport implementations now tested consistently.
51
        if url == "":
52
            url = "memory:/"
53
        if url[-1] != '/':
54
            url = url + '/'
55
        super(MemoryTransport, self).__init__(url)
56
        self._cwd = url[url.find(':') + 1:]
1553.5.16 by Martin Pool
MemoryTransport.rename() must raise exceptions on collision
57
        # dictionaries from absolute path to file mode
1530.1.16 by Robert Collins
Move mkdir and copy_to permissions tests to test_transport_impleentation.
58
        self._dirs = {}
1442.1.44 by Robert Collins
Many transport related tweaks:
59
        self._files = {}
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
60
        self._locks = {}
1442.1.44 by Robert Collins
Many transport related tweaks:
61
62
    def clone(self, offset=None):
63
        """See Transport.clone()."""
1530.1.3 by Robert Collins
transport implementations now tested consistently.
64
        if offset is None:
65
            return copy(self)
66
        segments = offset.split('/')
67
        cwdsegments = self._cwd.split('/')[:-1]
68
        while len(segments):
69
            segment = segments.pop(0)
70
            if segment == '.':
71
                continue
72
            if segment == '..':
73
                if len(cwdsegments) > 1:
74
                    cwdsegments.pop()
75
                continue
76
            cwdsegments.append(segment)
77
        url = self.base[:self.base.find(':') + 1] + '/'.join(cwdsegments) + '/'
78
        result = MemoryTransport(url)
79
        result._dirs = self._dirs
80
        result._files = self._files
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
81
        result._locks = self._locks
1530.1.3 by Robert Collins
transport implementations now tested consistently.
82
        return result
1442.1.44 by Robert Collins
Many transport related tweaks:
83
84
    def abspath(self, relpath):
85
        """See Transport.abspath()."""
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
86
        return self.base[:-1] + self._abspath(relpath)[len(self._cwd) - 1:]
1442.1.44 by Robert Collins
Many transport related tweaks:
87
88
    def append(self, relpath, f):
89
        """See Transport.append()."""
1530.1.3 by Robert Collins
transport implementations now tested consistently.
90
        _abspath = self._abspath(relpath)
91
        self._check_parent(_abspath)
1530.1.15 by Robert Collins
Move put mode tests into test_transport_implementation.
92
        orig_content, orig_mode = self._files.get(_abspath, ("", None))
93
        self._files[_abspath] = (orig_content + f.read(), orig_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.
94
        return len(orig_content)
1442.1.44 by Robert Collins
Many transport related tweaks:
95
1530.1.3 by Robert Collins
transport implementations now tested consistently.
96
    def _check_parent(self, _abspath):
97
        dir = os.path.dirname(_abspath)
98
        if dir != '/':
1442.1.44 by Robert Collins
Many transport related tweaks:
99
            if not dir in self._dirs:
1530.1.3 by Robert Collins
transport implementations now tested consistently.
100
                raise NoSuchFile(_abspath)
1442.1.44 by Robert Collins
Many transport related tweaks:
101
102
    def has(self, relpath):
103
        """See Transport.has()."""
1530.1.3 by Robert Collins
transport implementations now tested consistently.
104
        _abspath = self._abspath(relpath)
105
        return _abspath in self._files or _abspath in self._dirs
106
107
    def delete(self, relpath):
108
        """See Transport.delete()."""
109
        _abspath = self._abspath(relpath)
110
        if not _abspath in self._files:
111
            raise NoSuchFile(relpath)
112
        del self._files[_abspath]
1442.1.44 by Robert Collins
Many transport related tweaks:
113
114
    def get(self, relpath):
115
        """See Transport.get()."""
1530.1.3 by Robert Collins
transport implementations now tested consistently.
116
        _abspath = self._abspath(relpath)
117
        if not _abspath in self._files:
1442.1.44 by Robert Collins
Many transport related tweaks:
118
            raise NoSuchFile(relpath)
1530.1.15 by Robert Collins
Move put mode tests into test_transport_implementation.
119
        return StringIO(self._files[_abspath][0])
1442.1.44 by Robert Collins
Many transport related tweaks:
120
1185.58.4 by John Arbash Meinel
Added permission checking to Branch, and propogated that change into the stores.
121
    def put(self, relpath, f, mode=None):
1442.1.44 by Robert Collins
Many transport related tweaks:
122
        """See Transport.put()."""
1530.1.3 by Robert Collins
transport implementations now tested consistently.
123
        _abspath = self._abspath(relpath)
124
        self._check_parent(_abspath)
1530.1.15 by Robert Collins
Move put mode tests into test_transport_implementation.
125
        self._files[_abspath] = (f.read(), mode)
1442.1.44 by Robert Collins
Many transport related tweaks:
126
1185.58.4 by John Arbash Meinel
Added permission checking to Branch, and propogated that change into the stores.
127
    def mkdir(self, relpath, mode=None):
1442.1.44 by Robert Collins
Many transport related tweaks:
128
        """See Transport.mkdir()."""
1530.1.3 by Robert Collins
transport implementations now tested consistently.
129
        _abspath = self._abspath(relpath)
130
        self._check_parent(_abspath)
131
        if _abspath in self._dirs:
1442.1.44 by Robert Collins
Many transport related tweaks:
132
            raise FileExists(relpath)
1530.1.16 by Robert Collins
Move mkdir and copy_to permissions tests to test_transport_impleentation.
133
        self._dirs[_abspath]=mode
1442.1.44 by Robert Collins
Many transport related tweaks:
134
135
    def listable(self):
136
        """See Transport.listable."""
137
        return True
138
139
    def iter_files_recursive(self):
1530.1.3 by Robert Collins
transport implementations now tested consistently.
140
        for file in self._files:
141
            if file.startswith(self._cwd):
142
                yield file[len(self._cwd):]
1442.1.44 by Robert Collins
Many transport related tweaks:
143
    
1530.1.3 by Robert Collins
transport implementations now tested consistently.
144
    def list_dir(self, relpath):
145
        """See Transport.list_dir()."""
146
        _abspath = self._abspath(relpath)
147
        if _abspath != '/' and _abspath not in self._dirs:
148
            raise NoSuchFile(relpath)
149
        result = []
150
        for path in self._files:
151
            if (path.startswith(_abspath) and 
152
                path[len(_abspath) + 1:].find('/') == -1 and
153
                len(path) > len(_abspath)):
154
                result.append(path[len(_abspath) + 1:])
155
        for path in self._dirs:
156
            if (path.startswith(_abspath) and 
157
                path[len(_abspath) + 1:].find('/') == -1 and
1534.4.15 by Robert Collins
Remove shutil dependency in upgrade - create a delete_tree method for transports.
158
                len(path) > len(_abspath) and
159
                path[len(_abspath)] == '/'):
1530.1.3 by Robert Collins
transport implementations now tested consistently.
160
                result.append(path[len(_abspath) + 1:])
161
        return result
1553.5.16 by Martin Pool
MemoryTransport.rename() must raise exceptions on collision
162
163
    def rename(self, rel_from, rel_to):
164
        """Rename a file or directory; fail if the destination exists"""
165
        abs_from = self._abspath(rel_from)
166
        abs_to = self._abspath(rel_to)
167
        def replace(x):
168
            if x == abs_from:
169
                x = abs_to
170
            elif x.startswith(abs_from + '/'):
171
                x = abs_to + x[len(abs_from):]
172
            return x
173
        def do_renames(container):
174
            for path in container:
175
                new_path = replace(path)
176
                if new_path != path:
177
                    if new_path in container:
178
                        raise FileExists(new_path)
179
                    container[new_path] = container[path]
180
                    del container[path]
181
        do_renames(self._files)
182
        do_renames(self._dirs)
1442.1.44 by Robert Collins
Many transport related tweaks:
183
    
1534.4.15 by Robert Collins
Remove shutil dependency in upgrade - create a delete_tree method for transports.
184
    def rmdir(self, relpath):
185
        """See Transport.rmdir."""
186
        _abspath = self._abspath(relpath)
187
        if _abspath in self._files:
188
            self._translate_error(IOError(errno.ENOTDIR, relpath), relpath)
189
        for path in self._files:
190
            if path.startswith(_abspath):
1553.5.15 by Martin Pool
MemoryTransport should indicate ENOTEMPTY on rmdir of nonempty, same as unix
191
                self._translate_error(IOError(errno.ENOTEMPTY, relpath),
192
                                      relpath)
1534.4.15 by Robert Collins
Remove shutil dependency in upgrade - create a delete_tree method for transports.
193
        for path in self._dirs:
194
            if path.startswith(_abspath) and path != _abspath:
1553.5.10 by Martin Pool
New DirectoryNotEmpty exception, and raise this from local and memory
195
                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.
196
        if not _abspath in self._dirs:
197
            raise NoSuchFile(relpath)
198
        del self._dirs[_abspath]
199
1442.1.44 by Robert Collins
Many transport related tweaks:
200
    def stat(self, relpath):
201
        """See Transport.stat()."""
1530.1.3 by Robert Collins
transport implementations now tested consistently.
202
        _abspath = self._abspath(relpath)
203
        if _abspath in self._files:
1530.1.15 by Robert Collins
Move put mode tests into test_transport_implementation.
204
            return MemoryStat(len(self._files[_abspath][0]), False, 
205
                              self._files[_abspath][1])
1530.1.16 by Robert Collins
Move mkdir and copy_to permissions tests to test_transport_impleentation.
206
        elif _abspath == '':
1530.1.15 by Robert Collins
Move put mode tests into test_transport_implementation.
207
            return MemoryStat(0, True, None)
1530.1.16 by Robert Collins
Move mkdir and copy_to permissions tests to test_transport_impleentation.
208
        elif _abspath in self._dirs:
209
            return MemoryStat(0, True, self._dirs[_abspath])
1530.1.3 by Robert Collins
transport implementations now tested consistently.
210
        else:
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
211
            raise NoSuchFile(_abspath)
212
213
    def lock_read(self, relpath):
214
        """See Transport.lock_read()."""
215
        return _MemoryLock(self._abspath(relpath), self)
216
217
    def lock_write(self, relpath):
218
        """See Transport.lock_write()."""
219
        return _MemoryLock(self._abspath(relpath), self)
1530.1.3 by Robert Collins
transport implementations now tested consistently.
220
221
    def _abspath(self, relpath):
222
        """Generate an internal absolute path."""
223
        if relpath.find('..') != -1:
224
            raise AssertionError('relpath contains ..')
225
        if relpath == '.':
226
            return self._cwd[:-1]
227
        if relpath.endswith('/'):
228
            relpath = relpath[:-1]
1534.4.15 by Robert Collins
Remove shutil dependency in upgrade - create a delete_tree method for transports.
229
        if relpath.startswith('./'):
230
            relpath = relpath[2:]
1530.1.3 by Robert Collins
transport implementations now tested consistently.
231
        return self._cwd + relpath
232
233
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
234
class _MemoryLock(object):
235
    """This makes a lock."""
236
237
    def __init__(self, path, transport):
238
        assert isinstance(transport, MemoryTransport)
239
        self.path = path
240
        self.transport = transport
241
        if self.path in self.transport._locks:
242
            raise LockError('File %r already locked' % (self.path,))
243
        self.transport._locks[self.path] = self
244
245
    def __del__(self):
246
        # Should this warn, or actually try to cleanup?
247
        if self.transport:
248
            warn("MemoryLock %r not explicitly unlocked" % (self.path,))
249
            self.unlock()
250
251
    def unlock(self):
252
        del self.transport._locks[self.path]
253
        self.transport = None
254
255
1530.1.3 by Robert Collins
transport implementations now tested consistently.
256
class MemoryServer(Server):
257
    """Server for the MemoryTransport for testing with."""
258
259
    def setUp(self):
260
        """See bzrlib.transport.Server.setUp."""
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
261
        self._dirs = {}
262
        self._files = {}
263
        self._locks = {}
1530.1.3 by Robert Collins
transport implementations now tested consistently.
264
        self._scheme = "memory+%s:" % id(self)
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
265
        def memory_factory(url):
266
            result = MemoryTransport(url)
267
            result._dirs = self._dirs
268
            result._files = self._files
269
            result._locks = self._locks
270
            return result
271
        register_transport(self._scheme, memory_factory)
1530.1.3 by Robert Collins
transport implementations now tested consistently.
272
273
    def tearDown(self):
274
        """See bzrlib.transport.Server.tearDown."""
275
        # unregister this server
276
277
    def get_url(self):
278
        """See bzrlib.transport.Server.get_url."""
279
        return self._scheme
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.
280
281
282
def get_test_permutations():
283
    """Return the permutations to be used in testing."""
284
    return [(MemoryTransport, MemoryServer),
285
            ]