PEP 737: Add type.__format__() method (#3589)

* Add type.__format__() method.
* Add more formats to PyUnicode_FromFormat().
* Add PyType_GetModuleName() function.
This commit is contained in:
Victor Stinner 2024-01-15 18:37:21 +01:00 committed by GitHub
parent d8e113004d
commit 8cbcebf505
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 131 additions and 98 deletions

View File

@ -14,10 +14,19 @@ Abstract
Add new convenient APIs to format type names the same way in Python and
in C. No longer format type names differently depending on how types are
implemented. No longer truncate type names in the standard library.
implemented.
Recommend using the type fully qualified name in error messages and in
``__repr__()`` methods in new code.
``__repr__()`` methods in new code. Recommend not truncating type names
in new code.
Add ``N`` and ``#N`` formats to ``type.__format__()`` to format a type
fully qualified name. For example, ``f"{type(obj):N}"`` formats the
fully qualified name of an object *obj*.
Add ``%T``, ``%#T``, ``%N`` and ``%#N`` formats to
``PyUnicode_FromFormat()`` to format the fully qualified, respectively,
of an object type and of a type.
Make C code safer by avoiding borrowed reference which can lead to
crashes. The new C API is compatible with the limited C API.
@ -172,16 +181,17 @@ Specification
=============
* Add ``type.__fully_qualified_name__`` attribute.
* Add ``%T``, ``%#T``, ``%N``, ``%#N`` formats to
``PyUnicode_FromFormat()``.
* Add ``type.__format__()`` method.
* Add formats to ``PyUnicode_FromFormat()``.
* Add ``PyType_GetModuleName()`` function.
* Add ``PyType_GetFullyQualifiedName()`` function.
* Recommend using the type fully qualified name in error messages and
in ``__repr__()`` methods in new code.
* Recommend not truncating type names.
* Recommend not truncating type names in new code.
Python API
----------
Add type.__fully_qualified_name__ attribute
-------------------------------------------
Add ``type.__fully_qualified_name__`` read-only attribute, the fully
qualified name of a type: similar to
@ -190,39 +200,48 @@ qualified name of a type: similar to
equal to ``"__main__"``.
The ``type.__repr__()`` is left unchanged, it only omits the module if
the module is equal to ``"builtins"``. It includes the module if the
module is equal to ``"__main__"``. Pseudo-code::
def type_repr(cls):
if isinstance(cls.__module__, str) and cls.__module__ != "builtins":
name = f"{cls.__module__}.{cls.__qualname__}"
else:
name = cls.__qualname__
return f"<class '{name}'>"
the module is equal to ``"builtins"``.
Add PyUnicode_FromFormat() formats
----------------------------------
Add type.__format__() method
----------------------------
Add formats to ``PyUnicode_FromFormat()``:
Add ``type.__format__()`` method with the following formats:
* ``%T`` formats the type fully qualified name of an **object**:
similar to ``type(obj).__fully_qualified_name__``.
* ``%#T`` formats the type short name of an **object**:
similar to ``type(obj).__name__``.
* ``%N`` formats the fully qualified name of a **type**:
similar to ``type.__fully_qualified_name__``.
* ``%#N`` formats the short name of an object of a **type**:
similar to ``type.__name__``.
* ``N`` formats the type **fully qualified name**
(``type.__fully_qualified_name__``);
``N`` stands for **N**\ ame.
* ``#N`` (alternative form) formats the type **fully qualified name**
using the **colon** (``:``) separator, instead of the dot separator
(``.``), between the module name and the qualified name.
The hash character (``#``) in the format string stands for
`alternative format
<https://docs.python.org/3/library/string.html#format-specification-mini-language>`_.
For example, ``f"{123:x}"`` returns ``'7b'`` and ``f"{123:#x}"`` returns
``'0x7b'`` (``#`` adds ``'0x'`` prefix).
Examples using f-string::
The ``%T`` format is used by ``time.strftime()``, but it's not used by
``printf()``.
>>> import datetime
>>> f"{datetime.timedelta:N}" # fully qualified name
'datetime.timedelta'
>>> f"{datetime.timedelta:#N}" # fully qualified name, colon separator
'datetime:timedelta'
The colon (``:``) separator used by the ``#N`` format eliminates
guesswork when you want to import the name, see
``pkgutil.resolve_name()``, ``python -m inspect`` command line
interface, and ``setuptools`` entry points.
Add formats to PyUnicode_FromFormat()
-------------------------------------
Add the following formats to ``PyUnicode_FromFormat()``:
* ``%N`` formats the **fully qualified name** of a **type**
(``type.__fully_qualified_name__``); **N** stands for type **N**\ ame.
* ``%T`` formats the type **fully qualified name** of an **object**
(``type(obj).__fully_qualified_name__``); **T** stands for object
**T**\ ype.
* ``%#N`` and ``%#T``: the alternative form uses the **colon** separator
(``:``), instead of the dot separator (``.``), between the module name
and the qualified name.
For example, the existing code using *tp_name*:
@ -247,8 +266,45 @@ Advantages of the updated code:
* The ``PyTypeObject.tp_name`` bytes string no longer has to be decoded
from UTF-8 at each ``PyErr_Format()`` call, since
``type.__fully_qualified_name__`` is already a Unicode string.
* The formatted type name no longer depends on the type implementation.
* The type name is no longer truncated.
Note: The ``%T`` format is used by ``time.strftime()``, but not by
``printf()``.
Formats Summary
---------------
.. list-table::
:header-rows: 1
* - C object
- C type
- Python
- Format
* - ``%T``
- ``%N``
- ``:N``
- Type **fully qualified** name.
* - ``%#T``
- ``%#N``
- ``:#N``
- Type **fully qualified** name, **colon** separator.
Add PyType_GetModuleName() function
-----------------------------------
Add the ``PyType_GetModuleName()`` function to get the module name of a
type (``type.__module__``). API:
.. code-block:: c
PyObject* PyType_GetModuleName(PyTypeObject *type)
On success, return a new reference to the string. On error, raise an
exception and return ``NULL``.
Add PyType_GetFullyQualifiedName() function
-------------------------------------------
@ -279,12 +335,9 @@ in an unambiguous way.
Recommend not truncating type names
-----------------------------------
Type names must not be truncated. For example, the ``%.100s`` format
should be avoided: use the ``%s`` format instead (or ``%T`` and ``%#T``
formats in C).
Code in the standard library is updated to no longer truncate type
names.
Type names should not be truncated in new code. For example, the
``%.100s`` format should be avoided: use the ``%s`` format instead (or
``%T`` format in C).
Implementation
@ -303,12 +356,9 @@ Adding new APIs has no effect on the backward compatibility. Existing
APIs are left unchanged.
Replacing the type short name with the type fully qualified name is only
recommended in new code. Existing code should be left
unchanged and so remains backward compatible.
In the standard library, type names are no longer truncated. We believe
that no code should be affected in practice, since type names longer
than 100 characters are rare.
recommended in new code. No longer truncating type names is only
recommended in new code. Existing code should be left unchanged and so
remains backward compatible.
Rejected Ideas
@ -330,38 +380,14 @@ See the `pull request: type(str) returns the fully qualified name
<https://github.com/python/cpython/pull/112129>`_.
Add formats to type.__format__()
--------------------------------
Examples of proposed formats for ``type.__format__()``:
* ``f"{type(obj):z}"`` formats ``type(obj).__name__``.
* ``f"{type(obj):M.T}"`` formats ``type(obj).__fully_qualified_name__``.
* ``f"{type(obj):M:T}"`` formats ``type(obj).__fully_qualified_name__``
using colon (``:``) separator.
* ``f"{type(obj):T}"`` formats ``type(obj).__name__``.
* ``f"{type(obj):#T}"`` formats ``type(obj).__fully_qualified_name__``.
Using short format (such as ``z``, a single letter) requires to refer to
format documentation to understand how a type name is formatted, whereas
``type(obj).__name__`` is explicit.
The dot character (``.``) is already used for the "precision" in format
strings. The colon character (``:``) is already used to separated the
expression from the format specification. For example, ``f"{3.14:g}"``
uses ``g`` format which comes after the colon (``:``). Usually, a format
type is a single letter, such as ``g`` in ``f"{3.14:g}"``, not ``M.T``
or ``M:T``. Reusing dot and colon characters for a different purpose can
be misleading and make the format parser more complicated.
Add !t formatter to get an object type
--------------------------------------
Use ``f"{obj!t:T}"`` to format ``type(obj).__name__``, similar to
``f"{type(obj).__name__}"``.
Use ``f"{obj!t:T}"`` to format ``type(obj).__fully_qualified_name__``,
similar to ``f"{type(obj):T}"``.
When the ``!t`` formatter was proposed in 2018, `Eric Smith was opposed
to this
When the ``!t`` formatter was proposed in 2018, `Eric Smith was stronly
opposed to this
<https://mail.python.org/archives/list/python-dev@python.org/message/BMIW3FEB77OS7OB3YYUUDUBITPWLRG3U/>`_;
Eric is the author of the f-string :pep:`498` "Literal String Interpolation".
@ -370,23 +396,12 @@ Add formats to str % args
-------------------------
It was proposed to add formats to format a type name in ``str % arg``.
For example, ``%T`` and ``%#T`` formats.
For example, add the ``%T`` format to format a type fully qualified
name.
Nowadays, f-strings are preferred for new code.
Use colon separator in fully qualified name
-------------------------------------------
The colon (``:``) separator eliminates guesswork when you want to import
the name, see ``pkgutil.resolve_name()``. A type fully qualified name
can be formatted as ``f"{type.__module__}:{type.__qualname__}"``, or
``type.__qualname__`` if the type module is ``"builtins"``.
In the standard library, no code formats a type fully qualified name
this way.
Other ways to format type names in C
------------------------------------
@ -421,8 +436,23 @@ between different modules and make the API more error prone.
About the ``%t`` format, ``printf()`` now uses ``t`` as a length
modifier for ``ptrdiff_t`` argument.
``type.__qualname__`` can be used in Python and ``PyType_GetQualName()``
can be used in C to format a type qualified name.
The following APIs to be used to format a type:
.. list-table::
:header-rows: 1
* - C API
- Python API
- Format
* - ``PyType_GetName()``
- ``type.__name__``
- Type **short** name.
* - ``PyType_GetQualName()``
- ``type.__qualname__``
- Type **qualified** name.
* - ``PyType_GetModuleName()``
- ``type.__module__``
- Type **module** name.
Use %T format with Py_TYPE(): pass a type
@ -471,13 +501,15 @@ Python does crash.
Other proposed APIs to get a type fully qualified name
------------------------------------------------------
* ``type.__fullyqualname__`` attribute name: attribute without an underscore
* Add ``type.__fullyqualname__`` attribute: name without underscore
between words. Several dunders, including some of the most recently
added ones, include an underscore in the word: ``__class_getitem__``,
``__release_buffer__``, ``__type_params__``, ``__init_subclass__`` and
``__text_signature__``.
* ``type.__fqn__`` attribute name, where FQN stands for Fully Qualified
Name.
added ones, include an underscore in the word:
``__class_getitem__``, ``__release_buffer__``, ``__type_params__``,
``__init_subclass__`` and ``__text_signature__``.
* Add ``type.__fqn__`` attribute: FQN name stands for **F**\ ully
**Q**\ ualified **N**\ ame.
* Add ``type.fully_qualified_name()`` method. Methods added to ``type``
are inherited by all types and so can affect existing code.
* Add a function to the ``inspect`` module. Need to import the
``inspect`` module to use it.
@ -493,9 +525,10 @@ not treat the ``__main__`` module differently: include it in the name.
Existing code such as ``type.__repr__()``, ``collections.abc`` and
``unittest`` modules format a type name with
``f'{obj.__module__}.{obj.__qualname__}'`` and only omit the module part
if the module is equal to ``builtins``. Only the ``traceback`` and
``pdb`` modules also the module if it's equal to ``"builtins"`` or
``"__main__"``.
if the module is equal to ``builtins``.
Only the ``traceback`` and ``pdb`` modules also omit the module if it's
equal to ``"builtins"`` or ``"__main__"``.
The ``type.__fully_qualified_name__`` attribute omits the ``__main__``
module to produce shorter names for a common case: types defined in a