Python COM iterator

L

Larry Bates

Does anyone know if there is a way to make a Python COM object
act like a proper iterator in VB/Delphi?

Example:

Python COM object

class foo:
_public_methods_=['next']

def __init__(self):
self.numbers=[1,2,3,4,5,6,7,8]

def next(self):
try: return self.numbers.pop(0)
except IndexError:
#
# Normally in Python I would do this, but that raises a
# COMexception when used in a COM object.
#
raise StopIteration

def __iter__(self):
return self


I want to be able to write something like (VB):

oFOO=foo()
for each n in oFOO
'
' Do something with n
'
next

Seems like there should be a way. Hope explanation is clear enough.

Regards, Larry
 
C

Carsten Haese

Does anyone know if there is a way to make a Python COM object
act like a proper iterator in VB/Delphi?

I don't use COM, VB, or Delphi, but Google turned up these two
references:
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vbcn7/html/vaconUsingForEach.asp http://17slon.com/blogs/gabr/2007/03/fun-with-enumerators-part-1.html

Judging from those links, an object is iterable in VB and Delphi (or as
they call it, it is enumerable) if it exposes a GetEnumerator method
that returns an enumerator. The enumerator in turn needs to expose a
MoveNext method and a Current property.

Assuming that the Enumerable and the Enumerator are allowed to be the
same object, you'll probably need code that looks something like this:

class foo:
_public_methods_=['GetEnumerator','MoveNext']
# You'll need to figure out how to expose "self.Current"

def __init__(self):
self.numbers=[1,2,3,4,5,6,7,8]
self.Current = None

def MoveNext(self):
try:
self.Current = self.numbers.pop(0)
return True
except IndexError:
return False

def GetEnumerator(self):
return self

Good luck,

Carsten.
 
L

Larry Bates

Carsten said:
Does anyone know if there is a way to make a Python COM object
act like a proper iterator in VB/Delphi?

I don't use COM, VB, or Delphi, but Google turned up these two
references:
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vbcn7/html/vaconUsingForEach.asp http://17slon.com/blogs/gabr/2007/03/fun-with-enumerators-part-1.html

Judging from those links, an object is iterable in VB and Delphi (or as
they call it, it is enumerable) if it exposes a GetEnumerator method
that returns an enumerator. The enumerator in turn needs to expose a
MoveNext method and a Current property.

Assuming that the Enumerable and the Enumerator are allowed to be the
same object, you'll probably need code that looks something like this:

class foo:
_public_methods_=['GetEnumerator','MoveNext']
# You'll need to figure out how to expose "self.Current"

def __init__(self):
self.numbers=[1,2,3,4,5,6,7,8]
self.Current = None

def MoveNext(self):
try:
self.Current = self.numbers.pop(0)
return True
except IndexError:
return False

def GetEnumerator(self):
return self

Good luck,

Carsten.

I looked over the links and what you have proposed seems to make sense, but
I can't make it work. I have the following class defined and registered.

class foo:

_public_methods_=['GetEnumerator','MoveNext']
_public_attrs_=['Current']
_reg_clsid_='{FC2A0E7B-E428-4414-B1C4-60373BB12102}'
_reg_progid_="Syscon.foo"

def __init__(self):
self.numbers=[1,2,3,4,5,6,7,8]
self.Current = None

def MoveNext(self):
try:
self.Current = self.numbers.pop(0)
rtnval=True

except IndexError:
self.Current=None
rtnval=False

def GetEnumerator(self):
return self


Then I do:
.... print x
....
Traceback (most recent call last):
File "<interactive input>", line 1, in <module>
File "C:\Python25\lib\site-packages\win32com\client\dynamic.py", line 228, in
__getitem__
raise TypeError, "This object does not support enumeration"
TypeError: This object does not support enumeration
but this works fine:


I'm stumped.

-Larry
 
C

Carsten Haese

[...]

After more googling, staring at win32com's code, and a fair bit of trial
and error, I've come up with the following working example:

# server.py
import pythoncom

class HelloWorld:
_reg_clsid_ = "{CAB8BED1-9174-4AAD-ABC5-F377951CB71B}"
_reg_desc_ = "Python Test COM Server"
_reg_progid_ = "Python.TestServer"
_public_methods_ = ['Next']
_com_interfaces_ = [pythoncom.IID_IEnumVARIANT]

def __init__(self):
self.numbers=[1,2,3,4,5,6,7,8]

def Next(self, count):
assert count==1
try:
return (self.numbers.pop(0),)
except IndexError:
return ()

def _NewEnum(self):
import win32com.server.util
return win32com.server.util.wrap(self)

if __name__=='__main__':
import win32com.server.register
win32com.server.register.UseCommandLine(HelloWorld)

# client.py
import win32com.client
comobj = win32com.client.Dispatch("Python.TestServer")
for x in comobj:
print x

This works for me on Python 2.5 and pywin32 Build 210, but I don't know
whether clients in VB or Delphi are able to use this iterator.

-Carsten
 
L

Larry Bates

Carsten said:
[...]
On Tue, 2007-04-17 at 16:54 -0500, Larry Bates wrote:
Does anyone know if there is a way to make a Python COM object
act like a proper iterator in VB/Delphi?
[...]

After more googling, staring at win32com's code, and a fair bit of trial
and error, I've come up with the following working example:

# server.py
import pythoncom

class HelloWorld:
_reg_clsid_ = "{CAB8BED1-9174-4AAD-ABC5-F377951CB71B}"
_reg_desc_ = "Python Test COM Server"
_reg_progid_ = "Python.TestServer"
_public_methods_ = ['Next']
_com_interfaces_ = [pythoncom.IID_IEnumVARIANT]

def __init__(self):
self.numbers=[1,2,3,4,5,6,7,8]

def Next(self, count):
assert count==1
try:
return (self.numbers.pop(0),)
except IndexError:
return ()

def _NewEnum(self):
import win32com.server.util
return win32com.server.util.wrap(self)

if __name__=='__main__':
import win32com.server.register
win32com.server.register.UseCommandLine(HelloWorld)

# client.py
import win32com.client
comobj = win32com.client.Dispatch("Python.TestServer")
for x in comobj:
print x

This works for me on Python 2.5 and pywin32 Build 210, but I don't know
whether clients in VB or Delphi are able to use this iterator.

-Carsten

I tested in VB and by golly it works! What is odd is that this looks
NOTHING like what we got from the docs earlier. No GetEnumerator
method, no MoveNext method. I'm glad it works, but I'm a little
puzzled as to why it works.

Thanks loads.

-Larry
 
L

Larry Bates

Carsten said:
[...]
On Tue, 2007-04-17 at 16:54 -0500, Larry Bates wrote:
Does anyone know if there is a way to make a Python COM object
act like a proper iterator in VB/Delphi?
[...]

After more googling, staring at win32com's code, and a fair bit of trial
and error, I've come up with the following working example:

# server.py
import pythoncom

class HelloWorld:
_reg_clsid_ = "{CAB8BED1-9174-4AAD-ABC5-F377951CB71B}"
_reg_desc_ = "Python Test COM Server"
_reg_progid_ = "Python.TestServer"
_public_methods_ = ['Next']
_com_interfaces_ = [pythoncom.IID_IEnumVARIANT]

def __init__(self):
self.numbers=[1,2,3,4,5,6,7,8]

def Next(self, count):
assert count==1
try:
return (self.numbers.pop(0),)
except IndexError:
return ()

def _NewEnum(self):
import win32com.server.util
return win32com.server.util.wrap(self)

if __name__=='__main__':
import win32com.server.register
win32com.server.register.UseCommandLine(HelloWorld)

# client.py
import win32com.client
comobj = win32com.client.Dispatch("Python.TestServer")
for x in comobj:
print x

This works for me on Python 2.5 and pywin32 Build 210, but I don't know
whether clients in VB or Delphi are able to use this iterator.

-Carsten

I tested in VB and by golly it works! What is odd is that this looks
NOTHING like what we got from the docs earlier. No GetEnumerator
method, no MoveNext method. I'm glad it works, but I'm a little
puzzled as to why it works.

Thanks loads.

-Larry
 
S

Steve Holden

Larry said:
Carsten Haese wrote:
[iterative acess to COM objects]
I tested in VB and by golly it works! What is odd is that this looks
NOTHING like what we got from the docs earlier. No GetEnumerator
method, no MoveNext method. I'm glad it works, but I'm a little
puzzled as to why it works.
Presumably the magic of mark Hammond's wrapper classes providing
adaptation between Python iteration and COM enumerable collection
objects. win32all is *very* Pythonic.

Jim Hugunin, the author of IronPython (and of J[P]ython before that) has
commented that Microsoft users are frequently surprised by the small
amount of code required in IronPython to manipulate .NET objects.

regards
Steve
 
C

Carsten Haese

Larry said:
Carsten Haese wrote:
[iterative acess to COM objects]
I tested in VB and by golly it works! What is odd is that this looks
NOTHING like what we got from the docs earlier. No GetEnumerator
method, no MoveNext method. I'm glad it works, but I'm a little
puzzled as to why it works.

I'm glad, too.
Presumably the magic of mark Hammond's wrapper classes providing
adaptation between Python iteration and COM enumerable collection
objects. win32all is *very* Pythonic.

There is some magic, but it appears to be on the client side only. This
magic is what allows you to stick the win32com.client.Dispatcher object
into a for-loop.

If there were magic on the server side, it shouldn't be necessary to
declare that IID_IEnumVARIANT is exposed, and it shouldn't be necessary
to implement the _NewEnum and Next methods; it should be enough to have
__iter__ present and let the magic take care of the rest.

It appears that the reason why my first completely uninformed guess
didn't work and my second somewhat less uninformed guess did work is
that there are two different enumeration protocols in the wonderfully
confusing world of Win32. I'm guessing that GetEnumerator/MoveNext is
the .NET enumeration protocol and IID_IEnumVARIANT/Next is the COM
enumeration protocol.

-Carsten
 
R

Ross Ridge

Larry said:
I tested in VB and by golly it works! What is odd is that this looks
NOTHING like what we got from the docs earlier. No GetEnumerator
method, no MoveNext method. I'm glad it works, but I'm a little
puzzled as to why it works.

The documention Carsten Haese referenced earlier was for the .NET version
of VisualBasic, and shows how to use .NET's IEnumerate and IEnumerable
interfaces.

Steve Holden said:
Presumably the magic of mark Hammond's wrapper classes providing
adaptation between Python iteration and COM enumerable collection
objects. win32all is *very* Pythonic.

Maybe Mark Hammond's win32all provides such a magic wrapper, but Cartsen
Haese's example didn't use it. Instead it provided it own implementation
of the COM IEnumVARIANT interface, the OLE Automation (ie. VisualBasic
6 compatable) way implementing iteratable objects.

Ross Ridge
 
S

Steve Holden

Ross said:
The documention Carsten Haese referenced earlier was for the .NET version
of VisualBasic, and shows how to use .NET's IEnumerate and IEnumerable
interfaces.



Maybe Mark Hammond's win32all provides such a magic wrapper, but Cartsen
Haese's example didn't use it. Instead it provided it own implementation
of the COM IEnumVARIANT interface, the OLE Automation (ie. VisualBasic
6 compatable) way implementing iteratable objects.

Ross Ridge
So I saw when I read Carsten's reply to my post. Thanks. It's the
difference between COM and .NET (something I am currently having to come
to terms with myself, but strangely in C# rather than Python).

regards
Steve
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,996
Messages
2,570,237
Members
46,825
Latest member
VernonQuy6

Latest Threads

Top