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