/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 breezy/transport/memory.py

  • Committer: Jelmer Vernooij
  • Date: 2019-10-28 01:38:39 UTC
  • mto: This revision was merged to the branch mainline in revision 7412.
  • Revision ID: jelmer@jelmer.uk-20191028013839-q63zzm4yr0id9b3o
Allow unknown extras in git commits when just inspecting revisions, rather than importing.

Show diffs side-by-side

added added

removed removed

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