1
# Copyright (C) 2005, 2006 Canonical Ltd
1
# Copyright (C) 2005-2010 Canonical Ltd
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
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17
17
"""Implementation of Transport that uses memory for its storage.
28
27
from cStringIO import StringIO
31
from bzrlib.errors import TransportError, NoSuchFile, FileExists, LockError
34
from bzrlib.errors import (
32
41
from bzrlib.trace import mutter
33
from bzrlib.transport import (Transport, register_transport, Server)
34
import bzrlib.urlutils as urlutils
42
from bzrlib.transport import (
43
AppendBasedFileStream,
49
61
self.st_mode = S_IFDIR | perms
52
class MemoryTransport(Transport):
64
class MemoryTransport(transport.Transport):
53
65
"""This is an in memory file system for transient data storage."""
55
67
def __init__(self, url=""):
61
73
super(MemoryTransport, self).__init__(url)
62
self._cwd = url[url.find(':') + 3:]
74
split = url.find(':') + 3
75
self._scheme = url[:split]
76
self._cwd = url[split:]
63
77
# dictionaries from absolute path to file mode
64
78
self._dirs = {'/':None}
68
82
def clone(self, offset=None):
69
83
"""See Transport.clone()."""
70
if offset is None or offset == '':
72
segments = offset.split('/')
73
cwdsegments = self._cwd.split('/')[:-1]
75
segment = segments.pop(0)
79
if len(cwdsegments) > 1:
82
cwdsegments.append(segment)
83
url = self.base[:self.base.find(':') + 3] + '/'.join(cwdsegments) + '/'
84
result = MemoryTransport(url)
84
path = self._combine_paths(self._cwd, offset)
85
if len(path) == 0 or path[-1] != '/':
87
url = self._scheme + path
88
result = self.__class__(url)
85
89
result._dirs = self._dirs
86
90
result._files = self._files
87
91
result._locks = self._locks
126
130
raise NoSuchFile(relpath)
127
131
del self._files[_abspath]
133
def external_url(self):
134
"""See bzrlib.transport.Transport.external_url."""
135
# MemoryTransport's are only accessible in-process
137
raise InProcessTransport(self)
129
139
def get(self, relpath):
130
140
"""See Transport.get()."""
131
141
_abspath = self._abspath(relpath)
132
142
if not _abspath in self._files:
133
raise NoSuchFile(relpath)
143
if _abspath in self._dirs:
144
return LateReadError(relpath)
146
raise NoSuchFile(relpath)
134
147
return StringIO(self._files[_abspath][0])
136
149
def put_file(self, relpath, f, mode=None):
137
150
"""See Transport.put_file()."""
138
151
_abspath = self._abspath(relpath)
139
152
self._check_parent(_abspath)
140
self._files[_abspath] = (f.read(), mode)
154
if type(bytes) is not str:
155
# Although not strictly correct, we raise UnicodeEncodeError to be
156
# compatible with other transports.
157
raise UnicodeEncodeError(
158
'undefined', bytes, 0, 1,
159
'put_file must be given a file of bytes, not unicode.')
160
self._files[_abspath] = (bytes, mode)
142
163
def mkdir(self, relpath, mode=None):
143
164
"""See Transport.mkdir()."""
147
168
raise FileExists(relpath)
148
169
self._dirs[_abspath]=mode
171
def open_write_stream(self, relpath, mode=None):
172
"""See Transport.open_write_stream."""
173
self.put_bytes(relpath, "", mode)
174
result = AppendBasedFileStream(self, relpath)
175
_file_streams[self.abspath(relpath)] = result
150
178
def listable(self):
151
179
"""See Transport.listable."""
155
183
for file in self._files:
156
184
if file.startswith(self._cwd):
157
185
yield urlutils.escape(file[len(self._cwd):])
159
187
def list_dir(self, relpath):
160
188
"""See Transport.list_dir()."""
161
189
_abspath = self._abspath(relpath)
162
190
if _abspath != '/' and _abspath not in self._dirs:
163
191
raise NoSuchFile(relpath)
165
for path in self._files:
166
if (path.startswith(_abspath) and
167
path[len(_abspath) + 1:].find('/') == -1 and
168
len(path) > len(_abspath)):
169
result.append(path[len(_abspath) + 1:])
170
for path in self._dirs:
171
if (path.startswith(_abspath) and
172
path[len(_abspath) + 1:].find('/') == -1 and
173
len(path) > len(_abspath) and
174
path[len(_abspath)] == '/'):
175
result.append(path[len(_abspath) + 1:])
194
if not _abspath.endswith('/'):
197
for path_group in self._files, self._dirs:
198
for path in path_group:
199
if path.startswith(_abspath):
200
trailing = path[len(_abspath):]
201
if trailing and '/' not in trailing:
202
result.append(trailing)
176
203
return map(urlutils.escape, result)
178
205
def rename(self, rel_from, rel_to):
195
222
del container[path]
196
223
do_renames(self._files)
197
224
do_renames(self._dirs)
199
226
def rmdir(self, relpath):
200
227
"""See Transport.rmdir."""
201
228
_abspath = self._abspath(relpath)
202
229
if _abspath in self._files:
203
230
self._translate_error(IOError(errno.ENOTDIR, relpath), relpath)
204
231
for path in self._files:
205
if path.startswith(_abspath):
232
if path.startswith(_abspath + '/'):
206
233
self._translate_error(IOError(errno.ENOTEMPTY, relpath),
208
235
for path in self._dirs:
209
if path.startswith(_abspath) and path != _abspath:
236
if path.startswith(_abspath + '/') and path != _abspath:
210
237
self._translate_error(IOError(errno.ENOTEMPTY, relpath), relpath)
211
238
if not _abspath in self._dirs:
212
239
raise NoSuchFile(relpath)
216
243
"""See Transport.stat()."""
217
244
_abspath = self._abspath(relpath)
218
245
if _abspath in self._files:
219
return MemoryStat(len(self._files[_abspath][0]), False,
246
return MemoryStat(len(self._files[_abspath][0]), False,
220
247
self._files[_abspath][1])
221
248
elif _abspath in self._dirs:
222
249
return MemoryStat(0, True, self._dirs[_abspath])
234
261
def _abspath(self, relpath):
235
262
"""Generate an internal absolute path."""
236
263
relpath = urlutils.unescape(relpath)
237
if relpath.find('..') != -1:
238
raise AssertionError('relpath contains ..')
240
if (self._cwd == '/'):
242
return self._cwd[:-1]
243
if relpath.endswith('/'):
244
relpath = relpath[:-1]
245
if relpath.startswith('./'):
246
relpath = relpath[2:]
247
return self._cwd + relpath
264
if relpath[:1] == '/':
266
cwd_parts = self._cwd.split('/')
267
rel_parts = relpath.split('/')
269
for i in cwd_parts + rel_parts:
272
raise ValueError("illegal relpath %r under %r"
273
% (relpath, self._cwd))
275
elif i == '.' or i == '':
279
return '/' + '/'.join(r)
250
282
class _MemoryLock(object):
251
283
"""This makes a lock."""
253
285
def __init__(self, path, transport):
254
assert isinstance(transport, MemoryTransport)
256
287
self.transport = transport
257
288
if self.path in self.transport._locks:
269
300
self.transport = None
272
class MemoryServer(Server):
303
class MemoryServer(transport.Server):
273
304
"""Server for the MemoryTransport for testing with."""
276
"""See bzrlib.transport.Server.setUp."""
306
def start_server(self):
277
307
self._dirs = {'/':None}
280
310
self._scheme = "memory+%s:///" % id(self)
281
311
def memory_factory(url):
282
result = MemoryTransport(url)
312
from bzrlib.transport import memory
313
result = memory.MemoryTransport(url)
283
314
result._dirs = self._dirs
284
315
result._files = self._files
285
316
result._locks = self._locks
287
register_transport(self._scheme, memory_factory)
318
self._memory_factory = memory_factory
319
transport.register_transport(self._scheme, self._memory_factory)
290
"""See bzrlib.transport.Server.tearDown."""
321
def stop_server(self):
291
322
# unregister this server
323
transport.unregister_transport(self._scheme, self._memory_factory)
293
325
def get_url(self):
294
326
"""See bzrlib.transport.Server.get_url."""
295
327
return self._scheme
329
def get_bogus_url(self):
330
raise NotImplementedError
298
333
def get_test_permutations():
299
334
"""Return the permutations to be used in testing."""