I had already configured Python to run through the Apache webserver on my development server, but after a few issues on the production server (Apache freezing / crashing) I wanted to test running Python scripts with IIS7. The principle aim was to run TileCache through IIS rather than requiring Apache.
Why Not CGI?
While IIS 7 has Fast-CGI installed (see this IIS forum), even better performance can be achieved using ISAPI. This answer from ServerFault has a good summary on why ISAPI should be preferred over than CGI. Not only performance should be considered – maintenance should also be taken into account. If a web master or hosting service is familiar with IIS then they are also likely to be familiar with ISAPI.
From the PyISAPIe site:
The reason ISAPI applications have the capability of being better than CGI or FastCGI applications is mostly due to its tight integration with the web server environment. Instead of initializing an entire program from scratch (in this case, the Python interpreter) every time a request is made for a page, an ISAPI extension only has to provide a function that is called upon every request. For interpreting Python scripts on a per-request basis, this means that the interpreter can be initialized once and used many times, creating a very noticeable performance gain.
Some Definitions…
WSGI -The Web Server Gateway Interface defines a simple and universal interface between web servers and web applications or frameworks for the Python programming language.
My interpretation – WSGI glues together IIS and Python scripts, or any web server and Python application combination, in a standard way.
ISAPI – The Internet Server Application Programming Interface (ISAPI) is an N-tier API of Internet Information Services (IIS), Microsoft’s collection of Windows-based web server services. The most prominent application of IIS and ISAPI is Microsoft’s web server.
My uninformed interpretation – ISAPI allows DLLs written in a variety of languages to be loaded into IIS to handle web requests. ASP.NET would therefore be an ISAPI extension, set to handle requests which end in .aspx
The Python ISAPI Extension
I initially tried isapi-wsgi, but I didn’t have much luck setting it up (I gave up fairly quickly though). It also depended on “Mark Hammond’s Python win32 isapi extension” – and I was trying to keep the amount of packages installed and added to a minimum. I therefore chose to use PyISAPIe which has no dependencies, and only requires a DLL and a folder containing a few .py files.
Documentation and installation instructions are sparse, and assume good knowledge of Python, IIS, and web requests in general in which I was lacking. In retrospect they make perfect sense though! The following links seem to be about the sum knowledge of PyISAPIe on the web – the homepage, the install guide, getting started, the news group, and the API. It struck me a few times while trying to set up Python and IIS that the numbers of people that doing this could be incredibly low. The last Microsoft article I found relates to IIS 5..
Installation on IIS 7
1. First make sure IIS is actually installed (Server Manager >> Roles >> Add Web Server IIS). Ensure the ISAPI Extensions and ISAPI filters are both checked.
2. Download the latest PyISAPIe extension. Make sure you get the correct version for your version of Python. In this example I am using Python 2.5.4 and PyISAPIe 1.1.0-rc4-Py2.5
3. Where to put the files took a while to resolve..in the end it becomes apparent they can be placed anywhere! I got confused by my understanding of packages and by thinking I could test the Python scripts outside of a browser / IIS.
In the end I created a new folder in C:\Python25 named PyISAPIe. I then copied into this the pyISAPIe.dll and HTTP folder.
4. Next you’ll need to set up the handler in IIS. I wanted all files that ended with the .py extension to be handled by the PyISAPIe DLL, so I did the following:
- under sites add a new application (I used the name /apps)
- select the site, and then select “Handler Mappings”
- right-click and select “Add Script Map”
- set the “Request path” to *.py
- set the “Executable” to “C:\Python25\PyISAPIe\PyISAPIe.dll
- give the handler a relevant name such as “PyISAPIe”
- in the Request Restrictions section, I set my handler to run only when a request contains a .py file that exists on the server. I set it handle all verbs (GET and POST), and gave it Script access.
- select the “View Ordered List” in the Actions panel to see in what order handlers will be applied to a request. Make sure your .py handler has a higher priority than the default StaticFile handler, or it won’t get a chance to handle anything. The StaticFile handler may also appear to be handling requests if your custom handler fails as by default PyISAPIe moves to the next handler on an error.
Important! If you are using a64-bit machine, you will need to create a new application pool, and set “Enable 32-bit Applications” to true. This is false by default. I found this blog post fairly early on describing the issue but, as if by magic, the true setting for my application pool had at some point been set back to false, leading to hours of being confronted by the near useless message:
HTTP Error 404.4 - Not Found The resource you are looking for does not have a handler associated with it.
Testing
The Getting Started Guide should be followed, but I have a few gotchas, learnt the hard way. First save a Python script into your web application folder with a name such as hello.py with the test code from the guide:
from Http import *
def Request():
Header("Content-type: text/html")
Write("Hello, World!")
Note this script cannot be run from a Python editor, you will get warnings such as:
ImportError: No module named Http
Even if the HTTP module is in the PYTHONPATH, or site-packages folder you will still receive an error such as that below, as the DLL needs to be loaded through ISAPI.
ImportError: No module named PyISAPIe
Pointing to http://localhost/apps/hello.py should bring up the output of your Python script. Things get more confusing when you want to debug a more complicated script, as it appears nothing you change in your script has any effect. This is because once the script is run it stays loaded in memory, so no alterations are taken into account. To disable this (for testing only – change it back when everything is working correctly) modify the C:\Python25\PyISAPIe\Http\Isapi.py file.
def Request(): Script = Env.SCRIPT_NAME Key = Name = '__'+md5(Script).hexdigest().upper() Handler = Handlers.get(Key, None) # the following line will ensure the script is reloaded on each request Handler = None if not Handler: try: Handlers[Key] = imp.load_source(Key, Env.SCRIPT_TRANSLATED).Request except Exception, Val: # trigger a passthrough to the next ISAPI handler - # ONLY WORKS FOR WILDCARD APPLICATION MAPPINGS #return True # or just fail, preferable for an application map # show errors in the browser raise ImportError, "[Loading '%s'] %s" % (Env.SCRIPT_TRANSLATED, str(Val)) return Handlers[Key]()
Note the changes you make to the Isapi.py file itself requires IIS to be restarted to take effect. The above modifications only cause the scripts in your www/apps directory to be refreshed on each request.
If you always return true on any exception in the Isapi.py file (rather than throwing the error) then you may be met with the following message:
Possible recursion detected! You probably did a passthrough with PyISAPIe configured as an application map instead of a wildcard map.
I *think* this is because the first handler failed to return a proper response, and so it goes to the next handler for the application. Just failing should provide you with a better error message from Python.
If I’ve missed out any key steps in this summary feel free to add comments below and I’ll integrate them into the post. Last but not least many thanks to the developer of PyISAPIe – Phillip Sitbon.


Thanks for the post!!! I was having the same trouble finding anything about IIS 7 and Python. I am really just wadding into the IIS waters. I can find everything and follow your whole post except for one thing, the DLL file. I downloaded the PyISAPIe package and I have files that end in .h,def, and sln but no dll. Do I have to make this file?
@Cyrus
Hi Cyrus,
On the downloads page there are a few options:
PyISAPIe-1.1.0-rc4-Source.zip
PyISAPIe-1.1.0-rc4-Py2.6.zip
PyISAPIe-1.1.0-rc4-Py2.5.zip
The first is the sourcecode for the DLL (C++ and Visual Studio solution files). The second two point to the DLLs – select Py2.6 or Py2.5 depending on your installed Python version. These downloads contain the compiled DLLs.
@geographika
Thanks for that. I seem to have a common problem with many django-to-ISS wranglers and that is how to properly connect the two together. I was wondering if you had solved the riddle completely. I can run the hello.py script and I have also been able to copy the ISAPI.py file into my root directory and given it the name rundjango.py. Then when I hit http://mysite.com/rundjango.py I get the “Welcome to Django” screen. But how do I get the PyISAPI/ISAPI connective stuff to know when I hit http://mysite.com/myapp/ that this is python/django?
Nevermind, I got it working. Needed the most up to date Pyisapie files. Then had to mod Pyisapie.py for django 1.0. Thanks!
Great Stuff. I was playing with this about two months ago and came to an abrupt halt – python was proving too much of a learning curve. Just to confirm – have you defintely got tilecache working with this set up – and if do – how does it compare with mod_apache?
All the best,
Jonathan
@Jonathan
TileCache has been running for a couple of months now without any problems.
Both http://sei.maps.ie/wind and http://sei.maps.ie/geothermal are using caches.
I had both Apache and IIS running the same .map files on the same Windows 2008 64-bit server.
I used JMeter to test the performance between the two, but after a few consecutive requests Apache froze. I wasn’t able to find out why, but IIS and ISAPI were able to handle anything I threw at it.
I’d strongly recommend using IIS if using Windows. You can also take advantage of the 64-bit MapServer (although a 64-bit Python version is not yet ready as far as I know).
[...] This knowledge may save a couple of hours of frustration if you are running Python scripts on IIS through PyISAPIe. [...]
[...] then set up the PyISAPIe.DLL on IIS7 (using the same process as I detailed here), replacing the 32-bit DLL with my newly compiled 64-bit, and using the DefaultAppPool which should [...]
A quick hack I found very useful was to modify Http/Isapi.py such that it reloads the file if you have modified it:
from PyISAPIe import Env
from hashlib import md5
import imp
from os import stat
Handlers = {}
TimeMap = {}
def Request():
Script = Env.SCRIPT_NAME
Key = Name = ‘__’+md5(Script).hexdigest().upper()
Handler = Handlers.get(Key, None)
mtime = stat(Env.SCRIPT_TRANSLATED[4:])[8]
if not Handler or TimeMap[Key] < mtime:
Handlers[Key] = imp.load_source(Key, Env.SCRIPT_TRANSLATED).Request
TimeMap[Key] = mtime
return Handlers[Key]()
@Cyrus
Cyrus, can you describe what you did here? How does IIS know to run the rundjango.py file of you do not specify the filename on the command line?
@geographika
first of all: I like your blog and your simple way to explain things, I guess you did already safe some people some time!
you said, that you were able to get TileCache running on IIS7 with PyISAPIe. Could you give me a hint, how the original tilecache.py has to be changed?
——————————
from TileCache import Service, cgiHandler, cfgfiles
if __name__ == ‘__main__’:
svc = Service.load(*cfgfiles)
cgiHandler(svc)
——————————
I guess, since it isn’t a CGI script, that has to be adopted… currently I get:
Request handler failed
Traceback (most recent call last):
File “C:\Python25\PyISAPIe\Http\Isapi.py”, line 49, in Request
raise ImportError, “[Loading '%s'] %s” % (Env.SCRIPT_TRANSLATED, str(Val))
ImportError: [Loading '\\?\C:\inetpub\wwwroot\tilecache\tilecache.py'] ‘module’ object has no attribute ‘argv’
Hi Chris – I uploaded my TileCache script to http://bitbucket.org/geographika/mapserver-scripts/changeset/40e84703a58d
Let me know how you get on. I’ll try and turn my notes into a post at some point. I think there were a couple of changes I have to make in the TileCache scripts themselves due to Python errors.
[...] previously written about using PyISAPie to run Python under IIS – this allows Python scripts to run faster than using CGI. Rather than starting up the Python [...]
Very good example. Found another good site that shows exactly how to setup CGI II7 and C++, along with your example I have finally setup a nice web host to host my own site! DynDNS offers nice services to do this. There is something generic about have others like GoDaddy host your web site, especially if you are a 7 language programmer! I am not a web master, but I do have 20 years of C++ so why not host my own site! Others can do so as well, but the hardest part of it is finding clear concise examples, and Microsoft does not offer anything close to this. You are wasting your time if you think MS can give you any good help for free! This is the beauty of the web, people like us that can offer better help than major corporations! For free that is.
[...] and configure PyISAPIe. You can follow the instructions described on this post or in the README file included with PyISAPIe . If you want to run a 64 bit version, have a look at [...]
Hi,
Thanks for this article, it seems to be very useful.
I’m getting below error
HTTP Error 404.17 – Not Found
The requested content appears to be script and will not be served by the static file handler.
It would be really good if you could provide any pointers as to what is missing which causes this issue. Thanks in advance.
HTTP Error 404.17 – Not Found
[...] Setting up Python on IIS7 – http://geographika.co.uk/setting-up-python-on-iis7 [...]