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
71 from mod_python import apache
72
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')
127
131
132
133 recursive = False
134
135 _isSetUp = False
136
137
139 from mod_python import apache
140 try:
141 global _isSetUp
142 if not _isSetUp:
143 setup(req)
144 _isSetUp = True
145
146
147 local = req.connection.local_addr
148 local = httputil.Host(
149 local[0], local[1], req.connection.local_host or "")
150 remote = req.connection.remote_addr
151 remote = httputil.Host(
152 remote[0], remote[1], req.connection.remote_host or "")
153
154 scheme = req.parsed_uri[0] or 'http'
155 req.get_basic_auth_pw()
156
157 try:
158
159 q = apache.mpm_query
160 threaded = q(apache.AP_MPMQ_IS_THREADED)
161 forked = q(apache.AP_MPMQ_IS_FORKED)
162 except AttributeError:
163 bad_value = ("You must provide a PythonOption '%s', "
164 "either 'on' or 'off', when running a version "
165 "of mod_python < 3.1")
166
167 threaded = options.get('multithread', '').lower()
168 if threaded == 'on':
169 threaded = True
170 elif threaded == 'off':
171 threaded = False
172 else:
173 raise ValueError(bad_value % "multithread")
174
175 forked = options.get('multiprocess', '').lower()
176 if forked == 'on':
177 forked = True
178 elif forked == 'off':
179 forked = False
180 else:
181 raise ValueError(bad_value % "multiprocess")
182
183 sn = cherrypy.tree.script_name(req.uri or "/")
184 if sn is None:
185 send_response(req, '404 Not Found', [], '')
186 else:
187 app = cherrypy.tree.apps[sn]
188 method = req.method
189 path = req.uri
190 qs = req.args or ""
191 reqproto = req.protocol
192 headers = copyitems(req.headers_in)
193 rfile = _ReadOnlyRequest(req)
194 prev = None
195
196 try:
197 redirections = []
198 while True:
199 request, response = app.get_serving(local, remote, scheme,
200 "HTTP/1.1")
201 request.login = req.user
202 request.multithread = bool(threaded)
203 request.multiprocess = bool(forked)
204 request.app = app
205 request.prev = prev
206
207
208 try:
209 request.run(method, path, qs, reqproto, headers, rfile)
210 break
211 except cherrypy.InternalRedirect:
212 ir = sys.exc_info()[1]
213 app.release_serving()
214 prev = request
215
216 if not recursive:
217 if ir.path in redirections:
218 raise RuntimeError(
219 "InternalRedirector visited the same URL "
220 "twice: %r" % ir.path)
221 else:
222
223
224 if qs:
225 qs = "?" + qs
226 redirections.append(sn + path + qs)
227
228
229 method = "GET"
230 path = ir.path
231 qs = ir.query_string
232 rfile = BytesIO()
233
234 send_response(
235 req, response.output_status, response.header_list,
236 response.body, response.stream)
237 finally:
238 app.release_serving()
239 except:
240 tb = format_exc()
241 cherrypy.log(tb, 'MOD_PYTHON', severity=logging.ERROR)
242 s, h, b = bare_error()
243 send_response(req, s, h, b)
244 return apache.OK
245
246
269
270
271
272 import os
273 import re
274 try:
275 import subprocess
276
278 p = subprocess.Popen(fullcmd, shell=True,
279 stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
280 close_fds=True)
281 return p.stdout
282 except ImportError:
284 pipein, pipeout = os.popen4(fullcmd)
285 return pipeout
286
287
289 fullcmd = "%s %s" % (cmd, args)
290 pipeout = popen(fullcmd)
291 try:
292 firstline = pipeout.readline()
293 cmd_not_found = re.search(
294 ntob("(not recognized|No such file|not found)"),
295 firstline,
296 re.IGNORECASE
297 )
298 if cmd_not_found:
299 raise IOError('%s must be on your system path.' % cmd)
300 output = firstline + pipeout.read()
301 finally:
302 pipeout.close()
303 return output
304
305
307
308 template = """
309 # Apache2 server configuration file for running CherryPy with mod_python.
310
311 DocumentRoot "/"
312 Listen %(port)s
313 LoadModule python_module modules/mod_python.so
314
315 <Location %(loc)s>
316 SetHandler python-program
317 PythonHandler %(handler)s
318 PythonDebug On
319 %(opts)s
320 </Location>
321 """
322
323 - def __init__(self, loc="/", port=80, opts=None, apache_path="apache",
324 handler="cherrypy._cpmodpy::handler"):
325 self.loc = loc
326 self.port = port
327 self.opts = opts
328 self.apache_path = apache_path
329 self.handler = handler
330
332 opts = "".join([" PythonOption %s %s\n" % (k, v)
333 for k, v in self.opts])
334 conf_data = self.template % {"port": self.port,
335 "loc": self.loc,
336 "opts": opts,
337 "handler": self.handler,
338 }
339
340 mpconf = os.path.join(os.path.dirname(__file__), "cpmodpy.conf")
341 f = open(mpconf, 'wb')
342 try:
343 f.write(conf_data)
344 finally:
345 f.close()
346
347 response = read_process(self.apache_path, "-k start -f %s" % mpconf)
348 self.ready = True
349 return response
350
352 os.popen("apache -k stop")
353 self.ready = False
354