~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/transport/memory.py

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2008-05-29 15:57:16 UTC
  • mfrom: (3427.5.9 dep_warnings)
  • Revision ID: pqm@pqm.ubuntu.com-20080529155716-0w3kic8lioa63231
(jam) Enable Deprecation Warnings when running -Werror and when
        running selftest

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
# Copyright (C) 2005, 2006 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
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
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20
20
so this is primarily useful for testing.
21
21
"""
22
22
 
23
 
from copy import copy
24
23
import os
25
24
import errno
26
25
import re
27
 
from stat import *
 
26
from stat import S_IFREG, S_IFDIR
28
27
from cStringIO import StringIO
 
28
import warnings
29
29
 
 
30
from bzrlib.errors import (
 
31
    FileExists,
 
32
    LockError,
 
33
    InProcessTransport,
 
34
    NoSuchFile,
 
35
    TransportError,
 
36
    )
30
37
from bzrlib.trace import mutter
31
 
from bzrlib.errors import TransportError, NoSuchFile, FileExists, LockError
32
 
from bzrlib.transport import Transport, register_transport, Server
 
38
from bzrlib.transport import (
 
39
    AppendBasedFileStream,
 
40
    _file_streams,
 
41
    LateReadError,
 
42
    register_transport,
 
43
    Server,
 
44
    Transport,
 
45
    )
 
46
import bzrlib.urlutils as urlutils
 
47
 
33
48
 
34
49
 
35
50
class MemoryStat(object):
52
67
    def __init__(self, url=""):
53
68
        """Set the 'base' path where files will be stored."""
54
69
        if url == "":
55
 
            url = "memory:/"
 
70
            url = "memory:///"
56
71
        if url[-1] != '/':
57
72
            url = url + '/'
58
73
        super(MemoryTransport, self).__init__(url)
59
 
        self._cwd = url[url.find(':') + 1:]
 
74
        split = url.find(':') + 3
 
75
        self._scheme = url[:split]
 
76
        self._cwd = url[split:]
60
77
        # dictionaries from absolute path to file mode
61
 
        self._dirs = {}
 
78
        self._dirs = {'/':None}
62
79
        self._files = {}
63
80
        self._locks = {}
64
81
 
65
82
    def clone(self, offset=None):
66
83
        """See Transport.clone()."""
67
 
        if offset is None or offset == '':
68
 
            return copy(self)
69
 
        segments = offset.split('/')
70
 
        cwdsegments = self._cwd.split('/')[:-1]
71
 
        while len(segments):
72
 
            segment = segments.pop(0)
73
 
            if segment == '.':
74
 
                continue
75
 
            if segment == '..':
76
 
                if len(cwdsegments) > 1:
77
 
                    cwdsegments.pop()
78
 
                continue
79
 
            cwdsegments.append(segment)
80
 
        url = self.base[:self.base.find(':') + 1] + '/'.join(cwdsegments) + '/'
 
84
        path = self._combine_paths(self._cwd, offset)
 
85
        if len(path) == 0 or path[-1] != '/':
 
86
            path += '/'
 
87
        url = self._scheme + path
81
88
        result = MemoryTransport(url)
82
89
        result._dirs = self._dirs
83
90
        result._files = self._files
90
97
        # current environment - XXX RBC 20060404 move the clone '..' handling
91
98
        # into here and call abspath from clone
92
99
        temp_t = self.clone(relpath)
93
 
        if temp_t.base.count('/') == 1:
 
100
        if temp_t.base.count('/') == 3:
94
101
            return temp_t.base
95
102
        else:
96
103
            return temp_t.base[:-1]
97
104
 
98
 
    def append(self, relpath, f):
99
 
        """See Transport.append()."""
 
105
    def append_file(self, relpath, f, mode=None):
 
106
        """See Transport.append_file()."""
100
107
        _abspath = self._abspath(relpath)
101
108
        self._check_parent(_abspath)
102
109
        orig_content, orig_mode = self._files.get(_abspath, ("", None))
103
 
        self._files[_abspath] = (orig_content + f.read(), orig_mode)
 
110
        if mode is None:
 
111
            mode = orig_mode
 
112
        self._files[_abspath] = (orig_content + f.read(), mode)
104
113
        return len(orig_content)
105
114
 
106
115
    def _check_parent(self, _abspath):
112
121
    def has(self, relpath):
113
122
        """See Transport.has()."""
114
123
        _abspath = self._abspath(relpath)
115
 
        return _abspath in self._files or _abspath in self._dirs
 
124
        return (_abspath in self._files) or (_abspath in self._dirs)
116
125
 
117
126
    def delete(self, relpath):
118
127
        """See Transport.delete()."""
121
130
            raise NoSuchFile(relpath)
122
131
        del self._files[_abspath]
123
132
 
 
133
    def external_url(self):
 
134
        """See bzrlib.transport.Transport.external_url."""
 
135
        # MemoryTransport's are only accessible in-process
 
136
        # so we raise here
 
137
        raise InProcessTransport(self)
 
138
 
124
139
    def get(self, relpath):
125
140
        """See Transport.get()."""
126
141
        _abspath = self._abspath(relpath)
127
142
        if not _abspath in self._files:
128
 
            raise NoSuchFile(relpath)
 
143
            if _abspath in self._dirs:
 
144
                return LateReadError(relpath)
 
145
            else:
 
146
                raise NoSuchFile(relpath)
129
147
        return StringIO(self._files[_abspath][0])
130
148
 
131
 
    def put(self, relpath, f, mode=None):
132
 
        """See Transport.put()."""
 
149
    def put_file(self, relpath, f, mode=None):
 
150
        """See Transport.put_file()."""
133
151
        _abspath = self._abspath(relpath)
134
152
        self._check_parent(_abspath)
135
 
        self._files[_abspath] = (f.read(), mode)
 
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)
136
162
 
137
163
    def mkdir(self, relpath, mode=None):
138
164
        """See Transport.mkdir()."""
142
168
            raise FileExists(relpath)
143
169
        self._dirs[_abspath]=mode
144
170
 
 
171
    def open_write_stream(self, relpath, mode=None):
 
172
        """See Transport.open_write_stream."""
 
173
        self.put_bytes(relpath, "", mode)
 
174
        result = AppendBasedFileStream(self, relpath)
 
175
        _file_streams[self.abspath(relpath)] = result
 
176
        return result
 
177
 
145
178
    def listable(self):
146
179
        """See Transport.listable."""
147
180
        return True
149
182
    def iter_files_recursive(self):
150
183
        for file in self._files:
151
184
            if file.startswith(self._cwd):
152
 
                yield file[len(self._cwd):]
 
185
                yield urlutils.escape(file[len(self._cwd):])
153
186
    
154
187
    def list_dir(self, relpath):
155
188
        """See Transport.list_dir()."""
157
190
        if _abspath != '/' and _abspath not in self._dirs:
158
191
            raise NoSuchFile(relpath)
159
192
        result = []
160
 
        for path in self._files:
161
 
            if (path.startswith(_abspath) and 
162
 
                path[len(_abspath) + 1:].find('/') == -1 and
163
 
                len(path) > len(_abspath)):
164
 
                result.append(path[len(_abspath) + 1:])
165
 
        for path in self._dirs:
166
 
            if (path.startswith(_abspath) and 
167
 
                path[len(_abspath) + 1:].find('/') == -1 and
168
 
                len(path) > len(_abspath) and
169
 
                path[len(_abspath)] == '/'):
170
 
                result.append(path[len(_abspath) + 1:])
171
 
        return result
 
193
 
 
194
        if not _abspath.endswith('/'):
 
195
            _abspath += '/'
 
196
 
 
197
        for path_group in self._files, self._dirs:
 
198
            for path in path_group:
 
199
                if path.startswith(_abspath):
 
200
                    trailing = path[len(_abspath):]
 
201
                    if trailing and '/' not in trailing:
 
202
                        result.append(trailing)
 
203
        return map(urlutils.escape, result)
172
204
 
173
205
    def rename(self, rel_from, rel_to):
174
206
        """Rename a file or directory; fail if the destination exists"""
197
229
        if _abspath in self._files:
198
230
            self._translate_error(IOError(errno.ENOTDIR, relpath), relpath)
199
231
        for path in self._files:
200
 
            if path.startswith(_abspath):
 
232
            if path.startswith(_abspath + '/'):
201
233
                self._translate_error(IOError(errno.ENOTEMPTY, relpath),
202
234
                                      relpath)
203
235
        for path in self._dirs:
204
 
            if path.startswith(_abspath) and path != _abspath:
 
236
            if path.startswith(_abspath + '/') and path != _abspath:
205
237
                self._translate_error(IOError(errno.ENOTEMPTY, relpath), relpath)
206
238
        if not _abspath in self._dirs:
207
239
            raise NoSuchFile(relpath)
213
245
        if _abspath in self._files:
214
246
            return MemoryStat(len(self._files[_abspath][0]), False, 
215
247
                              self._files[_abspath][1])
216
 
        elif _abspath == '':
217
 
            return MemoryStat(0, True, None)
218
248
        elif _abspath in self._dirs:
219
249
            return MemoryStat(0, True, self._dirs[_abspath])
220
250
        else:
230
260
 
231
261
    def _abspath(self, relpath):
232
262
        """Generate an internal absolute path."""
233
 
        if relpath.find('..') != -1:
234
 
            raise AssertionError('relpath contains ..')
235
 
        if relpath == '.':
236
 
            return self._cwd[:-1]
237
 
        if relpath.endswith('/'):
238
 
            relpath = relpath[:-1]
239
 
        if relpath.startswith('./'):
240
 
            relpath = relpath[2:]
241
 
        return self._cwd + relpath
 
263
        relpath = urlutils.unescape(relpath)
 
264
        if relpath[:1] == '/':
 
265
            return relpath
 
266
        cwd_parts = self._cwd.split('/')
 
267
        rel_parts = relpath.split('/')
 
268
        r = []
 
269
        for i in cwd_parts + rel_parts:
 
270
            if i == '..':
 
271
                if not r:
 
272
                    raise ValueError("illegal relpath %r under %r"
 
273
                        % (relpath, self._cwd))
 
274
                r = r[:-1]
 
275
            elif i == '.' or i == '':
 
276
                pass
 
277
            else:
 
278
                r.append(i)
 
279
        return '/' + '/'.join(r)
242
280
 
243
281
 
244
282
class _MemoryLock(object):
245
283
    """This makes a lock."""
246
284
 
247
285
    def __init__(self, path, transport):
248
 
        assert isinstance(transport, MemoryTransport)
249
286
        self.path = path
250
287
        self.transport = transport
251
288
        if self.path in self.transport._locks:
255
292
    def __del__(self):
256
293
        # Should this warn, or actually try to cleanup?
257
294
        if self.transport:
258
 
            warn("MemoryLock %r not explicitly unlocked" % (self.path,))
 
295
            warnings.warn("MemoryLock %r not explicitly unlocked" % (self.path,))
259
296
            self.unlock()
260
297
 
261
298
    def unlock(self):
268
305
 
269
306
    def setUp(self):
270
307
        """See bzrlib.transport.Server.setUp."""
271
 
        self._dirs = {}
 
308
        self._dirs = {'/':None}
272
309
        self._files = {}
273
310
        self._locks = {}
274
 
        self._scheme = "memory+%s:" % id(self)
 
311
        self._scheme = "memory+%s:///" % id(self)
275
312
        def memory_factory(url):
276
313
            result = MemoryTransport(url)
277
314
            result._dirs = self._dirs