~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/transport/memory.py

Merged mailine

Show diffs side-by-side

added added

removed removed

Lines of Context:
15
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
16
"""Implementation of Transport that uses memory for its storage."""
17
17
 
 
18
from copy import copy
18
19
import os
19
20
import errno
 
21
from stat import *
20
22
from cStringIO import StringIO
21
23
 
22
24
from bzrlib.trace import mutter
23
25
from bzrlib.errors import TransportError, NoSuchFile, FileExists
24
 
from bzrlib.transport import Transport
 
26
from bzrlib.transport import Transport, register_transport, Server
25
27
 
26
28
class MemoryStat(object):
27
29
 
28
 
    def __init__(self, size):
 
30
    def __init__(self, size, is_dir, perms):
29
31
        self.st_size = size
 
32
        if perms is None:
 
33
            perms = 0644
 
34
        if not is_dir:
 
35
            self.st_mode = S_IFREG | perms
 
36
        else:
 
37
            self.st_mode = S_IFDIR | perms
30
38
 
31
39
 
32
40
class MemoryTransport(Transport):
33
41
    """This is the transport agent for local filesystem access."""
34
42
 
35
 
    def __init__(self):
 
43
    def __init__(self, url=""):
36
44
        """Set the 'base' path where files will be stored."""
37
 
        super(MemoryTransport, self).__init__('in-memory:')
38
 
        self._dirs = set()
 
45
        if url == "":
 
46
            url = "memory:/"
 
47
        if url[-1] != '/':
 
48
            url = url + '/'
 
49
        super(MemoryTransport, self).__init__(url)
 
50
        self._cwd = url[url.find(':') + 1:]
 
51
        self._dirs = {}
39
52
        self._files = {}
40
53
 
41
54
    def clone(self, offset=None):
42
55
        """See Transport.clone()."""
43
 
        return self
 
56
        if offset is None:
 
57
            return copy(self)
 
58
        segments = offset.split('/')
 
59
        cwdsegments = self._cwd.split('/')[:-1]
 
60
        while len(segments):
 
61
            segment = segments.pop(0)
 
62
            if segment == '.':
 
63
                continue
 
64
            if segment == '..':
 
65
                if len(cwdsegments) > 1:
 
66
                    cwdsegments.pop()
 
67
                continue
 
68
            cwdsegments.append(segment)
 
69
        url = self.base[:self.base.find(':') + 1] + '/'.join(cwdsegments) + '/'
 
70
        result = MemoryTransport(url)
 
71
        result._dirs = self._dirs
 
72
        result._files = self._files
 
73
        return result
44
74
 
45
75
    def abspath(self, relpath):
46
76
        """See Transport.abspath()."""
47
 
        return self.base + relpath
 
77
        return self.base[:-1] + self._abspath(relpath)
48
78
 
49
79
    def append(self, relpath, f):
50
80
        """See Transport.append()."""
51
 
        self._check_parent(relpath)
52
 
        self._files[relpath] = self._files.get(relpath, "") + f.read()
 
81
        _abspath = self._abspath(relpath)
 
82
        self._check_parent(_abspath)
 
83
        orig_content, orig_mode = self._files.get(_abspath, ("", None))
 
84
        self._files[_abspath] = (orig_content + f.read(), orig_mode)
53
85
 
54
 
    def _check_parent(self, relpath):
55
 
        dir = os.path.dirname(relpath)
56
 
        if dir != '':
 
86
    def _check_parent(self, _abspath):
 
87
        dir = os.path.dirname(_abspath)
 
88
        if dir != '/':
57
89
            if not dir in self._dirs:
58
 
                raise NoSuchFile(relpath)
 
90
                raise NoSuchFile(_abspath)
59
91
 
60
92
    def has(self, relpath):
61
93
        """See Transport.has()."""
62
 
        return relpath in self._files
 
94
        _abspath = self._abspath(relpath)
 
95
        return _abspath in self._files or _abspath in self._dirs
 
96
 
 
97
    def delete(self, relpath):
 
98
        """See Transport.delete()."""
 
99
        _abspath = self._abspath(relpath)
 
100
        if not _abspath in self._files:
 
101
            raise NoSuchFile(relpath)
 
102
        del self._files[_abspath]
63
103
 
64
104
    def get(self, relpath):
65
105
        """See Transport.get()."""
66
 
        if not relpath in self._files:
 
106
        _abspath = self._abspath(relpath)
 
107
        if not _abspath in self._files:
67
108
            raise NoSuchFile(relpath)
68
 
        return StringIO(self._files[relpath])
 
109
        return StringIO(self._files[_abspath][0])
69
110
 
70
 
    def put(self, relpath, f):
 
111
    def put(self, relpath, f, mode=None):
71
112
        """See Transport.put()."""
72
 
        self._check_parent(relpath)
73
 
        self._files[relpath] = f.read()
 
113
        _abspath = self._abspath(relpath)
 
114
        self._check_parent(_abspath)
 
115
        self._files[_abspath] = (f.read(), mode)
74
116
 
75
 
    def mkdir(self, relpath):
 
117
    def mkdir(self, relpath, mode=None):
76
118
        """See Transport.mkdir()."""
77
 
        self._check_parent(relpath)
78
 
        if relpath in self._dirs:
 
119
        _abspath = self._abspath(relpath)
 
120
        self._check_parent(_abspath)
 
121
        if _abspath in self._dirs:
79
122
            raise FileExists(relpath)
80
 
        self._dirs.add(relpath)
 
123
        self._dirs[_abspath]=mode
81
124
 
82
125
    def listable(self):
83
126
        """See Transport.listable."""
84
127
        return True
85
128
 
86
129
    def iter_files_recursive(self):
87
 
        return iter(self._files)
 
130
        for file in self._files:
 
131
            if file.startswith(self._cwd):
 
132
                yield file[len(self._cwd):]
88
133
    
89
 
#    def list_dir(self, relpath):
90
 
#    TODO if needed
 
134
    def list_dir(self, relpath):
 
135
        """See Transport.list_dir()."""
 
136
        _abspath = self._abspath(relpath)
 
137
        if _abspath != '/' and _abspath not in self._dirs:
 
138
            raise NoSuchFile(relpath)
 
139
        result = []
 
140
        for path in self._files:
 
141
            if (path.startswith(_abspath) and 
 
142
                path[len(_abspath) + 1:].find('/') == -1 and
 
143
                len(path) > len(_abspath)):
 
144
                result.append(path[len(_abspath) + 1:])
 
145
        for path in self._dirs:
 
146
            if (path.startswith(_abspath) and 
 
147
                path[len(_abspath) + 1:].find('/') == -1 and
 
148
                len(path) > len(_abspath)):
 
149
                result.append(path[len(_abspath) + 1:])
 
150
        return result
91
151
    
92
152
    def stat(self, relpath):
93
153
        """See Transport.stat()."""
94
 
        return MemoryStat(len(self._files[relpath]))
 
154
        _abspath = self._abspath(relpath)
 
155
        if _abspath in self._files:
 
156
            return MemoryStat(len(self._files[_abspath][0]), False, 
 
157
                              self._files[_abspath][1])
 
158
        elif _abspath == '':
 
159
            return MemoryStat(0, True, None)
 
160
        elif _abspath in self._dirs:
 
161
            return MemoryStat(0, True, self._dirs[_abspath])
 
162
        else:
 
163
            raise NoSuchFile(relpath)
95
164
 
96
165
#    def lock_read(self, relpath):
97
166
#   TODO if needed
98
167
#
99
168
#    def lock_write(self, relpath):
100
169
#   TODO if needed
 
170
 
 
171
    def _abspath(self, relpath):
 
172
        """Generate an internal absolute path."""
 
173
        if relpath.find('..') != -1:
 
174
            raise AssertionError('relpath contains ..')
 
175
        if relpath == '.':
 
176
            return self._cwd[:-1]
 
177
        if relpath.endswith('/'):
 
178
            relpath = relpath[:-1]
 
179
        return self._cwd + relpath
 
180
 
 
181
 
 
182
class MemoryServer(Server):
 
183
    """Server for the MemoryTransport for testing with."""
 
184
 
 
185
    def setUp(self):
 
186
        """See bzrlib.transport.Server.setUp."""
 
187
        self._scheme = "memory+%s:" % id(self)
 
188
        register_transport(self._scheme, MemoryTransport)
 
189
 
 
190
    def tearDown(self):
 
191
        """See bzrlib.transport.Server.tearDown."""
 
192
        # unregister this server
 
193
 
 
194
    def get_url(self):
 
195
        """See bzrlib.transport.Server.get_url."""
 
196
        return self._scheme
 
197
 
 
198
 
 
199
def get_test_permutations():
 
200
    """Return the permutations to be used in testing."""
 
201
    return [(MemoryTransport, MemoryServer),
 
202
            ]