TEORIE E TECNICHE DEL RICONOSCIMENTO Python Debugging Debugging • Uno dei piu’ importanti requisiti per un buon programmatore e’ la capacita’ di identificare gli errori (BUGS) in un programma: DEBUGGING • Il debugging puo’ essere considerato una forma di – Lavoro investigativo – Esperimento scientifico (si formula un’ipotesi sulla fonte dell’errore, la si scopre) IDEE BASE Two key ideas 1. The scientific method 2. Divide and conquer If you master those, you will find debugging easy, and possibly enjoyable The scientific method 1. Create a hypothesis 2. Design an experiment to test that hypothesis – Ensure that it yields insight 3. Understand the result of your experiment – If you don’t understand, then possibly suspend your main line of work to understand that Tips: • Be systematic – Never do anything if you don't have a reason – Don’t just flail • Random guessing is likely to dig you into a deeper hole • Don’t make assumptions (verify them) Example experiments 1. An alternate implementation of a function – Run all your test cases afterward 2. A new, simpler test case – Examples: smaller input, or test a function in isolation – Can help you understand the reason for a failure Your scientific notebook Record everything you do • Specific inputs and outputs (both expected and actual) • Specific versions of the program – If you get stuck, you can return to something that works – You can write multiple implementations of a function • What you have already tried • What you are in the middle of doing now – This may look like a stack! • What you are sure of, and why Your notebook also helps if you need to get help or reproduce your results Divide and conquer • Where is the defect (or “bug”)? • Your goal is to find the one place that it is • Finding a defect is often harder than fixing it • Initially, the defect might be anywhere in your program – It is impractical to find it if you have to look everywhere • Idea: bit by bit reduce the scope of your search • Eventually, the defect is localized to a few lines or one line – Then you can understand and fix it • 4 ways to divide and conquer: – – – – In the program code In test cases During the program execution During the development history Divide and conquer in the program code • Localize the defect to part of the program – e.g., one function, or one part of a function • Code that isn’t executed cannot contain the defect 3 approaches: • Test one function at a time • Add assertions or print statements – The defect is executed before the failing assertion (and maybe after a succeeding assertion) • Split complex expressions into simpler ones Example: Failure in result = set({graph.neighbors(user)}) Change it to nbors = graph.neighbors(user) nbors_set = {nbors} result = set(nbors_set) The error occurs on the “nbors_set = {nbors}" line Divide and conquer in test cases • Your program fails when run on some large input – It’s hard to comprehend the error message – The log of print statement output is overwhelming • Try a smaller input – Choose an input with some but not all characteristics of the large input – Example: Unicode characters, duplicates, zeroes in data, … Divide and conquer in execution time via print (or “logging”) statements • A sequence of print statements is a record of the execution of your program • The print statements let you see and search multiple moments in time • Print statements are a useful technique, in moderation • Be disciplined – Too much output is overwhelming rather than informative – Remember the scientific method: have a reason (a hypothesis to be tested) for each print statement – Don’t only use print statements Divide and conquer in development history • The code used to work (for some test case) • The code now fails • The defect is related to some line you changed • This is useful only if you kept a version of the code that worked (use good names!) • This is most useful if you have made few changes • Moral: test often! – Fewer lines to compare – You remember what you were thinking/doing recently Simple Debugging Tools print – shows what’s happening whether there’s a problem or not – does not stop execution assert – – – – Raises an exception if some condition is not met Does nothing if everything works Example: assert len(rj.edges()) == 16 Use this liberally! Not just for debugging! raw_input – Stops execution – (Designed to accept user input, but I rarely use it for this.) TIPI DI ERRORI Tipi di errori • Sintattici >>> def faa () SyntaxError: invalid syntax • Run time – exceptions • Semantic – Il programma gira ma non fa quel che deve fare ERRORI SINTATTICI • Prodotti da Python quando cerca di tradurre il source code in codice macchina • Facili da rimediare se si sa dove sono • A volte pero’ gli errori sono del tipo SyntaxError: invalid syntax SyntaxError: invalid token COME RIMEDIARE AGLI ERRORI SINTATTICI (i) (ii) (iii) (iv) (v) (vi) (vii) Make sure you are not using a Python keyword for a variable name. Check that you have a colon at the end of the header of every compound statement, including for, while, if, and def statements. Make sure that any strings in the code have matching quotation marks. If you have multiline strings with triple quotes (single or double), make sure you have terminated the string properly. An unterminated string may cause an invalid tokenerrorattheendofyourprogram,oritmaytreatthefollowing part of the program as a string until it comes to the next string. In the second case, it might not produce an error message at all! An unclosed opening operator – (, {, or [ – makes Python continue with the next line as part of the current statement. Generally, an error occurs almost immediately in the next line. Check for the classic = instead of == inside a conditional. Check the indentation to make sure it lines up the way it is supposed to.Python can handle space and tabs, but if you mix them it can cause problems. The best way to avoid this problem is to use a text editor that knows about Python and generates consistent indentation. ‘CONTINUO A MODIFICARE IL PROGRAMMA MA NON SUCCEDE NIENTE…’ • A volte il motivo per cui Python vi dice che c’e’ un errore ma voi non lo vedete e’ che Python sta cercando di eseguire una versione del documento diversa da quella che state modificando : – Avete modificato il file ma non lo avete salvato – Avete cambiato il nome del file ma state ancora caricando il vecchio file – Avete dato al vostro programma il nome di uno dei moduli di Python – Etc, etc ERRORI RUNTIME • Prodotti dall’interprete quando qualcosa va male durante l’esecuzione ESEMPI DI ERRORI RUNTIME • Il mio programma non fa nulla – Succede per esempio quando definite classi e funzioni ma poi non le chiamate • Il mio programma parte ma poi rimane sospeso (‘hanging’) – Loop infinito – Ricorsione infinita LOOP INFINITO x=1 y = -1 while x > 0 and y < 0 : x += 1 y -= 1 LOOP INFINITO Una soluzione: usare print x=1 y = -1 while x > 0 and y < 0 : x += 1 y -= 1 print "x: ", x print "y: ", y print "condition: ", (x > 0 and y < 0) RICORSIONE INFINITA Maximum recursion depth exceeded error Soluzioni: - Controllare che ci sia un caso base - Usare print EXCEPTIONS First function that was called (<module> means the interpreter) Traceback (most recent call last): File "nx_error.py", line 41, in <module> Second function print friends_of_friends(rj, myval) File "nx_error.py", line 30, in friends_of_friends that was called f = friends(graph, user) Call stack or traceback File "nx_error.py", line 25, in friends return set(graph.neighbors(user))# File "/Library/Frameworks/…/graph.py", line 978, in neighbors return list(self.adj[n]) Last function that TypeError: unhashable type: 'list' was called (this one suffered an error) List of all exceptions (errors): http://docs.python.org/2/library/exceptions.html#bltin-exceptions Two other resources, with more details about a few of the errors: http://inventwithpython.com/appendixd.html http://www.cs.arizona.edu/people/mccann/errors-python The error message: daunting but useful. You need to understand: • the literal meaning of the error • the underlying problems certain errors tend to suggest ERRORI RUNTIME PIU’ COMUNI • NameError: State usando una variabile non definita nell’ambiente corrente (per esempio una variabile locale ad un’altra funzione) • TypeError: Ci sono diverse possibilita’ – You are trying to use a value improperly. Example: indexing a string, list, or tuple with something other than an integer. – There is a mismatch between the items in a format string and the items passed for conversion. This can happen if either the number of items does not match or an invalid conversion is called for. – You are passing the wrong number of arguments to a function or method. For methods, look at the method definition and check that the first param- eter is self. Then look at the method invocation; make sure you are invoking the method on an object with the right type and providing the other arguments correctly. • KeyError: You are trying to access an element of a dictionary using a key that the dictionary does not contain. ERRORI RUNTIME PIU’ COMUNI • IndexError: The index you are using to access a list, string, or tuple is greater than its length minus one. Immediately before the site of the error, add a print statement to display the value of the index and the length of the array. Is the array the right size? Is the index the right value? • AttributeError: You are trying to access an attribute or method that does not exist. Check the spelling! You can use dir to list the attributes that do exist. If an AttributeError indicates that an object has NoneType, that means that it is None. One common cause is forgetting to return a value from a function; if you get to the end of a function without hitting a return statement, it returns None. Another common cause is using the result from a list method, like sort, that returns None. Soluzione • Usare pdb (vedi sotto) Ho inserito cosi’ tanti print … • … che non capisco piu’ cosa succede – Usate solo un numero minimo di print statements, quelli necessari per risolvere UN problema! SEMANTIC ERROR • Il programma gira ma non fa la cosa giusta – per esempio non aggiunge dati ad un dizionario • Il tipo di errore piu’ difficile da correggere! COSA FARE • Is there something the program was supposed to do but which doesn’t seem to be happening? Find the section of the code that performs that function and make sure it is executing when you think it should. • Is something happening that shouldn’t? Find code in your program that performs that function and see if it is executing when it shouldn’t. • Is a section of code producing an effect that is not what you expected? Make sure that you understand the code in question, especially if it involves invocations to functions or methods in other Python modules. Read the documentation for the functions you invoke. Try them out by writing simple test cases and checking the results. ESPRESSIONI COMPLESSE self.hands[i].addCard(self.hands[self.findNeighbor(i)].popCard()) Meglio: neighbor = self.findNeighbor(i) pickedCard = self.hands[neighbor].popCard() self.hands[i].addCard(pickedCard) CONTROLLARE IL VALORE DI UNA FUNZIONE return self.hands[i].removeMatches() Meglio: count = self.hands[i].removeMatches() return count PDB Pdb: Python debugger • Un debugger e’ un programma che permette di studiare un problem a run time per esempio – Visualizzando la stack di esecuzione – Facendo stepping del programma pdb.pm() >>> import pdb >>> def foo (): return x + 1 >>> def faa (): return foo() >>> faa() Traceback (most recent call last): File "<pyshell#12>", line 1, in <module> faa() File "<pyshell#11>", line 2, in faa return foo() File "<pyshell#4>", line 2, in foo return x + 1 NameError: global name 'x' is not defined >>>>>> pdb.pm() <pyshell#4>(2)foo() pdb.pm() (Pdb) h Documented commands (type help <topic>): ======================================== EOF bt cont enable jump pp run unt a c continue exit l q s until alias cl d h list quit step up args clear debug help n r tbreak w b commands disable ignore next restart u whatis break condition down j p return unalias where Miscellaneous help topics: ========================== exec pdb Undocumented commands: ====================== retval rv pdb.pm() (Pdb) w /Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/idlelib/run.py(298)runcod e() -> exec code in self.locals <pyshell#12>(1)<module>() <pyshell#11>(2)faa() <pyshell#4>(2)foo() pdb: tracing and stepping Example program: # epdb1.py -- experiment with the Python debugger, pdb a = "aaa" b = "bbb" c = "ccc" final = a + b + c print final Now add: pdb.set_trace() To get: # epdb1.py -- experiment with the Python debugger, pdb a = "aaa” pdb.set_trace() b = "bbb" c = "ccc" final = a + b + c print final pdb: stepping Now as you execute the program, Python enters the debugger after the first instruction: (Pdb) At this point you can execute one instruction at a time using ‘n’ ESEMPIO FIND_WORDS >>> def find_words(text, wordlength, result=[]): ... for word in text: ... if len(word) == wordlength: ... result.append(word) ... return result >>> find_words(['omg', 'teh', 'lolcat', 'sitted', 'on', 'teh', 'mat'], 3) [1] ['omg', 'teh', 'teh', 'mat'] >>> find_words(['omg', 'teh', 'lolcat', 'sitted', 'on', 'teh', 'mat'], 2, ['ur']) [2] ['ur', 'on'] >>> find_words(['omg', 'teh', 'lolcat', 'sitted', 'on', 'teh', 'mat'], 3) [3] ['omg', 'teh', 'teh', 'mat', 'omg', 'teh', 'teh', 'mat'] DEBUGGING FIND_WORDS CON PDB >>> import pdb >>> find_words(['cat'], 3) ['cat'] >>> pdb.run("find_words(['dog'], 3)") > <string>(1)<module>() (Pdb) step --Call-> <stdin>(1)find_words() (Pdb) args text = ['dog'] wordlength = 3 result = ['cat'] # [_first-run] # [_second-run]