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
@@ -36,17 +36,18 @@ Now, debug the object, and when done delete logging info:
36
36
  python mycomobj.py /nodebug
37
37
  """
38
38
 
39
- import sys
39
+ import logging
40
40
  import os
41
+ import sys
41
42
  import winreg
42
- import logging
43
+ from ctypes import WinError, c_ulong, c_wchar_p, create_string_buffer, sizeof, windll
44
+ from typing import Iterator, Tuple
43
45
 
44
46
  import comtypes
45
- from comtypes.typeinfo import LoadTypeLibEx, UnRegisterTypeLib, REGKIND_REGISTER
47
+ import comtypes.server.inprocserver
46
48
  from comtypes.hresult import *
47
49
  from comtypes.server import w_getopt
48
- import comtypes.server.inprocserver
49
- from ctypes import windll, c_ulong, c_wchar_p, WinError, sizeof, create_string_buffer
50
+ from comtypes.typeinfo import REGKIND_REGISTER, LoadTypeLibEx, UnRegisterTypeLib
50
51
 
51
52
  _debug = logging.getLogger(__name__).debug
52
53
 
@@ -100,7 +101,7 @@ class Registrar(object):
100
101
  'DeleteKey( %s\\CLSID\\%s\\Logging"'
101
102
  % (_explain(winreg.HKEY_CLASSES_ROOT), clsid)
102
103
  )
103
- hkey = winreg.OpenKey(winreg.HKEY_CLASSES_ROOT, r"CLSID\%s" % clsid)
104
+ hkey = winreg.OpenKey(winreg.HKEY_CLASSES_ROOT, rf"CLSID\{clsid}")
104
105
  winreg.DeleteKey(hkey, "Logging")
105
106
  except WindowsError as detail:
106
107
  if get_winerror(detail) != 2:
@@ -115,7 +116,7 @@ class Registrar(object):
115
116
  'CreateKey( %s\\CLSID\\%s\\Logging"'
116
117
  % (_explain(winreg.HKEY_CLASSES_ROOT), clsid)
117
118
  )
118
- hkey = winreg.CreateKey(winreg.HKEY_CLASSES_ROOT, r"CLSID\%s\Logging" % clsid)
119
+ hkey = winreg.CreateKey(winreg.HKEY_CLASSES_ROOT, rf"CLSID\{clsid}\Logging")
119
120
  for item in levels:
120
121
  name, value = item.split("=")
121
122
  v = getattr(logging, value)
@@ -147,8 +148,7 @@ class Registrar(object):
147
148
  self._register(cls, executable)
148
149
 
149
150
  def _register(self, cls, executable=None):
150
- table = self._registry_entries(cls)
151
- table.sort()
151
+ table = sorted(RegistryEntries(cls))
152
152
  _debug("Registering %s", cls)
153
153
  for hkey, subkey, valuename, value in table:
154
154
  _debug("[%s\\%s]", _explain(hkey), subkey)
@@ -159,7 +159,7 @@ class Registrar(object):
159
159
  tlib = getattr(cls, "_reg_typelib_", None)
160
160
  if tlib is not None:
161
161
  if hasattr(sys, "frozendllhandle"):
162
- dll = self._get_serverdll()
162
+ dll = _get_serverdll()
163
163
  _debug("LoadTypeLibEx(%s, REGKIND_REGISTER)", dll)
164
164
  LoadTypeLibEx(dll, REGKIND_REGISTER)
165
165
  else:
@@ -184,7 +184,7 @@ class Registrar(object):
184
184
  def _unregister(self, cls, force=False):
185
185
  # If force==False, we only remove those entries that we
186
186
  # actually would have written. It seems ATL does the same.
187
- table = [t[:2] for t in self._registry_entries(cls)]
187
+ table = [t[:2] for t in RegistryEntries(cls)]
188
188
  # only unique entries
189
189
  table = list(set(table))
190
190
  table.sort()
@@ -214,23 +214,29 @@ class Registrar(object):
214
214
  raise
215
215
  _debug("Done")
216
216
 
217
- def _get_serverdll(self):
218
- """Return the pathname of the dll hosting the COM object."""
219
- handle = getattr(sys, "frozendllhandle", None)
220
- if handle is not None:
221
- buf = create_string_buffer(260)
222
- windll.kernel32.GetModuleFileNameA(handle, buf, sizeof(buf))
223
- return buf[:]
224
- import _ctypes
225
217
 
226
- return _ctypes.__file__
218
+ def _get_serverdll():
219
+ """Return the pathname of the dll hosting the COM object."""
220
+ handle = getattr(sys, "frozendllhandle", None)
221
+ if handle is not None:
222
+ buf = create_string_buffer(260)
223
+ windll.kernel32.GetModuleFileNameA(handle, buf, sizeof(buf))
224
+ return buf[:]
225
+ import _ctypes
226
+
227
+ return _ctypes.__file__
228
+
229
+
230
+ class RegistryEntries(object):
231
+ def __init__(self, cls):
232
+ self._cls = cls
227
233
 
228
234
  def _get_full_classname(self, cls):
229
235
  """Return <modulename>.<classname> for 'cls'."""
230
236
  modname = cls.__module__
231
237
  if modname == "__main__":
232
238
  modname = os.path.splitext(os.path.basename(sys.argv[0]))[0]
233
- return "%s.%s" % (modname, cls.__name__)
239
+ return f"{modname}.{cls.__name__}"
234
240
 
235
241
  def _get_pythonpath(self, cls):
236
242
  """Return the filesystem path of the module containing 'cls'."""
@@ -238,8 +244,8 @@ class Registrar(object):
238
244
  dirname = os.path.dirname(sys.modules[modname].__file__)
239
245
  return os.path.abspath(dirname)
240
246
 
241
- def _registry_entries(self, cls):
242
- """Return a sequence of tuples containing registry entries.
247
+ def __iter__(self) -> Iterator[Tuple[int, str, str, str]]:
248
+ """Return a iterator of tuples containing registry entries.
243
249
 
244
250
  The tuples must be (key, subkey, name, value).
245
251
 
@@ -259,16 +265,13 @@ class Registrar(object):
259
265
  Note that the first part of the progid string is typically the
260
266
  IDL library name of the type library containing the coclass.
261
267
  """
268
+ cls = self._cls
262
269
  HKCR = winreg.HKEY_CLASSES_ROOT
263
270
 
264
- # table format: rootkey, subkey, valuename, value
265
- table = []
266
- append = lambda *args: table.append(args)
267
-
268
271
  # basic entry - names the comobject
269
- reg_clsid = str(
270
- cls._reg_clsid_
271
- ) # that's the only required attribute for registration
272
+
273
+ # that's the only required attribute for registration
274
+ reg_clsid = str(cls._reg_clsid_)
272
275
  reg_desc = getattr(cls, "_reg_desc_", "")
273
276
  if not reg_desc:
274
277
  # Simple minded algorithm to construct a description from
@@ -278,95 +281,83 @@ class Registrar(object):
278
281
  )
279
282
  if reg_desc:
280
283
  reg_desc = reg_desc.replace(".", " ")
281
- append(HKCR, "CLSID\\%s" % reg_clsid, "", reg_desc)
284
+ yield (HKCR, f"CLSID\\{reg_clsid}", "", reg_desc)
282
285
 
283
286
  reg_progid = getattr(cls, "_reg_progid_", None)
284
287
  if reg_progid:
285
288
  # for ProgIDFromCLSID:
286
- append(HKCR, "CLSID\\%s\\ProgID" % reg_clsid, "", reg_progid) # 1
289
+ yield (HKCR, f"CLSID\\{reg_clsid}\\ProgID", "", reg_progid) # 1
287
290
 
288
291
  # for CLSIDFromProgID
289
292
  if reg_desc:
290
- append(HKCR, reg_progid, "", reg_desc) # 2
291
- append(HKCR, "%s\\CLSID" % reg_progid, "", reg_clsid) # 3
293
+ yield (HKCR, reg_progid, "", reg_desc) # 2
294
+ yield (HKCR, f"{reg_progid}\\CLSID", "", reg_clsid) # 3
292
295
 
293
296
  reg_novers_progid = getattr(cls, "_reg_novers_progid_", None)
294
297
  if reg_novers_progid:
295
- append(
298
+ yield (
296
299
  HKCR,
297
- "CLSID\\%s\\VersionIndependentProgID" % reg_clsid, # 1a
300
+ f"CLSID\\{reg_clsid}\\VersionIndependentProgID", # 1a
298
301
  "",
299
302
  reg_novers_progid,
300
303
  )
301
304
  if reg_desc:
302
- append(HKCR, reg_novers_progid, "", reg_desc) # 2a
303
- append(HKCR, "%s\\CurVer" % reg_novers_progid, "", reg_progid) #
304
- append(HKCR, "%s\\CLSID" % reg_novers_progid, "", reg_clsid) # 3a
305
+ yield (HKCR, reg_novers_progid, "", reg_desc) # 2a
306
+ yield (HKCR, f"{reg_novers_progid}\\CurVer", "", reg_progid) #
307
+ yield (HKCR, f"{reg_novers_progid}\\CLSID", "", reg_clsid) # 3a
305
308
 
306
- clsctx = getattr(cls, "_reg_clsctx_", 0)
309
+ clsctx: int = getattr(cls, "_reg_clsctx_", 0)
310
+ localsvr_ctx = bool(clsctx & comtypes.CLSCTX_LOCAL_SERVER)
311
+ inprocsvr_ctx = bool(clsctx & comtypes.CLSCTX_INPROC_SERVER)
307
312
 
308
- if clsctx & comtypes.CLSCTX_LOCAL_SERVER and not hasattr(
309
- sys, "frozendllhandle"
310
- ):
313
+ if localsvr_ctx and not hasattr(sys, "frozendllhandle"):
311
314
  exe = sys.executable
312
315
  if " " in exe:
313
- exe = '"%s"' % exe
316
+ exe = f'"{exe}"'
314
317
  if not hasattr(sys, "frozen"):
315
318
  if not __debug__:
316
- exe = "%s -O" % exe
319
+ exe = f"{exe} -O"
317
320
  script = os.path.abspath(sys.modules[cls.__module__].__file__)
318
321
  if " " in script:
319
- script = '"%s"' % script
320
- append(
321
- HKCR,
322
- "CLSID\\%s\\LocalServer32" % reg_clsid,
323
- "",
324
- "%s %s" % (exe, script),
325
- )
322
+ script = f'"{script}"'
323
+ yield (HKCR, rf"CLSID\{reg_clsid}\LocalServer32", "", f"{exe} {script}")
326
324
  else:
327
- append(HKCR, "CLSID\\%s\\LocalServer32" % reg_clsid, "", "%s" % exe)
325
+ yield (HKCR, rf"CLSID\{reg_clsid}\LocalServer32", "", f"{exe}")
328
326
 
329
327
  # Register InprocServer32 only when run from script or from
330
328
  # py2exe dll server, not from py2exe exe server.
331
- if clsctx & comtypes.CLSCTX_INPROC_SERVER and getattr(sys, "frozen", None) in (
332
- None,
333
- "dll",
334
- ):
335
- append(
336
- HKCR, "CLSID\\%s\\InprocServer32" % reg_clsid, "", self._get_serverdll()
337
- )
329
+ if inprocsvr_ctx and getattr(sys, "frozen", None) in (None, "dll"):
330
+ yield (HKCR, rf"CLSID\{reg_clsid}\InprocServer32", "", _get_serverdll())
338
331
  # only for non-frozen inproc servers the PythonPath/PythonClass is needed.
339
332
  if (
340
333
  not hasattr(sys, "frozendllhandle")
341
334
  or not comtypes.server.inprocserver._clsid_to_class
342
335
  ):
343
- append(
336
+ yield (
344
337
  HKCR,
345
- "CLSID\\%s\\InprocServer32" % reg_clsid,
338
+ rf"CLSID\{reg_clsid}\InprocServer32",
346
339
  "PythonClass",
347
340
  self._get_full_classname(cls),
348
341
  )
349
- append(
342
+ yield (
350
343
  HKCR,
351
- "CLSID\\%s\\InprocServer32" % reg_clsid,
344
+ rf"CLSID\{reg_clsid}\InprocServer32",
352
345
  "PythonPath",
353
346
  self._get_pythonpath(cls),
354
347
  )
355
348
 
356
349
  reg_threading = getattr(cls, "_reg_threading_", None)
357
350
  if reg_threading is not None:
358
- append(
351
+ yield (
359
352
  HKCR,
360
- "CLSID\\%s\\InprocServer32" % reg_clsid,
353
+ rf"CLSID\{reg_clsid}\InprocServer32",
361
354
  "ThreadingModel",
362
355
  reg_threading,
363
356
  )
364
357
 
365
358
  reg_tlib = getattr(cls, "_reg_typelib_", None)
366
359
  if reg_tlib is not None:
367
- append(HKCR, "CLSID\\%s\\Typelib" % reg_clsid, "", reg_tlib[0])
368
-
369
- return table
360
+ yield (HKCR, rf"CLSID\{reg_clsid}\Typelib", "", reg_tlib[0])
370
361
 
371
362
 
372
363
  ################################################################
@@ -381,10 +372,7 @@ def unregister(cls):
381
372
 
382
373
 
383
374
  def UseCommandLine(*classes):
384
- usage = (
385
- """Usage: %s [-regserver] [-unregserver] [-nodebug] [-f logformat] [-l loggername=level]"""
386
- % sys.argv[0]
387
- )
375
+ usage = f"""Usage: {sys.argv[0]} [-regserver] [-unregserver] [-nodebug] [-f logformat] [-l loggername=level]"""
388
376
  opts, args = w_getopt.w_getopt(
389
377
  sys.argv[1:], "regserver unregserver embedding l: f: nodebug"
390
378
  )
@@ -0,0 +1,48 @@
1
+ from typing import Sequence, Tuple
2
+
3
+
4
+ class GetoptError(Exception):
5
+ pass
6
+
7
+
8
+ def w_getopt(
9
+ args: Sequence[str], options: str
10
+ ) -> Tuple[Sequence[Tuple[str, str]], Sequence[str]]:
11
+ """A getopt for Windows.
12
+
13
+ Options may start with either '-' or '/', the option names may
14
+ have more than one letter (/tlb or -RegServer), and option names
15
+ are case insensitive.
16
+
17
+ Returns two elements, just as getopt.getopt. The first is a list
18
+ of (option, value) pairs in the same way getopt.getopt does, but
19
+ there is no '-' or '/' prefix to the option name, and the option
20
+ name is always lower case. The second is the list of arguments
21
+ which do not belong to an option.
22
+
23
+ Different from getopt.getopt, a single argument not belonging to an option
24
+ does not terminate parsing.
25
+ """
26
+ opts = []
27
+ arguments = []
28
+ while args:
29
+ if args[0][:1] in "/-":
30
+ arg = args[0][1:] # strip the '-' or '/'
31
+ arg = arg.lower()
32
+
33
+ if arg + ":" in options:
34
+ try:
35
+ opts.append((arg, args[1]))
36
+ except IndexError:
37
+ raise GetoptError(f"option '{args[0]}' requires an argument")
38
+ args = args[1:]
39
+ elif arg in options:
40
+ opts.append((arg, ""))
41
+ else:
42
+ raise GetoptError(f"invalid option '{args[0]}'")
43
+ args = args[1:]
44
+ else:
45
+ arguments.append(args[0])
46
+ args = args[1:]
47
+
48
+ return opts, arguments
@@ -2,7 +2,7 @@ from ctypes import c_char_p, c_int, c_short, c_wchar_p
2
2
  from ctypes import POINTER
3
3
  from ctypes import byref, create_string_buffer, create_unicode_buffer
4
4
  from ctypes.wintypes import DWORD, WIN32_FIND_DATAA, WIN32_FIND_DATAW, MAX_PATH
5
- from typing import Tuple, TYPE_CHECKING
5
+ from typing import Tuple, TYPE_CHECKING, Literal
6
6
 
7
7
  from comtypes import IUnknown, GUID, COMMETHOD, HRESULT, CoClass
8
8
 
@@ -143,7 +143,7 @@ class IShellLinkA(IUnknown):
143
143
  def ShowCmd(self, piShowCmd: int) -> None: ...
144
144
  def SetIconLocation(self, pszIconPath: bytes, iIcon: int) -> hints.Hresult: ...
145
145
  def SetRelativePath(
146
- self, pszPathRel: bytes, dwReserved: hints.Literal[0]
146
+ self, pszPathRel: bytes, dwReserved: Literal[0]
147
147
  ) -> hints.Hresult: ...
148
148
  def Resolve(self, hwnd: int, fFlags: int) -> hints.Hresult: ...
149
149
  def SetPath(self, pszFile: bytes) -> hints.Hresult: ...
@@ -278,7 +278,7 @@ class IShellLinkW(IUnknown):
278
278
  def ShowCmd(self, piShowCmd: int) -> None: ...
279
279
  def SetIconLocation(self, pszIconPath: str, iIcon: int) -> hints.Hresult: ...
280
280
  def SetRelativePath(
281
- self, pszPathRel: str, dwReserved: hints.Literal[0]
281
+ self, pszPathRel: str, dwReserved: Literal[0]
282
282
  ) -> hints.Hresult: ...
283
283
  def Resolve(self, hwnd: int, fFlags: int) -> hints.Hresult: ...
284
284
  def SetPath(self, pszFile: str) -> hints.Hresult: ...
@@ -317,26 +317,3 @@ class ShellLink(CoClass):
317
317
  _reg_clsid_ = GUID("{00021401-0000-0000-C000-000000000046}")
318
318
  _idlflags_ = []
319
319
  _com_interfaces_ = [IShellLinkW, IShellLinkA]
320
-
321
-
322
- if __name__ == "__main__":
323
- import sys
324
- import comtypes
325
- from comtypes.client import CreateObject
326
- from comtypes.persist import IPersistFile
327
-
328
- shortcut = CreateObject(ShellLink)
329
- print(shortcut)
330
- ##help(shortcut)
331
-
332
- shortcut.SetPath(sys.executable)
333
-
334
- shortcut.SetDescription("Python %s" % sys.version)
335
- shortcut.SetIconLocation(sys.executable, 1)
336
-
337
- print(shortcut.GetPath(2))
338
- print(shortcut.GetIconLocation())
339
-
340
- pf = shortcut.QueryInterface(IPersistFile)
341
- pf.Save("foo.lnk", True)
342
- print(pf.GetCurFile())
@@ -64,7 +64,7 @@ def requires(resource, msg=None):
64
64
  return
65
65
  if not is_resource_enabled(resource):
66
66
  if msg is None:
67
- msg = "Use of the `%s' resource not enabled" % resource
67
+ msg = f"Use of the `{resource}` resource not enabled"
68
68
  raise ResourceDenied(msg)
69
69
 
70
70
 
@@ -94,12 +94,10 @@ def get_tests(package, mask, verbosity):
94
94
  except ResourceDenied as detail:
95
95
  skipped.append(modname)
96
96
  if verbosity > 1:
97
- print("Skipped %s: %s" % (modname, detail), file=sys.stderr)
97
+ print(f"Skipped {modname}: {detail}", file=sys.stderr)
98
98
  continue
99
99
  except Exception as detail:
100
- print(
101
- "Warning: could not import %s: %s" % (modname, detail), file=sys.stderr
102
- )
100
+ print(f"Warning: could not import {modname}: {detail}", file=sys.stderr)
103
101
  continue
104
102
  for name in dir(mod):
105
103
  if name.startswith("_"):
@@ -149,9 +147,9 @@ def test_with_refcounts(runner, verbosity, testcase):
149
147
  cleanup()
150
148
  refcounts[i] = sys.gettotalrefcount() - rc
151
149
  if [_f for _f in refcounts if _f]:
152
- print("%s leaks:\n\t" % testcase, refcounts)
150
+ print(f"{testcase} leaks:\n\t", refcounts)
153
151
  elif verbosity:
154
- print("%s: ok." % testcase)
152
+ print(f"{testcase}: ok.")
155
153
 
156
154
 
157
155
  class TestRunner(unittest.TextTestRunner):
@@ -180,21 +178,21 @@ class TestRunner(unittest.TextTestRunner):
180
178
  len(skipped) != 1 and "s" or "",
181
179
  )
182
180
  )
183
- self.stream.writeln("Unavailable resources: %s" % ", ".join(requested))
181
+ self.stream.writeln(f"Unavailable resources: {', '.join(requested)}")
184
182
  else:
185
183
  self.stream.writeln(
186
- "Ran %d test%s in %.3fs" % (run, run != 1 and "s" or "", timeTaken)
184
+ f"Ran {run} test{'s' if run != 1 else ''} in {timeTaken:.3f}s"
187
185
  )
188
186
  self.stream.writeln()
189
187
  if not result.wasSuccessful():
190
188
  self.stream.write("FAILED (")
191
189
  failed, errored = list(map(len, (result.failures, result.errors)))
192
190
  if failed:
193
- self.stream.write("failures=%d" % failed)
191
+ self.stream.write(f"failures={failed}")
194
192
  if errored:
195
193
  if failed:
196
194
  self.stream.write(", ")
197
- self.stream.write("errors=%d" % errored)
195
+ self.stream.write(f"errors={errored}")
198
196
  self.stream.writeln(")")
199
197
  else:
200
198
  self.stream.writeln("OK")
@@ -1,17 +1,14 @@
1
- import unittest, os
2
- from ctypes import *
3
- from comtypes import BSTR
4
- from comtypes.test import requires
5
-
6
- ##requires("memleaks")
1
+ import unittest
2
+ from ctypes import WINFUNCTYPE, c_uint, c_void_p, oledll, windll
7
3
 
4
+ from comtypes import BSTR
8
5
  from comtypes.test.find_memleak import find_memleak
9
6
 
10
7
 
11
8
  class Test(unittest.TestCase):
12
9
  def check_leaks(self, func, limit=0):
13
10
  bytes = find_memleak(func)
14
- self.assertFalse(bytes > limit, "Leaks %d bytes" % bytes)
11
+ self.assertFalse(bytes > limit, f"Leaks {bytes} bytes")
15
12
 
16
13
  def test_creation(self):
17
14
  def doit():
@@ -37,7 +37,7 @@ class Test(unittest.TestCase):
37
37
 
38
38
  def check_leaks(self, func, limit=0):
39
39
  bytes = find_memleak(func)
40
- self.assertFalse(bytes > limit, "Leaks %d bytes" % bytes)
40
+ self.assertFalse(bytes > limit, f"Leaks {bytes} bytes")
41
41
 
42
42
 
43
43
  if __name__ == "__main__":
@@ -31,13 +31,11 @@ class TestCase(unittest.TestCase):
31
31
  # names in the base class __map_case__ must also appear in the
32
32
  # subclass.
33
33
  for name in iem.IWebBrowser.__map_case__:
34
- self.assertTrue(
35
- name in iem.IWebBrowserApp.__map_case__, "%s missing" % name
36
- )
37
- self.assertTrue(name in iem.IWebBrowser2.__map_case__, "%s missing" % name)
34
+ self.assertTrue(name in iem.IWebBrowserApp.__map_case__, f"{name} missing")
35
+ self.assertTrue(name in iem.IWebBrowser2.__map_case__, f"{name} missing")
38
36
 
39
37
  for name in iem.IWebBrowserApp.__map_case__:
40
- self.assertTrue(name in iem.IWebBrowser2.__map_case__, "%s missing" % name)
38
+ self.assertTrue(name in iem.IWebBrowser2.__map_case__, f"{name} missing")
41
39
 
42
40
 
43
41
  if __name__ == "__main__":
@@ -74,7 +74,7 @@ class Test(unittest.TestCase):
74
74
  item.ProcessImageFileName
75
75
 
76
76
  bytes = find_memleak(doit, (20, 20))
77
- self.assertFalse(bytes, "Leaks %d bytes" % bytes)
77
+ self.assertFalse(bytes, f"Leaks {bytes} bytes")
78
78
 
79
79
  @unittest.skip("This test takes a long time. Do we need it? Can it be rewritten?")
80
80
  def test_leaks_2(self):
@@ -87,7 +87,7 @@ class Test(unittest.TestCase):
87
87
  iter(apps).Next(99)
88
88
 
89
89
  bytes = find_memleak(doit, (20, 20))
90
- self.assertFalse(bytes, "Leaks %d bytes" % bytes)
90
+ self.assertFalse(bytes, f"Leaks {bytes} bytes")
91
91
 
92
92
  @unittest.skip("This test takes a long time. Do we need it? Can it be rewritten?")
93
93
  def test_leaks_3(self):
@@ -102,7 +102,7 @@ class Test(unittest.TestCase):
102
102
  pass
103
103
 
104
104
  bytes = find_memleak(doit, (20, 20))
105
- self.assertFalse(bytes, "Leaks %d bytes" % bytes)
105
+ self.assertFalse(bytes, f"Leaks {bytes} bytes")
106
106
 
107
107
 
108
108
  class TestCollectionInterface(unittest.TestCase):
@@ -0,0 +1,146 @@
1
+ import ctypes
2
+ import unittest as ut
3
+ from ctypes import POINTER, byref, pointer
4
+ from unittest import mock
5
+
6
+ import comtypes
7
+ import comtypes.client
8
+ from comtypes import COMObject, IUnknown, hresult
9
+ from comtypes.automation import IDispatch
10
+
11
+ comtypes.client.GetModule("UIAutomationCore.dll")
12
+ comtypes.client.GetModule("scrrun.dll")
13
+ from comtypes.gen import Scripting as scrrun
14
+ from comtypes.gen import UIAutomationClient as uiac
15
+ from comtypes.gen import stdole
16
+
17
+
18
+ class Test_QueryInterface(ut.TestCase):
19
+ def test_custom_interface(self):
20
+ iuia = uiac.CUIAutomation().QueryInterface(uiac.IUIAutomation)
21
+ self.assertTrue(bool(iuia))
22
+ self.assertEqual(iuia.AddRef(), 2)
23
+ punk = iuia.QueryInterface(IUnknown)
24
+ self.assertEqual(iuia.AddRef(), 4)
25
+ self.assertEqual(punk.AddRef(), 5)
26
+ self.assertEqual(iuia.Release(), 4)
27
+ del iuia
28
+ self.assertEqual(punk.Release(), 2)
29
+ self.assertEqual(punk.Release(), 1)
30
+
31
+ def test_dispatch_interface(self):
32
+ dic = scrrun.Dictionary().QueryInterface(scrrun.IDictionary)
33
+ self.assertTrue(bool(dic))
34
+ self.assertEqual(dic.GetTypeInfoCount(), 1)
35
+ self.assertEqual(
36
+ dic.GetTypeInfo(0).GetTypeAttr().guid,
37
+ scrrun.IDictionary._iid_,
38
+ )
39
+ self.assertEqual(dic.GetIDsOfNames("Add"), [1])
40
+
41
+
42
+ class Test_IUnknown_QueryInterface(ut.TestCase):
43
+ def test_e_pointer(self):
44
+ hr = scrrun.Dictionary().IUnknown_QueryInterface(
45
+ None, pointer(scrrun.IDictionary._iid_), ctypes.c_void_p()
46
+ )
47
+ self.assertEqual(hr, hresult.E_POINTER)
48
+
49
+ def test_e_no_interface(self):
50
+ hr = scrrun.Dictionary().IUnknown_QueryInterface(
51
+ None, pointer(uiac.IUIAutomation._iid_), ctypes.c_void_p()
52
+ )
53
+ self.assertEqual(hr, hresult.E_NOINTERFACE)
54
+
55
+ def test_valid_pointer(self):
56
+ ptr = ctypes.c_void_p()
57
+ ctypes.oledll.ole32.CoCreateInstance(
58
+ byref(scrrun.Dictionary._reg_clsid_),
59
+ None,
60
+ comtypes.CLSCTX_SERVER,
61
+ byref(scrrun.IDictionary._iid_),
62
+ byref(ptr),
63
+ )
64
+ before_val = ptr.value
65
+ hr = scrrun.Dictionary().IUnknown_QueryInterface(
66
+ None, pointer(scrrun.IDictionary._iid_), ptr
67
+ )
68
+ self.assertEqual(hr, hresult.S_OK)
69
+ self.assertEqual(ptr.value, before_val)
70
+
71
+ def test_valid_interface(self):
72
+ dic = POINTER(IDispatch)()
73
+ hr = scrrun.Dictionary().IUnknown_QueryInterface(
74
+ None, pointer(scrrun.IDictionary._iid_), byref(dic)
75
+ )
76
+ self.assertEqual(hr, hresult.S_OK)
77
+ self.assertEqual(dic.AddRef(), 2) # type: ignore
78
+ self.assertEqual(dic.Release(), 1) # type: ignore
79
+ self.assertEqual(dic.GetTypeInfoCount(), 1) # type: ignore
80
+
81
+
82
+ class Test_IUnknown_AddRef_IUnknown_Release(ut.TestCase):
83
+ def test(self):
84
+ cuia = uiac.CUIAutomation()
85
+ self.assertEqual(cuia.IUnknown_AddRef(None), 1)
86
+ self.assertEqual(cuia.IUnknown_AddRef(None), 2)
87
+ with mock.patch.object(COMObject, "_final_release_") as release:
88
+ self.assertEqual(cuia.IUnknown_Release(None), 1)
89
+ release.assert_not_called()
90
+ self.assertEqual(cuia.IUnknown_Release(None), 0)
91
+ release.assert_called_once_with()
92
+
93
+
94
+ class Test_ISupportErrorInfo_InterfaceSupportsErrorInfo(ut.TestCase):
95
+ def test_s_ok(self):
96
+ cuia = uiac.CUIAutomation()
97
+ self.assertEqual(
98
+ cuia.ISupportErrorInfo_InterfaceSupportsErrorInfo(
99
+ None, pointer(IUnknown._iid_)
100
+ ),
101
+ hresult.S_OK,
102
+ )
103
+ self.assertEqual(
104
+ cuia.ISupportErrorInfo_InterfaceSupportsErrorInfo(
105
+ None, pointer(uiac.IUIAutomation._iid_)
106
+ ),
107
+ hresult.S_OK,
108
+ )
109
+
110
+ def test_s_false(self):
111
+ cuia = uiac.CUIAutomation()
112
+ self.assertEqual(
113
+ cuia.ISupportErrorInfo_InterfaceSupportsErrorInfo(
114
+ None, pointer(IDispatch._iid_)
115
+ ),
116
+ hresult.S_FALSE,
117
+ )
118
+ self.assertEqual(
119
+ cuia.ISupportErrorInfo_InterfaceSupportsErrorInfo(
120
+ None, pointer(scrrun.IDictionary._iid_)
121
+ ),
122
+ hresult.S_FALSE,
123
+ )
124
+
125
+
126
+ class Test_IProvideClassInfo_GetClassInfo(ut.TestCase):
127
+ def test(self):
128
+ tinfo = uiac.CUIAutomation().IProvideClassInfo_GetClassInfo()
129
+ self.assertEqual(tinfo.GetTypeAttr().guid, uiac.CUIAutomation._reg_clsid_)
130
+
131
+
132
+ class Test_IProvideClassInfo2_GetGUID(ut.TestCase):
133
+ def test(self):
134
+ # GUIDKIND_DEFAULT_SOURCE_DISP_IID = 1
135
+ self.assertEqual(
136
+ stdole.StdFont().IProvideClassInfo2_GetGUID(1),
137
+ stdole.FontEvents._iid_,
138
+ )
139
+
140
+
141
+ class Test_IPersist_GetClassID(ut.TestCase):
142
+ def test(self):
143
+ self.assertEqual(
144
+ uiac.CUIAutomation().IPersist_GetClassID(),
145
+ uiac.CUIAutomation._reg_clsid_,
146
+ )