Rev 811 | Blame | Last modification | View Log | RSS feed
#!/usr/bin/env python### Copyright 2010 TheSeven, benedikt93, Farthen### This file is part of emCORE.## emCORE is free software: you can redistribute it and/or# modify it under the terms of the GNU General Public License as# published by the Free Software Foundation, either version 2 of the# License, or (at your option) any later version.## emCORE is distributed in the hope that it will be useful,# but WITHOUT ANY WARRANTY; without even the implied warranty of# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.# See the GNU General Public License for more details.## You should have received a copy of the GNU General Public License# along with emCORE. If not, see <http://www.gnu.org/licenses/>.##"""This file includes some reusable functions and classes that might be usefulto all python scripts"""import sysfrom ctypes import *from _ctypes import _SimpleCDataclass Error(Exception):def __init__(self, value=None):self.value = valuedef __str__(self):if self.value != None:return repr(self.value)class ArgumentError(Error):passclass ArgumentTypeError(Error):def __init__(self, expected, seen=False):self.expected = expectedself.seen = seendef __str__(self):if self.seen:return "Expected %s but got %s" % (self.expected, self.seen)else:return "Expected %s, but saw something else" % self.expectedclass Logger(object):"""Simple stdout/stderr/file logger.Loglevel 3 is most verbose, Loglevel 0: Only log something if there is an error.Loglevel -1 means that nothing is logged.The write function doesn't care about the loglevel and always logs everything."""def __init__(self, loglevel = 2, target = "stderr", logfile = "tools.log"):"""loglevel: Possible values: 0 (only errors), 1 (warnings), 2 (info,recommended for production use), 3 and more (debug)logfile: File to log to if using the target = "file"target: Default logging target. Can be "stdout", "file" or "string""""self.loglevel = loglevelself.logfile = logfileself.target = targetdef write(self, text, indent = 0, target = None):if self.loglevel >= 0:if target is None: target = self.targettext = (indent * " ") + texttext = text.replace("\n", "\n" + (indent * " "), text.count("\n") - 1)if target == "stdout":if majorver() > 2:sys.stdout.write(text)else:sys.stdout.write(text.encode(sys.stdout.encoding or "latin1", "replace"))if target == "stderr":if majorver() > 2:sys.stdout.write(text)else:sys.stderr.write(text.encode(sys.stderr.encoding or "latin1", "replace"))elif target == "file":with open(self.logfile, 'a') as f:f.write(text)f.close()elif target == "string":return textdef debug(self, text, indent = 0, target = None):if self.loglevel >= 3:self.write("DEBUG: " + text, indent, target)def info(self, text, indent = 0, target = None):if self.loglevel >= 2:self.write(text, indent, target)def warn(self, text, indent = 0, target = None):if self.loglevel >= 1:self.write("WARNING: " + text, indent, target)def error(self, text, indent = 0, target = None):if self.loglevel >= 0:self.write("ERROR: " + text, indent, target)class Bunch(dict):"""This is a dict whose items can also be accessed withbunchinstance.something."""def __init__(self, **kw):dict.__init__(self, kw)self.__dict__ = selfdef __getstate__(self):return selfdef __setstate__(self, state):self.update(state)self.__dict__ = selfclass remote_pointer(dict):"""This points to a (remote) location.Otherwise it behaves like a Bunch.The second argument must be a Bunch object"""def __init__(self, address, bunch):dict.__init__(self, bunch.__dict__)self.__dict__ = selfself._address_ = addressdef __getstate__(self):return selfdef __setstate__(self, state):self.update(state)self.__dict__ = selfdef __str__(self):return "<remote_pointer object with address 0x%X>" % (self._address_)def __int__(self):return self._address_def __repr__(self):return self.__str__()class c_enum(_SimpleCData):"""Resembles the enum datatype from C with an 8 bit size.Returns the associated string of a value with c_enum[i]Returns the current value of the associated value as c_enum.__repr__()Comparison operators work with strings and values at the same time.ATTENTION: You can not really see if this is initialized or not.If it is uninitialized it will return the first entry of the enum.While this may be circumvented by changing the default value tosomething else this will not work if the enum is placed inside actypes structure as the __init__() method will not be called then."""_type_ = c_uint8._type_def __init__(self, value = 0):if type(value) == str:value = getattr(self, value)_SimpleCData.__init__(self, value)self[value]def __getattr__(self, name):if name == "value":return self.valuefor key, value in enumerate(self._fields_):if value == name:return keydef __getitem__(self, lookupkey):for key, value in enumerate(self._fields_):if key == lookupkey:return valueraise IndexError("Value %d not in range of possible enum values for %s!" % (lookupkey, self.__class__.__name__))def __str__(self):return self[self.value]def __repr__(self):return self.__str__()def __int__(self):return self.valuedef __eq__(self, other):if type(other) == str:try: return getattr(self, other) == self.valueexcept AttributeError: return Falseelse:return self.value == otherdef __lt__(self, other):if type(other) == str:try: return self.value < getattr(self, other)except AttributeError: return Falseelse:return self.value < otherdef __gt__(self, other):if type(other) == str:try: return self.value > getattr(self, other)except AttributeError: return Falseelse:return self.value > otherdef __le__(self, other):if self.value == other or self.value < other:return Truereturn Falsedef __ge__(self, other):if self.value == other or self.value > other:return Truereturn Falsedef __ne__(self, other):if type(other) == str:try: return getattr(self, other) != self.valueexcept AttributeError: return Trueelse:return self.value != otherclass ExtendedCStruct(LittleEndianStructure):"""This is a subclass of the LittleEndianStructure.It implements functions to easily convertstructures to/from strings and Bunches."""def __init__(self, data = None, base = 0, address = 0):LittleEndianStructure.__init__(self)if data != None:self._data_ = dataself._base_ = baseself._address_ = addressif address < base or address + sizeof(self) > base + len(data):raise Exception("Range 0x%08X+0x%X out of bounds [0x%08X:0x%08X]" % (address, sizeof(self), base, base + len(data)))memmove(addressof(self), data[address - base : address - base + sizeof(self)], sizeof(self))def __int__(self):return self._address_def _from_bunch(self, bunch):for field, _ in self._fields_:if field in bunch:setattr(self, field, getattr(bunch, field))def _to_bunch(self):bunch = Bunch()for field, _ in self._fields_:setattr(bunch, field, getattr(self, field))return bunchdef _from_string(self, string):memmove(addressof(self), string, sizeof(self))def _to_string(self):return string_at(addressof(self), sizeof(self))def getthread(address, threads):"""Returns the thread at <address> from the list of threads <threads>.Returns an empty thread if not found"""for thread in threads:if address == thread.addr:return threadfrom libemcoredata import scheduler_threadthread = scheduler_thread()._to_bunch()thread.name = "[Invalid Thread 0x%08X]" % addressreturn threaddef string_from_image(data, base, ptr, maxlen):if ptr == 0: name = "<NULL>"try:string = ""end = data.find(b"\0", ptr - base, ptr - base + maxlen)if end < 0: return "<BAD_STRING>"else:d = data[ptr - base : end]for i in range(len(d)):byte = ord(d[i : i + 1])if byte < 0x20: return "<BAD_STRING>"else: string = string + chr(byte)except: return "<BAD_PTR>"return stringdef gethwname(id):try:from libemcoredata import hwtypeshwtype = hwtypes[id][1]except KeyError:hwtype = "UNKNOWN (ID = 0x%X)" % idreturn hwtypedef gethwid(shortname):from libemcoredata import hwtypesfor hwid in hwtypes:if hwtypes[hwid][0] == shortname:return hwidreturn Falsedef trimdoc(docstring):"""Trims whitespace from docstrings"""if not docstring:return ''# Convert tabs to spaces (following the normal Python rules)# and split into a list of lines:lines = docstring.expandtabs().splitlines()# Determine minimum indentation (first line doesn't count):try: maxsize = sys.maxintexcept AttributeError: maxsize = sys.maxsizeindent = maxsizefor line in lines[1:]:stripped = line.lstrip()if stripped:indent = min(indent, len(line) - len(stripped))# Remove indentation (first line is special):trimmed = [lines[0].strip()]if indent < maxsize:for line in lines[1:]:trimmed.append(line[indent:].rstrip())# Strip off trailing and leading blank lines:while trimmed and not trimmed[-1]:trimmed.pop()while trimmed and not trimmed[0]:trimmed.pop(0)# Return a single string:return '\n'.join(trimmed)def getfuncdoc(funcdict):"""Extracts important information from a dict of functions like thedocstring and arguments and returns them in a human readable format"""import inspectimport refunctions = Bunch()for function in funcdict:function = funcdict[function].funcdocinfo = Bunch()name = function.__name__argspec = inspect.getargspec(function)args = argspec[0]docinfo['varargs'] = Falseif argspec[1]:docinfo['varargs'] = Truekwargvalues = argspec[3]kwargs = []if args:if kwargvalues:argnum = len(args) - len(kwargvalues)kwargnum = len(kwargvalues)kwargs = list(zip(args[argnum:], kwargvalues))else:argnum = len(args)else:argnum = 0docinfo['args'] = args[1:argnum]docinfo['kwargs'] = kwargsif function.__doc__:# strip unneccessary whitespacedocinfo['documentation'] = trimdoc(function.__doc__)else:docinfo['documentation'] = Nonefunctions[name] = docinforeturn functionsdef gendoc(funcdict, indentwidth = 4, logtarget = "string"):logger = Logger()doc = getfuncdoc(funcdict)ret = ""for function in sorted(doc.items()):function = function[0]ret += logger.log("def " + function + "(", target = logtarget)counter = 0if doc[function]['args']:for arg in doc[function]['args']:if counter > 0:sys.stdout.write(", ")counter += 1ret += logger.log(arg, target = logtarget)if doc[function]['kwargs']:for kwarg, kwargvalue in doc[function]['kwargs'].items():if counter > 0:sys.stdout.write(", ")counter += 1ret += logger.log(kwarg + "=" + str(kwargvalue), target = logtarget)if doc[function]['varargs']:ret += logger.log("*argv", target = logtarget)ret += logger.log("):\n", target = logtarget)if doc[function]['documentation']:ret += logger.log("\"\"\"\n", indent = indentwidth, target = logtarget)ret += logger.log(trimdoc(doc[function]['documentation']) + "\n", indent = 2 * indentwidth, target = logtarget)ret += logger.log("\"\"\"\n", indent = indentwidth, target = logtarget)ret += logger.log("\n", target = logtarget)return retdef to_bool(something):"""Converts quite everything into bool."""if type(something).__name__ in ("bool", "NoneType"):return somethingelif type(something).__name__ in ("int", "long"):return something != 0elif type(something) == str:if something.lower() in ['true', '1', 't', 'y', 'yes']:return Trueelif something.lower() in ['false', '0', 'f', 'n', 'no']:return Falseraise ArgumentTypeError("bool", "'%s' (%s)" % (something, type(something).__name__))def to_int(something):"""Converts quite everything to a hexadecimal represented integer.This works for default arguments too, because it returnsNone when it found that it got a NoneType object."""if type(something).__name__ in ("int", "long", "NoneType"):return somethingelif type(something) == str:try:if something[:2] == "0x": # Hexadecimal notationreturn int(something[2:], 16)elif something[:2] == "0b": # Binary notationreturn int(something[2:], 2)else: # Decimal notationreturn int(something, 10)except ValueError:raise ArgumentTypeError("integer", "'%s' (%s)" % (something, type(something).__name__))else:raise ArgumentTypeError("integer", "'%s' (%s)" % (something, type(something).__name__))def majorver():"""Returns the major version of python"""import sysreturn sys.hexversion // 0x1000000