Skip to content

Commit db0931d

Browse files
authored
Add type annotations to comtypes.automation (#374)
* add type annotations * improve `VARIANT.__repr__`
1 parent 5f5429f commit db0931d

3 files changed

Lines changed: 76 additions & 4 deletions

File tree

comtypes/automation.py

Lines changed: 67 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@
77
from ctypes import *
88
from ctypes import _Pointer
99
from _ctypes import CopyComPointer
10-
from comtypes import IUnknown, GUID, IID, STDMETHOD, BSTR, COMMETHOD, COMError
10+
from comtypes import (
11+
BSTR, COMError, COMMETHOD, GUID, IID, IUnknown, STDMETHOD, TYPE_CHECKING,
12+
)
1113
from comtypes.hresult import *
1214
import comtypes.patcher
1315
import comtypes
@@ -19,6 +21,12 @@ class _safearray(object):
1921

2022
from ctypes.wintypes import DWORD, LONG, UINT, VARIANT_BOOL, WCHAR, WORD
2123

24+
if TYPE_CHECKING:
25+
from typing import (
26+
Any, Callable, ClassVar, List, Optional, Tuple, Union as _UnionT,
27+
)
28+
from comtypes import hints
29+
2230

2331
if sys.version_info >= (3, 0):
2432
int_types = (int, )
@@ -149,6 +157,13 @@ def as_decimal(self):
149157
# The VARIANT structure is a good candidate for implementation in a C
150158
# helper extension. At least the get/set methods.
151159
class tagVARIANT(Structure):
160+
if TYPE_CHECKING:
161+
vt = hints.AnnoField() # type: int
162+
_ = hints.AnnoField() # type: U_VARIANT1.__tagVARIANT.U_VARIANT2
163+
null = hints.AnnoField() # type: ClassVar[VARIANT]
164+
empty = hints.AnnoField() # type: ClassVar[VARIANT]
165+
missing = hints.AnnoField() # type: ClassVar[VARIANT]
166+
152167
class U_VARIANT1(Union):
153168
class __tagVARIANT(Structure):
154169
# The C Header file defn of VARIANT is much more complicated, but
@@ -205,6 +220,12 @@ def __del__(self):
205220
def __repr__(self):
206221
if self.vt & VT_BYREF:
207222
return "VARIANT(vt=0x%x, byref(%r))" % (self.vt, self[0])
223+
elif self is type(self).null:
224+
return "VARIANT.null"
225+
elif self is type(self).empty:
226+
return "VARIANT.empty"
227+
elif self is type(self).missing:
228+
return "VARIANT.missing"
208229
return "VARIANT(vt=0x%x, %r)" % (self.vt, self.value)
209230

210231
@classmethod
@@ -650,6 +671,17 @@ def Next(self, celt):
650671

651672

652673
class tagEXCEPINFO(Structure):
674+
if TYPE_CHECKING:
675+
wCode = hints.AnnoField() # type: int
676+
wReserved = hints.AnnoField() # type: int
677+
bstrSource = hints.AnnoField() # type: str
678+
bstrDescription = hints.AnnoField() # type: str
679+
bstrHelpFile = hints.AnnoField() # type: str
680+
dwHelpContext = hints.AnnoField() # type: int
681+
pvReserved = hints.AnnoField() # type: Optional[int]
682+
pfnDeferredFillIn = hints.AnnoField() # type: Optional[int]
683+
scode = hints.AnnoField() # type: int
684+
653685
def __repr__(self):
654686
return "<EXCEPINFO %s>" % \
655687
((self.wCode, self.bstrSource, self.bstrDescription, self.bstrHelpFile, self.dwHelpContext,
@@ -669,6 +701,11 @@ def __repr__(self):
669701
EXCEPINFO = tagEXCEPINFO
670702

671703
class tagDISPPARAMS(Structure):
704+
if TYPE_CHECKING:
705+
rgvarg = hints.AnnoField() # type: Array[VARIANT]
706+
rgdispidNamedArgs = hints.AnnoField() # type: _Pointer[DISPID]
707+
cArgs = hints.AnnoField() # type: int
708+
cNamedArgs = hints.AnnoField() # type: int
672709
_fields_ = [
673710
# C:/Programme/gccxml/bin/Vc71/PlatformSDK/oaidl.h 696
674711
('rgvarg', POINTER(VARIANTARG)),
@@ -691,17 +728,39 @@ def __del__(self):
691728
DISPID_DESTRUCTOR = -7
692729
DISPID_COLLECT = -8
693730

731+
732+
if TYPE_CHECKING:
733+
RawGetIDsOfNamesFunc = Callable[
734+
[_byref_type, Array[c_wchar_p], int, int, Array[DISPID]], int,
735+
]
736+
RawInvokeFunc = Callable[
737+
[
738+
int, _byref_type, int, int, # dispIdMember, riid, lcid, wFlags
739+
_UnionT[_byref_type, DISPPARAMS], # *pDispParams
740+
_UnionT[_byref_type, VARIANT], # pVarResult
741+
_UnionT[_byref_type, EXCEPINFO, None], # pExcepInfo
742+
_UnionT[_byref_type, c_uint], # puArgErr
743+
],
744+
int
745+
]
746+
694747
class IDispatch(IUnknown):
748+
if TYPE_CHECKING:
749+
_disp_methods_ = hints.AnnoField() # type: ClassVar[List[comtypes._DispMemberSpec]]
750+
_GetTypeInfo = hints.AnnoField() # type: Callable[[int, int], IUnknown]
751+
__com_GetIDsOfNames = hints.AnnoField() # type: RawGetIDsOfNamesFunc
752+
__com_Invoke = hints.AnnoField() # type: RawInvokeFunc
753+
695754
_iid_ = GUID("{00020400-0000-0000-C000-000000000046}")
696755
_methods_ = [
697756
COMMETHOD([], HRESULT, 'GetTypeInfoCount',
698757
(['out'], POINTER(UINT) ) ),
699758
COMMETHOD([], HRESULT, 'GetTypeInfo',
700759
(['in'], UINT, 'index'),
701760
(['in'], LCID, 'lcid', 0),
702-
## Normally, we would declare this parameter in this way:
703-
## (['out'], POINTER(POINTER(ITypeInfo)) ) ),
704-
## but we cannot import comtypes.typeinfo at the top level (recursive imports!).
761+
# Normally, we would declare this parameter in this way:
762+
# (['out'], POINTER(POINTER(ITypeInfo)) ) ),
763+
# but we cannot import comtypes.typeinfo at the top level (recursive imports!).
705764
(['out'], POINTER(POINTER(IUnknown)) ) ),
706765
STDMETHOD(HRESULT, 'GetIDsOfNames', [POINTER(IID), POINTER(c_wchar_p),
707766
UINT, LCID, POINTER(DISPID)]),
@@ -711,12 +770,14 @@ class IDispatch(IUnknown):
711770
]
712771

713772
def GetTypeInfo(self, index, lcid=0):
773+
# type: (int, int) -> hints.ITypeInfo
714774
"""Return type information. Index 0 specifies typeinfo for IDispatch"""
715775
import comtypes.typeinfo
716776
result = self._GetTypeInfo(index, lcid)
717777
return result.QueryInterface(comtypes.typeinfo.ITypeInfo)
718778

719779
def GetIDsOfNames(self, *names, **kw):
780+
# type: (str, Any) -> List[int]
720781
"""Map string names to integer ids."""
721782
lcid = kw.pop("lcid", 0)
722783
assert not kw
@@ -726,6 +787,7 @@ def GetIDsOfNames(self, *names, **kw):
726787
return ids[:]
727788

728789
def _invoke(self, memid, invkind, lcid, *args):
790+
# type: (int, int, int, Any) -> Any
729791
var = VARIANT()
730792
argerr = c_uint()
731793
dp = DISPPARAMS()
@@ -747,6 +809,7 @@ def _invoke(self, memid, invkind, lcid, *args):
747809
return var._get_value(dynamic=True)
748810

749811
def Invoke(self, dispid, *args, **kw):
812+
# type: (int, Any, Any) -> Any
750813
"""Invoke a method or property."""
751814

752815
# Memory management in Dispatch::Invoke calls:

comtypes/hints.pyi

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@ from typing import (
44
)
55

66
# symbols those what might occur recursive imports in runtime.
7+
from comtypes.automation import IDispatch as IDispatch, VARIANT as VARIANT
78
from comtypes.server import IClassFactory as IClassFactory
9+
from comtypes.typeinfo import ITypeInfo as ITypeInfo
810

911

1012
def AnnoField() -> Any:

comtypes/test/test_variant.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,13 @@ def test_byref(self):
216216
variable.value = 96
217217
self.assertEqual(v[0], 96)
218218

219+
def test_repr(self):
220+
self.assertEqual(repr(VARIANT(c_int(42))), "VARIANT(vt=0x3, 42)")
221+
self.assertEqual(repr(VARIANT(byref(c_int(42)))), "VARIANT(vt=0x4003, byref(42))")
222+
self.assertEqual(repr(VARIANT.empty), "VARIANT.empty")
223+
self.assertEqual(repr(VARIANT.null), "VARIANT.null")
224+
self.assertEqual(repr(VARIANT.missing), "VARIANT.missing")
225+
219226

220227
class ArrayTest(unittest.TestCase):
221228
def test_double(self):

0 commit comments

Comments
 (0)