1
# Copyright (C) 2005, 2006 Canonical Ltd
1
# Copyright (C) 2005, 2006, 2007, 2008, 2009, 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
33
from bzrlib.errors import (
32
40
from bzrlib.trace import mutter
33
from bzrlib.transport import (Transport, register_transport, Server)
34
import bzrlib.urlutils as urlutils
41
from bzrlib.transport import (
42
AppendBasedFileStream,
61
76
super(MemoryTransport, self).__init__(url)
62
self._cwd = url[url.find(':') + 3:]
77
split = url.find(':') + 3
78
self._scheme = url[:split]
79
self._cwd = url[split:]
63
80
# dictionaries from absolute path to file mode
64
81
self._dirs = {'/':None}
68
85
def clone(self, offset=None):
69
86
"""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)
87
path = self._combine_paths(self._cwd, offset)
88
if len(path) == 0 or path[-1] != '/':
90
url = self._scheme + path
91
result = self.__class__(url)
85
92
result._dirs = self._dirs
86
93
result._files = self._files
87
94
result._locks = self._locks
99
106
return temp_t.base[:-1]
101
def append(self, relpath, f, mode=None):
102
"""See Transport.append()."""
108
def append_file(self, relpath, f, mode=None):
109
"""See Transport.append_file()."""
103
110
_abspath = self._abspath(relpath)
104
111
self._check_parent(_abspath)
105
112
orig_content, orig_mode = self._files.get(_abspath, ("", None))
117
124
def has(self, relpath):
118
125
"""See Transport.has()."""
119
126
_abspath = self._abspath(relpath)
120
return _abspath in self._files or _abspath in self._dirs
127
return (_abspath in self._files) or (_abspath in self._dirs)
122
129
def delete(self, relpath):
123
130
"""See Transport.delete()."""
126
133
raise NoSuchFile(relpath)
127
134
del self._files[_abspath]
136
def external_url(self):
137
"""See bzrlib.transport.Transport.external_url."""
138
# MemoryTransport's are only accessible in-process
140
raise InProcessTransport(self)
129
142
def get(self, relpath):
130
143
"""See Transport.get()."""
131
144
_abspath = self._abspath(relpath)
132
145
if not _abspath in self._files:
133
raise NoSuchFile(relpath)
146
if _abspath in self._dirs:
147
return LateReadError(relpath)
149
raise NoSuchFile(relpath)
134
150
return StringIO(self._files[_abspath][0])
136
def put(self, relpath, f, mode=None):
137
"""See Transport.put()."""
152
def put_file(self, relpath, f, mode=None):
153
"""See Transport.put_file()."""
138
154
_abspath = self._abspath(relpath)
139
155
self._check_parent(_abspath)
140
self._files[_abspath] = (f.read(), mode)
157
if type(bytes) is not str:
158
# Although not strictly correct, we raise UnicodeEncodeError to be
159
# compatible with other transports.
160
raise UnicodeEncodeError(
161
'undefined', bytes, 0, 1,
162
'put_file must be given a file of bytes, not unicode.')
163
self._files[_abspath] = (bytes, mode)
142
166
def mkdir(self, relpath, mode=None):
143
167
"""See Transport.mkdir()."""
147
171
raise FileExists(relpath)
148
172
self._dirs[_abspath]=mode
174
def open_write_stream(self, relpath, mode=None):
175
"""See Transport.open_write_stream."""
176
self.put_bytes(relpath, "", mode)
177
result = AppendBasedFileStream(self, relpath)
178
_file_streams[self.abspath(relpath)] = result
150
181
def listable(self):
151
182
"""See Transport.listable."""
154
185
def iter_files_recursive(self):
155
186
for file in self._files:
156
187
if file.startswith(self._cwd):
157
yield file[len(self._cwd):]
188
yield urlutils.escape(file[len(self._cwd):])
159
190
def list_dir(self, relpath):
160
191
"""See Transport.list_dir()."""
161
192
_abspath = self._abspath(relpath)
162
193
if _abspath != '/' and _abspath not in self._dirs:
163
194
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:])
197
if not _abspath.endswith('/'):
200
for path_group in self._files, self._dirs:
201
for path in path_group:
202
if path.startswith(_abspath):
203
trailing = path[len(_abspath):]
204
if trailing and '/' not in trailing:
205
result.append(trailing)
206
return map(urlutils.escape, result)
178
208
def rename(self, rel_from, rel_to):
179
209
"""Rename a file or directory; fail if the destination exists"""
195
225
del container[path]
196
226
do_renames(self._files)
197
227
do_renames(self._dirs)
199
229
def rmdir(self, relpath):
200
230
"""See Transport.rmdir."""
201
231
_abspath = self._abspath(relpath)
202
232
if _abspath in self._files:
203
233
self._translate_error(IOError(errno.ENOTDIR, relpath), relpath)
204
234
for path in self._files:
205
if path.startswith(_abspath):
235
if path.startswith(_abspath + '/'):
206
236
self._translate_error(IOError(errno.ENOTEMPTY, relpath),
208
238
for path in self._dirs:
209
if path.startswith(_abspath) and path != _abspath:
239
if path.startswith(_abspath + '/') and path != _abspath:
210
240
self._translate_error(IOError(errno.ENOTEMPTY, relpath), relpath)
211
241
if not _abspath in self._dirs:
212
242
raise NoSuchFile(relpath)
216
246
"""See Transport.stat()."""
217
247
_abspath = self._abspath(relpath)
218
248
if _abspath in self._files:
219
return MemoryStat(len(self._files[_abspath][0]), False,
249
return MemoryStat(len(self._files[_abspath][0]), False,
220
250
self._files[_abspath][1])
221
251
elif _abspath in self._dirs:
222
252
return MemoryStat(0, True, self._dirs[_abspath])
234
264
def _abspath(self, relpath):
235
265
"""Generate an internal absolute path."""
236
266
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
267
if relpath[:1] == '/':
269
cwd_parts = self._cwd.split('/')
270
rel_parts = relpath.split('/')
272
for i in cwd_parts + rel_parts:
275
raise ValueError("illegal relpath %r under %r"
276
% (relpath, self._cwd))
278
elif i == '.' or i == '':
282
return '/' + '/'.join(r)
250
285
class _MemoryLock(object):
251
286
"""This makes a lock."""
253
288
def __init__(self, path, transport):
254
assert isinstance(transport, MemoryTransport)
256
290
self.transport = transport
257
291
if self.path in self.transport._locks:
284
317
result._files = self._files
285
318
result._locks = self._locks
287
register_transport(self._scheme, memory_factory)
320
self._memory_factory = memory_factory
321
register_transport(self._scheme, self._memory_factory)
290
"""See bzrlib.transport.Server.tearDown."""
323
def stop_server(self):
291
324
# unregister this server
325
unregister_transport(self._scheme, self._memory_factory)
293
327
def get_url(self):
294
328
"""See bzrlib.transport.Server.get_url."""