~ubuntu-branches/ubuntu/lucid/bzr/lucid-proposed

« back to all changes in this revision

Viewing changes to bzrlib/transport/memory.py

  • Committer: Bazaar Package Importer
  • Author(s): Jeff Bailey
  • Date: 2006-03-20 08:31:00 UTC
  • mfrom: (1.1.2 upstream)
  • mto: This revision was merged to the branch mainline in revision 4.
  • Revision ID: james.westby@ubuntu.com-20060320083100-ovdi2ssuw0epcx8s
Tags: 0.8~200603200831-0ubuntu1
* Snapshot uploaded to Dapper at Martin Pool's request.

* Disable testsuite for upload.  Fakeroot and the testsuite don't
  play along.

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