~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: 2006-03-22 12:17:00 UTC
  • mfrom: (1616.1.10 bzr.mbp.integration)
  • Revision ID: pqm@pqm.ubuntu.com-20060322121700-79ce0be81013aba1
(mbp) pycurl fixes, other fixes, weave commands, verbose commit changes from robert

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
23
24
import os
24
25
import errno
25
26
import re
26
 
from stat import S_IFREG, S_IFDIR
 
27
from stat import *
27
28
from cStringIO import StringIO
28
 
import warnings
29
29
 
 
30
from bzrlib.trace import mutter
30
31
from bzrlib.errors import TransportError, NoSuchFile, FileExists, LockError
31
 
from bzrlib.trace import mutter
32
 
from bzrlib.transport import (
33
 
    LateReadError,
34
 
    register_transport,
35
 
    Server,
36
 
    Transport,
37
 
    )
38
 
import bzrlib.urlutils as urlutils
39
 
 
40
 
 
 
32
from bzrlib.transport import Transport, register_transport, Server
41
33
 
42
34
class MemoryStat(object):
43
35
 
59
51
    def __init__(self, url=""):
60
52
        """Set the 'base' path where files will be stored."""
61
53
        if url == "":
62
 
            url = "memory:///"
 
54
            url = "memory:/"
63
55
        if url[-1] != '/':
64
56
            url = url + '/'
65
57
        super(MemoryTransport, self).__init__(url)
66
 
        split = url.find(':') + 3
67
 
        self._scheme = url[:split]
68
 
        self._cwd = url[split:]
 
58
        self._cwd = url[url.find(':') + 1:]
69
59
        # dictionaries from absolute path to file mode
70
 
        self._dirs = {'/':None}
 
60
        self._dirs = {}
71
61
        self._files = {}
72
62
        self._locks = {}
73
63
 
74
64
    def clone(self, offset=None):
75
65
        """See Transport.clone()."""
76
 
        path = self._combine_paths(self._cwd, offset)
77
 
        if len(path) == 0 or path[-1] != '/':
78
 
            path += '/'
79
 
        url = self._scheme + path
 
66
        if offset is None:
 
67
            return copy(self)
 
68
        segments = offset.split('/')
 
69
        cwdsegments = self._cwd.split('/')[:-1]
 
70
        while len(segments):
 
71
            segment = segments.pop(0)
 
72
            if segment == '.':
 
73
                continue
 
74
            if segment == '..':
 
75
                if len(cwdsegments) > 1:
 
76
                    cwdsegments.pop()
 
77
                continue
 
78
            cwdsegments.append(segment)
 
79
        url = self.base[:self.base.find(':') + 1] + '/'.join(cwdsegments) + '/'
80
80
        result = MemoryTransport(url)
81
81
        result._dirs = self._dirs
82
82
        result._files = self._files
85
85
 
86
86
    def abspath(self, relpath):
87
87
        """See Transport.abspath()."""
88
 
        # while a little slow, this is sufficiently fast to not matter in our
89
 
        # current environment - XXX RBC 20060404 move the clone '..' handling
90
 
        # into here and call abspath from clone
91
 
        temp_t = self.clone(relpath)
92
 
        if temp_t.base.count('/') == 3:
93
 
            return temp_t.base
94
 
        else:
95
 
            return temp_t.base[:-1]
 
88
        return self.base[:-1] + self._abspath(relpath)[len(self._cwd) - 1:]
96
89
 
97
 
    def append_file(self, relpath, f, mode=None):
98
 
        """See Transport.append_file()."""
 
90
    def append(self, relpath, f):
 
91
        """See Transport.append()."""
99
92
        _abspath = self._abspath(relpath)
100
93
        self._check_parent(_abspath)
101
94
        orig_content, orig_mode = self._files.get(_abspath, ("", None))
102
 
        if mode is None:
103
 
            mode = orig_mode
104
 
        self._files[_abspath] = (orig_content + f.read(), mode)
 
95
        self._files[_abspath] = (orig_content + f.read(), orig_mode)
105
96
        return len(orig_content)
106
97
 
107
98
    def _check_parent(self, _abspath):
113
104
    def has(self, relpath):
114
105
        """See Transport.has()."""
115
106
        _abspath = self._abspath(relpath)
116
 
        return (_abspath in self._files) or (_abspath in self._dirs)
 
107
        return _abspath in self._files or _abspath in self._dirs
117
108
 
118
109
    def delete(self, relpath):
119
110
        """See Transport.delete()."""
126
117
        """See Transport.get()."""
127
118
        _abspath = self._abspath(relpath)
128
119
        if not _abspath in self._files:
129
 
            if _abspath in self._dirs:
130
 
                return LateReadError(relpath)
131
 
            else:
132
 
                raise NoSuchFile(relpath)
 
120
            raise NoSuchFile(relpath)
133
121
        return StringIO(self._files[_abspath][0])
134
122
 
135
 
    def put_file(self, relpath, f, mode=None):
136
 
        """See Transport.put_file()."""
 
123
    def put(self, relpath, f, mode=None):
 
124
        """See Transport.put()."""
137
125
        _abspath = self._abspath(relpath)
138
126
        self._check_parent(_abspath)
139
 
        bytes = f.read()
140
 
        if type(bytes) is not str:
141
 
            # Although not strictly correct, we raise UnicodeEncodeError to be
142
 
            # compatible with other transports.
143
 
            raise UnicodeEncodeError(
144
 
                'undefined', bytes, 0, 1,
145
 
                'put_file must be given a file of bytes, not unicode.')
146
 
        self._files[_abspath] = (bytes, mode)
 
127
        self._files[_abspath] = (f.read(), mode)
147
128
 
148
129
    def mkdir(self, relpath, mode=None):
149
130
        """See Transport.mkdir()."""
160
141
    def iter_files_recursive(self):
161
142
        for file in self._files:
162
143
            if file.startswith(self._cwd):
163
 
                yield urlutils.escape(file[len(self._cwd):])
 
144
                yield file[len(self._cwd):]
164
145
    
165
146
    def list_dir(self, relpath):
166
147
        """See Transport.list_dir()."""
168
149
        if _abspath != '/' and _abspath not in self._dirs:
169
150
            raise NoSuchFile(relpath)
170
151
        result = []
171
 
 
172
 
        if not _abspath.endswith('/'):
173
 
            _abspath += '/'
174
 
 
175
 
        for path_group in self._files, self._dirs:
176
 
            for path in path_group:
177
 
                if path.startswith(_abspath):
178
 
                    trailing = path[len(_abspath):]
179
 
                    if trailing and '/' not in trailing:
180
 
                        result.append(trailing)
181
 
        return map(urlutils.escape, result)
 
152
        for path in self._files:
 
153
            if (path.startswith(_abspath) and 
 
154
                path[len(_abspath) + 1:].find('/') == -1 and
 
155
                len(path) > len(_abspath)):
 
156
                result.append(path[len(_abspath) + 1:])
 
157
        for path in self._dirs:
 
158
            if (path.startswith(_abspath) and 
 
159
                path[len(_abspath) + 1:].find('/') == -1 and
 
160
                len(path) > len(_abspath) and
 
161
                path[len(_abspath)] == '/'):
 
162
                result.append(path[len(_abspath) + 1:])
 
163
        return result
182
164
 
183
165
    def rename(self, rel_from, rel_to):
184
166
        """Rename a file or directory; fail if the destination exists"""
207
189
        if _abspath in self._files:
208
190
            self._translate_error(IOError(errno.ENOTDIR, relpath), relpath)
209
191
        for path in self._files:
210
 
            if path.startswith(_abspath + '/'):
 
192
            if path.startswith(_abspath):
211
193
                self._translate_error(IOError(errno.ENOTEMPTY, relpath),
212
194
                                      relpath)
213
195
        for path in self._dirs:
214
 
            if path.startswith(_abspath + '/') and path != _abspath:
 
196
            if path.startswith(_abspath) and path != _abspath:
215
197
                self._translate_error(IOError(errno.ENOTEMPTY, relpath), relpath)
216
198
        if not _abspath in self._dirs:
217
199
            raise NoSuchFile(relpath)
223
205
        if _abspath in self._files:
224
206
            return MemoryStat(len(self._files[_abspath][0]), False, 
225
207
                              self._files[_abspath][1])
 
208
        elif _abspath == '':
 
209
            return MemoryStat(0, True, None)
226
210
        elif _abspath in self._dirs:
227
211
            return MemoryStat(0, True, self._dirs[_abspath])
228
212
        else:
238
222
 
239
223
    def _abspath(self, relpath):
240
224
        """Generate an internal absolute path."""
241
 
        relpath = urlutils.unescape(relpath)
242
225
        if relpath.find('..') != -1:
243
226
            raise AssertionError('relpath contains ..')
244
 
        if relpath == '':
245
 
            return '/'
246
 
        if relpath[0] == '/':
247
 
            return relpath
248
227
        if relpath == '.':
249
 
            if (self._cwd == '/'):
250
 
                return self._cwd
251
228
            return self._cwd[:-1]
252
229
        if relpath.endswith('/'):
253
230
            relpath = relpath[:-1]
270
247
    def __del__(self):
271
248
        # Should this warn, or actually try to cleanup?
272
249
        if self.transport:
273
 
            warnings.warn("MemoryLock %r not explicitly unlocked" % (self.path,))
 
250
            warn("MemoryLock %r not explicitly unlocked" % (self.path,))
274
251
            self.unlock()
275
252
 
276
253
    def unlock(self):
283
260
 
284
261
    def setUp(self):
285
262
        """See bzrlib.transport.Server.setUp."""
286
 
        self._dirs = {'/':None}
 
263
        self._dirs = {}
287
264
        self._files = {}
288
265
        self._locks = {}
289
 
        self._scheme = "memory+%s:///" % id(self)
 
266
        self._scheme = "memory+%s:" % id(self)
290
267
        def memory_factory(url):
291
268
            result = MemoryTransport(url)
292
269
            result._dirs = self._dirs