Overview
Teaching: 5 min Exercises: 10 minQuestions
What sorts of things frequently go wrong in programs?
How can I make my programs more robust?
Objectives
Diagnose corner cases in simple programs.
Write assertions with informative messages that test for corner cases in simple programs.
if, print, and sys.exit to detect and report errors.import sys
def average(values):
if len(values) == 0:
print('Error in average: no values supplied')
sys.exit(1)
return sum(values) / len(values)
assert to check internal correctness.assert statement checks whether a condition is true.
if, but signals an error instead of controlling a block of code.import sys
def average(values):
assert len(values) > 0, 'No values supplied'
return sum(values) / len(values)
def kelvin_to_celsius(k):
assert k >= 0.0, 'Temperature in Kelvin cannot be negative'
return k - 273.15
assert count_leading_zeros([]) == 0
assert count_leading_zeros([0]) == 1
assert count_leading_zeros([0, 1]) == 1
assert count_leading_zeros([0, 1, 0]) == 1
assert count_leading_zeros([0, 0, 1]) == 2
assert count_leading_zeros([1, 0]) == 0
Is This Needed?
The
oslibrary contains a function calledos.path.exists(path)that returnsTrueif the file or directory identified bypathexists, andFalseif it does not. A colleague of yours routinely uses it to check whether a file exists before trying to open it:... assert os.path.exists(filename), 'File {0} not found'.format(filename) reader = open(filename, 'r') ...Should you adopt this practice? Why or why not?
Finding Corner Cases
- Under what circumstances will the following function fail with an error?
- Under what circumstances will it return something other than what the user would expect?
def find_two_smallest(values): "Find the two smallest values in a list." copy = values[:] copy.sort() return copy[:2]
Test-Driven Development.
- Explain in simple language what
run_startsfunction is supposed to do.- Fill in the blanks in the function definition so that all of the assertions succeed.
- For what input(s) could different users reasonably expect this function to return different values? I.e., where could reasonable people still disagree about the function’s behavior?
- Add one or more assertions to test for each situation identified above.
def run_starts(values): if values == []: ____ result = [] previous = values[0] + 1 for v in values: if v < previous: ____ previous = ____ return result assert run_starts([]) == [] assert run_starts([1]) == [1] assert run_starts([1, 2]) == [1] assert run_starts([1, 2, 1]) == [1, 1] assert run_starts([1, 2, 3, 2, 3]) == [1, 2] assert run_starts([2, 3, 4, 0, 5, 7, 4, 6]) == [2, 0, 4]
Key Points
Fail early, fail loudly.
Can use
if,sys.exitto detect and report errors.Use
assertto check internal correctness.Practice defensive programming.
Practice test-driven development.