Skip to content

Commit cd16dd8

Browse files
vstinnerdiegorusso
authored andcommitted
pythongh-111696, PEP 737: Add %T and %N to PyUnicode_FromFormat() (python#116839)
1 parent 68fc9be commit cd16dd8

File tree

7 files changed

+135
-2
lines changed

7 files changed

+135
-2
lines changed

Doc/c-api/unicode.rst

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -518,6 +518,26 @@ APIs:
518518
- :c:expr:`PyObject*`
519519
- The result of calling :c:func:`PyObject_Repr`.
520520
521+
* - ``T``
522+
- :c:expr:`PyObject*`
523+
- Get the fully qualified name of an object type;
524+
call :c:func:`PyType_GetFullyQualifiedName`.
525+
526+
* - ``T#``
527+
- :c:expr:`PyObject*`
528+
- Similar to ``T`` format, but use a colon (``:``) as separator between
529+
the module name and the qualified name.
530+
531+
* - ``N``
532+
- :c:expr:`PyTypeObject*`
533+
- Get the fully qualified name of a type;
534+
call :c:func:`PyType_GetFullyQualifiedName`.
535+
536+
* - ``N#``
537+
- :c:expr:`PyTypeObject*`
538+
- Similar to ``N`` format, but use a colon (``:``) as separator between
539+
the module name and the qualified name.
540+
521541
.. note::
522542
The width formatter unit is number of characters rather than bytes.
523543
The precision formatter unit is number of bytes or :c:type:`wchar_t`
@@ -553,6 +573,9 @@ APIs:
553573
In previous versions it caused all the rest of the format string to be
554574
copied as-is to the result string, and any extra arguments discarded.
555575
576+
.. versionchanged:: 3.13
577+
Support for ``%T``, ``%T#``, ``%N`` and ``%N#`` formats added.
578+
556579
557580
.. c:function:: PyObject* PyUnicode_FromFormatV(const char *format, va_list vargs)
558581

Doc/whatsnew/3.13.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1668,6 +1668,12 @@ New Features
16681668
Equivalent to getting the ``type.__module__`` attribute.
16691669
(Contributed by Eric Snow and Victor Stinner in :gh:`111696`.)
16701670

1671+
* Add support for ``%T``, ``%T#``, ``%N`` and ``%N#`` formats to
1672+
:c:func:`PyUnicode_FromFormat`: format the fully qualified name of an object
1673+
type and of a type: call :c:func:`PyType_GetModuleName`. See :pep:`737` for
1674+
more information.
1675+
(Contributed by Victor Stinner in :gh:`111696`.)
1676+
16711677

16721678
Porting to Python 3.13
16731679
----------------------

Include/internal/pycore_typeobject.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,8 @@ extern PyTypeObject _PyBufferWrapper_Type;
150150
PyAPI_FUNC(PyObject*) _PySuper_Lookup(PyTypeObject *su_type, PyObject *su_obj,
151151
PyObject *name, int *meth_found);
152152

153+
extern PyObject* _PyType_GetFullyQualifiedName(PyTypeObject *type, char sep);
154+
153155

154156
#ifdef __cplusplus
155157
}

Lib/test/test_capi/test_unicode.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -609,6 +609,40 @@ def check_format(expected, format, *args):
609609
check_format('xyz',
610610
b'%V', None, b'xyz')
611611

612+
# test %T
613+
check_format('type: str',
614+
b'type: %T', py_object("abc"))
615+
check_format(f'type: st',
616+
b'type: %.2T', py_object("abc"))
617+
check_format(f'type: str',
618+
b'type: %10T', py_object("abc"))
619+
620+
class LocalType:
621+
pass
622+
obj = LocalType()
623+
fullname = f'{__name__}.{LocalType.__qualname__}'
624+
check_format(f'type: {fullname}',
625+
b'type: %T', py_object(obj))
626+
fullname_alt = f'{__name__}:{LocalType.__qualname__}'
627+
check_format(f'type: {fullname_alt}',
628+
b'type: %T#', py_object(obj))
629+
630+
# test %N
631+
check_format('type: str',
632+
b'type: %N', py_object(str))
633+
check_format(f'type: st',
634+
b'type: %.2N', py_object(str))
635+
check_format(f'type: str',
636+
b'type: %10N', py_object(str))
637+
638+
check_format(f'type: {fullname}',
639+
b'type: %N', py_object(type(obj)))
640+
check_format(f'type: {fullname_alt}',
641+
b'type: %N#', py_object(type(obj)))
642+
with self.assertRaisesRegex(TypeError, "%N argument must be a type"):
643+
check_format('type: str',
644+
b'type: %N', py_object("abc"))
645+
612646
# test %ls
613647
check_format('abc', b'%ls', c_wchar_p('abc'))
614648
check_format('\u4eba\u6c11', b'%ls', c_wchar_p('\u4eba\u6c11'))
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Add support for ``%T``, ``%T#``, ``%N`` and ``%N#`` formats to
2+
:c:func:`PyUnicode_FromFormat`: format the fully qualified name of an object
3+
type and of a type: call :c:func:`PyType_GetModuleName`. See :pep:`737` for
4+
more information. Patch by Victor Stinner.

Objects/typeobject.c

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1208,7 +1208,7 @@ type_set_module(PyTypeObject *type, PyObject *value, void *context)
12081208

12091209

12101210
PyObject *
1211-
PyType_GetFullyQualifiedName(PyTypeObject *type)
1211+
_PyType_GetFullyQualifiedName(PyTypeObject *type, char sep)
12121212
{
12131213
if (!(type->tp_flags & Py_TPFLAGS_HEAPTYPE)) {
12141214
return PyUnicode_FromString(type->tp_name);
@@ -1230,7 +1230,7 @@ PyType_GetFullyQualifiedName(PyTypeObject *type)
12301230
&& !_PyUnicode_Equal(module, &_Py_ID(builtins))
12311231
&& !_PyUnicode_Equal(module, &_Py_ID(__main__)))
12321232
{
1233-
result = PyUnicode_FromFormat("%U.%U", module, qualname);
1233+
result = PyUnicode_FromFormat("%U%c%U", module, sep, qualname);
12341234
}
12351235
else {
12361236
result = Py_NewRef(qualname);
@@ -1240,6 +1240,12 @@ PyType_GetFullyQualifiedName(PyTypeObject *type)
12401240
return result;
12411241
}
12421242

1243+
PyObject *
1244+
PyType_GetFullyQualifiedName(PyTypeObject *type)
1245+
{
1246+
return _PyType_GetFullyQualifiedName(type, '.');
1247+
}
1248+
12431249

12441250
static PyObject *
12451251
type_abstractmethods(PyTypeObject *type, void *context)

Objects/unicodeobject.c

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2791,6 +2791,64 @@ unicode_fromformat_arg(_PyUnicodeWriter *writer,
27912791
break;
27922792
}
27932793

2794+
case 'T':
2795+
{
2796+
PyObject *obj = va_arg(*vargs, PyObject *);
2797+
PyTypeObject *type = (PyTypeObject *)Py_NewRef(Py_TYPE(obj));
2798+
2799+
PyObject *type_name;
2800+
if (f[1] == '#') {
2801+
type_name = _PyType_GetFullyQualifiedName(type, ':');
2802+
f++;
2803+
}
2804+
else {
2805+
type_name = PyType_GetFullyQualifiedName(type);
2806+
}
2807+
Py_DECREF(type);
2808+
if (!type_name) {
2809+
return NULL;
2810+
}
2811+
2812+
if (unicode_fromformat_write_str(writer, type_name,
2813+
width, precision, flags) == -1) {
2814+
Py_DECREF(type_name);
2815+
return NULL;
2816+
}
2817+
Py_DECREF(type_name);
2818+
break;
2819+
}
2820+
2821+
case 'N':
2822+
{
2823+
PyObject *type_raw = va_arg(*vargs, PyObject *);
2824+
assert(type_raw != NULL);
2825+
2826+
if (!PyType_Check(type_raw)) {
2827+
PyErr_SetString(PyExc_TypeError, "%N argument must be a type");
2828+
return NULL;
2829+
}
2830+
PyTypeObject *type = (PyTypeObject*)type_raw;
2831+
2832+
PyObject *type_name;
2833+
if (f[1] == '#') {
2834+
type_name = _PyType_GetFullyQualifiedName(type, ':');
2835+
f++;
2836+
}
2837+
else {
2838+
type_name = PyType_GetFullyQualifiedName(type);
2839+
}
2840+
if (!type_name) {
2841+
return NULL;
2842+
}
2843+
if (unicode_fromformat_write_str(writer, type_name,
2844+
width, precision, flags) == -1) {
2845+
Py_DECREF(type_name);
2846+
return NULL;
2847+
}
2848+
Py_DECREF(type_name);
2849+
break;
2850+
}
2851+
27942852
default:
27952853
invalid_format:
27962854
PyErr_Format(PyExc_SystemError, "invalid format string: %s", p);

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy