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