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
 
239
class ExtendedCStruct(LittleEndianStructure):
240
    """
241
        This is a subclass of the LittleEndianStructure.
242
        It implements functions to easily convert
243
        structures to/from strings and Bunches.
244
    """
245
    def _from_bunch(self, bunch):
246
        for field, _ in self._fields_:
247
            if field in bunch:
248
                setattr(self, field, getattr(bunch, field))
249
 
250
    def _to_bunch(self):
510 farthen 251
        bunch = Bunch()
252
        for field, _ in self._fields_:
253
            setattr(bunch, field, getattr(self, field))
254
        return bunch
505 farthen 255
 
256
    def _from_string(self, string):
257
        memmove(addressof(self), string, sizeof(self))
258
 
259
    def _to_string(self):
260
        return string_at(addressof(self), sizeof(self))
261
 
262
 
607 farthen 263
def getthread(address, threads):
264
    """
265
        Returns the thread at <address> from the list of threads <threads>.
266
        Returns an empty thread if not found
267
    """
268
    for thread in threads:
269
        if address == thread.addr:
270
            return thread
679 farthen 271
    from libemcoredata import scheduler_thread
607 farthen 272
    thread = scheduler_thread()._to_bunch()
680 farthen 273
    thread.name = "[Invalid Thread 0x%08X]" % address
607 farthen 274
    return thread
275
 
276
 
398 farthen 277
def gethwname(id):
278
    try:
532 farthen 279
        from libemcoredata import hwtypes
549 farthen 280
        hwtype = hwtypes[id][1]
398 farthen 281
    except KeyError:
532 farthen 282
        hwtype = "UNKNOWN (ID = 0x%X)" % id
398 farthen 283
    return hwtype
284
 
549 farthen 285
def gethwid(shortname):
286
    from libemcoredata import hwtypes
287
    for hwid in hwtypes:
288
        if hwtypes[hwid][0] == shortname:
289
            return hwid
290
    return False
398 farthen 291
 
549 farthen 292
 
396 farthen 293
def trimdoc(docstring):
294
    """
295
        Trims whitespace from docstrings
296
    """
297
    if not docstring:
298
        return ''
299
    # Convert tabs to spaces (following the normal Python rules)
300
    # and split into a list of lines:
301
    lines = docstring.expandtabs().splitlines()
302
    # Determine minimum indentation (first line doesn't count):
582 farthen 303
    try: maxsize = sys.maxint
304
    except AttributeError: maxsize = sys.maxsize
305
    indent = maxsize
396 farthen 306
    for line in lines[1:]:
307
        stripped = line.lstrip()
308
        if stripped:
309
            indent = min(indent, len(line) - len(stripped))
310
    # Remove indentation (first line is special):
311
    trimmed = [lines[0].strip()]
582 farthen 312
    if indent < maxsize:
396 farthen 313
        for line in lines[1:]:
314
            trimmed.append(line[indent:].rstrip())
315
    # Strip off trailing and leading blank lines:
316
    while trimmed and not trimmed[-1]:
317
        trimmed.pop()
318
    while trimmed and not trimmed[0]:
319
        trimmed.pop(0)
320
    # Return a single string:
321
    return '\n'.join(trimmed)
322
 
323
 
324
def getfuncdoc(funcdict):
325
    """
326
        Extracts important information from a dict of functions like the
327
        docstring and arguments and returns them in a human readable format
328
    """
329
    import inspect
330
    import re
331
    functions = Bunch()
332
    for function in funcdict:
333
        function = funcdict[function].func
334
        docinfo = Bunch()
335
        name = function.__name__
579 farthen 336
        argspec = inspect.getargspec(function)
337
        args = argspec[0]
396 farthen 338
        docinfo['varargs'] = False
579 farthen 339
        if argspec[1]:
396 farthen 340
            docinfo['varargs'] = True
579 farthen 341
        kwargvalues = argspec[3]
342
        kwargs = []
396 farthen 343
        if args:
344
            if kwargvalues:
345
                argnum = len(args) - len(kwargvalues)
346
                kwargnum = len(kwargvalues)
579 farthen 347
                kwargs = list(zip(args[argnum:], kwargvalues))
396 farthen 348
            else:
349
                argnum = len(args)
350
        else:
351
            argnum = 0
352
        docinfo['args'] = args[1:argnum]
353
        docinfo['kwargs'] = kwargs
354
        if function.__doc__:
355
            # strip unneccessary whitespace
356
            docinfo['documentation'] = trimdoc(function.__doc__)
357
        else:
358
            docinfo['documentation'] = None
359
        functions[name] = docinfo
360
    return functions
361
 
362
 
363
def gendoc(funcdict, indentwidth = 4, logtarget = "string"):
364
    logger = Logger()
365
    doc = getfuncdoc(funcdict)
366
    ret = ""
367
    for function in sorted(doc.items()):
368
        function = function[0]
369
        ret += logger.log("def " + function + "(", target = logtarget)
370
        counter = 0
371
        if doc[function]['args']:
372
            for arg in doc[function]['args']:
373
                if counter > 0:
374
                    sys.stdout.write(", ")
375
                counter += 1
376
                ret += logger.log(arg, target = logtarget)
377
        if doc[function]['kwargs']:
378
            for kwarg, kwargvalue in doc[function]['kwargs'].items():
379
                if counter > 0:
380
                    sys.stdout.write(", ")
381
                counter += 1
382
                ret += logger.log(kwarg + "=" + str(kwargvalue), target = logtarget)
383
        if doc[function]['varargs']:
384
            ret += logger.log("*argv", target = logtarget)
385
        ret += logger.log("):\n", target = logtarget)
386
        if doc[function]['documentation']:
387
            ret += logger.log("\"\"\"\n", indent = indentwidth, target = logtarget)
388
            ret += logger.log(trimdoc(doc[function]['documentation']) + "\n", indent = 2 * indentwidth, target = logtarget)
389
            ret += logger.log("\"\"\"\n", indent = indentwidth, target = logtarget)
390
        ret += logger.log("\n", target = logtarget)
532 farthen 391
    return ret
392
 
393
 
394
def to_bool(something):
395
    """
396
        Converts quite everything into bool.
397
    """
584 theseven 398
    if type(something).__name__ in ("bool", "NoneType"):
532 farthen 399
        return something
584 theseven 400
    elif type(something).__name__ in ("int", "long"):
401
        return something != 0
402
    elif type(something) == str:
532 farthen 403
        if something.lower() in ['true', '1', 't', 'y', 'yes']:
404
            return True
405
        elif something.lower() in ['false', '0', 'f', 'n', 'no']:
406
            return False
584 theseven 407
    raise ArgumentTypeError("bool", "'%s' (%s)" % (something, type(something).__name__))
532 farthen 408
 
409
def to_int(something):
410
    """
411
        Converts quite everything to a hexadecimal represented integer.
412
        This works for default arguments too, because it returns
413
        None when it found that it got a NoneType object.
414
    """
584 theseven 415
    if type(something).__name__ in ("int", "long", "NoneType"):
532 farthen 416
        return something
417
    elif type(something) == str:
418
        try:
419
            if something[:2] == "0x": # Hexadecimal notation
420
                return int(something[2:], 16)
421
            elif something[:2] == "0b": # Binary notation
422
                return int(something[2:], 2)
423
            else: # Decimal notation
424
                return int(something, 10)
425
        except ValueError:
584 theseven 426
            raise ArgumentTypeError("integer", "'%s' (%s)" % (something, type(something).__name__))
532 farthen 427
    else:
584 theseven 428
        raise ArgumentTypeError("integer", "'%s' (%s)" % (something, type(something).__name__))
582 farthen 429
 
430
 
431
def majorver():
432
    """
433
        Returns the major version of python
434
    """
435
    import sys
436
    return sys.hexversion // 0x1000000