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":
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):
604 farthen 198
        if type(other) == str:
199
            try: return getattr(self, other) != self.value
200
            except AttributeError: return True
201
        else:
202
            return self.value != other
505 farthen 203
 
204
 
205
class ExtendedCStruct(LittleEndianStructure):
206
    """
207
        This is a subclass of the LittleEndianStructure.
208
        It implements functions to easily convert
209
        structures to/from strings and Bunches.
210
    """
211
    def _from_bunch(self, bunch):
212
        for field, _ in self._fields_:
213
            if field in bunch:
214
                setattr(self, field, getattr(bunch, field))
215
 
216
    def _to_bunch(self):
510 farthen 217
        bunch = Bunch()
218
        for field, _ in self._fields_:
219
            setattr(bunch, field, getattr(self, field))
220
        return bunch
505 farthen 221
 
222
    def _from_string(self, string):
223
        memmove(addressof(self), string, sizeof(self))
224
 
225
    def _to_string(self):
226
        return string_at(addressof(self), sizeof(self))
227
 
228
 
398 farthen 229
def gethwname(id):
230
    try:
532 farthen 231
        from libemcoredata import hwtypes
549 farthen 232
        hwtype = hwtypes[id][1]
398 farthen 233
    except KeyError:
532 farthen 234
        hwtype = "UNKNOWN (ID = 0x%X)" % id
398 farthen 235
    return hwtype
236
 
549 farthen 237
def gethwid(shortname):
238
    from libemcoredata import hwtypes
239
    for hwid in hwtypes:
240
        if hwtypes[hwid][0] == shortname:
241
            return hwid
242
    return False
398 farthen 243
 
549 farthen 244
 
396 farthen 245
def trimdoc(docstring):
246
    """
247
        Trims whitespace from docstrings
248
    """
249
    if not docstring:
250
        return ''
251
    # Convert tabs to spaces (following the normal Python rules)
252
    # and split into a list of lines:
253
    lines = docstring.expandtabs().splitlines()
254
    # Determine minimum indentation (first line doesn't count):
582 farthen 255
    try: maxsize = sys.maxint
256
    except AttributeError: maxsize = sys.maxsize
257
    indent = maxsize
396 farthen 258
    for line in lines[1:]:
259
        stripped = line.lstrip()
260
        if stripped:
261
            indent = min(indent, len(line) - len(stripped))
262
    # Remove indentation (first line is special):
263
    trimmed = [lines[0].strip()]
582 farthen 264
    if indent < maxsize:
396 farthen 265
        for line in lines[1:]:
266
            trimmed.append(line[indent:].rstrip())
267
    # Strip off trailing and leading blank lines:
268
    while trimmed and not trimmed[-1]:
269
        trimmed.pop()
270
    while trimmed and not trimmed[0]:
271
        trimmed.pop(0)
272
    # Return a single string:
273
    return '\n'.join(trimmed)
274
 
275
 
276
def getfuncdoc(funcdict):
277
    """
278
        Extracts important information from a dict of functions like the
279
        docstring and arguments and returns them in a human readable format
280
    """
281
    import inspect
282
    import re
283
    functions = Bunch()
284
    for function in funcdict:
285
        function = funcdict[function].func
286
        docinfo = Bunch()
287
        name = function.__name__
579 farthen 288
        argspec = inspect.getargspec(function)
289
        args = argspec[0]
396 farthen 290
        docinfo['varargs'] = False
579 farthen 291
        if argspec[1]:
396 farthen 292
            docinfo['varargs'] = True
579 farthen 293
        kwargvalues = argspec[3]
294
        kwargs = []
396 farthen 295
        if args:
296
            if kwargvalues:
297
                argnum = len(args) - len(kwargvalues)
298
                kwargnum = len(kwargvalues)
579 farthen 299
                kwargs = list(zip(args[argnum:], kwargvalues))
396 farthen 300
            else:
301
                argnum = len(args)
302
        else:
303
            argnum = 0
304
        docinfo['args'] = args[1:argnum]
305
        docinfo['kwargs'] = kwargs
306
        if function.__doc__:
307
            # strip unneccessary whitespace
308
            docinfo['documentation'] = trimdoc(function.__doc__)
309
        else:
310
            docinfo['documentation'] = None
311
        functions[name] = docinfo
312
    return functions
313
 
314
 
315
def gendoc(funcdict, indentwidth = 4, logtarget = "string"):
316
    logger = Logger()
317
    doc = getfuncdoc(funcdict)
318
    ret = ""
319
    for function in sorted(doc.items()):
320
        function = function[0]
321
        ret += logger.log("def " + function + "(", target = logtarget)
322
        counter = 0
323
        if doc[function]['args']:
324
            for arg in doc[function]['args']:
325
                if counter > 0:
326
                    sys.stdout.write(", ")
327
                counter += 1
328
                ret += logger.log(arg, target = logtarget)
329
        if doc[function]['kwargs']:
330
            for kwarg, kwargvalue in doc[function]['kwargs'].items():
331
                if counter > 0:
332
                    sys.stdout.write(", ")
333
                counter += 1
334
                ret += logger.log(kwarg + "=" + str(kwargvalue), target = logtarget)
335
        if doc[function]['varargs']:
336
            ret += logger.log("*argv", target = logtarget)
337
        ret += logger.log("):\n", target = logtarget)
338
        if doc[function]['documentation']:
339
            ret += logger.log("\"\"\"\n", indent = indentwidth, target = logtarget)
340
            ret += logger.log(trimdoc(doc[function]['documentation']) + "\n", indent = 2 * indentwidth, target = logtarget)
341
            ret += logger.log("\"\"\"\n", indent = indentwidth, target = logtarget)
342
        ret += logger.log("\n", target = logtarget)
532 farthen 343
    return ret
344
 
345
 
346
def to_bool(something):
347
    """
348
        Converts quite everything into bool.
349
    """
584 theseven 350
    if type(something).__name__ in ("bool", "NoneType"):
532 farthen 351
        return something
584 theseven 352
    elif type(something).__name__ in ("int", "long"):
353
        return something != 0
354
    elif type(something) == str:
532 farthen 355
        if something.lower() in ['true', '1', 't', 'y', 'yes']:
356
            return True
357
        elif something.lower() in ['false', '0', 'f', 'n', 'no']:
358
            return False
584 theseven 359
    raise ArgumentTypeError("bool", "'%s' (%s)" % (something, type(something).__name__))
532 farthen 360
 
361
def to_int(something):
362
    """
363
        Converts quite everything to a hexadecimal represented integer.
364
        This works for default arguments too, because it returns
365
        None when it found that it got a NoneType object.
366
    """
584 theseven 367
    if type(something).__name__ in ("int", "long", "NoneType"):
532 farthen 368
        return something
369
    elif type(something) == str:
370
        try:
371
            if something[:2] == "0x": # Hexadecimal notation
372
                return int(something[2:], 16)
373
            elif something[:2] == "0b": # Binary notation
374
                return int(something[2:], 2)
375
            else: # Decimal notation
376
                return int(something, 10)
377
        except ValueError:
584 theseven 378
            raise ArgumentTypeError("integer", "'%s' (%s)" % (something, type(something).__name__))
532 farthen 379
    else:
584 theseven 380
        raise ArgumentTypeError("integer", "'%s' (%s)" % (something, type(something).__name__))
582 farthen 381
 
382
 
383
def majorver():
384
    """
385
        Returns the major version of python
386
    """
387
    import sys
388
    return sys.hexversion // 0x1000000