Day of the Thread

If you think you’ve found a bug in Python, what’s next? I'll guide you through the process of submitting a patch, so you can avoid its pitfalls and find the shortest route to becoming a Python contributor!

This is the final post in a three part series. In Night of the Living Thread I fixed a bug in Python's threading implementation, so that threads wouldn't become zombies after a fork. In Dawn of the Thread I battled zombie threads in Python 2.6. Now, in the horrifying conclusion, I return to the original bugfix and submit it to the core Python team. Humanity's last hope is that we can get a patch accepted and stop the zombie threads...before it's too late.

The action starts when I open a bug in the Python bug tracker. The challenge is to make a demonstration of the bug. I need to convince the world that I'm not crazy: the dead really are rising and walking the earth! Luckily I have a short script from Night of the Living Thread that shows the zombification process clearly.

Day of the Dead

Next I have to fix the bug and submit a patch. I'm confused here, since the bug is in Python 2.7 and 3.3: do I submit fixes for both versions? The right thing to do is clone the Python source:

hg clone http://hg.python.org/cpython

I fix the bug at the tip of the default branch. The Lifecycle of a Patch doc in the Python Developer's Guide tells me to make a patch with hg diff. I attach it to the bug report by hitting the "Choose File" button and then "Submit Changes."

After this, the Python Developer's Guide is no more use. The abomination I am about to encounter isn't described in any guide: The Python bug tracker is a version of Roundup, hacked to pieces and sewn together with a code review tool called Rietveld. The resulting botched nightmare is covered in scabs, stitches, and suppurating wounds. It's a revolting Frankenstein's monster. (And I thought this was only a zombie movie.)

When I upload a patch to the bug tracker, Roundup, it is digested and spit out into the code review tool, Rietveld. It shows up like this, so a Python core developer can critique my bugfix. Charles-François Natali is my reviewer. He suggests a cleaner bugfix which you can read about in my earlier post, and shows me how to improve my unittest.

Tragically, a week passes before I know he's reviewed my patch. I keep visiting the issue in Roundup expecting to see comments there, but I'm not looking where I should be: there's a little blue link in Roundup that says "review", which leads to Rietveld. That's where I should go to see feedback. Precious time is lost as hordes of zombie threads continue to ravage the landscape.

Day of the Dead street

Even worse, my Gmail account thinks Rietveld's notifications are spam. It turns out that the bug tracker was once breached by spammers and used to send spam in the past, so Gmail is quick to characterize all messages from bugs.python.org as spam. I override Gmail's spam filter with a new filter:

Gmail python filter

Once I make the changes Charles-François suggests, I try to re-upload my patch. Clicking "Add Another Patch Set" in Rietveld doesn't work: it shows a page with a TypeError and a traceback. So I follow the instructions to upload a patch using the upload.py script from the command line and that throws an exception, too. I can't even cry out for help: hitting "reply" to add a comment in Rietveld fails. I tremble in fear.

Day of the Dead hands

Just when humanity's doom seems inevitable, I find a way out: It turns out I must upload my new patch as an additional attachment to the issue in Roundup. Then Roundup, after some delay, applies it to the code review in Rietveld. Finally, I can address Charles-François's objections, and he accepts my patch! Roundup informs me when he applies my changes to the 2.7, 3.3, and default branches.

As the darkness lifts I reflect on how contributing to Python has benefited me, despite the horror. For one thing, I learned a few things about Python. I learned that every module in the standard library imports its dependencies like this example from threading.py:

from time import time as _time, sleep as _sleep

When you execute a statement like from threading import *, Python only imports names that don't begin with an underscore. So renaming imported items is a good way to control which names a module exports by default, an alternative to the __all__ list.

The code-review process also taught me about addCleanup(), which is sometimes a nicer way to clean up after a test than either tearDown or a try/finally block. And I learned that concurrency bugs are easier to reproduce in Python 2 with sys.setcheckinterval(0) and in Python 3 with sys.setswitchinterval(1e-6).

But the main benefit of contributing to Python is the satisfaction and pride I gain: Python is my favorite language. I love it, and I saved it from zombies. Heroism is its own reward.

Day of the Dead final