Skip to content

Commit 5153b3a

Browse files
authored
Modify the metaclasses to ensure compatibility with Python 3.13 and earlier versions. (#636)
* Update `python-version`s in the CI pipeline. * Update `_make_safearray_type`. * Add early returns with `self` to `_coclass_meta.__new__`. * Add early returns with `self` to `_cominterface_meta.__new__`.
1 parent 112cd26 commit 5153b3a

4 files changed

Lines changed: 40 additions & 5 deletions

File tree

.github/workflows/autotest.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ jobs:
1010
strategy:
1111
matrix:
1212
os: [windows-latest]
13-
python-version: ['3.8', '3.9', '3.10', '3.11', '3.12']
13+
python-version: ['3.8', '3.9', '3.10', '3.11', '3.12', '3.13']
1414
architecture: ['x86', 'x64']
1515
npsupport: ['with npsupport', 'without npsupport']
1616
steps:
@@ -43,7 +43,7 @@ jobs:
4343
strategy:
4444
matrix:
4545
os: [windows-latest, windows-2019]
46-
python-version: ['3.8', '3.9', '3.10', '3.11', '3.12']
46+
python-version: ['3.8', '3.9', '3.10', '3.11', '3.12', '3.13']
4747
architecture: ['x86', 'x64']
4848
steps:
4949
- uses: actions/checkout@v4

comtypes/_meta.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,18 @@ def __new__(cls, name, bases, namespace):
6161
if "_reg_clsid_" in namespace:
6262
clsid = namespace["_reg_clsid_"]
6363
comtypes.com_coclass_registry[str(clsid)] = self
64+
65+
# `_coclass_pointer_meta` is a subclass inherited from `_coclass_meta`.
66+
# In other words, when the `__new__` method of this metaclass is called, an
67+
# instance of `_coclass_pointer_meta` might be created and assigned to `self`.
68+
if isinstance(self, _coclass_pointer_meta):
69+
# `self` is the `_coclass_pointer_meta` type or a `POINTER(coclass)` type.
70+
# Prevent creating/registering a pointer to a pointer (to a pointer...),
71+
# or specifying the metaclass type instance in the `bases` parameter when
72+
# instantiating it, which would lead to infinite recursion.
73+
# Depending on a version or revision of Python, this may be essential.
74+
return self
75+
6476
PTR = _coclass_pointer_meta(
6577
f"POINTER({self.__name__})",
6678
(self, c_void_p),

comtypes/_post_coinit/unknwn.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,31 @@ def __new__(cls, name, bases, namespace):
7676
if dispmethods is not None:
7777
self._disp_methods_ = dispmethods
7878

79+
# ` _compointer_meta` is a subclass inherited from `_cominterface_meta`.
80+
# `_compointer_base` uses `_compointer_meta` as its metaclass.
81+
# In other words, when the `__new__` method of this metaclass is called,
82+
# `_compointer_base` type or an subclass of it might be created and assigned
83+
# to `self`.
84+
if bases == (c_void_p,):
85+
# `self` is the `_compointer_base` type.
86+
# On some versions of Python, `_compointer_base` is not yet available in
87+
# the accessible namespace at this point in its initialization, and
88+
# referencing it could raise a `NameError`.
89+
# This metaclass is intended to be used only as a base class for defining
90+
# the `_compointer_meta` or as the metaclass for the `IUnknown`, so the
91+
# situation where the `bases` parameter is `(c_void_p,)` is limited to when
92+
# the `_compointer_meta` is specified as the metaclass of the
93+
# `_compointer_base`.
94+
# Prevent specifying the metaclass type instance in the `bases` parameter
95+
# when instantiating it, as this would lead to infinite recursion.
96+
return self
97+
if issubclass(self, _compointer_base):
98+
# `self` is a `POINTER(interface)` type.
99+
# Prevent creating/registering a pointer to a pointer (to a pointer...),
100+
# which would lead to infinite recursion.
101+
# Depending on a version or revision of Python, this may be essential.
102+
return self
103+
79104
# If we sublass a COM interface, for example:
80105
#
81106
# class IDispatch(IUnknown):

comtypes/safearray.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -81,9 +81,7 @@ def _make_safearray_type(itemtype):
8181
)
8282

8383
meta = type(_safearray.tagSAFEARRAY)
84-
sa_type = meta.__new__(
85-
meta, "SAFEARRAY_%s" % itemtype.__name__, (_safearray.tagSAFEARRAY,), {}
86-
)
84+
sa_type = meta(f"SAFEARRAY_{itemtype.__name__}", (_safearray.tagSAFEARRAY,), {})
8785

8886
try:
8987
vartype = _ctype_to_vartype[itemtype]

0 commit comments

Comments
 (0)