Created
July 24, 2016 08:39
-
-
Save ChunMinChang/c1a5fca53e992fc9acb474736a1f1325 to your computer and use it in GitHub Desktop.
Lesson 1 - Software Debugging
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# -*- coding: utf-8 -*- | |
# Utils | |
# ======================================= | |
# printTable | |
# ---------------- | |
# @table: | |
# [ | |
# (1-1, 1-2, ..., 1-m), | |
# (2-1, 2-2, ..., 2-m), | |
# .. | |
# (n-1, n-2, ..., n-m), | |
# ] | |
def printTable(table, withHeader=False): | |
# zipped = [ | |
# (1-1, 2-1, ..., n-1), | |
# (1-2, 2-2, ..., n-2), | |
# .... | |
# (1-m, 2-m, ..., n-m)] | |
zipped = zip(*table) | |
# colWidth = [maxLen(i-1), maxLen(i-2), ..., maxLen(i-m)], where i = 1 ~ n | |
colWidth = [max(len(ele) for ele in row) for row in zipped] | |
def printRow(row): | |
print '| ' + ' | '.join('{:{}}'.format(val, colWidth[idx]) for idx, val | |
in enumerate(row)) + ' |' | |
for row in table: # row = (i-1, i-2, ..., i-m), where i = 1 ~ n | |
printRow(row) | |
if withHeader and row is table[0]: | |
# Print a horizontal line after the header. | |
line = [ '-' * colWidth[idx] for idx, val in enumerate(row)] | |
printRow(line) | |
# Debugger | |
# ======================================= | |
# traceit | |
# ---------------- | |
# A function that will be used as a debugger to trace all of out code | |
# by calling sys.settrace(traceit). | |
# | |
# Parameters: | |
# @ frame: The current stack frame | |
# @ event: A string value representing the program running status | |
# 'call': A function is called. | |
# 'line': The interpreter is about to execute a new line of code | |
# or re-execute the condition of a loop. | |
# 'return': A function (or other code block) is about to return. | |
# 'exception': An exception has occurred. | |
# 'c_call': A C function is about to be called. | |
# 'c_return': A C function has returned. | |
# 'c_exception': A C function has raised an exception. | |
# @ arg: | |
# 'call': None | |
# 'line': None | |
# 'return': The value that will be returned | |
# 'exception': A tuple (exception, value, traceback) | |
# 'c_call': The C function object | |
# 'c_return': The C function object. | |
# 'c_exception': The C function object. | |
# | |
# See more in https://docs.python.org/3/library/sys.html#sys.settrace | |
# | |
breakpoints = [] | |
watchpoints = {} | |
def traceit(frame, event, arg): | |
if event == "line": | |
# Monitor the watchpoints's status | |
points = set(watchpoints.keys()) & set(frame.f_locals.keys()) | |
if points: | |
for p in points: | |
if frame.f_locals[p] != watchpoints[p]: | |
print p, ':', watchpoints[p], '->', frame.f_locals[p] | |
run(frame, event, arg) | |
# update watchpoints | |
watchpoints[p] = frame.f_locals[p] | |
if frame.f_lineno in breakpoints: | |
run(frame, event, arg) | |
return traceit | |
def run(frame, event, arg): | |
resume = False | |
while not resume: | |
print event, frame.f_lineno, frame.f_code.co_name, frame.f_locals | |
command = getCommand() | |
resume = debug(command, frame.f_locals) | |
def getCommand(): | |
command = raw_input("(my-spyder) ") | |
return command | |
def debug(command, locals): | |
def noCommand(arg): | |
print "No such command", repr(command) | |
return False | |
def quit(arg): | |
sys.exit(0) | |
def addBreakpoint(arg): | |
try: | |
breakpoints.append(int(arg)) | |
except: | |
print "You must supply a line number" | |
return False | |
def addWatchpoint(arg): | |
if arg is None: | |
print "You must supply a variable name" | |
else: | |
watchpoints[arg] = locals[arg] if arg in locals else None | |
print watchpoints | |
return False | |
def printVariable(arg): | |
if arg is None: | |
print locals | |
elif arg in locals: | |
print arg, '=', repr(locals[arg]) | |
else: | |
print 'No such variable:', arg | |
return False | |
# def step(arg): | |
# return False | |
def goon(arg): # continue | |
return True | |
if not command: | |
return noCommand() | |
# Capture the argument from command | |
arg = None | |
if command.find(' ') > 0: | |
arg = command.split(' ')[1] | |
commandOperator = { | |
'b': addBreakpoint, | |
'c': goon, | |
'p': printVariable, | |
'q': quit, | |
# 's': step, | |
'w': addWatchpoint, | |
} | |
operation = commandOperator.get(command[0], noCommand) | |
return operation(arg) | |
# Helpers | |
# ======================================= | |
# runTest | |
# ---------------- | |
# A tester to run all the tests and print the results | |
# compared to the answers. | |
# | |
# Parameters | |
# @ tests: A list containing inputs with expected results. | |
# [ | |
# (input data 1, expectation 1), | |
# (input data 2, expectation 2), | |
# ... | |
# ... | |
# (input data n, expectation n) | |
# ] | |
# | |
# @ challenger: A function to execute the test. | |
def runTest(tests, challenger): | |
table = [('Input', 'expectation', 'result', 'pass')] | |
for t in tests: | |
result = challenger(t[0]) | |
table.append((t[0], t[1], result, ('Y' if t[1] == result else 'N'))) | |
printTable(table, True); | |
# Main program | |
# ======================================= | |
# Finite State Machine for stripping html markup. | |
# | |
# out = out + char | |
# ┌──┐ ┌──┐ ┌──┐ | |
# ¬ < | ↓ ¬ ( > | ") | ↓ ¬ " | ↓ | |
# ┌──┴─────┐ < ┌──┴─────┐ " ┌──┴────┐ | |
# | ├───────────────>| ├─────>| | | |
# init ──>| Text | | Tag | | Quote | | |
# | |<───────────────┤ |<─────┤ | | |
# └────────┘ > └────────┘ " └───────┘ | |
# | |
# ¬ Tag & ¬ Quote Tag & ¬ Quote Tag & Quote | |
# | |
# Note that there is no state for (¬ Tag & Quote), | |
# so our state must be ¬ (¬ Tag & Quote) = Tag or ¬ Quote. | |
# | |
# Parameters | |
# @ string: A string that needs to remove html markup | |
# | |
# Return | |
# A string without html markup | |
def removeHtmlMarkup(string): | |
Tag = False | |
Quote = False | |
out = "" | |
for char in string: | |
assert Tag or not Quote | |
if not Quote and (char == '<' or char == '>'): | |
Tag = not Tag | |
elif Tag and (char == '"' or char == "'"): | |
Quote = not Quote | |
elif not Tag and not Quote: | |
out += char | |
return out | |
def main(): | |
# Put tests in format (input, expectation) into a list. | |
sheet = [('<b>foo</b>', 'foo'), # <b>foo</b> -> foo | |
('<b>"foo"</b>', '"foo"'), # <b>"foo"</b> -> "foo" | |
('"<b>foo</b>"', '"foo"'), # "<b>foo</b>" -> "foo" | |
('<"b">foo</"b">', 'foo'), # <"b">foo</"b"> -> foo | |
('<a href=">">foo</a>', 'foo')] # <a href=">">foo</a> -> foo | |
# Start testing | |
runTest(sheet, removeHtmlMarkup); | |
# Set the system’s trace function. | |
import sys | |
sys.settrace(traceit) | |
# Set a dummy breakpoint at runTest | |
breakpoints.append(227) | |
# Run the program. | |
main() | |
# Clear the system’s trace function. | |
sys.settrace(None) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment