Contact: fumanchu@aminus.org

Log in as guest/misc to create tickets

root/modpython_gateway.py

Revision 41 (checked in by fumanchu, 7 years ago)

Backward-incompatible changes from modpython ML discussion:

  1. The "import" PythonOption? is removed in favor of PythonFixupHandler? declarations.
  2. The "application" PythonOption? has been renamed "wsgi.application", to help with PythonOption? namespace issues.
  3. The "profile" function has been removed.
  • Property svn:eol-style set to native
Line 
1 """
2 WSGI wrapper for mod_python. Requires Python 2.2 or greater.
3
4
5 Example httpd.conf section for a CherryPy app called "mcontrol":
6
7 <Location /mcontrol>
8     SetHandler python-program
9     PythonFixupHandler mcontrol.cherry::startup
10     PythonHandler modpython_gateway::handler
11     PythonOption wsgi.application cherrypy._cpwsgi::wsgiApp
12 </Location>
13
14 Some WSGI implementations assume that the SCRIPT_NAME environ variable will
15 always be equal to "the root URL of the app"; Apache probably won't act as
16 you expect in that case. You can add another PythonOption directive to tell
17 modpython_gateway to force that behavior:
18
19     PythonOption SCRIPT_NAME /mcontrol
20
21 """
22
23 import traceback
24
25 from mod_python import apache
26
27
28 class InputWrapper(object):
29    
30     def __init__(self, req):
31         self.req = req
32    
33     def close(self):
34         pass
35    
36     def read(self, size=-1):
37         return self.req.read(size)
38    
39     def readline(self, size=-1):
40         return self.req.readline(size)
41    
42     def readlines(self, hint=-1):
43         return self.req.readlines(hint)
44    
45     def __iter__(self):
46         line = self.readline()
47         while line:
48             yield line
49             # Notice this won't prefetch the next line; it only
50             # gets called if the generator is resumed.
51             line = self.readline()
52
53
54 class ErrorWrapper(object):
55    
56     def __init__(self, req):
57         self.req = req
58    
59     def flush(self):
60         pass
61    
62     def write(self, msg):
63         self.req.log_error(msg)
64    
65     def writelines(self, seq):
66         self.write(''.join(seq))
67
68
69 bad_value = ("You must provide a PythonOption '%s', either 'on' or 'off', "
70              "when running a version of mod_python < 3.1")
71
72
73 class Handler:
74    
75     def __init__(self, req):
76         self.started = False
77        
78         options = req.get_options()
79        
80         # Threading and forking
81         try:
82             q = apache.mpm_query
83             threaded = q(apache.AP_MPMQ_IS_THREADED)
84             forked = q(apache.AP_MPMQ_IS_FORKED)
85         except AttributeError:
86             threaded = options.get('multithread', '').lower()
87             if threaded == 'on':
88                 threaded = True
89             elif threaded == 'off':
90                 threaded = False
91             else:
92                 raise ValueError(bad_value % "multithread")
93            
94             forked = options.get('multiprocess', '').lower()
95             if forked == 'on':
96                 forked = True
97             elif forked == 'off':
98                 forked = False
99             else:
100                 raise ValueError(bad_value % "multiprocess")
101        
102         env = self.environ = dict(apache.build_cgi_env(req))
103        
104         if 'SCRIPT_NAME' in options:
105             # Override SCRIPT_NAME and PATH_INFO if requested.
106             env['SCRIPT_NAME'] = options['SCRIPT_NAME']
107             env['PATH_INFO'] = req.uri[len(options['SCRIPT_NAME']):]
108        
109         env['wsgi.input'] = InputWrapper(req)
110         env['wsgi.errors'] = ErrorWrapper(req)
111         env['wsgi.version'] = (1,0)
112         env['wsgi.run_once'] = False
113         if env.get("HTTPS") in ('yes', 'on', '1'):
114             env['wsgi.url_scheme'] = 'https'
115         else:
116             env['wsgi.url_scheme'] = 'http'
117         env['wsgi.multithread']  = threaded
118         env['wsgi.multiprocess'] = forked
119        
120         self.request = req
121    
122     def run(self, application):
123         try:
124             result = application(self.environ, self.start_response)
125             for data in result:
126                 self.write(data)
127             if not self.started:
128                 self.request.set_content_length(0)
129             if hasattr(result, 'close'):
130                 result.close()
131         except:
132             traceback.print_exc(None, self.environ['wsgi.errors'])
133             if not self.started:
134                 self.request.status = 500
135                 self.request.content_type = 'text/plain'
136                 data = "A server error occurred. Please contact the administrator."
137                 self.request.set_content_length(len(data))
138                 self.request.write(data)
139    
140     def start_response(self, status, headers, exc_info=None):
141         if exc_info:
142             try:
143                 if self.started:
144                     raise exc_info[0], exc_info[1], exc_info[2]
145             finally:
146                 exc_info = None
147        
148         self.request.status = int(status[:3])
149        
150         for key, val in headers:
151             if key.lower() == 'content-length':
152                 self.request.set_content_length(int(val))
153             elif key.lower() == 'content-type':
154                 self.request.content_type = val
155             else:
156                 self.request.headers_out.add(key, val)
157        
158         return self.write
159    
160     def write(self, data):
161         if not self.started:
162             self.started = True
163         self.request.write(data)
164
165
166 def handler(req):
167     # Import the wsgi 'application' callable and pass it to Handler.run
168     modname, objname = req.get_options()['wsgi.application'].split('::', 1)
169     module = __import__(modname, globals(), locals(), [''])
170     app = getattr(module, objname)
171     Handler(req).run(app)
172    
173     # status was set in Handler; always return apache.OK
174     return apache.OK
175
Note: See TracBrowser for help on using the browser.