-
Notifications
You must be signed in to change notification settings - Fork 12
/
debugging.html
433 lines (345 loc) · 15.3 KB
/
debugging.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
<!DOCTYPE html>
<html>
<head>
<title>Debugging</title>
<link href="http://www-inst.eecs.berkeley.edu/~cs61a/su13/projects/ants/css/assignments.css" rel="stylesheet" type="text/css">
</head>
<body>
<div id='header'>
<div id ='logo'>
<h1>Debugging</h1>
</div>
</div>
<ul><li><a href="#introduction">Introduction</a></li><ul><li><a href="#traceback-messages">Traceback Messages</a></li><li><a href="#error-messages">Error Messages</a></li></ul><li><a href="#debugging-process">Debugging Process</a></li><ul><li><a href="#running-doctests">Running doctests</a></li><li><a href="#writing-your-own-tests">Writing your own tests</a></li><li><a href="#using-print-statements">Using <code>print</code> statements</a></li><li><a href="#long-term-debugging">Long-term debugging</a></li></ul><li><a href="#error-types">Error Types</a></li><li><a href="#common-bugs">Common Bugs</a></li></ul>
<h2 id="introduction">Introduction</h2>
<p>By now, you will have encountered various bugs when programming for
this class. Most often, you will try to run your code and see
something like this:</p>
<pre class="prettyprint">Traceback (most recent call last):
File "<pyshell#29>", line 3 in <module>
result = buggy(5)
File <pyshell#29>", line 5 in buggy
return f + x
TypeError: unsupported operand type(s) for +: 'function' and 'int'
</pre>
<p>This is called a <em>traceback</em> message. It prints out the chain of
function calls that led up to the error, with the most recent function
call at the bottom. You can follow this chain to figure out which
function(s) caused the problem.</p>
<h3 id="traceback-messages">Traceback Messages</h3>
<p>Notice that the lines in the traceback appear to be paired together.
The <strong>first</strong> line in such a pair has the following format:</p>
<pre class="prettyprint">File "<file name>", line <number>, in <function>
</pre>
<p>That line provides you with the following information:</p>
<ul>
<li><strong>File name</strong>: the name of the file that contains the problem.</li>
<li><strong>Number</strong>: the line number in the file that cuased the problem, or
the line number that contains the next function call</li>
<li><strong>Function</strong>: the name of the function in which the line can be
found.</li>
</ul>
<p>The <strong>second</strong> line in the pair (it's indented farther in than the
first) displays the actual line of code that makes the <em>next</em> function
call. This gives you a quick look at what arguments were passed into
the function, in what context the function was being used, etc.</p>
<p>Finally, remember that the traceback is organized with the "most
recent call last."</p>
<h3 id="error-messages">Error Messages</h3>
<p>The very last line in the traceback message is the error statement. An
<em>error statement</em> has the following format:</p>
<pre class="prettyprint"><error type>: <error message>
</pre>
<p>This line provides you with two pieces of information:</p>
<ul>
<li><strong>Error type</strong>: the type of error that was caused (e.g.
<code>SyntaxError</code>, <code>TypeError</code>). These are usually descriptive enough to
help you narrow down your search for the cause of error.</li>
<li><strong>Error message</strong>: a more detailed description of exactly what
caused the error. Different error types produce different error
messages.</li>
</ul>
<h2 id="debugging-process">Debugging Process</h2>
<h3 id="running-doctests">Running doctests</h3>
<p>Python has a great way to quickly write tests for your code. These are
called doctests, and look like this:</p>
<pre class="prettyprint">def foo(x):
"""A random function.
>>> foo(4)
4
>>> foo(5)
5
"""
</pre>
<p>The lines in the docstring that look like interpreter outputs are the
<strong>doctests</strong>. To run them, go to your terminal and
type:</p>
<pre class="prettyprint">python3 -m doctest file.py
</pre>
<p>This effectively loads your file into the Python interpreter, and
checks to see if each doctest input (e.g. <code>foo(4)</code>) is the same as the
specified output (e.g. <code>4</code>). If it isn't, a message will tell you
which doctests you failed.</p>
<p>The command line tool has a <code>-v</code> option that stands for "verbose."</p>
<pre class="prettyprint">python3 -m doctest file.py -v
</pre>
<p>In addition to telling you which doctests you failed, it will also
tell you which doctests passed. I personally find that information
unnecessary, so I usually leave <code>-v</code> out.</p>
<p>Usually, we will provide doctests for you in the starter files.
<strong>Always run these doctests</strong>. Submitting an assignment without
running doctests even once is practically throwing points away.</p>
<p>Also, <strong>do not manually type in doctests into the interpreter</strong>. The
whole point of writing doctests is so you don't have to do that.
Manually typing in doctests requires you to (1) open up a Python
shell, (2) type in every doctest, (3) manually check if the output
matches the doctest, (4) repeat. Running doctests from the command
line requires you to (1) type in a single line, and (2) that's it!</p>
<h3 id="writing-your-own-tests">Writing your own tests</h3>
<p>In addition to doctests, you can write your own tests. There are two
ways to do this: (1) write additional doctests, or (2) write testing
functions.</p>
<p>Writing your own tests is good practice for the future. Remember,
before the project deadlines, our autograder only runs sanity tests --
a subset of all the tests we will eventually run. In other words,
<strong>passing the autograder does not mean you get full credit</strong>. As such,
it is a very good idea to write your own test cases.</p>
<p>To write more doctests, simply follow the style of existing doctests.
You can also write your own functions (much like the <code>take_turn_test</code>
function from Project 1).</p>
<p>Some advice in writing tests:</p>
<ul>
<li><strong>Write some tests before you write code</strong>: this is called
test-driven development. Writing down how you expect the function to
behave first -- this can guide you when writing the actual code.</li>
<li><strong>Write more tests after you write code</strong>: once you are sure your
code passes the initial doctests, write some more tests to take care
of edge cases.</li>
<li><strong>Test edge cases</strong>: make sure your code works for all special
cases.</li>
</ul>
<h3 id="using-print-statements">Using <code>print</code> statements</h3>
<p>Once the doctests tell you where the error is, you have to figure what
went wrong. If the doctest gave you a traceback message, look at what
<a href="#error-types">type of error</a> it is to help narrow your search. Also
check that you aren't making any <a href="#common-bugs">common mistakes</a>.</p>
<p>When you first learn how to program, it can be hard to spot bugs in
your code. One common practice is to add <code>print</code> statements. For
example, let's say the following function <code>foo</code> keeps returning the
wrong thing:</p>
<pre class="prettyprint">def foo(x):
result = some_function(x)
return result // 5
</pre>
<p>We can add a print statement before the return to check what
<code>some_function</code> is returning:</p>
<pre class="prettyprint">def foo(x):
result = some_function(x)
print('result is', result)
return other_function(result)
</pre>
<p>If it turns out <code>result</code> is not what we expect it to be, we would go
look in <code>some_function</code> to see if it works properly. Otherwise, we
might have to add a print statement before the return to check
<code>other_function</code>:</p>
<pre class="prettyprint">def foo(x):
result = some_function(x)
print('result is', result)
tmp = other_function(result)
print('other_function returns', tmp)
return tmp
</pre>
<p>Some advice:</p>
<ul>
<li>Don't just print out a variable -- add some sort of message to make
it easier for you to read:</p>
<pre class="prettyprint">print(tmp) # harder to keep track
print('tmp was this:', tmp) # easier
</pre></li>
<li>Use <code>print</code> statements to view the results of function calls (i.e.
after function calls).</li>
<li>Use <code>print</code> statements at the end of a <code>while</code> loop to view the
state of the counter variables after each iteration:</p>
<pre class="prettyprint">i = 0
while i < n:
i += func(i)
print('i is', i)
</pre></li>
<li>Don't just put random <code>print</code> statements after lines that are
obviously correct.</li>
</ul>
<h3 id="long-term-debugging">Long-term debugging</h3>
<p>The <code>print</code> statements described above are meant for quick debugging
of one-time errors -- after figuring out the error, you would remove
all the <code>print</code> statements.</p>
<p>However, sometimes we would like to leave the debugging code if we
need to periodically test our file. It can get kind of annoying if
every time we run our file, debugging messages pop up. One way to
avoid this is to use a global <code>debug</code> variable:</p>
<pre class="prettyprint">debug = True
def foo(n):
i = 0
while i < n:
i += func(i)
if debug:
print('i is', i)
</pre>
<p>Now, whenever we want to do some debugging, we can set the global
<code>debug</code> variable to <code>True</code>, and when we don't want to see any
debugging input, we can turn it to <code>False</code> (such a variable is called
a "flag").</p>
<h2 id="error-types">Error Types</h2>
<p>The following are common error types that Python programmers run into.</p>
<ol>
<li><code>SyntaxError</code></p>
<ul>
<li><strong>Cause</strong>: code syntax mistake</li>
<li><strong>Example</strong>:</p>
<pre class="prettyprint"> File "file name", line number
def incorrect(f)
^
SyntaxError: invalid syntax
</pre></li>
<li><strong>Solution</strong>: the <code>^</code> symbol points to the code that contains
invalid syntax. The error message doesn't tell you <em>what</em> is
wrong, but it does tell you <em>where</em>.</li>
<li><strong>Notes</strong>: Python will check for <code>SyntaxErrors</code> before executing
any code. This is different from other errors, which are only
raiased during runtime.</li>
</ul></li>
<li><code>IndentationError</code></p>
<ul>
<li><strong>Cause</strong>: improper indentation</li>
<li><strong>Example</strong>:</p>
<pre class="prettyprint"> File "file name", line number
print('improper indentation')
IndentationError: unindent does not match any outer indentation level
</pre></li>
<li><strong>Solution</strong>: The line that is improperly indented is displayed.
Simply re-indent it.</li>
<li><strong>Notes</strong>: If you are inconsistent with tabs and spaces, Python
will raise one of these. Make sure you use either spaces or
tabs, not both!</li>
</ul></li>
<li><code>TypeError</code></p>
<ul>
<li><strong>Cause 1</strong>:</p>
<ul>
<li>Invalid operand types for primitive operators. You are
probably trying to add/subract/multiply/divide incompatible
types.</li>
<li><strong>Example</strong>:</p>
<pre class="prettyprint">TypeError: unsupported operand type(s) for +: 'function' and 'int'
</pre></li>
</ul></li>
<li><strong>Cause 2</strong>:</p>
<ul>
<li>Using non-function objects in function calls.</li>
<li><strong>Example</strong>:</p>
<pre class="prettyprint">>>> square = 3
>>> square(3)
Traceback (most recent call last):
...
TypeError: 'int' object is not callable
</pre></li>
</ul></li>
<li><strong>Cause 3</strong>:</p>
<ul>
<li>Passing an incorrect number of arguments to a function.</li>
<li><strong>Example</strong>:</p>
<pre class="prettyprint">>>> add(3)
Traceback (most recent call last):
...
TypeError: add expected 2 arguments, got 1
</pre></li>
</ul></li>
</ul></li>
<li><code>NameError</code></p>
<ul>
<li><strong>Cause</strong>: variable not assigned to anything OR it doesn't
exist. This includes function names.</li>
<li><strong>Example</strong>:</p>
<pre class="prettyprint">File "file name", line number
y = x + 3
NameError: global name 'x' is not defined
</pre></li>
<li><strong>Solution</strong>: Make sure you are initializing the variable (i.e.
assigning the variable to a value) before you use it.</li>
<li><strong>Notes</strong>: The reason the error message says "global name" is
because Python will start searching for the variable from a
function's local frame. If the variable is not found there,
Python will keep searching the parent frames until it reaches
the global frame. If it still can't find the variable, Python
raises the error.</li>
</ul></li>
<li><code>IndexError</code></p>
<ul>
<li><strong>Cause</strong>: trying to index a sequence (e.g. a tuple, list,
string) with a number that exceeds the size of the sequence.</li>
<li><strong>Example</strong>:</p>
<pre class="prettyprint">File "file name", line number
x[100]
IndexError: tuple index out of range
</pre></li>
<li><strong>Solution</strong>: Make sure the index is within the bounds of the
sequence. If you're using a variable as an index (e.g. <code>seq[x]</code>,
make sure the variable is assigned to a proper index.</li>
</ul></li>
</ol>
<h2 id="common-bugs">Common Bugs</h2>
<ul>
<li><strong>Spelling and Capitalization</strong>: Python is <em>case sensitive</em>. The
variable <code>hello</code> is not the same as <code>Hello</code> or <code>hello</code> or <code>helo</code>.
This will usually show up as a <code>NameError</code>, but sometimes misspelled
variables will actually have been defined. In that case, it can be
difficult to find errors, and it is never gratifying to discover
it's just a spelling mistake.</li>
<li><strong>Missing Parentheses</strong>: A common bug is to leave off the closing
parenthesis. This will show up as a <code>SyntaxError</code>. Consider the
following code:</p>
<pre class="prettyprint">def fun():
return foo(bar() # missing a parenthesis here
fun()
</pre>
<p>Python will raise a <code>SyntaxError</code>, but will point to the line
<em>after</em> the missing parenthesis:</p>
<pre class="prettyprint">File "file name", line "number"
fun()
^
SyntaxError: invalid syntax
</pre>
<p>In general, if Python points a <code>SyntaxError</code> to a seemingly correct
line, you are probably forgetting a parenthesis somewhere.</li>
<li><strong>Missing close quotes</strong>: this is similar to the previous bug, but
much easier to catch. Python will actually tell you the line that is
missing the quote:</p>
<pre class="prettyprint">File "file name", line "number"
return 'hi
^
SyntaxError: EOL while scanning string literal
</pre>
<p><code>EOL</code> stands for "End of Line."</li>
<li><strong><code>=</code> vs. <code>==</code></strong>: the single equal sign <code>=</code> is used for
<em>assignment</em>; the double equal sign <code>==</code> is used for testing
equivalence. The most common error of this form is something like:</p>
<pre class="prettyprint">if x = 3:
</pre></li>
<li><strong>Infinite Loops</strong>: Infinite loops are often caused by <code>while</code> loops
whose conditions never change. For example:</p>
<pre class="prettyprint">i = 0
while i < 10:
print(i)
</pre>
<p>Sometimes you might have incremented the wrong counter:</p>
<pre class="prettyprint">i, n = 0, 0
while i < 10:
print(i)
n += 1
</pre></li>
<li><strong>Off-by-one errors</strong>: sometimes a <code>while</code> loop or recursive
function might stop one iteration too short. Here, it's best to walk
through the iteration/recursion to see what number the loop stops
at.</li>
</ul>
</body>
</html>