comtypes 1.4.11__zip → 1.4.13__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 (151) hide show
  1. {comtypes-1.4.11 → comtypes-1.4.13}/CHANGES.txt +33 -0
  2. {comtypes-1.4.11/comtypes.egg-info → comtypes-1.4.13}/PKG-INFO +5 -5
  3. {comtypes-1.4.11 → comtypes-1.4.13}/README.md +3 -3
  4. {comtypes-1.4.11 → comtypes-1.4.13}/comtypes/__init__.py +8 -2
  5. {comtypes-1.4.11 → comtypes-1.4.13}/comtypes/_comobject.py +6 -6
  6. {comtypes-1.4.11 → comtypes-1.4.13}/comtypes/_memberspec.py +7 -7
  7. {comtypes-1.4.11 → comtypes-1.4.13}/comtypes/_meta.py +7 -3
  8. {comtypes-1.4.11 → comtypes-1.4.13}/comtypes/_npsupport.py +12 -17
  9. {comtypes-1.4.11 → comtypes-1.4.13}/comtypes/_post_coinit/_cominterface_meta_patcher.py +5 -5
  10. comtypes-1.4.13/comtypes/_post_coinit/activeobj.py +61 -0
  11. {comtypes-1.4.11 → comtypes-1.4.13}/comtypes/_post_coinit/misc.py +1 -23
  12. {comtypes-1.4.11 → comtypes-1.4.13}/comtypes/_post_coinit/unknwn.py +10 -8
  13. {comtypes-1.4.11 → comtypes-1.4.13}/comtypes/_vtbl.py +3 -3
  14. {comtypes-1.4.11 → comtypes-1.4.13}/comtypes/automation.py +10 -13
  15. {comtypes-1.4.11 → comtypes-1.4.13}/comtypes/client/__init__.py +2 -1
  16. {comtypes-1.4.11 → comtypes-1.4.13}/comtypes/client/_activeobj.py +8 -0
  17. {comtypes-1.4.11 → comtypes-1.4.13}/comtypes/client/_code_cache.py +1 -1
  18. {comtypes-1.4.11 → comtypes-1.4.13}/comtypes/client/_constants.py +1 -1
  19. {comtypes-1.4.11 → comtypes-1.4.13}/comtypes/client/_events.py +6 -6
  20. {comtypes-1.4.11 → comtypes-1.4.13}/comtypes/client/_generate.py +1 -1
  21. {comtypes-1.4.11 → comtypes-1.4.13}/comtypes/client/dynamic.py +3 -3
  22. {comtypes-1.4.11 → comtypes-1.4.13}/comtypes/client/lazybind.py +3 -3
  23. {comtypes-1.4.11 → comtypes-1.4.13}/comtypes/errorinfo.py +1 -1
  24. {comtypes-1.4.11 → comtypes-1.4.13}/comtypes/hresult.py +68 -0
  25. {comtypes-1.4.11 → comtypes-1.4.13}/comtypes/messageloop.py +1 -1
  26. {comtypes-1.4.11 → comtypes-1.4.13}/comtypes/patcher.py +2 -2
  27. {comtypes-1.4.11 → comtypes-1.4.13}/comtypes/persist.py +9 -2
  28. {comtypes-1.4.11 → comtypes-1.4.13}/comtypes/safearray.py +10 -10
  29. {comtypes-1.4.11 → comtypes-1.4.13}/comtypes/server/__init__.py +5 -37
  30. {comtypes-1.4.11 → comtypes-1.4.13}/comtypes/server/automation.py +2 -2
  31. {comtypes-1.4.11 → comtypes-1.4.13}/comtypes/server/connectionpoints.py +3 -3
  32. {comtypes-1.4.11 → comtypes-1.4.13}/comtypes/server/inprocserver.py +2 -2
  33. {comtypes-1.4.11 → comtypes-1.4.13}/comtypes/server/localserver.py +3 -3
  34. {comtypes-1.4.11 → comtypes-1.4.13}/comtypes/server/register.py +5 -5
  35. {comtypes-1.4.11 → comtypes-1.4.13}/comtypes/test/test_agilent.py +1 -1
  36. {comtypes-1.4.11 → comtypes-1.4.13}/comtypes/test/test_client_dynamic.py +1 -1
  37. {comtypes-1.4.11 → comtypes-1.4.13}/comtypes/test/test_comserver.py +15 -2
  38. {comtypes-1.4.11 → comtypes-1.4.13}/comtypes/test/test_createwrappers.py +1 -1
  39. {comtypes-1.4.11 → comtypes-1.4.13}/comtypes/test/test_dispifc_records.py +0 -2
  40. {comtypes-1.4.11 → comtypes-1.4.13}/comtypes/test/test_dispifc_safearrays.py +0 -2
  41. {comtypes-1.4.11 → comtypes-1.4.13}/comtypes/test/test_dispinterface.py +1 -1
  42. {comtypes-1.4.11 → comtypes-1.4.13}/comtypes/test/test_excel.py +2 -5
  43. {comtypes-1.4.11 → comtypes-1.4.13}/comtypes/test/test_getactiveobj.py +19 -2
  44. {comtypes-1.4.11 → comtypes-1.4.13}/comtypes/test/test_hresult.py +39 -0
  45. comtypes-1.4.13/comtypes/test/test_messageloop.py +117 -0
  46. {comtypes-1.4.11 → comtypes-1.4.13}/comtypes/test/test_midl_safearray_create.py +0 -2
  47. {comtypes-1.4.11 → comtypes-1.4.13}/comtypes/test/test_msscript.py +1 -1
  48. {comtypes-1.4.11 → comtypes-1.4.13}/comtypes/test/test_npsupport.py +22 -11
  49. comtypes-1.4.13/comtypes/test/test_persist.py +71 -0
  50. {comtypes-1.4.11 → comtypes-1.4.13}/comtypes/test/test_recordinfo.py +0 -2
  51. {comtypes-1.4.11 → comtypes-1.4.13}/comtypes/test/test_server.py +6 -6
  52. {comtypes-1.4.11 → comtypes-1.4.13}/comtypes/test/test_storage.py +1 -1
  53. {comtypes-1.4.11 → comtypes-1.4.13}/comtypes/test/test_stream.py +22 -2
  54. {comtypes-1.4.11 → comtypes-1.4.13}/comtypes/test/test_urlhistory.py +1 -1
  55. comtypes-1.4.13/comtypes/test/test_util.py +227 -0
  56. {comtypes-1.4.11 → comtypes-1.4.13}/comtypes/test/test_variant.py +41 -24
  57. comtypes-1.4.13/comtypes/test/test_viewobject.py +78 -0
  58. {comtypes-1.4.11 → comtypes-1.4.13}/comtypes/test/test_word.py +1 -1
  59. {comtypes-1.4.11 → comtypes-1.4.13}/comtypes/tools/codegenerator/codegenerator.py +1 -1
  60. {comtypes-1.4.11 → comtypes-1.4.13}/comtypes/tools/codegenerator/comments.py +1 -1
  61. {comtypes-1.4.11 → comtypes-1.4.13}/comtypes/tools/codegenerator/heads.py +5 -5
  62. {comtypes-1.4.11 → comtypes-1.4.13}/comtypes/tools/codegenerator/helpers.py +7 -7
  63. {comtypes-1.4.11 → comtypes-1.4.13}/comtypes/tools/codegenerator/namespaces.py +3 -3
  64. {comtypes-1.4.11 → comtypes-1.4.13}/comtypes/tools/codegenerator/typeannotator.py +2 -2
  65. {comtypes-1.4.11 → comtypes-1.4.13}/comtypes/tools/tlbparser.py +1 -1
  66. {comtypes-1.4.11 → comtypes-1.4.13}/comtypes/tools/typedesc.py +14 -14
  67. {comtypes-1.4.11 → comtypes-1.4.13}/comtypes/tools/typedesc_base.py +19 -19
  68. comtypes-1.4.13/comtypes/util.py +189 -0
  69. {comtypes-1.4.11 → comtypes-1.4.13}/comtypes/viewobject.py +29 -5
  70. {comtypes-1.4.11 → comtypes-1.4.13/comtypes.egg-info}/PKG-INFO +5 -5
  71. {comtypes-1.4.11 → comtypes-1.4.13}/comtypes.egg-info/SOURCES.txt +4 -0
  72. {comtypes-1.4.11 → comtypes-1.4.13}/pyproject.toml +12 -1
  73. {comtypes-1.4.11 → comtypes-1.4.13}/setup.cfg +1 -1
  74. comtypes-1.4.11/comtypes/test/test_persist.py +0 -38
  75. comtypes-1.4.11/comtypes/util.py +0 -107
  76. {comtypes-1.4.11 → comtypes-1.4.13}/LICENSE.txt +0 -0
  77. {comtypes-1.4.11 → comtypes-1.4.13}/MANIFEST.in +0 -0
  78. {comtypes-1.4.11 → comtypes-1.4.13}/comtypes/GUID.py +0 -0
  79. {comtypes-1.4.11 → comtypes-1.4.13}/comtypes/_post_coinit/__init__.py +0 -0
  80. {comtypes-1.4.11 → comtypes-1.4.13}/comtypes/_post_coinit/bstr.py +0 -0
  81. {comtypes-1.4.11 → comtypes-1.4.13}/comtypes/_post_coinit/instancemethod.py +0 -0
  82. {comtypes-1.4.11 → comtypes-1.4.13}/comtypes/_safearray.py +0 -0
  83. {comtypes-1.4.11 → comtypes-1.4.13}/comtypes/_tlib_version_checker.py +0 -0
  84. {comtypes-1.4.11 → comtypes-1.4.13}/comtypes/clear_cache.py +0 -0
  85. {comtypes-1.4.11 → comtypes-1.4.13}/comtypes/client/_create.py +0 -0
  86. {comtypes-1.4.11 → comtypes-1.4.13}/comtypes/client/_managing.py +0 -0
  87. {comtypes-1.4.11 → comtypes-1.4.13}/comtypes/connectionpoints.py +0 -0
  88. {comtypes-1.4.11 → comtypes-1.4.13}/comtypes/git.py +0 -0
  89. {comtypes-1.4.11 → comtypes-1.4.13}/comtypes/hints.pyi +0 -0
  90. {comtypes-1.4.11 → comtypes-1.4.13}/comtypes/logutil.py +0 -0
  91. {comtypes-1.4.11 → comtypes-1.4.13}/comtypes/server/w_getopt.py +0 -0
  92. {comtypes-1.4.11 → comtypes-1.4.13}/comtypes/shelllink.py +0 -0
  93. {comtypes-1.4.11 → comtypes-1.4.13}/comtypes/stream.py +0 -0
  94. {comtypes-1.4.11 → comtypes-1.4.13}/comtypes/test/TestComServer.idl +0 -0
  95. {comtypes-1.4.11 → comtypes-1.4.13}/comtypes/test/TestComServer.py +0 -0
  96. {comtypes-1.4.11 → comtypes-1.4.13}/comtypes/test/TestComServer.tlb +0 -0
  97. {comtypes-1.4.11 → comtypes-1.4.13}/comtypes/test/TestDispServer.idl +0 -0
  98. {comtypes-1.4.11 → comtypes-1.4.13}/comtypes/test/TestDispServer.py +0 -0
  99. {comtypes-1.4.11 → comtypes-1.4.13}/comtypes/test/TestDispServer.tlb +0 -0
  100. {comtypes-1.4.11 → comtypes-1.4.13}/comtypes/test/__init__.py +0 -0
  101. {comtypes-1.4.11 → comtypes-1.4.13}/comtypes/test/find_memleak.py +0 -0
  102. {comtypes-1.4.11 → comtypes-1.4.13}/comtypes/test/mylib.idl +0 -0
  103. {comtypes-1.4.11 → comtypes-1.4.13}/comtypes/test/mylib.tlb +0 -0
  104. {comtypes-1.4.11 → comtypes-1.4.13}/comtypes/test/mytypelib.idl +0 -0
  105. {comtypes-1.4.11 → comtypes-1.4.13}/comtypes/test/runtests.py +0 -0
  106. {comtypes-1.4.11 → comtypes-1.4.13}/comtypes/test/setup.py +0 -0
  107. {comtypes-1.4.11 → comtypes-1.4.13}/comtypes/test/test_BSTR.py +0 -0
  108. {comtypes-1.4.11 → comtypes-1.4.13}/comtypes/test/test_DISPPARAMS.py +0 -0
  109. {comtypes-1.4.11 → comtypes-1.4.13}/comtypes/test/test_GUID.py +0 -0
  110. {comtypes-1.4.11 → comtypes-1.4.13}/comtypes/test/test_QueryService.py +0 -0
  111. {comtypes-1.4.11 → comtypes-1.4.13}/comtypes/test/test_avmc.py +0 -0
  112. {comtypes-1.4.11 → comtypes-1.4.13}/comtypes/test/test_basic.py +0 -0
  113. {comtypes-1.4.11 → comtypes-1.4.13}/comtypes/test/test_casesensitivity.py +0 -0
  114. {comtypes-1.4.11 → comtypes-1.4.13}/comtypes/test/test_clear_cache.py +0 -0
  115. {comtypes-1.4.11 → comtypes-1.4.13}/comtypes/test/test_client.py +0 -0
  116. {comtypes-1.4.11 → comtypes-1.4.13}/comtypes/test/test_client_regenerate_modules.py +0 -0
  117. {comtypes-1.4.11 → comtypes-1.4.13}/comtypes/test/test_collections.py +0 -0
  118. {comtypes-1.4.11 → comtypes-1.4.13}/comtypes/test/test_comobject.py +0 -0
  119. {comtypes-1.4.11 → comtypes-1.4.13}/comtypes/test/test_dict.py +0 -0
  120. {comtypes-1.4.11 → comtypes-1.4.13}/comtypes/test/test_dyndispatch.py +0 -0
  121. {comtypes-1.4.11 → comtypes-1.4.13}/comtypes/test/test_errorinfo.py +0 -0
  122. {comtypes-1.4.11 → comtypes-1.4.13}/comtypes/test/test_findgendir.py +0 -0
  123. {comtypes-1.4.11 → comtypes-1.4.13}/comtypes/test/test_ie.py +0 -0
  124. {comtypes-1.4.11 → comtypes-1.4.13}/comtypes/test/test_ienum.py +0 -0
  125. {comtypes-1.4.11 → comtypes-1.4.13}/comtypes/test/test_imfattributes.py +0 -0
  126. {comtypes-1.4.11 → comtypes-1.4.13}/comtypes/test/test_inout_args.py +0 -0
  127. {comtypes-1.4.11 → comtypes-1.4.13}/comtypes/test/test_jscript.js +0 -0
  128. {comtypes-1.4.11 → comtypes-1.4.13}/comtypes/test/test_monikers.py +0 -0
  129. {comtypes-1.4.11 → comtypes-1.4.13}/comtypes/test/test_outparam.py +0 -0
  130. {comtypes-1.4.11 → comtypes-1.4.13}/comtypes/test/test_pump_events.py +0 -0
  131. {comtypes-1.4.11 → comtypes-1.4.13}/comtypes/test/test_safearray.py +0 -0
  132. {comtypes-1.4.11 → comtypes-1.4.13}/comtypes/test/test_sapi.py +0 -0
  133. {comtypes-1.4.11 → comtypes-1.4.13}/comtypes/test/test_server_register.py +0 -0
  134. {comtypes-1.4.11 → comtypes-1.4.13}/comtypes/test/test_shelllink.py +0 -0
  135. {comtypes-1.4.11 → comtypes-1.4.13}/comtypes/test/test_showevents.py +0 -0
  136. {comtypes-1.4.11 → comtypes-1.4.13}/comtypes/test/test_subinterface.py +0 -0
  137. {comtypes-1.4.11 → comtypes-1.4.13}/comtypes/test/test_typeannotator.py +0 -0
  138. {comtypes-1.4.11 → comtypes-1.4.13}/comtypes/test/test_typeinfo.py +0 -0
  139. {comtypes-1.4.11 → comtypes-1.4.13}/comtypes/test/test_w_getopt.py +0 -0
  140. {comtypes-1.4.11 → comtypes-1.4.13}/comtypes/test/test_win32com_interop.py +0 -0
  141. {comtypes-1.4.11 → comtypes-1.4.13}/comtypes/test/test_wmi.py +0 -0
  142. {comtypes-1.4.11 → comtypes-1.4.13}/comtypes/test/urlhist.tlb +0 -0
  143. {comtypes-1.4.11 → comtypes-1.4.13}/comtypes/tools/__init__.py +0 -0
  144. {comtypes-1.4.11 → comtypes-1.4.13}/comtypes/tools/codegenerator/__init__.py +0 -0
  145. {comtypes-1.4.11 → comtypes-1.4.13}/comtypes/tools/codegenerator/modulenamer.py +0 -0
  146. {comtypes-1.4.11 → comtypes-1.4.13}/comtypes/tools/codegenerator/packing.py +0 -0
  147. {comtypes-1.4.11 → comtypes-1.4.13}/comtypes/typeinfo.py +0 -0
  148. {comtypes-1.4.11 → comtypes-1.4.13}/comtypes.egg-info/dependency_links.txt +0 -0
  149. {comtypes-1.4.11 → comtypes-1.4.13}/comtypes.egg-info/entry_points.txt +0 -0
  150. {comtypes-1.4.11 → comtypes-1.4.13}/comtypes.egg-info/top_level.txt +0 -0
  151. {comtypes-1.4.11 → comtypes-1.4.13}/setup.py +0 -0
@@ -1,9 +1,13 @@
1
+ import contextlib
1
2
  import time
2
3
  import unittest
3
4
 
4
5
  import comtypes
5
6
  import comtypes.client
6
- import comtypes.test
7
+
8
+ with contextlib.redirect_stdout(None): # supress warnings
9
+ comtypes.client.GetModule("msvidctl.dll")
10
+ from comtypes.gen import MSVidCtlLib as msvidctl
7
11
 
8
12
  try:
9
13
  # pass Word libUUID
@@ -29,7 +33,7 @@ class Test_Word(unittest.TestCase):
29
33
  def setUp(self):
30
34
  try:
31
35
  comtypes.client.GetActiveObject("Word.Application")
32
- except WindowsError:
36
+ except OSError:
33
37
  pass
34
38
  else:
35
39
  # seems word is running, we cannot test this.
@@ -67,5 +71,18 @@ class Test_Word(unittest.TestCase):
67
71
  comtypes.client.GetActiveObject("Word.Application")
68
72
 
69
73
 
74
+ class Test_MSVidCtlLib(unittest.TestCase):
75
+ def test_register_and_revoke(self):
76
+ vidctl = comtypes.client.CreateObject(msvidctl.MSVidCtl)
77
+ with self.assertRaises(WindowsError):
78
+ comtypes.client.GetActiveObject(msvidctl.MSVidCtl)
79
+ handle = comtypes.client.RegisterActiveObject(vidctl, msvidctl.MSVidCtl)
80
+ activeobj = comtypes.client.GetActiveObject(msvidctl.MSVidCtl)
81
+ self.assertEqual(vidctl, activeobj)
82
+ comtypes.client.RevokeActiveObject(handle)
83
+ with self.assertRaises(WindowsError):
84
+ comtypes.client.GetActiveObject(msvidctl.MSVidCtl)
85
+
86
+
70
87
  if __name__ == "__main__":
71
88
  unittest.main()
@@ -1,3 +1,4 @@
1
+ import doctest
1
2
  import unittest as ut
2
3
 
3
4
  import comtypes.hresult
@@ -46,3 +47,41 @@ class Test_HRESULT_FROM_WIN32(ut.TestCase):
46
47
  ]:
47
48
  with self.subTest(w32=w32, hr=hr):
48
49
  self.assertEqual(comtypes.hresult.HRESULT_FROM_WIN32(w32), hr)
50
+
51
+
52
+ class Test_signed32bithex_to_int(ut.TestCase):
53
+ def test(self):
54
+ for val, expected in [
55
+ ("0x00000000", comtypes.hresult.S_OK),
56
+ ("0x00000001", comtypes.hresult.S_FALSE),
57
+ ("0x8000FFFF", comtypes.hresult.E_UNEXPECTED),
58
+ ("0x80004002", comtypes.hresult.E_NOINTERFACE),
59
+ # boundary values
60
+ ("0x7FFFFFFF", 2147483647),
61
+ ("0x80000000", -2147483648),
62
+ ("0xFFFFFFFF", -1),
63
+ ]:
64
+ with self.subTest(val=val, expected=expected):
65
+ self.assertEqual(comtypes.hresult.signed32bithex_to_int(val), expected)
66
+
67
+
68
+ class Test_int_to_signed32bithex(ut.TestCase):
69
+ def test(self):
70
+ for val, expected in [
71
+ (comtypes.hresult.S_OK, "0x00000000"),
72
+ (comtypes.hresult.S_FALSE, "0x00000001"),
73
+ (comtypes.hresult.E_UNEXPECTED, "0x8000FFFF"),
74
+ (comtypes.hresult.E_NOINTERFACE, "0x80004002"),
75
+ ]:
76
+ with self.subTest(val=val, expected=expected):
77
+ self.assertEqual(comtypes.hresult.int_to_signed32bithex(val), expected)
78
+
79
+
80
+ class DocTest(ut.TestCase):
81
+ def test(self):
82
+ doctest.testmod(
83
+ comtypes.hresult,
84
+ verbose=False,
85
+ optionflags=doctest.ELLIPSIS,
86
+ raise_on_error=True,
87
+ )
@@ -0,0 +1,117 @@
1
+ import threading
2
+ import time
3
+ import unittest
4
+ from ctypes import WinDLL
5
+ from typing import Tuple
6
+ from unittest.mock import Mock, patch
7
+
8
+ import comtypes.messageloop
9
+ from comtypes.messageloop import _MessageLoop
10
+
11
+ _user32 = WinDLL("user32")
12
+ _kernel32 = WinDLL("kernel32")
13
+
14
+ PostThreadMessageW = _user32.PostThreadMessageW
15
+ PostQuitMessage = _user32.PostQuitMessage
16
+ GetCurrentThreadId = _kernel32.GetCurrentThreadId
17
+
18
+ WM_QUIT = 0x0012
19
+ WM_USER = 0x0400
20
+
21
+
22
+ class InsertAndRemoveFilterTest(unittest.TestCase):
23
+ def test_insert_and_remove_filter(self):
24
+ msgloop = _MessageLoop()
25
+ flt = Mock(return_value=[False])
26
+ msgloop.insert_filter(flt)
27
+ msgloop.remove_filter(flt)
28
+
29
+
30
+ class RunInThreadTest(unittest.TestCase):
31
+ def setUp(self):
32
+ self.msgloop = _MessageLoop()
33
+
34
+ def run_msgloop_in_thread(self) -> Tuple[threading.Thread, int]:
35
+ """Helper method to run message loop in a separate thread."""
36
+ thread_ids = []
37
+
38
+ def thread_target():
39
+ with threading.Lock():
40
+ thread_ids.append(GetCurrentThreadId())
41
+ self.msgloop.run()
42
+
43
+ th = threading.Thread(target=thread_target, daemon=True)
44
+ th.start()
45
+ time.sleep(0.1) # Give thread time to start
46
+ assert len(thread_ids) == 1, "Thread ID was not captured"
47
+ return th, thread_ids[0]
48
+
49
+ def test_quit_message(self):
50
+ th, thread_id = self.run_msgloop_in_thread()
51
+ # Post WM_QUIT to terminate the message loop
52
+ PostThreadMessageW(thread_id, WM_QUIT, 0, 0)
53
+ th.join(timeout=1.0) # Ensure the thread completes
54
+ self.assertFalse(th.is_alive())
55
+
56
+ def test_blocking_filter(self):
57
+ blocking_filter = Mock(return_value=[True]) # Blocking
58
+ self.msgloop.insert_filter(blocking_filter)
59
+ th, thread_id = self.run_msgloop_in_thread()
60
+ # Post a regular message first (should be filtered)
61
+ PostThreadMessageW(thread_id, WM_USER, 0, 0)
62
+ time.sleep(0.1) # Give time for message processing
63
+ # Post WM_QUIT to terminate the message loop
64
+ PostThreadMessageW(thread_id, WM_QUIT, 0, 0)
65
+ th.join(timeout=2.0) # Wait for completion
66
+ self.assertFalse(th.is_alive())
67
+ blocking_filter.assert_called()
68
+
69
+ def test_nonblocking_filter(self):
70
+ message_filter = Mock(return_value=[]) # Non-blocking
71
+ self.msgloop.insert_filter(message_filter)
72
+ th, thread_id = self.run_msgloop_in_thread()
73
+ # Post some test messages
74
+ PostThreadMessageW(thread_id, WM_USER, 123, 456)
75
+ PostThreadMessageW(thread_id, WM_USER, 789, 101112)
76
+ time.sleep(0.3) # Give time for message processing
77
+ # Post WM_QUIT to terminate the message loop
78
+ PostThreadMessageW(thread_id, WM_QUIT, 0, 0)
79
+ th.join(timeout=2.0) # Wait for completion
80
+ self.assertFalse(th.is_alive())
81
+ message_filter.assert_called()
82
+
83
+ def test_multiple_filters(self):
84
+ filter1 = Mock(return_value=[]) # Non-blocking
85
+ filter2 = Mock(return_value=[True]) # Blocking
86
+ filter3 = Mock(return_value=[]) # should not be called
87
+ self.msgloop.insert_filter(filter1, index=0)
88
+ self.msgloop.insert_filter(filter2, index=1)
89
+ self.msgloop.insert_filter(filter3, index=2)
90
+ th, thread_id = self.run_msgloop_in_thread()
91
+ PostThreadMessageW(thread_id, WM_USER, 123, 456)
92
+ time.sleep(1.0) # Give time for message processing
93
+ # Post WM_QUIT to terminate the message loop
94
+ PostThreadMessageW(thread_id, WM_QUIT, 0, 0)
95
+ th.join(timeout=2.0) # Wait for completion
96
+ self.assertFalse(th.is_alive())
97
+ # any() evaluates filters in order: filter1 (falsy) then filter2 (truthy)
98
+ # any() stops at filter2 because it returns a truthy value.
99
+ filter1.assert_called()
100
+ filter2.assert_called()
101
+ filter3.assert_not_called()
102
+
103
+ def test_raises_error_on_getmsg(self):
104
+ # Mock GetMessage to return -1 (error condition)
105
+ with patch.object(comtypes.messageloop, "GetMessage", return_value=-1):
106
+ with self.assertRaises(OSError):
107
+ self.msgloop.run()
108
+
109
+ def test_exit_normally_without_error(self):
110
+ def run_and_exit_quickly():
111
+ PostQuitMessage(0) # Exit immediately
112
+ self.msgloop.run()
113
+
114
+ th = threading.Thread(target=run_and_exit_quickly, daemon=True)
115
+ th.start()
116
+ th.join(timeout=1.0)
117
+ self.assertFalse(th.is_alive())
@@ -1,5 +1,3 @@
1
- # coding: utf-8
2
-
3
1
  import unittest
4
2
  from ctypes import HRESULT, POINTER, c_int, pointer
5
3
 
@@ -11,7 +11,7 @@ from comtypes.client import CreateObject
11
11
  try:
12
12
  GUID.from_progid("MSScriptControl.ScriptControl")
13
13
  CreateObject("MSScriptControl.ScriptControl")
14
- except WindowsError:
14
+ except OSError:
15
15
  # doesn't exist on Windows CE or in 64-bit.
16
16
  pass
17
17
  else:
@@ -3,7 +3,7 @@ import functools
3
3
  import importlib
4
4
  import inspect
5
5
  import unittest
6
- from ctypes import POINTER, c_double, c_long, pointer
6
+ from ctypes import POINTER, c_double, c_long, c_longlong, pointer
7
7
  from decimal import Decimal
8
8
 
9
9
  import comtypes._npsupport
@@ -16,6 +16,7 @@ from comtypes.automation import (
16
16
  VT_BSTR,
17
17
  VT_DATE,
18
18
  VT_I4,
19
+ VT_I8,
19
20
  VT_VARIANT,
20
21
  _midlSAFEARRAY,
21
22
  )
@@ -23,13 +24,15 @@ from comtypes.safearray import safearray_as_ndarray
23
24
 
24
25
  try:
25
26
  import numpy
27
+
28
+ IMPORT_NUMPY_FAILED = False
26
29
  except ImportError:
27
- numpy = None
30
+ IMPORT_NUMPY_FAILED = True
28
31
 
29
32
 
30
33
  def setUpModule():
31
34
  """Only run the module if we can import numpy."""
32
- if numpy is None:
35
+ if IMPORT_NUMPY_FAILED:
33
36
  raise unittest.SkipTest("Skipping test_npsupport as numpy not installed.")
34
37
 
35
38
 
@@ -70,9 +73,7 @@ def enabled_disabled(disabled_error):
70
73
  from comtypes import npsupport
71
74
 
72
75
  if npsupport.enabled:
73
- raise EnvironmentError(
74
- "Expected numpy interop not to be enabled but it is."
75
- )
76
+ raise OSError("Expected numpy interop not to be enabled but it is.")
76
77
  with self.assertRaises(disabled_error):
77
78
  func(self)
78
79
 
@@ -112,10 +113,6 @@ class NumpySupportTestCase(unittest.TestCase):
112
113
  self.assertTrue(isinstance(fourth, numpy.ndarray))
113
114
  self.assertTrue(isinstance(fifth, tuple))
114
115
 
115
- @unittest.skip(
116
- "Skipping because numpy cannot currently create an array of variants "
117
- "because it doesn't recognise the VARIANT_BOOL typecode 'v'."
118
- )
119
116
  def test_datetime64_ndarray(self):
120
117
  comtypes.npsupport.enable()
121
118
  dates = numpy.array(
@@ -184,7 +181,7 @@ class NumpySupportTestCase(unittest.TestCase):
184
181
  def test_VT_I4_ndarray(self):
185
182
  t = _midlSAFEARRAY(c_long)
186
183
 
187
- in_arr = numpy.array([11, 22, 33])
184
+ in_arr = numpy.array([11, 22, 33], dtype=numpy.int32)
188
185
  sa = t.from_param(in_arr)
189
186
 
190
187
  arr = get_ndarray(sa)
@@ -194,6 +191,20 @@ class NumpySupportTestCase(unittest.TestCase):
194
191
  self.assertTrue((arr == in_arr).all())
195
192
  self.assertEqual(SafeArrayGetVartype(sa), VT_I4)
196
193
 
194
+ @enabled_disabled(disabled_error=ValueError)
195
+ def test_VT_I8_ndarray(self):
196
+ t = _midlSAFEARRAY(c_longlong)
197
+
198
+ in_arr = numpy.array([11, 22, 33], dtype=numpy.int64)
199
+ sa = t.from_param(in_arr)
200
+
201
+ arr = get_ndarray(sa)
202
+
203
+ self.assertTrue(isinstance(arr, numpy.ndarray))
204
+ self.assertEqual(numpy.dtype(int), arr.dtype)
205
+ self.assertTrue((arr == in_arr).all())
206
+ self.assertEqual(SafeArrayGetVartype(sa), VT_I8)
207
+
197
208
  @enabled_disabled(disabled_error=ValueError)
198
209
  def test_array(self):
199
210
  t = _midlSAFEARRAY(c_double)
@@ -0,0 +1,71 @@
1
+ import tempfile
2
+ import unittest as ut
3
+ from _ctypes import COMError
4
+ from pathlib import Path
5
+
6
+ from comtypes import GUID, CoCreateInstance, IPersist, hresult, persist
7
+ from comtypes.automation import VARIANT
8
+
9
+ CLSID_ShellLink = GUID("{00021401-0000-0000-C000-000000000046}")
10
+
11
+
12
+ class Test_IPersist(ut.TestCase):
13
+ def test_GetClassID(self):
14
+ p = CoCreateInstance(CLSID_ShellLink).QueryInterface(IPersist)
15
+ self.assertEqual(p.GetClassID(), CLSID_ShellLink)
16
+
17
+
18
+ class Test_IPersistFile(ut.TestCase):
19
+ def setUp(self):
20
+ td = tempfile.TemporaryDirectory()
21
+ self.addCleanup(td.cleanup)
22
+ self.tmp_dir = Path(td.name)
23
+
24
+ def _create_pf(self) -> persist.IPersistFile:
25
+ return CoCreateInstance(CLSID_ShellLink).QueryInterface(persist.IPersistFile)
26
+
27
+ def test_load(self):
28
+ pf = self._create_pf()
29
+ tgt_file = (self.tmp_dir / "tgt.txt").resolve()
30
+ tgt_file.touch()
31
+ pf.Load(str(tgt_file), persist.STGM_DIRECT)
32
+ self.assertEqual(pf.GetCurFile(), str(tgt_file))
33
+
34
+ def test_save(self):
35
+ pf = self._create_pf()
36
+ tgt_file = self.tmp_dir / "tgt.txt"
37
+ self.assertFalse(tgt_file.exists())
38
+ pf.Save(str(tgt_file), True)
39
+ self.assertEqual(pf.GetCurFile(), str(tgt_file))
40
+ self.assertTrue(tgt_file.exists())
41
+
42
+
43
+ class Test_DictPropertyBag(ut.TestCase):
44
+ def create_itf_ptr(self) -> persist.IPropertyBag:
45
+ # Create a DictPropertyBag instance with some initial values
46
+ impl = persist.DictPropertyBag(Key1="value1", Key2=123, Key3=True)
47
+ # Get the IPropertyBag interface pointer
48
+ itf = impl.QueryInterface(persist.IPropertyBag)
49
+ return itf
50
+
51
+ def test_read_existing_properties(self):
52
+ itf = self.create_itf_ptr()
53
+ self.assertEqual(itf.Read("Key1", VARIANT(), None), "value1")
54
+ self.assertEqual(itf.Read("Key2", VARIANT(), None), 123)
55
+ self.assertEqual(itf.Read("Key3", VARIANT(), None), True)
56
+
57
+ def test_write_new_property(self):
58
+ itf = self.create_itf_ptr()
59
+ itf.Write("Key4", "new_value")
60
+ self.assertEqual(itf.Read("Key4", VARIANT(), None), "new_value")
61
+
62
+ def test_update_existing_property(self):
63
+ itf = self.create_itf_ptr()
64
+ itf.Write("Key1", "updated_value")
65
+ self.assertEqual(itf.Read("Key1", VARIANT(), None), "updated_value")
66
+
67
+ def test_read_non_existent_property(self):
68
+ itf = self.create_itf_ptr()
69
+ with self.assertRaises(COMError) as cm:
70
+ itf.Read("NonExistentProp", VARIANT(), None)
71
+ self.assertEqual(cm.exception.hresult, hresult.E_INVALIDARG)
@@ -1,5 +1,3 @@
1
- # coding: utf-8
2
-
3
1
  import unittest
4
2
  from ctypes import byref, pointer, sizeof
5
3
 
@@ -8,7 +8,7 @@ import comtypes.typeinfo
8
8
  raise unittest.SkipTest("This test module cannot run as-is. Investigate why")
9
9
 
10
10
 
11
- class TypeLib(object):
11
+ class TypeLib:
12
12
  """This class collects IDL code fragments and eventually writes
13
13
  them into a .IDL file. The compile() method compiles the IDL file
14
14
  into a typelibrary and registers it. A function is also
@@ -64,7 +64,7 @@ class TypeLib(object):
64
64
  return tlb_path
65
65
 
66
66
 
67
- class Interface(object):
67
+ class Interface:
68
68
  def __init__(self, header):
69
69
  self.header = header
70
70
  self.code = ""
@@ -257,7 +257,7 @@ class MyServer(comtypes.CoClass, ConnectableObjectMixin):
257
257
  def test_events(self):
258
258
  p = wrap(self.create())
259
259
 
260
- class Handler(object):
260
+ class Handler:
261
261
  called = 0
262
262
 
263
263
  def OnSomething(self, this):
@@ -269,7 +269,7 @@ class MyServer(comtypes.CoClass, ConnectableObjectMixin):
269
269
  p.DoSomething()
270
270
  self.assertEqual(handler.called, 1)
271
271
 
272
- class Handler(object):
272
+ class Handler:
273
273
  called = 0
274
274
 
275
275
  def IMyEventInterface_OnSomething(self):
@@ -292,7 +292,7 @@ class MyServer(comtypes.CoClass, ConnectableObjectMixin):
292
292
  def test_DoSomethingElse(self):
293
293
  p = wrap(self.create())
294
294
 
295
- class Handler(object):
295
+ class Handler:
296
296
  called = 0
297
297
 
298
298
  def OnSomethingElse(self):
@@ -305,7 +305,7 @@ class MyServer(comtypes.CoClass, ConnectableObjectMixin):
305
305
  p.DoSomethingElse()
306
306
  self.assertEqual(handler.called, 1)
307
307
 
308
- class Handler(object):
308
+ class Handler:
309
309
  called = 0
310
310
 
311
311
  def OnSomethingElse(self, this, presult):
@@ -58,7 +58,7 @@ class Test_IStorage(unittest.TestCase):
58
58
  filepath = Path(storage.Stat(STATFLAG_DEFAULT).pwcsName)
59
59
  self.assertTrue(filepath.exists())
60
60
  stream = storage.CreateStream("example", self.CREATE_STM_FLAG, 0, 0)
61
- test_data = "Some data".encode("utf-8")
61
+ test_data = b"Some data"
62
62
  pv = (c_ubyte * len(test_data)).from_buffer(bytearray(test_data))
63
63
  stream.RemoteWrite(pv, len(test_data))
64
64
  stream.Commit(STGC_DEFAULT)
@@ -7,7 +7,9 @@ import comtypes.client
7
7
  comtypes.client.GetModule("portabledeviceapi.dll")
8
8
  from comtypes.gen.PortableDeviceApiLib import IStream
9
9
 
10
+ STATFLAG_DEFAULT = 0
10
11
  STGC_DEFAULT = 0
12
+ STGTY_STREAM = 2
11
13
  STREAM_SEEK_SET = 0
12
14
  STREAM_SEEK_CUR = 1
13
15
  STREAM_SEEK_END = 2
@@ -35,7 +37,7 @@ def _create_stream() -> IStream:
35
37
  class Test_RemoteWrite(ut.TestCase):
36
38
  def test_RemoteWrite(self):
37
39
  stream = _create_stream()
38
- test_data = "Some data".encode("utf-8")
40
+ test_data = b"Some data"
39
41
  pv = (c_ubyte * len(test_data)).from_buffer(bytearray(test_data))
40
42
 
41
43
  written = stream.RemoteWrite(pv, len(test_data))
@@ -47,7 +49,7 @@ class Test_RemoteWrite(ut.TestCase):
47
49
  class Test_RemoteRead(ut.TestCase):
48
50
  def test_RemoteRead(self):
49
51
  stream = _create_stream()
50
- test_data = "Some data".encode("utf-8")
52
+ test_data = b"Some data"
51
53
  pv = (c_ubyte * len(test_data)).from_buffer(bytearray(test_data))
52
54
  stream.RemoteWrite(pv, len(test_data))
53
55
 
@@ -125,6 +127,24 @@ class Test_RemoteCopyTo(ut.TestCase):
125
127
  self.assertEqual(bytearray(dst_buf)[0:dst_read], test_data)
126
128
 
127
129
 
130
+ class Test_Stat(ut.TestCase):
131
+ # https://learn.microsoft.com/en-us/windows/win32/api/objidl/nf-objidl-istream-stat
132
+ # https://learn.microsoft.com/en-us/windows/win32/api/objidl/ns-objidl-statstg
133
+ def test_returns_statstg_from_no_modified_stream(self):
134
+ stream = _create_stream()
135
+ statstg = stream.Stat(STATFLAG_DEFAULT)
136
+ self.assertIsNone(statstg.pwcsName)
137
+ self.assertEqual(statstg.type, STGTY_STREAM)
138
+ self.assertEqual(statstg.cbSize, 0)
139
+ mt, ct, at = statstg.mtime, statstg.ctime, statstg.atime
140
+ self.assertTrue(mt.dwLowDateTime == ct.dwLowDateTime == at.dwLowDateTime)
141
+ self.assertTrue(mt.dwHighDateTime == ct.dwHighDateTime == at.dwHighDateTime)
142
+ self.assertEqual(statstg.grfMode, 0)
143
+ self.assertEqual(statstg.grfLocksSupported, 0)
144
+ self.assertEqual(statstg.clsid, comtypes.GUID())
145
+ self.assertEqual(statstg.grfStateBits, 0)
146
+
147
+
128
148
  class Test_Clone(ut.TestCase):
129
149
  def test_Clone(self):
130
150
  orig = _create_stream()
@@ -18,7 +18,7 @@ from comtypes.gen import urlhistLib
18
18
  # generated code directly is to monkey-patch the
19
19
  # _STATURL.__ctypes_from_outparam__ method like this.
20
20
  @Patch(urlhistLib._STATURL)
21
- class _(object):
21
+ class _:
22
22
  def __ctypes_from_outparam__(self):
23
23
  from comtypes.util import cast_field
24
24