mhammond / pywin32

Python for Windows (pywin32) Extensions
4.99k stars 792 forks source link

Some Outlook collections not iterable #1943

Open DS-London opened 2 years ago

DS-London commented 2 years ago

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.

kxrob commented 2 years ago

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.

DS-London commented 2 years ago

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.

_Folders.txt _Accounts.txt

DS-London commented 1 year ago

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?

DS-London commented 1 year ago

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?