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
30
from bzrlib.errors import (
32
37
from bzrlib.trace import mutter
33
from bzrlib.transport import (Transport, register_transport, Server)
38
from bzrlib.transport import (
39
AppendBasedFileStream,
34
46
import bzrlib.urlutils as urlutils
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
path = self._combine_paths(self._cwd, offset)
85
if len(path) == 0 or path[-1] != '/':
87
url = self._scheme + path
84
88
result = MemoryTransport(url)
85
89
result._dirs = self._dirs
86
90
result._files = self._files
99
103
return temp_t.base[:-1]
101
def append(self, relpath, f, mode=None):
102
"""See Transport.append()."""
105
def append_file(self, relpath, f, mode=None):
106
"""See Transport.append_file()."""
103
107
_abspath = self._abspath(relpath)
104
108
self._check_parent(_abspath)
105
109
orig_content, orig_mode = self._files.get(_abspath, ("", None))
117
121
def has(self, relpath):
118
122
"""See Transport.has()."""
119
123
_abspath = self._abspath(relpath)
120
return _abspath in self._files or _abspath in self._dirs
124
return (_abspath in self._files) or (_abspath in self._dirs)
122
126
def delete(self, relpath):
123
127
"""See Transport.delete()."""
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
def put(self, relpath, f, mode=None):
137
"""See Transport.put()."""
149
def put_file(self, relpath, f, mode=None):
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."""
154
182
def iter_files_recursive(self):
155
183
for file in self._files:
156
184
if file.startswith(self._cwd):
157
yield file[len(self._cwd):]
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)
203
return map(urlutils.escape, result)
178
205
def rename(self, rel_from, rel_to):
179
206
"""Rename a file or directory; fail if the destination exists"""
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: