I will first introduce the general solution and then show how it can be adapted for this particular use case.
General solution
Nowadays there are easier ways to debug such things utilizing the new functionalities of the python import system. Basically just add your own module finders (MetaPathFinder) to sys.meta_path
. Here is an example for a script importing pandas
and listing all the succesful imports in imported
and unsuccesful imports in could_not_be_imported
:
import sys
imported = []
could_not_be_imported = []
class FirstFinder:
def find_spec(self, modulename, path=None, target=None):
imported.append(modulename)
class LastFinder:
def find_spec(self, modulename, path=None, target=None):
imported.remove(modulename)
could_not_be_imported.append(modulename)
# setup ("start recording imports")
sys.meta_path.insert(0, FirstFinder())
sys.meta_path.append(LastFinder())
import pandas # import anything here
# cleanup ("stop recording")
sys.meta_path = [
x for x in sys.meta_path if not isinstance(x, (FirstFinder, LastFinder))
]
The list containing the succesful imports is then imported
:
>>> imported
['pandas',
'numpy',
'numpy._globals',
# ...
'pandas.io.sql',
'pandas.io.stata',
'pandas.io.xml',
'pandas.util._tester',
'pandas._version']
and could_not_be_imported
:
>>> could_not_be_imported
['pickle5',
'org',
'fcntl',
'backports_abc',
'six.moves',
'backports_abc',
'backports_abc',
# ...
'cloudpickle',
'numexpr',
'bottleneck',
'org',
'backports_abc',
'backports_abc',
'backports_abc']
Note: There can be duplicates in either of the lists and all the modules are listed in order of imports. Modify as fits to your needs.
Specific use case: Finding Erroneous import
Modify the LastFinder
to be:
class LastFinder:
def find_spec(self, modulename, path=None, target=None):
imported.remove(modulename)
print(
"\nMissing module! ",
f"Tried to import: {modulename}\n",
"Last few last imports:\n\t",
"\n\t ".join(imported[-10:]),
)
could_not_be_imported.append(modulename)
This will then print, with every missing module, a list of the few last succesfully imported modules and the module you were trying to import. For example:
Missing module! Tried to import: bottleneck
Last few last imports:
pandas.core.ops.common
pandas.core.ops.docstrings
pandas.core.ops.mask_ops
pandas.core.ops.methods
pandas.core.missing
pandas.core.array_algos.quantile
pandas.core.sorting
pandas.core.arrays.boolean
pandas.core.arrays.masked
pandas.core.nanops
In this case this would mean that the pandas.core.nanops
was the last succcesful import before the unsuccesful import. Therefore, it would be quite easy to check from pandas.core.nanops.__file__
where the broken import is. (look for "bottleneck"
in the file). In my case, I found:
# pandas\core\nanops.py
# ...
bn = import_optional_dependency("bottleneck", errors="warn")
How this works?
The python import system goes through the sys.meta_path
to look for a spec finder which will have find_spec
method which returns something else than None
. If None
is returned, next finder in sys.meta_path
is used for the import.