Debugging complex code is not usually fun
It's hard to get motivated when faced with a big pile of numbers that just aren't lining up in the ways they are supposed to. Stupid numbers, do what you're told!
The task at hand
I've been rebuilding a 15 to 20-year-old ColdFusion currency trading learning simulation for a client. The math involved is mostly contained in SQL Server stored procedures, some values in tables are really constants (or at least most of the time), and variable names between steps of the process aren't consistent. This has made the rebuilding effort harder than it originally appeared. Add in finding some undesired behaviors in the old app and the desire to improve these situations going forward has been a bit of a slog actually.
I knew that today was the day I had to just dig in and iterate on this code until all of the numbers were correct now that I had a known good set of values to work against.
Since I needed to compare several numbers together, I thought I might use a Rich Table to make the output a little better organized. And I'm VERY glad I did.
Here is what I started with:
Not exactly pretty. It spit out the numbers it was calculating and I could compare them against the known good values visually. Looking and re-re-reading the numbers a half dozen times I was already bored and feeling tired despite having too much caffeine in my system.
So I implemented all of the expected, calculated and differences between the two in a Rich Table. I took a couple of moments to adjust some colors and right justifying all of these numbers because I knew I was going to be staring at this all freaking day.
I then went about debugging the first Total Unhedged number because all of the following calculations depended on this number being correct. And I quickly found the bug!
I would love to say it was because of this great new visualization, but it was just closely re-reading my Python code and comparing it (yet again) to the original portion of a stored procedure to find my subtle mistake.
One down, 6 more to go I thought. This is going to be a long day, but it turned out two more of the values were NEARLY correct now, just off due to rounding. This WAS easier to spot because of the tabular layout. I treated myself to a bit more table improvement, I wrote a little function named
calc_diff() that took the expected and actual values and color coded the difference red if there was a difference and green if not.
Table code example
The code for this is surprisingly easy. Rich was already a dependency of something else in my project's virtualenv so I didn't even need to install it. Here is a simplified example of the code for you:
from decimal import Decimal from rich.console import Console from rich.table import Table from rich.text import Text def calc_diff(expected, actual): diff = expected - actual color = "red" if diff == 0: color = "green" text = Text(str(diff)) text.stylize(color) return text # My actual pytest test def test_ex3_math(ex3_params): expected_total_unhedged = Decimal("3.394148") # call a method on a fixture class that does the math calculation for us, yours # would obviously be different total_unhedged = ex3_params.total_unhedged() # Build our table table = Table( title="Exercise 3 Answers", title_style="gold1", style="deep_sky_blue3" ) table.add_column( "Name", style="turquoise2", header_style="turquoise2", no_wrap=True ) table.add_column("Expected", header_style="turquoise2", justify="right") table.add_column("Actual", header_style="turquoise2", justify="right") table.add_column("Difference", header_style="turquoise2", justify="right") # Total Unhedged - just showing you one row here for sake of brevity table.add_row( "Total Unhedged", str(expected_total_unhedged), str(total_unhedged), calc_diff(expected_total_unhedged, total_unhedged), ) # Output the table console = Console(color_system="256") console.print(table) # Trigger our test to fail artificially assert False
Was it worth the extra time? DEFINITELY! I not only made it easier to compare the values as I worked but also motivated me a little more than usual as I turned each red line green.
Every little bit helps, right? Hopefully, you enjoyed this protip and can put it use in one of your upcoming debugging sessions.
If you or your company struggles with testing your Python or Django apps, check out TestStart our testing consulting package to help jump start your team into better productivity.