~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/transport/memory.py

  • Committer: Aaron Bentley
  • Date: 2009-03-24 15:47:32 UTC
  • mto: This revision was merged to the branch mainline in revision 4241.
  • Revision ID: aaron@aaronbentley.com-20090324154732-bwkvi4dx3o90a7dq
Add output, emit minimal inventory delta.

Show diffs side-by-side

added added

removed removed

Lines of Context:
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
 
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
16
 
17
17
"""Implementation of Transport that uses memory for its storage.
18
18
 
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
28
27
from cStringIO import StringIO
29
28
import warnings
30
29
 
31
 
from bzrlib.errors import TransportError, NoSuchFile, FileExists, LockError
 
30
from bzrlib.errors import (
 
31
    FileExists,
 
32
    LockError,
 
33
    InProcessTransport,
 
34
    NoSuchFile,
 
35
    TransportError,
 
36
    )
32
37
from bzrlib.trace import mutter
33
 
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
    )
34
46
import bzrlib.urlutils as urlutils
35
47
 
36
48
 
59
71
        if url[-1] != '/':
60
72
            url = url + '/'
61
73
        super(MemoryTransport, self).__init__(url)
62
 
        self._cwd = url[url.find(':') + 3:]
 
74
        split = url.find(':') + 3
 
75
        self._scheme = url[:split]
 
76
        self._cwd = url[split:]
63
77
        # dictionaries from absolute path to file mode
64
78
        self._dirs = {'/':None}
65
79
        self._files = {}
67
81
 
68
82
    def clone(self, offset=None):
69
83
        """See Transport.clone()."""
70
 
        if offset is None or offset == '':
71
 
            return copy(self)
72
 
        segments = offset.split('/')
73
 
        cwdsegments = self._cwd.split('/')[:-1]
74
 
        while len(segments):
75
 
            segment = segments.pop(0)
76
 
            if segment == '.':
77
 
                continue
78
 
            if segment == '..':
79
 
                if len(cwdsegments) > 1:
80
 
                    cwdsegments.pop()
81
 
                continue
82
 
            cwdsegments.append(segment)
83
 
        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
84
88
        result = MemoryTransport(url)
85
89
        result._dirs = self._dirs
86
90
        result._files = self._files
98
102
        else:
99
103
            return temp_t.base[:-1]
100
104
 
101
 
    def append(self, relpath, f, mode=None):
102
 
        """See Transport.append()."""
 
105
    def append_file(self, relpath, f, mode=None):
 
106
        """See Transport.append_file()."""
103
107
        _abspath = self._abspath(relpath)
104
108
        self._check_parent(_abspath)
105
109
        orig_content, orig_mode = self._files.get(_abspath, ("", None))
117
121
    def has(self, relpath):
118
122
        """See Transport.has()."""
119
123
        _abspath = self._abspath(relpath)
120
 
        return _abspath in self._files or _abspath in self._dirs
 
124
        return (_abspath in self._files) or (_abspath in self._dirs)
121
125
 
122
126
    def delete(self, relpath):
123
127
        """See Transport.delete()."""
126
130
            raise NoSuchFile(relpath)
127
131
        del self._files[_abspath]
128
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
 
129
139
    def get(self, relpath):
130
140
        """See Transport.get()."""
131
141
        _abspath = self._abspath(relpath)
132
142
        if not _abspath in self._files:
133
 
            raise NoSuchFile(relpath)
 
143
            if _abspath in self._dirs:
 
144
                return LateReadError(relpath)
 
145
            else:
 
146
                raise NoSuchFile(relpath)
134
147
        return StringIO(self._files[_abspath][0])
135
148
 
136
 
    def put(self, relpath, f, mode=None):
137
 
        """See Transport.put()."""
 
149
    def put_file(self, relpath, f, mode=None):
 
150
        """See Transport.put_file()."""
138
151
        _abspath = self._abspath(relpath)
139
152
        self._check_parent(_abspath)
140
 
        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)
141
162
 
142
163
    def mkdir(self, relpath, mode=None):
143
164
        """See Transport.mkdir()."""
147
168
            raise FileExists(relpath)
148
169
        self._dirs[_abspath]=mode
149
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
 
150
178
    def listable(self):
151
179
        """See Transport.listable."""
152
180
        return True
154
182
    def iter_files_recursive(self):
155
183
        for file in self._files:
156
184
            if file.startswith(self._cwd):
157
 
                yield file[len(self._cwd):]
158
 
    
 
185
                yield urlutils.escape(file[len(self._cwd):])
 
186
 
159
187
    def list_dir(self, relpath):
160
188
        """See Transport.list_dir()."""
161
189
        _abspath = self._abspath(relpath)
162
190
        if _abspath != '/' and _abspath not in self._dirs:
163
191
            raise NoSuchFile(relpath)
164
192
        result = []
165
 
        for path in self._files:
166
 
            if (path.startswith(_abspath) and 
167
 
                path[len(_abspath) + 1:].find('/') == -1 and
168
 
                len(path) > len(_abspath)):
169
 
                result.append(path[len(_abspath) + 1:])
170
 
        for path in self._dirs:
171
 
            if (path.startswith(_abspath) and 
172
 
                path[len(_abspath) + 1:].find('/') == -1 and
173
 
                len(path) > len(_abspath) and
174
 
                path[len(_abspath)] == '/'):
175
 
                result.append(path[len(_abspath) + 1:])
176
 
        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)
177
204
 
178
205
    def rename(self, rel_from, rel_to):
179
206
        """Rename a file or directory; fail if the destination exists"""
195
222
                    del container[path]
196
223
        do_renames(self._files)
197
224
        do_renames(self._dirs)
198
 
    
 
225
 
199
226
    def rmdir(self, relpath):
200
227
        """See Transport.rmdir."""
201
228
        _abspath = self._abspath(relpath)
202
229
        if _abspath in self._files:
203
230
            self._translate_error(IOError(errno.ENOTDIR, relpath), relpath)
204
231
        for path in self._files:
205
 
            if path.startswith(_abspath):
 
232
            if path.startswith(_abspath + '/'):
206
233
                self._translate_error(IOError(errno.ENOTEMPTY, relpath),
207
234
                                      relpath)
208
235
        for path in self._dirs:
209
 
            if path.startswith(_abspath) and path != _abspath:
 
236
            if path.startswith(_abspath + '/') and path != _abspath:
210
237
                self._translate_error(IOError(errno.ENOTEMPTY, relpath), relpath)
211
238
        if not _abspath in self._dirs:
212
239
            raise NoSuchFile(relpath)
216
243
        """See Transport.stat()."""
217
244
        _abspath = self._abspath(relpath)
218
245
        if _abspath in self._files:
219
 
            return MemoryStat(len(self._files[_abspath][0]), False, 
 
246
            return MemoryStat(len(self._files[_abspath][0]), False,
220
247
                              self._files[_abspath][1])
221
248
        elif _abspath in self._dirs:
222
249
            return MemoryStat(0, True, self._dirs[_abspath])
234
261
    def _abspath(self, relpath):
235
262
        """Generate an internal absolute path."""
236
263
        relpath = urlutils.unescape(relpath)
237
 
        if relpath.find('..') != -1:
238
 
            raise AssertionError('relpath contains ..')
239
 
        if relpath == '.':
240
 
            if (self._cwd == '/'):
241
 
                return self._cwd
242
 
            return self._cwd[:-1]
243
 
        if relpath.endswith('/'):
244
 
            relpath = relpath[:-1]
245
 
        if relpath.startswith('./'):
246
 
            relpath = relpath[2:]
247
 
        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)
248
280
 
249
281
 
250
282
class _MemoryLock(object):
251
283
    """This makes a lock."""
252
284
 
253
285
    def __init__(self, path, transport):
254
 
        assert isinstance(transport, MemoryTransport)
255
286
        self.path = path
256
287
        self.transport = transport
257
288
        if self.path in self.transport._locks: