|
9 | 9 | #include "pycore_interp.h" // struct _import_runtime_state |
10 | 10 | #include "pycore_long.h" // _PyLong_GetZero |
11 | 11 | #include "pycore_lazyimportobject.h" |
| 12 | +#include "pycore_traceback.h" |
| 13 | +#include "pycore_interpframe.h" |
12 | 14 | #include "pycore_magic_number.h" // PYC_MAGIC_NUMBER_TOKEN |
13 | 15 | #include "pycore_moduleobject.h" // _PyModule_GetDef() |
14 | 16 | #include "pycore_namespace.h" // _PyNamespace_Type |
@@ -3780,6 +3782,75 @@ _PyImport_LoadLazyImportTstate(PyThreadState *tstate, PyObject *lazy_import) |
3780 | 3782 | Py_XDECREF(obj); |
3781 | 3783 | obj = NULL; |
3782 | 3784 |
|
| 3785 | + /* If an error occurred and we have frame information, add it to the exception */ |
| 3786 | + if (PyErr_Occurred() && lz->lz_code != NULL && lz->lz_instr_offset >= 0) { |
| 3787 | + /* Get the current exception - this already has the full traceback from the access point */ |
| 3788 | + PyObject *exc = _PyErr_GetRaisedException(tstate); |
| 3789 | + |
| 3790 | + /* Get import name - this can fail and set an exception */ |
| 3791 | + PyObject *import_name = _PyLazyImport_GetName(lazy_import); |
| 3792 | + if (!import_name) { |
| 3793 | + /* Failed to get import name, just restore original exception */ |
| 3794 | + _PyErr_SetRaisedException(tstate, exc); |
| 3795 | + goto ok; |
| 3796 | + } |
| 3797 | + |
| 3798 | + /* Resolve line number from instruction offset on demand */ |
| 3799 | + int lineno = PyCode_Addr2Line((PyCodeObject *)lz->lz_code, lz->lz_instr_offset); |
| 3800 | + |
| 3801 | + /* Get strings - these can return NULL on encoding errors */ |
| 3802 | + const char *filename_str = PyUnicode_AsUTF8(lz->lz_code->co_filename); |
| 3803 | + if (!filename_str) { |
| 3804 | + /* Unicode conversion failed - clear error and restore original exception */ |
| 3805 | + PyErr_Clear(); |
| 3806 | + Py_DECREF(import_name); |
| 3807 | + _PyErr_SetRaisedException(tstate, exc); |
| 3808 | + goto ok; |
| 3809 | + } |
| 3810 | + |
| 3811 | + const char *funcname_str = PyUnicode_AsUTF8(lz->lz_code->co_name); |
| 3812 | + if (!funcname_str) { |
| 3813 | + /* Unicode conversion failed - clear error and restore original exception */ |
| 3814 | + PyErr_Clear(); |
| 3815 | + Py_DECREF(import_name); |
| 3816 | + _PyErr_SetRaisedException(tstate, exc); |
| 3817 | + goto ok; |
| 3818 | + } |
| 3819 | + |
| 3820 | + /* Create a cause exception showing where the lazy import was declared */ |
| 3821 | + PyObject *msg = PyUnicode_FromFormat( |
| 3822 | + "deferred import of '%U' raised an exception during resolution", |
| 3823 | + import_name |
| 3824 | + ); |
| 3825 | + Py_DECREF(import_name); /* Done with import_name regardless of what happens next */ |
| 3826 | + |
| 3827 | + if (!msg) { |
| 3828 | + /* Failed to create message - restore original exception */ |
| 3829 | + _PyErr_SetRaisedException(tstate, exc); |
| 3830 | + goto ok; |
| 3831 | + } |
| 3832 | + |
| 3833 | + PyObject *cause_exc = PyObject_CallOneArg(PyExc_ImportError, msg); |
| 3834 | + Py_DECREF(msg); /* Done with msg */ |
| 3835 | + |
| 3836 | + if (!cause_exc) { |
| 3837 | + /* Failed to create exception - restore original */ |
| 3838 | + _PyErr_SetRaisedException(tstate, exc); |
| 3839 | + goto ok; |
| 3840 | + } |
| 3841 | + |
| 3842 | + /* Add traceback entry for the lazy import declaration */ |
| 3843 | + _PyErr_SetRaisedException(tstate, cause_exc); |
| 3844 | + _PyTraceback_Add(funcname_str, filename_str, lineno); |
| 3845 | + PyObject *cause_with_tb = _PyErr_GetRaisedException(tstate); |
| 3846 | + |
| 3847 | + /* Set the cause on the original exception */ |
| 3848 | + PyException_SetCause(exc, cause_with_tb); /* Steals ref to cause_with_tb */ |
| 3849 | + |
| 3850 | + /* Restore the original exception with its full traceback */ |
| 3851 | + _PyErr_SetRaisedException(tstate, exc); |
| 3852 | + } |
| 3853 | + |
3783 | 3854 | ok: |
3784 | 3855 | Py_XDECREF(fromlist); |
3785 | 3856 | return obj; |
|
0 commit comments