comtypes 1.4.8__zip → 1.4.9__zip

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (146) hide show
  1. {comtypes-1.4.8 → comtypes-1.4.9}/CHANGES.txt +21 -0
  2. {comtypes-1.4.8/comtypes.egg-info → comtypes-1.4.9}/PKG-INFO +2 -2
  3. {comtypes-1.4.8 → comtypes-1.4.9}/README.md +1 -1
  4. {comtypes-1.4.8 → comtypes-1.4.9}/comtypes/GUID.py +6 -5
  5. {comtypes-1.4.8 → comtypes-1.4.9}/comtypes/__init__.py +16 -5
  6. comtypes-1.4.9/comtypes/_comobject.py +507 -0
  7. {comtypes-1.4.8 → comtypes-1.4.9}/comtypes/_memberspec.py +80 -8
  8. comtypes-1.4.9/comtypes/_post_coinit/_cominterface_meta_patcher.py +153 -0
  9. {comtypes-1.4.8 → comtypes-1.4.9}/comtypes/_post_coinit/misc.py +1 -1
  10. {comtypes-1.4.8 → comtypes-1.4.9}/comtypes/_post_coinit/unknwn.py +25 -171
  11. {comtypes-1.4.8 → comtypes-1.4.9}/comtypes/_safearray.py +1 -2
  12. comtypes-1.4.9/comtypes/_vtbl.py +448 -0
  13. {comtypes-1.4.8 → comtypes-1.4.9}/comtypes/automation.py +36 -45
  14. {comtypes-1.4.8 → comtypes-1.4.9}/comtypes/client/_code_cache.py +4 -4
  15. {comtypes-1.4.8 → comtypes-1.4.9}/comtypes/client/_constants.py +1 -1
  16. {comtypes-1.4.8 → comtypes-1.4.9}/comtypes/client/_events.py +1 -1
  17. {comtypes-1.4.8 → comtypes-1.4.9}/comtypes/client/_generate.py +4 -8
  18. {comtypes-1.4.8 → comtypes-1.4.9}/comtypes/client/lazybind.py +2 -2
  19. {comtypes-1.4.8 → comtypes-1.4.9}/comtypes/errorinfo.py +3 -3
  20. {comtypes-1.4.8 → comtypes-1.4.9}/comtypes/git.py +1 -2
  21. {comtypes-1.4.8 → comtypes-1.4.9}/comtypes/hints.pyi +7 -15
  22. {comtypes-1.4.8 → comtypes-1.4.9}/comtypes/safearray.py +10 -0
  23. comtypes-1.4.9/comtypes/server/__init__.py +86 -0
  24. {comtypes-1.4.8 → comtypes-1.4.9}/comtypes/server/inprocserver.py +22 -17
  25. {comtypes-1.4.8 → comtypes-1.4.9}/comtypes/server/localserver.py +28 -16
  26. {comtypes-1.4.8 → comtypes-1.4.9}/comtypes/server/register.py +61 -73
  27. comtypes-1.4.9/comtypes/server/w_getopt.py +48 -0
  28. {comtypes-1.4.8 → comtypes-1.4.9}/comtypes/shelllink.py +3 -26
  29. {comtypes-1.4.8 → comtypes-1.4.9}/comtypes/test/__init__.py +9 -11
  30. {comtypes-1.4.8 → comtypes-1.4.9}/comtypes/test/test_BSTR.py +4 -7
  31. {comtypes-1.4.8 → comtypes-1.4.9}/comtypes/test/test_avmc.py +1 -1
  32. {comtypes-1.4.8 → comtypes-1.4.9}/comtypes/test/test_casesensitivity.py +3 -5
  33. {comtypes-1.4.8 → comtypes-1.4.9}/comtypes/test/test_collections.py +3 -3
  34. comtypes-1.4.9/comtypes/test/test_comobject.py +146 -0
  35. comtypes-1.4.9/comtypes/test/test_comserver.py +319 -0
  36. {comtypes-1.4.8 → comtypes-1.4.9}/comtypes/test/test_createwrappers.py +2 -4
  37. comtypes-1.4.9/comtypes/test/test_dispinterface.py +153 -0
  38. {comtypes-1.4.8 → comtypes-1.4.9}/comtypes/test/test_excel.py +22 -12
  39. {comtypes-1.4.8 → comtypes-1.4.9}/comtypes/test/test_ie.py +2 -5
  40. {comtypes-1.4.8 → comtypes-1.4.9}/comtypes/test/test_inout_args.py +7 -4
  41. {comtypes-1.4.8 → comtypes-1.4.9}/comtypes/test/test_midl_safearray_create.py +9 -1
  42. {comtypes-1.4.8 → comtypes-1.4.9}/comtypes/test/test_outparam.py +17 -10
  43. {comtypes-1.4.8 → comtypes-1.4.9}/comtypes/test/test_safearray.py +1 -90
  44. comtypes-1.4.9/comtypes/test/test_server.py +350 -0
  45. comtypes-1.4.9/comtypes/test/test_server_register.py +536 -0
  46. {comtypes-1.4.8 → comtypes-1.4.9}/comtypes/test/test_showevents.py +7 -17
  47. {comtypes-1.4.8 → comtypes-1.4.9}/comtypes/test/test_variant.py +0 -17
  48. comtypes-1.4.9/comtypes/test/test_w_getopt.py +29 -0
  49. {comtypes-1.4.8 → comtypes-1.4.9}/comtypes/test/test_win32com_interop.py +80 -36
  50. {comtypes-1.4.8 → comtypes-1.4.9}/comtypes/test/test_wmi.py +0 -3
  51. {comtypes-1.4.8 → comtypes-1.4.9}/comtypes/tools/codegenerator/codegenerator.py +62 -68
  52. {comtypes-1.4.8 → comtypes-1.4.9}/comtypes/tools/codegenerator/helpers.py +8 -8
  53. {comtypes-1.4.8 → comtypes-1.4.9}/comtypes/tools/codegenerator/packing.py +4 -4
  54. {comtypes-1.4.8 → comtypes-1.4.9}/comtypes/tools/codegenerator/typeannotator.py +7 -7
  55. {comtypes-1.4.8 → comtypes-1.4.9}/comtypes/tools/typedesc.py +1 -6
  56. {comtypes-1.4.8 → comtypes-1.4.9}/comtypes/typeinfo.py +11 -22
  57. {comtypes-1.4.8 → comtypes-1.4.9}/comtypes/viewobject.py +1 -5
  58. {comtypes-1.4.8 → comtypes-1.4.9/comtypes.egg-info}/PKG-INFO +2 -2
  59. {comtypes-1.4.8 → comtypes-1.4.9}/comtypes.egg-info/SOURCES.txt +6 -3
  60. {comtypes-1.4.8 → comtypes-1.4.9}/pyproject.toml +1 -0
  61. comtypes-1.4.8/comtypes/_comobject.py +0 -833
  62. comtypes-1.4.8/comtypes/_idl_stuff.py +0 -67
  63. comtypes-1.4.8/comtypes/server/__init__.py +0 -70
  64. comtypes-1.4.8/comtypes/server/w_getopt.py +0 -75
  65. comtypes-1.4.8/comtypes/test/test_comserver.py +0 -239
  66. comtypes-1.4.8/comtypes/test/test_dispinterface.py +0 -137
  67. comtypes-1.4.8/comtypes/test/test_propputref.py +0 -40
  68. comtypes-1.4.8/comtypes/test/test_server.py +0 -354
  69. {comtypes-1.4.8 → comtypes-1.4.9}/LICENSE.txt +0 -0
  70. {comtypes-1.4.8 → comtypes-1.4.9}/MANIFEST.in +0 -0
  71. {comtypes-1.4.8 → comtypes-1.4.9}/comtypes/_meta.py +0 -0
  72. {comtypes-1.4.8 → comtypes-1.4.9}/comtypes/_npsupport.py +0 -0
  73. {comtypes-1.4.8 → comtypes-1.4.9}/comtypes/_post_coinit/__init__.py +0 -0
  74. {comtypes-1.4.8 → comtypes-1.4.9}/comtypes/_post_coinit/bstr.py +0 -0
  75. /comtypes-1.4.8/comtypes/_py_instance_method.py → /comtypes-1.4.9/comtypes/_post_coinit/instancemethod.py +0 -0
  76. {comtypes-1.4.8 → comtypes-1.4.9}/comtypes/_tlib_version_checker.py +0 -0
  77. {comtypes-1.4.8 → comtypes-1.4.9}/comtypes/clear_cache.py +0 -0
  78. {comtypes-1.4.8 → comtypes-1.4.9}/comtypes/client/__init__.py +0 -0
  79. {comtypes-1.4.8 → comtypes-1.4.9}/comtypes/client/dynamic.py +0 -0
  80. {comtypes-1.4.8 → comtypes-1.4.9}/comtypes/connectionpoints.py +0 -0
  81. {comtypes-1.4.8 → comtypes-1.4.9}/comtypes/hresult.py +0 -0
  82. {comtypes-1.4.8 → comtypes-1.4.9}/comtypes/logutil.py +0 -0
  83. {comtypes-1.4.8 → comtypes-1.4.9}/comtypes/messageloop.py +0 -0
  84. {comtypes-1.4.8 → comtypes-1.4.9}/comtypes/patcher.py +0 -0
  85. {comtypes-1.4.8 → comtypes-1.4.9}/comtypes/persist.py +0 -0
  86. {comtypes-1.4.8 → comtypes-1.4.9}/comtypes/server/automation.py +0 -0
  87. {comtypes-1.4.8 → comtypes-1.4.9}/comtypes/server/connectionpoints.py +0 -0
  88. {comtypes-1.4.8 → comtypes-1.4.9}/comtypes/stream.py +0 -0
  89. {comtypes-1.4.8 → comtypes-1.4.9}/comtypes/test/TestComServer.idl +0 -0
  90. {comtypes-1.4.8 → comtypes-1.4.9}/comtypes/test/TestComServer.py +0 -0
  91. {comtypes-1.4.8 → comtypes-1.4.9}/comtypes/test/TestComServer.tlb +0 -0
  92. {comtypes-1.4.8 → comtypes-1.4.9}/comtypes/test/TestDispServer.idl +0 -0
  93. {comtypes-1.4.8 → comtypes-1.4.9}/comtypes/test/TestDispServer.py +0 -0
  94. {comtypes-1.4.8 → comtypes-1.4.9}/comtypes/test/TestDispServer.tlb +0 -0
  95. {comtypes-1.4.8 → comtypes-1.4.9}/comtypes/test/find_memleak.py +0 -0
  96. {comtypes-1.4.8 → comtypes-1.4.9}/comtypes/test/mylib.idl +0 -0
  97. {comtypes-1.4.8 → comtypes-1.4.9}/comtypes/test/mylib.tlb +0 -0
  98. {comtypes-1.4.8 → comtypes-1.4.9}/comtypes/test/mytypelib.idl +0 -0
  99. {comtypes-1.4.8 → comtypes-1.4.9}/comtypes/test/runtests.py +0 -0
  100. {comtypes-1.4.8 → comtypes-1.4.9}/comtypes/test/setup.py +0 -0
  101. {comtypes-1.4.8 → comtypes-1.4.9}/comtypes/test/test_DISPPARAMS.py +0 -0
  102. {comtypes-1.4.8 → comtypes-1.4.9}/comtypes/test/test_GUID.py +0 -0
  103. {comtypes-1.4.8 → comtypes-1.4.9}/comtypes/test/test_QueryService.py +0 -0
  104. {comtypes-1.4.8 → comtypes-1.4.9}/comtypes/test/test_agilent.py +0 -0
  105. {comtypes-1.4.8 → comtypes-1.4.9}/comtypes/test/test_basic.py +0 -0
  106. {comtypes-1.4.8 → comtypes-1.4.9}/comtypes/test/test_clear_cache.py +0 -0
  107. {comtypes-1.4.8 → comtypes-1.4.9}/comtypes/test/test_client.py +0 -0
  108. {comtypes-1.4.8 → comtypes-1.4.9}/comtypes/test/test_client_dynamic.py +0 -0
  109. {comtypes-1.4.8 → comtypes-1.4.9}/comtypes/test/test_client_regenerate_modules.py +0 -0
  110. {comtypes-1.4.8 → comtypes-1.4.9}/comtypes/test/test_dict.py +0 -0
  111. {comtypes-1.4.8 → comtypes-1.4.9}/comtypes/test/test_dispifc_records.py +0 -0
  112. {comtypes-1.4.8 → comtypes-1.4.9}/comtypes/test/test_dispifc_safearrays.py +0 -0
  113. {comtypes-1.4.8 → comtypes-1.4.9}/comtypes/test/test_dyndispatch.py +0 -0
  114. {comtypes-1.4.8 → comtypes-1.4.9}/comtypes/test/test_findgendir.py +0 -0
  115. {comtypes-1.4.8 → comtypes-1.4.9}/comtypes/test/test_getactiveobj.py +0 -0
  116. {comtypes-1.4.8 → comtypes-1.4.9}/comtypes/test/test_ienum.py +0 -0
  117. {comtypes-1.4.8 → comtypes-1.4.9}/comtypes/test/test_imfattributes.py +0 -0
  118. {comtypes-1.4.8 → comtypes-1.4.9}/comtypes/test/test_jscript.js +0 -0
  119. {comtypes-1.4.8 → comtypes-1.4.9}/comtypes/test/test_monikers.py +0 -0
  120. {comtypes-1.4.8 → comtypes-1.4.9}/comtypes/test/test_msscript.py +0 -0
  121. {comtypes-1.4.8 → comtypes-1.4.9}/comtypes/test/test_npsupport.py +0 -0
  122. {comtypes-1.4.8 → comtypes-1.4.9}/comtypes/test/test_persist.py +0 -0
  123. {comtypes-1.4.8 → comtypes-1.4.9}/comtypes/test/test_pump_events.py +0 -0
  124. {comtypes-1.4.8 → comtypes-1.4.9}/comtypes/test/test_recordinfo.py +0 -0
  125. {comtypes-1.4.8 → comtypes-1.4.9}/comtypes/test/test_sapi.py +0 -0
  126. {comtypes-1.4.8 → comtypes-1.4.9}/comtypes/test/test_shelllink.py +0 -0
  127. {comtypes-1.4.8 → comtypes-1.4.9}/comtypes/test/test_storage.py +0 -0
  128. {comtypes-1.4.8 → comtypes-1.4.9}/comtypes/test/test_stream.py +0 -0
  129. {comtypes-1.4.8 → comtypes-1.4.9}/comtypes/test/test_subinterface.py +0 -0
  130. {comtypes-1.4.8 → comtypes-1.4.9}/comtypes/test/test_typeannotator.py +0 -0
  131. {comtypes-1.4.8 → comtypes-1.4.9}/comtypes/test/test_typeinfo.py +0 -0
  132. {comtypes-1.4.8 → comtypes-1.4.9}/comtypes/test/test_urlhistory.py +0 -0
  133. {comtypes-1.4.8 → comtypes-1.4.9}/comtypes/test/test_word.py +0 -0
  134. {comtypes-1.4.8 → comtypes-1.4.9}/comtypes/test/urlhist.tlb +0 -0
  135. {comtypes-1.4.8 → comtypes-1.4.9}/comtypes/tools/__init__.py +0 -0
  136. {comtypes-1.4.8 → comtypes-1.4.9}/comtypes/tools/codegenerator/__init__.py +0 -0
  137. {comtypes-1.4.8 → comtypes-1.4.9}/comtypes/tools/codegenerator/modulenamer.py +0 -0
  138. {comtypes-1.4.8 → comtypes-1.4.9}/comtypes/tools/codegenerator/namespaces.py +0 -0
  139. {comtypes-1.4.8 → comtypes-1.4.9}/comtypes/tools/tlbparser.py +0 -0
  140. {comtypes-1.4.8 → comtypes-1.4.9}/comtypes/tools/typedesc_base.py +0 -0
  141. {comtypes-1.4.8 → comtypes-1.4.9}/comtypes/util.py +0 -0
  142. {comtypes-1.4.8 → comtypes-1.4.9}/comtypes.egg-info/dependency_links.txt +0 -0
  143. {comtypes-1.4.8 → comtypes-1.4.9}/comtypes.egg-info/entry_points.txt +0 -0
  144. {comtypes-1.4.8 → comtypes-1.4.9}/comtypes.egg-info/top_level.txt +0 -0
  145. {comtypes-1.4.8 → comtypes-1.4.9}/setup.cfg +0 -0
  146. {comtypes-1.4.8 → comtypes-1.4.9}/setup.py +0 -0
@@ -1,5 +1,5 @@
1
1
  import ctypes
2
- from typing import Any, NamedTuple
2
+ from typing import Any, Literal, NamedTuple
3
3
  from typing import Dict, List, Tuple, Type
4
4
  from typing import Optional, Union as _UnionT
5
5
  from typing import Callable, Iterator
@@ -64,7 +64,7 @@ def _resolve_argspec(
64
64
  elif typ is ctypes.POINTER(VARIANT):
65
65
  defval = ctypes.pointer(VARIANT.missing)
66
66
  else:
67
- # msg = ("'optional' only allowed for VARIANT and VARIANT*, not for %s" % typ.__name__)
67
+ # msg = f"'optional' only allowed for VARIANT and VARIANT*, not for {typ.__name__}"
68
68
  # warnings.warn(msg, IDLWarning, stacklevel=2)
69
69
  defval = typ()
70
70
  if defval is _NOTHING:
@@ -92,7 +92,7 @@ class _ComMemberSpec(NamedTuple):
92
92
  class _DispMemberSpec(NamedTuple):
93
93
  """Specifier for a slot of dispinterface method or property."""
94
94
 
95
- what: str
95
+ what: Literal["DISPMETHOD", "DISPPROPERTY"]
96
96
  name: str
97
97
  idlflags: Tuple[_UnionT[str, int], ...]
98
98
  restype: Optional[Type[_CData]]
@@ -118,6 +118,78 @@ def _is_spec_prop(m: _MemberSpec):
118
118
  return any(f in ("propget", "propput", "propputref") for f in m.idlflags)
119
119
 
120
120
 
121
+ ################################################################
122
+ # IDL stuff
123
+
124
+
125
+ class helpstring(str):
126
+ "Specifies the helpstring for a COM method or property."
127
+
128
+
129
+ class defaultvalue(object):
130
+ "Specifies the default value for parameters marked optional."
131
+
132
+ def __init__(self, value):
133
+ self.value = value
134
+
135
+
136
+ class dispid(int):
137
+ "Specifies the DISPID of a method or property."
138
+
139
+
140
+ # XXX STDMETHOD, COMMETHOD, DISPMETHOD, and DISPPROPERTY should return
141
+ # instances with more methods or properties, and should not behave as an unpackable.
142
+
143
+
144
+ def STDMETHOD(restype, name, argtypes=()) -> _ComMemberSpec:
145
+ "Specifies a COM method slot without idlflags"
146
+ return _ComMemberSpec(restype, name, argtypes, None, (), None)
147
+
148
+
149
+ def DISPMETHOD(idlflags, restype, name, *argspec) -> _DispMemberSpec:
150
+ "Specifies a method of a dispinterface"
151
+ return _DispMemberSpec("DISPMETHOD", name, tuple(idlflags), restype, argspec)
152
+
153
+
154
+ def DISPPROPERTY(idlflags, proptype, name) -> _DispMemberSpec:
155
+ "Specifies a property of a dispinterface"
156
+ return _DispMemberSpec("DISPPROPERTY", name, tuple(idlflags), proptype, ())
157
+
158
+
159
+ # tuple(idlflags) is for the method itself: (dispid, 'readonly')
160
+
161
+ # sample generated code:
162
+ # DISPPROPERTY([5, 'readonly'], OLE_YSIZE_HIMETRIC, 'Height'),
163
+ # DISPMETHOD(
164
+ # [6], None, 'Render', ([], c_int, 'hdc'), ([], c_int, 'x'), ([], c_int, 'y')
165
+ # )
166
+
167
+
168
+ def COMMETHOD(idlflags, restype, methodname, *argspec) -> _ComMemberSpec:
169
+ """Specifies a COM method slot with idlflags.
170
+
171
+ XXX should explain the sematics of the arguments.
172
+ """
173
+ # collect all helpstring instances
174
+ # We should suppress docstrings when Python is started with -OO
175
+ # join them together(does this make sense?) and replace by None if empty.
176
+ helptext = "".join(t for t in idlflags if isinstance(t, helpstring)) or None
177
+ paramflags, argtypes = _resolve_argspec(argspec)
178
+ if "propget" in idlflags:
179
+ name = f"_get_{methodname}"
180
+ elif "propput" in idlflags:
181
+ name = f"_set_{methodname}"
182
+ elif "propputref" in idlflags:
183
+ name = f"_setref_{methodname}"
184
+ else:
185
+ name = methodname
186
+ return _ComMemberSpec(
187
+ restype, name, argtypes, paramflags, tuple(idlflags), helptext
188
+ )
189
+
190
+
191
+ ################################################################
192
+
121
193
  _PropFunc = Optional[Callable[..., Any]]
122
194
  _DocType = Optional[str]
123
195
 
@@ -310,7 +382,7 @@ class PropertyGenerator(object):
310
382
  # Hm, must be a descriptor where the __get__ method
311
383
  # returns a bound object having __getitem__ and
312
384
  # __setitem__ methods.
313
- prop = named_property("%s.%s" % (self._cls_name, name), fget, fset, doc)
385
+ prop = named_property(f"{self._cls_name}.{name}", fget, fset, doc)
314
386
  yield (name, prop)
315
387
 
316
388
  def to_propget_keys(self, m: _MemberSpec) -> Tuple[str, _DocType, int]:
@@ -360,7 +432,7 @@ class DispPropertyGenerator(PropertyGenerator):
360
432
 
361
433
 
362
434
  class ComMemberGenerator(object):
363
- def __init__(self, cls_name: str, vtbl_offset: int, iid: comtypes.GUID) -> None:
435
+ def __init__(self, cls_name: str, vtbl_offset: int, iid: "comtypes.GUID") -> None:
364
436
  self._vtbl_offset = vtbl_offset
365
437
  self._iid = iid
366
438
  self._props = ComPropertyGenerator(cls_name)
@@ -533,11 +605,11 @@ class bound_named_property(object):
533
605
  self.fset(self.instance, index, value)
534
606
 
535
607
  def __repr__(self):
536
- return "<bound_named_property %r at %x>" % (self.name, id(self))
608
+ return f"<bound_named_property {self.name!r} at {id(self):x}>"
537
609
 
538
610
  def __iter__(self):
539
611
  """Explicitly disallow iteration."""
540
- msg = "%r is not iterable" % self.name
612
+ msg = f"{self.name!r} is not iterable"
541
613
  raise TypeError(msg)
542
614
 
543
615
 
@@ -558,4 +630,4 @@ class named_property(object):
558
630
  raise AttributeError("Unsettable attribute")
559
631
 
560
632
  def __repr__(self):
561
- return "<named_property %r at %x>" % (self.name, id(self))
633
+ return f"<named_property {self.name!r} at {id(self):x}>"
@@ -0,0 +1,153 @@
1
+ import types
2
+ from _ctypes import COMError
3
+ from typing import Type
4
+
5
+ from comtypes import patcher
6
+
7
+
8
+ _all_slice = slice(None, None, None)
9
+
10
+
11
+ def case_insensitive(p: Type) -> None:
12
+ @patcher.Patch(p)
13
+ class CaseInsensitive(object):
14
+ # case insensitive attributes for COM methods and properties
15
+ def __getattr__(self, name):
16
+ """Implement case insensitive access to methods and properties"""
17
+ try:
18
+ fixed_name = self.__map_case__[name.lower()]
19
+ except KeyError:
20
+ raise AttributeError(name) # Should we use exception-chaining?
21
+ if fixed_name != name: # prevent unbounded recursion
22
+ return getattr(self, fixed_name)
23
+ raise AttributeError(name)
24
+
25
+ # __setattr__ is pretty heavy-weight, because it is called for
26
+ # EVERY attribute assignment. Settings a non-com attribute
27
+ # through this function takes 8.6 usec, while without this
28
+ # function it takes 0.7 sec - 12 times slower.
29
+ #
30
+ # How much faster would this be if implemented in C?
31
+ def __setattr__(self, name, value):
32
+ """Implement case insensitive access to methods and properties"""
33
+ object.__setattr__(self, self.__map_case__.get(name.lower(), name), value)
34
+
35
+
36
+ def reference_fix(pp: Type) -> None:
37
+ @patcher.Patch(pp)
38
+ class ReferenceFix(object):
39
+ def __setitem__(self, index, value):
40
+ # We override the __setitem__ method of the
41
+ # POINTER(POINTER(interface)) type, so that the COM
42
+ # reference count is managed correctly.
43
+ #
44
+ # This is so that we can implement COM methods that have to
45
+ # return COM pointers more easily and consistent. Instead of
46
+ # using CopyComPointer in the method implementation, we can
47
+ # simply do:
48
+ #
49
+ # def GetTypeInfo(self, this, ..., pptinfo):
50
+ # if not pptinfo: return E_POINTER
51
+ # pptinfo[0] = a_com_interface_pointer
52
+ # return S_OK
53
+ if index != 0:
54
+ # CopyComPointer, which is in _ctypes, does only
55
+ # handle an index of 0. This code does what
56
+ # CopyComPointer should do if index != 0.
57
+ if bool(value):
58
+ value.AddRef()
59
+ super(pp, self).__setitem__(index, value) # type: ignore
60
+ return
61
+ from _ctypes import CopyComPointer
62
+
63
+ CopyComPointer(value, self) # type: ignore
64
+
65
+
66
+ def sized(itf: Type) -> None:
67
+ @patcher.Patch(itf)
68
+ class _(object):
69
+ def __len__(self):
70
+ "Return the the 'self.Count' property."
71
+ return self.Count
72
+
73
+
74
+ def callable_and_subscriptable(itf: Type) -> None:
75
+ @patcher.Patch(itf)
76
+ class _(object):
77
+ # 'Item' is the 'default' value. Make it available by
78
+ # calling the instance (Not sure this makes sense, but
79
+ # win32com does this also).
80
+ def __call__(self, *args, **kw):
81
+ "Return 'self.Item(*args, **kw)'"
82
+ return self.Item(*args, **kw)
83
+
84
+ # does this make sense? It seems that all standard typelibs I've
85
+ # seen so far that support .Item also support ._NewEnum
86
+ @patcher.no_replace
87
+ def __getitem__(self, index):
88
+ "Return 'self.Item(index)'"
89
+ # Handle tuples and all-slice
90
+ if isinstance(index, tuple):
91
+ args = index
92
+ elif index == _all_slice:
93
+ args = ()
94
+ else:
95
+ args = (index,)
96
+
97
+ try:
98
+ result = self.Item(*args)
99
+ except COMError as err:
100
+ (hresult, text, details) = err.args
101
+ if hresult == -2147352565: # DISP_E_BADINDEX
102
+ raise IndexError("invalid index")
103
+ else:
104
+ raise
105
+
106
+ # Note that result may be NULL COM pointer. There is no way
107
+ # to interpret this properly, so it is returned as-is.
108
+
109
+ # Hm, should we call __ctypes_from_outparam__ on the
110
+ # result?
111
+ return result
112
+
113
+ @patcher.no_replace
114
+ def __setitem__(self, index, value):
115
+ "Attempt 'self.Item[index] = value'"
116
+ try:
117
+ self.Item[index] = value
118
+ except COMError as err:
119
+ (hresult, text, details) = err.args
120
+ if hresult == -2147352565: # DISP_E_BADINDEX
121
+ raise IndexError("invalid index")
122
+ else:
123
+ raise
124
+ except TypeError:
125
+ msg = f"{type(self)!r} object does not support item assignment"
126
+ raise TypeError(msg)
127
+
128
+
129
+ def iterator(itf: Type) -> None:
130
+ @patcher.Patch(itf)
131
+ class _(object):
132
+ def __iter__(self):
133
+ "Return an iterator over the _NewEnum collection."
134
+ # This method returns a pointer to _some_ _NewEnum interface.
135
+ # It relies on the fact that the code generator creates next()
136
+ # methods for them automatically.
137
+ #
138
+ # Better would maybe to return an object that
139
+ # implements the Python iterator protocol, and
140
+ # forwards the calls to the COM interface.
141
+ enum = self._NewEnum
142
+ if isinstance(enum, types.MethodType):
143
+ # _NewEnum should be a propget property, with dispid -4.
144
+ #
145
+ # Sometimes, however, it is a method.
146
+ enum = enum()
147
+ if hasattr(enum, "Next"):
148
+ return enum
149
+ # _NewEnum returns an IUnknown pointer, QueryInterface() it to
150
+ # IEnumVARIANT
151
+ from comtypes.automation import IEnumVARIANT
152
+
153
+ return enum.QueryInterface(IEnumVARIANT)
@@ -16,9 +16,9 @@ if TYPE_CHECKING:
16
16
  from comtypes import hints as hints # noqa # type: ignore
17
17
 
18
18
  from comtypes import GUID
19
- from comtypes._idl_stuff import COMMETHOD # noqa
20
19
  from comtypes import CLSCTX_SERVER, CLSCTX_LOCAL_SERVER, CLSCTX_REMOTE_SERVER
21
20
  from comtypes import _ole32, oledll, DWORD
21
+ from comtypes._memberspec import COMMETHOD
22
22
  from comtypes._post_coinit.unknwn import IUnknown
23
23
 
24
24
 
@@ -1,23 +1,17 @@
1
1
  # https://learn.microsoft.com/en-us/windows/win32/api/unknwn/
2
2
 
3
- from ctypes import byref, c_ulong, c_void_p, HRESULT, POINTER
4
- from _ctypes import COMError
5
-
6
3
  import logging
7
4
  import sys
8
- import types
9
- from typing import ClassVar, TYPE_CHECKING, TypeVar
10
- from typing import Optional
11
- from typing import List, Type
12
-
13
- from comtypes import GUID, patcher, _ole32_nohresult, com_interface_registry
14
- from comtypes._idl_stuff import STDMETHOD
15
- from comtypes._memberspec import ComMemberGenerator, DispMemberGenerator
16
- from comtypes._memberspec import _ComMemberSpec, _DispMemberSpec
17
- from comtypes._py_instance_method import instancemethod
5
+ from ctypes import HRESULT, POINTER, byref, c_ulong, c_void_p
6
+ from typing import TYPE_CHECKING, ClassVar, List, Optional, Type, TypeVar
18
7
 
8
+ from comtypes import GUID, _ole32_nohresult, com_interface_registry
9
+ from comtypes._memberspec import STDMETHOD, ComMemberGenerator, DispMemberGenerator
10
+ from comtypes._post_coinit import _cominterface_meta_patcher as _meta_patch
11
+ from comtypes._post_coinit.instancemethod import instancemethod
19
12
 
20
- _all_slice = slice(None, None, None)
13
+ if TYPE_CHECKING:
14
+ from comtypes._memberspec import _ComMemberSpec, _DispMemberSpec
21
15
 
22
16
  logger = logging.getLogger(__name__)
23
17
 
@@ -58,8 +52,8 @@ class _cominterface_meta(type):
58
52
 
59
53
  _case_insensitive_: bool
60
54
  _iid_: GUID
61
- _methods_: List[_ComMemberSpec]
62
- _disp_methods_: List[_DispMemberSpec]
55
+ _methods_: List["_ComMemberSpec"]
56
+ _disp_methods_: List["_DispMemberSpec"]
63
57
 
64
58
  # This flag is set to True by the atexit handler which calls
65
59
  # CoUninitialize.
@@ -136,68 +130,11 @@ class _cominterface_meta(type):
136
130
  _pointer_type_cache[self] = p
137
131
 
138
132
  if self._case_insensitive_:
139
- self._patch_case_insensitive_to_ptr_type(p)
140
- self._patch_reference_fix_to_ptrptr_type(POINTER(p)) # type: ignore
133
+ _meta_patch.case_insensitive(p)
134
+ _meta_patch.reference_fix(POINTER(p)) # type: ignore
141
135
 
142
136
  return self
143
137
 
144
- @staticmethod
145
- def _patch_case_insensitive_to_ptr_type(p: Type) -> None:
146
- @patcher.Patch(p)
147
- class CaseInsensitive(object):
148
- # case insensitive attributes for COM methods and properties
149
- def __getattr__(self, name):
150
- """Implement case insensitive access to methods and properties"""
151
- try:
152
- fixed_name = self.__map_case__[name.lower()]
153
- except KeyError:
154
- raise AttributeError(name) # Should we use exception-chaining?
155
- if fixed_name != name: # prevent unbounded recursion
156
- return getattr(self, fixed_name)
157
- raise AttributeError(name)
158
-
159
- # __setattr__ is pretty heavy-weight, because it is called for
160
- # EVERY attribute assignment. Settings a non-com attribute
161
- # through this function takes 8.6 usec, while without this
162
- # function it takes 0.7 sec - 12 times slower.
163
- #
164
- # How much faster would this be if implemented in C?
165
- def __setattr__(self, name, value):
166
- """Implement case insensitive access to methods and properties"""
167
- object.__setattr__(
168
- self, self.__map_case__.get(name.lower(), name), value
169
- )
170
-
171
- @staticmethod
172
- def _patch_reference_fix_to_ptrptr_type(pp: Type) -> None:
173
- @patcher.Patch(pp)
174
- class ReferenceFix(object):
175
- def __setitem__(self, index, value):
176
- # We override the __setitem__ method of the
177
- # POINTER(POINTER(interface)) type, so that the COM
178
- # reference count is managed correctly.
179
- #
180
- # This is so that we can implement COM methods that have to
181
- # return COM pointers more easily and consistent. Instead of
182
- # using CopyComPointer in the method implementation, we can
183
- # simply do:
184
- #
185
- # def GetTypeInfo(self, this, ..., pptinfo):
186
- # if not pptinfo: return E_POINTER
187
- # pptinfo[0] = a_com_interface_pointer
188
- # return S_OK
189
- if index != 0:
190
- # CopyComPointer, which is in _ctypes, does only
191
- # handle an index of 0. This code does what
192
- # CopyComPointer should do if index != 0.
193
- if bool(value):
194
- value.AddRef()
195
- super(pp, self).__setitem__(index, value) # type: ignore
196
- return
197
- from _ctypes import CopyComPointer
198
-
199
- CopyComPointer(value, self) # type: ignore
200
-
201
138
  def __setattr__(self, name, value):
202
139
  if name == "_methods_":
203
140
  # XXX I'm no longer sure why the code generator generates
@@ -225,94 +162,11 @@ class _cominterface_meta(type):
225
162
 
226
163
  # XXX These special methods should be generated by the code generator.
227
164
  if has_name("Count"):
228
-
229
- @patcher.Patch(self)
230
- class _(object):
231
- def __len__(self):
232
- "Return the the 'self.Count' property."
233
- return self.Count
234
-
165
+ _meta_patch.sized(self)
235
166
  if has_name("Item"):
236
-
237
- @patcher.Patch(self)
238
- class _(object):
239
- # 'Item' is the 'default' value. Make it available by
240
- # calling the instance (Not sure this makes sense, but
241
- # win32com does this also).
242
- def __call__(self, *args, **kw):
243
- "Return 'self.Item(*args, **kw)'"
244
- return self.Item(*args, **kw)
245
-
246
- # does this make sense? It seems that all standard typelibs I've
247
- # seen so far that support .Item also support ._NewEnum
248
- @patcher.no_replace
249
- def __getitem__(self, index):
250
- "Return 'self.Item(index)'"
251
- # Handle tuples and all-slice
252
- if isinstance(index, tuple):
253
- args = index
254
- elif index == _all_slice:
255
- args = ()
256
- else:
257
- args = (index,)
258
-
259
- try:
260
- result = self.Item(*args)
261
- except COMError as err:
262
- (hresult, text, details) = err.args
263
- if hresult == -2147352565: # DISP_E_BADINDEX
264
- raise IndexError("invalid index")
265
- else:
266
- raise
267
-
268
- # Note that result may be NULL COM pointer. There is no way
269
- # to interpret this properly, so it is returned as-is.
270
-
271
- # Hm, should we call __ctypes_from_outparam__ on the
272
- # result?
273
- return result
274
-
275
- @patcher.no_replace
276
- def __setitem__(self, index, value):
277
- "Attempt 'self.Item[index] = value'"
278
- try:
279
- self.Item[index] = value
280
- except COMError as err:
281
- (hresult, text, details) = err.args
282
- if hresult == -2147352565: # DISP_E_BADINDEX
283
- raise IndexError("invalid index")
284
- else:
285
- raise
286
- except TypeError:
287
- msg = "%r object does not support item assignment"
288
- raise TypeError(msg % type(self))
289
-
167
+ _meta_patch.callable_and_subscriptable(self)
290
168
  if has_name("_NewEnum"):
291
-
292
- @patcher.Patch(self)
293
- class _(object):
294
- def __iter__(self):
295
- "Return an iterator over the _NewEnum collection."
296
- # This method returns a pointer to _some_ _NewEnum interface.
297
- # It relies on the fact that the code generator creates next()
298
- # methods for them automatically.
299
- #
300
- # Better would maybe to return an object that
301
- # implements the Python iterator protocol, and
302
- # forwards the calls to the COM interface.
303
- enum = self._NewEnum
304
- if isinstance(enum, types.MethodType):
305
- # _NewEnum should be a propget property, with dispid -4.
306
- #
307
- # Sometimes, however, it is a method.
308
- enum = enum()
309
- if hasattr(enum, "Next"):
310
- return enum
311
- # _NewEnum returns an IUnknown pointer, QueryInterface() it to
312
- # IEnumVARIANT
313
- from comtypes.automation import IEnumVARIANT
314
-
315
- return enum.QueryInterface(IEnumVARIANT)
169
+ _meta_patch.iterator(self)
316
170
 
317
171
  def _make_case_insensitive(self):
318
172
  # The __map_case__ dictionary maps lower case names to the
@@ -325,7 +179,7 @@ class _cominterface_meta(type):
325
179
  d.update(getattr(self, "__map_case__", {}))
326
180
  self.__map_case__ = d
327
181
 
328
- def _make_dispmethods(self, methods: List[_DispMemberSpec]) -> None:
182
+ def _make_dispmethods(self, methods: List["_DispMemberSpec"]) -> None:
329
183
  if self._case_insensitive_:
330
184
  self._make_case_insensitive()
331
185
  # create dispinterface methods and properties on the interface 'self'
@@ -357,7 +211,7 @@ class _cominterface_meta(type):
357
211
  raise TypeError(f"baseinterface '{itf.__name__}' has no _methods_")
358
212
  return result
359
213
 
360
- def _make_methods(self, methods: List[_ComMemberSpec]) -> None:
214
+ def _make_methods(self, methods: List["_ComMemberSpec"]) -> None:
361
215
  if self._case_insensitive_:
362
216
  self._make_case_insensitive()
363
217
  # register com interface. we insist on an _iid_ in THIS class!
@@ -375,12 +229,12 @@ class _cominterface_meta(type):
375
229
  member_gen.add(m)
376
230
  for name, func, raw_func, is_prop in member_gen.methods():
377
231
  raw_mth = instancemethod(raw_func, None, self)
378
- setattr(self, "_%s__com_%s" % (self.__name__, name), raw_mth)
232
+ setattr(self, f"_{self.__name__}__com_{name}", raw_mth)
379
233
  mth = instancemethod(func, None, self)
380
234
  if not is_prop:
381
235
  # We install the method in the class, except when it's a property.
382
236
  # And we make sure we don't overwrite a property that's already present.
383
- mthname = name if not hasattr(self, name) else ("_%s" % name)
237
+ mthname = name if not hasattr(self, name) else f"_{name}"
384
238
  setattr(self, mthname, mth)
385
239
  # For a method, this is the real name.
386
240
  # For a property, this is the name WITHOUT the _set_ or _get_ prefix.
@@ -391,7 +245,7 @@ class _cominterface_meta(type):
391
245
  # create public properties / attribute accessors
392
246
  for name, accessor in member_gen.properties():
393
247
  # Again, we should not overwrite class attributes that are already present.
394
- propname = name if not hasattr(self, name) else ("_%s" % name)
248
+ propname = name if not hasattr(self, name) else f"_{name}"
395
249
  setattr(self, propname, accessor)
396
250
  # COM is case insensitive
397
251
  if self._case_insensitive_:
@@ -423,7 +277,7 @@ class _compointer_base(c_void_p, metaclass=_compointer_meta):
423
277
  #
424
278
  if not type(self)._com_shutting_down:
425
279
  _debug("Release %s", self)
426
- self.Release()
280
+ self.Release() # type: ignore
427
281
 
428
282
  def __eq__(self, other):
429
283
  if not isinstance(other, _compointer_base):
@@ -446,7 +300,7 @@ class _compointer_base(c_void_p, metaclass=_compointer_meta):
446
300
 
447
301
  def __repr__(self):
448
302
  ptr = super(_compointer_base, self).value
449
- return "<%s ptr=0x%x at %x>" % (self.__class__.__name__, ptr or 0, id(self))
303
+ return f"<{self.__class__.__name__} ptr=0x{ptr or 0:x} at {id(self):x}>"
450
304
 
451
305
  # This fixes the problem when there are multiple python interface types
452
306
  # wrapping the same COM interface. This could happen because some interfaces
@@ -482,8 +336,8 @@ class _compointer_base(c_void_p, metaclass=_compointer_meta):
482
336
  # a kind of QueryInterface
483
337
  return table[cls._iid_]
484
338
  except KeyError:
485
- raise TypeError("Interface %s not supported" % cls._iid_)
486
- return value.QueryInterface(cls.__com_interface__)
339
+ raise TypeError(f"Interface {cls._iid_} not supported")
340
+ return value.QueryInterface(cls.__com_interface__) # type: ignore
487
341
 
488
342
 
489
343
  ################################################################
@@ -524,7 +378,7 @@ class IUnknown(_IUnknown_Base, metaclass=_cominterface_meta):
524
378
 
525
379
  _case_insensitive_: ClassVar[bool] = False
526
380
  _iid_: ClassVar[GUID] = GUID("{00000000-0000-0000-C000-000000000046}")
527
- _methods_: ClassVar[List[_ComMemberSpec]] = [
381
+ _methods_: ClassVar[List["_ComMemberSpec"]] = [
528
382
  STDMETHOD(HRESULT, "QueryInterface", [POINTER(GUID), POINTER(c_void_p)]),
529
383
  STDMETHOD(c_ulong, "AddRef"),
530
384
  STDMETHOD(c_ulong, "Release"),
@@ -1,7 +1,7 @@
1
1
  """SAFEARRAY api functions, data types, and constants."""
2
2
 
3
3
  from ctypes import c_uint, c_ushort, c_void_p, POINTER, Structure, WinDLL
4
- from ctypes.wintypes import DWORD, LONG, UINT
4
+ from ctypes.wintypes import DWORD, LONG, UINT, USHORT
5
5
 
6
6
  from comtypes import HRESULT, GUID
7
7
 
@@ -19,7 +19,6 @@ from comtypes import HRESULT, GUID
19
19
 
20
20
  VARTYPE = c_ushort
21
21
  PVOID = c_void_p
22
- USHORT = c_ushort
23
22
 
24
23
  _oleaut32 = WinDLL("oleaut32")
25
24