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":
600 theseven 79
                sys.stdout.write(text)
501 farthen 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):
582 farthen 253
    try: maxsize = sys.maxint
254
    except AttributeError: maxsize = sys.maxsize
255
    indent = maxsize
396 farthen 256
    for line in lines[1:]:
257
        stripped = line.lstrip()
258
        if stripped:
259
            indent = min(indent, len(line) - len(stripped))
260
    # Remove indentation (first line is special):
261
    trimmed = [lines[0].strip()]
582 farthen 262
    if indent < maxsize:
396 farthen 263
        for line in lines[1:]:
264
            trimmed.append(line[indent:].rstrip())
265
    # Strip off trailing and leading blank lines:
266
    while trimmed and not trimmed[-1]:
267
        trimmed.pop()
268
    while trimmed and not trimmed[0]:
269
        trimmed.pop(0)
270
    # Return a single string:
271
    return '\n'.join(trimmed)
272
 
273
 
274
def getfuncdoc(funcdict):
275
    """
276
        Extracts important information from a dict of functions like the
277
        docstring and arguments and returns them in a human readable format
278
    """
279
    import inspect
280
    import re
281
    functions = Bunch()
282
    for function in funcdict:
283
        function = funcdict[function].func
284
        docinfo = Bunch()
285
        name = function.__name__
579 farthen 286
        argspec = inspect.getargspec(function)
287
        args = argspec[0]
396 farthen 288
        docinfo['varargs'] = False
579 farthen 289
        if argspec[1]:
396 farthen 290
            docinfo['varargs'] = True
579 farthen 291
        kwargvalues = argspec[3]
292
        kwargs = []
396 farthen 293
        if args:
294
            if kwargvalues:
295
                argnum = len(args) - len(kwargvalues)
296
                kwargnum = len(kwargvalues)
579 farthen 297
                kwargs = list(zip(args[argnum:], kwargvalues))
396 farthen 298
            else:
299
                argnum = len(args)
300
        else:
301
            argnum = 0
302
        docinfo['args'] = args[1:argnum]
303
        docinfo['kwargs'] = kwargs
304
        if function.__doc__:
305
            # strip unneccessary whitespace
306
            docinfo['documentation'] = trimdoc(function.__doc__)
307
        else:
308
            docinfo['documentation'] = None
309
        functions[name] = docinfo
310
    return functions
311
 
312
 
313
def gendoc(funcdict, indentwidth = 4, logtarget = "string"):
314
    logger = Logger()
315
    doc = getfuncdoc(funcdict)
316
    ret = ""
317
    for function in sorted(doc.items()):
318
        function = function[0]
319
        ret += logger.log("def " + function + "(", target = logtarget)
320
        counter = 0
321
        if doc[function]['args']:
322
            for arg in doc[function]['args']:
323
                if counter > 0:
324
                    sys.stdout.write(", ")
325
                counter += 1
326
                ret += logger.log(arg, target = logtarget)
327
        if doc[function]['kwargs']:
328
            for kwarg, kwargvalue in doc[function]['kwargs'].items():
329
                if counter > 0:
330
                    sys.stdout.write(", ")
331
                counter += 1
332
                ret += logger.log(kwarg + "=" + str(kwargvalue), target = logtarget)
333
        if doc[function]['varargs']:
334
            ret += logger.log("*argv", target = logtarget)
335
        ret += logger.log("):\n", target = logtarget)
336
        if doc[function]['documentation']:
337
            ret += logger.log("\"\"\"\n", indent = indentwidth, target = logtarget)
338
            ret += logger.log(trimdoc(doc[function]['documentation']) + "\n", indent = 2 * indentwidth, target = logtarget)
339
            ret += logger.log("\"\"\"\n", indent = indentwidth, target = logtarget)
340
        ret += logger.log("\n", target = logtarget)
532 farthen 341
    return ret
342
 
343
 
344
def to_bool(something):
345
    """
346
        Converts quite everything into bool.
347
    """
584 theseven 348
    if type(something).__name__ in ("bool", "NoneType"):
532 farthen 349
        return something
584 theseven 350
    elif type(something).__name__ in ("int", "long"):
351
        return something != 0
352
    elif type(something) == str:
532 farthen 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
584 theseven 357
    raise ArgumentTypeError("bool", "'%s' (%s)" % (something, type(something).__name__))
532 farthen 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
    """
584 theseven 365
    if type(something).__name__ in ("int", "long", "NoneType"):
532 farthen 366
        return something
367
    elif type(something) == str:
368
        try:
369
            if something[:2] == "0x": # Hexadecimal notation
370
                return int(something[2:], 16)
371
            elif something[:2] == "0b": # Binary notation
372
                return int(something[2:], 2)
373
            else: # Decimal notation
374
                return int(something, 10)
375
        except ValueError:
584 theseven 376
            raise ArgumentTypeError("integer", "'%s' (%s)" % (something, type(something).__name__))
532 farthen 377
    else:
584 theseven 378
        raise ArgumentTypeError("integer", "'%s' (%s)" % (something, type(something).__name__))
582 farthen 379
 
380
 
381
def majorver():
382
    """
383
        Returns the major version of python
384
    """
385
    import sys
386
    return sys.hexversion // 0x1000000