~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/transport/memory.py

[merge] update from bzr.dev

Show diffs side-by-side

added added

removed removed

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