diff --git a/Doc/c-api/unicode.rst b/Doc/c-api/unicode.rst index 78eec14e3a2..7320d035bab 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 0fe2dafbfd6..72b3a4c951e 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 a64c75c415c..a69f817c515 100644 --- a/Lib/test/test_capi/test_unicode.py +++ b/Lib/test/test_capi/test_unicode.py @@ -650,6 +650,40 @@ class CAPITest(unittest.TestCase): 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 00000000000..edef2777717 --- /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 59b350f0a60..5f15071d7d5 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);