Skip to content
Newer
Older
100644 115 lines (92 sloc) 6.7 KB
0e97f81 @aaronsw Oops, needed three dashes instead of two.
aaronsw authored May 23, 2011
1 ---
4c9310e @ayaz Added guide to deploy web.py on IIS7 and IIS6 using PyISAPIe.
ayaz authored May 21, 2011
2 layout: default
3 title: Deploying web.py on IIS7 vai PyISAPIe
0e97f81 @aaronsw Oops, needed three dashes instead of two.
aaronsw authored May 23, 2011
4 ---
4c9310e @ayaz Added guide to deploy web.py on IIS7 and IIS6 using PyISAPIe.
ayaz authored May 21, 2011
5
6 # Deploying web.py on IIS7 via PyISAPIe
7
8 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_.
9
10 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:
11
12 def _is_dev_mode():
13 # quick hack to check if the program is running in dev mode.
14 if os.environ.has_key('SERVER_SOFTWARE') \
15 or os.environ.has_key('PHP_FCGI_CHILDREN') \
16 or 'fcgi' in sys.argv or 'fastcgi' in sys.argv \
17 or 'mod_wsgi' in sys.argv:
18 return False
19 return True
20
21 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:
22
23 def _is_dev_mode():
24 return False
25 # quick hack to check if the program is running in dev mode.
26 if os.environ.has_key('SERVER_SOFTWARE') \
27 or os.environ.has_key('PHP_FCGI_CHILDREN') \
28 or 'fcgi' in sys.argv or 'fastcgi' in sys.argv \
29 or 'mod_wsgi' in sys.argv:
30 return False
31 return True
32
33 This innocuous little addition did away with the exception.
34
35 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:
36
37 import web
38 urls = (
39 '/.*', 'hello',
40 )
41 class hello:
42 def GET(self):
43 return "Hello, world."
44 application = web.application(urls, globals()).wsgifunc()
45
46 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.
47
48 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.
49
50 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:
51
52
53 from Http.WSGI import RunWSGI
54 from Http import Env
55 #from md5 import md5
56 from hashlib import md5
57 import imp
58 import os
59 import sys
60 sys.path.append(r"C:\websites\myproject")
61 from code import application
62 ScriptHandlers = {
63 "/api/": application,
64 }
65 def RunScript(Path):
66 global ScriptHandlers
67 try:
68 # attempt to call an already-loaded request function.
69 return ScriptHandlers[Path]()
70 except KeyError:
71 # uses the script path's md5 hash to ensure a unique
72 # name - not the best way to do it, but it keeps
73 # undesired characters out of the name that will
74 # mess up the loading.
75 Name = '__'+md5(Path).hexdigest().upper()
76 ScriptHandlers[Path] = \
77 imp.load_source(Name, Env.SCRIPT_TRANSLATED).Request
78 return ScriptHandlers[Path]()
79 # URL prefixes to map to the roots of each application.
80 Apps = {
81 "/api/" : lambda P: RunWSGI(application),
82 }
83 # The main request handler.
84 def Request():
85 # Might be better to do some caching here?
86 Name = Env.SCRIPT_NAME
87 # Apps might be better off as a tuple-of-tuples,
88 # but for the sake of representation I leave it
89 # as a dict.
90 for App, Handler in Apps.items():
91 if Name.startswith(App):
92 return Handler(Name)
93 # Cause 500 error: there should be a 404 handler, eh?
94 raise Exception, "Handler not found."
95
96 The important bits to note in the above code are the following:
97
98 * 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.)
99 * 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_).
100 * In the `Apps` dictionary, I again route the URL prefix to the `lambda` function which actually calls the `RunWSGI` function and feeds it `application`.
101 * 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`.
102
103 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).
104
105 And that's pretty much it.
106
107 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.
108
109 Good luck!
110
111 _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]._
112
113 [0] http://sourceforge.net/projects/pywin32/
114 [1] http://forums.iis.net/t/1122937.aspx
Something went wrong with that request. Please try again.