~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/transport/memory.py

  • Committer: Patch Queue Manager
  • Date: 2016-04-21 04:10:52 UTC
  • mfrom: (6616.1.1 fix-en-user-guide)
  • Revision ID: pqm@pqm.ubuntu.com-20160421041052-clcye7ns1qcl2n7w
(richard-wilbur) Ensure build of English use guide always uses English text
 even when user's locale specifies a different language. (Jelmer Vernooij)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (C) 2005-2011, 2016 Canonical Ltd
 
2
#
 
3
# This program is free software; you can redistribute it and/or modify
 
4
# it under the terms of the GNU General Public License as published by
 
5
# the Free Software Foundation; either version 2 of the License, or
 
6
# (at your option) any later version.
 
7
#
 
8
# This program is distributed in the hope that it will be useful,
 
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
11
# GNU General Public License for more details.
 
12
#
 
13
# You should have received a copy of the GNU General Public License
 
14
# along with this program; if not, write to the Free Software
 
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 
16
 
 
17
"""Implementation of Transport that uses memory for its storage.
 
18
 
 
19
The contents of the transport will be lost when the object is discarded,
 
20
so this is primarily useful for testing.
 
21
"""
 
22
 
 
23
from __future__ import absolute_import
 
24
 
 
25
import os
 
26
import errno
 
27
from stat import S_IFREG, S_IFDIR
 
28
from cStringIO import StringIO
 
29
 
 
30
from bzrlib import (
 
31
    transport,
 
32
    urlutils,
 
33
    )
 
34
from bzrlib.errors import (
 
35
    FileExists,
 
36
    LockError,
 
37
    InProcessTransport,
 
38
    NoSuchFile,
 
39
    )
 
40
from bzrlib.transport import (
 
41
    AppendBasedFileStream,
 
42
    _file_streams,
 
43
    LateReadError,
 
44
    )
 
45
 
 
46
 
 
47
 
 
48
class MemoryStat(object):
 
49
 
 
50
    def __init__(self, size, is_dir, perms):
 
51
        self.st_size = size
 
52
        if not is_dir:
 
53
            if perms is None:
 
54
                perms = 0644
 
55
            self.st_mode = S_IFREG | perms
 
56
        else:
 
57
            if perms is None:
 
58
                perms = 0755
 
59
            self.st_mode = S_IFDIR | perms
 
60
 
 
61
 
 
62
class MemoryTransport(transport.Transport):
 
63
    """This is an in memory file system for transient data storage."""
 
64
 
 
65
    def __init__(self, url=""):
 
66
        """Set the 'base' path where files will be stored."""
 
67
        if url == "":
 
68
            url = "memory:///"
 
69
        if url[-1] != '/':
 
70
            url = url + '/'
 
71
        super(MemoryTransport, self).__init__(url)
 
72
        split = url.find(':') + 3
 
73
        self._scheme = url[:split]
 
74
        self._cwd = url[split:]
 
75
        # dictionaries from absolute path to file mode
 
76
        self._dirs = {'/':None}
 
77
        self._files = {}
 
78
        self._locks = {}
 
79
 
 
80
    def clone(self, offset=None):
 
81
        """See Transport.clone()."""
 
82
        path = urlutils.URL._combine_paths(self._cwd, offset)
 
83
        if len(path) == 0 or path[-1] != '/':
 
84
            path += '/'
 
85
        url = self._scheme + path
 
86
        result = self.__class__(url)
 
87
        result._dirs = self._dirs
 
88
        result._files = self._files
 
89
        result._locks = self._locks
 
90
        return result
 
91
 
 
92
    def abspath(self, relpath):
 
93
        """See Transport.abspath()."""
 
94
        # while a little slow, this is sufficiently fast to not matter in our
 
95
        # current environment - XXX RBC 20060404 move the clone '..' handling
 
96
        # into here and call abspath from clone
 
97
        temp_t = self.clone(relpath)
 
98
        if temp_t.base.count('/') == 3:
 
99
            return temp_t.base
 
100
        else:
 
101
            return temp_t.base[:-1]
 
102
 
 
103
    def append_file(self, relpath, f, mode=None):
 
104
        """See Transport.append_file()."""
 
105
        _abspath = self._abspath(relpath)
 
106
        self._check_parent(_abspath)
 
107
        orig_content, orig_mode = self._files.get(_abspath, ("", None))
 
108
        if mode is None:
 
109
            mode = orig_mode
 
110
        self._files[_abspath] = (orig_content + f.read(), mode)
 
111
        return len(orig_content)
 
112
 
 
113
    def _check_parent(self, _abspath):
 
114
        dir = os.path.dirname(_abspath)
 
115
        if dir != '/':
 
116
            if not dir in self._dirs:
 
117
                raise NoSuchFile(_abspath)
 
118
 
 
119
    def has(self, relpath):
 
120
        """See Transport.has()."""
 
121
        _abspath = self._abspath(relpath)
 
122
        return (_abspath in self._files) or (_abspath in self._dirs)
 
123
 
 
124
    def delete(self, relpath):
 
125
        """See Transport.delete()."""
 
126
        _abspath = self._abspath(relpath)
 
127
        if not _abspath in self._files:
 
128
            raise NoSuchFile(relpath)
 
129
        del self._files[_abspath]
 
130
 
 
131
    def external_url(self):
 
132
        """See bzrlib.transport.Transport.external_url."""
 
133
        # MemoryTransport's are only accessible in-process
 
134
        # so we raise here
 
135
        raise InProcessTransport(self)
 
136
 
 
137
    def get(self, relpath):
 
138
        """See Transport.get()."""
 
139
        _abspath = self._abspath(relpath)
 
140
        if not _abspath in self._files:
 
141
            if _abspath in self._dirs:
 
142
                return LateReadError(relpath)
 
143
            else:
 
144
                raise NoSuchFile(relpath)
 
145
        return StringIO(self._files[_abspath][0])
 
146
 
 
147
    def put_file(self, relpath, f, mode=None):
 
148
        """See Transport.put_file()."""
 
149
        _abspath = self._abspath(relpath)
 
150
        self._check_parent(_abspath)
 
151
        raw_bytes = f.read()
 
152
        self._files[_abspath] = (raw_bytes, mode)
 
153
        return len(raw_bytes)
 
154
 
 
155
    def mkdir(self, relpath, mode=None):
 
156
        """See Transport.mkdir()."""
 
157
        _abspath = self._abspath(relpath)
 
158
        self._check_parent(_abspath)
 
159
        if _abspath in self._dirs:
 
160
            raise FileExists(relpath)
 
161
        self._dirs[_abspath]=mode
 
162
 
 
163
    def open_write_stream(self, relpath, mode=None):
 
164
        """See Transport.open_write_stream."""
 
165
        self.put_bytes(relpath, "", mode)
 
166
        result = AppendBasedFileStream(self, relpath)
 
167
        _file_streams[self.abspath(relpath)] = result
 
168
        return result
 
169
 
 
170
    def listable(self):
 
171
        """See Transport.listable."""
 
172
        return True
 
173
 
 
174
    def iter_files_recursive(self):
 
175
        for file in self._files:
 
176
            if file.startswith(self._cwd):
 
177
                yield urlutils.escape(file[len(self._cwd):])
 
178
 
 
179
    def list_dir(self, relpath):
 
180
        """See Transport.list_dir()."""
 
181
        _abspath = self._abspath(relpath)
 
182
        if _abspath != '/' and _abspath not in self._dirs:
 
183
            raise NoSuchFile(relpath)
 
184
        result = []
 
185
 
 
186
        if not _abspath.endswith('/'):
 
187
            _abspath += '/'
 
188
 
 
189
        for path_group in self._files, self._dirs:
 
190
            for path in path_group:
 
191
                if path.startswith(_abspath):
 
192
                    trailing = path[len(_abspath):]
 
193
                    if trailing and '/' not in trailing:
 
194
                        result.append(trailing)
 
195
        return map(urlutils.escape, result)
 
196
 
 
197
    def rename(self, rel_from, rel_to):
 
198
        """Rename a file or directory; fail if the destination exists"""
 
199
        abs_from = self._abspath(rel_from)
 
200
        abs_to = self._abspath(rel_to)
 
201
        def replace(x):
 
202
            if x == abs_from:
 
203
                x = abs_to
 
204
            elif x.startswith(abs_from + '/'):
 
205
                x = abs_to + x[len(abs_from):]
 
206
            return x
 
207
        def do_renames(container):
 
208
            for path in container:
 
209
                new_path = replace(path)
 
210
                if new_path != path:
 
211
                    if new_path in container:
 
212
                        raise FileExists(new_path)
 
213
                    container[new_path] = container[path]
 
214
                    del container[path]
 
215
        do_renames(self._files)
 
216
        do_renames(self._dirs)
 
217
 
 
218
    def rmdir(self, relpath):
 
219
        """See Transport.rmdir."""
 
220
        _abspath = self._abspath(relpath)
 
221
        if _abspath in self._files:
 
222
            self._translate_error(IOError(errno.ENOTDIR, relpath), relpath)
 
223
        for path in self._files:
 
224
            if path.startswith(_abspath + '/'):
 
225
                self._translate_error(IOError(errno.ENOTEMPTY, relpath),
 
226
                                      relpath)
 
227
        for path in self._dirs:
 
228
            if path.startswith(_abspath + '/') and path != _abspath:
 
229
                self._translate_error(IOError(errno.ENOTEMPTY, relpath), relpath)
 
230
        if not _abspath in self._dirs:
 
231
            raise NoSuchFile(relpath)
 
232
        del self._dirs[_abspath]
 
233
 
 
234
    def stat(self, relpath):
 
235
        """See Transport.stat()."""
 
236
        _abspath = self._abspath(relpath)
 
237
        if _abspath in self._files:
 
238
            return MemoryStat(len(self._files[_abspath][0]), False,
 
239
                              self._files[_abspath][1])
 
240
        elif _abspath in self._dirs:
 
241
            return MemoryStat(0, True, self._dirs[_abspath])
 
242
        else:
 
243
            raise NoSuchFile(_abspath)
 
244
 
 
245
    def lock_read(self, relpath):
 
246
        """See Transport.lock_read()."""
 
247
        return _MemoryLock(self._abspath(relpath), self)
 
248
 
 
249
    def lock_write(self, relpath):
 
250
        """See Transport.lock_write()."""
 
251
        return _MemoryLock(self._abspath(relpath), self)
 
252
 
 
253
    def _abspath(self, relpath):
 
254
        """Generate an internal absolute path."""
 
255
        relpath = urlutils.unescape(relpath)
 
256
        if relpath[:1] == '/':
 
257
            return relpath
 
258
        cwd_parts = self._cwd.split('/')
 
259
        rel_parts = relpath.split('/')
 
260
        r = []
 
261
        for i in cwd_parts + rel_parts:
 
262
            if i == '..':
 
263
                if not r:
 
264
                    raise ValueError("illegal relpath %r under %r"
 
265
                        % (relpath, self._cwd))
 
266
                r = r[:-1]
 
267
            elif i == '.' or i == '':
 
268
                pass
 
269
            else:
 
270
                r.append(i)
 
271
        return '/' + '/'.join(r)
 
272
 
 
273
 
 
274
class _MemoryLock(object):
 
275
    """This makes a lock."""
 
276
 
 
277
    def __init__(self, path, transport):
 
278
        self.path = path
 
279
        self.transport = transport
 
280
        if self.path in self.transport._locks:
 
281
            raise LockError('File %r already locked' % (self.path,))
 
282
        self.transport._locks[self.path] = self
 
283
 
 
284
    def unlock(self):
 
285
        del self.transport._locks[self.path]
 
286
        self.transport = None
 
287
 
 
288
 
 
289
class MemoryServer(transport.Server):
 
290
    """Server for the MemoryTransport for testing with."""
 
291
 
 
292
    def start_server(self):
 
293
        self._dirs = {'/':None}
 
294
        self._files = {}
 
295
        self._locks = {}
 
296
        self._scheme = "memory+%s:///" % id(self)
 
297
        def memory_factory(url):
 
298
            from bzrlib.transport import memory
 
299
            result = memory.MemoryTransport(url)
 
300
            result._dirs = self._dirs
 
301
            result._files = self._files
 
302
            result._locks = self._locks
 
303
            return result
 
304
        self._memory_factory = memory_factory
 
305
        transport.register_transport(self._scheme, self._memory_factory)
 
306
 
 
307
    def stop_server(self):
 
308
        # unregister this server
 
309
        transport.unregister_transport(self._scheme, self._memory_factory)
 
310
 
 
311
    def get_url(self):
 
312
        """See bzrlib.transport.Server.get_url."""
 
313
        return self._scheme
 
314
 
 
315
    def get_bogus_url(self):
 
316
        raise NotImplementedError
 
317
 
 
318
 
 
319
def get_test_permutations():
 
320
    """Return the permutations to be used in testing."""
 
321
    return [(MemoryTransport, MemoryServer),
 
322
            ]