Documentatio ion, , testin ing and debugg gging docstring defensive programming assert test driven developement assertions testing unittest debugger static type checking (mypy)
Ensuring good quality code ? Design phase Usage Development (hopefully) correct program User runs Coding program Idea success Fix bug Testing Goal Develop programs that runs forever / crash / work correctly Find bug incorrect output / Tools and techniques explosion / ...
What is is good code ? Readability • well-structured • documentation • comments • follow some standard structure (easy to recognize, follow PEP8 Style Guide) Correctness • outputs the correct answer on valid input • eventually stops with an answer on valid input (should not go in infinite loop) Reusable...
Why ? Documentation Testing Debugging specification of Correct Where is the #!¤$ functionality implementation ? bug ? docstring Try to predict behavior on • for users of the code unknown input ? • modules • methods Performance • classes guarantees ? comments • for readers of the code ” Program testing can be used to show the presence of bugs, but never to show their absence ” ̶ Edsger W. Dijkstra
| Built-in exceptio ions +-- OSError | +-- BlockingIOError | +-- ChildProcessError (class hierarchy) | +-- ConnectionError | | +-- BrokenPipeError | | +-- ConnectionAbortedError BaseException | | +-- ConnectionRefusedError +-- SystemExit | | +-- ConnectionResetError +-- KeyboardInterrupt | +-- FileExistsError docs.python.org/3/library/exceptions.html +-- GeneratorExit | +-- FileNotFoundError +-- Exception | +-- InterruptedError +-- StopIteration | +-- IsADirectoryError +-- StopAsyncIteration | +-- NotADirectoryError +-- ArithmeticError | +-- PermissionError | +-- FloatingPointError | +-- ProcessLookupError | +-- OverflowError | +-- TimeoutError | +-- ZeroDivisionError +-- ReferenceError +-- AssertionError +-- RuntimeError +-- AttributeError | +-- NotImplementedError +-- BufferError | +-- RecursionError +-- EOFError +-- SyntaxError +-- ImportError | +-- IndentationError | +-- ModuleNotFoundError | +-- TabError +-- LookupError +-- SystemError | +-- IndexError +-- Warning | +-- KeyError +-- DeprecationWarning +-- MemoryError +-- PendingDeprecationWarning +-- NameError +-- RuntimeWarning | +-- UnboundLocalError +-- SyntaxWarning +-- TypeError +-- UserWarning +-- ValueError +-- FutureWarning | +-- UnicodeError +-- ImportWarning | +-- UnicodeDecodeError +-- UnicodeWarning | +-- UnicodeEncodeError +-- BytesWarning | +-- UnicodeTranslateError +-- ResourceWarning
Testin ing for unexpected behaviour ? infinite-recursion1.py infinite-recursion3.py def f(depth): import sys f(depth + 1) # infinite recursion def f(depth): f(0) if depth > 100: print("runaway recursion???") Python shell sys.exit() # system function | RecursionError: maximum recursion depth exceeded f(depth + 1) raises SystemExit f(0) infinite-recursion2.py Python shell def f(depth): if depth > 100: | runaway recursion??? print("runaway recursion???") raise SystemExit # raise built-in exception let the program eventually fail f(depth + 1) check and raise exceptions f(0) Python shell check and call sys.exit | runaway recursion???
Catching unexpected behaviour – assert infinite-recursion4.py def f(depth): assert depth <= 100 # raise exception if False f(depth + 1) keyword assert checks if f(0) Python shell boolean expression is true, | File "...\infinite-recursion4.py", line 2, in f if not, raises exception | assert depth <= 100 | AssertionError AssertionError infinite-recursion5.py def f(depth): optional second parameter assert depth <= 100, "runaway recursion???" f(depth + 1) passed to the constructor of f(0) the exception Python shell | File ".../infinite-recursion5.py", line 2, in f | assert depth <= 100, "runaway recursion???" | AssertionError: runaway recursion???
isabling assert statements Dis assert statements are good to help check correctness of program – but can slow down program invoking Python with option – O disables all assertions (by setting __debug__ to False ) docs.python.org/3/reference/simple_stmts.html#assert
Example
Fir irst try ry... (seriously, , the bugs were not on purpose) intsqrt_buggy.py def int_sqrt(x): low = 0 high = x while low < high - 1: mid = (low + high) / 2 if mid ** 2 <= x: low = mid else: high = mid return low Python shell > int_sqrt(10) | 3.125 # 3.125 ** 2 = 9.765625 > int_sqrt(-10) | 0 # what should the answer be ?
Let u us add a specificatio ion... all methods, classes, and intsqrt.py modules can have a def int_sqrt(x): docstring (ideally """Compute the integer square root of an integer x. docstring have) as a specification input Assumes that x is an integer and x >= 0 requirements for methods: summarize Returns integer floor(sqrt(x))""" output purpose in first line, guarantees ... followed by input Python shell requirements and ouput > help(int_sqrt) guarantees | Help on function int_sqrt in module __main__: the docstring is assigned | | int_sqrt(x) to the object’s __doc__ | Compute the integer square root of an integer x. attribute | Assumes that x is an integer and x >= 0 | Returns integer floor(sqrt(x)) PEP 257 -- Docstring Conventions www.python.org/dev/peps/pep-0257/
Let u us check in input requirements... doing explicit checks for intsqrt.py valid input arguments is def int_sqrt(x): """Compute the integer square root of an integer x. part of defensive programming and helps Assumes that x is an integer and x >= 0 spotting errors early Returns integer floor(sqrt(x))""" (instead of continuing assert isinstance(x, int) check input assert 0 <= x requirements using likely wrong ... values... resulting in a Python shell final meaningless error) > int_sqrt(-10) | File "...\int_sqrt.py", line 7, in int_sqrt | assert 0 <= x | AssertionError
Let u us check if if output correct... intsqrt.py def int_sqrt(x): output check identifies """Compute the integer square root of an integer x. the error Assumes that x is an integer and x >= 0 mid = (low+high) / 2 Returns integer floor(sqrt(x))""" should have been assert isinstance(x, int) mid = (low+high) // 2 assert 0 <= x ... The output check helps assert isinstance(result, int) check assert result ** 2 <= x < (result + 1) ** 2 output us to ensure that return result functions specification is Python shell guaranteed in > int_sqrt(10) applications | File "...\int_sqrt.py", line 20, in int_sqrt | assert isinstance(result, int) | AssertionError
Let u us test s some in input valu lues... intsqrt.py def int_sqrt(x): test identifies ... wrong output for x = 1 assert int_sqrt(0) == 0 assert int_sqrt(1) == 1 assert int_sqrt(2) == 1 assert int_sqrt(3) == 1 assert int_sqrt(4) == 2 assert int_sqrt(5) == 2 assert int_sqrt(200) == 14 Python shell | Traceback (most recent call last): | File "...\int_sqrt.py", line 28, in <module> | assert int_sqrt(1) == 1 | File "...\int_sqrt.py", line 21, in int_sqrt | assert result ** 2 <= x < (result + 1) ** 2 | AssertionError
Let u us check progress of alg lgorithm... intsqrt.py ... test identifies wrong low, high = 0, x output for x = 1 while low < high - 1: check invariant assert low ** 2 <= x < high ** 2 but invariant apparently for loop mid = (low + high) / 2 correct ??? if mid ** 2 <= x: problem low = mid else: low == result == 0 high = mid high == 1 result = low implies loop never entered ... output check identifies the Python shell error | Traceback (most recent call last): | File "...\int_sqrt.py", line 28, in <module> high = x | assert int_sqrt(1) == 1 should have been | File "...\int_sqrt.py", line 21, in int_sqrt | high = x + 1 assert result ** 2 <= x < (result + 1) ** 2 | AssertionError
intsqrt.py def int_sqrt(x): """Compute the integer square root of an integer x. Fin inal program Assumes that x is an integer and x >= 0 Returns the integer floor(sqrt(x))""" assert isinstance(x, int) assert 0 <= x low, high = 0, x + 1 We have used assertions to: while low < high - 1: assert low ** 2 <= x < high ** 2 Test if input arguments / usage is mid = (low + high) // 2 if mid ** 2 <= x: valid (defensive programming) low = mid else: Test if computed result is correct high = mid result = low Test if an internal invariant in the assert isinstance(result, int) computation is satisfied assert result ** 2 <= x < (result + 1) ** 2 Perform a final test for a set of return result test cases (should be run assert int_sqrt(0) == 0 whenever we change anything in assert int_sqrt(1) == 1 assert int_sqrt(2) == 1 the implementation) assert int_sqrt(3) == 1 assert int_sqrt(4) == 2 assert int_sqrt(5) == 2 assert int_sqrt(200) == 14
Recommend
More recommend