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 sys
60
61 import cherrypy
62 from cherrypy._cpcompat import BytesIO, copyitems, ntob
63 from cherrypy._cperror import format_exc, bare_error
64 from cherrypy.lib import httputil
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 for function in options['cherrypy.setup'].split():
78 atoms = function.split('::', 1)
79 if len(atoms) == 1:
80 mod = __import__(atoms[0], globals(), locals())
81 else:
82 modname, fname = atoms
83 mod = __import__(modname, globals(), locals(), [fname])
84 func = getattr(mod, fname)
85 func()
86
87 cherrypy.config.update({'log.screen': False,
88 "tools.ignore_headers.on": True,
89 "tools.ignore_headers.headers": ['Range'],
90 })
91
92 engine = cherrypy.engine
93 if hasattr(engine, "signal_handler"):
94 engine.signal_handler.unsubscribe()
95 if hasattr(engine, "console_control_handler"):
96 engine.console_control_handler.unsubscribe()
97 engine.autoreload.unsubscribe()
98 cherrypy.server.unsubscribe()
99
100 def _log(msg, level):
101 newlevel = apache.APLOG_ERR
102 if logging.DEBUG >= level:
103 newlevel = apache.APLOG_DEBUG
104 elif logging.INFO >= level:
105 newlevel = apache.APLOG_INFO
106 elif logging.WARNING >= level:
107 newlevel = apache.APLOG_WARNING
108
109
110
111 apache.log_error(msg, newlevel, req.server)
112 engine.subscribe('log', _log)
113
114 engine.start()
115
116 def cherrypy_cleanup(data):
117 engine.exit()
118 try:
119
120 apache.register_cleanup(cherrypy_cleanup)
121 except AttributeError:
122 req.server.register_cleanup(req, cherrypy_cleanup)
123
124
126 expose = ('read', 'readline', 'readlines')
130
131
132 recursive = False
133
134 _isSetUp = False
136 from mod_python import apache
137 try:
138 global _isSetUp
139 if not _isSetUp:
140 setup(req)
141 _isSetUp = True
142
143
144 local = req.connection.local_addr
145 local = httputil.Host(local[0], local[1], req.connection.local_host or "")
146 remote = req.connection.remote_addr
147 remote = httputil.Host(remote[0], remote[1], req.connection.remote_host or "")
148
149 scheme = req.parsed_uri[0] or 'http'
150 req.get_basic_auth_pw()
151
152 try:
153
154 q = apache.mpm_query
155 threaded = q(apache.AP_MPMQ_IS_THREADED)
156 forked = q(apache.AP_MPMQ_IS_FORKED)
157 except AttributeError:
158 bad_value = ("You must provide a PythonOption '%s', "
159 "either 'on' or 'off', when running a version "
160 "of mod_python < 3.1")
161
162 threaded = options.get('multithread', '').lower()
163 if threaded == 'on':
164 threaded = True
165 elif threaded == 'off':
166 threaded = False
167 else:
168 raise ValueError(bad_value % "multithread")
169
170 forked = options.get('multiprocess', '').lower()
171 if forked == 'on':
172 forked = True
173 elif forked == 'off':
174 forked = False
175 else:
176 raise ValueError(bad_value % "multiprocess")
177
178 sn = cherrypy.tree.script_name(req.uri or "/")
179 if sn is None:
180 send_response(req, '404 Not Found', [], '')
181 else:
182 app = cherrypy.tree.apps[sn]
183 method = req.method
184 path = req.uri
185 qs = req.args or ""
186 reqproto = req.protocol
187 headers = copyitems(req.headers_in)
188 rfile = _ReadOnlyRequest(req)
189 prev = None
190
191 try:
192 redirections = []
193 while True:
194 request, response = app.get_serving(local, remote, scheme,
195 "HTTP/1.1")
196 request.login = req.user
197 request.multithread = bool(threaded)
198 request.multiprocess = bool(forked)
199 request.app = app
200 request.prev = prev
201
202
203 try:
204 request.run(method, path, qs, reqproto, headers, rfile)
205 break
206 except cherrypy.InternalRedirect:
207 ir = sys.exc_info()[1]
208 app.release_serving()
209 prev = request
210
211 if not recursive:
212 if ir.path in redirections:
213 raise RuntimeError("InternalRedirector visited the "
214 "same URL twice: %r" % ir.path)
215 else:
216
217 if qs:
218 qs = "?" + qs
219 redirections.append(sn + path + qs)
220
221
222 method = "GET"
223 path = ir.path
224 qs = ir.query_string
225 rfile = BytesIO()
226
227 send_response(req, response.output_status, response.header_list,
228 response.body, response.stream)
229 finally:
230 app.release_serving()
231 except:
232 tb = format_exc()
233 cherrypy.log(tb, 'MOD_PYTHON', severity=logging.ERROR)
234 s, h, b = bare_error()
235 send_response(req, s, h, b)
236 return apache.OK
237
238
261
262
263
264
265
266
267 import os
268 import re
269 try:
270 import subprocess
272 p = subprocess.Popen(fullcmd, shell=True,
273 stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
274 close_fds=True)
275 return p.stdout
276 except ImportError:
278 pipein, pipeout = os.popen4(fullcmd)
279 return pipeout
280
281
283 fullcmd = "%s %s" % (cmd, args)
284 pipeout = popen(fullcmd)
285 try:
286 firstline = pipeout.readline()
287 if (re.search(ntob("(not recognized|No such file|not found)"), firstline,
288 re.IGNORECASE)):
289 raise IOError('%s must be on your system path.' % cmd)
290 output = firstline + pipeout.read()
291 finally:
292 pipeout.close()
293 return output
294
295
297
298 template = """
299 # Apache2 server configuration file for running CherryPy with mod_python.
300
301 DocumentRoot "/"
302 Listen %(port)s
303 LoadModule python_module modules/mod_python.so
304
305 <Location %(loc)s>
306 SetHandler python-program
307 PythonHandler %(handler)s
308 PythonDebug On
309 %(opts)s
310 </Location>
311 """
312
313 - def __init__(self, loc="/", port=80, opts=None, apache_path="apache",
314 handler="cherrypy._cpmodpy::handler"):
315 self.loc = loc
316 self.port = port
317 self.opts = opts
318 self.apache_path = apache_path
319 self.handler = handler
320
322 opts = "".join([" PythonOption %s %s\n" % (k, v)
323 for k, v in self.opts])
324 conf_data = self.template % {"port": self.port,
325 "loc": self.loc,
326 "opts": opts,
327 "handler": self.handler,
328 }
329
330 mpconf = os.path.join(os.path.dirname(__file__), "cpmodpy.conf")
331 f = open(mpconf, 'wb')
332 try:
333 f.write(conf_data)
334 finally:
335 f.close()
336
337 response = read_process(self.apache_path, "-k start -f %s" % mpconf)
338 self.ready = True
339 return response
340
342 os.popen("apache -k stop")
343 self.ready = False
344