~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/transport/memory.py

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2006-04-19 08:36:50 UTC
  • mfrom: (1664.2.14 bzr.knitplanmerge)
  • Revision ID: pqm@pqm.ubuntu.com-20060419083650-f26d296f90d75d88
Implement plan_merge for knits

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
 
 
35
class MemoryStat(object):
 
36
 
 
37
    def __init__(self, size, is_dir, perms):
 
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
 
47
 
 
48
 
 
49
class MemoryTransport(Transport):
 
50
    """This is an in memory file system for transient data storage."""
 
51
 
 
52
    def __init__(self, url=""):
 
53
        """Set the 'base' path where files will be stored."""
 
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 = {}
 
62
        self._files = {}
 
63
        self._locks = {}
 
64
 
 
65
    def clone(self, offset=None):
 
66
        """See Transport.clone()."""
 
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
 
86
 
 
87
    def abspath(self, relpath):
 
88
        """See Transport.abspath()."""
 
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]
 
97
 
 
98
    def append(self, relpath, f):
 
99
        """See Transport.append()."""
 
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)
 
105
 
 
106
    def _check_parent(self, _abspath):
 
107
        dir = os.path.dirname(_abspath)
 
108
        if dir != '/':
 
109
            if not dir in self._dirs:
 
110
                raise NoSuchFile(_abspath)
 
111
 
 
112
    def has(self, relpath):
 
113
        """See Transport.has()."""
 
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]
 
123
 
 
124
    def get(self, relpath):
 
125
        """See Transport.get()."""
 
126
        _abspath = self._abspath(relpath)
 
127
        if not _abspath in self._files:
 
128
            raise NoSuchFile(relpath)
 
129
        return StringIO(self._files[_abspath][0])
 
130
 
 
131
    def put(self, relpath, f, mode=None):
 
132
        """See Transport.put()."""
 
133
        _abspath = self._abspath(relpath)
 
134
        self._check_parent(_abspath)
 
135
        self._files[_abspath] = (f.read(), mode)
 
136
 
 
137
    def mkdir(self, relpath, mode=None):
 
138
        """See Transport.mkdir()."""
 
139
        _abspath = self._abspath(relpath)
 
140
        self._check_parent(_abspath)
 
141
        if _abspath in self._dirs:
 
142
            raise FileExists(relpath)
 
143
        self._dirs[_abspath]=mode
 
144
 
 
145
    def listable(self):
 
146
        """See Transport.listable."""
 
147
        return True
 
148
 
 
149
    def iter_files_recursive(self):
 
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
 
 
210
    def stat(self, relpath):
 
211
        """See Transport.stat()."""
 
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
            ]