From 6020d66a0950c1fe29f63e26cba168fe6f3b4d44 Mon Sep 17 00:00:00 2001 From: junkmd Date: Sat, 2 Nov 2024 18:23:01 +0900 Subject: [PATCH 1/4] Restore comments. --- comtypes/_post_coinit/_cominterface_meta_patcher.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/comtypes/_post_coinit/_cominterface_meta_patcher.py b/comtypes/_post_coinit/_cominterface_meta_patcher.py index d70e73a07..d0c7d7f88 100644 --- a/comtypes/_post_coinit/_cominterface_meta_patcher.py +++ b/comtypes/_post_coinit/_cominterface_meta_patcher.py @@ -26,6 +26,7 @@ def __getattr__(self, name): # EVERY attribute assignment. Settings a non-com attribute # through this function takes 8.6 usec, while without this # function it takes 0.7 sec - 12 times slower. + # # How much faster would this be if implemented in C? def __setattr__(self, name, value): """Implement case insensitive access to methods and properties""" @@ -39,10 +40,12 @@ def __setitem__(self, index, value): # We override the __setitem__ method of the # POINTER(POINTER(interface)) type, so that the COM # reference count is managed correctly. + # # This is so that we can implement COM methods that have to # return COM pointers more easily and consistent. Instead of # using CopyComPointer in the method implementation, we can # simply do: + # # def GetTypeInfo(self, this, ..., pptinfo): # if not pptinfo: return E_POINTER # pptinfo[0] = a_com_interface_pointer @@ -108,7 +111,7 @@ def __getitem__(self, index): (hresult, text, details) = err.args if hresult == -2147352565: # DISP_E_BADINDEX raise IndexError("invalid index") - else: # Unknown error + else: raise # Note that result may be NULL COM pointer. There is no way @@ -127,7 +130,7 @@ def __setitem__(self, index, value): (hresult, text, details) = err.args if hresult == -2147352565: # DISP_E_BADINDEX raise IndexError("invalid index") - else: # Unknown error + else: raise except TypeError: msg = "%r object does not support item assignment" @@ -149,6 +152,7 @@ def __iter__(self): enum = self._NewEnum if isinstance(enum, types.MethodType): # _NewEnum should be a propget property, with dispid -4. + # # Sometimes, however, it is a method. enum = enum() if hasattr(enum, "Next"): From 3ddadc58dfa7bd3f64656a27d4844398935e48a3 Mon Sep 17 00:00:00 2001 From: junkmd Date: Sat, 2 Nov 2024 18:23:12 +0900 Subject: [PATCH 2/4] Remove the unused remnant. --- comtypes/_post_coinit/_cominterface_meta_patcher.py | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/comtypes/_post_coinit/_cominterface_meta_patcher.py b/comtypes/_post_coinit/_cominterface_meta_patcher.py index d0c7d7f88..78e064e66 100644 --- a/comtypes/_post_coinit/_cominterface_meta_patcher.py +++ b/comtypes/_post_coinit/_cominterface_meta_patcher.py @@ -62,17 +62,6 @@ def __setitem__(self, index, value): CopyComPointer(value, self) # type: ignore - def _make_specials(self): - # This call installs methods that forward the Python protocols - # to COM protocols. - - def has_name(name): - # Determine whether a property or method named 'name' - # exists - if self._case_insensitive_: - return name.lower() in self.__map_case__ - return hasattr(self, name) - def sized(itf: Type) -> None: @patcher.Patch(itf) From 83900173735a72574d6b9d176cdf6df8d07af478 Mon Sep 17 00:00:00 2001 From: junkmd Date: Sat, 2 Nov 2024 18:23:12 +0900 Subject: [PATCH 3/4] Use top level functions instead of methods. --- comtypes/_post_coinit/unknwn.py | 157 ++------------------------------ 1 file changed, 7 insertions(+), 150 deletions(-) diff --git a/comtypes/_post_coinit/unknwn.py b/comtypes/_post_coinit/unknwn.py index 7380f25b2..d0499ef23 100644 --- a/comtypes/_post_coinit/unknwn.py +++ b/comtypes/_post_coinit/unknwn.py @@ -1,24 +1,21 @@ # https://learn.microsoft.com/en-us/windows/win32/api/unknwn/ from ctypes import byref, c_ulong, c_void_p, HRESULT, POINTER -from _ctypes import COMError import logging import sys -import types from typing import ClassVar, TYPE_CHECKING, TypeVar from typing import Optional from typing import List, Type -from comtypes import GUID, patcher, _ole32_nohresult, com_interface_registry +from comtypes._post_coinit import _cominterface_meta_patcher as _meta_patch +from comtypes import GUID, _ole32_nohresult, com_interface_registry from comtypes._idl_stuff import STDMETHOD from comtypes._memberspec import ComMemberGenerator, DispMemberGenerator from comtypes._memberspec import _ComMemberSpec, _DispMemberSpec from comtypes._py_instance_method import instancemethod -_all_slice = slice(None, None, None) - logger = logging.getLogger(__name__) @@ -136,68 +133,11 @@ def __new__(cls, name, bases, namespace): _pointer_type_cache[self] = p if self._case_insensitive_: - self._patch_case_insensitive_to_ptr_type(p) - self._patch_reference_fix_to_ptrptr_type(POINTER(p)) # type: ignore + _meta_patch.case_insensitive(p) + _meta_patch.reference_fix(POINTER(p)) # type: ignore return self - @staticmethod - def _patch_case_insensitive_to_ptr_type(p: Type) -> None: - @patcher.Patch(p) - class CaseInsensitive(object): - # case insensitive attributes for COM methods and properties - def __getattr__(self, name): - """Implement case insensitive access to methods and properties""" - try: - fixed_name = self.__map_case__[name.lower()] - except KeyError: - raise AttributeError(name) # Should we use exception-chaining? - if fixed_name != name: # prevent unbounded recursion - return getattr(self, fixed_name) - raise AttributeError(name) - - # __setattr__ is pretty heavy-weight, because it is called for - # EVERY attribute assignment. Settings a non-com attribute - # through this function takes 8.6 usec, while without this - # function it takes 0.7 sec - 12 times slower. - # - # How much faster would this be if implemented in C? - def __setattr__(self, name, value): - """Implement case insensitive access to methods and properties""" - object.__setattr__( - self, self.__map_case__.get(name.lower(), name), value - ) - - @staticmethod - def _patch_reference_fix_to_ptrptr_type(pp: Type) -> None: - @patcher.Patch(pp) - class ReferenceFix(object): - def __setitem__(self, index, value): - # We override the __setitem__ method of the - # POINTER(POINTER(interface)) type, so that the COM - # reference count is managed correctly. - # - # This is so that we can implement COM methods that have to - # return COM pointers more easily and consistent. Instead of - # using CopyComPointer in the method implementation, we can - # simply do: - # - # def GetTypeInfo(self, this, ..., pptinfo): - # if not pptinfo: return E_POINTER - # pptinfo[0] = a_com_interface_pointer - # return S_OK - if index != 0: - # CopyComPointer, which is in _ctypes, does only - # handle an index of 0. This code does what - # CopyComPointer should do if index != 0. - if bool(value): - value.AddRef() - super(pp, self).__setitem__(index, value) # type: ignore - return - from _ctypes import CopyComPointer - - CopyComPointer(value, self) # type: ignore - def __setattr__(self, name, value): if name == "_methods_": # XXX I'm no longer sure why the code generator generates @@ -225,94 +165,11 @@ def has_name(name): # XXX These special methods should be generated by the code generator. if has_name("Count"): - - @patcher.Patch(self) - class _(object): - def __len__(self): - "Return the the 'self.Count' property." - return self.Count - + _meta_patch.sized(self) if has_name("Item"): - - @patcher.Patch(self) - class _(object): - # 'Item' is the 'default' value. Make it available by - # calling the instance (Not sure this makes sense, but - # win32com does this also). - def __call__(self, *args, **kw): - "Return 'self.Item(*args, **kw)'" - return self.Item(*args, **kw) - - # does this make sense? It seems that all standard typelibs I've - # seen so far that support .Item also support ._NewEnum - @patcher.no_replace - def __getitem__(self, index): - "Return 'self.Item(index)'" - # Handle tuples and all-slice - if isinstance(index, tuple): - args = index - elif index == _all_slice: - args = () - else: - args = (index,) - - try: - result = self.Item(*args) - except COMError as err: - (hresult, text, details) = err.args - if hresult == -2147352565: # DISP_E_BADINDEX - raise IndexError("invalid index") - else: - raise - - # Note that result may be NULL COM pointer. There is no way - # to interpret this properly, so it is returned as-is. - - # Hm, should we call __ctypes_from_outparam__ on the - # result? - return result - - @patcher.no_replace - def __setitem__(self, index, value): - "Attempt 'self.Item[index] = value'" - try: - self.Item[index] = value - except COMError as err: - (hresult, text, details) = err.args - if hresult == -2147352565: # DISP_E_BADINDEX - raise IndexError("invalid index") - else: - raise - except TypeError: - msg = "%r object does not support item assignment" - raise TypeError(msg % type(self)) - + _meta_patch.callable_and_subscriptable(self) if has_name("_NewEnum"): - - @patcher.Patch(self) - class _(object): - def __iter__(self): - "Return an iterator over the _NewEnum collection." - # This method returns a pointer to _some_ _NewEnum interface. - # It relies on the fact that the code generator creates next() - # methods for them automatically. - # - # Better would maybe to return an object that - # implements the Python iterator protocol, and - # forwards the calls to the COM interface. - enum = self._NewEnum - if isinstance(enum, types.MethodType): - # _NewEnum should be a propget property, with dispid -4. - # - # Sometimes, however, it is a method. - enum = enum() - if hasattr(enum, "Next"): - return enum - # _NewEnum returns an IUnknown pointer, QueryInterface() it to - # IEnumVARIANT - from comtypes.automation import IEnumVARIANT - - return enum.QueryInterface(IEnumVARIANT) + _meta_patch.iterator(self) def _make_case_insensitive(self): # The __map_case__ dictionary maps lower case names to the From 3230f7aad41f866e5e3dbc34e87b572d1e68f08a Mon Sep 17 00:00:00 2001 From: junkmd Date: Sat, 2 Nov 2024 18:23:12 +0900 Subject: [PATCH 4/4] Update import orderings. --- comtypes/_post_coinit/unknwn.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/comtypes/_post_coinit/unknwn.py b/comtypes/_post_coinit/unknwn.py index d0499ef23..3b345489c 100644 --- a/comtypes/_post_coinit/unknwn.py +++ b/comtypes/_post_coinit/unknwn.py @@ -1,21 +1,19 @@ # https://learn.microsoft.com/en-us/windows/win32/api/unknwn/ -from ctypes import byref, c_ulong, c_void_p, HRESULT, POINTER - import logging import sys +from ctypes import HRESULT, POINTER, byref, c_ulong, c_void_p from typing import ClassVar, TYPE_CHECKING, TypeVar from typing import Optional from typing import List, Type -from comtypes._post_coinit import _cominterface_meta_patcher as _meta_patch from comtypes import GUID, _ole32_nohresult, com_interface_registry from comtypes._idl_stuff import STDMETHOD from comtypes._memberspec import ComMemberGenerator, DispMemberGenerator from comtypes._memberspec import _ComMemberSpec, _DispMemberSpec +from comtypes._post_coinit import _cominterface_meta_patcher as _meta_patch from comtypes._py_instance_method import instancemethod - logger = logging.getLogger(__name__)