Some Outlook collections not iterable
There seems to be something amiss when win32com generates the wrapper for some MS Outlook collections.
With no cached files, this code (late binding) runs successfully.
ol = wc.Dispatch('Outlook.Application')
ns = ol.GetNamespace('MAPI')
for ac in ns.Accounts:
print(ac.DisplayName)
Using gencache (early binding), it does not:
ol = wc.gencache.EnsureDispatch('Outlook.Application')
ns = ol.GetNamespace('MAPI')
for ac in ns.Accounts:
print(ac.DisplayName)
And instead throws the 'Accounts' object is not iterable exception when attempting to iterate over the Accounts collection.
The same is true of the Stores collection for the NameSpace object. However the Folders collection does iterate without an error.
As an aside, VBA using the MS Outlook Object Library reference can iterate over the Accounts collection without issue.
Using pywin32 version 304 on Python 3.9 64-bit.
Note: This will also be handled by #1931 likely - after a pending push regarding common base class of the dynamic and gen wrappers and optimizations.
The dynamic wrapper (which was only duck type similar) tried and tries a COM enumeration even in absence of type info and also a .Count / .Item() pseudo iteration.
An upload / attachment of the current generated Outlook gen file ( sys.modules[ol.__module__].__file__ ) would though help to clarify.
I've attached the two .py files (with a .txt extension as can't seem to attached .py files) for the _Accounts and _Folders objects.
FWIW, I can access DISPID_NEWENUM (-4) and iterate over the IEnumVARIANT interface with Win32 in C++ for the Accounts object.
Hi, have upgraded to pywin32 305.
Still having the same issue with Outlook collections.
import win32com.client
ns = win32com.client.gencache.EnsureDispatch('outlook.application').GetNamespace("MAPI")
accs = ns.Accounts
This works:
for n in range(1,accs.Count+1):
ac = accs.Item(n)
print(ac.DisplayName)
As does this:
it = accs.__iter__()
ob = next(it,None)
while ob is not None:
print(ob.DisplayName)
ob = next(it,None)
This does not:
for ac in accs:
print(ac.DisplayName)
or even just this:
it = iter(accs)
Both return:
TypeError: 'Accounts' object is not iterable
I had assumed that iter() was calling the __iter__() method of the _Accounts object. In my gen_py files, the _Accounts python file has this section:
def __iter__(self):
"Return a Python iterator for this object"
try:
ob = self._oleobj_.InvokeTypes(-4,LCID,3,(13, 10),())
except pythoncom.error:
raise TypeError("This object does not support enumeration")
return win32com.client.util.Iterator(ob, '{000630C5-0000-0000-C000-000000000046}')
but this is not the case (I can test this by adding a print statement to the code in _Accounts.py and it gets printed if I call __iter__() but not when I call iter()).
Any idea what is going on?
One thing I have noticed for the _Namespace interface on the Outlook Type Library:
The Accounts property:
[id(0x0000fad0), propget, helpcontext(0x0000030a)]
Accounts* Accounts();
but the Folders property:
[id(0x00002103), propget, helpcontext(0x000002f5)]
_Folders* Folders();
In Python:
import win32com.client
ns = win32com.client.gencache.EnsureDispatch('outlook.application').GetNamespace("MAPI")
accs = ns.Accounts
print(type(accs))
fldrs = ns.Folders
print(type(fldrs))
yields:
<class 'win32com.gen_py.00062FFF-0000-0000-C000-000000000046x0x9x6.Accounts.Accounts'>
<class 'win32com.gen_py.00062FFF-0000-0000-C000-000000000046x0x9x6._Folders._Folders'>
And drilling down:
itf = iter(fldrs)
print(type(itf))
returns:
<class 'win32com.client.util.Iterator'>
But
ita = iter(type(accs))
print(type(ita))
gives the error:
TypeError: 'type' object is not iterable
Hence I wonder if this is a bug in the Outlook Type Library? Other comments/questions on SO back up my experience that code was working fine, and then failed: perhaps the type library was updated?