15
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16
16
"""Implementation of Transport that uses memory for its storage."""
20
22
from cStringIO import StringIO
22
24
from bzrlib.trace import mutter
23
from bzrlib.errors import TransportError, NoSuchFile, FileExists
24
from bzrlib.transport import Transport
25
from bzrlib.errors import TransportError, NoSuchFile, FileExists, LockError
26
from bzrlib.transport import Transport, register_transport, Server
26
28
class MemoryStat(object):
28
def __init__(self, size):
30
def __init__(self, size, is_dir, perms):
29
31
self.st_size = size
35
self.st_mode = S_IFREG | perms
37
self.st_mode = S_IFDIR | perms
32
40
class MemoryTransport(Transport):
33
"""This is the transport agent for local filesystem access."""
41
"""This is an in memory file system for transient data storage."""
43
def __init__(self, url=""):
36
44
"""Set the 'base' path where files will be stored."""
37
super(MemoryTransport, self).__init__('in-memory:')
49
super(MemoryTransport, self).__init__(url)
50
self._cwd = url[url.find(':') + 1:]
41
55
def clone(self, offset=None):
42
56
"""See Transport.clone()."""
59
segments = offset.split('/')
60
cwdsegments = self._cwd.split('/')[:-1]
62
segment = segments.pop(0)
66
if len(cwdsegments) > 1:
69
cwdsegments.append(segment)
70
url = self.base[:self.base.find(':') + 1] + '/'.join(cwdsegments) + '/'
71
result = MemoryTransport(url)
72
result._dirs = self._dirs
73
result._files = self._files
74
result._locks = self._locks
45
77
def abspath(self, relpath):
46
78
"""See Transport.abspath()."""
47
return self.base + relpath
79
return self.base[:-1] + self._abspath(relpath)[len(self._cwd) - 1:]
49
81
def append(self, relpath, f):
50
82
"""See Transport.append()."""
51
self._check_parent(relpath)
52
self._files[relpath] = self._files.get(relpath, "") + f.read()
83
_abspath = self._abspath(relpath)
84
self._check_parent(_abspath)
85
orig_content, orig_mode = self._files.get(_abspath, ("", None))
86
self._files[_abspath] = (orig_content + f.read(), orig_mode)
54
def _check_parent(self, relpath):
55
dir = os.path.dirname(relpath)
88
def _check_parent(self, _abspath):
89
dir = os.path.dirname(_abspath)
57
91
if not dir in self._dirs:
58
raise NoSuchFile(relpath)
92
raise NoSuchFile(_abspath)
60
94
def has(self, relpath):
61
95
"""See Transport.has()."""
62
return relpath in self._files
96
_abspath = self._abspath(relpath)
97
return _abspath in self._files or _abspath in self._dirs
99
def delete(self, relpath):
100
"""See Transport.delete()."""
101
_abspath = self._abspath(relpath)
102
if not _abspath in self._files:
103
raise NoSuchFile(relpath)
104
del self._files[_abspath]
64
106
def get(self, relpath):
65
107
"""See Transport.get()."""
66
if not relpath in self._files:
108
_abspath = self._abspath(relpath)
109
if not _abspath in self._files:
67
110
raise NoSuchFile(relpath)
68
return StringIO(self._files[relpath])
111
return StringIO(self._files[_abspath][0])
70
113
def put(self, relpath, f, mode=None):
71
114
"""See Transport.put()."""
72
self._check_parent(relpath)
73
self._files[relpath] = f.read()
115
_abspath = self._abspath(relpath)
116
self._check_parent(_abspath)
117
self._files[_abspath] = (f.read(), mode)
75
119
def mkdir(self, relpath, mode=None):
76
120
"""See Transport.mkdir()."""
77
self._check_parent(relpath)
78
if relpath in self._dirs:
121
_abspath = self._abspath(relpath)
122
self._check_parent(_abspath)
123
if _abspath in self._dirs:
79
124
raise FileExists(relpath)
80
self._dirs.add(relpath)
125
self._dirs[_abspath]=mode
82
127
def listable(self):
83
128
"""See Transport.listable."""
86
131
def iter_files_recursive(self):
87
return iter(self._files)
89
# def list_dir(self, relpath):
132
for file in self._files:
133
if file.startswith(self._cwd):
134
yield file[len(self._cwd):]
136
def list_dir(self, relpath):
137
"""See Transport.list_dir()."""
138
_abspath = self._abspath(relpath)
139
if _abspath != '/' and _abspath not in self._dirs:
140
raise NoSuchFile(relpath)
142
for path in self._files:
143
if (path.startswith(_abspath) and
144
path[len(_abspath) + 1:].find('/') == -1 and
145
len(path) > len(_abspath)):
146
result.append(path[len(_abspath) + 1:])
147
for path in self._dirs:
148
if (path.startswith(_abspath) and
149
path[len(_abspath) + 1:].find('/') == -1 and
150
len(path) > len(_abspath) and
151
path[len(_abspath)] == '/'):
152
result.append(path[len(_abspath) + 1:])
155
def rmdir(self, relpath):
156
"""See Transport.rmdir."""
157
_abspath = self._abspath(relpath)
158
if _abspath in self._files:
159
self._translate_error(IOError(errno.ENOTDIR, relpath), relpath)
160
for path in self._files:
161
if path.startswith(_abspath):
162
self._translate_error(IOError(errno.EBUSY, relpath), relpath)
163
for path in self._dirs:
164
if path.startswith(_abspath) and path != _abspath:
165
self._translate_error(IOError(errno.EBUSY, relpath), relpath)
166
if not _abspath in self._dirs:
167
raise NoSuchFile(relpath)
168
del self._dirs[_abspath]
92
170
def stat(self, relpath):
93
171
"""See Transport.stat()."""
94
return MemoryStat(len(self._files[relpath]))
96
# def lock_read(self, relpath):
99
# def lock_write(self, relpath):
172
_abspath = self._abspath(relpath)
173
if _abspath in self._files:
174
return MemoryStat(len(self._files[_abspath][0]), False,
175
self._files[_abspath][1])
177
return MemoryStat(0, True, None)
178
elif _abspath in self._dirs:
179
return MemoryStat(0, True, self._dirs[_abspath])
181
raise NoSuchFile(_abspath)
183
def lock_read(self, relpath):
184
"""See Transport.lock_read()."""
185
return _MemoryLock(self._abspath(relpath), self)
187
def lock_write(self, relpath):
188
"""See Transport.lock_write()."""
189
return _MemoryLock(self._abspath(relpath), self)
191
def _abspath(self, relpath):
192
"""Generate an internal absolute path."""
193
if relpath.find('..') != -1:
194
raise AssertionError('relpath contains ..')
196
return self._cwd[:-1]
197
if relpath.endswith('/'):
198
relpath = relpath[:-1]
199
if relpath.startswith('./'):
200
relpath = relpath[2:]
201
return self._cwd + relpath
204
class _MemoryLock(object):
205
"""This makes a lock."""
207
def __init__(self, path, transport):
208
assert isinstance(transport, MemoryTransport)
210
self.transport = transport
211
if self.path in self.transport._locks:
212
raise LockError('File %r already locked' % (self.path,))
213
self.transport._locks[self.path] = self
216
# Should this warn, or actually try to cleanup?
218
warn("MemoryLock %r not explicitly unlocked" % (self.path,))
222
del self.transport._locks[self.path]
223
self.transport = None
226
class MemoryServer(Server):
227
"""Server for the MemoryTransport for testing with."""
230
"""See bzrlib.transport.Server.setUp."""
234
self._scheme = "memory+%s:" % id(self)
235
def memory_factory(url):
236
result = MemoryTransport(url)
237
result._dirs = self._dirs
238
result._files = self._files
239
result._locks = self._locks
241
register_transport(self._scheme, memory_factory)
244
"""See bzrlib.transport.Server.tearDown."""
245
# unregister this server
248
"""See bzrlib.transport.Server.get_url."""
252
def get_test_permutations():
253
"""Return the permutations to be used in testing."""
254
return [(MemoryTransport, MemoryServer),