Skip to content

Commit

Permalink
pythongh-111696: Add %T format to PyUnicode_FromFormat()
Browse files Browse the repository at this point in the history
  • Loading branch information
vstinner committed Nov 9, 2023
1 parent b9f814c commit 6c09d87
Show file tree
Hide file tree
Showing 5 changed files with 63 additions and 0 deletions.
8 changes: 8 additions & 0 deletions Doc/c-api/unicode.rst
Expand Up @@ -518,6 +518,11 @@ APIs:
- :c:expr:`PyObject*`
- The result of calling :c:func:`PyObject_Repr`.
* - ``T``
- :c:expr:`PyObject*`
- Get the name of a type (``type.__name__``): the result of calling
``PyType_GetName(type)``.
.. note::
The width formatter unit is number of characters rather than bytes.
The precision formatter unit is number of bytes or :c:type:`wchar_t`
Expand Down Expand Up @@ -553,6 +558,9 @@ APIs:
In previous versions it caused all the rest of the format string to be
copied as-is to the result string, and any extra arguments discarded.
.. versionchanged:: 3.13
Support for ``"%T"`` added.
.. c:function:: PyObject* PyUnicode_FromFormatV(const char *format, va_list vargs)
Expand Down
4 changes: 4 additions & 0 deletions Doc/whatsnew/3.13.rst
Expand Up @@ -1164,6 +1164,10 @@ New Features
:c:func:`PyErr_WriteUnraisable`, but allow to customize the warning mesage.
(Contributed by Serhiy Storchaka in :gh:`108082`.)

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


Porting to Python 3.13
----------------------
Expand Down
17 changes: 17 additions & 0 deletions Lib/test/test_capi/test_unicode.py
Expand Up @@ -609,6 +609,23 @@ def check_format(expected, format, *args):
check_format('xyz',
b'%V', None, b'xyz')

# test %T and %#T
obj = 'abc'
obj_type = type(obj)
check_format('type: str',
b'type: %T', py_object(obj_type))
class LocalType:
pass
obj = LocalType()
obj_type = type(obj)
name = 'LocalType'
check_format(f'type: {name}',
b'type: %T', py_object(obj_type))
check_format(f'type: {name[:3]}',
b'type: %.3T', py_object(obj_type))
check_format(f'type: {name.rjust(20)}',
b'type: %20T', py_object(obj_type))

# test %ls
check_format('abc', b'%ls', c_wchar_p('abc'))
check_format('\u4eba\u6c11', b'%ls', c_wchar_p('\u4eba\u6c11'))
Expand Down
@@ -0,0 +1,2 @@
Add support for ``"%T"`` format to :c:func:`PyUnicode_FromFormat`: format the
name of a type (``type.__name__``). Patch by Victor Stinner.
32 changes: 32 additions & 0 deletions Objects/unicodeobject.c
Expand Up @@ -2464,6 +2464,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;
Expand Down Expand Up @@ -2787,6 +2788,37 @@ unicode_fromformat_arg(_PyUnicodeWriter *writer,
break;
}

case 'T':
{
PyObject *type_raw = va_arg(*vargs, PyObject *);
assert(type_raw != NULL);

if (!PyType_Check(type_raw)) {
PyErr_SetString(PyExc_TypeError,
"%T argument must be a type; did you forget Py_TYPE()?");
return NULL;
}
PyTypeObject *type = (PyTypeObject*)type_raw;

PyObject *type_name;
if (flags & F_ALT) {
type_name = _PyType_GetFullyQualName(type, 0);
}
else {
type_name = PyType_GetName(type);
}
if (!type_name) {
return NULL;
}
if (unicode_fromformat_write_str(writer, type_name,
width, precision, flags) == -1) {
Py_DECREF(type_name);
return NULL;
}
Py_DECREF(type_name);
break;
}

default:
invalid_format:
PyErr_Format(PyExc_SystemError, "invalid format string: %s", p);
Expand Down

0 comments on commit 6c09d87

Please sign in to comment.