This article aims to give developers ideas about how to better find the sources of errors in their PyS60 applications, as default error reports can be hard to understand and standalone applications don't even have error reports.
Contents |
A simple way of finding out what is wrong in a sequence of code is to show a confirmation message after an operation is successfully performed. The message can either be printed on the screen or spoken by the phone.
For example, let's say we want to alphabetize a list and append an element to it:
import appuifw, e32, audio
l=['alpha', 'beta', 'lambda', 'gamma']
n=len(l)
i=0
while(i<n-1):
j=i+1
while(j<n):
if(l[i]>l[j]):
a=l[i]
l[i]=l[j]
l[j]=a
j+=1
i+=1
#If all goes well to this point, we can print a message...
print "The list has been alphabetized"
#... or have the phone tell us
audio.say("The list has been alphabetized")
l.append('omega')
print "The element 'omega' has been added to the list"
applock=e32.Ao_lock()
applock.wait() #Tell the application not to terminate immediately
Known as the "exception harness", this method is more useful for standalone applications. It mimics a stack trace of the Python Script Shell.
try:
# Actual program is here.
1 / 0
except:
import sys
import traceback
import e32
import appuifw
appuifw.app.screen="normal" # Restore screen to normal size.
appuifw.app.focus=None # Disable focus callback.
body=appuifw.Text()
appuifw.app.body=body # Create and use a text control.
applock=e32.Ao_lock()
def quit():applock.signal()
appuifw.app.exit_key_handler=quit # Override softkey handler.
appuifw.app.menu=[(u"Exit", quit)] # Override application menu.
body.set(unicode("\n".join(traceback.format_exception(*sys.exc_info()))))
applock.wait() # Wait for exit key to be pressed.
appuifw.app.set_exit()
Here we record the error to a text log file.
def main():
try:
Your main code goes here.
except:
import sys
import traceback
import appuifw
cla, exc, trbk = sys.exc_info()
excName = cla.__name__
try:
excArgs = exc.__dict__["args"]
except KeyError:
excArgs = "<no args>"
excTb = traceback.format_tb(trbk, 5)
errorString = repr(excName) + '-' + repr(excArgs) + '-' + repr(excTb) + '\n'
print errorString
appuifw.note(u'Application errors, see log file for more information', "error")
file = open(u'C:\\Log.txt','a')
file.write(errorString)
file.close()
raise
if __name__ == "__main__":
main()
Here the errors will be recorded in the Log file at C:\\Log.text
Exceptions from callbacks are not propagated to the "main thread" (in quotes because no real threading is going on in the background, only Symbian active objects).
You can wrap each callback in a function that collects the exception in a global list and signals the lock you're waiting. You can then handle exceptions collected in the list sequentially when the lock is signalled.
#
# developed by jethro.fn.
#
import traceback
# Exceptions from callbacks are collected here.
exceptions = []
# Decorator for callback functions
def collect_exception(func):
# Using Python (>v2.2) nested scopes.
def callit(*args, **kwds):
global script_lock
try:
return func(*args, **kwds)
except:
excstr = "\n".join(traceback.format_exception(*sys.exc_info()))
exceptions.append(excstr) # Store exception string in a list.
script_lock.signal() # Notify main function about exception.
raise # Re-raise exception, just in case.
return callit
...
def main():
global exceptions, script_lock
script_lock = e32.Ao_lock()
...
appuifw.app.menu = [(u'Raise NameError',
collect_exception(raise_nameerror)),
(u'Exit',
script_lock.signal)]
...
script_lock.wait()
# Report exceptions. Could be more than one, in theory.
for excstr in exceptions:
appuifw.note(unicode(excstr), 'error')
exceptions = [] # All exceptions accounted for, clear list.
#
# GPL license : traceS60.py by cyke64
#
import sys
import linecache
from e32 import ao_sleep
refresh=lambda:ao_sleep(0)
class trace:
def __init__(self,runfile,f_all=u'e:\\traceit.txt',f_main=u'e:\\traceitmain.txt'):
self.out_all=open(f_all,'w')
self.out_main=open(f_main,'w')
self.runfile=runfile
def go(self):
sys.settrace(self.traceit)
def stop(self):
sys.settrace(None)
self.out_all.close()
self.out_main.close()
def traceit(self,frame, event, arg):
lineno = frame.f_lineno
name = frame.f_globals["__name__"]
if "__file__" in frame.f_globals:
file_trace=frame.f_globals["__file__"]
line=linecache.getline(file_trace,lineno)
else:
file_trace=self.runfile
line=linecache.getline(file_trace,lineno)
self.out_main.write("%s*%s*\n*%s*\n" %(event,lineno,line.rstrip()))
self.out_all.write("%s*%s*of %s(%s)\n*%s*\n" %(event,lineno,name,file_trace,line.rstrip()))
refresh()
return self.traceit
method use example :
import random
import traceS60
def main():
print "In main"
for i in range(5):
print i, random.randrange(0, 10)
print "Done."
# very important give in trace object the REAL path of your script !
tr=traceS60.trace('e:\\system\\apps\\python\\my\\silly_trace.py')
tr.go()
main()
tr.stop()
Mixing method 5 and 6 for a complete PyS60 debugging tool !
#
# Code mixed by Hiisi
#
import sys
import e32
import appuifw
import linecache
# Debugging Level (0: Release, 1: Show Traceback, 2: Trace All)
debuglevel = 1
# Exceptions from callbacks are stored here.
exceptions = []
class Trace:
"""Class for tracing script
This class is developed by cyke64.
Lisenced under GNU GPL.
"""
def __init__(self, runfile, f_all=u'E:\\Python\\traceit.txt',
f_main=u'E:\\Python\\traceitmain.txt'):
self.out_all=open(f_all, 'w')
self.out_main=open(f_main, 'w')
self.runfile = runfile
def go(self):
sys.settrace(self.traceit)
def stop(self):
sys.settrace(None)
self.out_all.close()
self.out_main.close()
def traceit(self, frame, event, arg):
lineno = frame.f_lineno
name = frame.f_globals['__name__']
if '__file__' in frame.f_globals:
file_trace=frame.f_globals['__file__']
line = linecache.getline(file_trace, lineno)
else:
file_trace = self.runfile
line = linecache.getline(file_trace, lineno)
self.out_main.write('%s*%s*\n*%s*\n' \
% (event, lineno, line.rstrip()))
self.out_all.write('%s*%s*of %s(%s)\n*%s*\n' \
%(event, lineno, name, file_trace, line.rstrip()))
e32.ao_sleep(0)
return self.traceit
def call_callback(func):
"""Catch exception
This function is developed by jethro.fn.
"""
def call_func(*args, **kwds):
import traceback
global exceptions
global script_lock
try:
return func(*args, **kwds)
except:
# Collect Exceptions
exception = ''.join(traceback.format_exception(*sys.exc_info()))
exceptions.append(exception)
# Signal lock in main thread (immediate termination)
if debuglevel == 2: script_lock.signal()
return call_func
def raise_nameerror():
"""Raise NameError"""
# foo = u'bar'
foo
def main():
"""Main thread"""
global script_lock
global exceptions
# main UI
script_lock = e32.Ao_lock()
appuifw.app.title = u'RaiseErrorTest'
appuifw.app.body = appuifw.Text()
appuifw.app.menu = [(u'Raise NameError',
call_callback(raise_nameerror)),
(u'Exit', script_lock.signal)]
appuifw.app.exit_key_handler = script_lock.signal
script_lock.wait()
# Handling exception
if debuglevel and exceptions:
show_traceback(exceptions)
exceptions = []
def show_traceback(exceptions):
"""Show traceback"""
appuifw.app.title = u'Traceback'
traceback = '%d error(s) occurred.\n\n' % len(exceptions)
for exception in exceptions:
traceback += '%s\n' % exception
body = appuifw.Text(unicode(traceback))
body.set_pos(0)
appuifw.app.body = body
appuifw.app.menu = [(u'Exit', script_lock.signal)]
script_lock.wait()
if __name__ == '__main__':
try:
if debuglevel == 2:
trace = Trace('E:\\Python\\RaiseErrorTest.py')
trace.go()
main()
trace.stop()
else:
main()
except:
raise
External links :
No related wiki articles found