Fixed python_path problem.
[smonitor.git] / lib / cherrypy / _cpmodpy.py
blobba2ab22f16a7b1c1398711b028294888675a51ab
1 """Native adapter for serving CherryPy via mod_python
3 Basic usage:
5 ##########################################
6 # Application in a module called myapp.py
7 ##########################################
9 import cherrypy
11 class Root:
12 @cherrypy.expose
13 def index(self):
14 return 'Hi there, Ho there, Hey there'
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})
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 ##########################################
32 # Start
33 DocumentRoot "/"
34 Listen 8080
35 LoadModule python_module /usr/lib/apache2/modules/mod_python.so
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
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.
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.
55 Then restart apache2 and access http://127.0.0.1:8080
56 """
58 import logging
59 import sys
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
67 # ------------------------------ Request-handling
71 def setup(req):
72 from mod_python import apache
74 # Run any setup functions defined by a "PythonOption cherrypy.setup" directive.
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()
87 cherrypy.config.update({'log.screen': False,
88 "tools.ignore_headers.on": True,
89 "tools.ignore_headers.headers": ['Range'],
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()
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 # On Windows, req.server is required or the msg will vanish. See
109 # http://www.modpython.org/pipermail/mod_python/2003-October/014291.html.
110 # Also, "When server is not specified...LogLevel does not apply..."
111 apache.log_error(msg, newlevel, req.server)
112 engine.subscribe('log', _log)
114 engine.start()
116 def cherrypy_cleanup(data):
117 engine.exit()
118 try:
119 # apache.register_cleanup wasn't available until 3.1.4.
120 apache.register_cleanup(cherrypy_cleanup)
121 except AttributeError:
122 req.server.register_cleanup(req, cherrypy_cleanup)
125 class _ReadOnlyRequest:
126 expose = ('read', 'readline', 'readlines')
127 def __init__(self, req):
128 for method in self.expose:
129 self.__dict__[method] = getattr(req, method)
132 recursive = False
134 _isSetUp = False
135 def handler(req):
136 from mod_python import apache
137 try:
138 global _isSetUp
139 if not _isSetUp:
140 setup(req)
141 _isSetUp = True
143 # Obtain a Request object from CherryPy
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 "")
149 scheme = req.parsed_uri[0] or 'http'
150 req.get_basic_auth_pw()
152 try:
153 # apache.mpm_query only became available in mod_python 3.1
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")
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")
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")
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
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
202 # Run the CherryPy Request object and obtain the response
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
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 # Add the *previous* path_info + qs to redirections.
217 if qs:
218 qs = "?" + qs
219 redirections.append(sn + path + qs)
221 # Munge environment and try again.
222 method = "GET"
223 path = ir.path
224 qs = ir.query_string
225 rfile = BytesIO()
227 send_response(req, response.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
239 def send_response(req, status, headers, body, stream=False):
240 # Set response status
241 req.status = int(status[:3])
243 # Set response headers
244 req.content_type = "text/plain"
245 for header, value in headers:
246 if header.lower() == 'content-type':
247 req.content_type = value
248 continue
249 req.headers_out.add(header, value)
251 if stream:
252 # Flush now so the status and headers are sent immediately.
253 req.flush()
255 # Set response body
256 if isinstance(body, basestring):
257 req.write(body)
258 else:
259 for seg in body:
260 req.write(seg)
264 # --------------- Startup tools for CherryPy + mod_python --------------- #
267 import os
268 import re
271 def read_process(cmd, args=""):
272 fullcmd = "%s %s" % (cmd, args)
273 pipein, pipeout = os.popen4(fullcmd)
274 try:
275 firstline = pipeout.readline()
276 if (re.search(ntob("(not recognized|No such file|not found)"), firstline,
277 re.IGNORECASE)):
278 raise IOError('%s must be on your system path.' % cmd)
279 output = firstline + pipeout.read()
280 finally:
281 pipeout.close()
282 return output
285 class ModPythonServer(object):
287 template = """
288 # Apache2 server configuration file for running CherryPy with mod_python.
290 DocumentRoot "/"
291 Listen %(port)s
292 LoadModule python_module modules/mod_python.so
294 <Location %(loc)s>
295 SetHandler python-program
296 PythonHandler %(handler)s
297 PythonDebug On
298 %(opts)s
299 </Location>
302 def __init__(self, loc="/", port=80, opts=None, apache_path="apache",
303 handler="cherrypy._cpmodpy::handler"):
304 self.loc = loc
305 self.port = port
306 self.opts = opts
307 self.apache_path = apache_path
308 self.handler = handler
310 def start(self):
311 opts = "".join([" PythonOption %s %s\n" % (k, v)
312 for k, v in self.opts])
313 conf_data = self.template % {"port": self.port,
314 "loc": self.loc,
315 "opts": opts,
316 "handler": self.handler,
319 mpconf = os.path.join(os.path.dirname(__file__), "cpmodpy.conf")
320 f = open(mpconf, 'wb')
321 try:
322 f.write(conf_data)
323 finally:
324 f.close()
326 response = read_process(self.apache_path, "-k start -f %s" % mpconf)
327 self.ready = True
328 return response
330 def stop(self):
331 os.popen("apache -k stop")
332 self.ready = False