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
549 farthen 230
        hwtype = hwtypes[id][1]
398 farthen 231
    except KeyError:
532 farthen 232
        hwtype = "UNKNOWN (ID = 0x%X)" % id
398 farthen 233
    return hwtype
234
 
549 farthen 235
def gethwid(shortname):
236
    from libemcoredata import hwtypes
237
    for hwid in hwtypes:
238
        if hwtypes[hwid][0] == shortname:
239
            return hwid
240
    return False
398 farthen 241
 
549 farthen 242
 
396 farthen 243
def trimdoc(docstring):
244
    """
245
        Trims whitespace from docstrings
246
    """
247
    if not docstring:
248
        return ''
249
    # Convert tabs to spaces (following the normal Python rules)
250
    # and split into a list of lines:
251
    lines = docstring.expandtabs().splitlines()
252
    # Determine minimum indentation (first line doesn't count):
253
    indent = sys.maxint
254
    for line in lines[1:]:
255
        stripped = line.lstrip()
256
        if stripped:
257
            indent = min(indent, len(line) - len(stripped))
258
    # Remove indentation (first line is special):
259
    trimmed = [lines[0].strip()]
260
    if indent < sys.maxint:
261
        for line in lines[1:]:
262
            trimmed.append(line[indent:].rstrip())
263
    # Strip off trailing and leading blank lines:
264
    while trimmed and not trimmed[-1]:
265
        trimmed.pop()
266
    while trimmed and not trimmed[0]:
267
        trimmed.pop(0)
268
    # Return a single string:
269
    return '\n'.join(trimmed)
270
 
271
 
272
def getfuncdoc(funcdict):
273
    """
274
        Extracts important information from a dict of functions like the
275
        docstring and arguments and returns them in a human readable format
276
    """
277
    import inspect
278
    import re
279
    functions = Bunch()
280
    for function in funcdict:
281
        function = funcdict[function].func
282
        docinfo = Bunch()
283
        name = function.__name__
284
        args = inspect.getargspec(function)[0]
285
        docinfo['varargs'] = False
286
        if inspect.getargspec(function)[1]:
287
            docinfo['varargs'] = True
288
        kwargvalues = inspect.getargspec(function)[3]
289
        kwargs = Bunch()
290
        if args:
291
            if kwargvalues:
292
                argnum = len(args) - len(kwargvalues)
293
                kwargnum = len(kwargvalues)
294
                kwargs = dict(zip(args[argnum:], kwargvalues))
295
            else:
296
                argnum = len(args)
297
        else:
298
            argnum = 0
299
        docinfo['args'] = args[1:argnum]
300
        docinfo['kwargs'] = kwargs
301
        if function.__doc__:
302
            # strip unneccessary whitespace
303
            docinfo['documentation'] = trimdoc(function.__doc__)
304
        else:
305
            docinfo['documentation'] = None
306
        functions[name] = docinfo
307
    return functions
308
 
309
 
310
def gendoc(funcdict, indentwidth = 4, logtarget = "string"):
311
    logger = Logger()
312
    doc = getfuncdoc(funcdict)
313
    ret = ""
314
    for function in sorted(doc.items()):
315
        function = function[0]
316
        ret += logger.log("def " + function + "(", target = logtarget)
317
        counter = 0
318
        if doc[function]['args']:
319
            for arg in doc[function]['args']:
320
                if counter > 0:
321
                    sys.stdout.write(", ")
322
                counter += 1
323
                ret += logger.log(arg, target = logtarget)
324
        if doc[function]['kwargs']:
325
            for kwarg, kwargvalue in doc[function]['kwargs'].items():
326
                if counter > 0:
327
                    sys.stdout.write(", ")
328
                counter += 1
329
                ret += logger.log(kwarg + "=" + str(kwargvalue), target = logtarget)
330
        if doc[function]['varargs']:
331
            ret += logger.log("*argv", target = logtarget)
332
        ret += logger.log("):\n", target = logtarget)
333
        if doc[function]['documentation']:
334
            ret += logger.log("\"\"\"\n", indent = indentwidth, target = logtarget)
335
            ret += logger.log(trimdoc(doc[function]['documentation']) + "\n", indent = 2 * indentwidth, target = logtarget)
336
            ret += logger.log("\"\"\"\n", indent = indentwidth, target = logtarget)
337
        ret += logger.log("\n", target = logtarget)
532 farthen 338
    return ret
339
 
340
 
341
def to_bool(something):
342
    """
343
        Converts quite everything into bool.
344
    """
345
    if type(something) == bool:
346
        return something
347
    if something is None:
348
        return False
349
    elif type(something) == int or type(something) == long:
350
        return bool(something)
351
    elif type(something == str):
352
        if something.lower() in ['true', '1', 't', 'y', 'yes']:
353
            return True
354
        elif something.lower() in ['false', '0', 'f', 'n', 'no']:
355
            return False
356
    raise ArgumentTypeError("bool", "'%s'" % something)
357
 
358
def to_int(something):
359
    """
360
        Converts quite everything to a hexadecimal represented integer.
361
        This works for default arguments too, because it returns
362
        None when it found that it got a NoneType object.
363
    """
364
    if type(something) == int or type(something) == long:
365
        return something
366
    elif something is None:
367
        return None
368
    elif type(something) == str:
369
        try:
370
            if something[:2] == "0x": # Hexadecimal notation
371
                return int(something[2:], 16)
372
            elif something[:2] == "0b": # Binary notation
373
                return int(something[2:], 2)
374
            else: # Decimal notation
375
                return int(something, 10)
376
        except ValueError:
377
            raise ArgumentTypeError("integer", "'%s'" % something)
378
    else:
379
        raise ArgumentTypeError("integer", "'%s'" % something)