Setting Up Python on IIS7

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://geographika.azurewebsites.net/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.



23 views shared on this article. Join in...

  1. Cyrus says:

    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?

  2. geographika says:

    @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.

  3. Cyrus says:

    @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?

  4. Cyrus says:

    Nevermind, I got it working. Needed the most up to date Pyisapie files. Then had to mod Pyisapie.py for django 1.0. Thanks!

  5. Jonathan says:

    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

    • geographika says:

      @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).

  6. joss says:

    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]()

  7. Josh says:

    @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?

  8. chris says:

    @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’

  9. 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.

  10. Vaibhav says:

    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.

  11. Detzler says:

    HTTP Error 404.17 – Not Found

  12. Ritesh Chandora says:

    Tried everything mentioned…. but no success..
    Pythong 2.6
    IIS 7
    django 1.5.5

    Am I missing something???

  13. I loved as much as you will receive carried out right here.
    The sketch is attractive, your authored subject matter stylish.

    nonetheless, you command get bought an edginess over that you wish be delivering the following.
    unwell unquestionably come more formerly again since exactly
    the same nearly a lot often inside case you shield
    this increase.



Pings to this post

  1. […] This knowledge may save a couple of hours of frustration if you are running Python scripts on IIS through PyISAPIe. […]

  2. […] 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 […]

  3. […] 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 […]

  4. […] 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 […]


Leave a Reply

Your email address will not be published. Required fields are marked *

Comment

You may use these tags : <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>