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
427 farthen 32
import libemcoredata
396 farthen 33
 
34
class Logger(object):
35
    """
501 farthen 36
        Simple stdout/stderr/file logger.
401 farthen 37
        Loglevel 3 is most verbose, Loglevel 0: Only log something if there is an error.
38
        Loglevel -1 means that nothing is logged.
501 farthen 39
        The write function doesn't care about the loglevel and always logs everything.
396 farthen 40
    """
501 farthen 41
    def __init__(self, loglevel = 2, target = "stderr", logfile = "tools.log"):
400 farthen 42
        """
43
            loglevel: Possible values: 0 (only errors), 1 (warnings), 2 (info,
44
                      recommended for production use), 3 and more (debug)
45
            logfile: File to log to if using the target = "file"
46
            target: Default logging target. Can be "stdout", "file" or "string"
47
        """
48
        self.loglevel = loglevel
49
        self.logfile = logfile
50
        self.target = target
396 farthen 51
 
501 farthen 52
    def write(self, text, indent = 0, target = None):
401 farthen 53
        if self.loglevel >= 0:
54
            if target is None: target = self.target
55
            text = (indent * " ") + text
56
            text = text.replace("\n", "\n" + (indent * " "), text.count("\n") - 1)
57
            if target == "stdout":
501 farthen 58
                sys.stderr.write(text)
59
            if target == "stderr":
60
                sys.stderr.write(text)
401 farthen 61
            elif target == "file":
62
                with open(self.logfile, 'a') as f:
63
                    f.write(text)
64
                    f.close()
65
            elif target == "string":
66
                return text
396 farthen 67
 
400 farthen 68
    def debug(self, text, indent = 0, target = None):
396 farthen 69
        if self.loglevel >= 3:
501 farthen 70
            self.write("DEBUG: " + text, indent, target)
396 farthen 71
 
400 farthen 72
    def info(self, text, indent = 0, target = None):
396 farthen 73
        if self.loglevel >= 2:
501 farthen 74
            self.write(text, indent, target)
396 farthen 75
 
400 farthen 76
    def warn(self, text, indent = 0, target = None):
396 farthen 77
        if self.loglevel >= 1:
501 farthen 78
            self.write("WARNING: " + text, indent, target)
396 farthen 79
 
400 farthen 80
    def error(self, text, indent = 0, target = None):
401 farthen 81
        if self.loglevel >= 0:
501 farthen 82
            self.write("ERROR: " + text, indent, target)
396 farthen 83
 
84
 
85
class Bunch(dict):
86
    """
87
        This is a dict whose items can also be accessed with
88
        bunchinstance.something.
89
    """
90
    def __init__(self, **kw):
91
        dict.__init__(self, kw)
92
        self.__dict__ = self
93
 
94
    def __getstate__(self):
95
        return self
96
 
97
    def __setstate__(self, state):
98
        self.update(state)
99
        self.__dict__ = self
100
 
101
 
505 farthen 102
class c_enum(_SimpleCData):
103
    """
104
        Resembles the enum datatype from C with an 8 bit size.
105
        Returns the associated string of a value with c_enum[i]
106
        Returns the current value of the associated value as c_enum.__repr__()
107
        Comparison operators work with strings and values at the same time.
108
 
109
        ATTENTION: You can not really see if this is initialized or not.
110
        If it is uninitialized it will return the first entry of the enum.
111
        While this may be circumvented by changing the default value to
112
        something else this will not work if the enum is placed inside a
113
        ctypes structure as the __init__() method will not be called then.
114
    """    
115
    _type_ = c_uint8._type_
116
 
117
    def __init__(self, value = 0):
118
        if type(value) == str:
119
            value = getattr(self, value)
120
        _SimpleCData.__init__(self, value)
121
        self[value]
122
 
123
    def __getattr__(self, name):
124
        if name == "value":
125
            return self.value
126
        for key, value in enumerate(self._fields_):
127
            if value == name:
128
                return key
129
 
130
    def __getitem__(self, lookupkey):
131
        for key, value in enumerate(self._fields_):
132
            if key == lookupkey:
133
                return value
134
        raise IndexError("Value %d not in range of possible enum values for %s!" % (lookupkey, self.__class__.__name__))
135
 
136
    def __str__(self):
137
        return self[self.value]
138
 
139
    def __repr__(self):
140
        return self.__str__()
141
 
142
    def __eq__(self, other):
143
        if type(other) == str:
144
            try: return getattr(self, other) == self.value
145
            except AttributeError: return False
146
        else:
147
            return self.value == other
148
 
149
    def __lt__(self, other):
150
        if type(other) == str:
151
            try: return self.value < getattr(self, other)
152
            except AttributeError: return False
153
        else:
154
            return self.value < other
155
 
156
    def __gt__(self, other):
157
        if type(other) == str:
158
            try: return  self.value > getattr(self, other)
159
            except AttributeError: return False
160
        else:
161
            return self.value > other
162
 
163
    def __le__(self, other):
164
        if self.value == other or self.value < other:
165
            return True
166
        return False
167
 
168
    def __ge__(self, other):
169
        if self.value == other or self.value > other:
170
            return True
171
        return False
172
 
173
    def __ne__(self, other):
174
        if self.value == other:
175
            return False
176
        return True
177
 
178
 
179
class ExtendedCStruct(LittleEndianStructure):
180
    """
181
        This is a subclass of the LittleEndianStructure.
182
        It implements functions to easily convert
183
        structures to/from strings and Bunches.
184
    """
185
    def _from_bunch(self, bunch):
186
        for field, _ in self._fields_:
187
            if field in bunch:
188
                setattr(self, field, getattr(bunch, field))
189
 
190
    def _to_bunch(self):
510 farthen 191
        bunch = Bunch()
192
        for field, _ in self._fields_:
193
            setattr(bunch, field, getattr(self, field))
194
        return bunch
505 farthen 195
 
196
    def _from_string(self, string):
197
        memmove(addressof(self), string, sizeof(self))
198
 
199
    def _to_string(self):
200
        return string_at(addressof(self), sizeof(self))
201
 
202
 
203
 
396 farthen 204
class Error(Exception):
205
    def __init__(self, value=None):
206
        self.value = value
207
    def __str__(self):
208
        if self.value != None:
209
            return repr(self.value)
210
 
211
 
398 farthen 212
def gethwname(id):
213
    try:
427 farthen 214
        hwtype = libemcoredata.hwtypes[id]
398 farthen 215
    except KeyError:
216
        hwtype = "UNKNOWN (ID = " + self._hex(id) + ")"
217
    return hwtype
218
 
219
 
396 farthen 220
def trimdoc(docstring):
221
    """
222
        Trims whitespace from docstrings
223
    """
224
    if not docstring:
225
        return ''
226
    # Convert tabs to spaces (following the normal Python rules)
227
    # and split into a list of lines:
228
    lines = docstring.expandtabs().splitlines()
229
    # Determine minimum indentation (first line doesn't count):
230
    indent = sys.maxint
231
    for line in lines[1:]:
232
        stripped = line.lstrip()
233
        if stripped:
234
            indent = min(indent, len(line) - len(stripped))
235
    # Remove indentation (first line is special):
236
    trimmed = [lines[0].strip()]
237
    if indent < sys.maxint:
238
        for line in lines[1:]:
239
            trimmed.append(line[indent:].rstrip())
240
    # Strip off trailing and leading blank lines:
241
    while trimmed and not trimmed[-1]:
242
        trimmed.pop()
243
    while trimmed and not trimmed[0]:
244
        trimmed.pop(0)
245
    # Return a single string:
246
    return '\n'.join(trimmed)
247
 
248
 
249
def getfuncdoc(funcdict):
250
    """
251
        Extracts important information from a dict of functions like the
252
        docstring and arguments and returns them in a human readable format
253
    """
254
    import inspect
255
    import re
256
    functions = Bunch()
257
    for function in funcdict:
258
        function = funcdict[function].func
259
        docinfo = Bunch()
260
        name = function.__name__
261
        args = inspect.getargspec(function)[0]
262
        docinfo['varargs'] = False
263
        if inspect.getargspec(function)[1]:
264
            docinfo['varargs'] = True
265
        kwargvalues = inspect.getargspec(function)[3]
266
        kwargs = Bunch()
267
        if args:
268
            if kwargvalues:
269
                argnum = len(args) - len(kwargvalues)
270
                kwargnum = len(kwargvalues)
271
                kwargs = dict(zip(args[argnum:], kwargvalues))
272
            else:
273
                argnum = len(args)
274
        else:
275
            argnum = 0
276
        docinfo['args'] = args[1:argnum]
277
        docinfo['kwargs'] = kwargs
278
        if function.__doc__:
279
            # strip unneccessary whitespace
280
            docinfo['documentation'] = trimdoc(function.__doc__)
281
        else:
282
            docinfo['documentation'] = None
283
        functions[name] = docinfo
284
    return functions
285
 
286
 
287
def gendoc(funcdict, indentwidth = 4, logtarget = "string"):
288
    logger = Logger()
289
    doc = getfuncdoc(funcdict)
290
    ret = ""
291
    for function in sorted(doc.items()):
292
        function = function[0]
293
        ret += logger.log("def " + function + "(", target = logtarget)
294
        counter = 0
295
        if doc[function]['args']:
296
            for arg in doc[function]['args']:
297
                if counter > 0:
298
                    sys.stdout.write(", ")
299
                counter += 1
300
                ret += logger.log(arg, target = logtarget)
301
        if doc[function]['kwargs']:
302
            for kwarg, kwargvalue in doc[function]['kwargs'].items():
303
                if counter > 0:
304
                    sys.stdout.write(", ")
305
                counter += 1
306
                ret += logger.log(kwarg + "=" + str(kwargvalue), target = logtarget)
307
        if doc[function]['varargs']:
308
            ret += logger.log("*argv", target = logtarget)
309
        ret += logger.log("):\n", target = logtarget)
310
        if doc[function]['documentation']:
311
            ret += logger.log("\"\"\"\n", indent = indentwidth, target = logtarget)
312
            ret += logger.log(trimdoc(doc[function]['documentation']) + "\n", indent = 2 * indentwidth, target = logtarget)
313
            ret += logger.log("\"\"\"\n", indent = indentwidth, target = logtarget)
314
        ret += logger.log("\n", target = logtarget)
315
    return ret