Skip to content

Commit 17fb589

Browse files
committed
Make special __dunder__ methods work with CoClass (mhammond#1699)
1 parent f4798e0 commit 17fb589

File tree

4 files changed

+40
-3
lines changed

4 files changed

+40
-3
lines changed

CHANGES.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ Note that build 228 was the last version supporting Python 2.
88

99
Since build 300:
1010
----------------
11+
* CoClass objects should work better with special methods like __len__ etc.
12+
(#1699)
13+
1114
* Creating a `win32crypt.CRYPT_ATTRIBUTE` object now correctly sets `cbData`.
1215

1316
* COM objects are now registered with the full path to pythoncomXX.dll, fixes

com/win32com/client/__init__.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -534,6 +534,23 @@ def __setattr__(self, attr, value):
534534
pass
535535
self.__dict__[attr] = value
536536

537+
# Special methods don't use __getattr__ etc, so explicitly delegate here.
538+
# Some wrapped objects might not have them, but that's OK - the attribute
539+
# error can just bubble up.
540+
def __call__(self, *args, **kwargs):
541+
return self.__dict__["_dispobj_"].__call__(*args, **kwargs)
542+
def __str__(self, *args):
543+
return self.__dict__["_dispobj_"].__str__(*args)
544+
def __int__(self, *args):
545+
return self.__dict__["_dispobj_"].__int__(*args)
546+
def __iter__(self):
547+
return self.__dict__["_dispobj_"].__iter__()
548+
def __len__(self):
549+
return self.__dict__["_dispobj_"].__len__()
550+
def __nonzero__(self):
551+
return self.__dict__["_dispobj_"].__nonzero__()
552+
553+
537554
# A very simple VARIANT class. Only to be used with poorly-implemented COM
538555
# objects. If an object accepts an arg which is a simple "VARIANT", but still
539556
# is very pickly about the actual variant type (eg, isn't happy with a VT_I4,

com/win32com/client/genpy.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -607,7 +607,7 @@ def WriteClass(self, generator):
607607
for item, flag in self.interfaces:
608608
if flag & pythoncom.IMPLTYPEFLAG_FDEFAULT: # and dual:
609609
defItem = item
610-
# If we have written a class, refeence its name, otherwise the IID
610+
# If we have written a class, reference its name, otherwise the IID
611611
if item.bWritten: key = item.python_name
612612
else: key = repr(str(item.clsid)) # really the iid.
613613
print('\t\t%s,' % (key,), file=stream)

com/win32com/test/testPyComTest.py

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -172,10 +172,17 @@ def TestCommon(o, is_generated):
172172
progress("Checking getting/passing IUnknown")
173173
check_get_set(o.GetSetUnknown, o)
174174
progress("Checking getting/passing IDispatch")
175-
if not isinstance(o.GetSetDispatch(o), o.__class__):
175+
# This might be called with either the interface or the CoClass - but these
176+
# functions always return from the interface.
177+
expected_class = o.__class__
178+
# CoClass instances have `default_interface`
179+
expected_class = getattr(expected_class, "default_interface", expected_class)
180+
if not isinstance(o.GetSetDispatch(o), expected_class):
176181
raise error("GetSetDispatch failed: %r" % (o.GetSetDispatch(o),))
177182
progress("Checking getting/passing IDispatch of known type")
178-
if o.GetSetInterface(o).__class__ != o.__class__:
183+
expected_class = o.__class__
184+
expected_class = getattr(expected_class, "default_interface", expected_class)
185+
if o.GetSetInterface(o).__class__ != expected_class:
179186
raise error("GetSetDispatch failed")
180187

181188
progress("Checking misc args")
@@ -408,6 +415,16 @@ def TestGenerated():
408415
counter = EnsureDispatch("PyCOMTest.SimpleCounter")
409416
TestCounter(counter, True)
410417

418+
# This dance lets us get a CoClass even though it's not explicitly registered.
419+
# This is `CoPyComTest`
420+
from win32com.client.CLSIDToClass import GetClass
421+
coclass_o = GetClass("{8EE0C520-5605-11D0-AE5F-CADD4C000000}")()
422+
TestCommon(coclass_o, True)
423+
424+
# This is `CoSimpleCounter` and the counter tests should work.
425+
coclass = GetClass("{B88DD310-BAE8-11D0-AE86-76F2C1000000}")()
426+
TestCounter(coclass, True)
427+
411428
# XXX - this is failing in dynamic tests, but should work fine.
412429
i1, i2 = o.GetMultipleInterfaces()
413430
if not isinstance(i1, DispatchBaseClass) or not isinstance(i2, DispatchBaseClass):

0 commit comments

Comments
 (0)