-
-
Notifications
You must be signed in to change notification settings - Fork 30.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
bpo-33237: Improve AttributeError message for partially initialized module. #6398
bpo-33237: Improve AttributeError message for partially initialized module. #6398
Conversation
3755151
to
770f2f5
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Very minor thing to fix, otherwise LGTM!
Objects/moduleobject.c
Outdated
initializing = PyObject_IsTrue(value); | ||
Py_DECREF(value); | ||
if (initializing < 0) | ||
PyErr_Clear(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Curly braces around the statement.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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()
.
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. |
See an example on the tracker. |
Restored support of non-module subclasses and added tests. @brettcannon, please make review again. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, it makes sense.
Objects/moduleobject.c
Outdated
int | ||
_PyModuleSpec_IsInitializing(PyObject *spec) | ||
{ | ||
if (spec != NULL && spec != Py_None) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Misc/NEWS.d/next/Core and Builtins/2018-07-24-12-54-57.bpo-33237.O95mps.rst
Show resolved
Hide resolved
Ah thanks, I succeded to reproduce the error message and I like it :-) |
https://bugs.python.org/issue33237