After refactoring some code in an application that uses Py4J (simply splitting the code that interacts with Py4J into a separate file), I noticed exceptions like
Exception TypeError: "'NoneType' object is not callable" in <function <lambda> at 0x10213e5f0> ignored
when my application shut down.
Here's a small test case that reproduces the problem with Python 2.7.1 (on my machine, at least):
In test.py
:
from py4j.java_gateway import JavaGateway
gateway = JavaGateway.launch_gateway(die_on_exit=True)
Running python test.py
produces no errors. In test2.py
, I simply import test
:
import test
Running python test2.py
results in
Exception TypeError: 'isinstance() arg 2 must be a class, type, or tuple of classes and types' in <function <lambda> at 0x1069f1758> ignored
The cause appears to be related to the order in which modules are cleaned up when Python shuts down. When running test.py
, the JavaGateway
instance (in __main__
) is cleaned up before any of the py4j
modules are cleaned up:
$ python -v test.py
...
# cleanup __main__
# cleanup[1] py4j
...
# cleanup[1] py4j.protocol
Compare this to running test2.py
, which imports test
:
$ python -v test2.py
...
# cleanup __main__
# cleanup[1] py4j
...
# cleanup[1] py4j.finalizer
...
# cleanup[1] py4j.java_collections
...
# cleanup[1] py4j.protocol
...
# cleanup[1] test
...
# cleanup[1] py4j.java_gateway
# cleanup[1] py4j.compat
...
In this case, py4j
modules are cleaned up before the test
module and the JavaGateway
instance are cleaned up.
The cause of this particular TypeError
exception isn't immediately clear because of the lambda
function. After renaming the lambda
functions in Py4J, it appears that it refers to the weakref.ref
callback in JavaObject
. I suspect that this problem may be due to the weakref
callback calling functions from modules that may have already been cleaned up. It looks like this specific 'isinstance() arg 2 must be a class, type, or tuple of classes and types'
exception might be occurring in a isinstance(s, unicode)
call in smart_decode
, which is odd.
Interestingly, adding an extra, unused py4j
import in test.py
fixes the exception and causes all of the py4j
modules to be cleaned up after the test
module and JavaGateway
instance.
I'm not sure if this is a serious issue:
- It requires a fairly specific pattern of imports and module dependencies to reproduce.
- It only occurs during Python shutdown.
- There's an easy (if non-obvious) workaround.