Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

bpo-45476: Disallow using PyFloat_AS_DOUBLE() as l-value #28976

Closed
wants to merge 1 commit into from
Closed

bpo-45476: Disallow using PyFloat_AS_DOUBLE() as l-value #28976

wants to merge 1 commit into from

Conversation

vstinner
Copy link
Member

@vstinner vstinner commented Oct 15, 2021

The following "GET" and "AS" functions can no longer be used as
l-value (to modify a Python object):

  • PyByteArray_GET_SIZE()
  • PyBytes_GET_SIZE()
  • PyCFunction_GET_CLASS()
  • PyCFunction_GET_FLAGS()
  • PyCFunction_GET_FUNCTION()
  • PyCFunction_GET_SELF()
  • PyDict_GET_SIZE()
  • PyFloat_AS_DOUBLE()
  • PyFunction_GET_ANNOTATIONS()
  • PyFunction_GET_CLOSURE()
  • PyFunction_GET_CODE()
  • PyFunction_GET_DEFAULTS()
  • PyFunction_GET_GLOBALS()
  • PyFunction_GET_KW_DEFAULTS()
  • PyFunction_GET_MODULE()
  • PyHeapType_GET_MEMBERS()
  • PyInstanceMethod_GET_FUNCTION()
  • PyList_GET_SIZE()
  • PyMemoryView_GET_BASE()
  • PyMemoryView_GET_BUFFER()
  • PyMethod_GET_FUNCTION()
  • PyMethod_GET_SELF()
  • PySet_GET_SIZE()
  • PyTuple_GET_SIZE()
  • PyWeakref_GET_OBJECT()

These macros are modified to use the _Py_RVALUE() macro.

https://bugs.python.org/issue45476

@@ -129,4 +129,8 @@
Py_FatalError("Unreachable C code path reached")
#endif

// Prevent using an expressing as an l-value:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo: "expressing" for "expression"

@vstinner vstinner marked this pull request as draft October 15, 2021 13:51
@vstinner
Copy link
Member Author

I wrote PEP 670 https://www.python.org/dev/peps/pep-0670/ to decide what's the best approach to fix https://bugs.python.org/issue45476

@vstinner
Copy link
Member Author

I rebased the PR and reverted PyBytes_AS_STRING() and PyByteArray_AS_STRING() changes.

@vstinner vstinner changed the title bpo-45476: Add _Py_RVALUE() macro bpo-45476: Disallow using PyFloat_AS_DOUBLE() as l-value Nov 30, 2021
@vstinner vstinner marked this pull request as ready for review November 30, 2021 13:07
The following "GET" and "AS" functions can no longer be used as
l-value (to modify a Python object):

* PyByteArray_GET_SIZE()
* PyBytes_GET_SIZE()
* PyCFunction_GET_CLASS()
* PyCFunction_GET_FLAGS()
* PyCFunction_GET_FUNCTION()
* PyCFunction_GET_SELF()
* PyDict_GET_SIZE()
* PyFloat_AS_DOUBLE()
* PyFunction_GET_ANNOTATIONS()
* PyFunction_GET_CLOSURE()
* PyFunction_GET_CODE()
* PyFunction_GET_DEFAULTS()
* PyFunction_GET_GLOBALS()
* PyFunction_GET_KW_DEFAULTS()
* PyFunction_GET_MODULE()
* PyHeapType_GET_MEMBERS()
* PyInstanceMethod_GET_FUNCTION()
* PyList_GET_SIZE()
* PyMemoryView_GET_BASE()
* PyMemoryView_GET_BUFFER()
* PyMethod_GET_FUNCTION()
* PyMethod_GET_SELF()
* PySet_GET_SIZE()
* PyTuple_GET_SIZE()
* PyWeakref_GET_OBJECT()

These macros are modified to use the _Py_RVALUE() macro.
@vstinner
Copy link
Member Author

@corona10 @erlend-aasland @encukou: Would you mind to review this change? It's a C API incompatible change made on purpose to aid detecting bugs in C extensions when the C API is misused.

@encukou
Copy link
Member

encukou commented Nov 30, 2021

Is there any deprecation warning for these changes?

@vstinner
Copy link
Member Author

I cannot find any of the 25 macros being used as a l-value in an assignement in the PyPI top 5000 packages. So the risk of breaking 3rd party packages is very low.

I searched for the following patterns:

$ ./search_pypi_top_5000.sh '(PyByteArray|PyBytes|PyCFunction|PyDict|PyFunction|PyHeapType|PyInstanceMethod|PyList|PyMemoryView|PyMethod|PySet|PyTuple|PyWeakref)_GET[a-zA-Z0_9_]+ *([a-zA-Z0-9_]+) *=[^<>=]'
pypi-top-5000_2021-08-17/Cython-0.29.24.tar.gz

$ ./search_pypi_top_5000.sh 'PyFloat_AS_DOUBLE.*[^<>=!]=[^<>=]'
# no result

Cython-0.29.24.tar.gz is a false positive because of my weak regex:

Cython-0.29.2: Cython/Compiler/Optimize.py:    PyBytes_GET_SIZE_func_type = PyrexTypes.CFuncType(

@vstinner
Copy link
Member Author

Is there any deprecation warning for these changes?

No. Which kind of warning do you expect?

I am not aware of any technical way to emit a compiler warning when a macro is only used as an l-value, see previous discussion about the Py_TYPE() change: python/steering-council#79

@vstinner
Copy link
Member Author

@rhettinger: You may want to review the updated PR. I made minor changes since you approved the PR.

@encukou
Copy link
Member

encukou commented Nov 30, 2021

The SC decision you linked says:

these changes must be announced better and in general the deprecation notice needs to stay for at least two releases in the documentation.

How does this PR do that?

@vstinner
Copy link
Member Author

Documentation of the modified macros:

The following functions are not documented:

  • PyCFunction_GET_CLASS()
  • PyCFunction_GET_FLAGS()
  • PyCFunction_GET_FUNCTION()
  • PyCFunction_GET_SELF()
  • PyDict_GET_SIZE()
  • PyFunction_GET_ANNOTATIONS()
  • PyFunction_GET_CLOSURE()
  • PyFunction_GET_CODE()
  • PyFunction_GET_DEFAULTS()
  • PyFunction_GET_GLOBALS()
  • PyFunction_GET_KW_DEFAULTS()
  • PyFunction_GET_MODULE()
  • PyHeapType_GET_MEMBERS()

@vstinner
Copy link
Member Author

Is there any deprecation warning for these changes?

For me, using these macros as l-value is an unintentional usage. I don't think that we have to go through a deprecation process for this specific corner case.

For "GET" functions, it sounds weird to me to use them to set an object attribute or to modify a Python object.

For the PyFloat_AS_DOUBLE() function, it's less clear to me what is the intended usage. A Python float object is immutable. Once it's created, it can be referenced at multiple places (stored in two lists). If a list stores the number 1.0, it would be surprising to modify it in-place somewhere else to change its value to 2.0. Maybe some weird projects use an in-place modification when the reference count is 1, to avoid the cost of creating a new object. It sounds dangerous but people are creative to abuse the C API :-) What reassures me is that in the PyPI top 5000, I cannot find a single project doing that.

@encukou
Copy link
Member

encukou commented Nov 30, 2021

I like to think that the target audience for deprecation messages and "versionchanged" notices is someone who inherited a decades-old legacy project, and is tasked with keeping it usable without knowing much about Python and current best practices. If it works for that audience, it should work for everyone else as well.
Changing old API is tricky because you don't usually know how it was documented/used in the past -- it's entirely possible that reasonable people looked at the code rather than the documentation.

@vstinner
Copy link
Member Author

I wrote PEP 674 "Disallow using macros as l-value" for this change: https://python.github.io/peps/pep-0674/

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants