Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Separate patch processing within _com_interface_meta into a module (part 3 of #646). #649

Merged
merged 4 commits into from
Nov 2, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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)