[python-win32] ASP Python

Jorgensen, Jens jens.jorgensen@tallan.com
Tue, 10 Jul 2001 09:30:58 -0500


--------------080505090300060204080400
Content-Type: text/plain; charset=UTF-8; format=flowed
Content-Transfer-Encoding: 8bit

Marc Miller wrote:


Do people do serious ASP programming in Python?   I've recently been
doing somewhat-serious ASP programming in Perl and have started
investigating Python as an alternative.   The lack of thorough
documentation and reasonable examples struck me, but that didn't stop me
from entering the Perl ASP world :-).   Anyway, I've got two questions
that maybe someone can help me with.
 
The first question deals with assigning to COM object properties which
take arguments.  A very similar question was asked in April by Gregory
P. Smith ("assigning vb properties from python via COM").  Consider the
following piece of JScript code:
 
 // example JScript code
 Application('x') = 4;
 Response.Write(Application('x'));
 
The output is "4".  This example doesn't compile in python-land; you get
an error because you're assigning to the result of a function call.  I
think there was some confusion on the other thread, so let me just
expand on what's happening here: JScript is calling the default method
on the application object, "Value", so this is equivalent to the
following JScript code:


 // example JScript code
 Application.Value('x') = 4;
 Response.Write(Application('x'));

JScript actually turns the assignment into a single COM call which is
equivalent to this pseudo-C++ code:
 
 // equivalent pseudo-C++ code
 VARIANT v = { 0 };   // put “4” inside a variant
 v.vt      = VT_I4;
 v.lval    = 4;
 
 BSTR  bstrPropName = SysAllocString(“x”);
 
 // assume pApplication is of type IApplicationObject (see asp.dll)
 pApplication->put_Value(bstrPropName, v);
 
 SysFreeString(bstrPropName);

Anyway, Perl doesn’t have as sophisticated IDispatch integration as
Jscript does and cannot recognize something that looks like an
assignment to the result of a function call as a property put to the COM
object in question.   So the Perl guys have this hack workaround which
works great (though it’s not terribly pretty): they implement a method
on their COM objects called “SetProperty”:
 
 # equivalent Perl code:
 $Application->SetProperty('Value', 'x', 4);
 $Response->Write($Application->Value('x'));
 
Which brings me back to Python.  I haven’t been able to find anything
similar in Python.   Fortunately, Python’s COM integration has much more
to it than Perl’s, so I can call the IDispatch directly and do what I
want.
 
 # equivalent Python code:
 o      = Application._oleobj_;
 dispId = o.GetIDsOfNames('Value');
 
 # perform the equivalent of Application('x') = 4
 o.Invoke(dispId, 0x0, pythoncom.DISPATCH_PROPERTYPUT, 0, 'x', 4);
 
 Response.Write(Application('x'));
 
Blech!  Does anyone know a better way?

Yes indeed. Many times you'll run across objects which use odd semantics
like this (Microsoft just *loves* to do this). When I do I like to use
makepy.py. This script takes a type library and generates a set of
wrapper objects for you. It generally does a slick job of handling stuff
like this.  This script is found in the win32com\client subdirectory of
your Python installation. It's an interactive Tk app. Run it and it
brings up a list of type libraries registered by the system. Just choose
one and click ok and it'll generate a python module for the library. By
default it puts the module into win32com\gen_py\{type lib guid}.py.
Alternatively you can use the -o param to put it into a file of your
choosing. So, I put mine into ASP.py and stored that somewhere in my
python path. Now, usually when you've used makepy.py you create objects
in the usual Python way, e.g.

app = ASP.Application()

but in this case the Application object already exists. We just want to
create a Python Application object we generated from the type library
and have it wrap this existing object so instead we say:

app = ASP.Application(oobj=Application)

If you look at the source of the ASP module you generated and check out
the CoClassBaseClass you'll see that its __init__ method takes oobj as a
named parameter for the object reference to use. If you don't supply one
it creates a new instance of the object using the CLSID of the CoClass
from the type library. So, using this I can now write:

<%@ LANGUAGE=Python %>
<html>
<body>
<%
import sys

sys.path.append('c:\\Python21\\local')

import ASP

app = ASP.Application(oobj=Application)

num = app.Value('num')
if not num :
    num = 0

Response.Write(num)

app.SetValue('num', num+1)
%>
</body>
</html>


That is actual running code I used. Note that on my box
c:\Python21\local is where I like to keep modules that I create.
Ordinarily this gets picked up automatically for my scripts because I
have it set in my environment as PYTHONPATH but IIS doesn't get my
environment so I add it manually.



 
Being able to store stuff in the application object, by the way, is
really useful for ASP programming (at least the stuff I’m working on).
It allows you to maintain a state across your web application from
request to request.  The caveat is that anything you want to store in
the Application object has to be able to fit inside a VARIANT.
Futhermore, if the thingy you want to store is an object (like
VT_DISPATCH), then it has to be free-threaded (I’m not so sure about
this restriction).   

I believe the case is not that the object has to be free-threaded but
that if it isn't then you lock that worker thread to a specific session
(greatly hindering scalability). I'm not positive these are the exact
semantics but its something like this.


This is a bummer, because I would like to store Perl or Python’s rich
data structures in there.

Perhaps pickle or something like that is in order? You could serialize
to a string and then rehydrate in your object.


However, I was playing around with Python and noticed something strange.
Consider the following ASP script:
 
 <%@ LANGUAGE="Python" %>
 <%
 import sys;
 if 'x' in dir(sys):
    Response.Write('x in dir %s' % sys.x);
    sys.x = sys.x + 1
 else:
    Response.Write('x not in dir');
    sys.x = 0
 %>
 
What happens on my server setup is cool: “sys.x” is a counter of how
many times I’ve refreshed the page.  Anyone know what’s going on here?
I have a hunch this is just an implementation side-effect and not
by-design, but it would be really useful.

This sounds dangerous and unintended to me but I don't really have a
clue.


I’m not subscribed to this list, so please copy me in any replies.

Perhaps you should consider joining. It's low volume and I'm guessing
you might learn a few valuable tidbits here and there.


-- 

Jens B. Jorgensen

jens.jorgensen@tallan.com <mailto:jens.jorgensen@tallan.com> 


--------------080505090300060204080400
Content-Type: text/html; charset=UTF-8
Content-Transfer-Encoding: 8bit

<html>
<head>
</head>
<body>
Marc Miller wrote:<br>
<blockquote type="cite" cite="mid:LAW2-OE42XKkTrEWtyL000039bc@hotmail.com">
  <div><font face="Courier New, Courier, Monospace"><span style="font-family: &apos;Courier New&apos;; font-size: 10pt; ">
Do people do serious ASP programming in Python?<span style="mso-spacerun: yes">
  </span>I've recently been doing somewhat-serious ASP programming in Perl
and have started investigating Python as an alternative.<span style="mso-spacerun: yes">
  </span>The lack of thorough documentation and reasonable examples struck
me, but that didn't stop me from entering the Perl ASP world :-).<span style="mso-spacerun: yes">
  </span>Anyway, I've got two questions that maybe someone can help me with.</span></font></div>
  <div> </div>
  <div><font face="Courier New, Courier, Monospace"><span style="font-family: &apos;Courier New&apos;; font-size: 10pt; "><span style="font-family: &apos;Courier New&apos;; font-size: 10pt; ">
The first question deals with assigning to COM object properties which take
arguments.<span style="mso-spacerun: yes">  </span>A very similar question
was asked in April by Gregory P. Smith ("assigning vb properties from python
via COM").<span style="mso-spacerun: yes">  </span>Consider the following
piece of JScript code:</span></span></font></div>
  <div> </div>
  <div><font face="Courier New"> // example JScript code</font></div>
  <div><font face="Courier New"><strong> Application('x') = 4;</strong></font></div>
  <div><font face="Courier New"> Response.Write(Application('x'));</font></div>
  <div> </div>
  <div><font face="Courier New, Courier, Monospace"><span style="font-family: &apos;Courier New&apos;; font-size: 10pt; "><span style="font-family: &apos;Courier New&apos;; font-size: 10pt; "><span style="font-family: &apos;Courier New&apos;; font-size: 10pt; ">
The output is "4".<span style="mso-spacerun: yes">  </span>This example doesn't
compile in python-land; you get an error because you're assigning to the
result of a function call.<span style="mso-spacerun: yes">  </span>I think
there was some confusion on the other thread, so let me just expand on what's
happening here: JScript is calling the default method on the application
object, "Value", so this is equivalent to the following JScript code:</span></span></span></font></div>
  <div><font face="Courier New, Courier, Monospace"><br>
  </font>
  <div><font face="Courier New, Courier, Monospace"><font face="Courier New">
 // example JScript code</font></font></div>
  <div><font face="Courier New, Courier, Monospace"><font face="Courier New"><strong>
 Application.Value('x') = 4;</strong></font></font></div>
  <div><font face="Courier New, Courier, Monospace"><font face="Courier New">
 Response.Write(Application('x'));</font><br>
  </font></div>
  <div><span style="font-family: &apos;Courier New&apos;; font-size: 10pt; "><font face="Courier New, Courier, Monospace">
JScript actually turns the assignment into a single COM call which is equivalent
to this pseudo-C++ code:</font></span></div>
  <div><span style="font-family: &apos;Courier New&apos;; font-size: 10pt; "></span><font face="Courier New, Courier, Monospace">
 </font></div>
  <div><span style="font-family: &apos;Courier New&apos;; font-size: 10pt; "><font face="Courier New, Courier, Monospace">
 // equivalent pseudo-C++ code<br>
 VARIANT v = { 0 };   // put “4” inside a variant<br>
 v.vt      = VT_I4;<br>
 v.lval    = 4;</font></span></div>
  <div><span style="font-family: &apos;Courier New&apos;; font-size: 10pt; "></span><font face="Courier New, Courier, Monospace">
 </font></div>
  <div><span style="font-family: &apos;Courier New&apos;; font-size: 10pt; "><font face="Courier New, Courier, Monospace">
 BSTR  bstrPropName = SysAllocString(“x”);</font></span></div>
  <div><span style="font-family: &apos;Courier New&apos;; font-size: 10pt; "></span><font face="Courier New, Courier, Monospace">
 </font></div>
  <div><span style="font-family: &apos;Courier New&apos;; font-size: 10pt; "><font face="Courier New, Courier, Monospace">
 // assume pApplication is of type IApplicationObject (see asp.dll)<br>
  <strong> pApplication-&gt;put_Value(bstrPropName, v);</strong></font></span></div>
  <div><span style="font-family: &apos;Courier New&apos;; font-size: 10pt; "></span><font face="Courier New, Courier, Monospace">
 </font></div>
  <div><span style="font-family: &apos;Courier New&apos;; font-size: 10pt; "><font face="Courier New, Courier, Monospace">
 SysFreeString(bstrPropName);<br>
  </font></span></div>
  <div><span style="font-family: &apos;Courier New&apos;; font-size: 10pt; "><span style="font-family: &apos;Courier New&apos;; font-size: 10pt; "><font face="Courier New, Courier, Monospace">
Anyway, Perl doesn’t have as sophisticated IDispatch integration as Jscript
does and cannot recognize something that looks like an assignment to the
result of a function call as a property put to the COM object in question.<span style="mso-spacerun: yes">
  </span>So the Perl guys have this hack workaround which works great (though
it’s not terribly pretty): they implement a method on their COM objects called
“SetProperty”:</font></span></span></div>
  <div><span style="font-family: &apos;Courier New&apos;; font-size: 10pt; "><span style="font-family: &apos;Courier New&apos;; font-size: 10pt; "></span></span><font face="Courier New, Courier, Monospace">
 </font></div>
  <div><span style="font-family: &apos;Courier New&apos;; font-size: 10pt; "><span style="font-family: &apos;Courier New&apos;; font-size: 10pt; "><font face="Courier New, Courier, Monospace">
 # equivalent Perl code:<br>
 $Application-&gt;SetProperty('Value', 'x', 4);<br>
 $Response-&gt;Write($Application-&gt;Value('x'));</font></span></span></div>
  <div><span style="font-family: &apos;Courier New&apos;; font-size: 10pt; "><span style="font-family: &apos;Courier New&apos;; font-size: 10pt; "></span></span><font face="Courier New, Courier, Monospace">
 </font></div>
  <div><span style="font-family: &apos;Courier New&apos;; font-size: 10pt; "><span style="font-family: &apos;Courier New&apos;; font-size: 10pt; "></span></span><span style="font-family: &apos;Courier New&apos;; font-size: 10pt; "><span style="font-family: &apos;Courier New&apos;; font-size: 10pt; "><span style="font-family: &apos;Courier New&apos;; font-size: 10pt; "><font face="Courier New, Courier, Monospace">
Which brings me back to Python.<span style="mso-spacerun: yes">  </span>I
haven’t been able to find anything similar in Python.<span style="mso-spacerun: yes">
  </span>Fortunately, Python’s COM integration has much more to it than Perl’s,
so I can call the IDispatch directly and do what I want.</font></span></span></span></div>
  <div><span style="font-family: &apos;Courier New&apos;; font-size: 10pt; "></span><font face="Courier New, Courier, Monospace">
 </font></div>
  <div><span style="font-family: &apos;Courier New&apos;; font-size: 10pt; "><font face="Courier New, Courier, Monospace">
 # equivalent Python code:<br>
 o      = Application._oleobj_;<br>
 dispId = o.GetIDsOfNames('Value');</font></span></div>
  <div><span style="font-family: &apos;Courier New&apos;; font-size: 10pt; "></span><font face="Courier New, Courier, Monospace">
 </font></div>
  <div><span style="font-family: &apos;Courier New&apos;; font-size: 10pt; "><font face="Courier New, Courier, Monospace">
 # perform the equivalent of Application('x') = 4<br>
  <strong> o.Invoke(dispId, 0x0, pythoncom.DISPATCH_PROPERTYPUT, 0, 'x',
4);</strong></font></span></div>
  <div><span style="font-family: &apos;Courier New&apos;; font-size: 10pt; "></span><font face="Courier New, Courier, Monospace">
 </font></div>
  <div><span style="font-family: &apos;Courier New&apos;; font-size: 10pt; "><font face="Courier New, Courier, Monospace">
 Response.Write(Application('x'));</font></span></div>
  <div><font face="Courier New, Courier, Monospace"> </font></div>
  <div><span style="font-family: &apos;Courier New&apos;; font-size: 10pt; "><font face="Courier New, Courier, Monospace">
Blech!<span style="mso-spacerun: yes">  </span>Does anyone know a better
way?</font></span></div>
  </div>
  </blockquote>
Yes indeed. Many times you'll run across objects which use odd semantics
like this (Microsoft just *loves* to do this). When I do I like to use makepy.py.
This script takes a type library and generates a set of wrapper objects for
you. It generally does a slick job of handling stuff like this.  This script
is found in the win32com\client subdirectory of your Python installation.
It's an interactive Tk app. Run it and it brings up a list of type libraries
registered by the system. Just choose one and click ok and it'll generate
a python module for the library. By default it puts the module into win32com\gen_py\{type
lib guid}.py. Alternatively you can use the -o param to put it into a file
of your choosing. So, I put mine into ASP.py and stored that somewhere in
my python path. Now, usually when you've used makepy.py you create objects
in the usual Python way, e.g.<br>
  <br>
app = ASP.Application()<br>
  <br>
but in this case the Application object already exists. We just want to create
a Python Application object we generated from the type library and have it
wrap this existing object so instead we say:<br>
  <br>
app = ASP.Application(oobj=Application)<br>
  <br>
If you look at the source of the ASP module you generated and check out the
CoClassBaseClass you'll see that its __init__ method takes oobj as a named
parameter for the object reference to use. If you don't supply one it creates
a new instance of the object using the CLSID of the CoClass from the type
library. So, using this I can now write:<br>
  <br>
&lt;%@ LANGUAGE=Python %&gt;<br>
&lt;html&gt;<br>
&lt;body&gt;<br>
&lt;%<br>
import sys<br>
  <br>
sys.path.append('c:\\Python21\\local')<br>
  <br>
import ASP<br>
  <br>
app = ASP.Application(oobj=Application)<br>
  <br>
num = app.Value('num')<br>
if not num :<br>
    num = 0<br>
  <br>
Response.Write(num)<br>
  <br>
app.SetValue('num', num+1)<br>
%&gt;<br>
&lt;/body&gt;<br>
&lt;/html&gt;<br>
  <br>
  <br>
That is actual running code I used. Note that on my box c:\Python21\local
is where I like to keep modules that I create. Ordinarily this gets picked
up automatically for my scripts because I have it set in my environment as
PYTHONPATH but IIS doesn't get my environment so I add it manually.<br>
  <br>
  <blockquote type="cite" cite="mid:LAW2-OE42XKkTrEWtyL000039bc@hotmail.com">
    <div>
    <div><span style="font-family: &apos;Courier New&apos;; font-size: 10pt; "></span><font face="Courier New, Courier, Monospace">
 </font></div>
    <div><span style="font-family: &apos;Courier New&apos;; font-size: 10pt; "><span style="font-family: &apos;Courier New&apos;; font-size: 10pt; "><font face="Courier New, Courier, Monospace">
Being able to store stuff in the application object, by the way, is really
useful for ASP programming (at least the stuff I’m working on).<span style="mso-spacerun: yes">
  </span>It allows you to maintain a state across your web application from
request to request.<span style="mso-spacerun: yes">  </span>The caveat is
that anything you want to store in the Application object has to be able
to fit inside a VARIANT.<span style="mso-spacerun: yes">  </span>Futhermore,
if the thingy you want to store is an object (like VT_DISPATCH), then it
has to be free-threaded (I’m not so sure about this restriction).<span style="mso-spacerun: yes">
  </span></font></span></span></div>
    </div>
    </blockquote>
I believe the case is not that the object has to be free-threaded but that
if it isn't then you lock that worker thread to a specific session (greatly
hindering scalability). I'm not positive these are the exact semantics but
its something like this.<br>
    <blockquote type="cite" cite="mid:LAW2-OE42XKkTrEWtyL000039bc@hotmail.com">
      <div>
      <div><span style="font-family: &apos;Courier New&apos;; font-size: 10pt; "><span style="font-family: &apos;Courier New&apos;; font-size: 10pt; "><font face="Courier New, Courier, Monospace">
This is a bummer, because I would like to store Perl or Python’s rich data
structures in there.</font></span></span></div>
      </div>
      </blockquote>
Perhaps pickle or something like that is in order? You could serialize to
a string and then rehydrate in your object.<br>
      <blockquote type="cite" cite="mid:LAW2-OE42XKkTrEWtyL000039bc@hotmail.com">
        <div>
        <div><span style="font-family: &apos;Courier New&apos;; font-size: 10pt; "><span style="font-family: &apos;Courier New&apos;; font-size: 10pt; "></span></span><font face="Courier New, Courier, Monospace">
 </font><span style="font-family: &apos;Courier New&apos;; font-size: 10pt; "><span style="font-family: &apos;Courier New&apos;; font-size: 10pt; "><span style="font-family: &apos;Courier New&apos;; font-size: 10pt; "><font face="Courier New, Courier, Monospace">
However, I was playing around with Python and noticed something strange.<span style="mso-spacerun: yes">
  </span>Consider the following ASP script:</font></span></span></span></div>
        <div><span style="font-family: &apos;Courier New&apos;; font-size: 10pt; "></span><font face="Courier New, Courier, Monospace">
 </font></div>
        <div><span style="font-family: &apos;Courier New&apos;; font-size: 10pt; "><font face="Courier New, Courier, Monospace">
 &lt;%@ LANGUAGE="Python" %&gt;</font></span></div>
        <div><span style="font-family: &apos;Courier New&apos;; font-size: 10pt; "><font face="Courier New, Courier, Monospace">
 &lt;%<br>
 import sys;</font></span></div>
        <div><span style="font-family: &apos;Courier New&apos;; font-size: 10pt; "><font face="Courier New, Courier, Monospace">
 if 'x' in dir(sys):<br>
    Response.Write('x in dir %s' % sys.x);<br>
    sys.x = sys.x + 1<br>
 else:<br>
    Response.Write('x not in dir');<br>
    sys.x = 0</font></span></div>
        <div><span style="font-family: &apos;Courier New&apos;; font-size: 10pt; "><font face="Courier New, Courier, Monospace">
 %&gt;</font></span></div>
        <div><span style="font-family: &apos;Courier New&apos;; font-size: 10pt; "></span><font face="Courier New, Courier, Monospace">
 </font></div>
        <div><span style="font-family: &apos;Courier New&apos;; font-size: 10pt; "><span style="font-family: &apos;Courier New&apos;; font-size: 10pt; "><font face="Courier New, Courier, Monospace">
What happens on my server setup is cool: “sys.x” is a counter of how many
times I’ve refreshed the page.<span style="mso-spacerun: yes">  </span>Anyone
know what’s going on here?<span style="mso-spacerun: yes">  </span>I have
a hunch this is just an implementation side-effect and not by-design, but
it would be really useful.</font></span></span></div>
        </div>
        </blockquote>
This sounds dangerous and unintended to me but I don't really have a clue.<br>
        <blockquote type="cite" cite="mid:LAW2-OE42XKkTrEWtyL000039bc@hotmail.com">
          <div>
          <div><span style="font-family: &apos;Courier New&apos;; font-size: 10pt; "><span style="font-family: &apos;Courier New&apos;; font-size: 10pt; "></span></span><font face="Courier New, Courier, Monospace">
 </font><span style="font-family: &apos;Courier New&apos;; font-size: 10pt; "><span style="font-family: &apos;Courier New&apos;; font-size: 10pt; "><span style="font-family: &apos;Courier New&apos;; font-size: 10pt; "><font face="Courier New, Courier, Monospace">
I’m not subscribed to this list, so please copy me in any replies.</font></span></span></span></div>
          </div>
          </blockquote>
Perhaps you should consider joining. It's low volume and I'm guessing you
might learn a few valuable tidbits here and there.<br>
          <br>
          <pre class="moz-signature" cols="$mailwrapcol">-- 
Jens B. Jorgensen
<a class="moz-txt-link-abbreviated" href="mailto:jens.jorgensen@tallan.com">jens.jorgensen@tallan.com</a>
</pre>
          <br>
          </body>
          </html>

--------------080505090300060204080400--