/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/transport/memory.py

  • Committer: Martin Pool
  • Date: 2006-03-09 02:04:59 UTC
  • mto: This revision was merged to the branch mainline in revision 1602.
  • Revision ID: mbp@sourcefrog.net-20060309020459-9909376bda89eb6f
Additional test that Branch5 uses lockdir properly

Show diffs side-by-side

added added

removed removed

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