https://github.com/python/cpython/commit/38ad651b678da098400a8df8c133ebf5ced12965
commit: 38ad651b678da098400a8df8c133ebf5ced12965
branch: main
author: Hugo van Kemenade <[email protected]>
committer: hugovk <[email protected]>
date: 2025-12-15T13:30:23+02:00
summary:

gh-76007: Deprecate `__version__` attribute in `ctypes` (#142679)

files:
A Misc/NEWS.d/next/Library/2025-12-13-21-19-28.gh-issue-76007.6fs_gT.rst
M Doc/deprecations/pending-removal-in-3.20.rst
M Doc/whatsnew/3.15.rst
M Lib/ctypes/__init__.py
M Lib/test/test_ctypes/__init__.py
M Modules/_ctypes/_ctypes.c
M Modules/_ctypes/callproc.c

diff --git a/Doc/deprecations/pending-removal-in-3.20.rst 
b/Doc/deprecations/pending-removal-in-3.20.rst
index 1e517531c953e9..4a6a6c3c43a722 100644
--- a/Doc/deprecations/pending-removal-in-3.20.rst
+++ b/Doc/deprecations/pending-removal-in-3.20.rst
@@ -7,6 +7,7 @@ Pending removal in Python 3.20
 
   - :mod:`argparse`
   - :mod:`csv`
+  - :mod:`ctypes`
   - :mod:`!ctypes.macholib`
   - :mod:`decimal` (use :data:`decimal.SPEC_VERSION` instead)
   - :mod:`http.server`
diff --git a/Doc/whatsnew/3.15.rst b/Doc/whatsnew/3.15.rst
index d9a34fe920d10d..ccf6c76f1e0fa5 100644
--- a/Doc/whatsnew/3.15.rst
+++ b/Doc/whatsnew/3.15.rst
@@ -1032,6 +1032,7 @@ New deprecations
 
     - :mod:`argparse`
     - :mod:`csv`
+    - :mod:`ctypes`
     - :mod:`!ctypes.macholib`
     - :mod:`decimal` (use :data:`decimal.SPEC_VERSION` instead)
     - :mod:`http.server`
diff --git a/Lib/ctypes/__init__.py b/Lib/ctypes/__init__.py
index ab5b656e6e5d6c..aec92f3aee2472 100644
--- a/Lib/ctypes/__init__.py
+++ b/Lib/ctypes/__init__.py
@@ -5,12 +5,9 @@
 import sysconfig as _sysconfig
 import types as _types
 
-__version__ = "1.1.0"
-
 from _ctypes import Union, Structure, Array
 from _ctypes import _Pointer
 from _ctypes import CFuncPtr as _CFuncPtr
-from _ctypes import __version__ as _ctypes_version
 from _ctypes import RTLD_LOCAL, RTLD_GLOBAL
 from _ctypes import ArgumentError
 from _ctypes import SIZEOF_TIME_T
@@ -18,9 +15,6 @@
 
 from struct import calcsize as _calcsize
 
-if __version__ != _ctypes_version:
-    raise Exception("Version number mismatch", __version__, _ctypes_version)
-
 if _os.name == "nt":
     from _ctypes import COMError, CopyComPointer, FormatError
 
@@ -673,3 +667,12 @@ def DllCanUnloadNow():
     raise SystemError(f"Unexpected sizeof(time_t): {SIZEOF_TIME_T=}")
 
 _reset_cache()
+
+
+def __getattr__(name):
+    if name == "__version__":
+        from warnings import _deprecated
+
+        _deprecated("__version__", remove=(3, 20))
+        return "1.1.0"  # Do not change
+    raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
diff --git a/Lib/test/test_ctypes/__init__.py b/Lib/test/test_ctypes/__init__.py
index eb9126cbe18081..d848beb1ff1857 100644
--- a/Lib/test/test_ctypes/__init__.py
+++ b/Lib/test/test_ctypes/__init__.py
@@ -1,4 +1,5 @@
 import os
+import unittest
 from test import support
 from test.support import import_helper
 
@@ -6,5 +7,21 @@
 # skip tests if the _ctypes extension was not built
 import_helper.import_module('ctypes')
 
+
+class TestModule(unittest.TestCase):
+    def test_deprecated__version__(self):
+        import ctypes
+        import _ctypes
+
+        for mod in (ctypes, _ctypes):
+            with self.subTest(mod=mod):
+                with self.assertWarnsRegex(
+                    DeprecationWarning,
+                    "'__version__' is deprecated and slated for removal in 
Python 3.20",
+                ) as cm:
+                    getattr(mod, "__version__")
+                self.assertEqual(cm.filename, __file__)
+
+
 def load_tests(*args):
     return support.load_package_tests(os.path.dirname(__file__), *args)
diff --git 
a/Misc/NEWS.d/next/Library/2025-12-13-21-19-28.gh-issue-76007.6fs_gT.rst 
b/Misc/NEWS.d/next/Library/2025-12-13-21-19-28.gh-issue-76007.6fs_gT.rst
new file mode 100644
index 00000000000000..99f73bb094c620
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2025-12-13-21-19-28.gh-issue-76007.6fs_gT.rst
@@ -0,0 +1 @@
+Deprecate ``__version__`` from :mod:`ctypes`. Patch by Hugo van Kemenade.
diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c
index 91fd23d413de21..774ac71ce9ec56 100644
--- a/Modules/_ctypes/_ctypes.c
+++ b/Modules/_ctypes/_ctypes.c
@@ -6334,7 +6334,6 @@ _ctypes_add_objects(PyObject *mod)
     MOD_ADD("FUNCFLAG_USE_ERRNO", PyLong_FromLong(FUNCFLAG_USE_ERRNO));
     MOD_ADD("FUNCFLAG_USE_LASTERROR", PyLong_FromLong(FUNCFLAG_USE_LASTERROR));
     MOD_ADD("FUNCFLAG_PYTHONAPI", PyLong_FromLong(FUNCFLAG_PYTHONAPI));
-    MOD_ADD("__version__", PyUnicode_FromString("1.1.0"));
 
     MOD_ADD("_memmove_addr", PyLong_FromVoidPtr(memmove));
     MOD_ADD("_memset_addr", PyLong_FromVoidPtr(memset));
diff --git a/Modules/_ctypes/callproc.c b/Modules/_ctypes/callproc.c
index a8c16547e4b217..9a1c1ff8bb9cda 100644
--- a/Modules/_ctypes/callproc.c
+++ b/Modules/_ctypes/callproc.c
@@ -1990,8 +1990,32 @@ buffer_info(PyObject *self, PyObject *arg)
 }
 
 
+static PyObject *
+_ctypes_getattr(PyObject *Py_UNUSED(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.1.0");  // Do not change
+    }
+
+    PyErr_Format(PyExc_AttributeError,
+                 "module '_ctypes' has no attribute %R", name);
+    return NULL;
+}
+
 
 PyMethodDef _ctypes_module_methods[] = {
+    {"__getattr__", _ctypes_getattr, METH_VARARGS},
     {"get_errno", get_errno, METH_NOARGS},
     {"set_errno", set_errno, METH_VARARGS},
     {"_unpickle", unpickle, METH_VARARGS },

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]

Reply via email to