119
111
def _check_parent(self, _abspath):
120
112
dir = os.path.dirname(_abspath)
122
if dir not in self._dirs:
114
if not dir in self._dirs:
123
115
raise NoSuchFile(_abspath)
125
117
def has(self, relpath):
126
118
"""See Transport.has()."""
127
119
_abspath = self._abspath(relpath)
128
for container in (self._files, self._dirs, self._symlinks):
129
if _abspath in container.keys():
120
return _abspath in self._files or _abspath in self._dirs
133
122
def delete(self, relpath):
134
123
"""See Transport.delete()."""
135
124
_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]
125
if not _abspath in self._files:
141
126
raise NoSuchFile(relpath)
143
def external_url(self):
144
"""See breezy.transport.Transport.external_url."""
145
# MemoryTransport's are only accessible in-process
147
raise InProcessTransport(self)
127
del self._files[_abspath]
149
129
def get(self, relpath):
150
130
"""See Transport.get()."""
151
_abspath = self._resolve_symlinks(relpath)
152
if _abspath not in self._files:
153
if _abspath in self._dirs:
154
return LateReadError(relpath)
156
raise NoSuchFile(relpath)
157
return BytesIO(self._files[_abspath][0])
159
def put_file(self, relpath, f, mode=None):
160
"""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)
131
_abspath = self._abspath(relpath)
132
if not _abspath in self._files:
133
raise NoSuchFile(relpath)
134
return StringIO(self._files[_abspath][0])
136
def put(self, relpath, f, mode=None):
137
"""See Transport.put()."""
138
_abspath = self._abspath(relpath)
139
self._check_parent(_abspath)
140
self._files[_abspath] = (f.read(), mode)
172
142
def mkdir(self, relpath, mode=None):
173
143
"""See Transport.mkdir()."""
174
_abspath = self._resolve_symlinks(relpath)
144
_abspath = self._abspath(relpath)
175
145
self._check_parent(_abspath)
176
146
if _abspath in self._dirs:
177
147
raise FileExists(relpath)
178
self._dirs[_abspath] = mode
180
def open_write_stream(self, relpath, mode=None):
181
"""See Transport.open_write_stream."""
182
self.put_bytes(relpath, b"", mode)
183
result = AppendBasedFileStream(self, relpath)
184
_file_streams[self.abspath(relpath)] = result
148
self._dirs[_abspath]=mode
187
150
def listable(self):
188
151
"""See Transport.listable."""
191
154
def iter_files_recursive(self):
192
for file in itertools.chain(self._files, self._symlinks):
155
for file in self._files:
193
156
if file.startswith(self._cwd):
194
yield urlutils.escape(file[len(self._cwd):])
157
yield file[len(self._cwd):]
196
159
def list_dir(self, relpath):
197
160
"""See Transport.list_dir()."""
198
_abspath = self._resolve_symlinks(relpath)
161
_abspath = self._abspath(relpath)
199
162
if _abspath != '/' and _abspath not in self._dirs:
200
163
raise NoSuchFile(relpath)
203
if not _abspath.endswith('/'):
206
for path_group in self._files, self._dirs, self._symlinks:
207
for path in path_group:
208
if path.startswith(_abspath):
209
trailing = path[len(_abspath):]
210
if trailing and '/' not in trailing:
211
result.append(urlutils.escape(trailing))
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:])
214
178
def rename(self, rel_from, rel_to):
215
179
"""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)
180
abs_from = self._abspath(rel_from)
181
abs_to = self._abspath(rel_to)
220
183
if x == abs_from:
222
185
elif x.startswith(abs_from + '/'):
223
186
x = abs_to + x[len(abs_from):]
226
188
def do_renames(container):
228
189
for path in container:
229
190
new_path = replace(path)
230
191
if new_path != path:
231
192
if new_path in container:
232
193
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)
194
container[new_path] = container[path]
196
do_renames(self._files)
197
do_renames(self._dirs)
255
199
def rmdir(self, relpath):
256
200
"""See Transport.rmdir."""
257
_abspath = self._resolve_symlinks(relpath)
201
_abspath = self._abspath(relpath)
258
202
if _abspath in self._files:
259
203
self._translate_error(IOError(errno.ENOTDIR, relpath), relpath)
260
for path in itertools.chain(self._files, self._symlinks):
261
if path.startswith(_abspath + '/'):
204
for path in self._files:
205
if path.startswith(_abspath):
262
206
self._translate_error(IOError(errno.ENOTEMPTY, relpath),
264
208
for path in self._dirs:
265
if path.startswith(_abspath + '/') and path != _abspath:
266
self._translate_error(
267
IOError(errno.ENOTEMPTY, relpath), relpath)
268
if _abspath not in self._dirs:
209
if path.startswith(_abspath) and path != _abspath:
210
self._translate_error(IOError(errno.ENOTEMPTY, relpath), relpath)
211
if not _abspath in self._dirs:
269
212
raise NoSuchFile(relpath)
270
213
del self._dirs[_abspath]
272
215
def stat(self, relpath):
273
216
"""See Transport.stat()."""
274
217
_abspath = self._abspath(relpath)
275
if _abspath in self._files.keys():
276
return MemoryStat(len(self._files[_abspath][0]), S_IFREG,
218
if _abspath in self._files:
219
return MemoryStat(len(self._files[_abspath][0]), False,
277
220
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)
221
elif _abspath in self._dirs:
222
return MemoryStat(0, True, self._dirs[_abspath])
283
224
raise NoSuchFile(_abspath)
290
231
"""See Transport.lock_write()."""
291
232
return _MemoryLock(self._abspath(relpath), self)
293
def _resolve_symlinks(self, relpath):
294
path = self._abspath(relpath)
295
while path in self._symlinks.keys():
296
path = self._symlinks[path]
299
234
def _abspath(self, relpath):
300
235
"""Generate an internal absolute path."""
301
236
relpath = urlutils.unescape(relpath)
302
if relpath[:1] == '/':
304
cwd_parts = self._cwd.split('/')
305
rel_parts = relpath.split('/')
307
for i in cwd_parts + rel_parts:
310
raise ValueError("illegal relpath %r under %r"
311
% (relpath, self._cwd))
313
elif i == '.' or i == '':
317
r = self._symlinks.get('/'.join(r), r)
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)
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
334
250
class _MemoryLock(object):
335
251
"""This makes a lock."""
337
253
def __init__(self, path, transport):
254
assert isinstance(transport, MemoryTransport)
339
256
self.transport = transport
340
257
if self.path in self.transport._locks:
341
258
raise LockError('File %r already locked' % (self.path,))
342
259
self.transport._locks[self.path] = self
262
# Should this warn, or actually try to cleanup?
264
warnings.warn("MemoryLock %r not explicitly unlocked" % (self.path,))
344
267
def unlock(self):
345
268
del self.transport._locks[self.path]
346
269
self.transport = None
349
class MemoryServer(transport.Server):
272
class MemoryServer(Server):
350
273
"""Server for the MemoryTransport for testing with."""
352
def start_server(self):
353
self._dirs = {'/': None}
276
"""See bzrlib.transport.Server.setUp."""
277
self._dirs = {'/':None}
357
280
self._scheme = "memory+%s:///" % id(self)
359
281
def memory_factory(url):
361
result = memory.MemoryTransport(url)
282
result = MemoryTransport(url)
362
283
result._dirs = self._dirs
363
284
result._files = self._files
364
result._symlinks = self._symlinks
365
285
result._locks = self._locks
367
self._memory_factory = memory_factory
368
transport.register_transport(self._scheme, self._memory_factory)
287
register_transport(self._scheme, memory_factory)
370
def stop_server(self):
290
"""See bzrlib.transport.Server.tearDown."""
371
291
# unregister this server
372
transport.unregister_transport(self._scheme, self._memory_factory)
374
293
def get_url(self):
375
"""See breezy.transport.Server.get_url."""
294
"""See bzrlib.transport.Server.get_url."""
376
295
return self._scheme
378
def get_bogus_url(self):
379
raise NotImplementedError
382
298
def get_test_permutations():
383
299
"""Return the permutations to be used in testing."""