mirror of https://github.com/python/cpython
gh-111696, PEP 737: Add %T and %N to PyUnicode_FromFormat() (#116839)
This commit is contained in:
parent
5f52d20a93
commit
7bbb9b57e6
|
@ -518,6 +518,26 @@ APIs:
|
||||||
- :c:expr:`PyObject*`
|
- :c:expr:`PyObject*`
|
||||||
- The result of calling :c:func:`PyObject_Repr`.
|
- The result of calling :c:func:`PyObject_Repr`.
|
||||||
|
|
||||||
|
* - ``T``
|
||||||
|
- :c:expr:`PyObject*`
|
||||||
|
- Get the fully qualified name of an object type;
|
||||||
|
call :c:func:`PyType_GetFullyQualifiedName`.
|
||||||
|
|
||||||
|
* - ``T#``
|
||||||
|
- :c:expr:`PyObject*`
|
||||||
|
- Similar to ``T`` format, but use a colon (``:``) as separator between
|
||||||
|
the module name and the qualified name.
|
||||||
|
|
||||||
|
* - ``N``
|
||||||
|
- :c:expr:`PyTypeObject*`
|
||||||
|
- Get the fully qualified name of a type;
|
||||||
|
call :c:func:`PyType_GetFullyQualifiedName`.
|
||||||
|
|
||||||
|
* - ``N#``
|
||||||
|
- :c:expr:`PyTypeObject*`
|
||||||
|
- Similar to ``N`` format, but use a colon (``:``) as separator between
|
||||||
|
the module name and the qualified name.
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
The width formatter unit is number of characters rather than bytes.
|
The width formatter unit is number of characters rather than bytes.
|
||||||
The precision formatter unit is number of bytes or :c:type:`wchar_t`
|
The precision formatter unit is number of bytes or :c:type:`wchar_t`
|
||||||
|
@ -553,6 +573,9 @@ APIs:
|
||||||
In previous versions it caused all the rest of the format string to be
|
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.
|
copied as-is to the result string, and any extra arguments discarded.
|
||||||
|
|
||||||
|
.. versionchanged:: 3.13
|
||||||
|
Support for ``%T``, ``%T#``, ``%N`` and ``%N#`` formats added.
|
||||||
|
|
||||||
|
|
||||||
.. c:function:: PyObject* PyUnicode_FromFormatV(const char *format, va_list vargs)
|
.. c:function:: PyObject* PyUnicode_FromFormatV(const char *format, va_list vargs)
|
||||||
|
|
||||||
|
|
|
@ -1668,6 +1668,12 @@ New Features
|
||||||
Equivalent to getting the ``type.__module__`` attribute.
|
Equivalent to getting the ``type.__module__`` attribute.
|
||||||
(Contributed by Eric Snow and Victor Stinner in :gh:`111696`.)
|
(Contributed by Eric Snow and Victor Stinner in :gh:`111696`.)
|
||||||
|
|
||||||
|
* 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.
|
||||||
|
(Contributed by Victor Stinner in :gh:`111696`.)
|
||||||
|
|
||||||
|
|
||||||
Porting to Python 3.13
|
Porting to Python 3.13
|
||||||
----------------------
|
----------------------
|
||||||
|
|
|
@ -150,6 +150,8 @@ extern PyTypeObject _PyBufferWrapper_Type;
|
||||||
PyAPI_FUNC(PyObject*) _PySuper_Lookup(PyTypeObject *su_type, PyObject *su_obj,
|
PyAPI_FUNC(PyObject*) _PySuper_Lookup(PyTypeObject *su_type, PyObject *su_obj,
|
||||||
PyObject *name, int *meth_found);
|
PyObject *name, int *meth_found);
|
||||||
|
|
||||||
|
extern PyObject* _PyType_GetFullyQualifiedName(PyTypeObject *type, char sep);
|
||||||
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
|
|
@ -609,6 +609,40 @@ class CAPITest(unittest.TestCase):
|
||||||
check_format('xyz',
|
check_format('xyz',
|
||||||
b'%V', None, b'xyz')
|
b'%V', None, b'xyz')
|
||||||
|
|
||||||
|
# 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 %ls
|
# test %ls
|
||||||
check_format('abc', b'%ls', c_wchar_p('abc'))
|
check_format('abc', b'%ls', c_wchar_p('abc'))
|
||||||
check_format('\u4eba\u6c11', b'%ls', c_wchar_p('\u4eba\u6c11'))
|
check_format('\u4eba\u6c11', b'%ls', c_wchar_p('\u4eba\u6c11'))
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
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. Patch by Victor Stinner.
|
|
@ -1208,7 +1208,7 @@ type_set_module(PyTypeObject *type, PyObject *value, void *context)
|
||||||
|
|
||||||
|
|
||||||
PyObject *
|
PyObject *
|
||||||
PyType_GetFullyQualifiedName(PyTypeObject *type)
|
_PyType_GetFullyQualifiedName(PyTypeObject *type, char sep)
|
||||||
{
|
{
|
||||||
if (!(type->tp_flags & Py_TPFLAGS_HEAPTYPE)) {
|
if (!(type->tp_flags & Py_TPFLAGS_HEAPTYPE)) {
|
||||||
return PyUnicode_FromString(type->tp_name);
|
return PyUnicode_FromString(type->tp_name);
|
||||||
|
@ -1230,7 +1230,7 @@ PyType_GetFullyQualifiedName(PyTypeObject *type)
|
||||||
&& !_PyUnicode_Equal(module, &_Py_ID(builtins))
|
&& !_PyUnicode_Equal(module, &_Py_ID(builtins))
|
||||||
&& !_PyUnicode_Equal(module, &_Py_ID(__main__)))
|
&& !_PyUnicode_Equal(module, &_Py_ID(__main__)))
|
||||||
{
|
{
|
||||||
result = PyUnicode_FromFormat("%U.%U", module, qualname);
|
result = PyUnicode_FromFormat("%U%c%U", module, sep, qualname);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
result = Py_NewRef(qualname);
|
result = Py_NewRef(qualname);
|
||||||
|
@ -1240,6 +1240,12 @@ PyType_GetFullyQualifiedName(PyTypeObject *type)
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PyObject *
|
||||||
|
PyType_GetFullyQualifiedName(PyTypeObject *type)
|
||||||
|
{
|
||||||
|
return _PyType_GetFullyQualifiedName(type, '.');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
type_abstractmethods(PyTypeObject *type, void *context)
|
type_abstractmethods(PyTypeObject *type, void *context)
|
||||||
|
|
|
@ -2791,6 +2791,64 @@ unicode_fromformat_arg(_PyUnicodeWriter *writer,
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case 'T':
|
||||||
|
{
|
||||||
|
PyObject *obj = va_arg(*vargs, PyObject *);
|
||||||
|
PyTypeObject *type = (PyTypeObject *)Py_NewRef(Py_TYPE(obj));
|
||||||
|
|
||||||
|
PyObject *type_name;
|
||||||
|
if (f[1] == '#') {
|
||||||
|
type_name = _PyType_GetFullyQualifiedName(type, ':');
|
||||||
|
f++;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
type_name = PyType_GetFullyQualifiedName(type);
|
||||||
|
}
|
||||||
|
Py_DECREF(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;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'N':
|
||||||
|
{
|
||||||
|
PyObject *type_raw = va_arg(*vargs, PyObject *);
|
||||||
|
assert(type_raw != NULL);
|
||||||
|
|
||||||
|
if (!PyType_Check(type_raw)) {
|
||||||
|
PyErr_SetString(PyExc_TypeError, "%N argument must be a type");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
PyTypeObject *type = (PyTypeObject*)type_raw;
|
||||||
|
|
||||||
|
PyObject *type_name;
|
||||||
|
if (f[1] == '#') {
|
||||||
|
type_name = _PyType_GetFullyQualifiedName(type, ':');
|
||||||
|
f++;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
type_name = PyType_GetFullyQualifiedName(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:
|
default:
|
||||||
invalid_format:
|
invalid_format:
|
||||||
PyErr_Format(PyExc_SystemError, "invalid format string: %s", p);
|
PyErr_Format(PyExc_SystemError, "invalid format string: %s", p);
|
||||||
|
|
Loading…
Reference in New Issue