diff --git a/peps/pep-0737.rst b/peps/pep-0737.rst index 005022c11..59a1a5df4 100644 --- a/peps/pep-0737.rst +++ b/peps/pep-0737.rst @@ -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"" +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 -`_. -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 `_. -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 `_; 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