Subversion Repositories freemyipod

Rev

Go to most recent revision | 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__
579 farthen 284
        argspec = inspect.getargspec(function)
285
        args = argspec[0]
396 farthen 286
        docinfo['varargs'] = False
579 farthen 287
        if argspec[1]:
396 farthen 288
            docinfo['varargs'] = True
579 farthen 289
        kwargvalues = argspec[3]
290
        kwargs = []
396 farthen 291
        if args:
292
            if kwargvalues:
293
                argnum = len(args) - len(kwargvalues)
294
                kwargnum = len(kwargvalues)
579 farthen 295
                kwargs = list(zip(args[argnum:], kwargvalues))
396 farthen 296
            else:
297
                argnum = len(args)
298
        else:
299
            argnum = 0
300
        docinfo['args'] = args[1:argnum]
301
        docinfo['kwargs'] = kwargs
302
        if function.__doc__:
303
            # strip unneccessary whitespace
304
            docinfo['documentation'] = trimdoc(function.__doc__)
305
        else:
306
            docinfo['documentation'] = None
307
        functions[name] = docinfo
308
    return functions
309
 
310
 
311
def gendoc(funcdict, indentwidth = 4, logtarget = "string"):
312
    logger = Logger()
313
    doc = getfuncdoc(funcdict)
314
    ret = ""
315
    for function in sorted(doc.items()):
316
        function = function[0]
317
        ret += logger.log("def " + function + "(", target = logtarget)
318
        counter = 0
319
        if doc[function]['args']:
320
            for arg in doc[function]['args']:
321
                if counter > 0:
322
                    sys.stdout.write(", ")
323
                counter += 1
324
                ret += logger.log(arg, target = logtarget)
325
        if doc[function]['kwargs']:
326
            for kwarg, kwargvalue in doc[function]['kwargs'].items():
327
                if counter > 0:
328
                    sys.stdout.write(", ")
329
                counter += 1
330
                ret += logger.log(kwarg + "=" + str(kwargvalue), target = logtarget)
331
        if doc[function]['varargs']:
332
            ret += logger.log("*argv", target = logtarget)
333
        ret += logger.log("):\n", target = logtarget)
334
        if doc[function]['documentation']:
335
            ret += logger.log("\"\"\"\n", indent = indentwidth, target = logtarget)
336
            ret += logger.log(trimdoc(doc[function]['documentation']) + "\n", indent = 2 * indentwidth, target = logtarget)
337
            ret += logger.log("\"\"\"\n", indent = indentwidth, target = logtarget)
338
        ret += logger.log("\n", target = logtarget)
532 farthen 339
    return ret
340
 
341
 
342
def to_bool(something):
343
    """
344
        Converts quite everything into bool.
345
    """
346
    if type(something) == bool:
347
        return something
348
    if something is None:
349
        return False
350
    elif type(something) == int or type(something) == long:
351
        return bool(something)
352
    elif type(something == str):
353
        if something.lower() in ['true', '1', 't', 'y', 'yes']:
354
            return True
355
        elif something.lower() in ['false', '0', 'f', 'n', 'no']:
356
            return False
357
    raise ArgumentTypeError("bool", "'%s'" % something)
358
 
359
def to_int(something):
360
    """
361
        Converts quite everything to a hexadecimal represented integer.
362
        This works for default arguments too, because it returns
363
        None when it found that it got a NoneType object.
364
    """
365
    if type(something) == int or type(something) == long:
366
        return something
367
    elif something is None:
368
        return None
369
    elif type(something) == str:
370
        try:
371
            if something[:2] == "0x": # Hexadecimal notation
372
                return int(something[2:], 16)
373
            elif something[:2] == "0b": # Binary notation
374
                return int(something[2:], 2)
375
            else: # Decimal notation
376
                return int(something, 10)
377
        except ValueError:
378
            raise ArgumentTypeError("integer", "'%s'" % something)
379
    else:
380
        raise ArgumentTypeError("integer", "'%s'" % something)