20
20
so this is primarily useful for testing.
23
from __future__ import absolute_import
26
from stat import S_IFREG, S_IFDIR
27
from cStringIO import StringIO
32
from stat import S_IFREG, S_IFDIR, S_IFLNK, S_ISDIR
34
from bzrlib.errors import (
38
from ..errors import (
37
41
InProcessTransport,
41
from bzrlib.trace import mutter
42
from bzrlib.transport import (
45
from ..transport import (
43
46
AppendBasedFileStream,
50
52
class MemoryStat(object):
52
def __init__(self, size, is_dir, perms):
54
def __init__(self, size, kind, perms=None):
53
55
self.st_size = size
57
self.st_mode = S_IFREG | perms
59
self.st_mode = kind | perms
61
self.st_mode = S_IFDIR | perms
63
self.st_mode = kind | perms
64
66
class MemoryTransport(transport.Transport):
75
77
self._scheme = url[:split]
76
78
self._cwd = url[split:]
77
79
# dictionaries from absolute path to file mode
78
self._dirs = {'/':None}
80
self._dirs = {'/': None}
82
85
def clone(self, offset=None):
83
86
"""See Transport.clone()."""
84
path = self._combine_paths(self._cwd, offset)
87
path = urlutils.URL._combine_paths(self._cwd, offset)
85
88
if len(path) == 0 or path[-1] != '/':
87
90
url = self._scheme + path
88
91
result = self.__class__(url)
89
92
result._dirs = self._dirs
93
result._symlinks = self._symlinks
90
94
result._files = self._files
91
95
result._locks = self._locks
105
109
def append_file(self, relpath, f, mode=None):
106
110
"""See Transport.append_file()."""
107
_abspath = self._abspath(relpath)
111
_abspath = self._resolve_symlinks(relpath)
108
112
self._check_parent(_abspath)
109
orig_content, orig_mode = self._files.get(_abspath, ("", None))
113
orig_content, orig_mode = self._files.get(_abspath, (b"", None))
112
116
self._files[_abspath] = (orig_content + f.read(), mode)
115
119
def _check_parent(self, _abspath):
116
120
dir = os.path.dirname(_abspath)
118
if not dir in self._dirs:
122
if dir not in self._dirs:
119
123
raise NoSuchFile(_abspath)
121
125
def has(self, relpath):
122
126
"""See Transport.has()."""
123
127
_abspath = self._abspath(relpath)
124
return (_abspath in self._files) or (_abspath in self._dirs)
128
for container in (self._files, self._dirs, self._symlinks):
129
if _abspath in container.keys():
126
133
def delete(self, relpath):
127
134
"""See Transport.delete()."""
128
135
_abspath = self._abspath(relpath)
129
if not _abspath in self._files:
136
if _abspath in self._files:
137
del self._files[_abspath]
138
elif _abspath in self._symlinks:
139
del self._symlinks[_abspath]
130
141
raise NoSuchFile(relpath)
131
del self._files[_abspath]
133
143
def external_url(self):
134
"""See bzrlib.transport.Transport.external_url."""
144
"""See breezy.transport.Transport.external_url."""
135
145
# MemoryTransport's are only accessible in-process
136
146
# so we raise here
137
147
raise InProcessTransport(self)
139
149
def get(self, relpath):
140
150
"""See Transport.get()."""
141
_abspath = self._abspath(relpath)
142
if not _abspath in self._files:
151
_abspath = self._resolve_symlinks(relpath)
152
if _abspath not in self._files:
143
153
if _abspath in self._dirs:
144
154
return LateReadError(relpath)
146
156
raise NoSuchFile(relpath)
147
return StringIO(self._files[_abspath][0])
157
return BytesIO(self._files[_abspath][0])
149
159
def put_file(self, relpath, f, mode=None):
150
160
"""See Transport.put_file()."""
151
_abspath = self._abspath(relpath)
152
self._check_parent(_abspath)
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)
161
_abspath = self._resolve_symlinks(relpath)
162
self._check_parent(_abspath)
164
self._files[_abspath] = (raw_bytes, mode)
165
return len(raw_bytes)
167
def symlink(self, source, target):
168
_abspath = self._resolve_symlinks(target)
169
self._check_parent(_abspath)
170
self._symlinks[_abspath] = self._abspath(source)
163
172
def mkdir(self, relpath, mode=None):
164
173
"""See Transport.mkdir()."""
165
_abspath = self._abspath(relpath)
174
_abspath = self._resolve_symlinks(relpath)
166
175
self._check_parent(_abspath)
167
176
if _abspath in self._dirs:
168
177
raise FileExists(relpath)
169
self._dirs[_abspath]=mode
178
self._dirs[_abspath] = mode
171
180
def open_write_stream(self, relpath, mode=None):
172
181
"""See Transport.open_write_stream."""
173
self.put_bytes(relpath, "", mode)
182
self.put_bytes(relpath, b"", mode)
174
183
result = AppendBasedFileStream(self, relpath)
175
184
_file_streams[self.abspath(relpath)] = result
194
203
if not _abspath.endswith('/'):
197
for path_group in self._files, self._dirs:
206
for path_group in self._files, self._dirs, self._symlinks:
198
207
for path in path_group:
199
208
if path.startswith(_abspath):
200
209
trailing = path[len(_abspath):]
201
210
if trailing and '/' not in trailing:
202
result.append(trailing)
203
return map(urlutils.escape, result)
211
result.append(urlutils.escape(trailing))
205
214
def rename(self, rel_from, rel_to):
206
215
"""Rename a file or directory; fail if the destination exists"""
207
abs_from = self._abspath(rel_from)
208
abs_to = self._abspath(rel_to)
216
abs_from = self._resolve_symlinks(rel_from)
217
abs_to = self._resolve_symlinks(rel_to)
210
220
if x == abs_from:
212
222
elif x.startswith(abs_from + '/'):
213
223
x = abs_to + x[len(abs_from):]
215
226
def do_renames(container):
216
228
for path in container:
217
229
new_path = replace(path)
218
230
if new_path != path:
219
231
if new_path in container:
220
232
raise FileExists(new_path)
221
container[new_path] = container[path]
223
do_renames(self._files)
224
do_renames(self._dirs)
233
renames.append((path, new_path))
234
for path, new_path in renames:
235
container[new_path] = container[path]
238
# If we modify the existing dicts, we're not atomic anymore and may
239
# fail differently depending on dict order. So work on copy, fail on
240
# error on only replace dicts if all goes well.
241
renamed_files = self._files.copy()
242
renamed_symlinks = self._symlinks.copy()
243
renamed_dirs = self._dirs.copy()
244
do_renames(renamed_files)
245
do_renames(renamed_symlinks)
246
do_renames(renamed_dirs)
247
# We may have been cloned so modify in place
249
self._files.update(renamed_files)
250
self._symlinks.clear()
251
self._symlinks.update(renamed_symlinks)
253
self._dirs.update(renamed_dirs)
226
255
def rmdir(self, relpath):
227
256
"""See Transport.rmdir."""
228
_abspath = self._abspath(relpath)
257
_abspath = self._resolve_symlinks(relpath)
229
258
if _abspath in self._files:
230
259
self._translate_error(IOError(errno.ENOTDIR, relpath), relpath)
231
for path in self._files:
260
for path in itertools.chain(self._files, self._symlinks):
232
261
if path.startswith(_abspath + '/'):
233
262
self._translate_error(IOError(errno.ENOTEMPTY, relpath),
235
264
for path in self._dirs:
236
265
if path.startswith(_abspath + '/') and path != _abspath:
237
self._translate_error(IOError(errno.ENOTEMPTY, relpath), relpath)
238
if not _abspath in self._dirs:
266
self._translate_error(
267
IOError(errno.ENOTEMPTY, relpath), relpath)
268
if _abspath not in self._dirs:
239
269
raise NoSuchFile(relpath)
240
270
del self._dirs[_abspath]
242
272
def stat(self, relpath):
243
273
"""See Transport.stat()."""
244
274
_abspath = self._abspath(relpath)
245
if _abspath in self._files:
246
return MemoryStat(len(self._files[_abspath][0]), False,
275
if _abspath in self._files.keys():
276
return MemoryStat(len(self._files[_abspath][0]), S_IFREG,
247
277
self._files[_abspath][1])
248
elif _abspath in self._dirs:
249
return MemoryStat(0, True, self._dirs[_abspath])
278
elif _abspath in self._dirs.keys():
279
return MemoryStat(0, S_IFDIR, self._dirs[_abspath])
280
elif _abspath in self._symlinks.keys():
281
return MemoryStat(0, S_IFLNK)
251
283
raise NoSuchFile(_abspath)
272
310
raise ValueError("illegal relpath %r under %r"
273
% (relpath, self._cwd))
311
% (relpath, self._cwd))
275
313
elif i == '.' or i == '':
317
r = self._symlinks.get('/'.join(r), r)
279
318
return '/' + '/'.join(r)
320
def symlink(self, source, link_name):
321
"""Create a symlink pointing to source named link_name."""
322
_abspath = self._abspath(link_name)
323
self._check_parent(_abspath)
324
self._symlinks[_abspath] = source.split('/')
326
def readlink(self, link_name):
327
_abspath = self._abspath(link_name)
329
return '/'.join(self._symlinks[_abspath])
331
raise NoSuchFile(link_name)
282
334
class _MemoryLock(object):
283
335
"""This makes a lock."""