~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/transport/memory.py

  • Committer: Patch Queue Manager
  • Date: 2015-09-30 16:43:21 UTC
  • mfrom: (6603.2.2 fix-keep-dirty)
  • Revision ID: pqm@pqm.ubuntu.com-20150930164321-ct2v2qnmvimqt8qf
(vila) Avoid associating dirty patch headers with the previous file in the
 patch. (Colin Watson)

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005, 2006 Canonical Ltd
 
1
# Copyright (C) 2005-2010 Canonical Ltd
2
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
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
27
from stat import S_IFREG, S_IFDIR
28
28
from cStringIO import StringIO
29
 
import warnings
30
29
 
31
 
from bzrlib.errors import TransportError, NoSuchFile, FileExists, LockError
32
 
from bzrlib.trace import mutter
33
 
from bzrlib.transport import (Transport, register_transport, Server)
34
 
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
    )
35
45
 
36
46
 
37
47
 
49
59
            self.st_mode = S_IFDIR | perms
50
60
 
51
61
 
52
 
class MemoryTransport(Transport):
 
62
class MemoryTransport(transport.Transport):
53
63
    """This is an in memory file system for transient data storage."""
54
64
 
55
65
    def __init__(self, url=""):
59
69
        if url[-1] != '/':
60
70
            url = url + '/'
61
71
        super(MemoryTransport, self).__init__(url)
62
 
        self._cwd = url[url.find(':') + 3:]
 
72
        split = url.find(':') + 3
 
73
        self._scheme = url[:split]
 
74
        self._cwd = url[split:]
63
75
        # dictionaries from absolute path to file mode
64
76
        self._dirs = {'/':None}
65
77
        self._files = {}
67
79
 
68
80
    def clone(self, offset=None):
69
81
        """See Transport.clone()."""
70
 
        if offset is None or offset == '':
71
 
            return copy(self)
72
 
        segments = offset.split('/')
73
 
        cwdsegments = self._cwd.split('/')[:-1]
74
 
        while len(segments):
75
 
            segment = segments.pop(0)
76
 
            if segment == '.':
77
 
                continue
78
 
            if segment == '..':
79
 
                if len(cwdsegments) > 1:
80
 
                    cwdsegments.pop()
81
 
                continue
82
 
            cwdsegments.append(segment)
83
 
        url = self.base[:self.base.find(':') + 3] + '/'.join(cwdsegments) + '/'
84
 
        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)
85
87
        result._dirs = self._dirs
86
88
        result._files = self._files
87
89
        result._locks = self._locks
98
100
        else:
99
101
            return temp_t.base[:-1]
100
102
 
101
 
    def append(self, relpath, f, mode=None):
102
 
        """See Transport.append()."""
 
103
    def append_file(self, relpath, f, mode=None):
 
104
        """See Transport.append_file()."""
103
105
        _abspath = self._abspath(relpath)
104
106
        self._check_parent(_abspath)
105
107
        orig_content, orig_mode = self._files.get(_abspath, ("", None))
117
119
    def has(self, relpath):
118
120
        """See Transport.has()."""
119
121
        _abspath = self._abspath(relpath)
120
 
        return _abspath in self._files or _abspath in self._dirs
 
122
        return (_abspath in self._files) or (_abspath in self._dirs)
121
123
 
122
124
    def delete(self, relpath):
123
125
        """See Transport.delete()."""
126
128
            raise NoSuchFile(relpath)
127
129
        del self._files[_abspath]
128
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
 
129
137
    def get(self, relpath):
130
138
        """See Transport.get()."""
131
139
        _abspath = self._abspath(relpath)
132
140
        if not _abspath in self._files:
133
 
            raise NoSuchFile(relpath)
 
141
            if _abspath in self._dirs:
 
142
                return LateReadError(relpath)
 
143
            else:
 
144
                raise NoSuchFile(relpath)
134
145
        return StringIO(self._files[_abspath][0])
135
146
 
136
 
    def put(self, relpath, f, mode=None):
137
 
        """See Transport.put()."""
 
147
    def put_file(self, relpath, f, mode=None):
 
148
        """See Transport.put_file()."""
138
149
        _abspath = self._abspath(relpath)
139
150
        self._check_parent(_abspath)
140
 
        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)
141
160
 
142
161
    def mkdir(self, relpath, mode=None):
143
162
        """See Transport.mkdir()."""
147
166
            raise FileExists(relpath)
148
167
        self._dirs[_abspath]=mode
149
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
 
150
176
    def listable(self):
151
177
        """See Transport.listable."""
152
178
        return True
154
180
    def iter_files_recursive(self):
155
181
        for file in self._files:
156
182
            if file.startswith(self._cwd):
157
 
                yield file[len(self._cwd):]
158
 
    
 
183
                yield urlutils.escape(file[len(self._cwd):])
 
184
 
159
185
    def list_dir(self, relpath):
160
186
        """See Transport.list_dir()."""
161
187
        _abspath = self._abspath(relpath)
162
188
        if _abspath != '/' and _abspath not in self._dirs:
163
189
            raise NoSuchFile(relpath)
164
190
        result = []
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:])
176
 
        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)
177
202
 
178
203
    def rename(self, rel_from, rel_to):
179
204
        """Rename a file or directory; fail if the destination exists"""
195
220
                    del container[path]
196
221
        do_renames(self._files)
197
222
        do_renames(self._dirs)
198
 
    
 
223
 
199
224
    def rmdir(self, relpath):
200
225
        """See Transport.rmdir."""
201
226
        _abspath = self._abspath(relpath)
202
227
        if _abspath in self._files:
203
228
            self._translate_error(IOError(errno.ENOTDIR, relpath), relpath)
204
229
        for path in self._files:
205
 
            if path.startswith(_abspath):
 
230
            if path.startswith(_abspath + '/'):
206
231
                self._translate_error(IOError(errno.ENOTEMPTY, relpath),
207
232
                                      relpath)
208
233
        for path in self._dirs:
209
 
            if path.startswith(_abspath) and path != _abspath:
 
234
            if path.startswith(_abspath + '/') and path != _abspath:
210
235
                self._translate_error(IOError(errno.ENOTEMPTY, relpath), relpath)
211
236
        if not _abspath in self._dirs:
212
237
            raise NoSuchFile(relpath)
216
241
        """See Transport.stat()."""
217
242
        _abspath = self._abspath(relpath)
218
243
        if _abspath in self._files:
219
 
            return MemoryStat(len(self._files[_abspath][0]), False, 
 
244
            return MemoryStat(len(self._files[_abspath][0]), False,
220
245
                              self._files[_abspath][1])
221
246
        elif _abspath in self._dirs:
222
247
            return MemoryStat(0, True, self._dirs[_abspath])
234
259
    def _abspath(self, relpath):
235
260
        """Generate an internal absolute path."""
236
261
        relpath = urlutils.unescape(relpath)
237
 
        if relpath.find('..') != -1:
238
 
            raise AssertionError('relpath contains ..')
239
 
        if relpath == '.':
240
 
            if (self._cwd == '/'):
241
 
                return 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
 
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)
248
278
 
249
279
 
250
280
class _MemoryLock(object):
251
281
    """This makes a lock."""
252
282
 
253
283
    def __init__(self, path, transport):
254
 
        assert isinstance(transport, MemoryTransport)
255
284
        self.path = path
256
285
        self.transport = transport
257
286
        if self.path in self.transport._locks:
258
287
            raise LockError('File %r already locked' % (self.path,))
259
288
        self.transport._locks[self.path] = self
260
289
 
261
 
    def __del__(self):
262
 
        # Should this warn, or actually try to cleanup?
263
 
        if self.transport:
264
 
            warnings.warn("MemoryLock %r not explicitly unlocked" % (self.path,))
265
 
            self.unlock()
266
 
 
267
290
    def unlock(self):
268
291
        del self.transport._locks[self.path]
269
292
        self.transport = None
270
293
 
271
294
 
272
 
class MemoryServer(Server):
 
295
class MemoryServer(transport.Server):
273
296
    """Server for the MemoryTransport for testing with."""
274
297
 
275
 
    def setUp(self):
276
 
        """See bzrlib.transport.Server.setUp."""
 
298
    def start_server(self):
277
299
        self._dirs = {'/':None}
278
300
        self._files = {}
279
301
        self._locks = {}
280
302
        self._scheme = "memory+%s:///" % id(self)
281
303
        def memory_factory(url):
282
 
            result = MemoryTransport(url)
 
304
            from bzrlib.transport import memory
 
305
            result = memory.MemoryTransport(url)
283
306
            result._dirs = self._dirs
284
307
            result._files = self._files
285
308
            result._locks = self._locks
286
309
            return result
287
 
        register_transport(self._scheme, memory_factory)
 
310
        self._memory_factory = memory_factory
 
311
        transport.register_transport(self._scheme, self._memory_factory)
288
312
 
289
 
    def tearDown(self):
290
 
        """See bzrlib.transport.Server.tearDown."""
 
313
    def stop_server(self):
291
314
        # unregister this server
 
315
        transport.unregister_transport(self._scheme, self._memory_factory)
292
316
 
293
317
    def get_url(self):
294
318
        """See bzrlib.transport.Server.get_url."""
295
319
        return self._scheme
296
320
 
 
321
    def get_bogus_url(self):
 
322
        raise NotImplementedError
 
323
 
297
324
 
298
325
def get_test_permutations():
299
326
    """Return the permutations to be used in testing."""