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

« back to all changes in this revision

Viewing changes to ivle/util.py

  • Committer: Matt Giuca
  • Date: 2009-05-12 15:08:45 UTC
  • mto: This revision was merged to the branch mainline in revision 1247.
  • Revision ID: matt.giuca@gmail.com-20090512150845-fg480l1qh7le0ypz
ivle-fetchsubmissions: In a stunningly awful hack, detects pre-Python2.6 and
    changes make_zip to behave correctly for the awful restriction in zipfile
    that you can't add directories.
    (Stops trying to add directories, and hacks in the footer writing if there
    is an empty file).

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 IVLEError(Exception):
 
29
    """Legacy general IVLE exception.
 
30
 
 
31
    This is the old "standard" exception class for IVLE errors. It is only
 
32
    used in fileservice, and should not be used in any new code.
 
33
    """
 
34
    def __init__(self, httpcode, message=None):
 
35
        self.httpcode = httpcode
 
36
        self.message = message
 
37
        self.args = (httpcode, message)
 
38
 
 
39
class IVLEJailError(Exception):
 
40
    """Exception proxying an in-jail error.
 
41
 
 
42
    This exception indicates an error that occurred inside an IVLE CGI script
 
43
    inside the jail. It should never be raised directly - only by the 
 
44
    interpreter.
 
45
 
 
46
    Information will be retrieved from it, and then treated as a normal
 
47
    error.
 
48
    """
 
49
    def __init__(self, type_str, message, info):
 
50
        self.type_str = type_str
 
51
        self.message = message
 
52
        self.info = info
 
53
 
 
54
class FakeObject(object):
 
55
    """ A representation of an object that can't be Pickled """
 
56
    def __init__(self, type, name, attrib={}):
 
57
        self.type = type
 
58
        self.name = name
 
59
        self.attrib = attrib
 
60
 
 
61
    def __repr__(self):
 
62
        return "<Fake %s %s>"%(self.type, self.name)
 
63
 
 
64
def split_path(path):
 
65
    """Given a path, returns a tuple consisting of the top-level directory in
 
66
    the path, and the rest of the path. Note that both items in the tuple will
 
67
    NOT begin with a slash, regardless of whether the original path did. Also
 
68
    normalises the path.
 
69
 
 
70
    Always returns a pair of strings, except for one special case, in which
 
71
    the path is completely empty (or just a single slash). In this case the
 
72
    return value will be (None, ''). But still always returns a pair.
 
73
 
 
74
    Examples:
 
75
 
 
76
    >>> split_path("")
 
77
    (None, '')
 
78
    >>> split_path("/")
 
79
    (None, '')
 
80
    >>> split_path("home")
 
81
    ('home', '')
 
82
    >>> split_path("home/docs/files")
 
83
    ('home', 'docs/files')
 
84
    >>> split_path("//home/docs/files")
 
85
    ('', 'home/docs/files')
 
86
    """
 
87
    path = os.path.normpath(path)
 
88
    # Ignore the opening slash
 
89
    if path.startswith(os.sep):
 
90
        path = path[len(os.sep):]
 
91
    if path == '' or path == '.':
 
92
        return (None, '')
 
93
    splitpath = path.split(os.sep, 1)
 
94
    if len(splitpath) == 1:
 
95
        return (splitpath[0], '')
 
96
    else:
 
97
        return tuple(splitpath)
 
98
 
 
99
def incomplete_utf8_sequence(byteseq):
 
100
    """Calculate the completeness of a UTF-8 encoded string.
 
101
 
 
102
    Given a UTF-8-encoded byte sequence (str), returns the number of bytes at
 
103
    the end of the string which comprise an incomplete UTF-8 character
 
104
    sequence.
 
105
 
 
106
    If the string is empty or ends with a complete character OR INVALID
 
107
    sequence, returns 0.
 
108
    Otherwise, returns 1-3 indicating the number of bytes in the final
 
109
    incomplete (but valid) character sequence.
 
110
 
 
111
    Does not check any bytes before the final sequence for correctness.
 
112
 
 
113
    >>> incomplete_utf8_sequence("")
 
114
    0
 
115
    >>> incomplete_utf8_sequence("xy")
 
116
    0
 
117
    >>> incomplete_utf8_sequence("xy\xc3\xbc")
 
118
    0
 
119
    >>> incomplete_utf8_sequence("\xc3")
 
120
    1
 
121
    >>> incomplete_utf8_sequence("\xbc\xc3")
 
122
    1
 
123
    >>> incomplete_utf8_sequence("xy\xbc\xc3")
 
124
    1
 
125
    >>> incomplete_utf8_sequence("xy\xe0\xa0")
 
126
    2
 
127
    >>> incomplete_utf8_sequence("xy\xf4")
 
128
    1
 
129
    >>> incomplete_utf8_sequence("xy\xf4\x8f")
 
130
    2
 
131
    >>> incomplete_utf8_sequence("xy\xf4\x8f\xa0")
 
132
    3
 
133
    """
 
134
    count = 0
 
135
    expect = None
 
136
    for b in byteseq[::-1]:
 
137
        b = ord(b)
 
138
        count += 1
 
139
        if b & 0x80 == 0x0:
 
140
            # 0xxxxxxx (single-byte character)
 
141
            expect = 1
 
142
            break
 
143
        elif b & 0xc0 == 0x80:
 
144
            # 10xxxxxx (subsequent byte)
 
145
            pass
 
146
        elif b & 0xe0 == 0xc0:
 
147
            # 110xxxxx (start of 2-byte sequence)
 
148
            expect = 2
 
149
            break
 
150
        elif b & 0xf0 == 0xe0:
 
151
            # 1110xxxx (start of 3-byte sequence)
 
152
            expect = 3
 
153
            break
 
154
        elif b & 0xf8 == 0xf0:
 
155
            # 11110xxx (start of 4-byte sequence)
 
156
            expect = 4
 
157
            break
 
158
        else:
 
159
            # Invalid byte
 
160
            return 0
 
161
 
 
162
        if count >= 4:
 
163
            # Seen too many "subsequent bytes", invalid
 
164
            return 0
 
165
 
 
166
    if expect is None:
 
167
        # We never saw a "first byte", invalid
 
168
        return 0
 
169
 
 
170
    # We now know expect and count
 
171
    if count >= expect:
 
172
        # Complete, or we saw an invalid sequence
 
173
        return 0
 
174
    elif count < expect:
 
175
        # Incomplete
 
176
        return count
 
177
 
 
178
def object_to_dict(attrnames, obj):
 
179
    """Convert an object into a dictionary.
 
180
 
 
181
    This takes a shallow copy of the object.
 
182
 
 
183
    @param attrnames: Set (or iterable) of names of attributes to be copied
 
184
                      into the dictionary. (We don't auto-lookup, because this
 
185
                      function needs to be used on magical objects).
 
186
    """
 
187
    return dict((k, getattr(obj, k))
 
188
        for k in attrnames if not k.startswith('_'))
 
189
 
 
190
def safe_rmtree(path, ignore_errors=False, onerror=None):
 
191
    """Recursively delete a directory tree.
 
192
 
 
193
    Copied from shutil.rmtree from Python 2.6, which does not follow symbolic
 
194
    links (it is otherwise unsafe to call as root on untrusted directories; do
 
195
    not use shutil.rmtree in this case, as you may be running Python 2.5).
 
196
 
 
197
    If ignore_errors is set, errors are ignored; otherwise, if onerror
 
198
    is set, it is called to handle the error with arguments (func,
 
199
    path, exc_info) where func is os.listdir, os.remove, or os.rmdir;
 
200
    path is the argument to that function that caused it to fail; and
 
201
    exc_info is a tuple returned by sys.exc_info().  If ignore_errors
 
202
    is false and onerror is None, an exception is raised.
 
203
 
 
204
    """
 
205
    if ignore_errors:
 
206
        def onerror(*args):
 
207
            pass
 
208
    elif onerror is None:
 
209
        def onerror(*args):
 
210
            raise
 
211
    try:
 
212
        if os.path.islink(path):
 
213
            # symlinks to directories are forbidden, see bug #1669
 
214
            raise OSError("Cannot call safe_rmtree on a symbolic link")
 
215
    except OSError:
 
216
        onerror(os.path.islink, path, sys.exc_info())
 
217
        # can't continue even if onerror hook returns
 
218
        return
 
219
    names = []
 
220
    try:
 
221
        names = os.listdir(path)
 
222
    except os.error, err:
 
223
        onerror(os.listdir, path, sys.exc_info())
 
224
    for name in names:
 
225
        fullname = os.path.join(path, name)
 
226
        try:
 
227
            mode = os.lstat(fullname).st_mode
 
228
        except os.error:
 
229
            mode = 0
 
230
        if stat.S_ISDIR(mode):
 
231
            safe_rmtree(fullname, ignore_errors, onerror)
 
232
        else:
 
233
            try:
 
234
                os.remove(fullname)
 
235
            except os.error, err:
 
236
                onerror(os.remove, fullname, sys.exc_info())
 
237
    try:
 
238
        os.rmdir(path)
 
239
    except os.error:
 
240
        onerror(os.rmdir, path, sys.exc_info())