Skip to content
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

Use PyErr_FormatUnraisable() on Python 3.13 #34

Merged
merged 1 commit into from Nov 16, 2023

Conversation

vstinner
Copy link
Contributor

Use the new public PyErr_FormatUnraisable() on Python 3.13.

The private _PyErr_WriteUnraisableMsg() function was removed in Python 3.13:
python/cpython#111643

@@ -6109,6 +6108,18 @@ static void _my_PyErr_WriteUnraisable(PyObject *t, PyObject *v, PyObject *tb,
if (extra_error_line == NULL)
extra_error_line = "";

#if PY_VERSION_HEX >= 0x030D0000
if (obj != NULL) {
PyErr_FormatUnraisable("Exception ignored %c%s%R%s",
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe it's an opportunity to enhance the error message?

Should it be "Exception ignored ...", "Exception ignored in ..." (add in ) or "Exception ignored: ..." (add : )?

@vstinner
Copy link
Contributor Author

cc @serhiy-storchaka @arigo

@vstinner
Copy link
Contributor Author

By the way, _PyThreadState_UncheckedGet() should be replaced with the new public PyThreadState_GetUnchecked() function.

Copy link

@serhiy-storchaka serhiy-storchaka left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You forget to add PyErr_Restore().

first_char, objdescr + 1, extra_error_line);
}
#else
PyObject *s;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This does not compile on older Visual Studio versions, but I'm unsure how old these need to be. You can't declare a variable anywhere else than at the start of a block in older versions of C.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, fixed. I made the assumption that cffi is built by a C99 compiler these days.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm trying to keep the code compiling for 2.7, and that might require much older VS on Windows.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm trying to keep the code compiling for 2.7

@arigo I'm hoping to convince you that that's not a realistic goal in 2023 with the resources available to this project 😉 . It's clearly still possible to make it work, but keeping test/CI infrastructure alive that's capable of actually verifying that (before breaking changes get merged) is already prohibitively expensive, and getting more so by the day. For the past few years, it's not even possible to download the 2.7 Python build tooling from Microsoft anymore- I keep a private stash of some of those tools around because I still have to deal with it occasionally, but that's not really workable for modern GHA runners. My general approach is "if it's not being tested, it should be assumed broken", and I can't justify spending any time trying to test or fix that. ☹️

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm trying hard for provide a best effort support for Python 2.7 in my https://pythoncapi-compat.readthedocs.io/ project. I had C code to support VS2015 which doesn't support C99 static inline. But I removed it, it made the code harder to maintain. Moreover, GitHub Action doesn't provide Python 2.7. I'm now considering to officially remove Python 2 support and remove Python 2 code in pythoncapi-compat.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, I've tried "best effort" support like this so many times, but even with the best of intentions, it's effectively always broken when not backed by automated testing. It also ends up actively discouraging contributions (like this one!)...

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@nitzmahone Sure, I see the point, but in this case the final result is that the pull request was changed to 4 obvious lines instead of a more complex #if dance that missed originally a PyErr_Restore(), in addition to breaking a compiler (that may or may not be too old to consider). I think it's a win for a project like CFFI.

@vstinner
Copy link
Contributor Author

You forget to add PyErr_Restore().

Oops, fixed.

@arigo
Copy link
Contributor

arigo commented Nov 15, 2023

@serhiy-storchaka has a point. And I'd be happier with some change that avoids making two versions of the formatting code. As far as I'm concerned, killing a semi-private API is a minor annoyance that I'd be more happy to work around, instead of trying to rewrite more CFFI logic, because that ends up making duplication. So I wouldn't kill PyUnicode_FromFormat() just because we appear to be able to in some CPython version.

Use the new public PyErr_FormatUnraisable() on Python 3.13.

The private _PyErr_WriteUnraisableMsg() function was removed in
Python 3.13:
python/cpython#111643
@vstinner
Copy link
Contributor Author

@arigo:

@serhiy-storchaka has a point. And I'd be happier with some change that avoids making two versions of the formatting code. As far as I'm concerned, killing a semi-private API is a minor annoyance that I'd be more happy to work around, instead of trying to rewrite more CFFI logic, because that ends up making duplication. So I wouldn't kill PyUnicode_FromFormat() just because we appear to be able to in some CPython version.

Sure, it makes sense. I rewrote my PR to also use the s string on Python 3.13. The patch is now just 4 lines.

@arigo: Would you mind to review the updated PR?

@arigo arigo enabled auto-merge (squash) November 16, 2023 08:15
@arigo arigo disabled auto-merge November 16, 2023 08:15
@arigo
Copy link
Contributor

arigo commented Nov 16, 2023

Sure. Now I have no hesitation to merge the pull request. @nitzmahone, can you please confirm if I should "Enable auto-merge (squash)" for this kind of case?

@nitzmahone
Copy link
Member

can you please confirm if I should "Enable auto-merge (squash)" for this kind of case?

Yeah, that's fine- basically "I'm happy with the code, merge on my behalf if all checks pass" vs babysitting it...

@arigo arigo merged commit 49127c6 into python-cffi:main Nov 16, 2023
53 checks passed
@vstinner vstinner deleted the unraisable branch November 16, 2023 16:28
@vstinner
Copy link
Contributor Author

Thanks for the review and merging my change! I first wanted to enhance the error message, but I understand the desire to have the same message on all Python versions and it makes sense.

Ah yeah, I missed PyErr_Restore(), since I'm used to call PyErr_WriteUnraisable() while an exception in set, not on a function which gets an exception as an argument. Hopefully, it was catched by review and fixed ;-)

@nitzmahone
Copy link
Member

nitzmahone commented Nov 20, 2023

@vstinner @arigo Bleh- I threw together a manylinux_2_28 container with 3.13.0a1 in it and hacked up cibuildwheel to work with it (in hopes of wiring up a 3.13 prerelease canary build here), and it's failing under 3.13 with an unresolved symbol for PyErr_FormatUnraisable...

Shouldn't this have been PyErr_WriteUnraisable, not PyErr_FormatUnraisable? (EDIT: NM this question, was looking at 3.12 docs). Seems like the underlying problem is that the new function is not exported by libpython in 3.13.0a1...

I don't see any libpython exports for FormatUnraisable:

(tmp-ad2272c74e493a7) [mdavis@mdavis-p1 lib]$ objdump -t libpython3.13.so | grep 'PyErr_.*Unrais'
00000000002237e0 l     F .text  00000000000001b8              _PyErr_WriteUnraisableDefaultHook
00000000002239a0 g     F .text  00000000000007aa              _PyErr_WriteUnraisableMsg
0000000000224150 g     F .text  000000000000000a              PyErr_WriteUnraisable

@nitzmahone
Copy link
Member

(or maybe the new libpython export didn't make it into 3.13.0a1 yet)- in any case, this doesn't appear to be fully working yet...

@nitzmahone
Copy link
Member

OK, looks like this will be fixed in 3.13.0a2 (I just built against the top of main, which I assume will be a2 tomorrow-ish):

(tmp-2fcf66c976036a9) [mdavis@mdavis-p1 tmp-2fcf66c976036a9]$ objdump -t ~/.pyenv/versions/3.13-dev/lib/libpython3.13.so | grep PyErr_.*Unrai
0000000000245d00 l     F .text  00000000000001b8              _PyErr_WriteUnraisableDefaultHook
0000000000245ec0 g     F .text  0000000000000096              PyErr_FormatUnraisable
0000000000245f60 g     F .text  0000000000000009              PyErr_WriteUnraisable

@nitzmahone
Copy link
Member

... and yep, full test suite passes against that build of "almost-alpha2", so I think we're good to go here...
============ 1951 passed, 89 skipped, 4 xfailed, 10 warnings in 255.53s (0:04:15) ============

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants