/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: 2020-05-24 00:42:36 UTC
  • mto: This revision was merged to the branch mainline in revision 7505.
  • Revision ID: jelmer@jelmer.uk-20200524004236-jdj6obo4k5lznqw2
Cleanup Windows functions.

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