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:
parent
d8e113004d
commit
8cbcebf505
|
@ -14,10 +14,19 @@ Abstract
|
||||||
|
|
||||||
Add new convenient APIs to format type names the same way in Python and
|
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
|
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
|
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
|
Make C code safer by avoiding borrowed reference which can lead to
|
||||||
crashes. The new C API is compatible with the limited C API.
|
crashes. The new C API is compatible with the limited C API.
|
||||||
|
@ -172,16 +181,17 @@ Specification
|
||||||
=============
|
=============
|
||||||
|
|
||||||
* Add ``type.__fully_qualified_name__`` attribute.
|
* Add ``type.__fully_qualified_name__`` attribute.
|
||||||
* Add ``%T``, ``%#T``, ``%N``, ``%#N`` formats to
|
* Add ``type.__format__()`` method.
|
||||||
``PyUnicode_FromFormat()``.
|
* Add formats to ``PyUnicode_FromFormat()``.
|
||||||
|
* Add ``PyType_GetModuleName()`` function.
|
||||||
* Add ``PyType_GetFullyQualifiedName()`` function.
|
* Add ``PyType_GetFullyQualifiedName()`` function.
|
||||||
* Recommend using the type fully qualified name in error messages and
|
* Recommend using the type fully qualified name in error messages and
|
||||||
in ``__repr__()`` methods in new code.
|
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
|
Add ``type.__fully_qualified_name__`` read-only attribute, the fully
|
||||||
qualified name of a type: similar to
|
qualified name of a type: similar to
|
||||||
|
@ -190,39 +200,48 @@ qualified name of a type: similar to
|
||||||
equal to ``"__main__"``.
|
equal to ``"__main__"``.
|
||||||
|
|
||||||
The ``type.__repr__()`` is left unchanged, it only omits the module if
|
The ``type.__repr__()`` is left unchanged, it only omits the module if
|
||||||
the module is equal to ``"builtins"``. It includes the module if the
|
the module is equal to ``"builtins"``.
|
||||||
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}'>"
|
|
||||||
|
|
||||||
|
|
||||||
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**:
|
* ``N`` formats the type **fully qualified name**
|
||||||
similar to ``type(obj).__fully_qualified_name__``.
|
(``type.__fully_qualified_name__``);
|
||||||
* ``%#T`` formats the type short name of an **object**:
|
``N`` stands for **N**\ ame.
|
||||||
similar to ``type(obj).__name__``.
|
* ``#N`` (alternative form) formats the type **fully qualified name**
|
||||||
* ``%N`` formats the fully qualified name of a **type**:
|
using the **colon** (``:``) separator, instead of the dot separator
|
||||||
similar to ``type.__fully_qualified_name__``.
|
(``.``), between the module name and the qualified name.
|
||||||
* ``%#N`` formats the short name of an object of a **type**:
|
|
||||||
similar to ``type.__name__``.
|
|
||||||
|
|
||||||
The hash character (``#``) in the format string stands for
|
Examples using f-string::
|
||||||
`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).
|
|
||||||
|
|
||||||
The ``%T`` format is used by ``time.strftime()``, but it's not used by
|
>>> import datetime
|
||||||
``printf()``.
|
>>> 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*:
|
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
|
* The ``PyTypeObject.tp_name`` bytes string no longer has to be decoded
|
||||||
from UTF-8 at each ``PyErr_Format()`` call, since
|
from UTF-8 at each ``PyErr_Format()`` call, since
|
||||||
``type.__fully_qualified_name__`` is already a Unicode string.
|
``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.
|
* 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
|
Add PyType_GetFullyQualifiedName() function
|
||||||
-------------------------------------------
|
-------------------------------------------
|
||||||
|
@ -279,12 +335,9 @@ in an unambiguous way.
|
||||||
Recommend not truncating type names
|
Recommend not truncating type names
|
||||||
-----------------------------------
|
-----------------------------------
|
||||||
|
|
||||||
Type names must not be truncated. For example, the ``%.100s`` format
|
Type names should not be truncated in new code. For example, the
|
||||||
should be avoided: use the ``%s`` format instead (or ``%T`` and ``%#T``
|
``%.100s`` format should be avoided: use the ``%s`` format instead (or
|
||||||
formats in C).
|
``%T`` format in C).
|
||||||
|
|
||||||
Code in the standard library is updated to no longer truncate type
|
|
||||||
names.
|
|
||||||
|
|
||||||
|
|
||||||
Implementation
|
Implementation
|
||||||
|
@ -303,12 +356,9 @@ Adding new APIs has no effect on the backward compatibility. Existing
|
||||||
APIs are left unchanged.
|
APIs are left unchanged.
|
||||||
|
|
||||||
Replacing the type short name with the type fully qualified name is only
|
Replacing the type short name with the type fully qualified name is only
|
||||||
recommended in new code. Existing code should be left
|
recommended in new code. No longer truncating type names is only
|
||||||
unchanged and so remains backward compatible.
|
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.
|
|
||||||
|
|
||||||
|
|
||||||
Rejected Ideas
|
Rejected Ideas
|
||||||
|
@ -330,38 +380,14 @@ See the `pull request: type(str) returns the fully qualified name
|
||||||
<https://github.com/python/cpython/pull/112129>`_.
|
<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
|
Add !t formatter to get an object type
|
||||||
--------------------------------------
|
--------------------------------------
|
||||||
|
|
||||||
Use ``f"{obj!t:T}"`` to format ``type(obj).__name__``, similar to
|
Use ``f"{obj!t:T}"`` to format ``type(obj).__fully_qualified_name__``,
|
||||||
``f"{type(obj).__name__}"``.
|
similar to ``f"{type(obj):T}"``.
|
||||||
|
|
||||||
When the ``!t`` formatter was proposed in 2018, `Eric Smith was opposed
|
When the ``!t`` formatter was proposed in 2018, `Eric Smith was stronly
|
||||||
to this
|
opposed to this
|
||||||
<https://mail.python.org/archives/list/python-dev@python.org/message/BMIW3FEB77OS7OB3YYUUDUBITPWLRG3U/>`_;
|
<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".
|
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``.
|
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.
|
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
|
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
|
About the ``%t`` format, ``printf()`` now uses ``t`` as a length
|
||||||
modifier for ``ptrdiff_t`` argument.
|
modifier for ``ptrdiff_t`` argument.
|
||||||
|
|
||||||
``type.__qualname__`` can be used in Python and ``PyType_GetQualName()``
|
The following APIs to be used to format a type:
|
||||||
can be used in C to format a type qualified name.
|
|
||||||
|
.. 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
|
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
|
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
|
between words. Several dunders, including some of the most recently
|
||||||
added ones, include an underscore in the word: ``__class_getitem__``,
|
added ones, include an underscore in the word:
|
||||||
``__release_buffer__``, ``__type_params__``, ``__init_subclass__`` and
|
``__class_getitem__``, ``__release_buffer__``, ``__type_params__``,
|
||||||
``__text_signature__``.
|
``__init_subclass__`` and ``__text_signature__``.
|
||||||
* ``type.__fqn__`` attribute name, where FQN stands for Fully Qualified
|
* Add ``type.__fqn__`` attribute: FQN name stands for **F**\ ully
|
||||||
Name.
|
**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
|
* Add a function to the ``inspect`` module. Need to import the
|
||||||
``inspect`` module to use it.
|
``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
|
Existing code such as ``type.__repr__()``, ``collections.abc`` and
|
||||||
``unittest`` modules format a type name with
|
``unittest`` modules format a type name with
|
||||||
``f'{obj.__module__}.{obj.__qualname__}'`` and only omit the module part
|
``f'{obj.__module__}.{obj.__qualname__}'`` and only omit the module part
|
||||||
if the module is equal to ``builtins``. Only the ``traceback`` and
|
if the module is equal to ``builtins``.
|
||||||
``pdb`` modules also the module if it's equal to ``"builtins"`` or
|
|
||||||
``"__main__"``.
|
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__``
|
The ``type.__fully_qualified_name__`` attribute omits the ``__main__``
|
||||||
module to produce shorter names for a common case: types defined in a
|
module to produce shorter names for a common case: types defined in a
|
||||||
|
|
Loading…
Reference in New Issue