From fd0b39fc4481327760528dee35665facfc3a0446 Mon Sep 17 00:00:00 2001 From: junkmd Date: Thu, 22 Oct 2026 09:29:19 +0900 Subject: [PATCH 1/2] feat: Update type hint imports for Python version compatibility and enhance type checking in code generator. Since `typedesc.*Interface*` cannot be mixins for each other, `TypeIs` is more appropriate than `TypeGuard` for type narrowing. --- comtypes/hints.pyi | 6 ++++-- comtypes/tools/codegenerator/codegenerator.py | 10 ++++++---- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/comtypes/hints.pyi b/comtypes/hints.pyi index bc0eee6b8..b64084fab 100644 --- a/comtypes/hints.pyi +++ b/comtypes/hints.pyi @@ -12,16 +12,18 @@ from typing import Union as _UnionT if sys.version_info >= (3, 10): from typing import Concatenate, ParamSpec, TypeAlias - from typing import TypeGuard as TypeGuard else: from typing_extensions import Concatenate, ParamSpec, TypeAlias - from typing_extensions import TypeGuard as TypeGuard if sys.version_info >= (3, 11): from typing import Self as Self from typing import Unpack as Unpack else: from typing_extensions import Self as Self from typing_extensions import Unpack as Unpack +if sys.version_info >= (3, 13): + from typing import TypeIs as TypeIs +else: + from typing_extensions import TypeIs as TypeIs import comtypes from comtypes import IUnknown as IUnknown, COMObject as COMObject, GUID as GUID diff --git a/comtypes/tools/codegenerator/codegenerator.py b/comtypes/tools/codegenerator/codegenerator.py index a3a0c8998..a7172fc1b 100644 --- a/comtypes/tools/codegenerator/codegenerator.py +++ b/comtypes/tools/codegenerator/codegenerator.py @@ -8,7 +8,7 @@ import textwrap import warnings from collections.abc import Iterable, Iterator, Mapping, Sequence -from typing import Any, Literal, Optional +from typing import TYPE_CHECKING, Any, Literal, Optional from typing import Union as _UnionT import comtypes @@ -25,6 +25,10 @@ ) from comtypes.tools.codegenerator.modulenamer import name_wrapper_module +if TYPE_CHECKING: + from comtypes import hints # type: ignore + + version = comtypes.__version__ logger = logging.getLogger(__name__) @@ -569,9 +573,7 @@ def CoClass(self, coclass: typedesc.CoClass) -> None: self.names.add(coclass.name) - def _is_interface_typedesc( - self, item: Any - ) -> "comtypes.hints.TypeGuard[_InterfaceTypeDesc]": + def _is_interface_typedesc(self, item: Any) -> "hints.TypeIs[_InterfaceTypeDesc]": return isinstance( item, ( From 94c8ad9d2cfa43fce518b9586fb9709ca98e7027 Mon Sep 17 00:00:00 2001 From: junkmd Date: Thu, 22 Oct 2026 09:29:48 +0900 Subject: [PATCH 2/2] refactor: Make type containers generic and enhance type hints. - Make `Typedef`, `PointerType`, `ArrayType`, and `SAFEARRAYType` generic to facilitate future refactoring in parser and codegenerator. - Add `overload`s and type annotations to `PTR`, `midlSAFEARRAY`, and `get_real_type`. - Note: `CvQualifiedType` is not genericized as it is currently unused and uninstantiated. --- comtypes/tools/codegenerator/helpers.py | 8 +++++++- comtypes/tools/tlbparser.py | 19 +++++++++++++------ comtypes/tools/typedesc.py | 8 +++++--- comtypes/tools/typedesc_base.py | 16 +++++++++------- 4 files changed, 34 insertions(+), 17 deletions(-) diff --git a/comtypes/tools/codegenerator/helpers.py b/comtypes/tools/codegenerator/helpers.py index 43dba5685..491496ba9 100644 --- a/comtypes/tools/codegenerator/helpers.py +++ b/comtypes/tools/codegenerator/helpers.py @@ -1,11 +1,13 @@ import keyword from collections.abc import Iterator -from typing import Any +from typing import Any, TypeVar, overload from typing import Union as _UnionT from comtypes.tools import typedesc from comtypes.tools.codegenerator.modulenamer import name_wrapper_module +_TD = TypeVar("_TD") + class lcid: def __repr__(self): @@ -53,6 +55,10 @@ def __repr__(self): } +@overload +def get_real_type(tp: typedesc.Typedef[_TD]) -> _TD: ... +@overload +def get_real_type(tp: _TD) -> _TD: ... def get_real_type(tp): if type(tp) is typedesc.Typedef: return get_real_type(tp.typ) diff --git a/comtypes/tools/tlbparser.py b/comtypes/tools/tlbparser.py index c4d9ce51b..747457665 100644 --- a/comtypes/tools/tlbparser.py +++ b/comtypes/tools/tlbparser.py @@ -4,7 +4,7 @@ from collections.abc import Mapping from ctypes import alignment, c_void_p, sizeof, windll from ctypes.wintypes import MAX_PATH -from typing import Any, Optional +from typing import Any, Optional, TypeVar, overload from typing import Union as _UnionT from comtypes import automation, typeinfo @@ -15,8 +15,14 @@ ################################ +_PT = TypeVar("_PT", bound=_UnionT["ParsedType", "SpecType"]) +_TypeDefNone = typedesc.Typedef[None] +@overload +def PTR(typ: _PT) -> typedesc.PointerType[_PT]: ... +@overload +def PTR(typ: _TypeDefNone) -> typedesc.PointerType[_TypeDefNone]: ... def PTR(typ): return typedesc.PointerType(typ, sizeof(c_void_p) * 8, alignment(c_void_p) * 8) @@ -62,7 +68,7 @@ def PTR(typ): ) -def midlSAFEARRAY(typ): +def midlSAFEARRAY(typ: _PT) -> typedesc.SAFEARRAYType[_PT]: return typedesc.SAFEARRAYType(typ) @@ -116,7 +122,8 @@ def midlSAFEARRAY(typ): typedesc.CoClass, typedesc.ComInterface, typedesc.DispInterface, - typedesc.Typedef, + typedesc.Typedef["_UnionT[ParsedType, SpecType]"], + _TypeDefNone, typedesc.Structure, typedesc.Union, typedesc.Enumeration, @@ -124,9 +131,9 @@ def midlSAFEARRAY(typ): SpecType = _UnionT[ typedesc.FundamentalType, - typedesc.PointerType, - typedesc.ArrayType, - typedesc.SAFEARRAYType, + typedesc.PointerType["_UnionT[ParsedType, SpecType]"], + typedesc.ArrayType["_UnionT[ParsedType, SpecType]"], + typedesc.SAFEARRAYType["_UnionT[ParsedType, SpecType]"], ] ################################################################ diff --git a/comtypes/tools/typedesc.py b/comtypes/tools/typedesc.py index 302ff80a7..5a218822f 100644 --- a/comtypes/tools/typedesc.py +++ b/comtypes/tools/typedesc.py @@ -3,7 +3,7 @@ import ctypes from collections.abc import Sequence -from typing import Any, Optional +from typing import Any, Generic, Optional, TypeVar from typing import Union as _UnionT from comtypes import typeinfo @@ -31,6 +31,8 @@ from comtypes.tools.typedesc_base import Variable as Variable from comtypes.typeinfo import TLIBATTR, ITypeLib +_TD = TypeVar("_TD") + class TypeLib: def __init__( @@ -83,8 +85,8 @@ def get_head(self) -> "External": return self -class SAFEARRAYType: - def __init__(self, typ: Any) -> None: +class SAFEARRAYType(Generic[_TD]): + def __init__(self, typ: _TD) -> None: self.typ = typ self.align = self.size = ctypes.sizeof(ctypes.c_void_p) * 8 diff --git a/comtypes/tools/typedesc_base.py b/comtypes/tools/typedesc_base.py index fb9ba9425..2fd6797a1 100644 --- a/comtypes/tools/typedesc_base.py +++ b/comtypes/tools/typedesc_base.py @@ -1,7 +1,9 @@ # typedesc.py - classes representing C type descriptions -from typing import Any, Optional, SupportsInt +from typing import Any, Generic, Optional, SupportsInt, TypeVar from typing import Union as _UnionT +_TD = TypeVar("_TD") + class Argument: "a Parameter in the argument list of a callable (Function, Method, ...)" @@ -114,27 +116,27 @@ def __init__(self, name: str, size: int, align: int) -> None: self.align = int(align) -class PointerType: +class PointerType(Generic[_TD]): location = None - def __init__(self, typ, size, align): + def __init__(self, typ: _TD, size: int, align: int) -> None: self.typ = typ self.size = int(size) self.align = int(align) -class Typedef: +class Typedef(Generic[_TD]): location = None - def __init__(self, name, typ): + def __init__(self, name: str, typ: _TD) -> None: self.name = name self.typ = typ -class ArrayType: +class ArrayType(Generic[_TD]): location = None - def __init__(self, typ: Any, min: int, max: int) -> None: + def __init__(self, typ: _TD, min: int, max: int) -> None: self.typ = typ self.min = min self.max = max