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