[python-win32] extending com objects

Tim Golden mail at timgolden.me.uk
Thu May 24 22:01:09 CEST 2007


Ross McKerchar wrote:
> I've spent a good few hours bashing away and have found two ways which 
> appear to work with my simplistic test suite. However I'm not 
> particularly comfortable with either of them (the black magic I'm using 
> is all a bit new to me). Consequently if anyone has time to have a look 
> at my solutions I'd be very grateful.
> 
> Based on what Tim Golden called a "Delegation model", involved holding 
> an internal copy of my com class and by implementing getattr/setattr 
> calling the com object when necessary. This turned out a fair bit more 
> complicated than I originally expected as lots of the com objects 
> methods returned other com objects from the same library. I wanted these 
> returned com objects to also be automatically wrapped in my extension 
> classes to save having to constantly wrap in my client code. 

This is exactly the problem I have with things
like active_directory and WMI. In the case of the
latter you're slightly better off because the thing
allows for a certain amount of introspection. With
the former, I've just bitten the bullet and explictly
wrapped the most popular classes (User, Group, OU etc.)

But I agree: it's something of a pain.

 > ... it also introduced a significant performance overhead
 > (about 15% runtime overhead from some rough tests).

Yes, this doesn't bother me directly, but I was
contacted by someone who wanted to do real-time
monitoring with WMI where it starts to matter, so
I did some work with him to speed things up.

> Attempt #2:
> 
> This very simple, alternative method then occured to me:
> 
> #My custom classes
> class IDocument: ...
> class IDatabase: ...
> class IDocumentCollection: ...
> 
> from win32com.client.gencache import GetModuleForProgID
> module = GetModuleForProgID('Lotus.NotesSession')
> implementedChildClasses = (IDocument,IDatabase,IDocumentCollection)
> for klass in implementedChildClasses:
>      #find parent class of same name
>      parentclass = getattr(module,klass.__name__)
>      parentclass.__dict__.update(klass.__dict__)

This -- just in case you haven't come across the expression --
is called monkey-patching. It's a slightly general-purpose
example of it, but it's something which is usefully easy
to do in Python. (Consequently, it's easy to abuse but...)

Frankly, I never even thought of doing it. You may already
have covered this, but it does have one or corner-cases
you might want to consider. If one of your methods needs
to completely cover one of the original ones *and* to call
the original one as well (say to get the data you're going
to wrap) then you have to do something a bit smarter than
the straight __dict__.update you're doing there. Not very
much harder, but you'd have to do it.

 > The beauty of this is that I
> dont have to worry about wrapping the results of any functions as I'm 
> always dealing with the real com class, just with some extra methods 
> added. 

Presumably, then, if the original COM class exposed,
say, a .GetGroups method which returned a list of
groups, each of which you wanted to wrap, in this
implementation, you'd add a .MyGetGroups method which
called the original and wrapped the result? This is
the sort of thing I was averting to above. You *could*
rename (on the fly) .GetGroups to ._GetGroups and
add your own .GetGroups which called ._GetGroups under
the covers etc. This is obviously only important if
you're trying to present an API faithful to a documented
original. If you're simply rolling your own, then you
can do what you like.

I think what you've done is fine, and I'll certainly
look into it for my own stuff, not least because,
as you point out, it gives a speed advantage.

Others may point out hidden flaws which I've missed,
and I'll be glad to hear them, but if it works for
you... :)

TJG


More information about the Python-win32 mailing list