/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: Aaron Bentley
  • Date: 2006-04-07 22:46:52 UTC
  • mfrom: (1645 +trunk)
  • mto: This revision was merged to the branch mainline in revision 1727.
  • Revision ID: aaron.bentley@utoronto.ca-20060407224652-4925bc3735b926f8
Merged latest bzr.dev

Show diffs side-by-side

added added

removed removed

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