Skip to content

Commit

Permalink
Separate patch processing within _com_interface_meta into a module …
Browse files Browse the repository at this point in the history
…(part 3 of #646). (#649)

* Remove unnecessary stuffs.

* Restore methods for keeping change history.

* Add comments.

* Remove redundant comments.
  • Loading branch information
junkmd authored Nov 2, 2024
1 parent 521402f commit 7d6828c
Showing 1 changed file with 86 additions and 92 deletions.
178 changes: 86 additions & 92 deletions comtypes/_post_coinit/_cominterface_meta_patcher.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
# https://learn.microsoft.com/en-us/windows/win32/api/unknwn/

from _ctypes import COMError

import types
Expand Down Expand Up @@ -80,93 +78,89 @@ def has_name(name):
return name.lower() in self.__map_case__
return hasattr(self, 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

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))

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)
# XXX These special methods should be generated by the code generator.
def _patch_sized(self):
@patcher.Patch(self)
class _(object):
def __len__(self):
"Return the the 'self.Count' property."
return self.Count

def _patch_callable_and_subscriptable(self):
@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: # Unknown error
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: # Unknown error
raise
except TypeError:
msg = "%r object does not support item assignment"
raise TypeError(msg % type(self))

def _patch_iterator(self):
@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)

0 comments on commit 7d6828c

Please sign in to comment.