python

You are currently browsing articles tagged python.

The other day at work we encountered an unusual exception in our nightly pounder test run after landing some new code to expose some internal state via a monitoring API. The problem occurred on shutdown. The new monitoring code was trying to log some information, but was encountering an exception. Our logging code was built on top of Python’s logging module, and we thought perhaps that something was shutting down the logging system without us knowing. We ourselves never explicitly shut it down, since we wanted it to live until the process exited.

The monitoring was done inside a daemon thread. The Python docs say only:

A thread can be flagged as a “daemon thread”. The significance of this flag is that the entire Python program exits when only daemon threads are left. “

Which sounds pretty good, right? This thread is just occasionally grabbing some data, and we don’t need to do anything special when the program shuts down. Yeah, I remember when I used to believe in things too.

Despite a global interpreter lock that prevents Python from being truly concurrent anyway, there is a very real possibility that the daemon threads can still execute after the Python runtime has started its own tear-down process. One step of this process appears to be to set the values inside globals() to None, meaning that any module resolution results in an AttributeError attempting to dereference NoneType. Other variations on this cause TypeError to be thrown.

The code which triggered this looked something like this, although with more abstraction layers which made hunting it down a little harder:

try:
    log.info("Some thread started!")
    try:
        do_something_every_so_often_in_a_loop_and_sleep()
    except somemodule.SomeException:
        pass
    else:
        pass
finally:
    log.info("Some thread exiting!")

The exception we were seeing was an AttributeError on the last line, the log.info() call. But that wasn’t even the original exception. It was actually another AttributeError caused by the somemodule.SomeException dereference. Because all the modules had been reset, somemodule was None too.

Unfortunately the docs are completely devoid of this information, at least in the threading sections which you would actually reference. The best information I was able to find was this email to python-list a few years back, and a few other emails which don’t really put the issue front and center.

In the end the solution for us was simply to make them non-daemon threads, notice when the app is being shut down and join them to the main thread. Another possibility for us was to catch AttributeError in our thread wrapper class — which is what the author of the aforementioned email does — but that seems like papering over a real bug and a real error. Because of this misbehavior, daemon threads lose almost all of their appeal, but oddly I can’t find people really publicly saying “don’t use them” except in scattered emails. It seems like it’s underground information known only to the Python cabal. (There is no cabal.)

So, I am going to say it. When I went searching there weren’t any helpful hints in a Google search of “python daemon threads considered harmful”. So, I am staking claim to that phrase. People of The Future: You’re welcome.

Tags: , , ,

Contrary to popular belief, not all code at ITA Software is in Lisp. So far all of my work has been in Python. And while Python has been my “quick and dirty” language of choice for many years now, it’s nice to be hacking in Python again for “real” code. The first and last real project I worked on that heavily used Python was Red Carpet back in 2002, and at the time we were targeting Python 1.5 and 2.0.

I really like having hard copies of reference materials on my desk. In a well-organized book, I can look up information faster than I can find it online with the Googling and the clicking and the latency. I also think that it’s probably good for me, since it gives me a break from the harshness of looking at a computer screen for a few moments and takes my hands off the keyboard. It’s a useful and pleasant diversion. When I was working at Novell on Beagle, I always had C# in a Nutshell and Lucene in Action within reach.

While I followed Python’s evolution closely enough to know about some of the newer features like list comprehensions and decorators, my old reference books — the Python Pocket Reference and Python Essential Reference — had gone long out of date and included neither of these nor many more improvements. Picking up the newer edition of the Python Pocket Reference was a no-brainer. A tougher choice was which to choose as my main reference material: the updated Python Essential Reference or Python in a Nutshell.

Based on Amazon’s reviews and my love for the C# Nutshell, I picked up the latter. Regrettably, I made the wrong choice. While it is comprehensive in its content, it feels random in its organization and its index is practically useless. Whereas C# is laid out alphabetically by namespace, making it easy to look up assemblies, objects, methods and properties, Python is laid out conceptually but not in any immediately identifiable order. If you haven’t looked something up before, it’s virtually impossible to do it just by flipping through it. Once you find the large section on file operations, you have to flip through roughly 20 pages before you get to os.path. This wouldn’t be a big problem if I could look up every method, object, and module in the index, but it is woefully spartan. I rely heavily on the indexes of my reference books, and I just can’t find anything in Python in a Nutshell. What are commonly used types that I pass into in a isinstance() call? I could not find them in this book. A large, comprehensive index would do this book a tremendous amount of good and transform it into a great reference for me.

A co-worker had the new edition of Python Essential Reference and it’s at least as good as its previous edition. While it doesn’t have an immediately intuitive layout for a quick flip-through, it has a truly amazing index. Just like the previous edition, I have had no problems finding anything I need, including a list of common types for the isinstance() question I asked earlier. It’s well-written and comprehensive as well. I will certainly pick up another copy for myself.

In summary? For Python reference books, buy Python Essential Reference. The Python Pocket Reference is good for quick stuff, like “what does else mean at the end of a for block?” And Python in a Nutshell, while a well-written book, lacks the organization needed to be a “go to” reference book.

Tags: , , ,