Skip to content

Commit 3df2159

Browse files
committed
Deprecate ctypes.DllGetClassObject and ctypes.DllCanUnloadNow
1 parent 5eb7fd4 commit 3df2159

File tree

6 files changed

+91
-20
lines changed

6 files changed

+91
-20
lines changed
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
Pending removal in Python 3.19
2+
------------------------------
3+
4+
* :mod:`ctypes`:
5+
6+
* :func:`~ctypes.DllGetClassObject` and :func:`~ctypes.DllCanUnloadNow`
7+
are deprecated and will be removed in Python 3.19.
8+
Their use for implementing in-process COM servers is unclear;
9+
discussion about replacements and use cases is welcome in :gh:`127369`.

Doc/library/ctypes.rst

Lines changed: 22 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2008,24 +2008,6 @@ Utility functions
20082008
.. audit-event:: ctypes.create_unicode_buffer init,size ctypes.create_unicode_buffer
20092009

20102010

2011-
.. function:: DllCanUnloadNow()
2012-
2013-
This function is a hook which allows implementing in-process
2014-
COM servers with ctypes. It is called from the DllCanUnloadNow function that
2015-
the _ctypes extension dll exports.
2016-
2017-
.. availability:: Windows
2018-
2019-
2020-
.. function:: DllGetClassObject()
2021-
2022-
This function is a hook which allows implementing in-process
2023-
COM servers with ctypes. It is called from the DllGetClassObject function
2024-
that the ``_ctypes`` extension dll exports.
2025-
2026-
.. availability:: Windows
2027-
2028-
20292011
.. function:: find_library(name)
20302012
:module: ctypes.util
20312013

@@ -2826,3 +2808,25 @@ Exceptions
28262808
.. availability:: Windows
28272809

28282810
.. versionadded:: next
2811+
2812+
2813+
Deprecated API
2814+
^^^^^^^^^^^^^^
2815+
2816+
.. function:: DllCanUnloadNow()
2817+
DllGetClassObject()
2818+
2819+
These functions are hooks which allowed implementing in-process
2820+
COM servers with ctypes.
2821+
To function, they required a third-party library, such as ``comtypes``,
2822+
along with third-party build/release tooling.
2823+
2824+
As of 2024, the state of tooling for in-process COM servers is unclear,
2825+
and is being discussed at :gh:`127369`.
2826+
2827+
Note that these functions will import and use the ``comtypes``
2828+
library, if it is installed.
2829+
2830+
.. availability:: Windows
2831+
2832+
.. deprecated-removed:: next 3.19

Doc/whatsnew/3.14.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -708,6 +708,8 @@ Deprecated
708708

709709
.. include:: ../deprecations/pending-removal-in-3.16.rst
710710

711+
.. include:: ../deprecations/pending-removal-in-3.19.rst
712+
711713
.. include:: ../deprecations/pending-removal-in-future.rst
712714

713715
Removed

Lib/ctypes/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import os as _os, sys as _sys
44
import types as _types
5+
import warnings
56

67
__version__ = "1.1.0"
78

@@ -564,6 +565,7 @@ def wstring_at(ptr, size=-1):
564565

565566
if _os.name == "nt": # COM stuff
566567
def DllGetClassObject(rclsid, riid, ppv):
568+
warnings._deprecated("ctypes.DllGetClassObject", remove=(3, 19))
567569
try:
568570
ccom = __import__("comtypes.server.inprocserver", globals(), locals(), ['*'])
569571
except ImportError:
@@ -572,12 +574,14 @@ def DllGetClassObject(rclsid, riid, ppv):
572574
return ccom.DllGetClassObject(rclsid, riid, ppv)
573575

574576
def DllCanUnloadNow():
577+
warnings._deprecated("ctypes.DllCanUnloadNow", remove=(3, 19))
575578
try:
576579
ccom = __import__("comtypes.server.inprocserver", globals(), locals(), ['*'])
577580
except ImportError:
578581
return 0 # S_OK
579582
return ccom.DllCanUnloadNow()
580583

584+
581585
from ctypes._endian import BigEndianStructure, LittleEndianStructure
582586
from ctypes._endian import BigEndianUnion, LittleEndianUnion
583587

Lib/test/test_ctypes/test_win32.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,10 @@
77
from ctypes import (CDLL, Structure, POINTER, pointer, sizeof, byref,
88
_pointer_type_cache,
99
c_void_p, c_char, c_int, c_long)
10+
import _ctypes
1011
from test import support
1112
from test.support import import_helper
13+
from test.support import warnings_helper
1214
from ._support import Py_TPFLAGS_DISALLOW_INSTANTIATION, Py_TPFLAGS_IMMUTABLETYPE
1315

1416

@@ -148,6 +150,28 @@ class RECT(Structure):
148150
# to not leak references, we must clean _pointer_type_cache
149151
del _pointer_type_cache[RECT]
150152

153+
@unittest.skipUnless(sys.platform == "win32", 'Windows-specific test')
154+
class InprocessCallbacks(unittest.TestCase):
155+
def test_deprecated_py_functions(self):
156+
with import_helper.isolated_modules():
157+
sys.modules['comtypes'] = None
158+
with warnings_helper.check_warnings((".*3.19", DeprecationWarning)):
159+
self.assertEqual(ctypes.DllCanUnloadNow(), 0)
160+
with warnings_helper.check_warnings((".*3.19", DeprecationWarning)):
161+
self.assertLess(ctypes.DllGetClassObject(0, 0, 0), 0)
162+
163+
def test_deprecated_dll_functions(self):
164+
"""C wrappers should warn, even with replaced hooks"""
165+
support.patch(self, ctypes, 'DllCanUnloadNow', lambda: 1234)
166+
support.patch(self, ctypes, 'DllGetClassObject', lambda *args: 1234)
167+
with import_helper.isolated_modules():
168+
sys.modules['comtypes'] = None
169+
ctypes_dll = ctypes.WinDLL(_ctypes.__file__)
170+
with warnings_helper.check_warnings((".*3.19", DeprecationWarning)):
171+
self.assertEqual(ctypes_dll.DllCanUnloadNow(), 1234)
172+
with warnings_helper.check_warnings((".*3.19", DeprecationWarning)):
173+
self.assertEqual(ctypes_dll.DllGetClassObject(0, 0, 0), 1234)
174+
151175

152176
if __name__ == '__main__':
153177
unittest.main()

Modules/_ctypes/callbacks.c

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -489,8 +489,22 @@ long Call_GetClassObject(REFCLSID rclsid, REFIID riid, LPVOID *ppv)
489489
long retval;
490490
static PyObject *context;
491491

492-
if (context == NULL)
492+
if (context == NULL) {
493493
context = PyUnicode_InternFromString("_ctypes.DllGetClassObject");
494+
}
495+
if (!context) {
496+
PyErr_WriteUnraisable(NULL);
497+
return E_FAIL;
498+
}
499+
500+
if (PyErr_WarnEx(
501+
PyExc_DeprecationWarning,
502+
"GetClassObject from _ctypes is deprecated and will be removed in Python 3.19",
503+
1) < 0)
504+
{
505+
PyErr_WriteUnraisable(context);
506+
return E_FAIL;
507+
}
494508

495509
func = _PyImport_GetModuleAttrString("ctypes", "DllGetClassObject");
496510
if (!func) {
@@ -565,8 +579,22 @@ long Call_CanUnloadNow(void)
565579
long retval;
566580
static PyObject *context;
567581

568-
if (context == NULL)
582+
if (context == NULL) {
569583
context = PyUnicode_InternFromString("_ctypes.DllCanUnloadNow");
584+
}
585+
if (!context) {
586+
PyErr_WriteUnraisable(NULL);
587+
return E_FAIL;
588+
}
589+
590+
if (PyErr_WarnEx(
591+
PyExc_DeprecationWarning,
592+
"DllCanUnloadNow from _ctypes is deprecated and will be removed in Python 3.19",
593+
1) < 0)
594+
{
595+
PyErr_WriteUnraisable(context);
596+
return E_FAIL;
597+
}
570598

571599
mod = PyImport_ImportModule("ctypes");
572600
if (!mod) {

0 commit comments

Comments
 (0)