~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/transport/memory.py

  • Committer: John Arbash Meinel
  • Date: 2009-10-12 21:44:27 UTC
  • mto: This revision was merged to the branch mainline in revision 4737.
  • Revision ID: john@arbash-meinel.com-20091012214427-zddi1kmc2jlf7v31
Py_ssize_t and its associated function typedefs are not available w/ python 2.4

So we define them in python-compat.h
Even further, gcc issued a warning for:
static int
_workaround_pyrex_096()
So we changed it to:
_workaround_pyrex_096(void)

Also, some python api funcs were incorrectly defined as 'char *' when they meant
'const char *'. Work around that with a (char *) cast, to avoid compiler warnings.

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
 
# 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
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) + '/'
81
 
        result = MemoryTransport(url)
 
84
        path = self._combine_paths(self._cwd, offset)
 
85
        if len(path) == 0 or path[-1] != '/':
 
86
            path += '/'
 
87
        url = self._scheme + path
 
88
        result = self.__class__(url)
82
89
        result._dirs = self._dirs
83
90
        result._files = self._files
84
91
        result._locks = self._locks
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, mode=None):
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))
114
121
    def has(self, relpath):
115
122
        """See Transport.has()."""
116
123
        _abspath = self._abspath(relpath)
117
 
        return _abspath in self._files or _abspath in self._dirs
 
124
        return (_abspath in self._files) or (_abspath in self._dirs)
118
125
 
119
126
    def delete(self, relpath):
120
127
        """See Transport.delete()."""
123
130
            raise NoSuchFile(relpath)
124
131
        del self._files[_abspath]
125
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
 
126
139
    def get(self, relpath):
127
140
        """See Transport.get()."""
128
141
        _abspath = self._abspath(relpath)
129
142
        if not _abspath in self._files:
130
 
            raise NoSuchFile(relpath)
 
143
            if _abspath in self._dirs:
 
144
                return LateReadError(relpath)
 
145
            else:
 
146
                raise NoSuchFile(relpath)
131
147
        return StringIO(self._files[_abspath][0])
132
148
 
133
 
    def put(self, relpath, f, mode=None):
134
 
        """See Transport.put()."""
 
149
    def put_file(self, relpath, f, mode=None):
 
150
        """See Transport.put_file()."""
135
151
        _abspath = self._abspath(relpath)
136
152
        self._check_parent(_abspath)
137
 
        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)
138
162
 
139
163
    def mkdir(self, relpath, mode=None):
140
164
        """See Transport.mkdir()."""
144
168
            raise FileExists(relpath)
145
169
        self._dirs[_abspath]=mode
146
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
 
147
178
    def listable(self):
148
179
        """See Transport.listable."""
149
180
        return True
151
182
    def iter_files_recursive(self):
152
183
        for file in self._files:
153
184
            if file.startswith(self._cwd):
154
 
                yield file[len(self._cwd):]
155
 
    
 
185
                yield urlutils.escape(file[len(self._cwd):])
 
186
 
156
187
    def list_dir(self, relpath):
157
188
        """See Transport.list_dir()."""
158
189
        _abspath = self._abspath(relpath)
159
190
        if _abspath != '/' and _abspath not in self._dirs:
160
191
            raise NoSuchFile(relpath)
161
192
        result = []
162
 
        for path in self._files:
163
 
            if (path.startswith(_abspath) and 
164
 
                path[len(_abspath) + 1:].find('/') == -1 and
165
 
                len(path) > len(_abspath)):
166
 
                result.append(path[len(_abspath) + 1:])
167
 
        for path in self._dirs:
168
 
            if (path.startswith(_abspath) and 
169
 
                path[len(_abspath) + 1:].find('/') == -1 and
170
 
                len(path) > len(_abspath) and
171
 
                path[len(_abspath)] == '/'):
172
 
                result.append(path[len(_abspath) + 1:])
173
 
        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)
174
204
 
175
205
    def rename(self, rel_from, rel_to):
176
206
        """Rename a file or directory; fail if the destination exists"""
192
222
                    del container[path]
193
223
        do_renames(self._files)
194
224
        do_renames(self._dirs)
195
 
    
 
225
 
196
226
    def rmdir(self, relpath):
197
227
        """See Transport.rmdir."""
198
228
        _abspath = self._abspath(relpath)
199
229
        if _abspath in self._files:
200
230
            self._translate_error(IOError(errno.ENOTDIR, relpath), relpath)
201
231
        for path in self._files:
202
 
            if path.startswith(_abspath):
 
232
            if path.startswith(_abspath + '/'):
203
233
                self._translate_error(IOError(errno.ENOTEMPTY, relpath),
204
234
                                      relpath)
205
235
        for path in self._dirs:
206
 
            if path.startswith(_abspath) and path != _abspath:
 
236
            if path.startswith(_abspath + '/') and path != _abspath:
207
237
                self._translate_error(IOError(errno.ENOTEMPTY, relpath), relpath)
208
238
        if not _abspath in self._dirs:
209
239
            raise NoSuchFile(relpath)
213
243
        """See Transport.stat()."""
214
244
        _abspath = self._abspath(relpath)
215
245
        if _abspath in self._files:
216
 
            return MemoryStat(len(self._files[_abspath][0]), False, 
 
246
            return MemoryStat(len(self._files[_abspath][0]), False,
217
247
                              self._files[_abspath][1])
218
 
        elif _abspath == '':
219
 
            return MemoryStat(0, True, None)
220
248
        elif _abspath in self._dirs:
221
249
            return MemoryStat(0, True, self._dirs[_abspath])
222
250
        else:
232
260
 
233
261
    def _abspath(self, relpath):
234
262
        """Generate an internal absolute path."""
235
 
        if relpath.find('..') != -1:
236
 
            raise AssertionError('relpath contains ..')
237
 
        if relpath == '.':
238
 
            return self._cwd[:-1]
239
 
        if relpath.endswith('/'):
240
 
            relpath = relpath[:-1]
241
 
        if relpath.startswith('./'):
242
 
            relpath = relpath[2:]
243
 
        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)
244
280
 
245
281
 
246
282
class _MemoryLock(object):
247
283
    """This makes a lock."""
248
284
 
249
285
    def __init__(self, path, transport):
250
 
        assert isinstance(transport, MemoryTransport)
251
286
        self.path = path
252
287
        self.transport = transport
253
288
        if self.path in self.transport._locks:
257
292
    def __del__(self):
258
293
        # Should this warn, or actually try to cleanup?
259
294
        if self.transport:
260
 
            warn("MemoryLock %r not explicitly unlocked" % (self.path,))
 
295
            warnings.warn("MemoryLock %r not explicitly unlocked" % (self.path,))
261
296
            self.unlock()
262
297
 
263
298
    def unlock(self):
270
305
 
271
306
    def setUp(self):
272
307
        """See bzrlib.transport.Server.setUp."""
273
 
        self._dirs = {}
 
308
        self._dirs = {'/':None}
274
309
        self._files = {}
275
310
        self._locks = {}
276
 
        self._scheme = "memory+%s:" % id(self)
 
311
        self._scheme = "memory+%s:///" % id(self)
277
312
        def memory_factory(url):
278
313
            result = MemoryTransport(url)
279
314
            result._dirs = self._dirs