Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,12 @@
`comtypes` allows you to define, call, and implement custom and dispatch-based COM interfaces in pure Python.

`comtypes` requires Windows and Python 3.9 or later.

- **Note about Python 3.15 and `enum` behavior**
Starting with Python 3.15, the internal handling of `IntFlag`(`Flag`) values is planned to change:
**Negative `IntFlag` members will be reinterpreted by masking them to the defined positive bit domain, instead of keeping their original negative literal values**.
This can affect enumeration types generated by `comtypes` from COM type libraries. Action is needed to maintain literal evaluation.
For details and ongoing discussion, see: [GH-894](https://github.com/enthought/comtypes/issues/894).
- Version [1.4.12](https://pypi.org/project/comtypes/1.4.12/) is the last version to support Python 3.8.
- Version <= [1.4.7](https://pypi.org/project/comtypes/1.4.7/) does not work with Python 3.13 as reported in [GH-618](https://github.com/enthought/comtypes/issues/618). Version [1.4.8](https://pypi.org/project/comtypes/1.4.8/) can work with Python 3.13.
- Version [1.4.6](https://pypi.org/project/comtypes/1.4.6/) is the last version to support Python 3.7.
Expand Down
16 changes: 16 additions & 0 deletions comtypes/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,22 @@
import logging
import sys

if sys.version_info >= (3, 15):
import warnings

_PYVER = f"{sys.version_info.major}.{sys.version_info.minor}"
warnings.warn(
(
f"You are running 'comtypes' on Python {_PYVER}, where the behavior of "
"enum types (such as IntFlag) may differ from Python <= 3.14.\n"
f"It is recommended to use a version compatible with Python {_PYVER}.\n"
"See: https://github.com/enthought/comtypes/issues/894"
),
FutureWarning,
stacklevel=2,
)


# HACK: Workaround for projects that depend on this package
# There should be several projects around the world that depend on this package
# and indirectly reference the symbols of `ctypes` from `comtypes`.
Expand Down
19 changes: 18 additions & 1 deletion comtypes/test/test_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,18 @@ def test_progid(self):
self.assertEqual(consts.TextCompare, Scripting.TextCompare)
self.assertEqual(consts.DatabaseCompare, Scripting.DatabaseCompare)

PY_3_15_ALPHA_BETA = (
sys.version_info.major == 3
and sys.version_info.minor == 15
and sys.version_info.releaselevel in ("alpha", "beta")
)
ENUMS_MESSAGE = (
"Starting from Python 3.15, negative members in `IntFlag` may "
"no longer be evaluated as literals.\nWe need to address this before "
"the release. See: https://github.com/enthought/comtypes/issues/894"
)

@ut.skipIf(PY_3_15_ALPHA_BETA, ENUMS_MESSAGE)
def test_enums_in_friendly_mod(self):
comtypes.client.GetModule("scrrun.dll")
comtypes.client.GetModule("msi.dll")
Expand All @@ -287,11 +299,16 @@ def test_enums_in_friendly_mod(self):
),
]:
for member in enumtype:
with self.subTest(enumtype=enumtype, member=member):
with self.subTest(
msg=self.ENUMS_MESSAGE,
enumtype=enumtype,
member=member,
):
self.assertIn(member.name, fadic)
self.assertEqual(fadic[member.name], member.value)
for member_name, member_value in fadic.items():
with self.subTest(
msg=self.ENUMS_MESSAGE,
enumtype=enumtype,
member_name=member_name,
member_value=member_value,
Expand Down
19 changes: 14 additions & 5 deletions comtypes/test/test_outparam.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
import logging
import unittest
from ctypes import c_wchar, c_wchar_p, cast, memmove, sizeof, wstring_at
from ctypes import (
c_wchar,
c_wchar_p,
cast,
create_unicode_buffer,
memmove,
sizeof,
wstring_at,
)
from unittest.mock import patch

from comtypes.malloc import CoGetMalloc, _CoTaskMemAlloc, _CoTaskMemFree
Expand Down Expand Up @@ -38,10 +46,11 @@ def comstring(text, typ=c_wchar_p):
class Test(unittest.TestCase):
@patch.object(c_wchar_p, "__ctypes_from_outparam__", from_outparam)
def test_c_char(self):
ptr = c_wchar_p("abc")
# The normal constructor does not allocate memory using `CoTaskMemAlloc`.
# Therefore, calling the patched `ptr.__ctypes_from_outparam__()` would
# attempt to free invalid memory, potentially leading to a crash.
# Allocate memory from the Python/C runtime heap.
# This ensures the address is valid but "unallocated" from COM.
buf = create_unicode_buffer("abc")
ptr = cast(buf, c_wchar_p)
# Confirm the memory is not managed by the COM task allocator.
self.assertEqual(malloc.DidAlloc(ptr), 0)

x = comstring("Hello, World")
Expand Down
4 changes: 2 additions & 2 deletions comtypes/test/test_puredispatch.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,13 +83,13 @@ def test_product_state(self):
)
# There is no product associated with the Null GUID.
pdcode = str(GUID())
expected = msi.MsiInstallState.msiInstallStateUnknown
expected = msi.msiInstallStateUnknown
self.assertEqual(expected, inst.ProductState(pdcode))
self.assertEqual(expected, inst.ProductState[pdcode])
# The `ProductState` property is a read-only property.
# https://learn.microsoft.com/en-us/windows/win32/msi/installer-productstate-property
with self.assertRaises(TypeError):
inst.ProductState[pdcode] = msi.MsiInstallState.msiInstallStateDefault # type: ignore
inst.ProductState[pdcode] = msi.msiInstallStateDefault # type: ignore
# NOTE: Named parameters are not yet implemented for the named property.
# See https://github.com/enthought/comtypes/issues/371
# TODO: After named parameters are supported, this will become a test to
Expand Down