Mastering Python Second Edition Release Code
This commit is contained in:
@@ -0,0 +1,4 @@
|
||||
Chapter 11, Debugging
|
||||
##############################################################################
|
||||
|
||||
| Solving bugs demonstrates several methods of hunting down bugs using tracing, logging and interactive debugging.
|
||||
@@ -0,0 +1,30 @@
|
||||
import os
|
||||
import inspect
|
||||
import linecache
|
||||
|
||||
|
||||
def print_code():
|
||||
while True:
|
||||
info = inspect.stack()[1]
|
||||
lineno = info.lineno
|
||||
function = info.function
|
||||
# Strip the path from the filename
|
||||
filename = os.path.split(info.filename)[-1]
|
||||
# Fetch the next line of code
|
||||
code = linecache.getline(info.filename, lineno + 1)
|
||||
print(f'{filename}:{lineno}:{function}: {code.strip()}')
|
||||
yield
|
||||
|
||||
|
||||
# Always prime the generator
|
||||
print_code = print_code()
|
||||
|
||||
|
||||
def some_test_function(a, b):
|
||||
next(print_code)
|
||||
c = a + b
|
||||
next(print_code)
|
||||
return c
|
||||
|
||||
|
||||
some_test_function('a', 'b')
|
||||
@@ -0,0 +1,56 @@
|
||||
>>> def hiding_generator():
|
||||
... print('a')
|
||||
... yield 'first value'
|
||||
... print('b')
|
||||
... yield 'second value'
|
||||
... print('c')
|
||||
|
||||
>>> generator = hiding_generator()
|
||||
|
||||
>>> next(generator)
|
||||
a
|
||||
'first value'
|
||||
|
||||
>>> next(generator)
|
||||
b
|
||||
'second value'
|
||||
|
||||
>>> next(generator)
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
StopIteration
|
||||
|
||||
##############################################################################
|
||||
|
||||
>>> import os
|
||||
>>> import inspect
|
||||
>>> import linecache
|
||||
|
||||
|
||||
>>> def print_code():
|
||||
... while True:
|
||||
... info = inspect.stack()[1]
|
||||
... lineno = info.lineno + 1
|
||||
... function = info.function
|
||||
... # Fetch the next line of code
|
||||
... code = linecache.getline(info.filename, lineno)
|
||||
... print(f'{lineno:03d} {function}: {code.strip()}')
|
||||
... yield
|
||||
|
||||
|
||||
# Always prime the generator
|
||||
|
||||
>>> print_code = print_code()
|
||||
|
||||
|
||||
>>> def some_test_function(a, b):
|
||||
... next(print_code)
|
||||
... c = a + b
|
||||
... next(print_code)
|
||||
... return c
|
||||
|
||||
>>> some_test_function('a', 'b')
|
||||
003 some_test_function: c = a + b
|
||||
005 some_test_function: return c
|
||||
'ab'
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
def some_test_function(a, b):
|
||||
c = a + b
|
||||
return c
|
||||
|
||||
|
||||
print(some_test_function('a', 'b'))
|
||||
@@ -0,0 +1,10 @@
|
||||
--- modulename: T_02_selective_trace, funcname: some_test_function
|
||||
0.00 T_02_selective_trace.py(19): c = a + b
|
||||
0.00 T_02_selective_trace.py(20): return c
|
||||
ab
|
||||
--- modulename: contextlib, funcname: __exit__
|
||||
0.00 contextlib.py(...): if typ... is None:
|
||||
0.00 contextlib.py(...): try:
|
||||
0.00 contextlib.py(...): next(self.gen)
|
||||
--- modulename: T_02_selective_trace, funcname: trace
|
||||
0.00 T_02_selective_trace.py(12): sys.settrace(None)
|
||||
@@ -0,0 +1,24 @@
|
||||
import sys
|
||||
import trace as trace_module
|
||||
import contextlib
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
def trace(count=False, trace=True, timing=True):
|
||||
tracer = trace_module.Trace(
|
||||
count=count, trace=trace, timing=timing)
|
||||
sys.settrace(tracer.globaltrace)
|
||||
yield tracer
|
||||
sys.settrace(None)
|
||||
|
||||
result = tracer.results()
|
||||
result.write_results(show_missing=False, summary=True)
|
||||
|
||||
|
||||
def some_test_function(a, b):
|
||||
c = a + b
|
||||
return c
|
||||
|
||||
|
||||
with trace():
|
||||
print(some_test_function('a', 'b'))
|
||||
@@ -0,0 +1,8 @@
|
||||
--- modulename: T_03_filename_trace, funcname: some_test_function
|
||||
T_03_filename_trace.py(27): c = a + b
|
||||
T_03_filename_trace.py(28): return c
|
||||
ab
|
||||
--- modulename: T_03_filename_trace, funcname: trace
|
||||
T_03_filename_trace.py(20): sys.settrace(None)
|
||||
lines cov% module (path)
|
||||
3 100% T_03_filename_trace (/CH_11_debugging/T_03_filename_trace.py)
|
||||
@@ -0,0 +1,33 @@
|
||||
import sys
|
||||
import trace as trace_module
|
||||
import contextlib
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
def trace(filename):
|
||||
tracer = trace_module.Trace()
|
||||
|
||||
def custom_trace(frame, event, arg):
|
||||
# Only trace for the given filename
|
||||
if filename != frame.f_code.co_filename:
|
||||
return custom_trace
|
||||
|
||||
# Let globaltrace handle the rest
|
||||
return tracer.globaltrace(frame, event, arg)
|
||||
|
||||
sys.settrace(custom_trace)
|
||||
yield tracer
|
||||
sys.settrace(None)
|
||||
|
||||
result = tracer.results()
|
||||
result.write_results(show_missing=False, summary=True)
|
||||
|
||||
|
||||
def some_test_function(a, b):
|
||||
c = a + b
|
||||
return c
|
||||
|
||||
|
||||
# Pass our current filename as `__file__`
|
||||
with trace(filename=__file__):
|
||||
print(some_test_function('a', 'b'))
|
||||
@@ -0,0 +1,3 @@
|
||||
DEBUG:root:add(a=1, b=123): 124
|
||||
DEBUG:root:add(a=1, b=456): 457
|
||||
DEBUG:root:add(a=456, b=1): 457
|
||||
@@ -0,0 +1,43 @@
|
||||
import pprint
|
||||
import inspect
|
||||
import logging
|
||||
import functools
|
||||
|
||||
|
||||
def debug(function):
|
||||
@functools.wraps(function)
|
||||
def _debug(*args, **kwargs):
|
||||
# Make sure `result` is always defined
|
||||
result = None
|
||||
try:
|
||||
result = function(*args, **kwargs)
|
||||
return result
|
||||
finally:
|
||||
# Extract the signature from the function
|
||||
signature = inspect.signature(function)
|
||||
# Fill the arguments
|
||||
arguments = signature.bind(*args, **kwargs)
|
||||
# NOTE: This only works for Python 3.5 and up!
|
||||
arguments.apply_defaults()
|
||||
|
||||
logging.debug('%s(%s): %s' % (
|
||||
function.__qualname__,
|
||||
', '.join('%s=%r' % (k, v) for k, v in
|
||||
arguments.arguments.items()),
|
||||
pprint.pformat(result),
|
||||
))
|
||||
|
||||
return _debug
|
||||
|
||||
|
||||
@debug
|
||||
def add(a, b=123):
|
||||
return a + b
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
logging.basicConfig(level=logging.DEBUG)
|
||||
|
||||
add(1)
|
||||
add(1, 456)
|
||||
add(b=1, a=456)
|
||||
@@ -0,0 +1,3 @@
|
||||
[DEBUG] T_05_logging_config.py:20:_debug: add(a=1, b=123): 124
|
||||
[DEBUG] T_05_logging_config.py:20:_debug: add(a=1, b=456): 457
|
||||
[DEBUG] T_05_logging_config.py:20:_debug: add(a=456, b=1): 457
|
||||
@@ -0,0 +1,45 @@
|
||||
import pprint
|
||||
import inspect
|
||||
import logging
|
||||
import functools
|
||||
|
||||
|
||||
def debug(function):
|
||||
@functools.wraps(function)
|
||||
def _debug(*args, **kwargs):
|
||||
try:
|
||||
result = function(*args, **kwargs)
|
||||
finally:
|
||||
# Extract the signature from the function
|
||||
signature = inspect.signature(function)
|
||||
# Fill the arguments
|
||||
arguments = signature.bind(*args, **kwargs)
|
||||
# NOTE: This only works for Python 3.5 and up!
|
||||
arguments.apply_defaults()
|
||||
|
||||
logging.debug('%s(%s): %s' % (
|
||||
function.__qualname__,
|
||||
', '.join('%s=%r' % (k, v) for k, v in
|
||||
arguments.arguments.items()),
|
||||
pprint.pformat(result),
|
||||
))
|
||||
|
||||
return _debug
|
||||
|
||||
|
||||
@debug
|
||||
def add(a, b=123):
|
||||
return a + b
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
log_format = (
|
||||
# Removed %(relativeCreated)d because of test unstability
|
||||
'[%(levelname)s] '
|
||||
'%(filename)s:%(lineno)d:%(funcName)s: %(message)s'
|
||||
)
|
||||
logging.basicConfig(level=logging.DEBUG, format=log_format)
|
||||
|
||||
add(1)
|
||||
add(1, 456)
|
||||
add(b=1, a=456)
|
||||
@@ -0,0 +1,24 @@
|
||||
import traceback
|
||||
|
||||
|
||||
class ShowMyStack:
|
||||
|
||||
def run(self, limit=None):
|
||||
print('Before stack print')
|
||||
traceback.print_stack(limit=limit)
|
||||
print('After stack print')
|
||||
|
||||
|
||||
class InheritShowMyStack(ShowMyStack):
|
||||
pass
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
show_stack = InheritShowMyStack()
|
||||
|
||||
print('Stack without limit')
|
||||
show_stack.run()
|
||||
print()
|
||||
|
||||
print('Stack with limit 1')
|
||||
show_stack.run(1)
|
||||
@@ -0,0 +1,4 @@
|
||||
import ctypes
|
||||
|
||||
# Get memory address 0, your kernel shouldn't allow this:
|
||||
ctypes.string_at(0)
|
||||
@@ -0,0 +1,7 @@
|
||||
import ctypes
|
||||
|
||||
try:
|
||||
# Get memory address 0, your kernel shouldn't allow this:
|
||||
ctypes.string_at(0)
|
||||
except Exception as e:
|
||||
print('Got exception:', e)
|
||||
@@ -0,0 +1,7 @@
|
||||
import ctypes
|
||||
import faulthandler
|
||||
|
||||
faulthandler.enable()
|
||||
|
||||
# Get memory address 0, your kernel shouldn't allow this:
|
||||
ctypes.string_at(0)
|
||||
@@ -0,0 +1,12 @@
|
||||
import code
|
||||
|
||||
|
||||
def start_console():
|
||||
some_variable = 123
|
||||
print(f'Launching console, some_variable: {some_variable}')
|
||||
code.interact(banner='console:', local=locals())
|
||||
print(f'Exited console, some_variable: {some_variable}')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
start_console()
|
||||
@@ -0,0 +1,12 @@
|
||||
import pdb
|
||||
|
||||
|
||||
def go_to_debugger():
|
||||
some_variable = 123
|
||||
print('Starting pdb trace')
|
||||
pdb.set_trace()
|
||||
print(f'Finished pdb, some_variable: {some_variable}')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
go_to_debugger()
|
||||
@@ -0,0 +1,11 @@
|
||||
import pdb
|
||||
|
||||
|
||||
def print_value(value):
|
||||
print('value:', value)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
pdb.set_trace()
|
||||
for i in range(5):
|
||||
print_value(i)
|
||||
@@ -0,0 +1,3 @@
|
||||
print('This still works')
|
||||
1 / 0
|
||||
print('We will never reach this')
|
||||
@@ -0,0 +1,6 @@
|
||||
def do_nothing(i):
|
||||
pass
|
||||
|
||||
|
||||
for i in range(10):
|
||||
do_nothing(i)
|
||||
@@ -0,0 +1,7 @@
|
||||
def print_value(value):
|
||||
print('value:', value)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
for i in range(5):
|
||||
print_value(i)
|
||||
Reference in New Issue
Block a user