| 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
|