Skip to content

Commit 6c09d87

Browse files
committed
gh-111696: Add %T format to PyUnicode_FromFormat()
1 parent b9f814c commit 6c09d87

File tree

5 files changed

+63
-0
lines changed

5 files changed

+63
-0
lines changed

Doc/c-api/unicode.rst

+8
Original file line numberDiff line numberDiff line change
@@ -518,6 +518,11 @@ APIs:
518518
- :c:expr:`PyObject*`
519519
- The result of calling :c:func:`PyObject_Repr`.
520520
521+
* - ``T``
522+
- :c:expr:`PyObject*`
523+
- Get the name of a type (``type.__name__``): the result of calling
524+
``PyType_GetName(type)``.
525+
521526
.. note::
522527
The width formatter unit is number of characters rather than bytes.
523528
The precision formatter unit is number of bytes or :c:type:`wchar_t`
@@ -553,6 +558,9 @@ APIs:
553558
In previous versions it caused all the rest of the format string to be
554559
copied as-is to the result string, and any extra arguments discarded.
555560
561+
.. versionchanged:: 3.13
562+
Support for ``"%T"`` added.
563+
556564
557565
.. c:function:: PyObject* PyUnicode_FromFormatV(const char *format, va_list vargs)
558566

Doc/whatsnew/3.13.rst

+4
Original file line numberDiff line numberDiff line change
@@ -1164,6 +1164,10 @@ New Features
11641164
:c:func:`PyErr_WriteUnraisable`, but allow to customize the warning mesage.
11651165
(Contributed by Serhiy Storchaka in :gh:`108082`.)
11661166

1167+
* Add support for ``"%T"`` format to :c:func:`PyUnicode_FromFormat`: format
1168+
the name of a type (``type.__name__``).
1169+
(Contributed by Victor Stinner in :gh:`111696`.)
1170+
11671171

11681172
Porting to Python 3.13
11691173
----------------------

Lib/test/test_capi/test_unicode.py

+17
Original file line numberDiff line numberDiff line change
@@ -609,6 +609,23 @@ def check_format(expected, format, *args):
609609
check_format('xyz',
610610
b'%V', None, b'xyz')
611611

612+
# test %T and %#T
613+
obj = 'abc'
614+
obj_type = type(obj)
615+
check_format('type: str',
616+
b'type: %T', py_object(obj_type))
617+
class LocalType:
618+
pass
619+
obj = LocalType()
620+
obj_type = type(obj)
621+
name = 'LocalType'
622+
check_format(f'type: {name}',
623+
b'type: %T', py_object(obj_type))
624+
check_format(f'type: {name[:3]}',
625+
b'type: %.3T', py_object(obj_type))
626+
check_format(f'type: {name.rjust(20)}',
627+
b'type: %20T', py_object(obj_type))
628+
612629
# test %ls
613630
check_format('abc', b'%ls', c_wchar_p('abc'))
614631
check_format('\u4eba\u6c11', b'%ls', c_wchar_p('\u4eba\u6c11'))
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Add support for ``"%T"`` format to :c:func:`PyUnicode_FromFormat`: format the
2+
name of a type (``type.__name__``). Patch by Victor Stinner.

Objects/unicodeobject.c

+32
Original file line numberDiff line numberDiff line change
@@ -2464,6 +2464,7 @@ unicode_fromformat_arg(_PyUnicodeWriter *writer,
24642464
switch (*f++) {
24652465
case '-': flags |= F_LJUST; continue;
24662466
case '0': flags |= F_ZERO; continue;
2467+
case '#': flags |= F_ALT; continue;
24672468
}
24682469
f--;
24692470
break;
@@ -2787,6 +2788,37 @@ unicode_fromformat_arg(_PyUnicodeWriter *writer,
27872788
break;
27882789
}
27892790

2791+
case 'T':
2792+
{
2793+
PyObject *type_raw = va_arg(*vargs, PyObject *);
2794+
assert(type_raw != NULL);
2795+
2796+
if (!PyType_Check(type_raw)) {
2797+
PyErr_SetString(PyExc_TypeError,
2798+
"%T argument must be a type; did you forget Py_TYPE()?");
2799+
return NULL;
2800+
}
2801+
PyTypeObject *type = (PyTypeObject*)type_raw;
2802+
2803+
PyObject *type_name;
2804+
if (flags & F_ALT) {
2805+
type_name = _PyType_GetFullyQualName(type, 0);
2806+
}
2807+
else {
2808+
type_name = PyType_GetName(type);
2809+
}
2810+
if (!type_name) {
2811+
return NULL;
2812+
}
2813+
if (unicode_fromformat_write_str(writer, type_name,
2814+
width, precision, flags) == -1) {
2815+
Py_DECREF(type_name);
2816+
return NULL;
2817+
}
2818+
Py_DECREF(type_name);
2819+
break;
2820+
}
2821+
27902822
default:
27912823
invalid_format:
27922824
PyErr_Format(PyExc_SystemError, "invalid format string: %s", p);

0 commit comments

Comments
 (0)