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.
- {comtypes-1.4.6 → comtypes-1.4.8}/CHANGES.txt +36 -0
- {comtypes-1.4.6/comtypes.egg-info → comtypes-1.4.8}/PKG-INFO +32 -38
- {comtypes-1.4.6 → comtypes-1.4.8}/README.md +30 -36
- {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/__init__.py +1 -1
- {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/_comobject.py +0 -1
- {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/_memberspec.py +1 -1
- comtypes-1.4.8/comtypes/_meta.py +95 -0
- {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/_npsupport.py +2 -1
- {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/_post_coinit/__init__.py +1 -0
- {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/_post_coinit/bstr.py +13 -6
- {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/_post_coinit/misc.py +20 -39
- {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/_post_coinit/unknwn.py +94 -69
- {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/automation.py +9 -15
- {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/client/__init__.py +21 -31
- {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/client/_code_cache.py +1 -0
- {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/client/_events.py +0 -1
- {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/client/_generate.py +5 -5
- {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/client/lazybind.py +0 -5
- {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/git.py +1 -0
- {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/hints.pyi +6 -4
- {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/persist.py +19 -3
- {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/safearray.py +2 -6
- {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/server/__init__.py +1 -0
- {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/server/register.py +1 -0
- {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/shelllink.py +90 -34
- {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/test/TestComServer.py +1 -1
- {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/test/TestDispServer.py +1 -1
- {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/test/__init__.py +0 -1
- {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/test/find_memleak.py +0 -1
- {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/test/test_agilent.py +0 -1
- {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/test/test_clear_cache.py +1 -0
- {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/test/test_client.py +9 -0
- {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/test/test_createwrappers.py +0 -2
- {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/test/test_dispinterface.py +0 -1
- {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/test/test_excel.py +0 -1
- {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/test/test_inout_args.py +1 -1
- comtypes-1.4.8/comtypes/test/test_monikers.py +70 -0
- comtypes-1.4.8/comtypes/test/test_persist.py +40 -0
- {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/test/test_server.py +6 -0
- comtypes-1.4.8/comtypes/test/test_shelllink.py +129 -0
- comtypes-1.4.8/comtypes/test/test_storage.py +127 -0
- comtypes-1.4.8/comtypes/test/test_stream.py +131 -0
- {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/test/test_typeannotator.py +1 -1
- {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/test/test_urlhistory.py +1 -0
- {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/test/test_variant.py +0 -2
- {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/tools/codegenerator/namespaces.py +97 -27
- {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/tools/codegenerator/typeannotator.py +2 -4
- {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/tools/tlbparser.py +33 -32
- {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/tools/typedesc.py +2 -2
- {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/typeinfo.py +42 -43
- {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/util.py +1 -0
- {comtypes-1.4.6 → comtypes-1.4.8/comtypes.egg-info}/PKG-INFO +32 -38
- {comtypes-1.4.6 → comtypes-1.4.8}/comtypes.egg-info/SOURCES.txt +5 -1
- comtypes-1.4.8/pyproject.toml +54 -0
- {comtypes-1.4.6 → comtypes-1.4.8}/setup.cfg +1 -1
- comtypes-1.4.6/comtypes/_meta.py +0 -70
- comtypes-1.4.6/comtypes/test/test_istream.py +0 -53
- comtypes-1.4.6/pyproject.toml +0 -3
- {comtypes-1.4.6 → comtypes-1.4.8}/LICENSE.txt +0 -0
- {comtypes-1.4.6 → comtypes-1.4.8}/MANIFEST.in +0 -0
- {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/GUID.py +0 -0
- {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/_idl_stuff.py +0 -0
- {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/_py_instance_method.py +0 -0
- {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/_safearray.py +0 -0
- {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/_tlib_version_checker.py +0 -0
- {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/clear_cache.py +0 -0
- {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/client/_constants.py +0 -0
- {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/client/dynamic.py +0 -0
- {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/connectionpoints.py +0 -0
- {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/errorinfo.py +0 -0
- {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/hresult.py +0 -0
- {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/logutil.py +0 -0
- {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/messageloop.py +0 -0
- {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/patcher.py +0 -0
- {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/server/automation.py +0 -0
- {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/server/connectionpoints.py +0 -0
- {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/server/inprocserver.py +0 -0
- {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/server/localserver.py +0 -0
- {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/server/w_getopt.py +0 -0
- {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/stream.py +0 -0
- {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/test/TestComServer.idl +0 -0
- {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/test/TestComServer.tlb +0 -0
- {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/test/TestDispServer.idl +0 -0
- {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/test/TestDispServer.tlb +0 -0
- {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/test/mylib.idl +0 -0
- {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/test/mylib.tlb +0 -0
- {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/test/mytypelib.idl +0 -0
- {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/test/runtests.py +0 -0
- {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/test/setup.py +0 -0
- {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/test/test_BSTR.py +0 -0
- {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/test/test_DISPPARAMS.py +0 -0
- {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/test/test_GUID.py +0 -0
- {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/test/test_QueryService.py +0 -0
- {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/test/test_avmc.py +0 -0
- {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/test/test_basic.py +0 -0
- {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/test/test_casesensitivity.py +0 -0
- {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/test/test_client_dynamic.py +0 -0
- {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/test/test_client_regenerate_modules.py +0 -0
- {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/test/test_collections.py +0 -0
- {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/test/test_comserver.py +0 -0
- {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/test/test_dict.py +0 -0
- {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/test/test_dispifc_records.py +0 -0
- {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/test/test_dispifc_safearrays.py +0 -0
- {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/test/test_dyndispatch.py +0 -0
- {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/test/test_findgendir.py +0 -0
- {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/test/test_getactiveobj.py +0 -0
- {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/test/test_ie.py +0 -0
- {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/test/test_ienum.py +0 -0
- {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/test/test_imfattributes.py +0 -0
- {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/test/test_jscript.js +0 -0
- {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/test/test_midl_safearray_create.py +0 -0
- {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/test/test_msscript.py +0 -0
- {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/test/test_npsupport.py +0 -0
- {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/test/test_outparam.py +0 -0
- {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/test/test_propputref.py +0 -0
- {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/test/test_pump_events.py +0 -0
- {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/test/test_recordinfo.py +0 -0
- {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/test/test_safearray.py +0 -0
- {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/test/test_sapi.py +0 -0
- {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/test/test_showevents.py +0 -0
- {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/test/test_subinterface.py +0 -0
- {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/test/test_typeinfo.py +0 -0
- {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/test/test_win32com_interop.py +0 -0
- {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/test/test_wmi.py +0 -0
- {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/test/test_word.py +0 -0
- {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/test/urlhist.tlb +0 -0
- {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/tools/__init__.py +0 -0
- {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/tools/codegenerator/__init__.py +0 -0
- {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/tools/codegenerator/codegenerator.py +0 -0
- {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/tools/codegenerator/helpers.py +0 -0
- {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/tools/codegenerator/modulenamer.py +0 -0
- {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/tools/codegenerator/packing.py +0 -0
- {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/tools/typedesc_base.py +0 -0
- {comtypes-1.4.6 → comtypes-1.4.8}/comtypes/viewobject.py +0 -0
- {comtypes-1.4.6 → comtypes-1.4.8}/comtypes.egg-info/dependency_links.txt +0 -0
- {comtypes-1.4.6 → comtypes-1.4.8}/comtypes.egg-info/entry_points.txt +0 -0
- {comtypes-1.4.6 → comtypes-1.4.8}/comtypes.egg-info/top_level.txt +0 -0
- {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,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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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 = "
|
|
156
|
+
code = f"{alias} = {definition}"
|
|
141
157
|
if comment:
|
|
142
|
-
code = code + " #
|
|
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, [])
|
|
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,
|
|
250
|
+
for enum_name, members in self._iter_items():
|
|
196
251
|
lines = []
|
|
197
252
|
lines.append(f"# values for enumeration '{enum_name}'")
|
|
198
|
-
for
|
|
199
|
-
|
|
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,
|
|
266
|
+
for enum_name, members in self._iter_items():
|
|
207
267
|
lines = []
|
|
208
268
|
lines.append(f"class {enum_name}(IntFlag):")
|
|
209
|
-
for
|
|
210
|
-
|
|
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]]] = {}
|