/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: Alexander Belchenko
  • Date: 2006-06-29 08:41:31 UTC
  • mto: (1860.1.1 win32.installer)
  • mto: This revision was merged to the branch mainline in revision 1906.
  • Revision ID: bialix@ukr.net-20060629084131-3ea4d44e3204e36f
win32 installer for bzr.dev.0.9

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 S_IFREG, S_IFDIR
 
28
from cStringIO import StringIO
 
29
import warnings
 
30
 
 
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
 
35
 
50
36
 
51
37
 
52
38
class MemoryStat(object):
53
39
 
54
 
    def __init__(self, size, kind, perms=None):
 
40
    def __init__(self, size, is_dir, perms):
55
41
        self.st_size = size
56
 
        if not S_ISDIR(kind):
 
42
        if not is_dir:
57
43
            if perms is None:
58
 
                perms = 0o644
59
 
            self.st_mode = kind | perms
 
44
                perms = 0644
 
45
            self.st_mode = S_IFREG | perms
60
46
        else:
61
47
            if perms is None:
62
 
                perms = 0o755
63
 
            self.st_mode = kind | perms
64
 
 
65
 
 
66
 
class MemoryTransport(transport.Transport):
 
48
                perms = 0755
 
49
            self.st_mode = S_IFDIR | perms
 
50
 
 
51
 
 
52
class MemoryTransport(Transport):
67
53
    """This is an in memory file system for transient data storage."""
68
54
 
69
55
    def __init__(self, url=""):
73
59
        if url[-1] != '/':
74
60
            url = url + '/'
75
61
        super(MemoryTransport, self).__init__(url)
76
 
        split = url.find(':') + 3
77
 
        self._scheme = url[:split]
78
 
        self._cwd = url[split:]
 
62
        self._cwd = url[url.find(':') + 3:]
79
63
        # dictionaries from absolute path to file mode
80
 
        self._dirs = {'/': None}
81
 
        self._symlinks = {}
 
64
        self._dirs = {'/':None}
82
65
        self._files = {}
83
66
        self._locks = {}
84
67
 
85
68
    def clone(self, offset=None):
86
69
        """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)
 
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)
92
85
        result._dirs = self._dirs
93
 
        result._symlinks = self._symlinks
94
86
        result._files = self._files
95
87
        result._locks = self._locks
96
88
        return result
106
98
        else:
107
99
            return temp_t.base[:-1]
108
100
 
109
 
    def append_file(self, relpath, f, mode=None):
110
 
        """See Transport.append_file()."""
111
 
        _abspath = self._resolve_symlinks(relpath)
 
101
    def append(self, relpath, f, mode=None):
 
102
        """See Transport.append()."""
 
103
        _abspath = self._abspath(relpath)
112
104
        self._check_parent(_abspath)
113
 
        orig_content, orig_mode = self._files.get(_abspath, (b"", None))
 
105
        orig_content, orig_mode = self._files.get(_abspath, ("", None))
114
106
        if mode is None:
115
107
            mode = orig_mode
116
108
        self._files[_abspath] = (orig_content + f.read(), mode)
119
111
    def _check_parent(self, _abspath):
120
112
        dir = os.path.dirname(_abspath)
121
113
        if dir != '/':
122
 
            if dir not in self._dirs:
 
114
            if not dir in self._dirs:
123
115
                raise NoSuchFile(_abspath)
124
116
 
125
117
    def has(self, relpath):
126
118
        """See Transport.has()."""
127
119
        _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
 
120
        return _abspath in self._files or _abspath in self._dirs
132
121
 
133
122
    def delete(self, relpath):
134
123
        """See Transport.delete()."""
135
124
        _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:
 
125
        if not _abspath in self._files:
141
126
            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)
 
127
        del self._files[_abspath]
148
128
 
149
129
    def get(self, relpath):
150
130
        """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)
 
131
        _abspath = self._abspath(relpath)
 
132
        if not _abspath in self._files:
 
133
            raise NoSuchFile(relpath)
 
134
        return StringIO(self._files[_abspath][0])
 
135
 
 
136
    def put(self, relpath, f, mode=None):
 
137
        """See Transport.put()."""
 
138
        _abspath = self._abspath(relpath)
 
139
        self._check_parent(_abspath)
 
140
        self._files[_abspath] = (f.read(), mode)
171
141
 
172
142
    def mkdir(self, relpath, mode=None):
173
143
        """See Transport.mkdir()."""
174
 
        _abspath = self._resolve_symlinks(relpath)
 
144
        _abspath = self._abspath(relpath)
175
145
        self._check_parent(_abspath)
176
146
        if _abspath in self._dirs:
177
147
            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
 
148
        self._dirs[_abspath]=mode
186
149
 
187
150
    def listable(self):
188
151
        """See Transport.listable."""
189
152
        return True
190
153
 
191
154
    def iter_files_recursive(self):
192
 
        for file in itertools.chain(self._files, self._symlinks):
 
155
        for file in self._files:
193
156
            if file.startswith(self._cwd):
194
 
                yield urlutils.escape(file[len(self._cwd):])
195
 
 
 
157
                yield file[len(self._cwd):]
 
158
    
196
159
    def list_dir(self, relpath):
197
160
        """See Transport.list_dir()."""
198
 
        _abspath = self._resolve_symlinks(relpath)
 
161
        _abspath = self._abspath(relpath)
199
162
        if _abspath != '/' and _abspath not in self._dirs:
200
163
            raise NoSuchFile(relpath)
201
164
        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))
 
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:])
212
176
        return result
213
177
 
214
178
    def rename(self, rel_from, rel_to):
215
179
        """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
 
 
 
180
        abs_from = self._abspath(rel_from)
 
181
        abs_to = self._abspath(rel_to)
219
182
        def replace(x):
220
183
            if x == abs_from:
221
184
                x = abs_to
222
185
            elif x.startswith(abs_from + '/'):
223
186
                x = abs_to + x[len(abs_from):]
224
187
            return x
225
 
 
226
188
        def do_renames(container):
227
 
            renames = []
228
189
            for path in container:
229
190
                new_path = replace(path)
230
191
                if new_path != path:
231
192
                    if new_path in container:
232
193
                        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
 
 
 
194
                    container[new_path] = container[path]
 
195
                    del container[path]
 
196
        do_renames(self._files)
 
197
        do_renames(self._dirs)
 
198
    
255
199
    def rmdir(self, relpath):
256
200
        """See Transport.rmdir."""
257
 
        _abspath = self._resolve_symlinks(relpath)
 
201
        _abspath = self._abspath(relpath)
258
202
        if _abspath in self._files:
259
203
            self._translate_error(IOError(errno.ENOTDIR, relpath), relpath)
260
 
        for path in itertools.chain(self._files, self._symlinks):
261
 
            if path.startswith(_abspath + '/'):
 
204
        for path in self._files:
 
205
            if path.startswith(_abspath):
262
206
                self._translate_error(IOError(errno.ENOTEMPTY, relpath),
263
207
                                      relpath)
264
208
        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:
 
209
            if path.startswith(_abspath) and path != _abspath:
 
210
                self._translate_error(IOError(errno.ENOTEMPTY, relpath), relpath)
 
211
        if not _abspath in self._dirs:
269
212
            raise NoSuchFile(relpath)
270
213
        del self._dirs[_abspath]
271
214
 
272
215
    def stat(self, relpath):
273
216
        """See Transport.stat()."""
274
217
        _abspath = self._abspath(relpath)
275
 
        if _abspath in self._files.keys():
276
 
            return MemoryStat(len(self._files[_abspath][0]), S_IFREG,
 
218
        if _abspath in self._files:
 
219
            return MemoryStat(len(self._files[_abspath][0]), False, 
277
220
                              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)
 
221
        elif _abspath in self._dirs:
 
222
            return MemoryStat(0, True, self._dirs[_abspath])
282
223
        else:
283
224
            raise NoSuchFile(_abspath)
284
225
 
290
231
        """See Transport.lock_write()."""
291
232
        return _MemoryLock(self._abspath(relpath), self)
292
233
 
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
234
    def _abspath(self, relpath):
300
235
        """Generate an internal absolute path."""
301
236
        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
        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
332
248
 
333
249
 
334
250
class _MemoryLock(object):
335
251
    """This makes a lock."""
336
252
 
337
253
    def __init__(self, path, transport):
 
254
        assert isinstance(transport, MemoryTransport)
338
255
        self.path = path
339
256
        self.transport = transport
340
257
        if self.path in self.transport._locks:
341
258
            raise LockError('File %r already locked' % (self.path,))
342
259
        self.transport._locks[self.path] = self
343
260
 
 
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
 
344
267
    def unlock(self):
345
268
        del self.transport._locks[self.path]
346
269
        self.transport = None
347
270
 
348
271
 
349
 
class MemoryServer(transport.Server):
 
272
class MemoryServer(Server):
350
273
    """Server for the MemoryTransport for testing with."""
351
274
 
352
 
    def start_server(self):
353
 
        self._dirs = {'/': None}
 
275
    def setUp(self):
 
276
        """See bzrlib.transport.Server.setUp."""
 
277
        self._dirs = {'/':None}
354
278
        self._files = {}
355
 
        self._symlinks = {}
356
279
        self._locks = {}
357
280
        self._scheme = "memory+%s:///" % id(self)
358
 
 
359
281
        def memory_factory(url):
360
 
            from . import memory
361
 
            result = memory.MemoryTransport(url)
 
282
            result = MemoryTransport(url)
362
283
            result._dirs = self._dirs
363
284
            result._files = self._files
364
 
            result._symlinks = self._symlinks
365
285
            result._locks = self._locks
366
286
            return result
367
 
        self._memory_factory = memory_factory
368
 
        transport.register_transport(self._scheme, self._memory_factory)
 
287
        register_transport(self._scheme, memory_factory)
369
288
 
370
 
    def stop_server(self):
 
289
    def tearDown(self):
 
290
        """See bzrlib.transport.Server.tearDown."""
371
291
        # unregister this server
372
 
        transport.unregister_transport(self._scheme, self._memory_factory)
373
292
 
374
293
    def get_url(self):
375
 
        """See breezy.transport.Server.get_url."""
 
294
        """See bzrlib.transport.Server.get_url."""
376
295
        return self._scheme
377
296
 
378
 
    def get_bogus_url(self):
379
 
        raise NotImplementedError
380
 
 
381
297
 
382
298
def get_test_permutations():
383
299
    """Return the permutations to be used in testing."""