Skip to content

Commit

Permalink
PEP 674: Leave PyDescr_TYPE() unchanged (#2277)
Browse files Browse the repository at this point in the history
Add Version History.
  • Loading branch information
vstinner authored Jan 26, 2022
1 parent 854f676 commit e3836e9
Showing 1 changed file with 74 additions and 56 deletions.
130 changes: 74 additions & 56 deletions pep-0674.rst
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
PEP: 674
Title: Disallow using macros as l-value
Title: Disallow using macros as an l-value
Author: Victor Stinner <vstinner@python.org>
Status: Draft
Type: Standards Track
Expand All @@ -10,15 +10,15 @@ Python-Version: 3.11
Abstract
========

Incompatible C API change disallowing using macros as l-value to:
Incompatible C API change disallowing using macros as an l-value to:

* Allow evolving CPython internals;
* Ease the C API implementation on other Python implementation;
* Help migrating existing C extensions to the HPy API.

On the PyPI top 5000 projects, only 14 projects (0.3%) are affected by 4
macro changes. Moreover, 24 projects just have to regenerate their
Cython code to use ``Py_SET_TYPE()``.
Only 13 out of the top 5000 PyPI projects (0.3%) are affected by 2 macro
changes. An additional 22 projects just have to regenerate their Cython
code.

In practice, the majority of affected projects only have to make two
changes:
Expand All @@ -32,8 +32,8 @@ changes:
Rationale
=========

Using a macro as a l-value
--------------------------
Using a macro as a an l-value
-----------------------------

In the Python C API, some functions are implemented as macro because
writing a macro is simpler than writing a regular function. If a macro
Expand All @@ -48,7 +48,7 @@ This macro can be used as a **r-value** to **get** an object type::

type = Py_TYPE(object);

It can also be used as **l-value** to **set** an object type::
It can also be used as an **l-value** to **set** an object type::

Py_TYPE(object) = new_type;

Expand All @@ -73,7 +73,7 @@ first to fix this C API compatibility issue. It is a concrete example of
a Python optimization blocked indirectly by the C API.

This issue was already fixed in Python 3.10: the ``Py_REFCNT()`` macro
has been already modified to disallow using it as a l-value.
has been already modified to disallow using it as an l-value.

These statements are endorsed by Sam Gross (nogil developer).

Expand Down Expand Up @@ -119,8 +119,8 @@ These statements are endorsed by Tim Felgentreff (GraalVM Python developer).
Specification
=============

Disallow using macros as l-value
--------------------------------
Disallow using macros as an l-value
-----------------------------------

PyObject and PyVarObject macros
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Expand Down Expand Up @@ -207,12 +207,6 @@ PyDateTime "GET" macros
* ``PyDateTime_TIME_GET_SECOND()``
* ``PyDateTime_TIME_GET_TZINFO()``

PyDescr macros
^^^^^^^^^^^^^^

* ``PyDescr_NAME()``
* ``PyDescr_TYPE()``

Port C extensions to Python 3.11
--------------------------------

Expand All @@ -231,57 +225,71 @@ losing support with older Python versions. The project provides a header
file which provides ``Py_SET_REFCNT()``, ``Py_SET_TYPE()`` and
``Py_SET_SIZE()`` functions to Python 3.8 and older.

PyTuple_GET_ITEM() and PyList_GET_ITEM()
----------------------------------------

The ``PyTuple_GET_ITEM()`` and ``PyList_GET_ITEM()`` macros are left
unchanged.

The code pattern ``&PyTuple_GET_ITEM(tuple, 0)`` and
``&PyList_GET_ITEM(list, 0)`` is still commonly used to get access to
the inner ``PyObject**`` array.

Changing these macros is out of the scope of this PEP.


Backwards Compatibility
=======================

The proposed C API changes are backward incompatible on purpose.

At December 1, 2021, a code search on the PyPI top 5000 projects (4760
projects in practice, others don't have a source archive) found that
`only 14 projects are affected
<https://bugs.python.org/issue45476#msg407456>`_ (0.3%):
On January 26, 2022, a code search on the top 5000 PyPI projects (4762
projects in practice; others don't have a source archive) found that
only 13 projects are affected (0.3%):

* Cython (0.29.26)
* PyGObject (3.42.0)
* datatable (1.0.0)
* frozendict (2.1.1)
* guppy3 (3.1.2)
* M2Crypto (0.38.0)
* mecab-python3 (1.0.4)
* mypy (0.910)
* Naked (0.1.31)
* numpy (1.22.1)
* pickle5 (0.0.12)
* psycopg2 (2.9.3)
* pycurl (7.44.1)
* pysha3 (1.0.2)
* python-snappy (0.6.0)
* recordclass (0.16.3)
* recordclass (0.17.1)
* scipy (1.7.3)
* zodbpickle (2.2.0)
* zstd (1.5.0.2)

These 14 projects only use 4 macros as l-value:
Of these 13 projects, only 2 macros are used as an l-value:
``Py_TYPE()`` and ``Py_SIZE()``.

* ``PyDescr_NAME()`` and ``PyDescr_TYPE()`` (2 projects)
* ``Py_SIZE()`` (8 projects)
* ``Py_TYPE()`` (4 projects)
An additional 22 projects just have to regenerate their Cython code to
use ``Py_SET_TYPE()`` and ``Py_SET_SIZE()``.

Moreover, `24 projects just have to regenerate their Cython code
<https://bugs.python.org/issue45476#msg407416>`_ to use
``Py_SET_TYPE()``.
This change does not follow the :pep:`387` deprecation process. There is
no known way to emit a deprecation warning only when a macro is used as
an l-value, but not when it's used differently (ex: as a r-value).

This change does not follow the :pep:`387` deprecation process. There is no
known way to emit a deprecation warning only when a macro is used as a
l-value, but not when it's used differently (ex: as a r-value).
PyTuple_GET_ITEM() and PyList_GET_ITEM() are left unchanged
-----------------------------------------------------------

The ``PyTuple_GET_ITEM()`` and ``PyList_GET_ITEM()`` macros are left
unchanged.

The code patterns ``&PyTuple_GET_ITEM(tuple, 0)`` and
``&PyList_GET_ITEM(list, 0)`` are still commonly used to get access to
the inner ``PyObject**`` array.

Changing these macros is out of the scope of this PEP.

PyDescr_NAME() and PyDescr_TYPE() are left unchanged
----------------------------------------------------

The ``PyDescr_NAME()`` and ``PyDescr_TYPE()`` macros are left unchanged.

These macros give access to ``PyDescrObject.d_name`` and
``PyDescrObject.d_type`` members. They can be used as l-values to set
these members.

The SWIG project uses these macros as l-values to set these members. It
would be possible to modify SWIG to prevent setting ``PyDescrObject``
structure members directly, but it is not really worth it since the
``PyDescrObject`` structure is not performance critical and is unlikely
to change soon.

See the `bpo-46538 <https://bugs.python.org/issue46538>`_ "[C API] Make
the PyDescrObject structure opaque: PyDescr_NAME() and PyDescr_TYPE()"
issue for more details.


Relationship with the HPy project
Expand Down Expand Up @@ -332,8 +340,9 @@ extensions to HPy is expected to take a few years.

This PEP proposes to make the C API "less bad" by fixing one problem
which is clearily identified as causing practical issues: macros used as
l-values. This PEP only requires to update a minority of C extensions,
and usually only a few lines need to be changed in impacted extensions.
l-values. This PEP only requires updating a minority of C
extensions, and usually only a few lines need to be changed in impacted
extensions.

For example, NumPy 1.22 is made of 307,300 lines of C code, and adapting
NumPy to the this PEP only modified 11 lines (use Py_SET_TYPE and
Expand All @@ -358,8 +367,8 @@ example, a ``Py_SET_TYPE()`` function has been added to Python 3.9 and
the ``Py_TYPE()`` documentation now requires to use the
``Py_SET_TYPE()`` function to set an object type.

If developers use macros as l-value, it's their responsibility when
their code breaks, not the Python responsibility. We are operating under
If developers use macros as an l-value, it's their responsibility when
their code breaks, not Python's responsibility. We are operating under
the consenting adults principle: we expect users of the Python C API to
use it as documented and expect them to take care of the fallout, if
things break when they don't.
Expand All @@ -370,9 +379,9 @@ API documentation. The majority of developers are only using CPython and
so are not aware of compatibility issues with other Python
implementations.

Moreover, continuing to allow using macros as l-value does not help the
HPy project and leaves the burden of emulating them on GraalVM's Python
implementation.
Moreover, continuing to allow using macros as an l-value does not help
the HPy project, and leaves the burden of emulating them on GraalVM's
Python implementation.


Macros already modified
Expand Down Expand Up @@ -423,6 +432,15 @@ References
<https://bugs.python.org/issue30459>`_ (May 2017)


Version History
===============

* Version 3: No longer change PyDescr_TYPE() and PyDescr_NAME() macros
* Version 2: Add "Relationship with the HPy project" section, remove
the PyPy section
* Version 1: First public version


Copyright
=========

Expand Down

0 comments on commit e3836e9

Please sign in to comment.