~azzar1/unity/add-show-desktop-key

1079 by William Grant
Merge setup-refactor branch. This completely breaks existing installations;
1
# IVLE - Informatics Virtual Learning Environment
2
# Copyright (C) 2007-2008 The University of Melbourne
3
#
4
# This program is free software; you can redistribute it and/or modify
5
# it under the terms of the GNU General Public License as published by
6
# the Free Software Foundation; either version 2 of the License, or
7
# (at your option) any later version.
8
#
9
# This program is distributed in the hope that it will be useful,
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
# GNU General Public License for more details.
13
#
14
# You should have received a copy of the GNU General Public License
15
# along with this program; if not, write to the Free Software
16
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
17
18
# Module: TestFilespace
19
# Author: Dilshan Angampitiya
20
#         Steven Bird (revisions)
21
#         David Coles (revisions and moved to module)
22
# Date:   24/1/2008
23
24
import StringIO
25
26
class TestFilespace:
27
    """
28
    Our dummy file system which is accessed by code being tested.
29
    Implemented as a dictionary which maps filenames to strings
30
    """
31
    def __init__(self, files=None):
32
        "Initialise, optionally with filename-filedata pairs"
33
34
        if files == None:
35
            files = {}
36
37
        # dict mapping files to strings
38
        self._files = {}
39
        self._files.update(files)
40
        # set of file names
41
        self._modified_files = set([])
42
        # dict mapping files to stringIO objects
43
        self._open_files = {}
44
45
    def add_file(self, filename, data):
46
        " Add a file to the filespace "
47
        self._files[filename] = data
48
49
    def openfile(self, filename, mode='r'):
50
        """ Open a file from the filespace with the given mode.
51
        Return a StringIO subclass object with the file contents.
52
        """
53
        # currently very messy, needs to be cleaned up
54
        # Probably most of this should be in the initialiser to the TestStringIO
55
        
56
        import re
57
58
        if filename in self._open_files:
59
            raise IOError("File already open: %s" %filename)
60
61
        if not re.compile("[rwa][+b]{0,2}").match(mode):
62
            raise IOError("invalid mode %s" %mode)
63
        
64
        ## TODO: validate filename?
65
        
66
        mode.replace("b",'')
67
        
68
        # initialise the file properly (truncate/create if required)
69
        if mode[0] == 'w':
70
            self._files[filename] = ''
71
            self._modified_files.add(filename)
72
        elif filename not in self._files:
73
            if mode[0] == 'a':
74
                self._files[filename] = ''
75
                self._modified_files.add(filename)
76
            else:
77
                raise IOError(2, "Access to file denied: %s" %filename)
78
79
        # for append mode, remember the existing data
80
        if mode[0] == 'a':
81
            existing_data = self._files[filename]
82
        else:
83
            existing_data = ""
84
85
        # determine what operations are allowed
86
        reading_ok = (len(mode) == 2 or mode[0] == 'r')
87
        writing_ok = (len(mode) == 2 or mode[0] in 'wa')
88
89
        # for all writing modes, start off with blank file
90
        if mode[0] == 'w':
91
            initial_data = ''
92
        else:
93
            initial_data = self._files[filename]
94
95
        file_object = TestStringIO(initial_data, filename, self, reading_ok, writing_ok, existing_data)
96
        self._open_files[filename] = file_object
97
        
98
        return file_object
99
100
    def flush_all(self):
101
        """ Flush all open files
102
        """
103
        for file_object in self._open_files.values():
104
            file_object.flush()
105
106
    def updatefile(self,filename, data):
107
        """ Callback function used by an open file to inform when it has been updated.
108
        """
109
        if filename in self._open_files:
110
            self._files[filename] = data
111
            if self._open_files[filename].is_modified():
112
                self._modified_files.add(filename)
113
        else:
114
            raise IOError(2, "Access to file denied: %s" %filename)
115
116
    def closefile(self, filename):
117
        """ Callback function used by an open file to inform when it has been closed.
118
        """
119
        if filename in self._open_files:
120
            del self._open_files[filename]
121
122
    def get_modified_files(self):
123
        """" A subset of the filespace containing only those files which have been
124
        modified
125
        """
126
        modified_files = {}
127
        for filename in self._modified_files:
128
            modified_files[filename] = self._files[filename]
129
130
        return modified_files
131
132
    def get_open_files(self):
133
        " Return the names of all open files "
134
        return self._open_files.keys()
135
            
136
    def copy(self):
137
        """ Return a copy of the current filespace.
138
        Only the files are copied, not the modified or open file lists.
139
        """
140
        self.flush_all()
141
        return TestFilespace(self._files)
142
143
class TestStringIO(StringIO.StringIO):
144
    """
145
    A subclass of StringIO which acts as a file in our dummy file system
146
    """
147
    def __init__(self, string, filename, filespace, reading_ok, writing_ok, existing_data):
148
        """ Initialise with the filedata, file name and infomation on what ops are
149
        acceptable """
150
        StringIO.StringIO.__init__(self, string)
151
        self._filename = filename
152
        self._filespace = filespace
153
        self._reading_ok = reading_ok
154
        self._writing_ok = writing_ok
155
        self._existing_data = existing_data
156
        self._modified = False
157
        self._open = True
158
159
    # Override all standard file ops. Make sure that they are valid with the given
160
    # permissions and if so then call the corresponding method in StringIO
161
    
162
    def read(self, *args):
163
        if not self._reading_ok:
164
            raise IOError(9, "Bad file descriptor")
165
        else:
166
            return StringIO.StringIO.read(self, *args)
167
168
    def readline(self, *args):
169
        if not self._reading_ok:
170
            raise IOError(9, "Bad file descriptor")
171
        else:
172
            return StringIO.StringIO.readline(self, *args)
173
174
    def readlines(self, *args):
175
        if not self._reading_ok:
176
            raise IOError(9, "Bad file descriptor")
177
        else:
178
            return StringIO.StringIO.readlines(self, *args)
179
180
    def seek(self, *args):
181
        if not self._reading_ok:
182
            raise IOError(9, "Bad file descriptor")
183
        else:
184
            return StringIO.StringIO.seek(self, *args)
185
186
    def truncate(self, *args):
187
        self._modified = True
188
        if not self._writing_ok:
189
            raise IOError(9, "Bad file descriptor")
190
        else:
191
            return StringIO.StringIO.truncate(self, *args)
192
        
193
    def write(self, *args):
194
        self._modified = True
195
        if not self._writing_ok:
196
            raise IOError(9, "Bad file descriptor")
197
        else:
198
            return StringIO.StringIO.write(self, *args)
199
200
    def writelines(self, *args):
201
        self._modified = True
202
        if not self._writing_ok:
203
            raise IOError(9, "Bad file descriptor")
204
        else:
205
            return StringIO.StringIO.writelines(self, *args)
206
207
    def is_modified(self):
208
        " Return true if the file has been written to, or truncated"
209
        return self._modified
210
        
211
    def flush(self):
212
        " Update the contents of the filespace with the new data "
213
        self._filespace.updatefile(self._filename, self._existing_data+self.getvalue())
214
        return StringIO.StringIO.flush(self)
215
216
    def close(self):
217
        " Flush the file and close it "
218
        self.flush()
219
        self._filespace.closefile(self._filename)
220
        return StringIO.StringIO.close(self)
221