comtypes 1.4.6__zip → 1.4.8__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 (138) hide show
  1. {comtypes-1.4.6 → comtypes-1.4.8}/CHANGES.txt +36 -0
  2. {comtypes-1.4.6/comtypes.egg-info → comtypes-1.4.8}/PKG-INFO +32 -38
  3. {comtypes-1.4.6 → comtypes-1.4.8}/README.md +30 -36
  4. {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/__init__.py +1 -1
  5. {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/_comobject.py +0 -1
  6. {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/_memberspec.py +1 -1
  7. comtypes-1.4.8/comtypes/_meta.py +95 -0
  8. {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/_npsupport.py +2 -1
  9. {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/_post_coinit/__init__.py +1 -0
  10. {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/_post_coinit/bstr.py +13 -6
  11. {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/_post_coinit/misc.py +20 -39
  12. {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/_post_coinit/unknwn.py +94 -69
  13. {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/automation.py +9 -15
  14. {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/client/__init__.py +21 -31
  15. {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/client/_code_cache.py +1 -0
  16. {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/client/_events.py +0 -1
  17. {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/client/_generate.py +5 -5
  18. {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/client/lazybind.py +0 -5
  19. {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/git.py +1 -0
  20. {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/hints.pyi +6 -4
  21. {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/persist.py +19 -3
  22. {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/safearray.py +2 -6
  23. {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/server/__init__.py +1 -0
  24. {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/server/register.py +1 -0
  25. {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/shelllink.py +90 -34
  26. {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/test/TestComServer.py +1 -1
  27. {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/test/TestDispServer.py +1 -1
  28. {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/test/__init__.py +0 -1
  29. {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/test/find_memleak.py +0 -1
  30. {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/test/test_agilent.py +0 -1
  31. {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/test/test_clear_cache.py +1 -0
  32. {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/test/test_client.py +9 -0
  33. {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/test/test_createwrappers.py +0 -2
  34. {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/test/test_dispinterface.py +0 -1
  35. {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/test/test_excel.py +0 -1
  36. {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/test/test_inout_args.py +1 -1
  37. comtypes-1.4.8/comtypes/test/test_monikers.py +70 -0
  38. comtypes-1.4.8/comtypes/test/test_persist.py +40 -0
  39. {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/test/test_server.py +6 -0
  40. comtypes-1.4.8/comtypes/test/test_shelllink.py +129 -0
  41. comtypes-1.4.8/comtypes/test/test_storage.py +127 -0
  42. comtypes-1.4.8/comtypes/test/test_stream.py +131 -0
  43. {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/test/test_typeannotator.py +1 -1
  44. {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/test/test_urlhistory.py +1 -0
  45. {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/test/test_variant.py +0 -2
  46. {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/tools/codegenerator/namespaces.py +97 -27
  47. {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/tools/codegenerator/typeannotator.py +2 -4
  48. {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/tools/tlbparser.py +33 -32
  49. {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/tools/typedesc.py +2 -2
  50. {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/typeinfo.py +42 -43
  51. {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/util.py +1 -0
  52. {comtypes-1.4.6 → comtypes-1.4.8/comtypes.egg-info}/PKG-INFO +32 -38
  53. {comtypes-1.4.6 → comtypes-1.4.8}/comtypes.egg-info/SOURCES.txt +5 -1
  54. comtypes-1.4.8/pyproject.toml +54 -0
  55. {comtypes-1.4.6 → comtypes-1.4.8}/setup.cfg +1 -1
  56. comtypes-1.4.6/comtypes/_meta.py +0 -70
  57. comtypes-1.4.6/comtypes/test/test_istream.py +0 -53
  58. comtypes-1.4.6/pyproject.toml +0 -3
  59. {comtypes-1.4.6 → comtypes-1.4.8}/LICENSE.txt +0 -0
  60. {comtypes-1.4.6 → comtypes-1.4.8}/MANIFEST.in +0 -0
  61. {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/GUID.py +0 -0
  62. {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/_idl_stuff.py +0 -0
  63. {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/_py_instance_method.py +0 -0
  64. {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/_safearray.py +0 -0
  65. {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/_tlib_version_checker.py +0 -0
  66. {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/clear_cache.py +0 -0
  67. {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/client/_constants.py +0 -0
  68. {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/client/dynamic.py +0 -0
  69. {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/connectionpoints.py +0 -0
  70. {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/errorinfo.py +0 -0
  71. {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/hresult.py +0 -0
  72. {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/logutil.py +0 -0
  73. {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/messageloop.py +0 -0
  74. {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/patcher.py +0 -0
  75. {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/server/automation.py +0 -0
  76. {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/server/connectionpoints.py +0 -0
  77. {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/server/inprocserver.py +0 -0
  78. {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/server/localserver.py +0 -0
  79. {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/server/w_getopt.py +0 -0
  80. {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/stream.py +0 -0
  81. {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/test/TestComServer.idl +0 -0
  82. {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/test/TestComServer.tlb +0 -0
  83. {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/test/TestDispServer.idl +0 -0
  84. {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/test/TestDispServer.tlb +0 -0
  85. {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/test/mylib.idl +0 -0
  86. {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/test/mylib.tlb +0 -0
  87. {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/test/mytypelib.idl +0 -0
  88. {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/test/runtests.py +0 -0
  89. {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/test/setup.py +0 -0
  90. {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/test/test_BSTR.py +0 -0
  91. {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/test/test_DISPPARAMS.py +0 -0
  92. {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/test/test_GUID.py +0 -0
  93. {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/test/test_QueryService.py +0 -0
  94. {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/test/test_avmc.py +0 -0
  95. {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/test/test_basic.py +0 -0
  96. {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/test/test_casesensitivity.py +0 -0
  97. {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/test/test_client_dynamic.py +0 -0
  98. {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/test/test_client_regenerate_modules.py +0 -0
  99. {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/test/test_collections.py +0 -0
  100. {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/test/test_comserver.py +0 -0
  101. {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/test/test_dict.py +0 -0
  102. {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/test/test_dispifc_records.py +0 -0
  103. {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/test/test_dispifc_safearrays.py +0 -0
  104. {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/test/test_dyndispatch.py +0 -0
  105. {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/test/test_findgendir.py +0 -0
  106. {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/test/test_getactiveobj.py +0 -0
  107. {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/test/test_ie.py +0 -0
  108. {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/test/test_ienum.py +0 -0
  109. {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/test/test_imfattributes.py +0 -0
  110. {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/test/test_jscript.js +0 -0
  111. {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/test/test_midl_safearray_create.py +0 -0
  112. {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/test/test_msscript.py +0 -0
  113. {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/test/test_npsupport.py +0 -0
  114. {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/test/test_outparam.py +0 -0
  115. {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/test/test_propputref.py +0 -0
  116. {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/test/test_pump_events.py +0 -0
  117. {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/test/test_recordinfo.py +0 -0
  118. {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/test/test_safearray.py +0 -0
  119. {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/test/test_sapi.py +0 -0
  120. {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/test/test_showevents.py +0 -0
  121. {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/test/test_subinterface.py +0 -0
  122. {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/test/test_typeinfo.py +0 -0
  123. {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/test/test_win32com_interop.py +0 -0
  124. {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/test/test_wmi.py +0 -0
  125. {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/test/test_word.py +0 -0
  126. {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/test/urlhist.tlb +0 -0
  127. {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/tools/__init__.py +0 -0
  128. {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/tools/codegenerator/__init__.py +0 -0
  129. {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/tools/codegenerator/codegenerator.py +0 -0
  130. {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/tools/codegenerator/helpers.py +0 -0
  131. {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/tools/codegenerator/modulenamer.py +0 -0
  132. {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/tools/codegenerator/packing.py +0 -0
  133. {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/tools/typedesc_base.py +0 -0
  134. {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/viewobject.py +0 -0
  135. {comtypes-1.4.6 → comtypes-1.4.8}/comtypes.egg-info/dependency_links.txt +0 -0
  136. {comtypes-1.4.6 → comtypes-1.4.8}/comtypes.egg-info/entry_points.txt +0 -0
  137. {comtypes-1.4.6 → comtypes-1.4.8}/comtypes.egg-info/top_level.txt +0 -0
  138. {comtypes-1.4.6 → comtypes-1.4.8}/setup.py +0 -0
@@ -0,0 +1,127 @@
1
+ from _ctypes import COMError
2
+ from ctypes import byref, c_ubyte, POINTER
3
+ import contextlib
4
+ from pathlib import Path
5
+ import unittest
6
+
7
+ import comtypes
8
+ import comtypes.client
9
+
10
+
11
+ with contextlib.redirect_stdout(None): # supress warnings
12
+ mod = comtypes.client.GetModule("msvidctl.dll")
13
+
14
+ from comtypes.gen.MSVidCtlLib import IStorage
15
+
16
+
17
+ STATFLAG_DEFAULT = 0
18
+ STGC_DEFAULT = 0
19
+ STGM_CREATE = 0x00001000
20
+ STGM_DIRECT = 0x00000000
21
+ STGM_DELETEONRELEASE = 0x04000000
22
+ STGM_READ = 0x00000000
23
+ STGM_READWRITE = 0x00000002
24
+ STGM_SHARE_EXCLUSIVE = 0x00000010
25
+ STGM_TRANSACTED = 0x00010000
26
+ STGMOVE_MOVE = 0
27
+ STREAM_SEEK_SET = 0
28
+
29
+ STG_E_PATHNOTFOUND = -2147287038
30
+
31
+
32
+ class Test_IStorage(unittest.TestCase):
33
+ CREATE_DOC_FLAG = (
34
+ STGM_DIRECT
35
+ | STGM_READWRITE
36
+ | STGM_CREATE
37
+ | STGM_SHARE_EXCLUSIVE
38
+ | STGM_DELETEONRELEASE
39
+ )
40
+ CREATE_STM_FLAG = STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE
41
+ OPEN_STM_FLAG = STGM_READ | STGM_SHARE_EXCLUSIVE
42
+ CREATE_STG_FLAG = STGM_TRANSACTED | STGM_READWRITE | STGM_SHARE_EXCLUSIVE
43
+ OPEN_STG_FLAG = STGM_TRANSACTED | STGM_READWRITE | STGM_SHARE_EXCLUSIVE
44
+
45
+ def _create_docfile(self) -> IStorage:
46
+ stg = POINTER(IStorage)()
47
+ comtypes._ole32.StgCreateDocfile(None, self.CREATE_DOC_FLAG, 0, byref(stg))
48
+ return stg # type: ignore
49
+
50
+ def test_CreateStream(self):
51
+ storage = self._create_docfile()
52
+ filepath = Path(storage.Stat(STATFLAG_DEFAULT).pwcsName)
53
+ self.assertTrue(filepath.exists())
54
+ stream = storage.CreateStream("example", self.CREATE_STM_FLAG, 0, 0)
55
+ test_data = "Some data".encode("utf-8")
56
+ pv = (c_ubyte * len(test_data)).from_buffer(bytearray(test_data))
57
+ stream.RemoteWrite(pv, len(test_data))
58
+ stream.Commit(STGC_DEFAULT)
59
+ stream.RemoteSeek(0, STREAM_SEEK_SET)
60
+ read_buffer, data_read = stream.RemoteRead(1024)
61
+ self.assertEqual(bytearray(read_buffer)[0:data_read], test_data)
62
+ storage.Commit(STGC_DEFAULT)
63
+ self.assertTrue(filepath.exists())
64
+ del storage
65
+ self.assertFalse(filepath.exists())
66
+
67
+ def test_CreateStorage(self):
68
+ parent = self._create_docfile()
69
+ child = parent.CreateStorage("child", self.CREATE_STG_FLAG, 0, 0)
70
+ self.assertEqual("child", child.Stat(STATFLAG_DEFAULT).pwcsName)
71
+
72
+ def test_OpenStorage(self):
73
+ parent = self._create_docfile()
74
+ created_child = parent.CreateStorage("child", self.CREATE_STG_FLAG, 0, 0)
75
+ del created_child
76
+ opened_child = parent.OpenStorage("child", None, self.OPEN_STG_FLAG, None, 0)
77
+ self.assertEqual("child", opened_child.Stat(STATFLAG_DEFAULT).pwcsName)
78
+
79
+ def test_RemoteCopyTo(self):
80
+ src_stg = self._create_docfile()
81
+ src_stg.CreateStorage("child", self.CREATE_STG_FLAG, 0, 0)
82
+ dst_stg = self._create_docfile()
83
+ src_stg.RemoteCopyTo(0, None, None, dst_stg)
84
+ src_stg.Commit(STGC_DEFAULT)
85
+ del src_stg
86
+ opened_stg = dst_stg.OpenStorage("child", None, self.OPEN_STG_FLAG, None, 0)
87
+ self.assertEqual("child", opened_stg.Stat(STATFLAG_DEFAULT).pwcsName)
88
+
89
+ def test_MoveElementTo(self):
90
+ src_stg = self._create_docfile()
91
+ src_stg.CreateStorage("foo", self.CREATE_STG_FLAG, 0, 0)
92
+ dst_stg = self._create_docfile()
93
+ src_stg.MoveElementTo("foo", dst_stg, "bar", STGMOVE_MOVE)
94
+ opened_stg = dst_stg.OpenStorage("bar", None, self.OPEN_STG_FLAG, None, 0)
95
+ self.assertEqual("bar", opened_stg.Stat(STATFLAG_DEFAULT).pwcsName)
96
+ with self.assertRaises(COMError) as ctx:
97
+ src_stg.OpenStorage("foo", None, self.OPEN_STG_FLAG, None, 0)
98
+ self.assertEqual(ctx.exception.hresult, STG_E_PATHNOTFOUND)
99
+
100
+ def test_Revert(self):
101
+ storage = self._create_docfile()
102
+ foo = storage.CreateStorage("foo", self.CREATE_STG_FLAG, 0, 0)
103
+ foo.CreateStorage("bar", self.CREATE_STG_FLAG, 0, 0)
104
+ bar = foo.OpenStorage("bar", None, self.OPEN_STG_FLAG, None, 0)
105
+ self.assertEqual("bar", bar.Stat(STATFLAG_DEFAULT).pwcsName)
106
+ foo.Revert()
107
+ with self.assertRaises(COMError) as ctx:
108
+ foo.OpenStorage("bar", None, self.OPEN_STG_FLAG, None, 0)
109
+ self.assertEqual(ctx.exception.hresult, STG_E_PATHNOTFOUND)
110
+
111
+ def test_DestroyElement(self):
112
+ storage = self._create_docfile()
113
+ storage.CreateStorage("example", self.CREATE_STG_FLAG, 0, 0)
114
+ storage.DestroyElement("example")
115
+ with self.assertRaises(COMError) as ctx:
116
+ storage.OpenStorage("example", None, self.OPEN_STG_FLAG, None, 0)
117
+ self.assertEqual(ctx.exception.hresult, STG_E_PATHNOTFOUND)
118
+
119
+ def test_RenameElement(self):
120
+ storage = self._create_docfile()
121
+ storage.CreateStorage("example", self.CREATE_STG_FLAG, 0, 0)
122
+ storage.RenameElement("example", "sample")
123
+ sample = storage.OpenStorage("sample", None, self.OPEN_STG_FLAG, None, 0)
124
+ self.assertEqual("sample", sample.Stat(STATFLAG_DEFAULT).pwcsName)
125
+ with self.assertRaises(COMError) as ctx:
126
+ storage.OpenStorage("example", None, self.OPEN_STG_FLAG, None, 0)
127
+ self.assertEqual(ctx.exception.hresult, STG_E_PATHNOTFOUND)
@@ -0,0 +1,131 @@
1
+ import unittest as ut
2
+
3
+ from ctypes import POINTER, byref, c_bool, c_ubyte, c_ulonglong, oledll, pointer
4
+ import comtypes
5
+ import comtypes.client
6
+
7
+ comtypes.client.GetModule("portabledeviceapi.dll")
8
+ from comtypes.gen.PortableDeviceApiLib import IStream
9
+
10
+
11
+ STGC_DEFAULT = 0
12
+ STREAM_SEEK_SET = 0
13
+ STREAM_SEEK_CUR = 1
14
+ STREAM_SEEK_END = 2
15
+
16
+
17
+ def _create_stream() -> IStream:
18
+ # Create an IStream
19
+ stream = POINTER(IStream)() # type: ignore
20
+ comtypes._ole32.CreateStreamOnHGlobal(None, c_bool(True), byref(stream))
21
+ return stream # type: ignore
22
+
23
+
24
+ class Test_RemoteWrite(ut.TestCase):
25
+ def test_RemoteWrite(self):
26
+ stream = _create_stream()
27
+ test_data = "Some data".encode("utf-8")
28
+ pv = (c_ubyte * len(test_data)).from_buffer(bytearray(test_data))
29
+
30
+ written = stream.RemoteWrite(pv, len(test_data))
31
+
32
+ # Verification
33
+ self.assertEqual(written, len(test_data))
34
+
35
+
36
+ class Test_RemoteRead(ut.TestCase):
37
+ def test_RemoteRead(self):
38
+ stream = _create_stream()
39
+ test_data = "Some data".encode("utf-8")
40
+ pv = (c_ubyte * len(test_data)).from_buffer(bytearray(test_data))
41
+ stream.RemoteWrite(pv, len(test_data))
42
+
43
+ # Make sure the data actually gets written before trying to read back
44
+ stream.Commit(STGC_DEFAULT)
45
+ # Move the stream back to the beginning
46
+ stream.RemoteSeek(0, STREAM_SEEK_SET)
47
+
48
+ buffer_size = 1024
49
+
50
+ read_buffer, data_read = stream.RemoteRead(buffer_size)
51
+
52
+ # Verification
53
+ self.assertEqual(data_read, len(test_data))
54
+ self.assertEqual(bytearray(read_buffer)[0:data_read], test_data)
55
+
56
+
57
+ class Test_RemoteSeek(ut.TestCase):
58
+ def _create_sample_stream(self) -> IStream:
59
+ stream = _create_stream()
60
+ test_data = b"spam egg bacon ham"
61
+ pv = (c_ubyte * len(test_data)).from_buffer(bytearray(test_data))
62
+ stream.RemoteWrite(pv, len(test_data))
63
+ stream.Commit(STGC_DEFAULT)
64
+ return stream
65
+
66
+ def test_takes_STREAM_SEEK_SET_as_origin(self):
67
+ stream = self._create_sample_stream()
68
+ newpos = stream.RemoteSeek(9, STREAM_SEEK_SET)
69
+ self.assertEqual(newpos, 9)
70
+ buf, read = stream.RemoteRead(1024)
71
+ self.assertEqual(bytearray(buf)[0:read], b"bacon ham")
72
+
73
+ def test_takes_STREAM_SEEK_CUR_as_origin(self):
74
+ stream = self._create_sample_stream()
75
+ stream.RemoteSeek(8, STREAM_SEEK_SET)
76
+ newpos = stream.RemoteSeek(7, STREAM_SEEK_CUR)
77
+ self.assertEqual(newpos, 15)
78
+ buf, read = stream.RemoteRead(1024)
79
+ self.assertEqual(bytearray(buf)[0:read], b"ham")
80
+
81
+ def test_takes_STREAM_SEEK_END_as_origin(self):
82
+ stream = self._create_sample_stream()
83
+ stream.RemoteSeek(8, STREAM_SEEK_SET)
84
+ newpos = stream.RemoteSeek(-13, STREAM_SEEK_END)
85
+ self.assertEqual(newpos, 5)
86
+ buf, read = stream.RemoteRead(1024)
87
+ self.assertEqual(bytearray(buf)[0:read], b"egg bacon ham")
88
+
89
+
90
+ class Test_SetSize(ut.TestCase):
91
+ def test_SetSize(self):
92
+ stream = _create_stream()
93
+ stream.SetSize(42)
94
+ pui = pointer(c_ulonglong())
95
+ oledll.shlwapi.IStream_Size(stream, pui)
96
+ self.assertEqual(pui.contents.value, 42)
97
+
98
+
99
+ class Test_RemoteCopyTo(ut.TestCase):
100
+ def test_RemoteCopyTo(self):
101
+ src = _create_stream()
102
+ dst = _create_stream()
103
+ test_data = b"parrot"
104
+ pv = (c_ubyte * len(test_data)).from_buffer(bytearray(test_data))
105
+ src_written = src.RemoteWrite(pv, len(test_data))
106
+ src.Commit(STGC_DEFAULT)
107
+ src.RemoteSeek(0, STREAM_SEEK_SET)
108
+ cpy_read, cpy_written = src.RemoteCopyTo(dst, src_written)
109
+ self.assertEqual(cpy_read, len(test_data))
110
+ self.assertEqual(cpy_written, len(test_data))
111
+ dst.Commit(STGC_DEFAULT)
112
+ dst.RemoteSeek(0, STREAM_SEEK_SET)
113
+ dst_buf, dst_read = dst.RemoteRead(1024)
114
+ self.assertEqual(bytearray(dst_buf)[0:dst_read], test_data)
115
+
116
+
117
+ class Test_Clone(ut.TestCase):
118
+ def test_Clone(self):
119
+ orig = _create_stream()
120
+ test_data = b"spam egg bacon ham"
121
+ pv = (c_ubyte * len(test_data)).from_buffer(bytearray(test_data))
122
+ orig.RemoteWrite(pv, len(test_data))
123
+ orig.Commit(STGC_DEFAULT)
124
+ orig.RemoteSeek(0, STREAM_SEEK_SET)
125
+ new_stm = orig.Clone()
126
+ buf, read = new_stm.RemoteRead(1024)
127
+ self.assertEqual(bytearray(buf)[0:read], test_data)
128
+
129
+
130
+ if __name__ == "__main__":
131
+ ut.main()
@@ -7,6 +7,7 @@ from comtypes.tools.tlbparser import (
7
7
  HRESULT_type,
8
8
  VARIANT_type,
9
9
  VARIANT_BOOL_type,
10
+ void_type,
10
11
  )
11
12
 
12
13
 
@@ -20,7 +21,6 @@ idispatch_type = typedesc.ComInterface(
20
21
  ["restricted"],
21
22
  None,
22
23
  )
23
- void_type = typedesc.FundamentalType("void", 0, 0)
24
24
 
25
25
 
26
26
  class Test_AvoidUsingKeywords(unittest.TestCase):
@@ -9,6 +9,7 @@ from comtypes.patcher import Patch
9
9
  GetModule(os.path.join(os.path.dirname(__file__), "urlhist.tlb"))
10
10
  from comtypes.gen import urlhistLib
11
11
 
12
+
12
13
  # The pwcsTitle and pwcsUrl fields of the _STATURL structure must be
13
14
  # freed by the caller. The only way to do this without patching the
14
15
  # generated code directly is to monkey-patch the
@@ -1,5 +1,3 @@
1
- from __future__ import print_function
2
-
3
1
  import datetime
4
2
  import decimal
5
3
  import sys
@@ -1,12 +1,28 @@
1
+ from collections import Counter
1
2
  import textwrap
3
+ from typing import overload
4
+ from typing import Optional, Union as _UnionT
2
5
  from typing import Dict, List, Set, Tuple
6
+ from typing import Iterator, Mapping, Sequence
7
+ import warnings
3
8
 
4
9
 
5
10
  class ImportedNamespaces(object):
6
- def __init__(self):
7
- self.data = {}
11
+ def __init__(self) -> None:
12
+ self.data: Dict[str, Optional[str]] = {}
8
13
 
9
- def add(self, name1, name2=None, symbols=None):
14
+ @overload
15
+ def add(self, modulename: str, /) -> None: ...
16
+ @overload
17
+ def add(self, modulename: str, symbolname: str, /) -> None: ...
18
+ @overload
19
+ def add(self, symbolname: str, /, *, symbols: Mapping[str, str]) -> None: ...
20
+ def add(
21
+ self,
22
+ name1: str,
23
+ name2: Optional[str] = None,
24
+ symbols: Optional[Mapping[str, str]] = None,
25
+ ) -> None:
10
26
  """Adds a namespace will be imported.
11
27
 
12
28
  Examples:
@@ -43,7 +59,7 @@ class ImportedNamespaces(object):
43
59
  from_, import_ = name1, name2
44
60
  self.data[import_] = from_
45
61
 
46
- def __contains__(self, item):
62
+ def __contains__(self, item: _UnionT[str, Tuple[str, str]]) -> bool:
47
63
  """Returns item has already added.
48
64
 
49
65
  Examples:
@@ -77,31 +93,31 @@ class ImportedNamespaces(object):
77
93
  names.add(key)
78
94
  return names
79
95
 
80
- def _make_line(self, from_, imports):
96
+ def _make_line(self, from_: str, imports: Sequence[str]) -> str:
81
97
  import_ = ", ".join(imports)
82
- code = "from %s import %s" % (from_, import_)
98
+ code = f"from {from_} import {import_}"
83
99
  if len(code) <= 80:
84
100
  return code
85
101
  wrapper = textwrap.TextWrapper(
86
102
  subsequent_indent=" ", initial_indent=" ", break_long_words=False
87
103
  )
88
104
  import_ = "\n".join(wrapper.wrap(import_))
89
- code = "from %s import (\n%s\n)" % (from_, import_)
105
+ code = f"from {from_} import (\n{import_}\n)"
90
106
  return code
91
107
 
92
- def getvalue(self):
93
- ns = {}
94
- lines = []
108
+ def getvalue(self) -> str:
109
+ ns: Dict[str, Optional[Set[str]]] = {}
110
+ lines: List[str] = []
95
111
  for key, val in self.data.items():
96
112
  if val is None:
97
113
  ns[key] = val
98
114
  elif key == "*":
99
- lines.append("from %s import *" % val)
115
+ lines.append(f"from {val} import *")
100
116
  else:
101
- ns.setdefault(val, set()).add(key)
117
+ ns.setdefault(val, set()).add(key) # type: ignore
102
118
  for key, val in ns.items():
103
119
  if val is None:
104
- lines.append("import %s" % key)
120
+ lines.append(f"import {key}")
105
121
  else:
106
122
  names = sorted(val, key=lambda s: s.lower())
107
123
  lines.append(self._make_line(key, names))
@@ -109,10 +125,10 @@ class ImportedNamespaces(object):
109
125
 
110
126
 
111
127
  class DeclaredNamespaces(object):
112
- def __init__(self):
113
- self.data = {}
128
+ def __init__(self) -> None:
129
+ self.data: Dict[Tuple[str, str], Optional[str]] = {}
114
130
 
115
- def add(self, alias, definition, comment=None):
131
+ def add(self, alias: str, definition: str, comment: Optional[str] = None) -> None:
116
132
  """Adds a namespace will be declared.
117
133
 
118
134
  Examples:
@@ -134,18 +150,18 @@ class DeclaredNamespaces(object):
134
150
  names.add(alias)
135
151
  return names
136
152
 
137
- def getvalue(self):
153
+ def getvalue(self) -> str:
138
154
  lines = []
139
155
  for (alias, definition), comment in self.data.items():
140
- code = "%s = %s" % (alias, definition)
156
+ code = f"{alias} = {definition}"
141
157
  if comment:
142
- code = code + " # %s" % comment
158
+ code = code + f" # {comment}"
143
159
  lines.append(code)
144
160
  return "\n".join(lines)
145
161
 
146
162
 
147
163
  class EnumerationNamespaces(object):
148
- def __init__(self):
164
+ def __init__(self) -> None:
149
165
  self.data: Dict[str, List[Tuple[str, int]]] = {}
150
166
 
151
167
  def add(self, enum_name: str, member_name: str, value: int) -> None:
@@ -159,6 +175,14 @@ class EnumerationNamespaces(object):
159
175
  >>> assert enums
160
176
  >>> enums.add('Foo', 'spam', 2)
161
177
  >>> enums.add('Bar', 'bacon', 3)
178
+ >>> enums.add('Bar', 'egg', 4)
179
+ >>> import warnings
180
+ >>> with warnings.catch_warnings(record=True) as w:
181
+ ... enums.add('Bar', 'egg', 5)
182
+ ... print(w[-1].message.args[0].replace(', ', ',\\n'))
183
+ The 'egg' member of the 'Bar' enumeration is already assigned 4,
184
+ but it will be overwritten with 5,
185
+ based on the type information.
162
186
  >>> assert 'Foo' in enums
163
187
  >>> assert 'Baz' not in enums
164
188
  >>> print(enums.to_intflags())
@@ -169,6 +193,8 @@ class EnumerationNamespaces(object):
169
193
  <BLANKLINE>
170
194
  class Bar(IntFlag):
171
195
  bacon = 3
196
+ # egg = 4 # duplicated. Perhaps there is a bug in the type library?
197
+ egg = 5 # duplicated. Perhaps there is a bug in the type library?
172
198
  >>> print(enums.to_constants())
173
199
  # values for enumeration 'Foo'
174
200
  ham = 1
@@ -177,9 +203,24 @@ class EnumerationNamespaces(object):
177
203
  <BLANKLINE>
178
204
  # values for enumeration 'Bar'
179
205
  bacon = 3
206
+ egg = 4 # duplicated within the 'Bar'. Perhaps there is a bug?
207
+ egg = 5 # duplicated within the 'Bar'. Perhaps there is a bug?
180
208
  Bar = c_int # enum
181
209
  """
182
- self.data.setdefault(enum_name, []).append((member_name, value))
210
+ members = self.data.setdefault(enum_name, [])
211
+ if members:
212
+ mapping = dict(members)
213
+ if member_name in mapping:
214
+ # This may be a bug in the COM type library.
215
+ # See also https://github.com/enthought/comtypes/issues/550
216
+ warnings.warn(
217
+ f"The '{member_name}' member of the '{enum_name}' enumeration "
218
+ f"is already assigned {mapping[member_name]}, "
219
+ f"but it will be overwritten with {value}, "
220
+ "based on the type information.",
221
+ UserWarning,
222
+ )
223
+ members.append((member_name, value))
183
224
 
184
225
  def __contains__(self, item: str) -> bool:
185
226
  return item in self.data
@@ -190,23 +231,52 @@ class EnumerationNamespaces(object):
190
231
  def get_symbols(self) -> Set[str]:
191
232
  return set(self.data)
192
233
 
234
+ def _iter_members(
235
+ self, members: Sequence[Tuple[str, int]]
236
+ ) -> Iterator[Tuple[str, bool, int]]:
237
+ key_counter = Counter(m for m, _ in members)
238
+ decrementee = dict(key_counter) # shallow copy
239
+ for name, value in members:
240
+ decrementee[name] -= 1
241
+ # definition, is_dupl, rest_dupl_count
242
+ yield f"{name} = {value}", key_counter[name] > 1, decrementee[name]
243
+
244
+ def _iter_items(self) -> Iterator[Tuple[str, Iterator[Tuple[str, bool, int]]]]:
245
+ for name, members in self.data.items():
246
+ yield name, self._iter_members(members)
247
+
193
248
  def to_constants(self) -> str:
194
249
  blocks = []
195
- for enum_name, enum_members in self.data.items():
250
+ for enum_name, members in self._iter_items():
196
251
  lines = []
197
252
  lines.append(f"# values for enumeration '{enum_name}'")
198
- for n, v in enum_members:
199
- lines.append(f"{n} = {v}")
253
+ for definition, is_dupl, _ in members:
254
+ if is_dupl:
255
+ msg1 = f"duplicated within the '{enum_name}'."
256
+ msg2 = "Perhaps there is a bug?"
257
+ lines.append(f"{definition} # {msg1} {msg2}")
258
+ else:
259
+ lines.append(definition)
200
260
  lines.append(f"{enum_name} = c_int # enum")
201
261
  blocks.append("\n".join(lines))
202
262
  return "\n\n".join(blocks)
203
263
 
204
264
  def to_intflags(self) -> str:
205
265
  blocks = []
206
- for enum_name, enum_members in self.data.items():
266
+ for enum_name, members in self._iter_items():
207
267
  lines = []
208
268
  lines.append(f"class {enum_name}(IntFlag):")
209
- for member_name, value in enum_members:
210
- lines.append(f" {member_name} = {value}")
269
+ for definition, is_dupl, rest_dupl_count in members:
270
+ if is_dupl:
271
+ msg = "duplicated. Perhaps there is a bug in the type library?"
272
+ base_line = f"{definition} # {msg}"
273
+ if rest_dupl_count > 0:
274
+ # Prevent raising `TypeError: Attempted to reuse key:`.
275
+ # See https://github.com/enthought/comtypes/issues/550
276
+ lines.append(f" # {base_line}")
277
+ else:
278
+ lines.append(f" {base_line}")
279
+ else:
280
+ lines.append(f" {definition}")
211
281
  blocks.append("\n".join(lines))
212
282
  return "\n\n\n".join(blocks)
@@ -33,8 +33,7 @@ class _MethodAnnotator(abc.ABC, Generic[_T_MTD]):
33
33
  return result
34
34
 
35
35
  @abc.abstractmethod
36
- def getvalue(self, name: str) -> str:
37
- ...
36
+ def getvalue(self, name: str) -> str: ...
38
37
 
39
38
 
40
39
  _CatMths = Tuple[ # categorized methods
@@ -47,8 +46,7 @@ class _MethodsAnnotator(abc.ABC, Generic[_T_MTD]):
47
46
  self.data: List[str] = []
48
47
 
49
48
  @abc.abstractmethod
50
- def to_method_annotator(self, method: _T_MTD) -> _MethodAnnotator[_T_MTD]:
51
- ...
49
+ def to_method_annotator(self, method: _T_MTD) -> _MethodAnnotator[_T_MTD]: ...
52
50
 
53
51
  def _iter_methods(self, members: Iterable[_T_MTD]) -> Iterator[_CatMths[_T_MTD]]:
54
52
  methods: Dict[str, List[Optional[_T_MTD]]] = {}