~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-10-16 21:31:35 UTC
  • mfrom: (3363.19.4 fix-iter-changes)
  • Revision ID: pqm@pqm.ubuntu.com-20081016213135-0115pw9c95l2dyxq
PreviewTree.iter_changes accepts all standard parameters (abentley)

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 TransportError, NoSuchFile, FileExists, LockError
 
30
from bzrlib.errors import (
 
31
    FileExists,
 
32
    LockError,
 
33
    InProcessTransport,
 
34
    NoSuchFile,
 
35
    TransportError,
 
36
    )
31
37
from bzrlib.trace import mutter
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
    )
33
46
import bzrlib.urlutils as urlutils
34
47
 
35
48
 
58
71
        if url[-1] != '/':
59
72
            url = url + '/'
60
73
        super(MemoryTransport, self).__init__(url)
61
 
        self._cwd = url[url.find(':') + 3:]
 
74
        split = url.find(':') + 3
 
75
        self._scheme = url[:split]
 
76
        self._cwd = url[split:]
62
77
        # dictionaries from absolute path to file mode
63
78
        self._dirs = {'/':None}
64
79
        self._files = {}
66
81
 
67
82
    def clone(self, offset=None):
68
83
        """See Transport.clone()."""
69
 
        if offset is None or offset == '':
70
 
            return copy(self)
71
 
        segments = offset.split('/')
72
 
        cwdsegments = self._cwd.split('/')[:-1]
73
 
        while len(segments):
74
 
            segment = segments.pop(0)
75
 
            if segment == '.':
76
 
                continue
77
 
            if segment == '..':
78
 
                if len(cwdsegments) > 1:
79
 
                    cwdsegments.pop()
80
 
                continue
81
 
            cwdsegments.append(segment)
82
 
        url = self.base[:self.base.find(':') + 3] + '/'.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
83
88
        result = MemoryTransport(url)
84
89
        result._dirs = self._dirs
85
90
        result._files = self._files
97
102
        else:
98
103
            return temp_t.base[:-1]
99
104
 
100
 
    def append(self, relpath, f, mode=None):
101
 
        """See Transport.append()."""
 
105
    def append_file(self, relpath, f, mode=None):
 
106
        """See Transport.append_file()."""
102
107
        _abspath = self._abspath(relpath)
103
108
        self._check_parent(_abspath)
104
109
        orig_content, orig_mode = self._files.get(_abspath, ("", None))
116
121
    def has(self, relpath):
117
122
        """See Transport.has()."""
118
123
        _abspath = self._abspath(relpath)
119
 
        return _abspath in self._files or _abspath in self._dirs
 
124
        return (_abspath in self._files) or (_abspath in self._dirs)
120
125
 
121
126
    def delete(self, relpath):
122
127
        """See Transport.delete()."""
125
130
            raise NoSuchFile(relpath)
126
131
        del self._files[_abspath]
127
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
 
128
139
    def get(self, relpath):
129
140
        """See Transport.get()."""
130
141
        _abspath = self._abspath(relpath)
131
142
        if not _abspath in self._files:
132
 
            raise NoSuchFile(relpath)
 
143
            if _abspath in self._dirs:
 
144
                return LateReadError(relpath)
 
145
            else:
 
146
                raise NoSuchFile(relpath)
133
147
        return StringIO(self._files[_abspath][0])
134
148
 
135
 
    def put(self, relpath, f, mode=None):
136
 
        """See Transport.put()."""
 
149
    def put_file(self, relpath, f, mode=None):
 
150
        """See Transport.put_file()."""
137
151
        _abspath = self._abspath(relpath)
138
152
        self._check_parent(_abspath)
139
 
        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)
140
162
 
141
163
    def mkdir(self, relpath, mode=None):
142
164
        """See Transport.mkdir()."""
146
168
            raise FileExists(relpath)
147
169
        self._dirs[_abspath]=mode
148
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
 
149
178
    def listable(self):
150
179
        """See Transport.listable."""
151
180
        return True
153
182
    def iter_files_recursive(self):
154
183
        for file in self._files:
155
184
            if file.startswith(self._cwd):
156
 
                yield file[len(self._cwd):]
 
185
                yield urlutils.escape(file[len(self._cwd):])
157
186
    
158
187
    def list_dir(self, relpath):
159
188
        """See Transport.list_dir()."""
161
190
        if _abspath != '/' and _abspath not in self._dirs:
162
191
            raise NoSuchFile(relpath)
163
192
        result = []
164
 
        for path in self._files:
165
 
            if (path.startswith(_abspath) and 
166
 
                path[len(_abspath) + 1:].find('/') == -1 and
167
 
                len(path) > len(_abspath)):
168
 
                result.append(path[len(_abspath) + 1:])
169
 
        for path in self._dirs:
170
 
            if (path.startswith(_abspath) and 
171
 
                path[len(_abspath) + 1:].find('/') == -1 and
172
 
                len(path) > len(_abspath) and
173
 
                path[len(_abspath)] == '/'):
174
 
                result.append(path[len(_abspath) + 1:])
175
 
        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)
176
204
 
177
205
    def rename(self, rel_from, rel_to):
178
206
        """Rename a file or directory; fail if the destination exists"""
201
229
        if _abspath in self._files:
202
230
            self._translate_error(IOError(errno.ENOTDIR, relpath), relpath)
203
231
        for path in self._files:
204
 
            if path.startswith(_abspath):
 
232
            if path.startswith(_abspath + '/'):
205
233
                self._translate_error(IOError(errno.ENOTEMPTY, relpath),
206
234
                                      relpath)
207
235
        for path in self._dirs:
208
 
            if path.startswith(_abspath) and path != _abspath:
 
236
            if path.startswith(_abspath + '/') and path != _abspath:
209
237
                self._translate_error(IOError(errno.ENOTEMPTY, relpath), relpath)
210
238
        if not _abspath in self._dirs:
211
239
            raise NoSuchFile(relpath)
233
261
    def _abspath(self, relpath):
234
262
        """Generate an internal absolute path."""
235
263
        relpath = urlutils.unescape(relpath)
236
 
        if relpath.find('..') != -1:
237
 
            raise AssertionError('relpath contains ..')
238
 
        if relpath == '.':
239
 
            if (self._cwd == '/'):
240
 
                return self._cwd
241
 
            return self._cwd[:-1]
242
 
        if relpath.endswith('/'):
243
 
            relpath = relpath[:-1]
244
 
        if relpath.startswith('./'):
245
 
            relpath = relpath[2:]
246
 
        return self._cwd + 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)
247
280
 
248
281
 
249
282
class _MemoryLock(object):
250
283
    """This makes a lock."""
251
284
 
252
285
    def __init__(self, path, transport):
253
 
        assert isinstance(transport, MemoryTransport)
254
286
        self.path = path
255
287
        self.transport = transport
256
288
        if self.path in self.transport._locks:
260
292
    def __del__(self):
261
293
        # Should this warn, or actually try to cleanup?
262
294
        if self.transport:
263
 
            warn("MemoryLock %r not explicitly unlocked" % (self.path,))
 
295
            warnings.warn("MemoryLock %r not explicitly unlocked" % (self.path,))
264
296
            self.unlock()
265
297
 
266
298
    def unlock(self):