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

« back to all changes in this revision

Viewing changes to ivle/util.py

  • Committer: William Grant
  • Date: 2012-06-28 01:52:02 UTC
  • Revision ID: me@williamgrant.id.au-20120628015202-f6ru7o367gt6nvgz
Hah

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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: util
 
19
# Author: Matt Giuca
 
20
# Date: 12/12/2007
 
21
 
 
22
# Contains common utility functions.
 
23
 
 
24
import os
 
25
import sys
 
26
import stat
 
27
 
 
28
class IVLEJailError(Exception):
 
29
    """Exception proxying an in-jail error.
 
30
 
 
31
    This exception indicates an error that occurred inside an IVLE CGI script
 
32
    inside the jail. It should never be raised directly - only by the 
 
33
    interpreter.
 
34
 
 
35
    Information will be retrieved from it, and then treated as a normal
 
36
    error.
 
37
    """
 
38
    def __init__(self, type_str, message, info):
 
39
        self.type_str = type_str
 
40
        self.message = message
 
41
        self.info = info
 
42
 
 
43
class FakeObject(object):
 
44
    """ A representation of an object that can't be Pickled """
 
45
    def __init__(self, type, name, attrib={}):
 
46
        self.type = type
 
47
        self.name = name
 
48
        self.attrib = attrib
 
49
 
 
50
    def __repr__(self):
 
51
        return "<Fake %s %s>"%(self.type, self.name)
 
52
 
 
53
def split_path(path):
 
54
    """Given a path, returns a tuple consisting of the top-level directory in
 
55
    the path, and the rest of the path. Note that both items in the tuple will
 
56
    NOT begin with a slash, regardless of whether the original path did. Also
 
57
    normalises the path.
 
58
 
 
59
    Always returns a pair of strings, except for one special case, in which
 
60
    the path is completely empty (or just a single slash). In this case the
 
61
    return value will be (None, ''). But still always returns a pair.
 
62
 
 
63
    Examples:
 
64
 
 
65
    >>> split_path("")
 
66
    (None, '')
 
67
    >>> split_path("/")
 
68
    (None, '')
 
69
    >>> split_path("home")
 
70
    ('home', '')
 
71
    >>> split_path("home/docs/files")
 
72
    ('home', 'docs/files')
 
73
    >>> split_path("//home/docs/files")
 
74
    ('', 'home/docs/files')
 
75
    """
 
76
    path = os.path.normpath(path)
 
77
    # Ignore the opening slash
 
78
    if path.startswith(os.sep):
 
79
        path = path[len(os.sep):]
 
80
    if path == '' or path == '.':
 
81
        return (None, '')
 
82
    splitpath = path.split(os.sep, 1)
 
83
    if len(splitpath) == 1:
 
84
        return (splitpath[0], '')
 
85
    else:
 
86
        return tuple(splitpath)
 
87
 
 
88
def relpath(path, start=os.path.curdir):
 
89
    """Return a relative version of a path.
 
90
    XXX Backported from Python 2.6 posixpath.py.
 
91
    """
 
92
 
 
93
    if not path:
 
94
        raise ValueError("no path specified")
 
95
 
 
96
    start_list = os.path.abspath(start).split(os.path.sep)
 
97
    path_list = os.path.abspath(path).split(os.path.sep)
 
98
 
 
99
    # Work out how much of the filepath is shared by start and path.
 
100
    i = len(os.path.commonprefix([start_list, path_list]))
 
101
 
 
102
    rel_list = [os.path.pardir] * (len(start_list)-i) + path_list[i:]
 
103
    if not rel_list:
 
104
        return os.path.curdir
 
105
    return os.path.join(*rel_list)
 
106
 
 
107
def incomplete_utf8_sequence(byteseq):
 
108
    """Calculate the completeness of a UTF-8 encoded string.
 
109
 
 
110
    Given a UTF-8-encoded byte sequence (str), returns the number of bytes at
 
111
    the end of the string which comprise an incomplete UTF-8 character
 
112
    sequence.
 
113
 
 
114
    If the string is empty or ends with a complete character OR INVALID
 
115
    sequence, returns 0.
 
116
    Otherwise, returns 1-3 indicating the number of bytes in the final
 
117
    incomplete (but valid) character sequence.
 
118
 
 
119
    Does not check any bytes before the final sequence for correctness.
 
120
 
 
121
    >>> incomplete_utf8_sequence("")
 
122
    0
 
123
    >>> incomplete_utf8_sequence("xy")
 
124
    0
 
125
    >>> incomplete_utf8_sequence("xy\xc3\xbc")
 
126
    0
 
127
    >>> incomplete_utf8_sequence("\xc3")
 
128
    1
 
129
    >>> incomplete_utf8_sequence("\xbc\xc3")
 
130
    1
 
131
    >>> incomplete_utf8_sequence("xy\xbc\xc3")
 
132
    1
 
133
    >>> incomplete_utf8_sequence("xy\xe0\xa0")
 
134
    2
 
135
    >>> incomplete_utf8_sequence("xy\xf4")
 
136
    1
 
137
    >>> incomplete_utf8_sequence("xy\xf4\x8f")
 
138
    2
 
139
    >>> incomplete_utf8_sequence("xy\xf4\x8f\xa0")
 
140
    3
 
141
    """
 
142
    count = 0
 
143
    expect = None
 
144
    for b in byteseq[::-1]:
 
145
        b = ord(b)
 
146
        count += 1
 
147
        if b & 0x80 == 0x0:
 
148
            # 0xxxxxxx (single-byte character)
 
149
            expect = 1
 
150
            break
 
151
        elif b & 0xc0 == 0x80:
 
152
            # 10xxxxxx (subsequent byte)
 
153
            pass
 
154
        elif b & 0xe0 == 0xc0:
 
155
            # 110xxxxx (start of 2-byte sequence)
 
156
            expect = 2
 
157
            break
 
158
        elif b & 0xf0 == 0xe0:
 
159
            # 1110xxxx (start of 3-byte sequence)
 
160
            expect = 3
 
161
            break
 
162
        elif b & 0xf8 == 0xf0:
 
163
            # 11110xxx (start of 4-byte sequence)
 
164
            expect = 4
 
165
            break
 
166
        else:
 
167
            # Invalid byte
 
168
            return 0
 
169
 
 
170
        if count >= 4:
 
171
            # Seen too many "subsequent bytes", invalid
 
172
            return 0
 
173
 
 
174
    if expect is None:
 
175
        # We never saw a "first byte", invalid
 
176
        return 0
 
177
 
 
178
    # We now know expect and count
 
179
    if count >= expect:
 
180
        # Complete, or we saw an invalid sequence
 
181
        return 0
 
182
    elif count < expect:
 
183
        # Incomplete
 
184
        return count
 
185
 
 
186
def safe_rmtree(path, ignore_errors=False, onerror=None):
 
187
    """Recursively delete a directory tree.
 
188
 
 
189
    Copied from shutil.rmtree from Python 2.6, which does not follow symbolic
 
190
    links (it is otherwise unsafe to call as root on untrusted directories; do
 
191
    not use shutil.rmtree in this case, as you may be running Python 2.5).
 
192
 
 
193
    If ignore_errors is set, errors are ignored; otherwise, if onerror
 
194
    is set, it is called to handle the error with arguments (func,
 
195
    path, exc_info) where func is os.listdir, os.remove, or os.rmdir;
 
196
    path is the argument to that function that caused it to fail; and
 
197
    exc_info is a tuple returned by sys.exc_info().  If ignore_errors
 
198
    is false and onerror is None, an exception is raised.
 
199
 
 
200
    """
 
201
    if ignore_errors:
 
202
        def onerror(*args):
 
203
            pass
 
204
    elif onerror is None:
 
205
        def onerror(*args):
 
206
            raise
 
207
    try:
 
208
        if os.path.islink(path):
 
209
            # symlinks to directories are forbidden, see bug #1669
 
210
            raise OSError("Cannot call safe_rmtree on a symbolic link")
 
211
    except OSError:
 
212
        onerror(os.path.islink, path, sys.exc_info())
 
213
        # can't continue even if onerror hook returns
 
214
        return
 
215
    names = []
 
216
    try:
 
217
        names = os.listdir(path)
 
218
    except os.error, err:
 
219
        onerror(os.listdir, path, sys.exc_info())
 
220
    for name in names:
 
221
        fullname = os.path.join(path, name)
 
222
        try:
 
223
            mode = os.lstat(fullname).st_mode
 
224
        except os.error:
 
225
            mode = 0
 
226
        if stat.S_ISDIR(mode):
 
227
            safe_rmtree(fullname, ignore_errors, onerror)
 
228
        else:
 
229
            try:
 
230
                os.remove(fullname)
 
231
            except os.error, err:
 
232
                onerror(os.remove, fullname, sys.exc_info())
 
233
    try:
 
234
        os.rmdir(path)
 
235
    except os.error:
 
236
        onerror(os.rmdir, path, sys.exc_info())
 
237
 
 
238
def format_submission_principal(user, principal):
 
239
    """Render a list of users to fit in the offering project listing.
 
240
 
 
241
    Given a user and a list of submitters, returns 'solo' if the
 
242
    only submitter is the user, or a string of the form
 
243
    'with A, B and C' if there are any other submitters.
 
244
 
 
245
    If submitters is None, we assume that the list of members could
 
246
    not be determined, so we just return 'group'.
 
247
    """
 
248
    if principal is None:
 
249
        return 'group'
 
250
 
 
251
    if principal is user:
 
252
        return 'solo'
 
253
 
 
254
    display_names = sorted(
 
255
        member.display_name for member in principal.members
 
256
        if member is not user)
 
257
 
 
258
    if len(display_names) == 0:
 
259
        return 'solo (%s)' % principal.name
 
260
    elif len(display_names) == 1:
 
261
        return 'with %s (%s)' % (display_names[0], principal.name)
 
262
    elif len(display_names) > 5:
 
263
        return 'with %d others (%s)' % (len(display_names), principal.name)
 
264
    else:
 
265
        return 'with %s and %s (%s)' % (', '.join(display_names[:-1]),
 
266
                                        display_names[-1], principal.name)