~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/transport/memory.py

Merge bzr.dev

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
31
from bzrlib.trace import mutter
31
 
from bzrlib.errors import TransportError, NoSuchFile, FileExists, LockError
32
 
from bzrlib.transport import Transport, register_transport, Server
 
32
from bzrlib.transport import (Transport, register_transport, Server)
 
33
import bzrlib.urlutils as urlutils
 
34
 
33
35
 
34
36
 
35
37
class MemoryStat(object):
52
54
    def __init__(self, url=""):
53
55
        """Set the 'base' path where files will be stored."""
54
56
        if url == "":
55
 
            url = "memory:/"
 
57
            url = "memory:///"
56
58
        if url[-1] != '/':
57
59
            url = url + '/'
58
60
        super(MemoryTransport, self).__init__(url)
59
 
        self._cwd = url[url.find(':') + 1:]
 
61
        split = url.find(':') + 3
 
62
        self._scheme = url[:split]
 
63
        self._cwd = url[split:]
60
64
        # dictionaries from absolute path to file mode
61
 
        self._dirs = {}
 
65
        self._dirs = {'/':None}
62
66
        self._files = {}
63
67
        self._locks = {}
64
68
 
65
69
    def clone(self, offset=None):
66
70
        """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) + '/'
 
71
        path = self._combine_paths(self._cwd, offset)
 
72
        if len(path) == 0 or path[-1] != '/':
 
73
            path += '/'
 
74
        url = self._scheme + path
81
75
        result = MemoryTransport(url)
82
76
        result._dirs = self._dirs
83
77
        result._files = self._files
90
84
        # current environment - XXX RBC 20060404 move the clone '..' handling
91
85
        # into here and call abspath from clone
92
86
        temp_t = self.clone(relpath)
93
 
        if temp_t.base.count('/') == 1:
 
87
        if temp_t.base.count('/') == 3:
94
88
            return temp_t.base
95
89
        else:
96
90
            return temp_t.base[:-1]
97
91
 
98
 
    def append(self, relpath, f, mode=None):
99
 
        """See Transport.append()."""
 
92
    def append_file(self, relpath, f, mode=None):
 
93
        """See Transport.append_file()."""
100
94
        _abspath = self._abspath(relpath)
101
95
        self._check_parent(_abspath)
102
96
        orig_content, orig_mode = self._files.get(_abspath, ("", None))
114
108
    def has(self, relpath):
115
109
        """See Transport.has()."""
116
110
        _abspath = self._abspath(relpath)
117
 
        return _abspath in self._files or _abspath in self._dirs
 
111
        return (_abspath in self._files) or (_abspath in self._dirs)
118
112
 
119
113
    def delete(self, relpath):
120
114
        """See Transport.delete()."""
130
124
            raise NoSuchFile(relpath)
131
125
        return StringIO(self._files[_abspath][0])
132
126
 
133
 
    def put(self, relpath, f, mode=None):
134
 
        """See Transport.put()."""
 
127
    def put_file(self, relpath, f, mode=None):
 
128
        """See Transport.put_file()."""
135
129
        _abspath = self._abspath(relpath)
136
130
        self._check_parent(_abspath)
137
131
        self._files[_abspath] = (f.read(), mode)
151
145
    def iter_files_recursive(self):
152
146
        for file in self._files:
153
147
            if file.startswith(self._cwd):
154
 
                yield file[len(self._cwd):]
 
148
                yield urlutils.escape(file[len(self._cwd):])
155
149
    
156
150
    def list_dir(self, relpath):
157
151
        """See Transport.list_dir()."""
159
153
        if _abspath != '/' and _abspath not in self._dirs:
160
154
            raise NoSuchFile(relpath)
161
155
        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
 
156
 
 
157
        if not _abspath.endswith('/'):
 
158
            _abspath += '/'
 
159
 
 
160
        for path_group in self._files, self._dirs:
 
161
            for path in path_group:
 
162
                if path.startswith(_abspath):
 
163
                    trailing = path[len(_abspath):]
 
164
                    if trailing and '/' not in trailing:
 
165
                        result.append(trailing)
 
166
        return map(urlutils.escape, result)
174
167
 
175
168
    def rename(self, rel_from, rel_to):
176
169
        """Rename a file or directory; fail if the destination exists"""
215
208
        if _abspath in self._files:
216
209
            return MemoryStat(len(self._files[_abspath][0]), False, 
217
210
                              self._files[_abspath][1])
218
 
        elif _abspath == '':
219
 
            return MemoryStat(0, True, None)
220
211
        elif _abspath in self._dirs:
221
212
            return MemoryStat(0, True, self._dirs[_abspath])
222
213
        else:
232
223
 
233
224
    def _abspath(self, relpath):
234
225
        """Generate an internal absolute path."""
 
226
        relpath = urlutils.unescape(relpath)
235
227
        if relpath.find('..') != -1:
236
228
            raise AssertionError('relpath contains ..')
 
229
        if relpath == '':
 
230
            return '/'
 
231
        if relpath[0] == '/':
 
232
            return relpath
237
233
        if relpath == '.':
 
234
            if (self._cwd == '/'):
 
235
                return self._cwd
238
236
            return self._cwd[:-1]
239
237
        if relpath.endswith('/'):
240
238
            relpath = relpath[:-1]
257
255
    def __del__(self):
258
256
        # Should this warn, or actually try to cleanup?
259
257
        if self.transport:
260
 
            warn("MemoryLock %r not explicitly unlocked" % (self.path,))
 
258
            warnings.warn("MemoryLock %r not explicitly unlocked" % (self.path,))
261
259
            self.unlock()
262
260
 
263
261
    def unlock(self):
270
268
 
271
269
    def setUp(self):
272
270
        """See bzrlib.transport.Server.setUp."""
273
 
        self._dirs = {}
 
271
        self._dirs = {'/':None}
274
272
        self._files = {}
275
273
        self._locks = {}
276
 
        self._scheme = "memory+%s:" % id(self)
 
274
        self._scheme = "memory+%s:///" % id(self)
277
275
        def memory_factory(url):
278
276
            result = MemoryTransport(url)
279
277
            result._dirs = self._dirs