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