Uh oh!
There was an error while loading. Please reload this page.
bpo-33237: Improve AttributeError message for partially initialized module. - #6398
Conversation
3755151 to
770f2f5Compare
brettcannon
left a comment
There was a problem hiding this comment.
Very minor thing to fix, otherwise LGTM!
| initializing = PyObject_IsTrue(value); | ||
| Py_DECREF(value); | ||
| if (initializing < 0) | ||
| PyErr_Clear(); |
There was a problem hiding this comment.
Curly braces around the statement.
There was a problem hiding this comment.
What if test value == Py_True instead of PyObject_IsTrue(value)? This would simplify the code, and I don't think that we should expect any true value except True.
There was a problem hiding this comment.
I'm torn. I do agree the code is simpler and you shouldn't expect it, but I also don't know if it's worth restricting the expectations that tightly. Then again, who is going to set this other than import itself?
IOW I don't have an opinion so whatever you prefer. 😄
There was a problem hiding this comment.
And please note, that this PR changes the behavior of PyImport_ImportModuleLevelObject(). I have extracted the common code into _PyModule_IsInitializing(), but the code in PyImport_ImportModuleLevelObject() is not exactly the same as in module_getattro(). It used _PyObject_GetAttrId(mod, &PyId___spec__), but now it uses _PyDict_GetItemId(((PyModuleObject *)m)->md_dict, &PyId___spec__), and supports only the module type instances. I.e. __getattr__() will be now bypassed when looking up __spec__.
There was a problem hiding this comment.
That is a breaking changing due to the fact that people could return any object for their module thanks to importlib.abc.Loader.create_module().
vstinner
commented
Oct 20, 2018
How can I trigger the modified code? The following circular import error doesn't seem to be covered by this change: I get the same traceback and exception with this change. |
serhiy-storchaka
commented
Oct 20, 2018
See an example on the tracker. |
serhiy-storchaka
commented
Oct 21, 2018
Restored support of non-module subclasses and added tests. @brettcannon, please make review again. |
vstinner
left a comment
There was a problem hiding this comment.
LGTM. I really like the much better error message!
I just a few minor remarks on the implementation and the NEWS entry.
| } | ||
| } | ||
| } | ||
| PyErr_Clear(); |
There was a problem hiding this comment.
Why do you always clear the current exception? What if the function is called with an exception set? Maybe start with a "assert(!PyErr_Occurred());".
I prefer the old code which only clears the exception on error.
There was a problem hiding this comment.
It is not wrong to call this function with NULL and an exception set. Checking this here saves several lines of code and/or indentation level at caller places.
The exception is cleared on error in most cases. The only exception from this rule is for _PyDict_GetItemId() returned NULL.
| int | ||
| _PyModuleSpec_IsInitializing(PyObject *spec) | ||
| { | ||
| if (spec != NULL && spec != Py_None) { |
There was a problem hiding this comment.
Maybe just return 0 (without calling PyErr_Clear) if this condition is false? It would avoid one identation level :-)
There was a problem hiding this comment.
We need to call PyErr_Clear() in all cases except spec == Py_None. The latter condition was added for mistake (left from old versions). I'll remove it.
Uh oh!
There was an error while loading. Please reload this page.
vstinner
commented
Oct 22, 2018
Ah thanks, I succeded to reproduce the error message and I like it :-) |
https://bugs.python.org/issue33237