13
13
# You should have received a copy of the GNU General Public License
14
14
# along with this program; if not, write to the Free Software
15
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16
"""Implementation of Transport that uses memory for its storage."""
17
"""Implementation of Transport that uses memory for its storage.
19
The contents of the transport will be lost when the object is discarded,
20
so this is primarily useful for testing.
20
28
from cStringIO import StringIO
22
30
from bzrlib.trace import mutter
23
from bzrlib.transport import Transport, \
24
TransportError, NoSuchFile, FileExists
31
from bzrlib.errors import TransportError, NoSuchFile, FileExists, LockError
32
from bzrlib.transport import Transport, register_transport, Server
27
34
class MemoryStat(object):
29
def __init__(self, size):
36
def __init__(self, size, is_dir, perms):
30
37
self.st_size = size
41
self.st_mode = S_IFREG | perms
45
self.st_mode = S_IFDIR | perms
33
48
class MemoryTransport(Transport):
34
"""This is the transport agent for local filesystem access."""
49
"""This is an in memory file system for transient data storage."""
51
def __init__(self, url=""):
37
52
"""Set the 'base' path where files will be stored."""
38
super(MemoryTransport, self).__init__('in-memory:')
57
super(MemoryTransport, self).__init__(url)
58
self._cwd = url[url.find(':') + 1:]
59
# dictionaries from absolute path to file mode
42
64
def clone(self, offset=None):
43
65
"""See Transport.clone()."""
68
segments = offset.split('/')
69
cwdsegments = self._cwd.split('/')[:-1]
71
segment = segments.pop(0)
75
if len(cwdsegments) > 1:
78
cwdsegments.append(segment)
79
url = self.base[:self.base.find(':') + 1] + '/'.join(cwdsegments) + '/'
80
result = MemoryTransport(url)
81
result._dirs = self._dirs
82
result._files = self._files
83
result._locks = self._locks
46
86
def abspath(self, relpath):
47
87
"""See Transport.abspath()."""
48
return self.base + relpath
88
return self.base[:-1] + self._abspath(relpath)[len(self._cwd) - 1:]
50
90
def append(self, relpath, f):
51
91
"""See Transport.append()."""
52
self._check_parent(relpath)
53
self._files[relpath] = self._files.get(relpath, "") + f.read()
92
_abspath = self._abspath(relpath)
93
self._check_parent(_abspath)
94
orig_content, orig_mode = self._files.get(_abspath, ("", None))
95
self._files[_abspath] = (orig_content + f.read(), orig_mode)
96
return len(orig_content)
55
def _check_parent(self, relpath):
56
dir = os.path.dirname(relpath)
98
def _check_parent(self, _abspath):
99
dir = os.path.dirname(_abspath)
58
101
if not dir in self._dirs:
59
raise NoSuchFile(relpath)
102
raise NoSuchFile(_abspath)
61
104
def has(self, relpath):
62
105
"""See Transport.has()."""
63
return relpath in self._files
106
_abspath = self._abspath(relpath)
107
return _abspath in self._files or _abspath in self._dirs
109
def delete(self, relpath):
110
"""See Transport.delete()."""
111
_abspath = self._abspath(relpath)
112
if not _abspath in self._files:
113
raise NoSuchFile(relpath)
114
del self._files[_abspath]
65
116
def get(self, relpath):
66
117
"""See Transport.get()."""
67
if not relpath in self._files:
118
_abspath = self._abspath(relpath)
119
if not _abspath in self._files:
68
120
raise NoSuchFile(relpath)
69
return StringIO(self._files[relpath])
121
return StringIO(self._files[_abspath][0])
71
def put(self, relpath, f):
123
def put(self, relpath, f, mode=None):
72
124
"""See Transport.put()."""
73
self._check_parent(relpath)
74
self._files[relpath] = f.read()
125
_abspath = self._abspath(relpath)
126
self._check_parent(_abspath)
127
self._files[_abspath] = (f.read(), mode)
76
def mkdir(self, relpath):
129
def mkdir(self, relpath, mode=None):
77
130
"""See Transport.mkdir()."""
78
self._check_parent(relpath)
79
if relpath in self._dirs:
131
_abspath = self._abspath(relpath)
132
self._check_parent(_abspath)
133
if _abspath in self._dirs:
80
134
raise FileExists(relpath)
81
self._dirs.add(relpath)
135
self._dirs[_abspath]=mode
83
137
def listable(self):
84
138
"""See Transport.listable."""
87
141
def iter_files_recursive(self):
88
return iter(self._files)
90
# def list_dir(self, relpath):
142
for file in self._files:
143
if file.startswith(self._cwd):
144
yield file[len(self._cwd):]
146
def list_dir(self, relpath):
147
"""See Transport.list_dir()."""
148
_abspath = self._abspath(relpath)
149
if _abspath != '/' and _abspath not in self._dirs:
150
raise NoSuchFile(relpath)
152
for path in self._files:
153
if (path.startswith(_abspath) and
154
path[len(_abspath) + 1:].find('/') == -1 and
155
len(path) > len(_abspath)):
156
result.append(path[len(_abspath) + 1:])
157
for path in self._dirs:
158
if (path.startswith(_abspath) and
159
path[len(_abspath) + 1:].find('/') == -1 and
160
len(path) > len(_abspath) and
161
path[len(_abspath)] == '/'):
162
result.append(path[len(_abspath) + 1:])
165
def rename(self, rel_from, rel_to):
166
"""Rename a file or directory; fail if the destination exists"""
167
abs_from = self._abspath(rel_from)
168
abs_to = self._abspath(rel_to)
172
elif x.startswith(abs_from + '/'):
173
x = abs_to + x[len(abs_from):]
175
def do_renames(container):
176
for path in container:
177
new_path = replace(path)
179
if new_path in container:
180
raise FileExists(new_path)
181
container[new_path] = container[path]
183
do_renames(self._files)
184
do_renames(self._dirs)
186
def rmdir(self, relpath):
187
"""See Transport.rmdir."""
188
_abspath = self._abspath(relpath)
189
if _abspath in self._files:
190
self._translate_error(IOError(errno.ENOTDIR, relpath), relpath)
191
for path in self._files:
192
if path.startswith(_abspath):
193
self._translate_error(IOError(errno.ENOTEMPTY, relpath),
195
for path in self._dirs:
196
if path.startswith(_abspath) and path != _abspath:
197
self._translate_error(IOError(errno.ENOTEMPTY, relpath), relpath)
198
if not _abspath in self._dirs:
199
raise NoSuchFile(relpath)
200
del self._dirs[_abspath]
93
202
def stat(self, relpath):
94
203
"""See Transport.stat()."""
95
return MemoryStat(len(self._files[relpath]))
97
# def lock_read(self, relpath):
100
# def lock_write(self, relpath):
204
_abspath = self._abspath(relpath)
205
if _abspath in self._files:
206
return MemoryStat(len(self._files[_abspath][0]), False,
207
self._files[_abspath][1])
209
return MemoryStat(0, True, None)
210
elif _abspath in self._dirs:
211
return MemoryStat(0, True, self._dirs[_abspath])
213
raise NoSuchFile(_abspath)
215
def lock_read(self, relpath):
216
"""See Transport.lock_read()."""
217
return _MemoryLock(self._abspath(relpath), self)
219
def lock_write(self, relpath):
220
"""See Transport.lock_write()."""
221
return _MemoryLock(self._abspath(relpath), self)
223
def _abspath(self, relpath):
224
"""Generate an internal absolute path."""
225
if relpath.find('..') != -1:
226
raise AssertionError('relpath contains ..')
228
return self._cwd[:-1]
229
if relpath.endswith('/'):
230
relpath = relpath[:-1]
231
if relpath.startswith('./'):
232
relpath = relpath[2:]
233
return self._cwd + relpath
236
class _MemoryLock(object):
237
"""This makes a lock."""
239
def __init__(self, path, transport):
240
assert isinstance(transport, MemoryTransport)
242
self.transport = transport
243
if self.path in self.transport._locks:
244
raise LockError('File %r already locked' % (self.path,))
245
self.transport._locks[self.path] = self
248
# Should this warn, or actually try to cleanup?
250
warn("MemoryLock %r not explicitly unlocked" % (self.path,))
254
del self.transport._locks[self.path]
255
self.transport = None
258
class MemoryServer(Server):
259
"""Server for the MemoryTransport for testing with."""
262
"""See bzrlib.transport.Server.setUp."""
266
self._scheme = "memory+%s:" % id(self)
267
def memory_factory(url):
268
result = MemoryTransport(url)
269
result._dirs = self._dirs
270
result._files = self._files
271
result._locks = self._locks
273
register_transport(self._scheme, memory_factory)
276
"""See bzrlib.transport.Server.tearDown."""
277
# unregister this server
280
"""See bzrlib.transport.Server.get_url."""
284
def get_test_permutations():
285
"""Return the permutations to be used in testing."""
286
return [(MemoryTransport, MemoryServer),