pywin32 icon indicating copy to clipboard operation
pywin32 copied to clipboard

Indexing of objects with Items broken

Open ghost opened this issue 9 years ago • 3 comments

As I read in several places, Objects with the Item() method like should map indexing obj[i] to obj.Item(i).

The problem is: It just does not work for me. After running makepy for the Word 14 Object Library, using

import win32com.client

app = win32com.client.Dispatch("Word.Application")

app.Dialogs.Item(1)  # Succeeds and returns a Library.Dialog Object

app.Dialogs[1]  # Type Error: 'Dialogs' object does not support indexing

I dug deeper and I found that there is indeed no getitem method in the corresponding class created by makepy. The code responsible I identified in the genpy file is:


def WriteClassBody(self, generator):
    ...
    specialItems = {..., "item"=None, ...}
    for name in names:
        ...
        if dispid==pythoncom.DISPID_VALUE:
            lkey = "value"
        ...
        else:
            lkey = name.lower()
       
        if lkey in specialItems and specialItems[lkey] is None:
            specialItems[lkey] = (entry, ...)

So in principle, it should recognize "item" as special item and remember that it has been there (later there is a section in the code where it checks if "item" was there and adds a getitem method if it was). The problem is that dispid for "item" is actually equal to pythoncom.DISPID_VALUE and therefore the lkey is set to "value" which is not special anymore. You can easily verify that by changing "if dispid==pythoncom.DISPID_VALUE:" to "if dispid==pythoncom.DISPID_VALUE and name.lower() != "item":" and running makepy again; the getitem methods are created and indexing works as it should.

Reported by: kernchen

Original Ticket: pywin32/bugs/715

ghost avatar Mar 04 '16 16:03 ghost

I would like to push this issue to bring it to attention again (I am the originator of this issue). I do not know enough about the internals to decide whether my proposed solution is acceptable in general. If someone with more background would comment on this, I would be happy to make a pull request if it is acceptable.

crazyfermions avatar Dec 18 '18 13:12 crazyfermions

Sorry for the delay - that code is quite a mess, and "Item" is a special case based only on name. The intent of DISPID_VALUE is that you should be able to call (rather than index) so app.Dialogs(1) might work. Do you get an __iter__ method which works (ie, actually iterates?)

Later in the code we are doing things like if resultCLSID == "None" and "Item" in self.mapFuncs: - it might be that it would be "safer" (in terms of not breaking other objects where what we do is correct even when the name is "Item", we simply should change l530 (if specialItems["item"]:) to also look for "Item" in self.mapFuncs.

mhammond avatar Jan 03 '19 05:01 mhammond

Yes, everything else works as intended; you can iterate over it and you can also use the call syntax to address the items. It's just the __getitem__ method which is missing.

crazyfermions avatar Jan 07 '19 17:01 crazyfermions

It's just the __getitem__ method which is missing.

Could it be the same as https://github.com/mhammond/pywin32/issues/627 ?

Avasam avatar Feb 24 '25 02:02 Avasam