> In casting around for a solution I poked into the code for
> DispatchWithEvents.  If I understand it correctly, it is
> possible to include
> IE automation methods as part of the event class something like this:
>
> class Yie:
>     --- class variables ---
>     def __init__(self):
>         --- instance variables ---
>     def NavigateWait1( self, url):
>         --- code to navigate and wait for completion ---
>     --- methods to provide other IE automation services ---
>     def OnVisible(self, vis):
>         --- code to handle OnVisible event ---
>     --- methods to handle other IE events ---
>
> Using this design it is possible to use the class something like this:
>
> ie = win32com.client.DispatchWithEvents(
>         'InternetExplorer.Application',Yie)
> ie.Visible=True
> ie.NavigateWait1('www.google.com')
>
> Since the revised Yie class holds BOTH the event and
> automation methods the
> need for the dictionary goes away and the On-event routines
> can reference
> whatever instance attributes/methods are needed. A bit of
> experimentation
> shows that this does indeed work (if anyone is interested,
> I'll gladly post
> the code).
>
> Question 1: Is this a good design/idea/advisable?

Yep, that looks fine.

> One of the things that has to happen for this to work is that
> a navigating
> IE automation method (Navigate2, GoBack, etc.) MUST pump
> messages or IE
> hangs.  The net of this is that the IE automation code to
> support navigation
> needs to look something like this:
>
>     def NavigateWait1( self, url, timeout):
>         startTime = time.time()
>         self.Navigate2( url )
>         while True:
>             pythoncom.PumpWaitingMessages()
>             rc = win32event.MsgWaitForMultipleObjects(
>                 (self._eventNone,), 0, 250,
>                 win32event.QS_ALLEVENTS)
>             if (rc == win32event.WAIT_TIMEOUT):
>                 if time.clock() - startTime >= timeout:
>                     return

You may actually find that this is not necessary if your main Python thread
is running a message loop.  These message loops are tricky, and used
primarily as a way to "marshal" calls to an appropriate thread - eg, the IE
event probably comes in on a different thread - that thread posts a message
to the main thread, which is expected to run its loop so the correct magic
will happen.  Thus, the behaviour of and requirement for message loops
depends on the object being used, and also the COM threading model of the
thread using the object.

> Question 2: Is this safe?  Python source gets translated into
> byte code.  I
> understand that a single byte code is atomic and can not be
> interrupted (is
> this true?).  But how about a statement?  There are a number
> of places in
> the automation code where several statements have to execute
> entirely or the
> internal state of the automation instance is inconsistent.
> Do I need to
> protect these with a Mutex?

You do not need to use a mutex to protect the integrity of the core Python
data structures (eg, it should be impossible to "crash" a list-object) -
however, you will need a mutex if there is a possibility 2 threads could
cause an object to get in a logically inconsistent state (ie, Python will
not crash, but your program may exhibit bugs if 2 threads try and append an
item to a list at the same time and then check that it is the end item, for
example)

> Alternately, can I depend on
> pythoncom.PumpWaitingMessages to be atomic such that the
> event routines CAN
> ONLY occur while the while loop is invoking PumpWaitingMessages?

I think Tim is correct - but this is not a "rule" as such.  It would
probably be possible to voilate this rule if you really tried, but in the
general case, if any events for a single object need a message loop, all
such events will.

> The event routine can return nothing, in which case IE does
> whatever the
> browser's configuration defines.  Alternately, the event
> routine can return
> True to cancel the NewWindow request (see
> http://msdn2.microsoft.com/en-us/library/aa768288.aspx).
> Finally, the event
> routine can return the IDispatch interface pointer of an
> InternetExplorer
> object that will host the new window.  This latter bit means
> that the event
> routine must instantiate a new instance something like this:
>
> new_ie = win32com.client.DispatchWithEvents(
>          'InternetExplorer.Application',Yie)
>
> Based on my knowledge of Python, I think this should work
> just fine.  It is,
> after all, just a method in a class creating a new instance
> of the class.
> Some experimentation shows that the OnNewWindow3 event
> routine can do this:
>
>     new_ie = win32com.client.DispatchWithEvents(
>          'InternetExplorer.Application',Yie)
>     return new_ie, false
>
> and the new IE instance is created.  But I'm a bit concerned
> that the new IE
> may start generating events BEFORE I return from the original IE's
> NewWindow3 event routine!

That shouldn't be possible until the message loop is established (assuming
these events are being delivered via the message loop)

> If it does, this means that the
> Python COM routines
> can potentially be re-entered while processing an event.

In the general case, that certainly is possible - but generally your event
handlers will not need to be running a message loop, just the main thread
itself should be.  It appears your code as posted is already reentrant -
your event handler runs a short message loop - if an event was delivered in
that time, I'd expect us to reenter into the new event handler.

> Alternately, does
> PumpWaitingMessages insure that at most one message at a time
> occurs so that
> the NewWindow3 event routine is guaranteed to complete BEFORE
> another event
> on the original or new IE can occur?

PumpWaitingMessages is a "normal" Windows message loop, fetching and
dispatching one message at the time.  It has no COM knowledge at all (it
just happens to enable COM to deliver events in some cases)

> Question 3: Does PumpWaitingMessages insure that at MOST one
> message/event
> is pumped such that reentrancy is not an issue?

It pumps one message at a time, but reentrancy is possible in the general
case - eg, an event handler may change an attribute on the object, which
itself may deliver a synch event.  In the free-threaded case this is far
more likely, but running a message loop in an event handler could also cause
it.

Mark

_______________________________________________
Python-win32 mailing list
Python-win32@python.org
http://mail.python.org/mailman/listinfo/python-win32

Reply via email to