{"id":950,"hash":"59bf147c95c23ae0b6c84f2a7a7e64e3f77265dcf874a492c13f0a02b23ec69e","pattern":"KeyError in module &#39;threading&#39; after a successful py.test run","full_message":"I'm running a set of tests with py.test. They pass. Yippie! But I'm getting this message:\n\nException KeyError: KeyError(4427427920,) in <module 'threading' from '/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/threading.pyc'> ignored\n\nHow should I go about tracking down the source of that? (I'm not using threading directly, but am using gevent.)","ecosystem":"pypi","package_name":"gevent","package_version":null,"solution":"I observed a similar issue and decided to see what's going on exactly - let me describe my findings. I hope someone will find it useful.\n\nShort story\n\nIt is indeed related to monkey-patching the threading module. In fact, I can easily trigger the exception by importing the threading module before monkey-patching threads. The following 2 lines are enough:\n\nimport threading\nimport gevent.monkey; gevent.monkey.patch_thread()\n\nWhen executed it spits the message about ignored KeyError:\n\n(env)czajnik@autosan:~$ python test.py \nException KeyError: KeyError(139924387112272,) in <module 'threading' from '/usr/lib/python2.7/threading.pyc'> ignored\n\nIf you swap the import lines, the problem is gone. \n\nLong story\n\nI could stop my debugging here, but I decided it's worth to understand the exact cause of the problem.\n\nFirst step was to find the code that prints the message about ignored exception. It was a little hard for me to find it (grepping for Exception.*ignored yielded nothing), but grepping around CPython source code I've eventually found a function called void PyErr_WriteUnraisable(PyObject *obj) in Python/error.c, with a very interesting comment:\n\n/* Call when an exception has occurred but there is no way for Python\n   to handle it.  Examples: exception in __del__ or during GC. */\n\nI decided to check who's calling it, with a little help from gdb, just to get the following C-level stack trace:\n\n#0  0x0000000000542c40 in PyErr_WriteUnraisable ()\n#1  0x00000000004af2d3 in Py_Finalize ()\n#2  0x00000000004aa72e in Py_Main ()\n#3  0x00007ffff68e576d in __libc_start_main (main=0x41b980 <main>, argc=2,\n    ubp_av=0x7fffffffe5f8, init=<optimized out>, fini=<optimized out>, \n    rtld_fini=<optimized out>, stack_end=0x7fffffffe5e8) at libc-start.c:226\n#4  0x000000000041b9b1 in _start ()\n\nNow we can clearly see that the exception is thrown while Py_Finalize executes - this call is responsible for shutting down the Python interpreter, freeing allocated memory, etc. It's called just before exitting.\n\nNext step was to look at Py_Finalize() code (it's in Python/pythonrun.c). The very first call it makes is wait_for_thread_shutdown() - worth looking at, as we know the problem is related to threading. This function in turn calls _shutdown callable in the threading module. Good, we can go back to python code now. \n\nLooking at threading.py I've found the following interesting parts:\n\nclass _MainThread(Thread):\n\n    def _exitfunc(self):\n        self._Thread__stop()\n        t = _pickSomeNonDaemonThread()\n        if t:\n            if __debug__:\n                self._note(\"%s: waiting for other threads\", self)\n        while t:\n            t.join()\n            t = _pickSomeNonDaemonThread()\n        if __debug__:\n            self._note(\"%s: exiting\", self)\n        self._Thread__delete()\n\n# Create the main thread object,\n# and make it available for the interpreter\n# (Py_Main) as threading._shutdown.\n\n_shutdown = _MainThread()._exitfunc\n\nClearly, the responsibility of threading._shutdown() call is to join all non-daemon threads and delete main thread (whatever that means exactly). I decided to patch threading.py a bit - wrap the whole _exitfunc() body with try/except and print the stack trace with traceback module. This gave the following trace:\n\nTraceback (most recent call last):\n  File \"/usr/lib/python2.7/threading.py\", line 785, in _exitfunc\n    self._Thread__delete()\n  File \"/usr/lib/python2.7/threading.py\", line 639, in __delete\n    del _active[_get_ident()]\nKeyError: 26805584\n\nNow we know the exact place where the exception is thrown - inside Thread.__delete() method. \n\nThe rest of the story is obvious after reading threading.py for a while. The _active dictionary maps thread IDs (as returned by _get_ident()) to Thread instances, for all threads created. When threading module is loaded, an instance of _MainThread class is always created and added to _active (even if no other threads are explicitly created). \n\nThe problem is that one of the methods patched by gevent's monkey-patching is _get_ident() - original one maps to thread.get_ident(), monkey-patching replaces it with green_thread.get_ident(). Obviously both calls return different IDs for main thread.\n\nNow, if threading module is loaded before monkey-patching, _get_ident() call returns one value when _MainThread instance is created and added to _active, and another value at the time _exitfunc() is called - hence KeyError in del _active[_get_ident()].\n\nOn the contrary, if monkey-patching is done before threading is loaded, all is fine - at the time _MainThread instance is being added to _active, _get_ident() is already patched, and the same thread ID is returned at cleanup time. That's it!\n\nTo make sure I import modules in the right order, I added the following snippet to my code, just before monkey-patching call:\n\nimport sys\nif 'threading' in sys.modules:\n        raise Exception('threading module loaded before patching!')\nimport gevent.monkey; gevent.monkey.patch_thread()\n\nI hope you find my debugging story useful :)","confidence":0.95,"source":"stackoverflow","source_url":"https://stackoverflow.com/questions/8774958/keyerror-in-module-threading-after-a-successful-py-test-run","votes":69,"created_at":"2026-04-19T04:52:04.243301+00:00","updated_at":"2026-04-19T04:52:04.243301+00:00"}