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
|
||||
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
|
||||
|
|
Loading…
Reference in New Issue