diff --git a/Doc/deprecations/pending-removal-in-3.20.rst b/Doc/deprecations/pending-removal-in-3.20.rst index 8bc863b185d..c86979c8ff9 100644 --- a/Doc/deprecations/pending-removal-in-3.20.rst +++ b/Doc/deprecations/pending-removal-in-3.20.rst @@ -19,5 +19,6 @@ Pending removal in Python 3.20 - :mod:`tabnanny` - :mod:`tkinter.font` - :mod:`tkinter.ttk` + - :mod:`zlib` - (Contributed by Hugo van Kemenade in :gh:`76007`.) + (Contributed by Hugo van Kemenade and Stan Ulbrych in :gh:`76007`.) diff --git a/Doc/whatsnew/3.15.rst b/Doc/whatsnew/3.15.rst index c5321ee9983..8a757724442 100644 --- a/Doc/whatsnew/3.15.rst +++ b/Doc/whatsnew/3.15.rst @@ -828,8 +828,9 @@ New deprecations - :mod:`tabnanny` - :mod:`tkinter.font` - :mod:`tkinter.ttk` + - :mod:`zlib` - (Contributed by Hugo van Kemenade in :gh:`76007`.) + (Contributed by Hugo van Kemenade and Stan Ulbrych in :gh:`76007`.) .. Add deprecations above alphabetically, not here at the end. diff --git a/Lib/test/test_zlib.py b/Lib/test/test_zlib.py index c57ab51eca1..ed9d8540815 100644 --- a/Lib/test/test_zlib.py +++ b/Lib/test/test_zlib.py @@ -1222,5 +1222,15 @@ def __index__(self): return 100 +class TestModule(unittest.TestCase): + def test_deprecated__version__(self): + with self.assertWarnsRegex( + DeprecationWarning, + "'__version__' is deprecated and slated for removal in Python 3.20", + ) as cm: + getattr(zlib, "__version__") + self.assertEqual(cm.filename, __file__) + + if __name__ == "__main__": unittest.main() diff --git a/Misc/NEWS.d/next/Library/2025-10-14-20-27-06.gh-issue-76007.2NcUbo.rst b/Misc/NEWS.d/next/Library/2025-10-14-20-27-06.gh-issue-76007.2NcUbo.rst new file mode 100644 index 00000000000..567fb5ef904 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-10-14-20-27-06.gh-issue-76007.2NcUbo.rst @@ -0,0 +1,2 @@ +:mod:`zlib`: Deprecate ``__version__`` and schedule for removal in Python +3.20. diff --git a/Modules/zlibmodule.c b/Modules/zlibmodule.c index 36c933bf618..6bac09aa6c2 100644 --- a/Modules/zlibmodule.c +++ b/Modules/zlibmodule.c @@ -2015,6 +2015,27 @@ zlib_crc32_combine_impl(PyObject *module, unsigned int crc1, return crc32_combine(crc1, crc2, len); } +static PyObject * +zlib_getattr(PyObject *self, PyObject *args) +{ + PyObject *name; + if (!PyArg_UnpackTuple(args, "__getattr__", 1, 1, &name)) { + return NULL; + } + + if (PyUnicode_Check(name) && PyUnicode_EqualToUTF8(name, "__version__")) { + if (PyErr_WarnEx(PyExc_DeprecationWarning, + "'__version__' is deprecated and slated for removal in Python 3.20", + 1) < 0) { + return NULL; + } + return PyUnicode_FromString("1.0"); + } + + PyErr_Format(PyExc_AttributeError, "module 'zlib' has no attribute %R", name); + return NULL; +} + static PyMethodDef zlib_methods[] = { ZLIB_ADLER32_METHODDEF @@ -2025,6 +2046,7 @@ static PyMethodDef zlib_methods[] = ZLIB_CRC32_COMBINE_METHODDEF ZLIB_DECOMPRESS_METHODDEF ZLIB_DECOMPRESSOBJ_METHODDEF + {"__getattr__", zlib_getattr, METH_VARARGS, "Module __getattr__"}, {NULL, NULL} }; @@ -2221,9 +2243,6 @@ zlib_exec(PyObject *mod) return -1; } #endif - if (PyModule_AddStringConstant(mod, "__version__", "1.0") < 0) { - return -1; - } return 0; }