1
# Copyright (C) 2005 Canonical Ltd
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.
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.
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
"""Implementation of Transport that uses memory for its storage."""
22
from cStringIO import StringIO
24
from bzrlib.trace import mutter
25
from bzrlib.errors import TransportError, NoSuchFile, FileExists, LockError
26
from bzrlib.transport import Transport, register_transport, Server
28
class MemoryStat(object):
30
def __init__(self, size, is_dir, perms):
35
self.st_mode = S_IFREG | perms
37
self.st_mode = S_IFDIR | perms
40
class MemoryTransport(Transport):
41
"""This is an in memory file system for transient data storage."""
43
def __init__(self, url=""):
44
"""Set the 'base' path where files will be stored."""
49
super(MemoryTransport, self).__init__(url)
50
self._cwd = url[url.find(':') + 1:]
55
def clone(self, offset=None):
56
"""See Transport.clone()."""
59
segments = offset.split('/')
60
cwdsegments = self._cwd.split('/')[:-1]
62
segment = segments.pop(0)
66
if len(cwdsegments) > 1:
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
77
def abspath(self, relpath):
78
"""See Transport.abspath()."""
79
return self.base[:-1] + self._abspath(relpath)[len(self._cwd) - 1:]
81
def append(self, relpath, f):
82
"""See Transport.append()."""
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)
88
def _check_parent(self, _abspath):
89
dir = os.path.dirname(_abspath)
91
if not dir in self._dirs:
92
raise NoSuchFile(_abspath)
94
def has(self, relpath):
95
"""See Transport.has()."""
96
_abspath = self._abspath(relpath)
97
return _abspath in self._files or _abspath in self._dirs
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]
106
def get(self, relpath):
107
"""See Transport.get()."""
108
_abspath = self._abspath(relpath)
109
if not _abspath in self._files:
110
raise NoSuchFile(relpath)
111
return StringIO(self._files[_abspath][0])
113
def put(self, relpath, f, mode=None):
114
"""See Transport.put()."""
115
_abspath = self._abspath(relpath)
116
self._check_parent(_abspath)
117
self._files[_abspath] = (f.read(), mode)
119
def mkdir(self, relpath, mode=None):
120
"""See Transport.mkdir()."""
121
_abspath = self._abspath(relpath)
122
self._check_parent(_abspath)
123
if _abspath in self._dirs:
124
raise FileExists(relpath)
125
self._dirs[_abspath]=mode
128
"""See Transport.listable."""
131
def iter_files_recursive(self):
132
for file in self._files:
133
if file.startswith(self._cwd):
134
yield file[len(self._cwd):]
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)
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:])
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]
170
def stat(self, relpath):
171
"""See Transport.stat()."""
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])
177
return MemoryStat(0, True, None)
178
elif _abspath in self._dirs:
179
return MemoryStat(0, True, self._dirs[_abspath])
181
raise NoSuchFile(_abspath)
183
def lock_read(self, relpath):
184
"""See Transport.lock_read()."""
185
return _MemoryLock(self._abspath(relpath), self)
187
def lock_write(self, relpath):
188
"""See Transport.lock_write()."""
189
return _MemoryLock(self._abspath(relpath), self)
191
def _abspath(self, relpath):
192
"""Generate an internal absolute path."""
193
if relpath.find('..') != -1:
194
raise AssertionError('relpath contains ..')
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
204
class _MemoryLock(object):
205
"""This makes a lock."""
207
def __init__(self, path, transport):
208
assert isinstance(transport, MemoryTransport)
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
216
# Should this warn, or actually try to cleanup?
218
warn("MemoryLock %r not explicitly unlocked" % (self.path,))
222
del self.transport._locks[self.path]
223
self.transport = None
226
class MemoryServer(Server):
227
"""Server for the MemoryTransport for testing with."""
230
"""See bzrlib.transport.Server.setUp."""
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
241
register_transport(self._scheme, memory_factory)
244
"""See bzrlib.transport.Server.tearDown."""
245
# unregister this server
248
"""See bzrlib.transport.Server.get_url."""
252
def get_test_permutations():
253
"""Return the permutations to be used in testing."""
254
return [(MemoryTransport, MemoryServer),