freemyipod r396 - Code Review

Jump to: navigation, search
Repository:freemyipod
Revision:r395‎ | r396 | r397 >
Date:01:10, 30 December 2010
Author:farthen
Status:new
Tags:
Comment:
embios.py/libembios: Extract reusable functions and classes and put them into misc.py.
libembios: Running it directly with "gendoc"as first argument generates a human readable function reference. Running it with "test" runs the tests.
Modified paths:
  • /embios/trunk/tools/embios.py (modified) (history)
  • /embios/trunk/tools/libembios.py (modified) (history)
  • /embios/trunk/tools/misc.py (added) (history)

Diff [purge]

Index: embios/trunk/tools/misc.py
@@ -0,0 +1,185 @@
 2+#!/usr/bin/env python
 3+#
 4+#
 5+# Copyright 2010 TheSeven, benedikt93, Farthen
 6+#
 7+#
 8+# This file is part of emBIOS.
 9+#
 10+# emBIOS is free software: you can redistribute it and/or
 11+# modify it under the terms of the GNU General Public License as
 12+# published by the Free Software Foundation, either version 2 of the
 13+# License, or (at your option) any later version.
 14+#
 15+# emBIOS is distributed in the hope that it will be useful,
 16+# but WITHOUT ANY WARRANTY; without even the implied warranty of
 17+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 18+# See the GNU General Public License for more details.
 19+#
 20+# You should have received a copy of the GNU General Public License
 21+# along with emBIOS. If not, see <http://www.gnu.org/licenses/>.
 22+#
 23+#
 24+
 25+"""
 26+ This file includes some reusable functions and classes that might be useful
 27+ to all python scripts
 28+"""
 29+
 30+import sys
 31+
 32+class Logger(object):
 33+ """
 34+ Simple stdout logger.
 35+ Loglevel 4 is most verbose, Loglevel 0: Only say something if there is an error.
 36+ The log function doesn't care about the loglevel and always logs to stdout.
 37+ """
 38+ def __init__(self):
 39+ # Possible values: 0 (only errors), 1 (warnings), 2 (info, recommended for production use), 3 and more (debug)
 40+ self.loglevel = 3
 41+
 42+ def log(self, text, indent = 0, target = "stdout"):
 43+ text = (indent * " ") + text
 44+ text = text.replace("\n", "\n" + (indent * " "), text.count("\n") - 1)
 45+ if target == "stdout":
 46+ sys.stdout.write(text)
 47+ elif target == "string":
 48+ return text
 49+
 50+ def debug(self, text, indent = 0, target = "stdout"):
 51+ if self.loglevel >= 3:
 52+ self.log("DEBUG: " + text, indent, target)
 53+
 54+ def info(self, text, indent = 0, target = "stdout"):
 55+ if self.loglevel >= 2:
 56+ self.log(text, indent, target)
 57+
 58+ def warn(self, text, indent = 0, target = "stdout"):
 59+ if self.loglevel >= 1:
 60+ self.log("WARNING: " + text, indent, target)
 61+
 62+ def error(self, text, indent = 0, target = "stdout"):
 63+ self.log("ERROR: " + text, indent, target)
 64+
 65+
 66+class Bunch(dict):
 67+ """
 68+ This is a dict whose items can also be accessed with
 69+ bunchinstance.something.
 70+ """
 71+ def __init__(self, **kw):
 72+ dict.__init__(self, kw)
 73+ self.__dict__ = self
 74+
 75+ def __getstate__(self):
 76+ return self
 77+
 78+ def __setstate__(self, state):
 79+ self.update(state)
 80+ self.__dict__ = self
 81+
 82+
 83+class Error(Exception):
 84+ def __init__(self, value=None):
 85+ self.value = value
 86+ def __str__(self):
 87+ if self.value != None:
 88+ return repr(self.value)
 89+
 90+
 91+def trimdoc(docstring):
 92+ """
 93+ Trims whitespace from docstrings
 94+ """
 95+ if not docstring:
 96+ return ''
 97+ # Convert tabs to spaces (following the normal Python rules)
 98+ # and split into a list of lines:
 99+ lines = docstring.expandtabs().splitlines()
 100+ # Determine minimum indentation (first line doesn't count):
 101+ indent = sys.maxint
 102+ for line in lines[1:]:
 103+ stripped = line.lstrip()
 104+ if stripped:
 105+ indent = min(indent, len(line) - len(stripped))
 106+ # Remove indentation (first line is special):
 107+ trimmed = [lines[0].strip()]
 108+ if indent < sys.maxint:
 109+ for line in lines[1:]:
 110+ trimmed.append(line[indent:].rstrip())
 111+ # Strip off trailing and leading blank lines:
 112+ while trimmed and not trimmed[-1]:
 113+ trimmed.pop()
 114+ while trimmed and not trimmed[0]:
 115+ trimmed.pop(0)
 116+ # Return a single string:
 117+ return '\n'.join(trimmed)
 118+
 119+
 120+def getfuncdoc(funcdict):
 121+ """
 122+ Extracts important information from a dict of functions like the
 123+ docstring and arguments and returns them in a human readable format
 124+ """
 125+ import inspect
 126+ import re
 127+ functions = Bunch()
 128+ for function in funcdict:
 129+ function = funcdict[function].func
 130+ docinfo = Bunch()
 131+ name = function.__name__
 132+ args = inspect.getargspec(function)[0]
 133+ docinfo['varargs'] = False
 134+ if inspect.getargspec(function)[1]:
 135+ docinfo['varargs'] = True
 136+ kwargvalues = inspect.getargspec(function)[3]
 137+ kwargs = Bunch()
 138+ if args:
 139+ if kwargvalues:
 140+ argnum = len(args) - len(kwargvalues)
 141+ kwargnum = len(kwargvalues)
 142+ kwargs = dict(zip(args[argnum:], kwargvalues))
 143+ else:
 144+ argnum = len(args)
 145+ else:
 146+ argnum = 0
 147+ docinfo['args'] = args[1:argnum]
 148+ docinfo['kwargs'] = kwargs
 149+ if function.__doc__:
 150+ # strip unneccessary whitespace
 151+ docinfo['documentation'] = trimdoc(function.__doc__)
 152+ else:
 153+ docinfo['documentation'] = None
 154+ functions[name] = docinfo
 155+ return functions
 156+
 157+
 158+def gendoc(funcdict, indentwidth = 4, logtarget = "string"):
 159+ logger = Logger()
 160+ doc = getfuncdoc(funcdict)
 161+ ret = ""
 162+ for function in sorted(doc.items()):
 163+ function = function[0]
 164+ ret += logger.log("def " + function + "(", target = logtarget)
 165+ counter = 0
 166+ if doc[function]['args']:
 167+ for arg in doc[function]['args']:
 168+ if counter > 0:
 169+ sys.stdout.write(", ")
 170+ counter += 1
 171+ ret += logger.log(arg, target = logtarget)
 172+ if doc[function]['kwargs']:
 173+ for kwarg, kwargvalue in doc[function]['kwargs'].items():
 174+ if counter > 0:
 175+ sys.stdout.write(", ")
 176+ counter += 1
 177+ ret += logger.log(kwarg + "=" + str(kwargvalue), target = logtarget)
 178+ if doc[function]['varargs']:
 179+ ret += logger.log("*argv", target = logtarget)
 180+ ret += logger.log("):\n", target = logtarget)
 181+ if doc[function]['documentation']:
 182+ ret += logger.log("\"\"\"\n", indent = indentwidth, target = logtarget)
 183+ ret += logger.log(trimdoc(doc[function]['documentation']) + "\n", indent = 2 * indentwidth, target = logtarget)
 184+ ret += logger.log("\"\"\"\n", indent = indentwidth, target = logtarget)
 185+ ret += logger.log("\n", target = logtarget)
 186+ return ret
\ No newline at end of file
Index: embios/trunk/tools/embios.py
@@ -23,8 +23,6 @@
2424
2525 import sys
2626 import os
27 -import inspect
28 -import re
2927 import time
3028 import struct
3129 import locale
@@ -32,8 +30,8 @@
3331 from functools import wraps
3432
3533 import libembios
36 -from libembios import Error
3734 import libembiosdata
 35+from misc import Error, Logger, getfuncdoc
3836
3937
4038 class NotImplementedError(Error):
@@ -59,38 +57,8 @@
6058 It is auto generated from various places.
6159 """
6260 logger = Logger()
63 - cmddict= Commandline.cmddict
64 - doc = {}
65 - # This sorts the output of various internal functions
66 - # and puts everything in easy readable form
67 - for function in cmddict:
68 - function = cmddict[function].func
69 - docinfo = {}
70 - name = function.__name__
71 - args = inspect.getargspec(function)[0]
72 - docinfo['varargs'] = False
73 - if inspect.getargspec(function)[1]:
74 - docinfo['varargs'] = True
75 - kwargvalues = inspect.getargspec(function)[3]
76 - kwargs = {}
77 - if args:
78 - if kwargvalues:
79 - argnum = len(args) - len(kwargvalues)
80 - kwargnum = len(kwargvalues)
81 - kwargs = dict(zip(args[argnum:], kwargvalues))
82 - else:
83 - argnum = len(args)
84 - else:
85 - argnum = 0
86 - docinfo['args'] = args[1:argnum]
87 - docinfo['kwargs'] = kwargs
88 - if function.__doc__:
89 - # strip unneccessary whitespace
90 - docinfo['documentation'] = re.sub(r'\n ', '\n', function.__doc__)
91 - else:
92 - docinfo['documentation'] = None
93 - doc[name] = docinfo
94 -
 61+ cmddict = Commandline.cmddict
 62+ doc = getfuncdoc(cmddict)
9563 if not specific:
9664 logger.log("Please provide a command and (if needed) parameters as command line arguments\n\n")
9765 logger.log("Available commands:\n\n")
@@ -99,7 +67,7 @@
10068 for function in sorted(doc.items()):
10169 function = function[0]
10270 if specific == False or specific == function:
103 - logger.log(" " + function + " ")
 71+ logger.log(function + " ", 2)
10472 for arg in doc[function]['args']:
10573 logger.log("<" + arg + "> ")
10674 if doc[function]['kwargs']:
@@ -107,9 +75,10 @@
10876 logger.log("[" + kwarg + "] ")
10977 if doc[function]['varargs']:
11078 logger.log("<db1> ... <dbN>")
 79+ logger.log("\n")
11180 if doc[function]['documentation']:
112 - logger.log(doc[function]['documentation']+"\n")
113 -
 81+ logger.log(doc[function]['documentation']+"\n", 4)
 82+ logger.log("\n")
11483 logger.log("\n")
11584
11685 if errormsg:
@@ -117,35 +86,6 @@
11887 exit(2)
11988
12089
121 -class Logger(object):
122 - """
123 - Simple stdout logger.
124 - Loglevel 4 is most verbose, Loglevel 0: Only say something if there is an error.
125 - The log function doesn't care about the loglevel and always logs to stdout.
126 - """
127 - def __init__(self):
128 - # Possible values: 0 (only errors), 1 (warnings), 2 (info, recommended for production use), 3 and more (debug)
129 - self.loglevel = 3
130 -
131 - def log(self, text):
132 - sys.stdout.write(text)
133 -
134 - def debug(self, text):
135 - if self.loglevel >= 3:
136 - self.log("DEBUG: " + text)
137 -
138 - def info(self, text):
139 - if self.loglevel >= 2:
140 - self.log(text)
141 -
142 - def warn(self, text):
143 - if self.loglevel >= 1:
144 - self.log("WARNING: " + text)
145 -
146 - def error(self, text):
147 - self.log("ERROR: " + text)
148 -
149 -
15090 def command(func):
15191 """
15292 Decorator for all commands.
@@ -506,17 +446,17 @@
507447 % (len(threads), cpuload * 100, coreload * 100, threadload * 100))
508448 self.logger.info("Thread dump:\n")
509449 for thread in threads:
510 - self.logger.info(" "+thread.name+":\n")
511 - self.logger.info(" Thread id: " + str(thread.id)+"\n")
512 - self.logger.info(" Thread type: " + thread.type+"\n")
513 - self.logger.info(" Thread state: " + thread.state+"\n")
514 - self.logger.info(" Block type: " + thread.block_type+"\n")
515 - self.logger.info(" Blocked by: " + self._hex(thread.blocked_by_ptr)+"\n")
516 - self.logger.info(" Priority: " + str(thread.priority)+"/255\n")
517 - self.logger.info(" Current CPU load: %.1f%%\n" % ((thread.cpuload * 100) / 255.))
518 - self.logger.info(" CPU time (total): "+str(datetime.timedelta(microseconds = thread.cputime_total))+"\n")
519 - self.logger.info(" Stack address: " + self._hex(thread.stackaddr)+"\n")
520 - self.logger.info(" Registers:\n")
 450+ self.logger.info(thread.name+":\n", 2)
 451+ self.logger.info("Thread id: " + str(thread.id)+"\n", 4)
 452+ self.logger.info("Thread type: " + thread.type+"\n", 4)
 453+ self.logger.info("Thread state: " + thread.state+"\n", 4)
 454+ self.logger.info("Block type: " + thread.block_type+"\n", 4)
 455+ self.logger.info("Blocked by: " + self._hex(thread.blocked_by_ptr)+"\n", 4)
 456+ self.logger.info("Priority: " + str(thread.priority)+"/255\n", 4)
 457+ self.logger.info("Current CPU load: %.1f%%\n" % ((thread.cpuload * 100) / 255.), 4)
 458+ self.logger.info("CPU time (total): "+str(datetime.timedelta(microseconds = thread.cputime_total))+"\n", 4)
 459+ self.logger.info("Stack address: " + self._hex(thread.stackaddr)+"\n", 4)
 460+ self.logger.info("Registers:\n", 4)
521461 for registerrange in range(4):
522462 self.logger.info(" ")
523463 for register in range(registerrange, 16, 4):
@@ -523,7 +463,7 @@
524464 registerrepr = "r"+str(register)
525465 self.logger.info("{0:3s}: 0x{1:08X} ".format(registerrepr, thread.regs["r"+str(register)]))
526466 self.logger.info("\n")
527 - self.logger.info(" cpsr: 0x{0:08X}".format(thread.regs.cpsr))
 467+ self.logger.info("cpsr: 0x{0:08X}".format(thread.regs.cpsr), 6)
528468 self.logger.info("\n")
529469
530470 @command
Index: embios/trunk/tools/libembios.py
@@ -26,15 +26,9 @@
2727 import usb.core
2828 import libembiosdata
2929
 30+from misc import Bunch, Error
3031 from functools import wraps
3132
32 -class Error(Exception):
33 - def __init__(self, value=None):
34 - self.value = value
35 - def __str__(self):
36 - if self.value != None:
37 - return repr(self.value)
38 -
3933 class ArgumentError(Error):
4034 pass
4135
@@ -49,25 +43,8 @@
5044
5145 class ReceiveError(Error):
5246 pass
53 -
54 -
55 -class Bunch(dict):
56 - """
57 - This is a dict whose items can also be accessed with
58 - bunchinstance.something.
59 - """
60 - def __init__(self, **kw):
61 - dict.__init__(self, kw)
62 - self.__dict__ = self
6347
64 - def __getstate__(self):
65 - return self
66 -
67 - def __setstate__(self, state):
68 - self.update(state)
69 - self.__dict__ = self
7048
71 -
7249 def command(timeout = None):
7350 """
7451 Decorator for all commands.
@@ -96,6 +73,8 @@
9774 if timeout is not None:
9875 self.lib.dev.timeout = oldtimeout
9976 return ret
 77+ func._command = True
 78+ wrapper.func = func
10079 return wrapper
10180 return decorator
10281
@@ -962,31 +941,45 @@
963942
964943
965944 if __name__ == "__main__":
966 - # Some tests
967 - import sys
968 - embios = Embios()
969 - resp = embios.getversioninfo()
970 - sys.stdout.write("Embios device version information: " + libembiosdata.swtypes[resp.swtypeid] + " v" + str(resp.majorv) + "." + str(resp.minorv) +
971 - "." + str(resp.patchv) + " r" + str(resp.revision) + " running on " + libembiosdata.hwtypes[resp.hwtypeid] + "\n")
972 - resp = embios.getusermemrange()
973 - sys.stdout.write("Usermemrange: "+hex(resp.lower)+" - "+hex(resp.upper)+"\n")
974 - memaddr = resp.lower
975 - maxlen = resp.upper - resp.lower
976 - f = open("./embios.py", "rb")
977 - sys.stdout.write("Loading test file (embios.py) to send over USB...\n")
978 - datastr = f.read()[:maxlen]
979 - sys.stdout.write("Sending data...\n")
980 - embios.write(memaddr, datastr)
981 - sys.stdout.write("Encrypting data with the hardware key...\n")
982 - embios.aesencrypt(memaddr, len(datastr), 0)
983 - sys.stdout.write("Reading data back and saving it to 'libembios-test-encrypted.bin'...\n")
984 - f = open("./libembios-test-encrypted.bin", "wb")
985 - f.write(embios.read(memaddr, len(datastr)))
986 - sys.stdout.write("Decrypting the data again...\n")
987 - embios.aesdecrypt(memaddr, len(datastr), 0)
988 - sys.stdout.write("Reading data back from device...\n")
989 - readdata = embios.read(memaddr, len(datastr))
990 - if readdata == datastr:
991 - sys.stdout.write("Data matches!")
992 - else:
993 - sys.stdout.write("Data does NOT match. Something went wrong")
\ No newline at end of file
 945+ from misc import Logger
 946+ logger = Logger()
 947+ if sys.argv[1] == "test":
 948+ # Some tests
 949+ import sys
 950+ embios = Embios()
 951+ resp = embios.getversioninfo()
 952+ logger.log("Embios device version information: " + libembiosdata.swtypes[resp.swtypeid] + " v" + str(resp.majorv) + "." + str(resp.minorv) +
 953+ "." + str(resp.patchv) + " r" + str(resp.revision) + " running on " + libembiosdata.hwtypes[resp.hwtypeid] + "\n")
 954+ resp = embios.getusermemrange()
 955+ logger.log("Usermemrange: "+hex(resp.lower)+" - "+hex(resp.upper)+"\n")
 956+ memaddr = resp.lower
 957+ maxlen = resp.upper - resp.lower
 958+ f = open("./embios.py", "rb")
 959+ logger.log("Loading test file (embios.py) to send over USB...\n")
 960+ datastr = f.read()[:maxlen]
 961+ logger.log("Sending data...\n")
 962+ embios.write(memaddr, datastr)
 963+ logger.log("Encrypting data with the hardware key...\n")
 964+ embios.aesencrypt(memaddr, len(datastr), 0)
 965+ logger.log("Reading data back and saving it to 'libembios-test-encrypted.bin'...\n")
 966+ f = open("./libembios-test-encrypted.bin", "wb")
 967+ f.write(embios.read(memaddr, len(datastr)))
 968+ logger.log("Decrypting the data again...\n")
 969+ embios.aesdecrypt(memaddr, len(datastr), 0)
 970+ logger.log("Reading data back from device...\n")
 971+ readdata = embios.read(memaddr, len(datastr))
 972+ if readdata == datastr:
 973+ logger.log("Data matches!")
 974+ else:
 975+ logger.log("Data does NOT match. Something went wrong")
 976+
 977+ elif sys.argv[1] == "gendoc":
 978+ # Generates Documentation
 979+ from misc import gendoc
 980+ logger.log("Generating documentation\n")
 981+ cmddict = {}
 982+ for attr, value in Embios.__dict__.iteritems():
 983+ if getattr(value, 'func', False):
 984+ if getattr(value.func, '_command', False):
 985+ cmddict[value.func.__name__] = value
 986+ logger.log(gendoc(cmddict))
\ No newline at end of file