I would like to specify clearly a new “soft deprecation” status in PEP 387: Backwards Compatibility Policy for functions which should be avoided for new code since these functions will not be developed further, but it’s ok to keep them in old code since their removal is not scheduled.
Over the least years, whenever I saw anything marked as deprecated for at least 2 Python releases, I eaggerly removed it as soon as possible. In Python 3.13, I started to dig deeper and found many “inactive” deprecations: deprecations only in documentation or code comments: mostly invisible to users.
Example in code (ctypes):
# XXX Deprecated def SetPointerType(pointer, cls): ...
Example in the doc (C API), comment not visible in rendered HTML documentation:
.. XXX deprecated, will be removed .. c:function:: int PyArg_Parse(PyObject *args, const char *format, ...)
Another example, I was surprised that the
optparse module got deprecated in its documentation as soon as the new shiny
argparse module was added to Python 2.7, whereas the
getopt was not deprecated. I like how
optparse is deprecated in the documentation:
Deprecated since version 3.2: The optparse module is deprecated and will not be developed further; development will continue with the argparse module.
No removal is planned. There is no urgency for users to upgrade their code to argpase. This deprecation is there since Python 2.7 (2010) and Python 3.2 (2012): for 13 years. I proposed a PR to deprecate getopt, but CAM doesn’t seem to fully embrace the idea of “a deprecation” (even if I don’t schedule its removal).
Another tricky deprecation is the
PyObject_SetAttrString(obj, attr_name, NULL) C API which can be used to delete an attribute, instead of using
PyObject_SetAttrString(obj, attr_name). Serhiy Storchaka explained that this API is error-prone since the 3rd argument can be NULL because a function call. See the issue and my PR discussion for details.
PyObject_SetAttrString(obj, attr_name, NULL) should be deprecated, but it’s unclear how since currently it’s valid code for 30 years.
I dislike this gray area of things not really deprecated but still deprecated. It would be nice to have a well defined status “deprecated with no plan to remove it” in PEP 387. It doesn’t mean that this function can no be removed anymore. It’s a way to announce to users that they should avoid it for new code, it’s ok continuing to use it in existing code, and there is no urgency to migrate existing usage.
Deprecation is usually implemented at different places:
- Just a sentence in the documentation without markup
.. deprecated:: 3.xmarkup in the documentation
- Just a sentence in the function docstring
- DeprecationWarning emitted in the code
I propose to define a “soft deprecation”:
.. deprecated::markup in the documentation, or maybe (better?) a new
.. soft-deprecated::markup in the documentation (which would link to what it means in details)?
- Emit PendingDeprecation in the code
- Maybe be explicit that it’s “soft deprecation” and that the feature is not scheduled for removal
For me, each deprecation change should require a new discussion:
- not deprecated => soft deprecation: it should only impact users who check for warnings in tests, but PendingDeprecationWarning is safe to ignore, no?
- soft deprecation (PendingDepecationWarning) => “hard” deprecation (DeprecationWarning): so far, all “hard” deprecations that I saw were always be followed by removal. So we must think ahead: how to have a smooth migration plan, how to write
- “hard” deprecation => removal (usually, this one is easy, but not always)
Today, the most common case is to move from “not deprecated” to “hard” deprecation immediately. IMO it’s fine for most cases, especially when the code is known be error-prone, caused security vulnerabilities, is rarely used, etc.
At each status change, we should investigate how the code is used in CPython itself (remember asyncore/asynchat or imp module in the Python stdlib and test suite!), in PyPI top 5,000 projects, look for usage in the Internet (code search, questions on forums, etc.), check also for issues in the Python bug tracker, etc. It’s not because a function is still widely used that i cannot be removed. It should be a consensus.
What do you think? Should we keep this “gray area”? Is it worth it to specify a “soft deprecation”?
See also my issue: Keep deprecated functions in Python 3.13: remove this deprecation.