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

This commit is contained in:
Hugo van Kemenade 2025-12-15 13:30:23 +02:00 committed by GitHub
parent fb554ad68d
commit 38ad651b67
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 53 additions and 7 deletions

View file

@ -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`

View file

@ -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`

View file

@ -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}")

View file

@ -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)

View file

@ -0,0 +1 @@
Deprecate ``__version__`` from :mod:`ctypes`. Patch by Hugo van Kemenade.

View file

@ -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));

View file

@ -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 },