| Name |
Space |
Section |
Page |
Version |
Status |
Curator |
Reviewed |
Author(s) |
| Serving a Pylons app with IIS |
Pylons CookBook |
|
Serving a Pylons app with IIS |
1.0 |
Draft |
Graham Higgins |
False |
|
Serving a Pylons app with IIS
Pylons (or any other python web framework) is not easy under IIS. But if you really want to do it, (to get integrated authentication is the reason I wanted to) you can do it. There are other options, namely one that uses ASP, but this one, believe it or not, looked the cleanest.
Here's how it looks:
IIS < ISAPI < PythonWin32 ISAPI < ISAPI_WSGI < Pylons
This guide assumes you have Python Win32, IIS and Pylons installed. I'll take you through three successively more complex configurations, finally ending with a Pylons IIS integrated app.
ISAPI + Python
First, before we get fancy, try the basic Python Win32 ISAPI application. It should be at:
C:\Python24\Lib\site-packages\isapi\samples\advanced.py
You can install it by executing the file. Better at first to run it on the command line so you can see the output:
python advanced.py install
You can remove the IIS virtual directory that it creates with a simple command or by deleting it in the IIS MMC:
python advanced.py remove
This should create a virtual directory in IIS called AdvancedPythonSample.
I found no good, clear, explanation of this, but basically, the way it works is that you place the installation routine for your IIS virtual directory in if _name_ == '_main_': and this install routine creates a dll in the same directory which is named the same as your script preceded by an underscore character. This DLL is the mechanism that proxies requests back to the same Python module that created it.
Then, each time you do a HTTP GET on that virtual dir, the DLL imports your module (the same one with the if _name_ == '_main_) but doesn't run the install routine. It just runs whatever code is in the root namespace of the module.
In advanced.py on line 120, the meat of the script that executes at runtime is this:
1
2
3 | # The entry points for the ISAPI extension.
def __ExtensionFactory__():
return Extension()
|
The primary place in Extension() that generates the HTTP response is
HttpExtensionProc(). this is the place later where we'll insert our Pylons app.
Now point your browser at "http://localhost/AdvancedPythonSample/"
And it should say This module has been imported 0 times. It will say this no matter how many times you reload it. Something is broken with the example Python script that watches the filesystem. Don't worry, ISAPI + Python is working.
The Virtual Directory fake-out
I should mention that the virtual directory setup as AdvancedPythonSample points to the c:\inetpub\wwwroot folder. This is the default if it is not specified when created. This folder is NOT served by your virtual directory. It's deceiving, but IIS is proxying all requests through to your dll and it is loading modules in another directory. You can set access restrictions on this virtual directory and choose integrated auth with disabled anonymous connections as you normally would. But be aware the the permissions changes you make to the files in c:\inetpub\wwwroot will not affect your app because those files are not even being used. Permission could be modified on your application files like the controller modules.
Python Win32 Tracing
Before we move on, we should set up tracing. What you've got here is a fairly complicated chain of software and some of it puposefully obscures what's going on (hello ISAPI). IIS is running Python in-process so you cannot see debug output from the python interpreter unless you run a tracing util from pythonwin32. Run:
C:\Python24\Lib\site-packages\win32\lib\win32traceutil.py
This should startup and say it's waiting. Now when you do a page reload, the window with your win32traceutil.py running will scroll by some stuff showing you that it's doing trace output of the request processing cycle including your HTTP response.
This is very important. I could not write this guide without the tracing. You have no idea what's going on without it.
Time to move on.
ISAPI + WSGI
Now, on to WSGI
Install ISAPI WSGI
. I recommend using the latest from the SVN trunk. Download Subversion
first if you don't have it. Then run:
easy_install http://isapi-wsgi.googlecode.com/svn/trunk
Install the most recent version of wsgiref:
From here you can follow the docs from the website: InstallationInstructions
The isapi_wsgi.py script will install a virtual directory called isapi-wsgi-test and the WSGI app will be called test so the URL is
"http://localhost/isapi-wsgi-test/"
You should get:
If you look at isapi_wsgi.py you'll see that it has the same if _name_ == '_main_': at the bottom that installs the app. This is the same as the non-WSGI version in advanced.py. However in the top half, the class ISAPISimpleHandler implements HttpExtensionProc() and allows you to pass in a WSGI app. isapi_wsgi.py is, in effect, library that you will re-use to call your Pylons app and also a demo of how to use it.
ISAPI + Pylons
Fix zipped eggs
There is one nagging bug right now affecting Pylons apps. When running under IIS, we use paste.deply.loadapp to create our Pylons app. For some reason, when Pylons is instantiated and tries to access the eggs in your site-packages dir, it cannot read the eggs that are in zip format. To work around this, I had to manually extract them to a folder with the same name as the zip file, remove the zip file and name the folder .egg like the old file. More knowledge of setup tools may offer another solution. I had to do this for myghty, routes, simplejson and WSGIutils.
I wrote a python script that will fix this for you. attachment:expand_eggs.py Run the following command (at your own risk of course) and it should redownload all the zipped eggs as folder eggs.
python expand_eggs.py myghty routes simplejson WSGIutils
Make a setuptools link to your Python app
IIS needs to be able to find your Pylons app. So the other thing you need to do is create an egg link in site-packages pointing back to your app dir. If your Pylons app is in c:\code\pylonstest then run this command in that folder:
Create your installer / proxy module
Pylons produces WSGI apps. Here's where the "WSGI from the ground up" differences from Django and Tubogears pays off! So when using Pylons, we instantiate our Pylons app (in my example the variable is called pylonsapp and then call isapi_wsgi.ISAPISimpleHandler(app = pylonsapp)
I've provided an example file below. If your Pylons app is in c:\code\pylonstest then create a file in that folder called pylonstest_isapi_extension.py and place this in it:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28 | from paste.deploy import loadapp
appdir = 'c:/code/pylonstest/'
pylonsapp = loadapp('config:' + appdir + 'development.ini', relative_to=appdir)
traceon = 1
import isapi_wsgi
# The entry points for the ISAPI extension.
def __ExtensionFactory__():
return isapi_wsgi.ISAPISimpleHandler(app = pylonsapp)
if __name__=='__main__':
# If run from the command-line, install ourselves.
from isapi.install import *
params = ISAPIParameters()
# Setup the virtual directories - this is a list of directories our
# extension uses - in this case only 1.
# Each extension has a "script map" - this is the mapping of ISAPI
# extensions.
sm = \[
ScriptMapParams(Extension="*", Flags=0)
]
vd = VirtualDirParameters(Name="pylonstest",
Description = "Pylons app running via ISAPI-WSGI",
Path = appdir.replace('/','\\'),
ScriptMaps = sm,
ScriptMapUpdate = "replace"
)
params.VirtualDirs = \[vd]
HandleCommandLine(params)
|
I have set the path of the virtual directory to the path of the Pylons app, however, remember the fake-out mentioned above. All requests are proxied to the DLL, not to the files in this folder. This is a convenience measure for setting permissions.
This is the quickest way to get it going. It may be useful to separate the install routine from the module that runs the website into different files.
Install your ISAPI app
python pylonstest_isapi_extension.py install
The URL for this application will be "http://localhost/pylonstest/app/"
It should return the standard public/index.html start page.
If you were to make a controller called hello with
then the URL for that controller would be "http://localhost/pylonstest/app/hello"
With Pylons, this is obviously sub-optimal. You probably don't want your WSGI app name in the URL as well as your IIS virtual directory name. Probably one but maybe both should be removable. This was done to allow multiple WSGI apps to be loaded in the virtual dir. At this point, you must modify isapi_wsgi.ISAPISimpleHandler to remove the "app" from the URL. To create your app at the root of your web server, you'll have to either modify the !VirtualDirParameters to install at the root or manually create the application link in the root to the dll you generate.
Web Server Integrated Auth
Use the IIS MMC and go to the properties for your virtual directory, turn on integrated authentication and turn off anonymous access.
Then hit the page via IE, you'll be 'automatically' logged in and request.environ, your environment variables, will contain the authorization type and later the user id that authenticated is specified. You can then use this in your controller code.
'HTTP_AUTHORIZATION': 'Negotiate TlRMTVNTUAA.......
'REMOTE_USER': 'PTEST\\Administrator'
Hitting it via firefox and you're offered a login prompt and when you log in, request.environ will contain
'HTTP_AUTHORIZATION': 'NTLM TlRMTVNTUAADAA.......
'REMOTE_USER': 'PTEST\\Administrator'
Caveats
IIS must be restarted to see certain changes. Starting and stopping the website within MMC is not enough. I recommend the command line iisreset to stop and start IIS. This will clear the environment and cleanly reload any changes to your code. You modules are reloaded on every request as in cgi but I've noticed some things get out of whack in a development scenario.
Watch the processes on your system for python.exe processes that are 'unclaimed' sometimes IIS leaves a python.exe running and this can mess up your Python Trace Collector and it won't show any activity.
You must run you app (extension dll) from a local drive.
If you get an error like The system cannot find the path specified. IIS is having a problem finding your application. In this case, you probably won't get anything from the Python Trace Collector. I've had this error because i was trying to run an app from a mapped drive. IIS wouldn't load the extension dll from that location.