Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
190 changes: 101 additions & 89 deletions peps/pep-0737.rst
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
PEP: 737
Title: Unify type name formatting
Title: C API to format a type fully qualified name
Author: Victor Stinner <vstinner@python.org>
Discussions-To: https://discuss.python.org/t/pep-737-unify-type-name-formatting/39872
Status: Draft
Expand All @@ -12,17 +12,12 @@ Post-History: `29-Nov-2023 <https://discuss.python.org/t/pep-737-unify-type-name
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.
Add new convenient C APIs to format a type fully qualified name. No longer
format type names differently depending on how types are implemented.

Recommend using the type fully qualified name in error messages and in
``__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*.
``__repr__()`` methods in new C code. Recommend not truncating type
names in new C code.

Add ``%T``, ``%#T``, ``%N`` and ``%#N`` formats to
``PyUnicode_FromFormat()`` to format the fully qualified, respectively,
Expand Down Expand Up @@ -180,65 +175,58 @@ limit of 500 bytes is outdated
Specification
=============

* Add ``type.__fully_qualified_name__`` attribute.
* Add ``type.__format__()`` method.
* Add formats to ``PyUnicode_FromFormat()``.
* Add ``PyType_GetModuleName()`` function.
* Add ``PyType_GetFullyQualifiedName()`` function.
* Add ``PyType_GetModuleName()`` function.
* Add formats to ``PyUnicode_FromFormat()``.
* Recommend using the type fully qualified name in error messages and
in ``__repr__()`` methods in new code.
* Recommend not truncating type names in new code.
in ``__repr__()`` methods in new C code.
* Recommend not truncating type names in new C code.


Add type.__fully_qualified_name__ attribute
Add PyType_GetFullyQualifiedName() function
-------------------------------------------

Add ``type.__fully_qualified_name__`` read-only attribute, the fully
Add the ``PyType_GetFullyQualifiedName()`` function to get the fully
qualified name of a type: similar to
``f"{type.__module__}.{type.__qualname__}"``, or ``type.__qualname__`` if
``type.__module__`` is not a string or is equal to ``"builtins"`` or is
equal to ``"__main__"``.
``f"{type.__module__}.{type.__qualname__}"``, or ``type.__qualname__``
if ``type.__module__`` is not a string or is equal to ``"builtins"`` or
is equal to ``"__main__"``.

The ``type.__repr__()`` is left unchanged, it only omits the module if
the module is equal to ``"builtins"``.
API:

.. code-block:: c

Add type.__format__() method
----------------------------
PyObject* PyType_GetFullyQualifiedName(PyTypeObject *type)

Add ``type.__format__()`` method with the following formats:
On success, return a new reference to the string. On error, raise an
exception and return ``NULL``.

* ``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.

Examples using f-string::
Add PyType_GetModuleName() function
-----------------------------------

>>> import datetime
>>> f"{datetime.timedelta:N}" # fully qualified name
'datetime.timedelta'
>>> f"{datetime.timedelta:#N}" # fully qualified name, colon separator
'datetime:timedelta'
Add the ``PyType_GetModuleName()`` function to get the module name of a
type (``type.__module__`` string). API:

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.
.. 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 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`` formats the **fully qualified name** of a **type**,
similar to ``PyType_GetFullyQualifiedName(type)``;
**N** stands for type **N**\ ame.
* ``%T`` formats the type **fully qualified name** of an **object**,
similar to ``PyType_GetFullyQualifiedName(Py_TYPE(obj))``;
**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.
Expand All @@ -263,9 +251,6 @@ Advantages of the updated code:
* Safer C code: avoid ``Py_TYPE()`` which returns a borrowed reference.
* The ``PyTypeObject.tp_name`` member is no longer read explicitly: the
code becomes compatible with the limited C API.
* 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.

Expand All @@ -281,50 +266,19 @@ Formats Summary

* - 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 the ``PyType_GetFullyQualifiedName()`` function to get the fully
qualified name of a type (``type.__fully_qualified_name__``). API:

.. code-block:: c

PyObject* PyType_GetFullyQualifiedName(PyTypeObject *type)

On success, return a new reference to the string. On error, raise an
exception and return ``NULL``.


Recommend using the type fully qualified name
---------------------------------------------

The type fully qualified name is recommended in error messages and in
``__repr__()`` methods in new code.
``__repr__()`` methods in new C code.

In non-trivial applications, it is likely to have two types with the
same short name defined in two different modules, especially with
Expand All @@ -335,7 +289,7 @@ in an unambiguous way.
Recommend not truncating type names
-----------------------------------

Type names should not be truncated in new code. For example, the
Type names should not be truncated in new C code. For example, the
``%.100s`` format should be avoided: use the ``%s`` format instead (or
``%T`` format in C).

Expand All @@ -352,18 +306,76 @@ Backwards Compatibility

Changes proposed in this PEP are backward compatible.

Adding new APIs has no effect on the backward compatibility. Existing
APIs are left unchanged.
Adding new C APIs has no effect on the backward compatibility. Existing
C APIs are left unchanged. No Python API is changed.

Replacing the type short name with the type fully qualified name is only
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.
recommended in new C code. No longer truncating type names is only
recommended in new C code. Existing code should be left unchanged and so
remains backward compatible. There is no recommendation for Python code.


Rejected Ideas
==============

Add type.__fully_qualified_name__ attribute
-------------------------------------------

Add ``type.__fully_qualified_name__`` read-only attribute, the fully
qualified name of a type: similar to
``f"{type.__module__}.{type.__qualname__}"``, or ``type.__qualname__`` if
``type.__module__`` is not a string or is equal to ``"builtins"`` or is
equal to ``"__main__"``.

The ``type.__repr__()`` is left unchanged, it only omits the module if
the module is equal to ``"builtins"``.

This change was `rejected by the Steering Council
<https://discuss.python.org/t/pep-737-unify-type-name-formatting/39872/51>`__:

We can see the usefulness of the C API changes proposed by the PEP
and would likely accept those changes as is.

We see less justification for the Python level changes. We
especially question the need for ``__fully_qualified_name__``.

Thomas Wouters added:

If there really is a desire for formatting types the exact same way
the C API does it, a utility function would make more sense to me,
personally, than ``type.__format__``, but I think the SC could be
persuaded given some concrete use-cases.


Add type.__format__() method
----------------------------

Add ``type.__format__()`` method with the following formats:

* ``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.

Examples using f-string::

>>> 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.

This change was `rejected by the Steering Council
<https://discuss.python.org/t/pep-737-unify-type-name-formatting/39872/52>`__.


Change str(type)
----------------

Expand All @@ -386,7 +398,7 @@ Add !t formatter to get an object type
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 stronly
When the ``!t`` formatter was proposed in 2018, `Eric Smith was strongly
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".
Expand Down