GeezerVisonThe World Wide Web, restored to 800 pixels of glory. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Home|About|Gaming cabinet|Archives|How it works|Starter sites | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
GeezerVison is showing github.com — bpo-33330: PyImport_Cleanup check for exc leaks by vstinner · Pull Request #7068 · python/cpython Conversation
|
|
The commit d393c1b replaced interp->modules with PyImport_GetModuleDict(), but this function fails with a fatal error if modules is NULL. I'm not sure that it was a deliberate change. Moreover, this commit has been partially reverted (interp->modules is back!). |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
PyImport_Cleanup() is part of the public API, but it's documented as "Empty the module table. For internal use only." :-( Why do we have public API for internal use only? Likely a mistake from the past. |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
| void | ||
| PyImport_Cleanup(void) | ||
| { | ||
| assert(!PyErr_Occurred()); |
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
I'm wondering if it is worth to call PyErr_WriteUnraisable() here. This will expose some information about the exception raised outside of this function.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
The contract of this function is that the caller must not hold an exception. If the assertion fails, I would prefer to fix the caller rather than working around the bug using PyErr_WriteUnraisable().
The point is not only to catch bugs in the current code, but also ease debug when the callers code will be modified. Python shutdown process is very fragile and error prone.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
| during interpreter shutdown (and then never finish it). | ||
| See http://bugs.python.org/issue8713#msg195178 for an example. | ||
| */ | ||
| assert(!PyErr_Occurred()); |
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
Yesterday I wrote a patch that improves errors handling in the garbage collector. bpo-33612 is the result of testing it. And I have added similar asserts in _PyGC_CollectNoFail() (but the patch is not completed yet, maybe I'll move them).
At least move the second assert inside the "else" block.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
| if (PyDict_CheckExact(modules)) { | ||
| pos = 0; | ||
| while (PyDict_Next(modules, &pos, &key, &value)) { | ||
| assert(!PyErr_Occurred()); |
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
Neither PyDict_Next() nor any other calls before can raise an exception.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
This is the theory. In practice, something called before might leak an exception. It's to catch bugs "just in case". The assertion is cheap. Debugging an "exception leak" can be very painful.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
There is no code between this line and previous error check that can raise an exception and don't silence it. I have checked this.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
| PyErr_WriteUnraisable(NULL); | ||
| continue; | ||
| } | ||
| assert(!PyErr_Occurred()); |
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
PyObject_GetIter(), PyIter_Next() and PyObject_GetItem() can raise and exception if corresponding slots are implemented incorrectly in extensions. assert looks good here.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
| Py_DECREF(iterator); | ||
| } | ||
| } | ||
| assert(!PyErr_Occurred()); |
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
This never fails.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
| PyErr_Clear(); | ||
| } | ||
| Py_XDECREF(dict); | ||
| assert(!PyErr_Occurred()); |
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
This never fails.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
| references left to it), we need to delete the "builtins" | ||
| module last. Likewise, we don't delete sys until the very | ||
| end because it is implicitly referenced (e.g. by print). */ | ||
| assert(!PyErr_Occurred()); |
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
This never fails.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
| Py_INCREF(mod); | ||
| if (Py_VerboseFlag && PyUnicode_Check(name)) | ||
| PySys_FormatStderr("# cleanup[3] wiping %U\n", name); | ||
| assert(!PyErr_Occurred()); |
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
This never fails.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
I would left only few asserts after calls that can raise exceptions if the third-party code has bugs. And one at the end of |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
The last 2 years, I added many "assert(!PyErr_Occurred());" in ceval.c and object.c, and it helped me a lot to detect some very old bugs. The rationale here is the same. |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
| const char * const *p; | ||
|
|
||
| if (modules == NULL) | ||
| PyObject *modules = interp->modules; |
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
I'd rather avoid this, if possible. Under what conditions does the use of PyImport_GetModuleDict() cause a fatal error?
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
If you call PyImport_Cleanup() twice. In Python 3.6, calling PyImport_Cleanup() a second time does nothing: that's the purpose of the test on the next line.
PyImport_Cleanup() is part of the public API, even if it's documented as "please don't call this function" ;-) I would prefer to restore Python 3.6 behaviour.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
Ah, thanks for clarifying. LGTM then. I still plan on landing #3606 at some point, but at that point I'll have to deal with recent interp->modules usage anyway. :)
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
See also #7126. |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
LGTM, except that GC is covered by a separate PR.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
I will wait until #7126 is merged, and then rebase this PR on top of it. |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
Why not rebase and merge this PR? |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
It's just that I wrote it 2 months ago, and I lost interest in this minor enhancement in the meanwhile. I'm trying to reduce my number of open PRs. Feel free to reuse my patch to create a new PR if you want. |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
Successfully merging this pull request may close these issues.
There was an error while loading. Please reload this page.