50
50
class MemoryStat(object):
52
def __init__(self, size, kind, perms=None):
52
def __init__(self, size, is_dir, perms):
53
53
self.st_size = size
57
self.st_mode = kind | perms
57
self.st_mode = S_IFREG | perms
61
self.st_mode = kind | perms
61
self.st_mode = S_IFDIR | perms
64
64
class MemoryTransport(transport.Transport):
107
105
def append_file(self, relpath, f, mode=None):
108
106
"""See Transport.append_file()."""
109
_abspath = self._resolve_symlinks(relpath)
107
_abspath = self._abspath(relpath)
110
108
self._check_parent(_abspath)
111
orig_content, orig_mode = self._files.get(_abspath, (b"", None))
109
orig_content, orig_mode = self._files.get(_abspath, ("", None))
114
112
self._files[_abspath] = (orig_content + f.read(), mode)
117
115
def _check_parent(self, _abspath):
118
116
dir = os.path.dirname(_abspath)
120
if dir not in self._dirs:
118
if not dir in self._dirs:
121
119
raise NoSuchFile(_abspath)
123
121
def has(self, relpath):
124
122
"""See Transport.has()."""
125
123
_abspath = self._abspath(relpath)
126
for container in (self._files, self._dirs, self._symlinks):
127
if _abspath in container.keys():
124
return (_abspath in self._files) or (_abspath in self._dirs)
131
126
def delete(self, relpath):
132
127
"""See Transport.delete()."""
133
128
_abspath = self._abspath(relpath)
134
if _abspath in self._files:
135
del self._files[_abspath]
136
elif _abspath in self._symlinks:
137
del self._symlinks[_abspath]
129
if not _abspath in self._files:
139
130
raise NoSuchFile(relpath)
131
del self._files[_abspath]
141
133
def external_url(self):
142
134
"""See breezy.transport.Transport.external_url."""
157
149
def put_file(self, relpath, f, mode=None):
158
150
"""See Transport.put_file()."""
159
_abspath = self._resolve_symlinks(relpath)
151
_abspath = self._abspath(relpath)
160
152
self._check_parent(_abspath)
161
153
raw_bytes = f.read()
162
154
self._files[_abspath] = (raw_bytes, mode)
163
155
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)
170
157
def mkdir(self, relpath, mode=None):
171
158
"""See Transport.mkdir()."""
172
_abspath = self._resolve_symlinks(relpath)
159
_abspath = self._abspath(relpath)
173
160
self._check_parent(_abspath)
174
161
if _abspath in self._dirs:
175
162
raise FileExists(relpath)
176
self._dirs[_abspath] = mode
163
self._dirs[_abspath]=mode
178
165
def open_write_stream(self, relpath, mode=None):
179
166
"""See Transport.open_write_stream."""
180
self.put_bytes(relpath, b"", mode)
167
self.put_bytes(relpath, "", mode)
181
168
result = AppendBasedFileStream(self, relpath)
182
169
_file_streams[self.abspath(relpath)] = result
189
176
def iter_files_recursive(self):
190
for file in itertools.chain(self._files, self._symlinks):
177
for file in self._files:
191
178
if file.startswith(self._cwd):
192
179
yield urlutils.escape(file[len(self._cwd):])
194
181
def list_dir(self, relpath):
195
182
"""See Transport.list_dir()."""
196
_abspath = self._resolve_symlinks(relpath)
183
_abspath = self._abspath(relpath)
197
184
if _abspath != '/' and _abspath not in self._dirs:
198
185
raise NoSuchFile(relpath)
212
199
def rename(self, rel_from, rel_to):
213
200
"""Rename a file or directory; fail if the destination exists"""
214
abs_from = self._resolve_symlinks(rel_from)
215
abs_to = self._resolve_symlinks(rel_to)
201
abs_from = self._abspath(rel_from)
202
abs_to = self._abspath(rel_to)
218
204
if x == abs_from:
220
206
elif x.startswith(abs_from + '/'):
221
207
x = abs_to + x[len(abs_from):]
224
209
def do_renames(container):
226
210
for path in container:
227
211
new_path = replace(path)
228
212
if new_path != path:
229
213
if new_path in container:
230
214
raise FileExists(new_path)
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)
215
container[new_path] = container[path]
217
do_renames(self._files)
218
do_renames(self._dirs)
253
220
def rmdir(self, relpath):
254
221
"""See Transport.rmdir."""
255
_abspath = self._resolve_symlinks(relpath)
222
_abspath = self._abspath(relpath)
256
223
if _abspath in self._files:
257
224
self._translate_error(IOError(errno.ENOTDIR, relpath), relpath)
258
for path in itertools.chain(self._files, self._symlinks):
225
for path in self._files:
259
226
if path.startswith(_abspath + '/'):
260
227
self._translate_error(IOError(errno.ENOTEMPTY, relpath),
262
229
for path in self._dirs:
263
230
if path.startswith(_abspath + '/') and path != _abspath:
264
self._translate_error(
265
IOError(errno.ENOTEMPTY, relpath), relpath)
266
if _abspath not in self._dirs:
231
self._translate_error(IOError(errno.ENOTEMPTY, relpath), relpath)
232
if not _abspath in self._dirs:
267
233
raise NoSuchFile(relpath)
268
234
del self._dirs[_abspath]
270
236
def stat(self, relpath):
271
237
"""See Transport.stat()."""
272
238
_abspath = self._abspath(relpath)
273
if _abspath in self._files.keys():
274
return MemoryStat(len(self._files[_abspath][0]), S_IFREG,
239
if _abspath in self._files:
240
return MemoryStat(len(self._files[_abspath][0]), False,
275
241
self._files[_abspath][1])
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)
242
elif _abspath in self._dirs:
243
return MemoryStat(0, True, self._dirs[_abspath])
281
245
raise NoSuchFile(_abspath)
288
252
"""See Transport.lock_write()."""
289
253
return _MemoryLock(self._abspath(relpath), self)
291
def _resolve_symlinks(self, relpath):
292
path = self._abspath(relpath)
293
while path in self._symlinks.keys():
294
path = self._symlinks[path]
297
255
def _abspath(self, relpath):
298
256
"""Generate an internal absolute path."""
299
257
relpath = urlutils.unescape(relpath)
308
266
raise ValueError("illegal relpath %r under %r"
309
% (relpath, self._cwd))
267
% (relpath, self._cwd))
311
269
elif i == '.' or i == '':
315
r = self._symlinks.get('/'.join(r), r)
316
273
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)
332
276
class _MemoryLock(object):
333
277
"""This makes a lock."""