1 """Native adapter for serving CherryPy via mod_python
2
3 Basic usage:
4
5 ##########################################
6 # Application in a module called myapp.py
7 ##########################################
8
9 import cherrypy
10
11 class Root:
12 @cherrypy.expose
13 def index(self):
14 return 'Hi there, Ho there, Hey there'
15
16
17 # We will use this method from the mod_python configuration
18 # as the entry point to our application
19 def setup_server():
20 cherrypy.tree.mount(Root())
21 cherrypy.config.update({'environment': 'production',
22 'log.screen': False,
23 'show_tracebacks': False})
24
25 ##########################################
26 # mod_python settings for apache2
27 # This should reside in your httpd.conf
28 # or a file that will be loaded at
29 # apache startup
30 ##########################################
31
32 # Start
33 DocumentRoot "/"
34 Listen 8080
35 LoadModule python_module /usr/lib/apache2/modules/mod_python.so
36
37 <Location "/">
38 PythonPath "sys.path+['/path/to/my/application']"
39 SetHandler python-program
40 PythonHandler cherrypy._cpmodpy::handler
41 PythonOption cherrypy.setup myapp::setup_server
42 PythonDebug On
43 </Location>
44 # End
45
46 The actual path to your mod_python.so is dependent on your
47 environment. In this case we suppose a global mod_python
48 installation on a Linux distribution such as Ubuntu.
49
50 We do set the PythonPath configuration setting so that
51 your application can be found by from the user running
52 the apache2 instance. Of course if your application
53 resides in the global site-package this won't be needed.
54
55 Then restart apache2 and access http://127.0.0.1:8080
56 """
57
58 import logging
59 import StringIO
60
61 import cherrypy
62 from cherrypy._cperror import format_exc, bare_error
63 from cherrypy.lib import http
64
65
66
67
68
69
70
72 from mod_python import apache
73
74
75 options = req.get_options()
76 if 'cherrypy.setup' in options:
77 atoms = options['cherrypy.setup'].split('::', 1)
78 if len(atoms) == 1:
79 mod = __import__(atoms[0], globals(), locals())
80 else:
81 modname, fname = atoms
82 mod = __import__(modname, globals(), locals(), [fname])
83 func = getattr(mod, fname)
84 func()
85
86 cherrypy.config.update({'log.screen': False,
87 "tools.ignore_headers.on": True,
88 "tools.ignore_headers.headers": ['Range'],
89 })
90
91 engine = cherrypy.engine
92 if hasattr(engine, "signal_handler"):
93 engine.signal_handler.unsubscribe()
94 if hasattr(engine, "console_control_handler"):
95 engine.console_control_handler.unsubscribe()
96 engine.autoreload.unsubscribe()
97 cherrypy.server.unsubscribe()
98
99 def _log(msg, level):
100 newlevel = apache.APLOG_ERR
101 if logging.DEBUG >= level:
102 newlevel = apache.APLOG_DEBUG
103 elif logging.INFO >= level:
104 newlevel = apache.APLOG_INFO
105 elif logging.WARNING >= level:
106 newlevel = apache.APLOG_WARNING
107
108
109
110 apache.log_error(msg, newlevel, req.server)
111 engine.subscribe('log', _log)
112
113 engine.start()
114
115 def cherrypy_cleanup(data):
116 engine.exit()
117 try:
118
119 apache.register_cleanup(cherrypy_cleanup)
120 except AttributeError:
121 req.server.register_cleanup(req, cherrypy_cleanup)
122
123
125 expose = ('read', 'readline', 'readlines')
129
130
131 recursive = False
132
133 _isSetUp = False
135 from mod_python import apache
136 try:
137 global _isSetUp
138 if not _isSetUp:
139 setup(req)
140 _isSetUp = True
141
142
143 local = req.connection.local_addr
144 local = http.Host(local[0], local[1], req.connection.local_host or "")
145 remote = req.connection.remote_addr
146 remote = http.Host(remote[0], remote[1], req.connection.remote_host or "")
147
148 scheme = req.parsed_uri[0] or 'http'
149 req.get_basic_auth_pw()
150
151 try:
152
153 q = apache.mpm_query
154 threaded = q(apache.AP_MPMQ_IS_THREADED)
155 forked = q(apache.AP_MPMQ_IS_FORKED)
156 except AttributeError:
157 bad_value = ("You must provide a PythonOption '%s', "
158 "either 'on' or 'off', when running a version "
159 "of mod_python < 3.1")
160
161 threaded = options.get('multithread', '').lower()
162 if threaded == 'on':
163 threaded = True
164 elif threaded == 'off':
165 threaded = False
166 else:
167 raise ValueError(bad_value % "multithread")
168
169 forked = options.get('multiprocess', '').lower()
170 if forked == 'on':
171 forked = True
172 elif forked == 'off':
173 forked = False
174 else:
175 raise ValueError(bad_value % "multiprocess")
176
177 sn = cherrypy.tree.script_name(req.uri or "/")
178 if sn is None:
179 send_response(req, '404 Not Found', [], '')
180 else:
181 app = cherrypy.tree.apps[sn]
182 method = req.method
183 path = req.uri
184 qs = req.args or ""
185 reqproto = req.protocol
186 headers = req.headers_in.items()
187 rfile = _ReadOnlyRequest(req)
188 prev = None
189
190 try:
191 redirections = []
192 while True:
193 request, response = app.get_serving(local, remote, scheme,
194 "HTTP/1.1")
195 request.login = req.user
196 request.multithread = bool(threaded)
197 request.multiprocess = bool(forked)
198 request.app = app
199 request.prev = prev
200
201
202 try:
203 request.run(method, path, qs, reqproto, headers, rfile)
204 break
205 except cherrypy.InternalRedirect, ir:
206 app.release_serving()
207 prev = request
208
209 if not recursive:
210 if ir.path in redirections:
211 raise RuntimeError("InternalRedirector visited the "
212 "same URL twice: %r" % ir.path)
213 else:
214
215 if qs:
216 qs = "?" + qs
217 redirections.append(sn + path + qs)
218
219
220 method = "GET"
221 path = ir.path
222 qs = ir.query_string
223 rfile = StringIO.StringIO()
224
225 send_response(req, response.status, response.header_list,
226 response.body, response.stream)
227 finally:
228 app.release_serving()
229 except:
230 tb = format_exc()
231 cherrypy.log(tb, 'MOD_PYTHON', severity=logging.ERROR)
232 s, h, b = bare_error()
233 send_response(req, s, h, b)
234 return apache.OK
235
236
238
239 req.status = int(status[:3])
240
241
242 req.content_type = "text/plain"
243 for header, value in headers:
244 if header.lower() == 'content-type':
245 req.content_type = value
246 continue
247 req.headers_out.add(header, value)
248
249 if stream:
250
251 req.flush()
252
253
254 if isinstance(body, basestring):
255 req.write(body)
256 else:
257 for seg in body:
258 req.write(seg)
259
260
261
262
263
264
265 import os
266 import re
267
268
270 pipein, pipeout = os.popen4("%s %s" % (cmd, args))
271 try:
272 firstline = pipeout.readline()
273 if (re.search(r"(not recognized|No such file|not found)", firstline,
274 re.IGNORECASE)):
275 raise IOError('%s must be on your system path.' % cmd)
276 output = firstline + pipeout.read()
277 finally:
278 pipeout.close()
279 return output
280
281
283
284 template = """
285 # Apache2 server configuration file for running CherryPy with mod_python.
286
287 DocumentRoot "/"
288 Listen %(port)s
289 LoadModule python_module modules/mod_python.so
290
291 <Location %(loc)s>
292 SetHandler python-program
293 PythonHandler %(handler)s
294 PythonDebug On
295 %(opts)s
296 </Location>
297 """
298
299 - def __init__(self, loc="/", port=80, opts=None, apache_path="apache",
300 handler="cherrypy._cpmodpy::handler"):
301 self.loc = loc
302 self.port = port
303 self.opts = opts
304 self.apache_path = apache_path
305 self.handler = handler
306
308 opts = "".join([" PythonOption %s %s\n" % (k, v)
309 for k, v in self.opts])
310 conf_data = self.template % {"port": self.port,
311 "loc": self.loc,
312 "opts": opts,
313 "handler": self.handler,
314 }
315
316 mpconf = os.path.join(os.path.dirname(__file__), "cpmodpy.conf")
317 f = open(mpconf, 'wb')
318 try:
319 f.write(conf_data)
320 finally:
321 f.close()
322
323 response = read_process(self.apache_path, "-k start -f %s" % mpconf)
324 self.ready = True
325 return response
326
328 os.popen("apache -k stop")
329 self.ready = False
330