Subversion Repositories freemyipod

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
396 farthen 1
#!/usr/bin/env python
2
#
3
#
4
#    Copyright 2010 TheSeven, benedikt93, Farthen
5
#
6
#
427 farthen 7
#    This file is part of emCORE.
396 farthen 8
#
427 farthen 9
#    emCORE is free software: you can redistribute it and/or
396 farthen 10
#    modify it under the terms of the GNU General Public License as
11
#    published by the Free Software Foundation, either version 2 of the
12
#    License, or (at your option) any later version.
13
#
427 farthen 14
#    emCORE is distributed in the hope that it will be useful,
396 farthen 15
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
16
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
17
#    See the GNU General Public License for more details.
18
#
19
#    You should have received a copy of the GNU General Public License
427 farthen 20
#    along with emCORE.  If not, see <http://www.gnu.org/licenses/>.
396 farthen 21
#
22
#
23
 
24
"""
25
    This file includes some reusable functions and classes that might be useful
26
    to all python scripts
27
"""
28
 
29
import sys
505 farthen 30
from ctypes import *
31
from _ctypes import _SimpleCData
396 farthen 32
 
532 farthen 33
 
34
class Error(Exception):
35
    def __init__(self, value=None):
36
        self.value = value
37
    def __str__(self):
38
        if self.value != None:
39
            return repr(self.value)
40
 
41
class ArgumentError(Error):
42
    pass
43
 
44
class ArgumentTypeError(Error):
45
    def __init__(self, expected, seen=False):
46
        self.expected = expected
47
        self.seen = seen
48
    def __str__(self):
49
        if self.seen:
50
            return "Expected %s but got %s" % (self.expected, self.seen)
51
        else:
52
            return "Expected %s, but saw something else" % self.expected
53
 
54
 
396 farthen 55
class Logger(object):
56
    """
501 farthen 57
        Simple stdout/stderr/file logger.
401 farthen 58
        Loglevel 3 is most verbose, Loglevel 0: Only log something if there is an error.
59
        Loglevel -1 means that nothing is logged.
501 farthen 60
        The write function doesn't care about the loglevel and always logs everything.
396 farthen 61
    """
501 farthen 62
    def __init__(self, loglevel = 2, target = "stderr", logfile = "tools.log"):
400 farthen 63
        """
64
            loglevel: Possible values: 0 (only errors), 1 (warnings), 2 (info,
65
                      recommended for production use), 3 and more (debug)
66
            logfile: File to log to if using the target = "file"
67
            target: Default logging target. Can be "stdout", "file" or "string"
68
        """
69
        self.loglevel = loglevel
70
        self.logfile = logfile
71
        self.target = target
396 farthen 72
 
501 farthen 73
    def write(self, text, indent = 0, target = None):
401 farthen 74
        if self.loglevel >= 0:
75
            if target is None: target = self.target
76
            text = (indent * " ") + text
77
            text = text.replace("\n", "\n" + (indent * " "), text.count("\n") - 1)
78
            if target == "stdout":
501 farthen 79
                sys.stderr.write(text)
80
            if target == "stderr":
81
                sys.stderr.write(text)
401 farthen 82
            elif target == "file":
83
                with open(self.logfile, 'a') as f:
84
                    f.write(text)
85
                    f.close()
86
            elif target == "string":
87
                return text
396 farthen 88
 
400 farthen 89
    def debug(self, text, indent = 0, target = None):
396 farthen 90
        if self.loglevel >= 3:
501 farthen 91
            self.write("DEBUG: " + text, indent, target)
396 farthen 92
 
400 farthen 93
    def info(self, text, indent = 0, target = None):
396 farthen 94
        if self.loglevel >= 2:
501 farthen 95
            self.write(text, indent, target)
396 farthen 96
 
400 farthen 97
    def warn(self, text, indent = 0, target = None):
396 farthen 98
        if self.loglevel >= 1:
501 farthen 99
            self.write("WARNING: " + text, indent, target)
396 farthen 100
 
400 farthen 101
    def error(self, text, indent = 0, target = None):
401 farthen 102
        if self.loglevel >= 0:
501 farthen 103
            self.write("ERROR: " + text, indent, target)
396 farthen 104
 
105
 
106
class Bunch(dict):
107
    """
108
        This is a dict whose items can also be accessed with
109
        bunchinstance.something.
110
    """
111
    def __init__(self, **kw):
112
        dict.__init__(self, kw)
113
        self.__dict__ = self
114
 
115
    def __getstate__(self):
116
        return self
117
 
118
    def __setstate__(self, state):
119
        self.update(state)
120
        self.__dict__ = self
121
 
122
 
505 farthen 123
class c_enum(_SimpleCData):
124
    """
125
        Resembles the enum datatype from C with an 8 bit size.
126
        Returns the associated string of a value with c_enum[i]
127
        Returns the current value of the associated value as c_enum.__repr__()
128
        Comparison operators work with strings and values at the same time.
129
 
130
        ATTENTION: You can not really see if this is initialized or not.
131
        If it is uninitialized it will return the first entry of the enum.
132
        While this may be circumvented by changing the default value to
133
        something else this will not work if the enum is placed inside a
134
        ctypes structure as the __init__() method will not be called then.
135
    """    
136
    _type_ = c_uint8._type_
137
 
138
    def __init__(self, value = 0):
139
        if type(value) == str:
140
            value = getattr(self, value)
141
        _SimpleCData.__init__(self, value)
142
        self[value]
143
 
144
    def __getattr__(self, name):
145
        if name == "value":
146
            return self.value
147
        for key, value in enumerate(self._fields_):
148
            if value == name:
149
                return key
150
 
151
    def __getitem__(self, lookupkey):
152
        for key, value in enumerate(self._fields_):
153
            if key == lookupkey:
154
                return value
155
        raise IndexError("Value %d not in range of possible enum values for %s!" % (lookupkey, self.__class__.__name__))
156
 
157
    def __str__(self):
158
        return self[self.value]
159
 
160
    def __repr__(self):
161
        return self.__str__()
162
 
514 farthen 163
    def __int__(self):
164
        return self.value
165
 
505 farthen 166
    def __eq__(self, other):
167
        if type(other) == str:
168
            try: return getattr(self, other) == self.value
169
            except AttributeError: return False
170
        else:
171
            return self.value == other
172
 
173
    def __lt__(self, other):
174
        if type(other) == str:
175
            try: return self.value < getattr(self, other)
176
            except AttributeError: return False
177
        else:
178
            return self.value < other
179
 
180
    def __gt__(self, other):
181
        if type(other) == str:
182
            try: return  self.value > getattr(self, other)
183
            except AttributeError: return False
184
        else:
185
            return self.value > other
186
 
187
    def __le__(self, other):
188
        if self.value == other or self.value < other:
189
            return True
190
        return False
191
 
192
    def __ge__(self, other):
193
        if self.value == other or self.value > other:
194
            return True
195
        return False
196
 
197
    def __ne__(self, other):
198
        if self.value == other:
199
            return False
200
        return True
201
 
202
 
203
class ExtendedCStruct(LittleEndianStructure):
204
    """
205
        This is a subclass of the LittleEndianStructure.
206
        It implements functions to easily convert
207
        structures to/from strings and Bunches.
208
    """
209
    def _from_bunch(self, bunch):
210
        for field, _ in self._fields_:
211
            if field in bunch:
212
                setattr(self, field, getattr(bunch, field))
213
 
214
    def _to_bunch(self):
510 farthen 215
        bunch = Bunch()
216
        for field, _ in self._fields_:
217
            setattr(bunch, field, getattr(self, field))
218
        return bunch
505 farthen 219
 
220
    def _from_string(self, string):
221
        memmove(addressof(self), string, sizeof(self))
222
 
223
    def _to_string(self):
224
        return string_at(addressof(self), sizeof(self))
225
 
226
 
398 farthen 227
def gethwname(id):
228
    try:
532 farthen 229
        from libemcoredata import hwtypes
230
        hwtype = hwtypes[id]
398 farthen 231
    except KeyError:
532 farthen 232
        hwtype = "UNKNOWN (ID = 0x%X)" % id
398 farthen 233
    return hwtype
234
 
235
 
396 farthen 236
def trimdoc(docstring):
237
    """
238
        Trims whitespace from docstrings
239
    """
240
    if not docstring:
241
        return ''
242
    # Convert tabs to spaces (following the normal Python rules)
243
    # and split into a list of lines:
244
    lines = docstring.expandtabs().splitlines()
245
    # Determine minimum indentation (first line doesn't count):
246
    indent = sys.maxint
247
    for line in lines[1:]:
248
        stripped = line.lstrip()
249
        if stripped:
250
            indent = min(indent, len(line) - len(stripped))
251
    # Remove indentation (first line is special):
252
    trimmed = [lines[0].strip()]
253
    if indent < sys.maxint:
254
        for line in lines[1:]:
255
            trimmed.append(line[indent:].rstrip())
256
    # Strip off trailing and leading blank lines:
257
    while trimmed and not trimmed[-1]:
258
        trimmed.pop()
259
    while trimmed and not trimmed[0]:
260
        trimmed.pop(0)
261
    # Return a single string:
262
    return '\n'.join(trimmed)
263
 
264
 
265
def getfuncdoc(funcdict):
266
    """
267
        Extracts important information from a dict of functions like the
268
        docstring and arguments and returns them in a human readable format
269
    """
270
    import inspect
271
    import re
272
    functions = Bunch()
273
    for function in funcdict:
274
        function = funcdict[function].func
275
        docinfo = Bunch()
276
        name = function.__name__
277
        args = inspect.getargspec(function)[0]
278
        docinfo['varargs'] = False
279
        if inspect.getargspec(function)[1]:
280
            docinfo['varargs'] = True
281
        kwargvalues = inspect.getargspec(function)[3]
282
        kwargs = Bunch()
283
        if args:
284
            if kwargvalues:
285
                argnum = len(args) - len(kwargvalues)
286
                kwargnum = len(kwargvalues)
287
                kwargs = dict(zip(args[argnum:], kwargvalues))
288
            else:
289
                argnum = len(args)
290
        else:
291
            argnum = 0
292
        docinfo['args'] = args[1:argnum]
293
        docinfo['kwargs'] = kwargs
294
        if function.__doc__:
295
            # strip unneccessary whitespace
296
            docinfo['documentation'] = trimdoc(function.__doc__)
297
        else:
298
            docinfo['documentation'] = None
299
        functions[name] = docinfo
300
    return functions
301
 
302
 
303
def gendoc(funcdict, indentwidth = 4, logtarget = "string"):
304
    logger = Logger()
305
    doc = getfuncdoc(funcdict)
306
    ret = ""
307
    for function in sorted(doc.items()):
308
        function = function[0]
309
        ret += logger.log("def " + function + "(", target = logtarget)
310
        counter = 0
311
        if doc[function]['args']:
312
            for arg in doc[function]['args']:
313
                if counter > 0:
314
                    sys.stdout.write(", ")
315
                counter += 1
316
                ret += logger.log(arg, target = logtarget)
317
        if doc[function]['kwargs']:
318
            for kwarg, kwargvalue in doc[function]['kwargs'].items():
319
                if counter > 0:
320
                    sys.stdout.write(", ")
321
                counter += 1
322
                ret += logger.log(kwarg + "=" + str(kwargvalue), target = logtarget)
323
        if doc[function]['varargs']:
324
            ret += logger.log("*argv", target = logtarget)
325
        ret += logger.log("):\n", target = logtarget)
326
        if doc[function]['documentation']:
327
            ret += logger.log("\"\"\"\n", indent = indentwidth, target = logtarget)
328
            ret += logger.log(trimdoc(doc[function]['documentation']) + "\n", indent = 2 * indentwidth, target = logtarget)
329
            ret += logger.log("\"\"\"\n", indent = indentwidth, target = logtarget)
330
        ret += logger.log("\n", target = logtarget)
532 farthen 331
    return ret
332
 
333
 
334
def to_bool(something):
335
    """
336
        Converts quite everything into bool.
337
    """
338
    if type(something) == bool:
339
        return something
340
    if something is None:
341
        return False
342
    elif type(something) == int or type(something) == long:
343
        return bool(something)
344
    elif type(something == str):
345
        if something.lower() in ['true', '1', 't', 'y', 'yes']:
346
            return True
347
        elif something.lower() in ['false', '0', 'f', 'n', 'no']:
348
            return False
349
    raise ArgumentTypeError("bool", "'%s'" % something)
350
 
351
def to_int(something):
352
    """
353
        Converts quite everything to a hexadecimal represented integer.
354
        This works for default arguments too, because it returns
355
        None when it found that it got a NoneType object.
356
    """
357
    if type(something) == int or type(something) == long:
358
        return something
359
    elif something is None:
360
        return None
361
    elif type(something) == str:
362
        try:
363
            if something[:2] == "0x": # Hexadecimal notation
364
                return int(something[2:], 16)
365
            elif something[:2] == "0b": # Binary notation
366
                return int(something[2:], 2)
367
            else: # Decimal notation
368
                return int(something, 10)
369
        except ValueError:
370
            raise ArgumentTypeError("integer", "'%s'" % something)
371
    else:
372
        raise ArgumentTypeError("integer", "'%s'" % something)