-
-
Notifications
You must be signed in to change notification settings - Fork 7.9k
Don't abort on FT2Font weakref. #9074
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
Conversation
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.
A minor comment, but this works for me.
src/file_compat.h
Outdated
@@ -126,6 +126,9 @@ static NPY_INLINE FILE *mpl_PyFile_Dup(PyObject *file, char *mode, mpl_off_t *or | |||
*/ | |||
static NPY_INLINE int mpl_PyFile_DupClose(PyObject *file, FILE *handle, mpl_off_t orig_pos) | |||
{ | |||
PyObject *type = NULL, *value = NULL, *tb = 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.
These should probably get some differentiating name, like an orig_
prefix or something.
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.
grepping through https://github.com/python/cpython/blob/f320be77ffb73e3b9e7fc98c37b8df3975d84b40/Modules/_io/fileio.c shows that cpython tends to stash the current exception (if any) in *exc, *val, *tb (but not without a prefix). We're not shadowing anything anyways...
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.
Sure, but those are generally around one other call, whereas this one is around almost an entire function body.
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.
added exc_ prefix.
@@ -200,14 +211,23 @@ static NPY_INLINE PyObject *mpl_PyFile_OpenFile(PyObject *filename, const char * | |||
|
|||
static NPY_INLINE int mpl_PyFile_CloseFile(PyObject *file) | |||
{ | |||
PyObject *type, *value, *tb; |
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 here too.
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.
as above
Currently, import weakref, matplotlib.ft2font weakref.ref(matplotlib.ft2font.FT2Font("/usr/share/fonts/TTF/DejaVuSans.ttf")) aborts Py3 with terminate called after throwing an instance of 'char const*' Fatal Python error: Aborted whereas on Py2 a normal `TypeError: cannot create weak reference to 'matplotlib.ft2font.FT2Font' object` is raised. This is because the following happens: - Py3 sets the TypeError for failure to create a weakref. - The FT2Font object gets GC'd as nothing is referring to it (yes, creating a weakref to a temporary is a bit silly). - The FT2Font destructor calls close_file_callback, which calls mpl_PyFile_DupClose, which calls PyObject_AsFileDescriptor... which, on Py3, calls the fileno() method on the file object. - PyObject_AsFileDescriptor fails because the originally set TypeError has not been cleared, so mpl_PyFile_DupClose likewise fails, causing close_file_callback to throw a C++ exception. Instead, carefully stash and restore the current exception state, both in mpl_PyFile_DupClose, and likewise below, in mpl_PyFile_CloseFile (the latter is needed because otherwise PyObject_CallMethod will clear the exception, cf comment in CPython's Objects/abstract.c: /* PyObject_Call() must not be called with an exception set, because it may clear it (directly or indirectly) and so the caller loses its exception */ Note that if an exception *actually* gets raised by mpl_PyFile_DupClose or mpl_PyFile_CloseFile, it will overwrite the TypeError. Strictly speaking, on Py3, it may be preferrable to chain the exceptions, but this seems a bit overkill (mostly, I just want to avoid aborting the whole Python process).
77bff5f
to
063dae8
Compare
Currently,
aborts Py3 with
whereas on Py2 a normal
TypeError: cannot create weak reference to 'matplotlib.ft2font.FT2Font' object
is raised.This is because the following happens:
creating a weakref to a temporary is a bit silly).
mpl_PyFile_DupClose, which calls PyObject_AsFileDescriptor... which,
on Py3, calls the fileno() method on the file object.
has not been cleared, so mpl_PyFile_DupClose likewise fails, causing
close_file_callback to throw a C++ exception.
Instead, carefully stash and restore the current exception state, both
in mpl_PyFile_DupClose, and likewise below, in mpl_PyFile_CloseFile (the
latter is needed because otherwise PyObject_CallMethod will clear the
exception, cf comment in CPython's Objects/abstract.c:
Note that if an exception actually gets raised by mpl_PyFile_DupClose
or mpl_PyFile_CloseFile, it will overwrite the TypeError. Strictly
speaking, on Py3, it may be preferrable to chain the exceptions, but
this seems a bit overkill (mostly, I just want to avoid aborting the
whole Python process).
PR Summary
PR Checklist