/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/transport/memory.py

  • Committer: John Arbash Meinel
  • Date: 2006-04-25 15:05:42 UTC
  • mfrom: (1185.85.85 bzr-encoding)
  • mto: This revision was merged to the branch mainline in revision 1752.
  • Revision ID: john@arbash-meinel.com-20060425150542-c7b518dca9928691
[merge] the old bzr-encoding changes, reparenting them on bzr.dev

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005-2011, 2016 Canonical Ltd
2
 
#
 
1
# Copyright (C) 2005, 2006 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  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 __future__ import absolute_import
24
 
 
25
 
import contextlib
26
 
from io import (
27
 
    BytesIO,
28
 
    )
29
 
import itertools
 
23
from copy import copy
30
24
import os
31
25
import errno
32
 
from stat import S_IFREG, S_IFDIR, S_IFLNK, S_ISDIR
33
 
 
34
 
from .. import (
35
 
    transport,
36
 
    urlutils,
37
 
    )
38
 
from ..errors import (
39
 
    FileExists,
40
 
    LockError,
41
 
    InProcessTransport,
42
 
    NoSuchFile,
43
 
    TransportNotPossible,
44
 
    )
45
 
from ..transport import (
46
 
    AppendBasedFileStream,
47
 
    _file_streams,
48
 
    LateReadError,
49
 
    )
 
26
import re
 
27
from stat import *
 
28
from cStringIO import StringIO
 
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
                              urlescape, urlunescape)
 
34
 
50
35
 
51
36
 
52
37
class MemoryStat(object):
53
38
 
54
 
    def __init__(self, size, kind, perms=None):
 
39
    def __init__(self, size, is_dir, perms):
55
40
        self.st_size = size
56
 
        if not S_ISDIR(kind):
 
41
        if not is_dir:
57
42
            if perms is None:
58
 
                perms = 0o644
59
 
            self.st_mode = kind | perms
 
43
                perms = 0644
 
44
            self.st_mode = S_IFREG | perms
60
45
        else:
61
46
            if perms is None:
62
 
                perms = 0o755
63
 
            self.st_mode = kind | perms
64
 
 
65
 
 
66
 
class MemoryTransport(transport.Transport):
 
47
                perms = 0755
 
48
            self.st_mode = S_IFDIR | perms
 
49
 
 
50
 
 
51
class MemoryTransport(Transport):
67
52
    """This is an in memory file system for transient data storage."""
68
53
 
69
54
    def __init__(self, url=""):
70
55
        """Set the 'base' path where files will be stored."""
71
56
        if url == "":
72
 
            url = "memory:///"
 
57
            url = "memory:/"
73
58
        if url[-1] != '/':
74
59
            url = url + '/'
75
60
        super(MemoryTransport, self).__init__(url)
76
 
        split = url.find(':') + 3
77
 
        self._scheme = url[:split]
78
 
        self._cwd = url[split:]
 
61
        self._cwd = url[url.find(':') + 1:]
79
62
        # dictionaries from absolute path to file mode
80
 
        self._dirs = {'/': None}
81
 
        self._symlinks = {}
 
63
        self._dirs = {}
82
64
        self._files = {}
83
65
        self._locks = {}
84
66
 
85
67
    def clone(self, offset=None):
86
68
        """See Transport.clone()."""
87
 
        path = urlutils.URL._combine_paths(self._cwd, offset)
88
 
        if len(path) == 0 or path[-1] != '/':
89
 
            path += '/'
90
 
        url = self._scheme + path
91
 
        result = self.__class__(url)
 
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(':') + 1] + '/'.join(cwdsegments) + '/'
 
83
        result = MemoryTransport(url)
92
84
        result._dirs = self._dirs
93
 
        result._symlinks = self._symlinks
94
85
        result._files = self._files
95
86
        result._locks = self._locks
96
87
        return result
101
92
        # current environment - XXX RBC 20060404 move the clone '..' handling
102
93
        # into here and call abspath from clone
103
94
        temp_t = self.clone(relpath)
104
 
        if temp_t.base.count('/') == 3:
 
95
        if temp_t.base.count('/') == 1:
105
96
            return temp_t.base
106
97
        else:
107
98
            return temp_t.base[:-1]
108
99
 
109
 
    def append_file(self, relpath, f, mode=None):
110
 
        """See Transport.append_file()."""
111
 
        _abspath = self._resolve_symlinks(relpath)
 
100
    def append(self, relpath, f, mode=None):
 
101
        """See Transport.append()."""
 
102
        _abspath = self._abspath(relpath)
112
103
        self._check_parent(_abspath)
113
 
        orig_content, orig_mode = self._files.get(_abspath, (b"", None))
 
104
        orig_content, orig_mode = self._files.get(_abspath, ("", None))
114
105
        if mode is None:
115
106
            mode = orig_mode
116
107
        self._files[_abspath] = (orig_content + f.read(), mode)
119
110
    def _check_parent(self, _abspath):
120
111
        dir = os.path.dirname(_abspath)
121
112
        if dir != '/':
122
 
            if dir not in self._dirs:
 
113
            if not dir in self._dirs:
123
114
                raise NoSuchFile(_abspath)
124
115
 
125
116
    def has(self, relpath):
126
117
        """See Transport.has()."""
127
118
        _abspath = self._abspath(relpath)
128
 
        for container in (self._files, self._dirs, self._symlinks):
129
 
            if _abspath in container.keys():
130
 
                return True
131
 
        return False
 
119
        return _abspath in self._files or _abspath in self._dirs
132
120
 
133
121
    def delete(self, relpath):
134
122
        """See Transport.delete()."""
135
123
        _abspath = self._abspath(relpath)
136
 
        if _abspath in self._files:
137
 
            del self._files[_abspath]
138
 
        elif _abspath in self._symlinks:
139
 
            del self._symlinks[_abspath]
140
 
        else:
 
124
        if not _abspath in self._files:
141
125
            raise NoSuchFile(relpath)
142
 
 
143
 
    def external_url(self):
144
 
        """See breezy.transport.Transport.external_url."""
145
 
        # MemoryTransport's are only accessible in-process
146
 
        # so we raise here
147
 
        raise InProcessTransport(self)
 
126
        del self._files[_abspath]
148
127
 
149
128
    def get(self, relpath):
150
129
        """See Transport.get()."""
151
 
        _abspath = self._resolve_symlinks(relpath)
152
 
        if _abspath not in self._files:
153
 
            if _abspath in self._dirs:
154
 
                return LateReadError(relpath)
155
 
            else:
156
 
                raise NoSuchFile(relpath)
157
 
        return BytesIO(self._files[_abspath][0])
158
 
 
159
 
    def put_file(self, relpath, f, mode=None):
160
 
        """See Transport.put_file()."""
161
 
        _abspath = self._resolve_symlinks(relpath)
162
 
        self._check_parent(_abspath)
163
 
        raw_bytes = f.read()
164
 
        self._files[_abspath] = (raw_bytes, mode)
165
 
        return len(raw_bytes)
166
 
 
167
 
    def symlink(self, source, target):
168
 
        _abspath = self._resolve_symlinks(target)
169
 
        self._check_parent(_abspath)
170
 
        self._symlinks[_abspath] = self._abspath(source)
 
130
        _abspath = self._abspath(relpath)
 
131
        if not _abspath in self._files:
 
132
            raise NoSuchFile(relpath)
 
133
        return StringIO(self._files[_abspath][0])
 
134
 
 
135
    def put(self, relpath, f, mode=None):
 
136
        """See Transport.put()."""
 
137
        _abspath = self._abspath(relpath)
 
138
        self._check_parent(_abspath)
 
139
        self._files[_abspath] = (f.read(), mode)
171
140
 
172
141
    def mkdir(self, relpath, mode=None):
173
142
        """See Transport.mkdir()."""
174
 
        _abspath = self._resolve_symlinks(relpath)
 
143
        _abspath = self._abspath(relpath)
175
144
        self._check_parent(_abspath)
176
145
        if _abspath in self._dirs:
177
146
            raise FileExists(relpath)
178
 
        self._dirs[_abspath] = mode
179
 
 
180
 
    def open_write_stream(self, relpath, mode=None):
181
 
        """See Transport.open_write_stream."""
182
 
        self.put_bytes(relpath, b"", mode)
183
 
        result = AppendBasedFileStream(self, relpath)
184
 
        _file_streams[self.abspath(relpath)] = result
185
 
        return result
 
147
        self._dirs[_abspath]=mode
186
148
 
187
149
    def listable(self):
188
150
        """See Transport.listable."""
189
151
        return True
190
152
 
191
153
    def iter_files_recursive(self):
192
 
        for file in itertools.chain(self._files, self._symlinks):
 
154
        for file in self._files:
193
155
            if file.startswith(self._cwd):
194
 
                yield urlutils.escape(file[len(self._cwd):])
195
 
 
 
156
                yield file[len(self._cwd):]
 
157
    
196
158
    def list_dir(self, relpath):
197
159
        """See Transport.list_dir()."""
198
 
        _abspath = self._resolve_symlinks(relpath)
 
160
        _abspath = self._abspath(relpath)
199
161
        if _abspath != '/' and _abspath not in self._dirs:
200
162
            raise NoSuchFile(relpath)
201
163
        result = []
202
 
 
203
 
        if not _abspath.endswith('/'):
204
 
            _abspath += '/'
205
 
 
206
 
        for path_group in self._files, self._dirs, self._symlinks:
207
 
            for path in path_group:
208
 
                if path.startswith(_abspath):
209
 
                    trailing = path[len(_abspath):]
210
 
                    if trailing and '/' not in trailing:
211
 
                        result.append(urlutils.escape(trailing))
 
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:])
212
175
        return result
213
176
 
214
177
    def rename(self, rel_from, rel_to):
215
178
        """Rename a file or directory; fail if the destination exists"""
216
 
        abs_from = self._resolve_symlinks(rel_from)
217
 
        abs_to = self._resolve_symlinks(rel_to)
218
 
 
 
179
        abs_from = self._abspath(rel_from)
 
180
        abs_to = self._abspath(rel_to)
219
181
        def replace(x):
220
182
            if x == abs_from:
221
183
                x = abs_to
222
184
            elif x.startswith(abs_from + '/'):
223
185
                x = abs_to + x[len(abs_from):]
224
186
            return x
225
 
 
226
187
        def do_renames(container):
227
 
            renames = []
228
188
            for path in container:
229
189
                new_path = replace(path)
230
190
                if new_path != path:
231
191
                    if new_path in container:
232
192
                        raise FileExists(new_path)
233
 
                    renames.append((path, new_path))
234
 
            for path, new_path in renames:
235
 
                container[new_path] = container[path]
236
 
                del container[path]
237
 
 
238
 
        # If we modify the existing dicts, we're not atomic anymore and may
239
 
        # fail differently depending on dict order. So work on copy, fail on
240
 
        # error on only replace dicts if all goes well.
241
 
        renamed_files = self._files.copy()
242
 
        renamed_symlinks = self._symlinks.copy()
243
 
        renamed_dirs = self._dirs.copy()
244
 
        do_renames(renamed_files)
245
 
        do_renames(renamed_symlinks)
246
 
        do_renames(renamed_dirs)
247
 
        # We may have been cloned so modify in place
248
 
        self._files.clear()
249
 
        self._files.update(renamed_files)
250
 
        self._symlinks.clear()
251
 
        self._symlinks.update(renamed_symlinks)
252
 
        self._dirs.clear()
253
 
        self._dirs.update(renamed_dirs)
254
 
 
 
193
                    container[new_path] = container[path]
 
194
                    del container[path]
 
195
        do_renames(self._files)
 
196
        do_renames(self._dirs)
 
197
    
255
198
    def rmdir(self, relpath):
256
199
        """See Transport.rmdir."""
257
 
        _abspath = self._resolve_symlinks(relpath)
 
200
        _abspath = self._abspath(relpath)
258
201
        if _abspath in self._files:
259
202
            self._translate_error(IOError(errno.ENOTDIR, relpath), relpath)
260
 
        for path in itertools.chain(self._files, self._symlinks):
261
 
            if path.startswith(_abspath + '/'):
 
203
        for path in self._files:
 
204
            if path.startswith(_abspath):
262
205
                self._translate_error(IOError(errno.ENOTEMPTY, relpath),
263
206
                                      relpath)
264
207
        for path in self._dirs:
265
 
            if path.startswith(_abspath + '/') and path != _abspath:
266
 
                self._translate_error(
267
 
                    IOError(errno.ENOTEMPTY, relpath), relpath)
268
 
        if _abspath not in self._dirs:
 
208
            if path.startswith(_abspath) and path != _abspath:
 
209
                self._translate_error(IOError(errno.ENOTEMPTY, relpath), relpath)
 
210
        if not _abspath in self._dirs:
269
211
            raise NoSuchFile(relpath)
270
212
        del self._dirs[_abspath]
271
213
 
272
214
    def stat(self, relpath):
273
215
        """See Transport.stat()."""
274
216
        _abspath = self._abspath(relpath)
275
 
        if _abspath in self._files.keys():
276
 
            return MemoryStat(len(self._files[_abspath][0]), S_IFREG,
 
217
        if _abspath in self._files:
 
218
            return MemoryStat(len(self._files[_abspath][0]), False, 
277
219
                              self._files[_abspath][1])
278
 
        elif _abspath in self._dirs.keys():
279
 
            return MemoryStat(0, S_IFDIR, self._dirs[_abspath])
280
 
        elif _abspath in self._symlinks.keys():
281
 
            return MemoryStat(0, S_IFLNK)
 
220
        elif _abspath == '':
 
221
            return MemoryStat(0, True, None)
 
222
        elif _abspath in self._dirs:
 
223
            return MemoryStat(0, True, self._dirs[_abspath])
282
224
        else:
283
225
            raise NoSuchFile(_abspath)
284
226
 
290
232
        """See Transport.lock_write()."""
291
233
        return _MemoryLock(self._abspath(relpath), self)
292
234
 
293
 
    def _resolve_symlinks(self, relpath):
294
 
        path = self._abspath(relpath)
295
 
        while path in self._symlinks.keys():
296
 
            path = self._symlinks[path]
297
 
        return path
298
 
 
299
235
    def _abspath(self, relpath):
300
236
        """Generate an internal absolute path."""
301
 
        relpath = urlutils.unescape(relpath)
302
 
        if relpath[:1] == '/':
303
 
            return relpath
304
 
        cwd_parts = self._cwd.split('/')
305
 
        rel_parts = relpath.split('/')
306
 
        r = []
307
 
        for i in cwd_parts + rel_parts:
308
 
            if i == '..':
309
 
                if not r:
310
 
                    raise ValueError("illegal relpath %r under %r"
311
 
                                     % (relpath, self._cwd))
312
 
                r = r[:-1]
313
 
            elif i == '.' or i == '':
314
 
                pass
315
 
            else:
316
 
                r.append(i)
317
 
                r = self._symlinks.get('/'.join(r), r)
318
 
        return '/' + '/'.join(r)
319
 
 
320
 
    def symlink(self, source, link_name):
321
 
        """Create a symlink pointing to source named link_name."""
322
 
        _abspath = self._abspath(link_name)
323
 
        self._check_parent(_abspath)
324
 
        self._symlinks[_abspath] = source.split('/')
325
 
 
326
 
    def readlink(self, link_name):
327
 
        _abspath = self._abspath(link_name)
328
 
        try:
329
 
            return '/'.join(self._symlinks[_abspath])
330
 
        except KeyError:
331
 
            raise NoSuchFile(link_name)
 
237
        relpath = urlunescape(relpath)
 
238
        if relpath.find('..') != -1:
 
239
            raise AssertionError('relpath contains ..')
 
240
        if relpath == '.':
 
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
332
247
 
333
248
 
334
249
class _MemoryLock(object):
335
250
    """This makes a lock."""
336
251
 
337
252
    def __init__(self, path, transport):
 
253
        assert isinstance(transport, MemoryTransport)
338
254
        self.path = path
339
255
        self.transport = transport
340
256
        if self.path in self.transport._locks:
341
257
            raise LockError('File %r already locked' % (self.path,))
342
258
        self.transport._locks[self.path] = self
343
259
 
 
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
 
344
266
    def unlock(self):
345
267
        del self.transport._locks[self.path]
346
268
        self.transport = None
347
269
 
348
270
 
349
 
class MemoryServer(transport.Server):
 
271
class MemoryServer(Server):
350
272
    """Server for the MemoryTransport for testing with."""
351
273
 
352
 
    def start_server(self):
353
 
        self._dirs = {'/': None}
 
274
    def setUp(self):
 
275
        """See bzrlib.transport.Server.setUp."""
 
276
        self._dirs = {}
354
277
        self._files = {}
355
 
        self._symlinks = {}
356
278
        self._locks = {}
357
 
        self._scheme = "memory+%s:///" % id(self)
358
 
 
 
279
        self._scheme = "memory+%s:" % id(self)
359
280
        def memory_factory(url):
360
 
            from . import memory
361
 
            result = memory.MemoryTransport(url)
 
281
            result = MemoryTransport(url)
362
282
            result._dirs = self._dirs
363
283
            result._files = self._files
364
 
            result._symlinks = self._symlinks
365
284
            result._locks = self._locks
366
285
            return result
367
 
        self._memory_factory = memory_factory
368
 
        transport.register_transport(self._scheme, self._memory_factory)
 
286
        register_transport(self._scheme, memory_factory)
369
287
 
370
 
    def stop_server(self):
 
288
    def tearDown(self):
 
289
        """See bzrlib.transport.Server.tearDown."""
371
290
        # unregister this server
372
 
        transport.unregister_transport(self._scheme, self._memory_factory)
373
291
 
374
292
    def get_url(self):
375
 
        """See breezy.transport.Server.get_url."""
 
293
        """See bzrlib.transport.Server.get_url."""
376
294
        return self._scheme
377
295
 
378
 
    def get_bogus_url(self):
379
 
        raise NotImplementedError
380
 
 
381
296
 
382
297
def get_test_permutations():
383
298
    """Return the permutations to be used in testing."""