Reputation: 8049
I am calling importlib.import_module("somemodulename")
to import some customized user defined module. This import may fail. I want to give the user the information about why it failed so I need to print the traceback but it tends to be very long full of lines from importlib
module. For example it is:
Traceback (most recent call last):
File "C:\abc\cde\efg\importer.py", line 101, in load
self.__module = importlib.import_module(self.__name)
File "C:\Programming\Python36-32\lib\importlib\__init__.py", line 126, in import_module
return _bootstrap._gcd_import(name[level:], package, level)
File "<frozen importlib._bootstrap>", line 978, in _gcd_import
File "<frozen importlib._bootstrap>", line 961, in _find_and_load
File "<frozen importlib._bootstrap>", line 950, in _find_and_load_unlocked
File "<frozen importlib._bootstrap>", line 655, in _load_unlocked
File "<frozen importlib._bootstrap_external>", line 678, in exec_module
File "<frozen importlib._bootstrap>", line 205, in _call_with_frames_removed
File "C:\abc\cde\efg\modules\testerror.py", line 3, in <module>
x = 1 / 0 # intentional error for module testing purposes
ZeroDivisionError: division by zero
Obviously most of the lines are of no interest to the user. I want the traceback to look like this:
Traceback (most recent call last):
File "C:\abc\cde\efg\modules\testerror.py", line 3, in <module>
x = 1 / 0 # intentional error for module testing purposes
ZeroDivisionError: division by zero
But the problem is that I cannot rely on the number of rows generated from the importlib
package (this is an implementation detail which may be changed in future versions, may be platform or Python installation specific), which I want to skip, neither on the number of rows generated by the custom module error (he can import some other modules on his own, which may cause the error), which I want to keep. To complicate it further, the user can also call importlib
module function, which is fine in that case and should be included in the traceback.
In other words, I need to get rid of all the first block of importlib
error lines from the traceback. Any ideas other than parsing the traceback lines in some 'clever' way, which I think would be a hackish and very fragile solution relying on too many implementation details from the importlib
package?
Note: I am using Python 3.5+, the solution should be platform agnostic
Upvotes: 2
Views: 1600
Reputation: 8049
OK, I dug deeper into the traceback
and importlib
modules and I think I have found the solution. This seems to work fine for modules in normal Python files. Not sure however about frozen packages, zipped packages etc. Needs more testing for these scenarios.
import importlib
import sys
import traceback
moduleName = "somepackage.somemodule"
try:
importlib.import_module(moduleName)
except Exception:
spec = importlib.util.find_spec(moduleToImport)
if spec is None:
# if the module is not found, then do not print traceback at all
count = 0
else:
fileName = spec.loader.get_filename()
extracts = traceback.extract_tb(sys.exc_info()[2])
count = len(extracts)
# find the first occurrence of the module file name
for i, extract in enumerate(extracts):
if extract[0] == fileName:
break
count -= 1
# keep only the count of last lines
print(traceback.format_exc(limit=-count))
Upvotes: 4