Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Newer
Older
100644 113 lines (90 sloc) 6.853 kB
4c9310e @ayaz Added guide to deploy web.py on IIS7 and IIS6 using PyISAPIe.
ayaz authored
1 layout: default
2 title: Deploying web.py on IIS7 vai PyISAPIe
3
4 # Deploying web.py on IIS7 via PyISAPIe
5
6 This guide is an account of the various steps, including snippets of relevant code that tweaked and added, in order to get a `web.py` script to work on IIS7 using `PyISAPIe`. Please note that you must have Python as well as the [PyWin32 extensions][0] installed on Windows. _This guide was tested on two different 64-bit versions of Windows server with 32-bit versions of Python 2.6.6 installed and on IIS7 and IIS6_.
7
8 First and foremost, I had to install the `web.py` module on the system. Having had trouble before with IIS with `web.py` installed through `easy_install`, I decided to be safe and installed it from source. Getting `web.py` to work with PyISAPIe required a small hack. In the file `Lib\site-packages\web\wsgi.py` lies the following function:
9
10 def _is_dev_mode():
11 # quick hack to check if the program is running in dev mode.
12 if os.environ.has_key('SERVER_SOFTWARE') \
13 or os.environ.has_key('PHP_FCGI_CHILDREN') \
14 or 'fcgi' in sys.argv or 'fastcgi' in sys.argv \
15 or 'mod_wsgi' in sys.argv:
16 return False
17 return True
18
19 In its pristine state, when `web.py` is imported from a source file through PyISAPIe, an exception is thrown. The exception, while I don't have the exact message, is about it complaining about `sys.argv` not having an attribute `argv`, which reads fishy. Since the function `_is_dev_mode()` only checks whether `web.py` is being run in development mode, I thought I didn't care about it since I wanted everything to run in production mode. I edited the function such that its body would be bypassed, while it returned a `False` boolean value. It looked like this:
20
21 def _is_dev_mode():
22 return False
23 # quick hack to check if the program is running in dev mode.
24 if os.environ.has_key('SERVER_SOFTWARE') \
25 or os.environ.has_key('PHP_FCGI_CHILDREN') \
26 or 'fcgi' in sys.argv or 'fastcgi' in sys.argv \
27 or 'mod_wsgi' in sys.argv:
28 return False
29 return True
30
31 This innocuous little addition did away with the exception.
32
33 Next up, I used default Hello World-esque example of `web.py`. I called it `code.py` (I placed it inside the folder `C:\websites\myproject`). It looked like this:
34
35 import web
36 urls = (
37 '/.*', 'hello',
38 )
39 class hello:
40 def GET(self):
41 return "Hello, world."
42 application = web.application(urls, globals()).wsgifunc()
43
44 It was pretty simple. You have to pay particular attention on the call to `web.application`. I called the `wsgifunc()` to return a WSGI-compatible function to boot the application.
45
46 I set up a website under IIS using the IIS Management Console. Since I was working on a 64-bit server edition of Windows and had chosen to use 32-bit version of Python and all modules, I made sure to enable **32-bit support** for the application pool being used for the website. This was important.
47
48 I decided to keep the PyISAPIe folder inside the folder where `code.py` rested. This PyISAPIe folder contained the `PyISAPIe.dll` file, and the `Http` folder. Inside the `Http` folder, I placed the most important file of all: the `Isapi.py`. That file could be thought of as the starting point for each request that is made, what glues the Request to the proper Handler and code. I worked with the `Examples\WSGI\Isapi.py` available as part of PyISAPIe. I tweaked the file to look like this:
49
50
51 from Http.WSGI import RunWSGI
52 from Http import Env
53 #from md5 import md5
54 from hashlib import md5
55 import imp
56 import os
57 import sys
58 sys.path.append(r"C:\websites\myproject")
59 from code import application
60 ScriptHandlers = {
61 "/api/": application,
62 }
63 def RunScript(Path):
64 global ScriptHandlers
65 try:
66 # attempt to call an already-loaded request function.
67 return ScriptHandlers[Path]()
68 except KeyError:
69 # uses the script path's md5 hash to ensure a unique
70 # name - not the best way to do it, but it keeps
71 # undesired characters out of the name that will
72 # mess up the loading.
73 Name = '__'+md5(Path).hexdigest().upper()
74 ScriptHandlers[Path] = \
75 imp.load_source(Name, Env.SCRIPT_TRANSLATED).Request
76 return ScriptHandlers[Path]()
77 # URL prefixes to map to the roots of each application.
78 Apps = {
79 "/api/" : lambda P: RunWSGI(application),
80 }
81 # The main request handler.
82 def Request():
83 # Might be better to do some caching here?
84 Name = Env.SCRIPT_NAME
85 # Apps might be better off as a tuple-of-tuples,
86 # but for the sake of representation I leave it
87 # as a dict.
88 for App, Handler in Apps.items():
89 if Name.startswith(App):
90 return Handler(Name)
91 # Cause 500 error: there should be a 404 handler, eh?
92 raise Exception, "Handler not found."
93
94 The important bits to note in the above code are the following:
95
96 * I import `application` from my `code` module. I set the PATH to include the directory in which the file `code.py` is so that the `import` statement does not complain. (I've to admit that the idea of import `application` and feeding it into `RunWSGI` came to while I was in the loo.)
97 * I defined a script handler which matches the URL prefix I want to associate with my `web.py` script. (_In hindsight, this isn't necessary, as the `RunScript()` is not being used in this example_).
98 * In the `Apps` dictionary, I again route the URL prefix to the `lambda` function which actually calls the `RunWSGI` function and feeds it `application`.
99 * I also imported the `md5` function from the `hashlib` module instead of the `md5` module as originally defined in the file. This was because Python complained about `md5` module being deprecated and suggested instead of use `hashlib`.
100
101 I then defined a wild-card (Script map) extension in IIS for the website, mapping all requests to the `PyISAPIe.dll` file in _my project folder_. Which `PyISAPIe.dll` file is used is important. By default, it will look for the `Http` folder in the same directory where the DLL is. I restarted IIS (and possibly even Windows, just to be sure).
102
103 And that's pretty much it.
104
105 There's a caveat though. If you have specific URLs in your `web.py` script, you will have to modify each of those URLs to add the `/api/` prefix to them (or whatever URL prefix you set in the `Isapi.py`). Without that, `web.py` will not match any URLs in the file.
106
107 Good luck!
108
109 _PS: If you want to avoid using PyISAPIe, there is a simpler way of deploying web.py on IIS. It is documented crudely over [here][1]._
110
111 [0] http://sourceforge.net/projects/pywin32/
112 [1] http://forums.iis.net/t/1122937.aspx
Something went wrong with that request. Please try again.