diff --git a/comtypes/tools/codegenerator/codegenerator.py b/comtypes/tools/codegenerator/codegenerator.py index a7172fc1..b8f44961 100644 --- a/comtypes/tools/codegenerator/codegenerator.py +++ b/comtypes/tools/codegenerator/codegenerator.py @@ -85,28 +85,97 @@ def adjust_blank( yield self.stream self.last_item = item - def generate(self, item: Any) -> None: + def generate( + self, + item: _UnionT[ + tlbparser.ParsedType, + tlbparser.SpecType, + typedesc.EnumValue, + typedesc.StructureHead, + typedesc.StructureBody, + typedesc.ComInterfaceHead, + typedesc.DispInterfaceHead, + typedesc.ComInterfaceBody, + typedesc.DispInterfaceBody, + ], + ) -> None: if item in self.done: return if self._is_interface_typedesc(item): self._define_interface(item) return if isinstance(item, typedesc.StructureHead): - name = getattr(item.struct, "name", None) + name = item.struct.name + elif isinstance( + item, + ( + typedesc.External, + typedesc.PointerType, + typedesc.ArrayType, + typedesc.SAFEARRAYType, + typedesc.StructureBody, + ), + ): + name = None else: - name = getattr(item, "name", None) - if name in self.known_symbols: + name = item.name + if name is not None and name in self.known_symbols: self.imports.add(name, symbols=self.known_symbols) self.done.add(item) if isinstance(item, typedesc.Structure): self.done.add(item.get_head()) self.done.add(item.get_body()) return - mth = getattr(self, type(item).__name__) # to avoid infinite recursion, we have to mark it as done # before actually generating the code. self.done.add(item) - mth(item) + self._dispatch_by(item) + + def _dispatch_by(self, item: Any) -> None: + if isinstance(item, typedesc.ArrayType): + self.ArrayType(item) + elif isinstance(item, typedesc.EnumValue): + self.EnumValue(item) + elif isinstance(item, typedesc.Enumeration): + self.Enumeration(item) + elif isinstance(item, typedesc.Typedef): + self.Typedef(item) + elif isinstance(item, typedesc.FundamentalType): + self.FundamentalType(item) + elif isinstance(item, typedesc.StructureHead): + self.StructureHead(item) + elif isinstance(item, typedesc.Structure): + self.Structure(item) + elif isinstance(item, typedesc.Union): + self.Union(item) + elif isinstance(item, typedesc.StructureBody): + self.StructureBody(item) + elif isinstance(item, typedesc.TypeLib): + self.TypeLib(item) + elif isinstance(item, typedesc.External): + self.External(item) + elif isinstance(item, typedesc.Constant): + self.Constant(item) + elif isinstance(item, typedesc.SAFEARRAYType): + self.SAFEARRAYType(item) + elif isinstance(item, typedesc.PointerType): + self.PointerType(item) + elif isinstance(item, typedesc.CoClass): + self.CoClass(item) + elif isinstance(item, typedesc.ComInterface): + self.ComInterface(item) + elif isinstance(item, typedesc.ComInterfaceHead): + self.ComInterfaceHead(item) + elif isinstance(item, typedesc.ComInterfaceBody): + self.ComInterfaceBody(item) + elif isinstance(item, typedesc.DispInterface): + self.DispInterface(item) + elif isinstance(item, typedesc.DispInterfaceHead): + self.DispInterfaceHead(item) + elif isinstance(item, typedesc.DispInterfaceBody): + self.DispInterfaceBody(item) + else: + raise TypeError(f"Unsupported: {type(item).__name__!r}") def generate_all(self, items: Iterable[tlbparser.ParsedType]) -> None: for item in items: @@ -382,16 +451,16 @@ def StructureBody(self, body: typedesc.StructureBody) -> None: fields: list[typedesc.Field] = [] methods: list[typedesc.Method] = [] for m in body.struct.members: - if type(m) is typedesc.Field: + if isinstance(m, typedesc.Field): fields.append(m) - if type(m.typ) is typedesc.Typedef: + if isinstance(m.typ, typedesc.Typedef): self.generate(get_real_type(m.typ)) self.generate(m.typ) - elif type(m) is typedesc.Method: + elif isinstance(m, typedesc.Method): methods.append(m) self.generate(m.returns) self.generate_all(m.iterArgTypes()) - elif type(m) is typedesc.Constructor: + elif isinstance(m, typedesc.Constructor): pass # we don't need _pack_ on Unions (I hope, at least), and not @@ -528,17 +597,17 @@ def SAFEARRAYType(self, sa: typedesc.SAFEARRAYType) -> None: self.imports.add("comtypes.automation", "_midlSAFEARRAY") def PointerType(self, tp: typedesc.PointerType) -> None: - if type(tp.typ) is typedesc.ComInterface: + if isinstance(tp.typ, typedesc.ComInterface): # this defines the class self.generate(tp.typ.get_head()) # this defines the _methods_ self.more.add(tp.typ) - elif type(tp.typ) is typedesc.PointerType: + elif isinstance(tp.typ, typedesc.PointerType): self.generate(tp.typ) - elif type(tp.typ) in (typedesc.Union, typedesc.Structure): + elif isinstance(tp.typ, (typedesc.Union, typedesc.Structure)): self.generate(tp.typ.get_head()) self.more.add(tp.typ) - elif type(tp.typ) is typedesc.Typedef: + elif isinstance(tp.typ, typedesc.Typedef): self.generate(tp.typ) else: self.generate(tp.typ) @@ -610,8 +679,7 @@ def _define_interface(self, item: _InterfaceTypeDesc) -> None: else: raise TypeError self.done.add(item) # to avoid infinite recursion. - mth = getattr(self, type(item).__name__) - mth(item) + self._dispatch_by(item) def ComInterface(self, itf: typedesc.ComInterface) -> None: self.generate(itf.get_head()) diff --git a/comtypes/tools/tlbparser.py b/comtypes/tools/tlbparser.py index 74745766..123b9838 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, TypeVar, overload +from typing import Any, Optional, TypeVar from typing import Union as _UnionT from comtypes import automation, typeinfo @@ -16,14 +16,9 @@ ################################ _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): +def PTR(typ: _PT) -> typedesc.PointerType[_PT]: return typedesc.PointerType(typ, sizeof(c_void_p) * 8, alignment(c_void_p) * 8) @@ -57,8 +52,34 @@ def PTR(typ): bases=[], size=sizeof(automation.VARIANT) * 8, ) -IDISPATCH_type = typedesc.Typedef("IDispatch", None) -IUNKNOWN_type = typedesc.Typedef("IUnknown", None) + +# These are simplified definitions of `IUnknown` and `IDispatch` from `stdole`. +# Their full definitions include method arguments and IDL flags. However, these +# basic interfaces are replaced with suitable definitions for each use case, so +# they do not need to be modeled perfectly here. Even a complete model would not +# be used, and adding more redundant fields would make maintenance harder. +IUNKNOWN_type = typedesc.ComInterface( + "IUnknown", None, "{00000000-0000-0000-C000-000000000046}", [], None +) +IUNKNOWN_type.extend_members( + [ + typedesc.ComMethod(1, 1610612736, "QueryInterface", HRESULT_type, [], None), + typedesc.ComMethod(1, 1610612737, "AddRef", ulong_type, [], None), + typedesc.ComMethod(1, 1610612738, "Release", ulong_type, [], None), + ] +) +IDISPATCH_type = typedesc.ComInterface( + "IDispatch", IUNKNOWN_type, "{00020400-0000-0000-C000-000000000046}", [], None +) +IDISPATCH_type.extend_members( + [ + typedesc.ComMethod(1, 1610678272, "GetTypeInfoCount", HRESULT_type, [], None), + typedesc.ComMethod(1, 1610678273, "GetTypeInfo", HRESULT_type, [], None), + typedesc.ComMethod(1, 1610678274, "GetIDsOfNames", HRESULT_type, [], None), + typedesc.ComMethod(1, 1610678275, "Invoke", HRESULT_type, [], None), + ] +) + DECIMAL_type = typedesc.Structure( "DECIMAL", align=alignment(automation.DECIMAL) * 8, @@ -123,7 +144,6 @@ def midlSAFEARRAY(typ: _PT) -> typedesc.SAFEARRAYType[_PT]: typedesc.ComInterface, typedesc.DispInterface, typedesc.Typedef["_UnionT[ParsedType, SpecType]"], - _TypeDefNone, typedesc.Structure, typedesc.Union, typedesc.Enumeration, diff --git a/comtypes/tools/typedesc.py b/comtypes/tools/typedesc.py index 5a218822..70121ac8 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, Generic, Optional, TypeVar +from typing import Any, Generic, Optional, TypeVar, final from typing import Union as _UnionT from comtypes import typeinfo @@ -34,6 +34,7 @@ _TD = TypeVar("_TD") +@final class TypeLib: def __init__( self, name: str, guid: str, major: int, minor: int, doc: Optional[str] = None @@ -48,6 +49,7 @@ def __repr__(self): return f"" +@final class Constant: def __init__( self, @@ -62,6 +64,7 @@ def __init__( self.doc = doc +@final class External: def __init__( self, @@ -85,12 +88,14 @@ def get_head(self) -> "External": return self +@final class SAFEARRAYType(Generic[_TD]): def __init__(self, typ: _TD) -> None: self.typ = typ self.align = self.size = ctypes.sizeof(ctypes.c_void_p) * 8 +@final class ComMethod: # custom COM method, parsed from typelib def __init__( @@ -116,6 +121,7 @@ def add_argument( self.arguments.append((typ, name, idlflags, default)) +@final class DispMethod: # dispatchable COM method, parsed from typelib def __init__( @@ -141,6 +147,7 @@ def add_argument( self.arguments.append((typ, name, idlflags, default)) +@final class DispProperty: # dispatchable COM property, parsed from typelib def __init__( @@ -153,16 +160,19 @@ def __init__( self.doc = doc +@final class DispInterfaceHead: def __init__(self, itf: "DispInterface") -> None: self.itf = itf +@final class DispInterfaceBody: def __init__(self, itf: "DispInterface") -> None: self.itf = itf +@final class DispInterface: def __init__( self, @@ -191,16 +201,19 @@ def get_head(self) -> DispInterfaceHead: return self.itf_head +@final class ComInterfaceHead: def __init__(self, itf: "ComInterface") -> None: self.itf = itf +@final class ComInterfaceBody: def __init__(self, itf: "ComInterface") -> None: self.itf = itf +@final class ComInterface: def __init__( self, @@ -233,6 +246,7 @@ def get_head(self) -> ComInterfaceHead: _Interface = _UnionT[ComInterface, DispInterface] +@final class CoClass: def __init__( self, diff --git a/comtypes/tools/typedesc_base.py b/comtypes/tools/typedesc_base.py index 2fd6797a..1cd1352a 100644 --- a/comtypes/tools/typedesc_base.py +++ b/comtypes/tools/typedesc_base.py @@ -1,5 +1,5 @@ # typedesc.py - classes representing C type descriptions -from typing import Any, Generic, Optional, SupportsInt, TypeVar +from typing import Any, Generic, Optional, SupportsInt, TypeVar, final from typing import Union as _UnionT _TD = TypeVar("_TD") @@ -72,6 +72,7 @@ def __init__(self, name, returns, attributes, extern): self.extern = extern +@final class Constructor(_HasArgs): location = None @@ -98,6 +99,7 @@ def __init__(self, returns, attributes): self.attributes = attributes +@final class Method(_HasArgs): location = None @@ -107,6 +109,7 @@ def __init__(self, name, returns): self.returns = returns +@final class FundamentalType: location = None @@ -116,6 +119,7 @@ def __init__(self, name: str, size: int, align: int) -> None: self.align = int(align) +@final class PointerType(Generic[_TD]): location = None @@ -125,6 +129,7 @@ def __init__(self, typ: _TD, size: int, align: int) -> None: self.align = int(align) +@final class Typedef(Generic[_TD]): location = None @@ -133,6 +138,7 @@ def __init__(self, name: str, typ: _TD) -> None: self.typ = typ +@final class ArrayType(Generic[_TD]): location = None @@ -142,6 +148,7 @@ def __init__(self, typ: _TD, min: int, max: int) -> None: self.max = max +@final class StructureHead: location = None @@ -149,6 +156,7 @@ def __init__(self, struct: "_Struct_Union_Base") -> None: self.struct = struct +@final class StructureBody: location = None @@ -178,6 +186,7 @@ def get_head(self) -> StructureHead: return self.struct_head +@final class Structure(_Struct_Union_Base): def __init__( self, @@ -200,6 +209,7 @@ def __init__( super().__init__() +@final class Union(_Struct_Union_Base): def __init__( self, @@ -222,6 +232,7 @@ def __init__( super().__init__() +@final class Field: def __init__( self, name: str, typ: Any, bits: Optional[Any], offset: SupportsInt @@ -239,6 +250,7 @@ def __init__(self, typ, const, volatile): self.volatile = volatile +@final class Enumeration: location = None @@ -252,6 +264,7 @@ def add_value(self, v: "EnumValue") -> None: self.values.append(v) +@final class EnumValue: def __init__(self, name: str, value: int, enumeration: Enumeration) -> None: self.name = name