~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/transport/memory.py

merge from integration.

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
25
from bzrlib.errors import TransportError, NoSuchFile, FileExists
24
 
from bzrlib.transport import Transport
 
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 = {}
40
53
 
41
54
    def clone(self, offset=None):
42
55
        """See Transport.clone()."""
43
 
        return self
 
56
        if offset is None:
 
57
            return copy(self)
 
58
        segments = offset.split('/')
 
59
        cwdsegments = self._cwd.split('/')[:-1]
 
60
        while len(segments):
 
61
            segment = segments.pop(0)
 
62
            if segment == '.':
 
63
                continue
 
64
            if segment == '..':
 
65
                if len(cwdsegments) > 1:
 
66
                    cwdsegments.pop()
 
67
                continue
 
68
            cwdsegments.append(segment)
 
69
        url = self.base[:self.base.find(':') + 1] + '/'.join(cwdsegments) + '/'
 
70
        result = MemoryTransport(url)
 
71
        result._dirs = self._dirs
 
72
        result._files = self._files
 
73
        return result
44
74
 
45
75
    def abspath(self, relpath):
46
76
        """See Transport.abspath()."""
47
 
        return self.base + relpath
 
77
        return self.base[:-1] + self._abspath(relpath)
48
78
 
49
79
    def append(self, relpath, f):
50
80
        """See Transport.append()."""
51
 
        self._check_parent(relpath)
52
 
        self._files[relpath] = self._files.get(relpath, "") + f.read()
 
81
        _abspath = self._abspath(relpath)
 
82
        self._check_parent(_abspath)
 
83
        orig_content, orig_mode = self._files.get(_abspath, ("", None))
 
84
        self._files[_abspath] = (orig_content + f.read(), orig_mode)
53
85
 
54
 
    def _check_parent(self, relpath):
55
 
        dir = os.path.dirname(relpath)
56
 
        if dir != '':
 
86
    def _check_parent(self, _abspath):
 
87
        dir = os.path.dirname(_abspath)
 
88
        if dir != '/':
57
89
            if not dir in self._dirs:
58
 
                raise NoSuchFile(relpath)
 
90
                raise NoSuchFile(_abspath)
59
91
 
60
92
    def has(self, relpath):
61
93
        """See Transport.has()."""
62
 
        return relpath in self._files
 
94
        _abspath = self._abspath(relpath)
 
95
        return _abspath in self._files or _abspath in self._dirs
 
96
 
 
97
    def delete(self, relpath):
 
98
        """See Transport.delete()."""
 
99
        _abspath = self._abspath(relpath)
 
100
        if not _abspath in self._files:
 
101
            raise NoSuchFile(relpath)
 
102
        del self._files[_abspath]
63
103
 
64
104
    def get(self, relpath):
65
105
        """See Transport.get()."""
66
 
        if not relpath in self._files:
 
106
        _abspath = self._abspath(relpath)
 
107
        if not _abspath in self._files:
67
108
            raise NoSuchFile(relpath)
68
 
        return StringIO(self._files[relpath])
 
109
        return StringIO(self._files[_abspath][0])
69
110
 
70
111
    def put(self, relpath, f, mode=None):
71
112
        """See Transport.put()."""
72
 
        self._check_parent(relpath)
73
 
        self._files[relpath] = f.read()
 
113
        _abspath = self._abspath(relpath)
 
114
        self._check_parent(_abspath)
 
115
        self._files[_abspath] = (f.read(), mode)
74
116
 
75
117
    def mkdir(self, relpath, mode=None):
76
118
        """See Transport.mkdir()."""
77
 
        self._check_parent(relpath)
78
 
        if relpath in self._dirs:
 
119
        _abspath = self._abspath(relpath)
 
120
        self._check_parent(_abspath)
 
121
        if _abspath in self._dirs:
79
122
            raise FileExists(relpath)
80
 
        self._dirs.add(relpath)
 
123
        self._dirs[_abspath]=mode
81
124
 
82
125
    def listable(self):
83
126
        """See Transport.listable."""
84
127
        return True
85
128
 
86
129
    def iter_files_recursive(self):
87
 
        return iter(self._files)
88
 
    
89
 
#    def list_dir(self, relpath):
90
 
#    TODO if needed
91
 
    
 
130
        for file in self._files:
 
131
            if file.startswith(self._cwd):
 
132
                yield file[len(self._cwd):]
 
133
    
 
134
    def list_dir(self, relpath):
 
135
        """See Transport.list_dir()."""
 
136
        _abspath = self._abspath(relpath)
 
137
        if _abspath != '/' and _abspath not in self._dirs:
 
138
            raise NoSuchFile(relpath)
 
139
        result = []
 
140
        for path in self._files:
 
141
            if (path.startswith(_abspath) and 
 
142
                path[len(_abspath) + 1:].find('/') == -1 and
 
143
                len(path) > len(_abspath)):
 
144
                result.append(path[len(_abspath) + 1:])
 
145
        for path in self._dirs:
 
146
            if (path.startswith(_abspath) and 
 
147
                path[len(_abspath) + 1:].find('/') == -1 and
 
148
                len(path) > len(_abspath) and
 
149
                path[len(_abspath)] == '/'):
 
150
                result.append(path[len(_abspath) + 1:])
 
151
        return result
 
152
    
 
153
    def rmdir(self, relpath):
 
154
        """See Transport.rmdir."""
 
155
        _abspath = self._abspath(relpath)
 
156
        if _abspath in self._files:
 
157
            self._translate_error(IOError(errno.ENOTDIR, relpath), relpath)
 
158
        for path in self._files:
 
159
            if path.startswith(_abspath):
 
160
                self._translate_error(IOError(errno.EBUSY, relpath), relpath)
 
161
        for path in self._dirs:
 
162
            if path.startswith(_abspath) and path != _abspath:
 
163
                self._translate_error(IOError(errno.EBUSY, relpath), relpath)
 
164
        if not _abspath in self._dirs:
 
165
            raise NoSuchFile(relpath)
 
166
        del self._dirs[_abspath]
 
167
 
92
168
    def stat(self, relpath):
93
169
        """See Transport.stat()."""
94
 
        return MemoryStat(len(self._files[relpath]))
 
170
        _abspath = self._abspath(relpath)
 
171
        if _abspath in self._files:
 
172
            return MemoryStat(len(self._files[_abspath][0]), False, 
 
173
                              self._files[_abspath][1])
 
174
        elif _abspath == '':
 
175
            return MemoryStat(0, True, None)
 
176
        elif _abspath in self._dirs:
 
177
            return MemoryStat(0, True, self._dirs[_abspath])
 
178
        else:
 
179
            raise NoSuchFile(relpath)
95
180
 
96
181
#    def lock_read(self, relpath):
97
182
#   TODO if needed
98
183
#
99
184
#    def lock_write(self, relpath):
100
185
#   TODO if needed
 
186
 
 
187
    def _abspath(self, relpath):
 
188
        """Generate an internal absolute path."""
 
189
        if relpath.find('..') != -1:
 
190
            raise AssertionError('relpath contains ..')
 
191
        if relpath == '.':
 
192
            return self._cwd[:-1]
 
193
        if relpath.endswith('/'):
 
194
            relpath = relpath[:-1]
 
195
        if relpath.startswith('./'):
 
196
            relpath = relpath[2:]
 
197
        return self._cwd + relpath
 
198
 
 
199
 
 
200
class MemoryServer(Server):
 
201
    """Server for the MemoryTransport for testing with."""
 
202
 
 
203
    def setUp(self):
 
204
        """See bzrlib.transport.Server.setUp."""
 
205
        self._scheme = "memory+%s:" % id(self)
 
206
        register_transport(self._scheme, MemoryTransport)
 
207
 
 
208
    def tearDown(self):
 
209
        """See bzrlib.transport.Server.tearDown."""
 
210
        # unregister this server
 
211
 
 
212
    def get_url(self):
 
213
        """See bzrlib.transport.Server.get_url."""
 
214
        return self._scheme
 
215
 
 
216
 
 
217
def get_test_permutations():
 
218
    """Return the permutations to be used in testing."""
 
219
    return [(MemoryTransport, MemoryServer),
 
220
            ]