/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: Marius Kruger
  • Date: 2010-07-10 21:28:56 UTC
  • mto: (5384.1.1 integration)
  • mto: This revision was merged to the branch mainline in revision 5385.
  • Revision ID: marius.kruger@enerweb.co.za-20100710212856-uq4ji3go0u5se7hx
* Update documentation
* add NEWS

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005-2011, 2016 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
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
30
23
import os
31
24
import errno
32
 
from stat import S_IFREG, S_IFDIR, S_IFLNK, S_ISDIR
 
25
import re
 
26
from stat import S_IFREG, S_IFDIR
 
27
from cStringIO import StringIO
 
28
import warnings
33
29
 
34
 
from .. import (
 
30
from bzrlib import (
35
31
    transport,
36
32
    urlutils,
37
33
    )
38
 
from ..errors import (
 
34
from bzrlib.errors import (
39
35
    FileExists,
40
36
    LockError,
41
37
    InProcessTransport,
42
38
    NoSuchFile,
43
 
    TransportNotPossible,
 
39
    TransportError,
44
40
    )
45
 
from ..transport import (
 
41
from bzrlib.trace import mutter
 
42
from bzrlib.transport import (
46
43
    AppendBasedFileStream,
47
44
    _file_streams,
48
45
    LateReadError,
49
46
    )
50
47
 
51
48
 
 
49
 
52
50
class MemoryStat(object):
53
51
 
54
 
    def __init__(self, size, kind, perms=None):
 
52
    def __init__(self, size, is_dir, perms):
55
53
        self.st_size = size
56
 
        if not S_ISDIR(kind):
 
54
        if not is_dir:
57
55
            if perms is None:
58
 
                perms = 0o644
59
 
            self.st_mode = kind | perms
 
56
                perms = 0644
 
57
            self.st_mode = S_IFREG | perms
60
58
        else:
61
59
            if perms is None:
62
 
                perms = 0o755
63
 
            self.st_mode = kind | perms
 
60
                perms = 0755
 
61
            self.st_mode = S_IFDIR | perms
64
62
 
65
63
 
66
64
class MemoryTransport(transport.Transport):
77
75
        self._scheme = url[:split]
78
76
        self._cwd = url[split:]
79
77
        # dictionaries from absolute path to file mode
80
 
        self._dirs = {'/': None}
81
 
        self._symlinks = {}
 
78
        self._dirs = {'/':None}
82
79
        self._files = {}
83
80
        self._locks = {}
84
81
 
85
82
    def clone(self, offset=None):
86
83
        """See Transport.clone()."""
87
 
        path = urlutils.URL._combine_paths(self._cwd, offset)
 
84
        path = self._combine_paths(self._cwd, offset)
88
85
        if len(path) == 0 or path[-1] != '/':
89
86
            path += '/'
90
87
        url = self._scheme + path
91
88
        result = self.__class__(url)
92
89
        result._dirs = self._dirs
93
 
        result._symlinks = self._symlinks
94
90
        result._files = self._files
95
91
        result._locks = self._locks
96
92
        return result
108
104
 
109
105
    def append_file(self, relpath, f, mode=None):
110
106
        """See Transport.append_file()."""
111
 
        _abspath = self._resolve_symlinks(relpath)
 
107
        _abspath = self._abspath(relpath)
112
108
        self._check_parent(_abspath)
113
 
        orig_content, orig_mode = self._files.get(_abspath, (b"", None))
 
109
        orig_content, orig_mode = self._files.get(_abspath, ("", None))
114
110
        if mode is None:
115
111
            mode = orig_mode
116
112
        self._files[_abspath] = (orig_content + f.read(), mode)
119
115
    def _check_parent(self, _abspath):
120
116
        dir = os.path.dirname(_abspath)
121
117
        if dir != '/':
122
 
            if dir not in self._dirs:
 
118
            if not dir in self._dirs:
123
119
                raise NoSuchFile(_abspath)
124
120
 
125
121
    def has(self, relpath):
126
122
        """See Transport.has()."""
127
123
        _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
 
124
        return (_abspath in self._files) or (_abspath in self._dirs)
132
125
 
133
126
    def delete(self, relpath):
134
127
        """See Transport.delete()."""
135
128
        _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:
 
129
        if not _abspath in self._files:
141
130
            raise NoSuchFile(relpath)
 
131
        del self._files[_abspath]
142
132
 
143
133
    def external_url(self):
144
 
        """See breezy.transport.Transport.external_url."""
 
134
        """See bzrlib.transport.Transport.external_url."""
145
135
        # MemoryTransport's are only accessible in-process
146
136
        # so we raise here
147
137
        raise InProcessTransport(self)
148
138
 
149
139
    def get(self, relpath):
150
140
        """See Transport.get()."""
151
 
        _abspath = self._resolve_symlinks(relpath)
152
 
        if _abspath not in self._files:
 
141
        _abspath = self._abspath(relpath)
 
142
        if not _abspath in self._files:
153
143
            if _abspath in self._dirs:
154
144
                return LateReadError(relpath)
155
145
            else:
156
146
                raise NoSuchFile(relpath)
157
 
        return BytesIO(self._files[_abspath][0])
 
147
        return StringIO(self._files[_abspath][0])
158
148
 
159
149
    def put_file(self, relpath, f, mode=None):
160
150
        """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)
 
151
        _abspath = self._abspath(relpath)
 
152
        self._check_parent(_abspath)
 
153
        bytes = f.read()
 
154
        if type(bytes) is not str:
 
155
            # Although not strictly correct, we raise UnicodeEncodeError to be
 
156
            # compatible with other transports.
 
157
            raise UnicodeEncodeError(
 
158
                'undefined', bytes, 0, 1,
 
159
                'put_file must be given a file of bytes, not unicode.')
 
160
        self._files[_abspath] = (bytes, mode)
 
161
        return len(bytes)
171
162
 
172
163
    def mkdir(self, relpath, mode=None):
173
164
        """See Transport.mkdir()."""
174
 
        _abspath = self._resolve_symlinks(relpath)
 
165
        _abspath = self._abspath(relpath)
175
166
        self._check_parent(_abspath)
176
167
        if _abspath in self._dirs:
177
168
            raise FileExists(relpath)
178
 
        self._dirs[_abspath] = mode
 
169
        self._dirs[_abspath]=mode
179
170
 
180
171
    def open_write_stream(self, relpath, mode=None):
181
172
        """See Transport.open_write_stream."""
182
 
        self.put_bytes(relpath, b"", mode)
 
173
        self.put_bytes(relpath, "", mode)
183
174
        result = AppendBasedFileStream(self, relpath)
184
175
        _file_streams[self.abspath(relpath)] = result
185
176
        return result
189
180
        return True
190
181
 
191
182
    def iter_files_recursive(self):
192
 
        for file in itertools.chain(self._files, self._symlinks):
 
183
        for file in self._files:
193
184
            if file.startswith(self._cwd):
194
185
                yield urlutils.escape(file[len(self._cwd):])
195
186
 
196
187
    def list_dir(self, relpath):
197
188
        """See Transport.list_dir()."""
198
 
        _abspath = self._resolve_symlinks(relpath)
 
189
        _abspath = self._abspath(relpath)
199
190
        if _abspath != '/' and _abspath not in self._dirs:
200
191
            raise NoSuchFile(relpath)
201
192
        result = []
203
194
        if not _abspath.endswith('/'):
204
195
            _abspath += '/'
205
196
 
206
 
        for path_group in self._files, self._dirs, self._symlinks:
 
197
        for path_group in self._files, self._dirs:
207
198
            for path in path_group:
208
199
                if path.startswith(_abspath):
209
200
                    trailing = path[len(_abspath):]
210
201
                    if trailing and '/' not in trailing:
211
 
                        result.append(urlutils.escape(trailing))
212
 
        return result
 
202
                        result.append(trailing)
 
203
        return map(urlutils.escape, result)
213
204
 
214
205
    def rename(self, rel_from, rel_to):
215
206
        """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
 
 
 
207
        abs_from = self._abspath(rel_from)
 
208
        abs_to = self._abspath(rel_to)
219
209
        def replace(x):
220
210
            if x == abs_from:
221
211
                x = abs_to
222
212
            elif x.startswith(abs_from + '/'):
223
213
                x = abs_to + x[len(abs_from):]
224
214
            return x
225
 
 
226
215
        def do_renames(container):
227
 
            renames = []
228
216
            for path in container:
229
217
                new_path = replace(path)
230
218
                if new_path != path:
231
219
                    if new_path in container:
232
220
                        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)
 
221
                    container[new_path] = container[path]
 
222
                    del container[path]
 
223
        do_renames(self._files)
 
224
        do_renames(self._dirs)
254
225
 
255
226
    def rmdir(self, relpath):
256
227
        """See Transport.rmdir."""
257
 
        _abspath = self._resolve_symlinks(relpath)
 
228
        _abspath = self._abspath(relpath)
258
229
        if _abspath in self._files:
259
230
            self._translate_error(IOError(errno.ENOTDIR, relpath), relpath)
260
 
        for path in itertools.chain(self._files, self._symlinks):
 
231
        for path in self._files:
261
232
            if path.startswith(_abspath + '/'):
262
233
                self._translate_error(IOError(errno.ENOTEMPTY, relpath),
263
234
                                      relpath)
264
235
        for path in self._dirs:
265
236
            if path.startswith(_abspath + '/') and path != _abspath:
266
 
                self._translate_error(
267
 
                    IOError(errno.ENOTEMPTY, relpath), relpath)
268
 
        if _abspath not in self._dirs:
 
237
                self._translate_error(IOError(errno.ENOTEMPTY, relpath), relpath)
 
238
        if not _abspath in self._dirs:
269
239
            raise NoSuchFile(relpath)
270
240
        del self._dirs[_abspath]
271
241
 
272
242
    def stat(self, relpath):
273
243
        """See Transport.stat()."""
274
244
        _abspath = self._abspath(relpath)
275
 
        if _abspath in self._files.keys():
276
 
            return MemoryStat(len(self._files[_abspath][0]), S_IFREG,
 
245
        if _abspath in self._files:
 
246
            return MemoryStat(len(self._files[_abspath][0]), False,
277
247
                              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)
 
248
        elif _abspath in self._dirs:
 
249
            return MemoryStat(0, True, self._dirs[_abspath])
282
250
        else:
283
251
            raise NoSuchFile(_abspath)
284
252
 
290
258
        """See Transport.lock_write()."""
291
259
        return _MemoryLock(self._abspath(relpath), self)
292
260
 
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
261
    def _abspath(self, relpath):
300
262
        """Generate an internal absolute path."""
301
263
        relpath = urlutils.unescape(relpath)
308
270
            if i == '..':
309
271
                if not r:
310
272
                    raise ValueError("illegal relpath %r under %r"
311
 
                                     % (relpath, self._cwd))
 
273
                        % (relpath, self._cwd))
312
274
                r = r[:-1]
313
275
            elif i == '.' or i == '':
314
276
                pass
315
277
            else:
316
278
                r.append(i)
317
 
                r = self._symlinks.get('/'.join(r), r)
318
279
        return '/' + '/'.join(r)
319
280
 
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)
332
 
 
333
281
 
334
282
class _MemoryLock(object):
335
283
    """This makes a lock."""
341
289
            raise LockError('File %r already locked' % (self.path,))
342
290
        self.transport._locks[self.path] = self
343
291
 
 
292
    def __del__(self):
 
293
        # Should this warn, or actually try to cleanup?
 
294
        if self.transport:
 
295
            warnings.warn("MemoryLock %r not explicitly unlocked" % (self.path,))
 
296
            self.unlock()
 
297
 
344
298
    def unlock(self):
345
299
        del self.transport._locks[self.path]
346
300
        self.transport = None
350
304
    """Server for the MemoryTransport for testing with."""
351
305
 
352
306
    def start_server(self):
353
 
        self._dirs = {'/': None}
 
307
        self._dirs = {'/':None}
354
308
        self._files = {}
355
 
        self._symlinks = {}
356
309
        self._locks = {}
357
310
        self._scheme = "memory+%s:///" % id(self)
358
 
 
359
311
        def memory_factory(url):
360
 
            from . import memory
 
312
            from bzrlib.transport import memory
361
313
            result = memory.MemoryTransport(url)
362
314
            result._dirs = self._dirs
363
315
            result._files = self._files
364
 
            result._symlinks = self._symlinks
365
316
            result._locks = self._locks
366
317
            return result
367
318
        self._memory_factory = memory_factory
372
323
        transport.unregister_transport(self._scheme, self._memory_factory)
373
324
 
374
325
    def get_url(self):
375
 
        """See breezy.transport.Server.get_url."""
 
326
        """See bzrlib.transport.Server.get_url."""
376
327
        return self._scheme
377
328
 
378
329
    def get_bogus_url(self):