~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/transport/memory.py

  • Committer: Patch Queue Manager
  • Date: 2012-10-25 11:13:27 UTC
  • mfrom: (6570.1.6 rubberstamp)
  • Revision ID: pqm@pqm.ubuntu.com-20121025111327-p0ylql0nh9fla0rs
(gz) Set approved revision and vote "Approve" when using lp-propose
 --approve (Jonathan Lange)

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005, 2006 Canonical Ltd
2
 
 
 
1
# Copyright (C) 2005-2010 Canonical Ltd
 
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
5
5
# the Free Software Foundation; either version 2 of the License, or
6
6
# (at your option) any later version.
7
 
 
 
7
#
8
8
# This program is distributed in the hope that it will be useful,
9
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
11
# GNU General Public License for more details.
12
 
 
 
12
#
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
 
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
16
 
17
17
"""Implementation of Transport that uses memory for its storage.
18
18
 
20
20
so this is primarily useful for testing.
21
21
"""
22
22
 
23
 
from copy import copy
 
23
from __future__ import absolute_import
 
24
 
24
25
import os
25
26
import errno
26
 
import re
27
 
from stat import *
 
27
from stat import S_IFREG, S_IFDIR
28
28
from cStringIO import StringIO
29
29
 
30
 
from bzrlib.errors import TransportError, NoSuchFile, FileExists, LockError
31
 
from bzrlib.trace import mutter
32
 
from bzrlib.transport import (Transport, register_transport, Server)
33
 
import bzrlib.urlutils as urlutils
 
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
    )
34
45
 
35
46
 
36
47
 
48
59
            self.st_mode = S_IFDIR | perms
49
60
 
50
61
 
51
 
class MemoryTransport(Transport):
 
62
class MemoryTransport(transport.Transport):
52
63
    """This is an in memory file system for transient data storage."""
53
64
 
54
65
    def __init__(self, url=""):
58
69
        if url[-1] != '/':
59
70
            url = url + '/'
60
71
        super(MemoryTransport, self).__init__(url)
61
 
        self._cwd = url[url.find(':') + 3:]
 
72
        split = url.find(':') + 3
 
73
        self._scheme = url[:split]
 
74
        self._cwd = url[split:]
62
75
        # dictionaries from absolute path to file mode
63
76
        self._dirs = {'/':None}
64
77
        self._files = {}
66
79
 
67
80
    def clone(self, offset=None):
68
81
        """See Transport.clone()."""
69
 
        if offset is None or offset == '':
70
 
            return copy(self)
71
 
        segments = offset.split('/')
72
 
        cwdsegments = self._cwd.split('/')[:-1]
73
 
        while len(segments):
74
 
            segment = segments.pop(0)
75
 
            if segment == '.':
76
 
                continue
77
 
            if segment == '..':
78
 
                if len(cwdsegments) > 1:
79
 
                    cwdsegments.pop()
80
 
                continue
81
 
            cwdsegments.append(segment)
82
 
        url = self.base[:self.base.find(':') + 3] + '/'.join(cwdsegments) + '/'
83
 
        result = MemoryTransport(url)
 
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)
84
87
        result._dirs = self._dirs
85
88
        result._files = self._files
86
89
        result._locks = self._locks
97
100
        else:
98
101
            return temp_t.base[:-1]
99
102
 
100
 
    def append(self, relpath, f, mode=None):
101
 
        """See Transport.append()."""
 
103
    def append_file(self, relpath, f, mode=None):
 
104
        """See Transport.append_file()."""
102
105
        _abspath = self._abspath(relpath)
103
106
        self._check_parent(_abspath)
104
107
        orig_content, orig_mode = self._files.get(_abspath, ("", None))
116
119
    def has(self, relpath):
117
120
        """See Transport.has()."""
118
121
        _abspath = self._abspath(relpath)
119
 
        return _abspath in self._files or _abspath in self._dirs
 
122
        return (_abspath in self._files) or (_abspath in self._dirs)
120
123
 
121
124
    def delete(self, relpath):
122
125
        """See Transport.delete()."""
125
128
            raise NoSuchFile(relpath)
126
129
        del self._files[_abspath]
127
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
 
128
137
    def get(self, relpath):
129
138
        """See Transport.get()."""
130
139
        _abspath = self._abspath(relpath)
131
140
        if not _abspath in self._files:
132
 
            raise NoSuchFile(relpath)
 
141
            if _abspath in self._dirs:
 
142
                return LateReadError(relpath)
 
143
            else:
 
144
                raise NoSuchFile(relpath)
133
145
        return StringIO(self._files[_abspath][0])
134
146
 
135
 
    def put(self, relpath, f, mode=None):
136
 
        """See Transport.put()."""
 
147
    def put_file(self, relpath, f, mode=None):
 
148
        """See Transport.put_file()."""
137
149
        _abspath = self._abspath(relpath)
138
150
        self._check_parent(_abspath)
139
 
        self._files[_abspath] = (f.read(), mode)
 
151
        bytes = f.read()
 
152
        if type(bytes) is not str:
 
153
            # Although not strictly correct, we raise UnicodeEncodeError to be
 
154
            # compatible with other transports.
 
155
            raise UnicodeEncodeError(
 
156
                'undefined', bytes, 0, 1,
 
157
                'put_file must be given a file of bytes, not unicode.')
 
158
        self._files[_abspath] = (bytes, mode)
 
159
        return len(bytes)
140
160
 
141
161
    def mkdir(self, relpath, mode=None):
142
162
        """See Transport.mkdir()."""
146
166
            raise FileExists(relpath)
147
167
        self._dirs[_abspath]=mode
148
168
 
 
169
    def open_write_stream(self, relpath, mode=None):
 
170
        """See Transport.open_write_stream."""
 
171
        self.put_bytes(relpath, "", mode)
 
172
        result = AppendBasedFileStream(self, relpath)
 
173
        _file_streams[self.abspath(relpath)] = result
 
174
        return result
 
175
 
149
176
    def listable(self):
150
177
        """See Transport.listable."""
151
178
        return True
153
180
    def iter_files_recursive(self):
154
181
        for file in self._files:
155
182
            if file.startswith(self._cwd):
156
 
                yield file[len(self._cwd):]
157
 
    
 
183
                yield urlutils.escape(file[len(self._cwd):])
 
184
 
158
185
    def list_dir(self, relpath):
159
186
        """See Transport.list_dir()."""
160
187
        _abspath = self._abspath(relpath)
161
188
        if _abspath != '/' and _abspath not in self._dirs:
162
189
            raise NoSuchFile(relpath)
163
190
        result = []
164
 
        for path in self._files:
165
 
            if (path.startswith(_abspath) and 
166
 
                path[len(_abspath) + 1:].find('/') == -1 and
167
 
                len(path) > len(_abspath)):
168
 
                result.append(path[len(_abspath) + 1:])
169
 
        for path in self._dirs:
170
 
            if (path.startswith(_abspath) and 
171
 
                path[len(_abspath) + 1:].find('/') == -1 and
172
 
                len(path) > len(_abspath) and
173
 
                path[len(_abspath)] == '/'):
174
 
                result.append(path[len(_abspath) + 1:])
175
 
        return result
 
191
 
 
192
        if not _abspath.endswith('/'):
 
193
            _abspath += '/'
 
194
 
 
195
        for path_group in self._files, self._dirs:
 
196
            for path in path_group:
 
197
                if path.startswith(_abspath):
 
198
                    trailing = path[len(_abspath):]
 
199
                    if trailing and '/' not in trailing:
 
200
                        result.append(trailing)
 
201
        return map(urlutils.escape, result)
176
202
 
177
203
    def rename(self, rel_from, rel_to):
178
204
        """Rename a file or directory; fail if the destination exists"""
194
220
                    del container[path]
195
221
        do_renames(self._files)
196
222
        do_renames(self._dirs)
197
 
    
 
223
 
198
224
    def rmdir(self, relpath):
199
225
        """See Transport.rmdir."""
200
226
        _abspath = self._abspath(relpath)
201
227
        if _abspath in self._files:
202
228
            self._translate_error(IOError(errno.ENOTDIR, relpath), relpath)
203
229
        for path in self._files:
204
 
            if path.startswith(_abspath):
 
230
            if path.startswith(_abspath + '/'):
205
231
                self._translate_error(IOError(errno.ENOTEMPTY, relpath),
206
232
                                      relpath)
207
233
        for path in self._dirs:
208
 
            if path.startswith(_abspath) and path != _abspath:
 
234
            if path.startswith(_abspath + '/') and path != _abspath:
209
235
                self._translate_error(IOError(errno.ENOTEMPTY, relpath), relpath)
210
236
        if not _abspath in self._dirs:
211
237
            raise NoSuchFile(relpath)
215
241
        """See Transport.stat()."""
216
242
        _abspath = self._abspath(relpath)
217
243
        if _abspath in self._files:
218
 
            return MemoryStat(len(self._files[_abspath][0]), False, 
 
244
            return MemoryStat(len(self._files[_abspath][0]), False,
219
245
                              self._files[_abspath][1])
220
246
        elif _abspath in self._dirs:
221
247
            return MemoryStat(0, True, self._dirs[_abspath])
233
259
    def _abspath(self, relpath):
234
260
        """Generate an internal absolute path."""
235
261
        relpath = urlutils.unescape(relpath)
236
 
        if relpath.find('..') != -1:
237
 
            raise AssertionError('relpath contains ..')
238
 
        if relpath == '.':
239
 
            if (self._cwd == '/'):
240
 
                return self._cwd
241
 
            return self._cwd[:-1]
242
 
        if relpath.endswith('/'):
243
 
            relpath = relpath[:-1]
244
 
        if relpath.startswith('./'):
245
 
            relpath = relpath[2:]
246
 
        return self._cwd + relpath
 
262
        if relpath[:1] == '/':
 
263
            return relpath
 
264
        cwd_parts = self._cwd.split('/')
 
265
        rel_parts = relpath.split('/')
 
266
        r = []
 
267
        for i in cwd_parts + rel_parts:
 
268
            if i == '..':
 
269
                if not r:
 
270
                    raise ValueError("illegal relpath %r under %r"
 
271
                        % (relpath, self._cwd))
 
272
                r = r[:-1]
 
273
            elif i == '.' or i == '':
 
274
                pass
 
275
            else:
 
276
                r.append(i)
 
277
        return '/' + '/'.join(r)
247
278
 
248
279
 
249
280
class _MemoryLock(object):
250
281
    """This makes a lock."""
251
282
 
252
283
    def __init__(self, path, transport):
253
 
        assert isinstance(transport, MemoryTransport)
254
284
        self.path = path
255
285
        self.transport = transport
256
286
        if self.path in self.transport._locks:
257
287
            raise LockError('File %r already locked' % (self.path,))
258
288
        self.transport._locks[self.path] = self
259
289
 
260
 
    def __del__(self):
261
 
        # Should this warn, or actually try to cleanup?
262
 
        if self.transport:
263
 
            warn("MemoryLock %r not explicitly unlocked" % (self.path,))
264
 
            self.unlock()
265
 
 
266
290
    def unlock(self):
267
291
        del self.transport._locks[self.path]
268
292
        self.transport = None
269
293
 
270
294
 
271
 
class MemoryServer(Server):
 
295
class MemoryServer(transport.Server):
272
296
    """Server for the MemoryTransport for testing with."""
273
297
 
274
 
    def setUp(self):
275
 
        """See bzrlib.transport.Server.setUp."""
 
298
    def start_server(self):
276
299
        self._dirs = {'/':None}
277
300
        self._files = {}
278
301
        self._locks = {}
279
302
        self._scheme = "memory+%s:///" % id(self)
280
303
        def memory_factory(url):
281
 
            result = MemoryTransport(url)
 
304
            from bzrlib.transport import memory
 
305
            result = memory.MemoryTransport(url)
282
306
            result._dirs = self._dirs
283
307
            result._files = self._files
284
308
            result._locks = self._locks
285
309
            return result
286
 
        register_transport(self._scheme, memory_factory)
 
310
        self._memory_factory = memory_factory
 
311
        transport.register_transport(self._scheme, self._memory_factory)
287
312
 
288
 
    def tearDown(self):
289
 
        """See bzrlib.transport.Server.tearDown."""
 
313
    def stop_server(self):
290
314
        # unregister this server
 
315
        transport.unregister_transport(self._scheme, self._memory_factory)
291
316
 
292
317
    def get_url(self):
293
318
        """See bzrlib.transport.Server.get_url."""
294
319
        return self._scheme
295
320
 
 
321
    def get_bogus_url(self):
 
322
        raise NotImplementedError
 
323
 
296
324
 
297
325
def get_test_permutations():
298
326
    """Return the permutations to be used in testing."""