From 7bfeb8bd3c3d13ee5b8977ae787231b9eb62333f Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Mon, 8 Apr 2024 19:27:25 +0300 Subject: [PATCH] gh-117642: Fix PEP 737 implementation (GH-117643) * Fix implementation of %#T and %#N (they were implemented as %T# and %N#). * Restore tests removed in gh-116417. --- Doc/c-api/unicode.rst | 6 ++-- Doc/whatsnew/3.13.rst | 2 +- Lib/test/test_capi/test_unicode.py | 34 +++++++++++++++++++ ...-04-08-18-53-33.gh-issue-117642._-tYH_.rst | 1 + Objects/unicodeobject.c | 7 ++-- 5 files changed, 42 insertions(+), 8 deletions(-) create mode 100644 Misc/NEWS.d/next/C API/2024-04-08-18-53-33.gh-issue-117642._-tYH_.rst diff --git a/Doc/c-api/unicode.rst b/Doc/c-api/unicode.rst index 78eec14e3a24d65..7320d035bab513e 100644 --- a/Doc/c-api/unicode.rst +++ b/Doc/c-api/unicode.rst @@ -523,7 +523,7 @@ APIs: - Get the fully qualified name of an object type; call :c:func:`PyType_GetFullyQualifiedName`. - * - ``T#`` + * - ``#T`` - :c:expr:`PyObject*` - Similar to ``T`` format, but use a colon (``:``) as separator between the module name and the qualified name. @@ -533,7 +533,7 @@ APIs: - Get the fully qualified name of a type; call :c:func:`PyType_GetFullyQualifiedName`. - * - ``N#`` + * - ``#N`` - :c:expr:`PyTypeObject*` - Similar to ``N`` format, but use a colon (``:``) as separator between the module name and the qualified name. @@ -574,7 +574,7 @@ APIs: copied as-is to the result string, and any extra arguments discarded. .. versionchanged:: 3.13 - Support for ``%T``, ``%T#``, ``%N`` and ``%N#`` formats added. + Support for ``%T``, ``%#T``, ``%N`` and ``%#N`` formats added. .. c:function:: PyObject* PyUnicode_FromFormatV(const char *format, va_list vargs) diff --git a/Doc/whatsnew/3.13.rst b/Doc/whatsnew/3.13.rst index 0fe2dafbfd6f023..72b3a4c951eda6b 100644 --- a/Doc/whatsnew/3.13.rst +++ b/Doc/whatsnew/3.13.rst @@ -1775,7 +1775,7 @@ New Features Equivalent to getting the ``type.__module__`` attribute. (Contributed by Eric Snow and Victor Stinner in :gh:`111696`.) -* Add support for ``%T``, ``%T#``, ``%N`` and ``%N#`` formats to +* Add support for ``%T``, ``%#T``, ``%N`` and ``%#N`` formats to :c:func:`PyUnicode_FromFormat`: format the fully qualified name of an object type and of a type: call :c:func:`PyType_GetModuleName`. See :pep:`737` for more information. diff --git a/Lib/test/test_capi/test_unicode.py b/Lib/test/test_capi/test_unicode.py index a64c75c415c3fe5..a69f817c515ba7d 100644 --- a/Lib/test/test_capi/test_unicode.py +++ b/Lib/test/test_capi/test_unicode.py @@ -650,6 +650,40 @@ def check_format(expected, format, *args): check_format('\U0001f4bb+' if sizeof(c_wchar) > 2 else '\U0001f4bb', b'%.2lV', None, c_wchar_p('\U0001f4bb+\U0001f40d')) + # test %T + check_format('type: str', + b'type: %T', py_object("abc")) + check_format(f'type: st', + b'type: %.2T', py_object("abc")) + check_format(f'type: str', + b'type: %10T', py_object("abc")) + + class LocalType: + pass + obj = LocalType() + fullname = f'{__name__}.{LocalType.__qualname__}' + check_format(f'type: {fullname}', + b'type: %T', py_object(obj)) + fullname_alt = f'{__name__}:{LocalType.__qualname__}' + check_format(f'type: {fullname_alt}', + b'type: %#T', py_object(obj)) + + # test %N + check_format('type: str', + b'type: %N', py_object(str)) + check_format(f'type: st', + b'type: %.2N', py_object(str)) + check_format(f'type: str', + b'type: %10N', py_object(str)) + + check_format(f'type: {fullname}', + b'type: %N', py_object(type(obj))) + check_format(f'type: {fullname_alt}', + b'type: %#N', py_object(type(obj))) + with self.assertRaisesRegex(TypeError, "%N argument must be a type"): + check_format('type: str', + b'type: %N', py_object("abc")) + # test variable width and precision check_format(' abc', b'%*s', c_int(5), b'abc') check_format('ab', b'%.*s', c_int(2), b'abc') diff --git a/Misc/NEWS.d/next/C API/2024-04-08-18-53-33.gh-issue-117642._-tYH_.rst b/Misc/NEWS.d/next/C API/2024-04-08-18-53-33.gh-issue-117642._-tYH_.rst new file mode 100644 index 000000000000000..edef2777717014f --- /dev/null +++ b/Misc/NEWS.d/next/C API/2024-04-08-18-53-33.gh-issue-117642._-tYH_.rst @@ -0,0 +1 @@ +Fix :pep:`737` implementation for ``%#T`` and ``%#N``. diff --git a/Objects/unicodeobject.c b/Objects/unicodeobject.c index 59b350f0a609a65..5f15071d7d54efd 100644 --- a/Objects/unicodeobject.c +++ b/Objects/unicodeobject.c @@ -2468,6 +2468,7 @@ unicode_fromformat_arg(_PyUnicodeWriter *writer, switch (*f++) { case '-': flags |= F_LJUST; continue; case '0': flags |= F_ZERO; continue; + case '#': flags |= F_ALT; continue; } f--; break; @@ -2797,9 +2798,8 @@ unicode_fromformat_arg(_PyUnicodeWriter *writer, PyTypeObject *type = (PyTypeObject *)Py_NewRef(Py_TYPE(obj)); PyObject *type_name; - if (f[1] == '#') { + if (flags & F_ALT) { type_name = _PyType_GetFullyQualifiedName(type, ':'); - f++; } else { type_name = PyType_GetFullyQualifiedName(type); @@ -2830,9 +2830,8 @@ unicode_fromformat_arg(_PyUnicodeWriter *writer, PyTypeObject *type = (PyTypeObject*)type_raw; PyObject *type_name; - if (f[1] == '#') { + if (flags & F_ALT) { type_name = _PyType_GetFullyQualifiedName(type, ':'); - f++; } else { type_name = PyType_GetFullyQualifiedName(type);