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":
610 farthen 79
                if majorver() > 2:
80
                    sys.stdout.write(text)
81
                else:
616 theseven 82
                    sys.stdout.write(text.encode(sys.stdout.encoding or "latin1", "replace"))
501 farthen 83
            if target == "stderr":
610 farthen 84
                if majorver() > 2:
85
                    sys.stdout.write(text)
86
                else:
616 theseven 87
                    sys.stderr.write(text.encode(sys.stderr.encoding or "latin1", "replace"))
401 farthen 88
            elif target == "file":
89
                with open(self.logfile, 'a') as f:
90
                    f.write(text)
91
                    f.close()
92
            elif target == "string":
93
                return text
396 farthen 94
 
400 farthen 95
    def debug(self, text, indent = 0, target = None):
396 farthen 96
        if self.loglevel >= 3:
501 farthen 97
            self.write("DEBUG: " + text, indent, target)
396 farthen 98
 
400 farthen 99
    def info(self, text, indent = 0, target = None):
396 farthen 100
        if self.loglevel >= 2:
501 farthen 101
            self.write(text, indent, target)
396 farthen 102
 
400 farthen 103
    def warn(self, text, indent = 0, target = None):
396 farthen 104
        if self.loglevel >= 1:
501 farthen 105
            self.write("WARNING: " + text, indent, target)
396 farthen 106
 
400 farthen 107
    def error(self, text, indent = 0, target = None):
401 farthen 108
        if self.loglevel >= 0:
501 farthen 109
            self.write("ERROR: " + text, indent, target)
396 farthen 110
 
111
 
112
class Bunch(dict):
113
    """
114
        This is a dict whose items can also be accessed with
115
        bunchinstance.something.
116
    """
117
    def __init__(self, **kw):
118
        dict.__init__(self, kw)
119
        self.__dict__ = self
120
 
121
    def __getstate__(self):
122
        return self
123
 
124
    def __setstate__(self, state):
125
        self.update(state)
126
        self.__dict__ = self
127
 
128
 
607 farthen 129
class remote_pointer(dict):
130
    """
131
        This points to a (remote) location.
132
        Otherwise it behaves like a Bunch.
133
        The second argument must be a Bunch object
134
    """
135
    def __init__(self, address, bunch):
136
        dict.__init__(self, bunch.__dict__)
137
        self.__dict__ = self
138
        self._address_ = address
139
 
140
    def __getstate__(self):
141
        return self
142
 
143
    def __setstate__(self, state):
144
        self.update(state)
145
        self.__dict__ = self
146
 
147
    def __str__(self):
148
        return "<remote_pointer object with address 0x%X>" % (self._address_)
149
 
150
    def __int__(self):
151
        return self._address_
152
 
153
    def __repr__(self):
154
        return self.__str__()
155
 
156
 
505 farthen 157
class c_enum(_SimpleCData):
158
    """
159
        Resembles the enum datatype from C with an 8 bit size.
160
        Returns the associated string of a value with c_enum[i]
161
        Returns the current value of the associated value as c_enum.__repr__()
162
        Comparison operators work with strings and values at the same time.
163
 
164
        ATTENTION: You can not really see if this is initialized or not.
165
        If it is uninitialized it will return the first entry of the enum.
166
        While this may be circumvented by changing the default value to
167
        something else this will not work if the enum is placed inside a
168
        ctypes structure as the __init__() method will not be called then.
169
    """    
170
    _type_ = c_uint8._type_
171
 
172
    def __init__(self, value = 0):
173
        if type(value) == str:
174
            value = getattr(self, value)
175
        _SimpleCData.__init__(self, value)
176
        self[value]
177
 
178
    def __getattr__(self, name):
179
        if name == "value":
180
            return self.value
181
        for key, value in enumerate(self._fields_):
182
            if value == name:
183
                return key
184
 
185
    def __getitem__(self, lookupkey):
186
        for key, value in enumerate(self._fields_):
187
            if key == lookupkey:
188
                return value
189
        raise IndexError("Value %d not in range of possible enum values for %s!" % (lookupkey, self.__class__.__name__))
190
 
191
    def __str__(self):
192
        return self[self.value]
193
 
194
    def __repr__(self):
195
        return self.__str__()
196
 
514 farthen 197
    def __int__(self):
198
        return self.value
199
 
505 farthen 200
    def __eq__(self, other):
201
        if type(other) == str:
202
            try: return getattr(self, other) == self.value
203
            except AttributeError: return False
204
        else:
205
            return self.value == other
206
 
207
    def __lt__(self, other):
208
        if type(other) == str:
209
            try: return self.value < getattr(self, other)
210
            except AttributeError: return False
211
        else:
212
            return self.value < other
213
 
214
    def __gt__(self, other):
215
        if type(other) == str:
216
            try: return  self.value > getattr(self, other)
217
            except AttributeError: return False
218
        else:
219
            return self.value > other
220
 
221
    def __le__(self, other):
222
        if self.value == other or self.value < other:
223
            return True
224
        return False
225
 
226
    def __ge__(self, other):
227
        if self.value == other or self.value > other:
228
            return True
229
        return False
230
 
231
    def __ne__(self, other):
604 farthen 232
        if type(other) == str:
233
            try: return getattr(self, other) != self.value
234
            except AttributeError: return True
235
        else:
236
            return self.value != other
505 farthen 237
 
238
class ExtendedCStruct(LittleEndianStructure):
239
    """
240
        This is a subclass of the LittleEndianStructure.
241
        It implements functions to easily convert
242
        structures to/from strings and Bunches.
243
    """
811 theseven 244
    def __init__(self, data = None, base = 0, address = 0):
245
        LittleEndianStructure.__init__(self)
246
        if data != None:
247
            self._data_ = data
248
            self._base_ = base
249
            self._address_ = address
250
            if address < base or address + sizeof(self) > base + len(data):
251
                raise Exception("Range 0x%08X+0x%X out of bounds [0x%08X:0x%08X]" % (address, sizeof(self), base, base + len(data)))
252
            memmove(addressof(self), data[address - base : address - base + sizeof(self)], sizeof(self))
253
 
254
    def __int__(self):
255
        return self._address_
256
 
505 farthen 257
    def _from_bunch(self, bunch):
258
        for field, _ in self._fields_:
259
            if field in bunch:
260
                setattr(self, field, getattr(bunch, field))
261
 
262
    def _to_bunch(self):
510 farthen 263
        bunch = Bunch()
264
        for field, _ in self._fields_:
265
            setattr(bunch, field, getattr(self, field))
266
        return bunch
505 farthen 267
 
268
    def _from_string(self, string):
269
        memmove(addressof(self), string, sizeof(self))
270
 
271
    def _to_string(self):
272
        return string_at(addressof(self), sizeof(self))
273
 
274
 
607 farthen 275
def getthread(address, threads):
276
    """
277
        Returns the thread at <address> from the list of threads <threads>.
278
        Returns an empty thread if not found
279
    """
280
    for thread in threads:
281
        if address == thread.addr:
282
            return thread
679 farthen 283
    from libemcoredata import scheduler_thread
607 farthen 284
    thread = scheduler_thread()._to_bunch()
680 farthen 285
    thread.name = "[Invalid Thread 0x%08X]" % address
607 farthen 286
    return thread
287
 
288
 
398 farthen 289
def gethwname(id):
290
    try:
532 farthen 291
        from libemcoredata import hwtypes
549 farthen 292
        hwtype = hwtypes[id][1]
398 farthen 293
    except KeyError:
532 farthen 294
        hwtype = "UNKNOWN (ID = 0x%X)" % id
398 farthen 295
    return hwtype
296
 
549 farthen 297
def gethwid(shortname):
298
    from libemcoredata import hwtypes
299
    for hwid in hwtypes:
300
        if hwtypes[hwid][0] == shortname:
301
            return hwid
302
    return False
398 farthen 303
 
549 farthen 304
 
396 farthen 305
def trimdoc(docstring):
306
    """
307
        Trims whitespace from docstrings
308
    """
309
    if not docstring:
310
        return ''
311
    # Convert tabs to spaces (following the normal Python rules)
312
    # and split into a list of lines:
313
    lines = docstring.expandtabs().splitlines()
314
    # Determine minimum indentation (first line doesn't count):
582 farthen 315
    try: maxsize = sys.maxint
316
    except AttributeError: maxsize = sys.maxsize
317
    indent = maxsize
396 farthen 318
    for line in lines[1:]:
319
        stripped = line.lstrip()
320
        if stripped:
321
            indent = min(indent, len(line) - len(stripped))
322
    # Remove indentation (first line is special):
323
    trimmed = [lines[0].strip()]
582 farthen 324
    if indent < maxsize:
396 farthen 325
        for line in lines[1:]:
326
            trimmed.append(line[indent:].rstrip())
327
    # Strip off trailing and leading blank lines:
328
    while trimmed and not trimmed[-1]:
329
        trimmed.pop()
330
    while trimmed and not trimmed[0]:
331
        trimmed.pop(0)
332
    # Return a single string:
333
    return '\n'.join(trimmed)
334
 
335
 
336
def getfuncdoc(funcdict):
337
    """
338
        Extracts important information from a dict of functions like the
339
        docstring and arguments and returns them in a human readable format
340
    """
341
    import inspect
342
    import re
343
    functions = Bunch()
344
    for function in funcdict:
345
        function = funcdict[function].func
346
        docinfo = Bunch()
347
        name = function.__name__
579 farthen 348
        argspec = inspect.getargspec(function)
349
        args = argspec[0]
396 farthen 350
        docinfo['varargs'] = False
579 farthen 351
        if argspec[1]:
396 farthen 352
            docinfo['varargs'] = True
579 farthen 353
        kwargvalues = argspec[3]
354
        kwargs = []
396 farthen 355
        if args:
356
            if kwargvalues:
357
                argnum = len(args) - len(kwargvalues)
358
                kwargnum = len(kwargvalues)
579 farthen 359
                kwargs = list(zip(args[argnum:], kwargvalues))
396 farthen 360
            else:
361
                argnum = len(args)
362
        else:
363
            argnum = 0
364
        docinfo['args'] = args[1:argnum]
365
        docinfo['kwargs'] = kwargs
366
        if function.__doc__:
367
            # strip unneccessary whitespace
368
            docinfo['documentation'] = trimdoc(function.__doc__)
369
        else:
370
            docinfo['documentation'] = None
371
        functions[name] = docinfo
372
    return functions
373
 
374
 
375
def gendoc(funcdict, indentwidth = 4, logtarget = "string"):
376
    logger = Logger()
377
    doc = getfuncdoc(funcdict)
378
    ret = ""
379
    for function in sorted(doc.items()):
380
        function = function[0]
381
        ret += logger.log("def " + function + "(", target = logtarget)
382
        counter = 0
383
        if doc[function]['args']:
384
            for arg in doc[function]['args']:
385
                if counter > 0:
386
                    sys.stdout.write(", ")
387
                counter += 1
388
                ret += logger.log(arg, target = logtarget)
389
        if doc[function]['kwargs']:
390
            for kwarg, kwargvalue in doc[function]['kwargs'].items():
391
                if counter > 0:
392
                    sys.stdout.write(", ")
393
                counter += 1
394
                ret += logger.log(kwarg + "=" + str(kwargvalue), target = logtarget)
395
        if doc[function]['varargs']:
396
            ret += logger.log("*argv", target = logtarget)
397
        ret += logger.log("):\n", target = logtarget)
398
        if doc[function]['documentation']:
399
            ret += logger.log("\"\"\"\n", indent = indentwidth, target = logtarget)
400
            ret += logger.log(trimdoc(doc[function]['documentation']) + "\n", indent = 2 * indentwidth, target = logtarget)
401
            ret += logger.log("\"\"\"\n", indent = indentwidth, target = logtarget)
402
        ret += logger.log("\n", target = logtarget)
532 farthen 403
    return ret
404
 
405
 
406
def to_bool(something):
407
    """
408
        Converts quite everything into bool.
409
    """
584 theseven 410
    if type(something).__name__ in ("bool", "NoneType"):
532 farthen 411
        return something
584 theseven 412
    elif type(something).__name__ in ("int", "long"):
413
        return something != 0
414
    elif type(something) == str:
532 farthen 415
        if something.lower() in ['true', '1', 't', 'y', 'yes']:
416
            return True
417
        elif something.lower() in ['false', '0', 'f', 'n', 'no']:
418
            return False
584 theseven 419
    raise ArgumentTypeError("bool", "'%s' (%s)" % (something, type(something).__name__))
532 farthen 420
 
421
def to_int(something):
422
    """
423
        Converts quite everything to a hexadecimal represented integer.
424
        This works for default arguments too, because it returns
425
        None when it found that it got a NoneType object.
426
    """
584 theseven 427
    if type(something).__name__ in ("int", "long", "NoneType"):
532 farthen 428
        return something
429
    elif type(something) == str:
430
        try:
431
            if something[:2] == "0x": # Hexadecimal notation
432
                return int(something[2:], 16)
433
            elif something[:2] == "0b": # Binary notation
434
                return int(something[2:], 2)
435
            else: # Decimal notation
436
                return int(something, 10)
437
        except ValueError:
584 theseven 438
            raise ArgumentTypeError("integer", "'%s' (%s)" % (something, type(something).__name__))
532 farthen 439
    else:
584 theseven 440
        raise ArgumentTypeError("integer", "'%s' (%s)" % (something, type(something).__name__))
582 farthen 441
 
442
 
443
def majorver():
444
    """
445
        Returns the major version of python
446
    """
447
    import sys
448
    return sys.hexversion // 0x1000000