~bzr-pqm/bzr/bzr.dev

1442.1.44 by Robert Collins
Many transport related tweaks:
1
# Copyright (C) 2005 Canonical Ltd
2
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
7
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
# GNU General Public License for more details.
12
13
# You should have received a copy of the GNU General Public License
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
16
"""Implementation of Transport that uses memory for its storage."""
17
1530.1.3 by Robert Collins
transport implementations now tested consistently.
18
from copy import copy
1442.1.44 by Robert Collins
Many transport related tweaks:
19
import os
20
import errno
1530.1.3 by Robert Collins
transport implementations now tested consistently.
21
from stat import *
1442.1.44 by Robert Collins
Many transport related tweaks:
22
from cStringIO import StringIO
23
24
from bzrlib.trace import mutter
1185.31.44 by John Arbash Meinel
Cleaned up Exceptions for all transports.
25
from bzrlib.errors import TransportError, NoSuchFile, FileExists
1530.1.3 by Robert Collins
transport implementations now tested consistently.
26
from bzrlib.transport import Transport, register_transport, Server
1442.1.44 by Robert Collins
Many transport related tweaks:
27
28
class MemoryStat(object):
29
1530.1.15 by Robert Collins
Move put mode tests into test_transport_implementation.
30
    def __init__(self, size, is_dir, perms):
1442.1.44 by Robert Collins
Many transport related tweaks:
31
        self.st_size = size
1530.1.15 by Robert Collins
Move put mode tests into test_transport_implementation.
32
        if perms is None:
33
            perms = 0644
1530.1.3 by Robert Collins
transport implementations now tested consistently.
34
        if not is_dir:
1530.1.15 by Robert Collins
Move put mode tests into test_transport_implementation.
35
            self.st_mode = S_IFREG | perms
1530.1.3 by Robert Collins
transport implementations now tested consistently.
36
        else:
1530.1.15 by Robert Collins
Move put mode tests into test_transport_implementation.
37
            self.st_mode = S_IFDIR | perms
1442.1.44 by Robert Collins
Many transport related tweaks:
38
39
40
class MemoryTransport(Transport):
41
    """This is the transport agent for local filesystem access."""
42
1530.1.3 by Robert Collins
transport implementations now tested consistently.
43
    def __init__(self, url=""):
1442.1.44 by Robert Collins
Many transport related tweaks:
44
        """Set the 'base' path where files will be stored."""
1530.1.3 by Robert Collins
transport implementations now tested consistently.
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:]
1530.1.16 by Robert Collins
Move mkdir and copy_to permissions tests to test_transport_impleentation.
51
        self._dirs = {}
1442.1.44 by Robert Collins
Many transport related tweaks:
52
        self._files = {}
53
54
    def clone(self, offset=None):
55
        """See Transport.clone()."""
1530.1.3 by Robert Collins
transport implementations now tested consistently.
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
1442.1.44 by Robert Collins
Many transport related tweaks:
74
75
    def abspath(self, relpath):
76
        """See Transport.abspath()."""
1530.1.3 by Robert Collins
transport implementations now tested consistently.
77
        return self.base[:-1] + self._abspath(relpath)
1442.1.44 by Robert Collins
Many transport related tweaks:
78
79
    def append(self, relpath, f):
80
        """See Transport.append()."""
1530.1.3 by Robert Collins
transport implementations now tested consistently.
81
        _abspath = self._abspath(relpath)
82
        self._check_parent(_abspath)
1530.1.15 by Robert Collins
Move put mode tests into test_transport_implementation.
83
        orig_content, orig_mode = self._files.get(_abspath, ("", None))
84
        self._files[_abspath] = (orig_content + f.read(), orig_mode)
1442.1.44 by Robert Collins
Many transport related tweaks:
85
1530.1.3 by Robert Collins
transport implementations now tested consistently.
86
    def _check_parent(self, _abspath):
87
        dir = os.path.dirname(_abspath)
88
        if dir != '/':
1442.1.44 by Robert Collins
Many transport related tweaks:
89
            if not dir in self._dirs:
1530.1.3 by Robert Collins
transport implementations now tested consistently.
90
                raise NoSuchFile(_abspath)
1442.1.44 by Robert Collins
Many transport related tweaks:
91
92
    def has(self, relpath):
93
        """See Transport.has()."""
1530.1.3 by Robert Collins
transport implementations now tested consistently.
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]
1442.1.44 by Robert Collins
Many transport related tweaks:
103
104
    def get(self, relpath):
105
        """See Transport.get()."""
1530.1.3 by Robert Collins
transport implementations now tested consistently.
106
        _abspath = self._abspath(relpath)
107
        if not _abspath in self._files:
1442.1.44 by Robert Collins
Many transport related tweaks:
108
            raise NoSuchFile(relpath)
1530.1.15 by Robert Collins
Move put mode tests into test_transport_implementation.
109
        return StringIO(self._files[_abspath][0])
1442.1.44 by Robert Collins
Many transport related tweaks:
110
1185.58.4 by John Arbash Meinel
Added permission checking to Branch, and propogated that change into the stores.
111
    def put(self, relpath, f, mode=None):
1442.1.44 by Robert Collins
Many transport related tweaks:
112
        """See Transport.put()."""
1530.1.3 by Robert Collins
transport implementations now tested consistently.
113
        _abspath = self._abspath(relpath)
114
        self._check_parent(_abspath)
1530.1.15 by Robert Collins
Move put mode tests into test_transport_implementation.
115
        self._files[_abspath] = (f.read(), mode)
1442.1.44 by Robert Collins
Many transport related tweaks:
116
1185.58.4 by John Arbash Meinel
Added permission checking to Branch, and propogated that change into the stores.
117
    def mkdir(self, relpath, mode=None):
1442.1.44 by Robert Collins
Many transport related tweaks:
118
        """See Transport.mkdir()."""
1530.1.3 by Robert Collins
transport implementations now tested consistently.
119
        _abspath = self._abspath(relpath)
120
        self._check_parent(_abspath)
121
        if _abspath in self._dirs:
1442.1.44 by Robert Collins
Many transport related tweaks:
122
            raise FileExists(relpath)
1530.1.16 by Robert Collins
Move mkdir and copy_to permissions tests to test_transport_impleentation.
123
        self._dirs[_abspath]=mode
1442.1.44 by Robert Collins
Many transport related tweaks:
124
125
    def listable(self):
126
        """See Transport.listable."""
127
        return True
128
129
    def iter_files_recursive(self):
1530.1.3 by Robert Collins
transport implementations now tested consistently.
130
        for file in self._files:
131
            if file.startswith(self._cwd):
132
                yield file[len(self._cwd):]
1442.1.44 by Robert Collins
Many transport related tweaks:
133
    
1530.1.3 by Robert Collins
transport implementations now tested consistently.
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
1442.1.44 by Robert Collins
Many transport related tweaks:
151
    
152
    def stat(self, relpath):
153
        """See Transport.stat()."""
1530.1.3 by Robert Collins
transport implementations now tested consistently.
154
        _abspath = self._abspath(relpath)
155
        if _abspath in self._files:
1530.1.15 by Robert Collins
Move put mode tests into test_transport_implementation.
156
            return MemoryStat(len(self._files[_abspath][0]), False, 
157
                              self._files[_abspath][1])
1530.1.16 by Robert Collins
Move mkdir and copy_to permissions tests to test_transport_impleentation.
158
        elif _abspath == '':
1530.1.15 by Robert Collins
Move put mode tests into test_transport_implementation.
159
            return MemoryStat(0, True, None)
1530.1.16 by Robert Collins
Move mkdir and copy_to permissions tests to test_transport_impleentation.
160
        elif _abspath in self._dirs:
161
            return MemoryStat(0, True, self._dirs[_abspath])
1530.1.3 by Robert Collins
transport implementations now tested consistently.
162
        else:
163
            raise NoSuchFile(relpath)
1442.1.44 by Robert Collins
Many transport related tweaks:
164
165
#    def lock_read(self, relpath):
166
#   TODO if needed
167
#
168
#    def lock_write(self, relpath):
169
#   TODO if needed
1530.1.3 by Robert Collins
transport implementations now tested consistently.
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
1530.1.11 by Robert Collins
Push the transport permutations list into each transport module allowing for automatic testing of new modules that are registered as transports.
197
198
199
def get_test_permutations():
200
    """Return the permutations to be used in testing."""
201
    return [(MemoryTransport, MemoryServer),
202
            ]