~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/transport/memory.py

  • Committer: Vincent Ladeuil
  • Date: 2013-08-09 15:09:23 UTC
  • mto: This revision was merged to the branch mainline in revision 6587.
  • Revision ID: v.ladeuil+lp@free.fr-20130809150923-y71dusyorep0hbkt
Fix various typos in docstrings. Rename 'buffer' to 'buf' since it's now a python builtin function.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005 Canonical Ltd
2
 
 
 
1
# Copyright (C) 2005-2010 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
 
"""Implementation of Transport that uses memory for its storage."""
 
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 
16
 
 
17
"""Implementation of Transport that uses memory for its storage.
 
18
 
 
19
The contents of the transport will be lost when the object is discarded,
 
20
so this is primarily useful for testing.
 
21
"""
 
22
 
 
23
from __future__ import absolute_import
17
24
 
18
25
import os
19
26
import errno
 
27
from stat import S_IFREG, S_IFDIR
20
28
from cStringIO import StringIO
21
29
 
22
 
from bzrlib.trace import mutter
23
 
from bzrlib.errors import TransportError, NoSuchFile, FileExists
24
 
from bzrlib.transport import Transport
 
30
from bzrlib import (
 
31
    transport,
 
32
    urlutils,
 
33
    )
 
34
from bzrlib.errors import (
 
35
    FileExists,
 
36
    LockError,
 
37
    InProcessTransport,
 
38
    NoSuchFile,
 
39
    )
 
40
from bzrlib.transport import (
 
41
    AppendBasedFileStream,
 
42
    _file_streams,
 
43
    LateReadError,
 
44
    )
 
45
 
 
46
 
25
47
 
26
48
class MemoryStat(object):
27
49
 
28
 
    def __init__(self, size):
 
50
    def __init__(self, size, is_dir, perms):
29
51
        self.st_size = size
30
 
 
31
 
 
32
 
class MemoryTransport(Transport):
33
 
    """This is the transport agent for local filesystem access."""
34
 
 
35
 
    def __init__(self):
 
52
        if not is_dir:
 
53
            if perms is None:
 
54
                perms = 0644
 
55
            self.st_mode = S_IFREG | perms
 
56
        else:
 
57
            if perms is None:
 
58
                perms = 0755
 
59
            self.st_mode = S_IFDIR | perms
 
60
 
 
61
 
 
62
class MemoryTransport(transport.Transport):
 
63
    """This is an in memory file system for transient data storage."""
 
64
 
 
65
    def __init__(self, url=""):
36
66
        """Set the 'base' path where files will be stored."""
37
 
        super(MemoryTransport, self).__init__('in-memory:')
38
 
        self._dirs = set()
 
67
        if url == "":
 
68
            url = "memory:///"
 
69
        if url[-1] != '/':
 
70
            url = url + '/'
 
71
        super(MemoryTransport, self).__init__(url)
 
72
        split = url.find(':') + 3
 
73
        self._scheme = url[:split]
 
74
        self._cwd = url[split:]
 
75
        # dictionaries from absolute path to file mode
 
76
        self._dirs = {'/':None}
39
77
        self._files = {}
 
78
        self._locks = {}
40
79
 
41
80
    def clone(self, offset=None):
42
81
        """See Transport.clone()."""
43
 
        return self
 
82
        path = urlutils.URL._combine_paths(self._cwd, offset)
 
83
        if len(path) == 0 or path[-1] != '/':
 
84
            path += '/'
 
85
        url = self._scheme + path
 
86
        result = self.__class__(url)
 
87
        result._dirs = self._dirs
 
88
        result._files = self._files
 
89
        result._locks = self._locks
 
90
        return result
44
91
 
45
92
    def abspath(self, relpath):
46
93
        """See Transport.abspath()."""
47
 
        return self.base + relpath
48
 
 
49
 
    def append(self, relpath, f):
50
 
        """See Transport.append()."""
51
 
        self._check_parent(relpath)
52
 
        self._files[relpath] = self._files.get(relpath, "") + f.read()
53
 
 
54
 
    def _check_parent(self, relpath):
55
 
        dir = os.path.dirname(relpath)
56
 
        if dir != '':
 
94
        # while a little slow, this is sufficiently fast to not matter in our
 
95
        # current environment - XXX RBC 20060404 move the clone '..' handling
 
96
        # into here and call abspath from clone
 
97
        temp_t = self.clone(relpath)
 
98
        if temp_t.base.count('/') == 3:
 
99
            return temp_t.base
 
100
        else:
 
101
            return temp_t.base[:-1]
 
102
 
 
103
    def append_file(self, relpath, f, mode=None):
 
104
        """See Transport.append_file()."""
 
105
        _abspath = self._abspath(relpath)
 
106
        self._check_parent(_abspath)
 
107
        orig_content, orig_mode = self._files.get(_abspath, ("", None))
 
108
        if mode is None:
 
109
            mode = orig_mode
 
110
        self._files[_abspath] = (orig_content + f.read(), mode)
 
111
        return len(orig_content)
 
112
 
 
113
    def _check_parent(self, _abspath):
 
114
        dir = os.path.dirname(_abspath)
 
115
        if dir != '/':
57
116
            if not dir in self._dirs:
58
 
                raise NoSuchFile(relpath)
 
117
                raise NoSuchFile(_abspath)
59
118
 
60
119
    def has(self, relpath):
61
120
        """See Transport.has()."""
62
 
        return relpath in self._files
 
121
        _abspath = self._abspath(relpath)
 
122
        return (_abspath in self._files) or (_abspath in self._dirs)
 
123
 
 
124
    def delete(self, relpath):
 
125
        """See Transport.delete()."""
 
126
        _abspath = self._abspath(relpath)
 
127
        if not _abspath in self._files:
 
128
            raise NoSuchFile(relpath)
 
129
        del self._files[_abspath]
 
130
 
 
131
    def external_url(self):
 
132
        """See bzrlib.transport.Transport.external_url."""
 
133
        # MemoryTransport's are only accessible in-process
 
134
        # so we raise here
 
135
        raise InProcessTransport(self)
63
136
 
64
137
    def get(self, relpath):
65
138
        """See Transport.get()."""
66
 
        if not relpath in self._files:
67
 
            raise NoSuchFile(relpath)
68
 
        return StringIO(self._files[relpath])
 
139
        _abspath = self._abspath(relpath)
 
140
        if not _abspath in self._files:
 
141
            if _abspath in self._dirs:
 
142
                return LateReadError(relpath)
 
143
            else:
 
144
                raise NoSuchFile(relpath)
 
145
        return StringIO(self._files[_abspath][0])
69
146
 
70
 
    def put(self, relpath, f, mode=None):
71
 
        """See Transport.put()."""
72
 
        self._check_parent(relpath)
73
 
        self._files[relpath] = f.read()
 
147
    def put_file(self, relpath, f, mode=None):
 
148
        """See Transport.put_file()."""
 
149
        _abspath = self._abspath(relpath)
 
150
        self._check_parent(_abspath)
 
151
        bytes = f.read()
 
152
        if type(bytes) is not str:
 
153
            # Although not strictly correct, we raise UnicodeEncodeError to be
 
154
            # compatible with other transports.
 
155
            raise UnicodeEncodeError(
 
156
                'undefined', bytes, 0, 1,
 
157
                'put_file must be given a file of bytes, not unicode.')
 
158
        self._files[_abspath] = (bytes, mode)
 
159
        return len(bytes)
74
160
 
75
161
    def mkdir(self, relpath, mode=None):
76
162
        """See Transport.mkdir()."""
77
 
        self._check_parent(relpath)
78
 
        if relpath in self._dirs:
 
163
        _abspath = self._abspath(relpath)
 
164
        self._check_parent(_abspath)
 
165
        if _abspath in self._dirs:
79
166
            raise FileExists(relpath)
80
 
        self._dirs.add(relpath)
 
167
        self._dirs[_abspath]=mode
 
168
 
 
169
    def open_write_stream(self, relpath, mode=None):
 
170
        """See Transport.open_write_stream."""
 
171
        self.put_bytes(relpath, "", mode)
 
172
        result = AppendBasedFileStream(self, relpath)
 
173
        _file_streams[self.abspath(relpath)] = result
 
174
        return result
81
175
 
82
176
    def listable(self):
83
177
        """See Transport.listable."""
84
178
        return True
85
179
 
86
180
    def iter_files_recursive(self):
87
 
        return iter(self._files)
88
 
    
89
 
#    def list_dir(self, relpath):
90
 
#    TODO if needed
91
 
    
 
181
        for file in self._files:
 
182
            if file.startswith(self._cwd):
 
183
                yield urlutils.escape(file[len(self._cwd):])
 
184
 
 
185
    def list_dir(self, relpath):
 
186
        """See Transport.list_dir()."""
 
187
        _abspath = self._abspath(relpath)
 
188
        if _abspath != '/' and _abspath not in self._dirs:
 
189
            raise NoSuchFile(relpath)
 
190
        result = []
 
191
 
 
192
        if not _abspath.endswith('/'):
 
193
            _abspath += '/'
 
194
 
 
195
        for path_group in self._files, self._dirs:
 
196
            for path in path_group:
 
197
                if path.startswith(_abspath):
 
198
                    trailing = path[len(_abspath):]
 
199
                    if trailing and '/' not in trailing:
 
200
                        result.append(trailing)
 
201
        return map(urlutils.escape, result)
 
202
 
 
203
    def rename(self, rel_from, rel_to):
 
204
        """Rename a file or directory; fail if the destination exists"""
 
205
        abs_from = self._abspath(rel_from)
 
206
        abs_to = self._abspath(rel_to)
 
207
        def replace(x):
 
208
            if x == abs_from:
 
209
                x = abs_to
 
210
            elif x.startswith(abs_from + '/'):
 
211
                x = abs_to + x[len(abs_from):]
 
212
            return x
 
213
        def do_renames(container):
 
214
            for path in container:
 
215
                new_path = replace(path)
 
216
                if new_path != path:
 
217
                    if new_path in container:
 
218
                        raise FileExists(new_path)
 
219
                    container[new_path] = container[path]
 
220
                    del container[path]
 
221
        do_renames(self._files)
 
222
        do_renames(self._dirs)
 
223
 
 
224
    def rmdir(self, relpath):
 
225
        """See Transport.rmdir."""
 
226
        _abspath = self._abspath(relpath)
 
227
        if _abspath in self._files:
 
228
            self._translate_error(IOError(errno.ENOTDIR, relpath), relpath)
 
229
        for path in self._files:
 
230
            if path.startswith(_abspath + '/'):
 
231
                self._translate_error(IOError(errno.ENOTEMPTY, relpath),
 
232
                                      relpath)
 
233
        for path in self._dirs:
 
234
            if path.startswith(_abspath + '/') and path != _abspath:
 
235
                self._translate_error(IOError(errno.ENOTEMPTY, relpath), relpath)
 
236
        if not _abspath in self._dirs:
 
237
            raise NoSuchFile(relpath)
 
238
        del self._dirs[_abspath]
 
239
 
92
240
    def stat(self, relpath):
93
241
        """See Transport.stat()."""
94
 
        return MemoryStat(len(self._files[relpath]))
95
 
 
96
 
#    def lock_read(self, relpath):
97
 
#   TODO if needed
98
 
#
99
 
#    def lock_write(self, relpath):
100
 
#   TODO if needed
 
242
        _abspath = self._abspath(relpath)
 
243
        if _abspath in self._files:
 
244
            return MemoryStat(len(self._files[_abspath][0]), False,
 
245
                              self._files[_abspath][1])
 
246
        elif _abspath in self._dirs:
 
247
            return MemoryStat(0, True, self._dirs[_abspath])
 
248
        else:
 
249
            raise NoSuchFile(_abspath)
 
250
 
 
251
    def lock_read(self, relpath):
 
252
        """See Transport.lock_read()."""
 
253
        return _MemoryLock(self._abspath(relpath), self)
 
254
 
 
255
    def lock_write(self, relpath):
 
256
        """See Transport.lock_write()."""
 
257
        return _MemoryLock(self._abspath(relpath), self)
 
258
 
 
259
    def _abspath(self, relpath):
 
260
        """Generate an internal absolute path."""
 
261
        relpath = urlutils.unescape(relpath)
 
262
        if relpath[:1] == '/':
 
263
            return relpath
 
264
        cwd_parts = self._cwd.split('/')
 
265
        rel_parts = relpath.split('/')
 
266
        r = []
 
267
        for i in cwd_parts + rel_parts:
 
268
            if i == '..':
 
269
                if not r:
 
270
                    raise ValueError("illegal relpath %r under %r"
 
271
                        % (relpath, self._cwd))
 
272
                r = r[:-1]
 
273
            elif i == '.' or i == '':
 
274
                pass
 
275
            else:
 
276
                r.append(i)
 
277
        return '/' + '/'.join(r)
 
278
 
 
279
 
 
280
class _MemoryLock(object):
 
281
    """This makes a lock."""
 
282
 
 
283
    def __init__(self, path, transport):
 
284
        self.path = path
 
285
        self.transport = transport
 
286
        if self.path in self.transport._locks:
 
287
            raise LockError('File %r already locked' % (self.path,))
 
288
        self.transport._locks[self.path] = self
 
289
 
 
290
    def unlock(self):
 
291
        del self.transport._locks[self.path]
 
292
        self.transport = None
 
293
 
 
294
 
 
295
class MemoryServer(transport.Server):
 
296
    """Server for the MemoryTransport for testing with."""
 
297
 
 
298
    def start_server(self):
 
299
        self._dirs = {'/':None}
 
300
        self._files = {}
 
301
        self._locks = {}
 
302
        self._scheme = "memory+%s:///" % id(self)
 
303
        def memory_factory(url):
 
304
            from bzrlib.transport import memory
 
305
            result = memory.MemoryTransport(url)
 
306
            result._dirs = self._dirs
 
307
            result._files = self._files
 
308
            result._locks = self._locks
 
309
            return result
 
310
        self._memory_factory = memory_factory
 
311
        transport.register_transport(self._scheme, self._memory_factory)
 
312
 
 
313
    def stop_server(self):
 
314
        # unregister this server
 
315
        transport.unregister_transport(self._scheme, self._memory_factory)
 
316
 
 
317
    def get_url(self):
 
318
        """See bzrlib.transport.Server.get_url."""
 
319
        return self._scheme
 
320
 
 
321
    def get_bogus_url(self):
 
322
        raise NotImplementedError
 
323
 
 
324
 
 
325
def get_test_permutations():
 
326
    """Return the permutations to be used in testing."""
 
327
    return [(MemoryTransport, MemoryServer),
 
328
            ]