Active Scripting is a COM-based technology from Microsoft that allows applications to plug in different languages for macros or scripting. Rather than force the application to choose a single scripting language, end users of the application can pick the language they're most comfortable with. The application need not know anything specific about the scripting language; indeed, the language may have been written (or support for Active Scripting added) after the application was written.
The best examples of this technology are Microsoft's Internet Explorer (IE) and Internet Information Server (IIS). IE supports client-side scripting; an HTML file can have script code embedded in it in any language, and when the browser displays the page, it executes the code. Hence the term client-side scripting: the script code is actually executed on the client PC, the PC with the browser. IIS includes a component called Active Server Pages (ASP) that supports active scripting. Similar to IE, the code is embedded inside HTML files, but ASP executes the code at the server before it's converted to the final HTML sent to the client.
Many Microsoft applications are starting to support Active Scripting. These applications typically come standard with VBScript and JScript* language implementations, and all documentation and sample code use one of these languages. In fact, many users of these applications (IE and IIS users included) aren't aware you can use additional languages as well as the standard ones. Using a language such as Python and its extensive library can help you take full advantage of these applications.
Each application that supports Active Scripting exposes an object model to the script code. Regardless of the language the script is using, the object model avail-
* VBScript is a redistributable Active Scripting implementation by Microsoft using a subset of the Visual Basic language. JScript, also implemented by Microsoft and redistributable, uses syntax somewhat like Java. |
able is identical. Although this is the key strength of Active Scripting, it's also a problem when attempting to document how to use it. If we discuss how to use Active Scripting within IE, it really doesn't help you use Active Scripting within IIS. Each application supports its own object model, so the code you write is completely different. Therefore, we start this chapter with a cursory look at some of the popular Active Scripting applications from Microsoft, showing sample VBScript alongside Python code. We won't discuss any of these applications in detail, just enough for you to understand how to use Python in their environments. We then focus on how Active Scripting works and Python's implementation of Active Scripting. These will give you enough grounding so you can use Python inside any Active Scripting application armed with only the documentation for the application and its VBScript or JScript samples.
Finally, we take a look at adding Active Scripting support to your Python application. This lets your application's customers use any language they choose. You will be amazed at how simple it is to do from Python.
Further information on Active Scripting, including a number of Active Scripting-related tools can be found at http://msdn.microsoft.com/scripting/.
When you install the Python Win32 extensions, the Python Active Scripting implementation is automatically registered. If for some reason you need to install it manually, you should run the Python script python\win32comext\axscript\client\ pyscript.py. You can run this script from Windows Explorer, PythonWin, or a command prompt.
The most popular and visible Active Scripting applications come from Microsoft; This isn't surprising, since Microsoft developed the Active Scripting implementation:
Here we discuss Internet Explorer, Internet Information Server, and the Windows Scripting Host (WSH). Of these, IE is probably the most fun to play with, but has the least practical use. Using Active Scripting implies that the particular language is installed along with the browser, and for the vast majority of Internet Explorer users, this will not be true of Python. IIS and WSH are more practical, as they usually run on a machine over which you have more control; for example, it's often not a problem to install Python on the same machine that runs IIS. Similarly, WSH
is often used for Windows NT administration, so installing Python on the servers you are administering also shouldn't present a problem.
Internet Explorer, Version 4 and above, supports Active Scripting by embedding script code inside the HTML. As the HTML is parsed and rendered by IE, the code is detected, the particular language loaded, and the code executed. The script code is embedded in the HTML inside blocks delimited by <SCRIPT> and </SCRIPT> tags. The following is an example script that displays a message box:
<SCRIPT>
alert("Hello there")
</SCRIPT>
The default language for IE is VBScript, so this code is executed using VBScript. Because the script is so simple, the only change needed to make it use Python is to specify the language, as shown:
<SCRIPT Language=Python>
alert("Hello there")
</SCRIPT>
If you wish to experiment, you can save these three lines as a HTML file and open it in Internet Explorer. You should see a message box, followed by a blank web page.
You may be wondering where the alert() function came from, since it's not a standard Python built-in function. This is the key to how Active Scripting works. As the application (in this case IE) loads the particular language, it notifies the language of global functions and objects the language should make available; alert() is one of the functions made available by Internet Explorer. This is how the application exposes its object model to the language; the programmer uses these functions and objects to manipulate the application.
The object model exposed by Internet Explorer is similar to the Dynamic HTML object model. There is a window object, a document object, and so forth, all of which allow you to get information from the browser (for example, the current URL) or to direct the browser to perform some action (for example, open a new URL or reposition the window).
As mentioned previously, the fact that Python must be installed on each client PC is likely to be a barrier to using Python in the IE environment. It may be tempting to believe you could use Python to overcome some limitations in VBScript or JScript: for example, access the registry, open files, or create socket connections. Unfortunately, Python also restricts the capabilities available to the programmer when used with Internet Explorer. The whole concept of client-side scripting is dangerous, as you are downloading arbitrary code from a web page and running it on your computer. To avoid the potential problems with people writing malicious code, IE explicitly tells Python that it should operate in an untrusted mode, and Python complies by seriously restricting the operations the script can perform. For example, when running in Internet Explorer, it's impossible to import Python extension modules (such as win32api), open local files, create sockets, and so forth.
For this reason, we won't spend any time discussing the object model for Internet Explorer. Instead, we present a few examples and discuss the differences between VBScript and Python.
The following code presents an HTML file containing a form with one edit control and three buttons. A VBScript handler is added for one of the buttons and Python handlers to the remaining two. All the buttons perform the same operation: update one of the text controls with the current URL.
To keep the size of the example down, we avoided all HTML elements not absolutely necessary to demonstrate the scripts. For your own HTML, you should make all efforts to ensure your files conform to valid HTML (e.g., are enclosed in <HTML> and <BODY> tags):
<FORM NAME="TestForm">
<!-- A single text control all the buttons will use --
<INPUT TYPE="Text"
NAME="txtControl"
>
<!-- VBScript using inline event handling--
<INPUT NAME=butVB"
TYPE="Button"
VALUE="VB Script"
OnClick="txtControl.value=document.location"
>
<!-- Python using inline event handling--
<INPUT NAME="butPyl"
TYPE="Button"
VALUE="Python inline"
OnClick="TestForm.txtControl.value=document.location"
LANGUAGE=Python
>
<!-- Python using an implicit event handler --
<INPUT NAME="butPy2"
TYPE="Button"
VALUE="Python implicit"
>
<!--Now the code for the implicit handler --
<SCRIPT LANGUAGE=Python>
# Note we can not indent this block,
# as the leading whitespace will upset Python
def butPy2_onClick():
TestForm.txtControl.value=document.location
</SCRIPT>
</FORM>
This code demonstrates a few key points relating to the Python implementation of Active Scripting. If you examine the VBScript handler for the first button, notice that it can refer to the txtControl object without any special prefixes or annotations; because both the script and the control are part of the same form, a concept similar to scoping is applied. Python doesn't support this degree of scoping, so the Python handlers all must name each object explicitly.
Another important point is that in all Active Scripting applications, you must be careful regarding whitespace. Although HTML is generally insensitive to whitespace, any Python code embedded still is.
The event handling in Python attempts to conform to the conventions VBScript and JScript programmers would be familiar with. Event handling can be inline, where the script code is specified directly in the definition of the object as we demonstrated in the previous example. Alternatively, you can write a function with the name object_event(), where object is the name of the object, and event is the name of an event this object fires. Nothing is needed to explicitly attach the event to this function: the name of the function is all that is needed to get this behavior. This is demonstrated using PyBut2_onClick(), which responds to the onClick event for the PyBut2 object.
For more examples using Python within Internet Explorer, please see the Active Scripting samples, available from the PythonCOM README, installed with the Python for Windows extensions. For more information on the Internet Explorer object model, please see http://www.microsoft.com/windows/ie/.
The Microsoft Internet Information Server is a web server that supports the standard Unix web-server scripting technique called the Common Gateway Interface (CGI). In addition to CGI, IIS also supports Active Server Pages (ASP), which use Active Scripting to implement a richer scripting model than offered by CGI.
Active Server Pages uses a scheme to embed the script code that is similar to Internet Explorer. Code is still delimited with <SCRIPT> tags, but the tag has a RunAt=Server attribute added. For example:
<SCRIPT RunAt=Server Language=Python>
# This code will run at the server
</SCRIPT>
<SCRIPT Language=Python>
# This code will run at the client
</SCRIPT>
Although this sounds simple, the complication again is that the object model exposed by ASP is quite different to that exposed by IE. If you attempt to call the alert() function, your code fails as alert() doesn't exist in the ASP object model.
In addition to using <SCRIPT> tags, ASP allows alternative forms delimited by <% %> and <%= %> tags. Script code delimited by <% and %> tags are almost identical to those delimited by <SCRIPT> tags. The <%= %> tag allows you to specify arbitrary Python expressions, and the result of the expression is replaced in the output. For example, if ASP encounters HTML of the form:
Hello <%= name %>
The value of the name variable is printed in the output sent to the client.
The source files for Active Server Pages are stored as .asp files, although for all intents and purposes they are HTML files. The .asp extension allows IIS to determine if the page could potentially contain scripting code and hence should be executed under the control of ASP.
ASP also allows Python to run completely unrestricted. There are no limitations on the files that can be opened or the operations that can be performed. As the script code is maintained on the server, it's assumed the scripts are trusted. For a final feature that makes Python and ASP a great match, you can specify the default language for an ASP script. If an ASP page begins with the code <% LANGUAGE=Python %>, Python is used as the default language for all <SCRIPT>, <%, and <%= tags.
ASP exposes seven key objects, as detailed in Table 21-1. For details on the attributes and methods available on these objects, refer to the ASP documentation.
|
The following example uses the Server, Reponse, and Request objects. It's simple and again skips the <HTML> and other tags your file normally has. It begins by nominating Python as the default language for the page. This means all other code in the file uses Python without explicitly specifying it for each script block. The first script block uses the Request object to look up the URL of the current page and save it to a local variable. You then use <%= tags to print the variable value and call the Server.MapPath() method to translate this to a local filesystem reference. The final script block loops over all variables in the Request.Server-Variables collection, printing the variable name and value using the Response. Write() method:
<!--
ServerSample.asp - an example of Python
and Active Scripting
-->
<%$cs:Language=Python %>
<%
# Save the URL into a local variable
url = Request.ServerVariables("PATH_INFO")
%>
<H2>Current Document</H2>
The URL to this file is <pre><%= url %></pre><p>
The local path to this URL is <pre><%= Server.mappath(url) %></pre>
<H2>Client Request Headers</H2>
<%
for key in Request.ServerVariables:
Response.Write("%s=%s<br>" % (key, Request.ServerVariables(key)))
%>
The Response.Write() method sends output to the client. The string can contain any valid HTML, including tags. It's worth noting that the Python print statement is never redirected by the scripting engine. For example, it could be argued that when used with ASP, the print statement should send the output to the Response.Write() method, but this doesn't happen.
We are now-ready to run the script, but how do we do this? At this point, we better take a slight diversion into how you configure IIS to run scripts.
The process of configuring IIS is simple. Before you do anything, you must ensure that Python is installed on the IIS server, and the Active Scripting engine is registered, as we discuss previously in this chapter. There is nothing Python-specific that needs to be configured within IIS; all you need to do is tell IIS where to find your scripts. Do this with the following process:
1. Start the Internet Service Manager on the machine where IIS is installed.
2. Locate and select the default web server entry under your ASP server.
3. Select Action New Virtual Directory. The New Virtual Directory Wizard begins.
4. Enter PythonTest as the alias that accesses the virtual directory and select Next.
5. You are then prompted to enter the physical path of the directory containing the content. Enter the name of the directory where your test or sample .asp files are located and select Next.
6. You are then prompted for the permissions for the new directory. The default options are acceptable, so you can select Finish.
The ISM should then look something like Figure 21-1.
Figure 21-1. Internet Service Manager after creating the new directory |
Let's now execute the sample script. The simplest thing is to test your script by running Internet Explorer on the same machine as the server. If you enter a URL, http://localhost/PythonTest/ServerSample.asp, it executes the ServerSample.asp file from the physical path you specified for the PythonTest virtual directory.
Once this is done, the Explorer window should look like Figure 21-2.
Figure 21-2. The sample ASP file running under IIS |
Another handy Active Scripting tool is the Windows Scripting Host (WSH). WSH exposes an object model that makes it particularly suitable for tasks that are normally attempted with Windows batch files. WSH is often used for general Windows administration and can run the logon scripts as each user logs on to the network.
The Windows Scripting Host is included with Windows 98 and 2000, or can be obtained from http://msdn.microsoft.com/scripting for Windows 95 and NT. It's packaged as two executables: cscript.exe, a command-line tool, suitable for run-
ning from the Windows command prompt; and wscript.exe, a GUI application generally run from Windows Explorer.
WSH uses simple text files to hold the script code; Python files run under the Windows Scripting Host using the extension .pys. There are no tags or any other special characters needed, so WSH files that use Python are syntactically identical to a Python source file. The only difference is that if the code were executed directly by Python, the object model exposed by WSH is not available.
Version 5 of the Windows Scripting Host supports the objects described in Table 21-2.
|
The following code is a simple demo of Python used by the WSH. It prints the name of the script, the first argument passed to the script on the command line, and the first network connection:
# wsh.pys
# A Windows Scripting Host file that uses Python.
WScript.Echo("The script name is", WScript.ScriptName)
if len(WScript.Arguments):
WScript.Echo("The first argument is", WScript.arguments(0))
net = WScript.CreateObject("Wscript.Network")
netInfo = net.EnumNetworkDrives()
WScript.Echo(netInfo[0], "=", netInfo[1])
You can run this script from a command prompt and see the output:
C:\Scripts>cscript wsh.pys Hello
Microsoft (R) Windows Scripting Host Version 5.0 for Windows
Copyright (C) Microsoft Corporation 1996-1997. All rights reserved.
The script name is wsh.pys
The first argument is Hello
Z: = \\SKIPPY\d_drive
C:\Scripts>
Alternatively, you could use the GUI version of WSH by executing the command:
C:\Scripts>wscript C:\Scripts\wsh.pys Hello
This command can be executed from a command prompt of from the Windows Start Run menu item. It should be possible to execute a .pys file by double-clicking it in Windows Explorer, but Python currently doesn't support this.
When you execute the script under the GUI version, notice that all the WScript. Echo() calls are displayed as message boxes rather than printing a console message. This means that the example generates three message boxes. Depending on the needs of your script, either or both versions of WSH may be suitable.
One of the questions that may come to mind is ''Why bother?'' Python provides much of this functionality via the Win32 Extensions, and the native Python code is often simpler than the WSH version. For example, sys.argv and os.environ[] are certainly less to type than WScript .Arguments and WScript.CreateObject (“WScript.Shell”).Environment.
To further blur the distinction, it's worth noting that much of the functionality provided by WSH is exposed via the WScript.CreateObject() function. This function is almost identical to the Python function win32com.client.Dispatch(); it creates a COM object by name.
What this means is as long as WSH is installed on a machine, Python can still make use of most WSH objects, even when not running under WSH. For example, you can call EnumNetworkDrives() from a Python prompt as follows:
>>> from win32com.client import Dispatch
>>> net=Dispatch("Wscript.Network")
>>> netInfo=net.EnumNetworkDrives()
>>> print "%s=%s" % (netInfo[0], netInfo[1])
Z:=\\SKIPPY\d_drive
>>>
You have the best of both worlds: you can make use of all Python's features when running under WSH, and also make use of the best WSH features when running standard Python.
A popular extension to Active Scripting is Active Debugging, a technology that allows you to debug your scripts. Regardless of the language your scripts are implemented in, you can debug them in a single debugging environment provided by Microsoft. Interestingly, this allows you to step through multiple languages; for example, as Python code calls VB code that calls Python code, you can step through each individual line.
Microsoft provides two Active Debugging environments. The Windows Script Debugger is a free tool available from http://msdn.microsoft.com/scripting/, and while a nice debugger, the Microsoft Visual Interdev provides a richer debugging environment. The only drawback with Interdev is that it comes with Microsoft Visual C++ and is not available for free download.
There is not much to say about Active Debugging when using Python. You simply use the debugger, and Python is right there, ready to be debugged.
Unfortunately, the Active Debugging specification is new and not everything works quite as expected. For example, it's impossible to debug scripts under Internet Explorer 4, as the Active Desktop enhancements cause the debugger to complain that you are attempting to debug the desktop. A solution to this particular problem is to debug your script using a different EXE that still uses the IE4 control, e.g., PythonWin and the webrowser.py sample. A further complication is that not all Active Scripting hosts support Active Debugging. The Debugging interfaces are optional, and if the host doesn't support debugging the utility of the debugger is severely limited.
This section discusses some internals of the Active Scripting mechanism. Although this information isn't necessary for you to use Python in an Active Scripting environment, it may help you understand some of the intricacies of the Active Scripting implementations. Understanding this section is required for the next section.
As mentioned previously, Active Scripting is a COM-based technology and works by providing an object model for the end user. It should come as no surprise that this object model is provided by COM.
To summarize a complicated specification in a few paragraphs, here's the general process an application uses when using Active Scripting:
1. Determines the language to be used for a particular script block and creates the language engine as a normal COM object. Thus, VBScript, JScript, and Python are the COM ProgIDs used for the languages described here.
2. Passes the language engine a series of named items. Each named item is a COM object (that is, an IDispatch pointer) with a name and attributes. The attributes include whether the item is considered global and whether the item fires events.
3. Each named item is placed in the namespace by the language engine. Any named items that fire events have the event-handling mechanism put in place. In addition, any object considered global should have all its methods and properties made available as global methods and properties.
4. The application then gives the language engine the script code to execute. When the code refers to an object previously placed in the namespace, the language engine makes a standard COM call on the COM IDispatch pointer passed for the named item.
Let's reconsider the original example in the previous section "Internet Explorer." When IE loads the Python engine, it passes a number of standard items (such as Window, Document, and so forth). In addition to the standard items, a number of additional items are passed dependent on the HTML content. In the example, an item named TestForm and each element in the form are added. Many of these items may source events (such as the user interface Window and form element objects), but only a few are considered global. Internet Explorer designates the Window items as global, and if you refer to the IE documentation you will note that the Window item has an alert() method; hence you can refer to the alert() method globally, as in that first example:
Internally, everything is achieved using normal COM concepts; all method calls and property references are done with IDispatch, as discussed in Chapter 12, Advanced Python and COM. All event handling is done using normal IConnectionPoint interfaces, and although beyond the scope of this book, is fully supported by the standard Python COM framework.
As a fitting finale to this section of the book, we will discuss how you can incorporate Active Scripting in your Python application. If you are working with Python, you already have a very cool extension language built right-in, but there may be good reasons for wanting to extend this to include other languages, marketing being just one!
If there is any end of the Active Scripting specification to work with, creating an application that supports Active Scripting is the one to choose over building a language engine. The COM specifications for applications using Active Scripting are not difficult to understand, and Python has additional helpers that reduce this to a small amount of code.
The sample application exposes an Active Scripting object model. It loads script code from a text file and executes it using any Active Scripting-supported language: two more examples will demonstrate VBScript and Python. The object model the application exposes is simple; there's a single Application object supporting a single method Echo, which displays a message box.
These are the steps:
1. Creates a PyIActiveScriptSite object. The win32com.axscript.server. axsite module provides an Active Scripting Site base-class that is suitable for our purposes. All you need to do is add an error handler; it's simple and prints the message to the console.
2. Defines the object model. The win32com.axscript.server.axsite module allows you to specify a dictionary of string-object pairs, where string is the name for the object model, and object is any PyIDispatch object. The example provides a simple Python COM object. For more information on implementing COM objects using Python, see Chapter 12.
3. Loads the code and passes it to the required language engine. This sample accepts two command-line parameters, the name of the language to use, and the name of a text file containing the code:
# ActiveApp.py - Demonstration of a Python Active Scripting Application
import string, sys
from win32com.axscript import axscript
from win32com.axscript.server import axsite
import pythoncom
import win32com.server.util
class MySite(axsite.AXSite):
# Our error handler will print to the console.
def OnScriptError(self, activeScriptError):
exc = activeScriptError.GetExceptionInfo()
print "Exception:", exc[1]
try:
sourceText = activeScriptError.GetSourceLineText()
except pythoncom.com_error:
sourceText = None
if sourceText is not None:
context, lineNo, charNo = activeScriptError.GetSourcePosition()
print sourceText
indent = " " * (charNo-1)
print indent + "ˆ"
return winerror.S_OK
# A named object for our namespace
# A normal Python COM object (minus registration info)
class Application:
_public_methods_ = [ 'Echo' ]
def Echo(self, *args):
print string.join(map(str, args))
# Our function that creates the site, creates the engine
# and runs the code.
def RunCode(engineName, code):
app = win32com.server.util.wrap( Application() )
# Create a dictionary holding our object model.
model = {
'Application' : app,
}
# Create the scripting site.
site = MySite(model)
# Create the engine and add the code.
engine = site.AddEngine(engineName)
engine.AddCode(code)
# Run the code.
engine.Start()
if __name__ == '__main__':
if len(sys.argv) < 3:
print "Usage: ActiveApp.py Language ScriptFile"
else:
code = open(sys.argv[2]).read()
RunCode( sys.argv[1], code )
The error-handling function takes up the bulk of the code as it attempts to accurately indicate the exact location of the error. The code that runs the engine is surprisingly small. You create the script site and the engine, and add the code. When you start the engine, all previously loaded code is executed.
To test this Active Scripting engine, let's use two text files: the first is for VBScript:
rem Sample VBScript code to be used with ActiveApp.py
sub JustForTheSakeOfIt
Application.Echo("Hello from VBScript")
end sub
rem Now call the Sub
JustForTheSakeOfIt
The second is for Python:
# Sample Python code to be used with ActiveApp.py
def JustForTheSakeOfIt():
Application.Echo("Hello from Python")
JustForTheSakeOfIt()
Testing the application is straightforward. Assuming that the Python program and both sample text files are in the same directory, use the following commands:
C:\Scripts>ActiveApp.py VBScript ActiveApp_VBScript.txt
Hello from VBScript
C:\Scripts>
And testing Python:
C:\Scripts>ActiveApp.py Python ActiveApp_Python.txt
Hello from Python
C:\Scripts>
Finally, to test the error handler, mix the filename and the language. Let's ask VBScript to execute the Python code:
C:\Scripts>ActiveApp.py VBScript ActiveApp_Python.txt
Exception: Microsoft VBScript compilation error
# Same Python code to be used with ActiveApp.py
ˆ
Traceback (innermost last):
File "C:\Scripts\ActiveApp.py", line 49, in ?
RunCode( sys.argv[1], code )
File "C:\Scripts\ActiveApp.py", line 40, in RunCode
engine.AddCode(code)
File "C:\Program Files\Python\win32comext\axscript\server\axsite.py", line 31, in AddCode
self.eParse.ParseScriptText(code, None, None, None, 0, 0, 0)
pywintypes.com_error: (-2146827264, 'OLE error 0x800a0400', (0, 'Microsoft
VBScript compilation error', 'E
xpected statement', None, 0, -2146827264), None)
C:\Scripts>
The error handler worked fine, but the error information was propagated back to the calling function; in this case, the engine.AddCode() line in the sample. Obviously you'd want to add an exception handler for this case.
Although this has not been a thorough discussion of adding Active Scripting to your application, it should whet your appetite and give you enough information to get started. To really take advantage of Active Scripting, you should consult the Active Scripting documentation, available on MSDN.
In this chapter, we looked at using Python in an Active Scripting environment. The most common use of Active Scripting and Python is using Python as a scripting language in another application, and we discussed some popular Microsoft applications that can work with Python.
We also showed a brief teaser how you could leverage Active Scripting in your own Python application, allowing your users to choose the specific language they use; we all know that not every Windows programmer has seen the Python light!