Rev 532 | 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":sys.stderr.write(text)if target == "stderr":sys.stderr.write(text)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 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 self.value == other:return Falsereturn Trueclass ExtendedCStruct(LittleEndianStructure):"""This is a subclass of the LittleEndianStructure.It implements functions to easily convertstructures to/from strings and Bunches."""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 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):indent = sys.maxintfor 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 < sys.maxint: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__args = inspect.getargspec(function)[0]docinfo['varargs'] = Falseif inspect.getargspec(function)[1]:docinfo['varargs'] = Truekwargvalues = inspect.getargspec(function)[3]kwargs = Bunch()if args:if kwargvalues:argnum = len(args) - len(kwargvalues)kwargnum = len(kwargvalues)kwargs = dict(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) == bool:return somethingif something is None:return Falseelif type(something) == int or type(something) == long:return bool(something)elif 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'" % something)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) == int or type(something) == long:return somethingelif something is None:return Noneelif 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'" % something)else:raise ArgumentTypeError("integer", "'%s'" % something)