1 """Native adapter for serving CherryPy via mod_python
5 ##########################################
6 # Application in a module called myapp.py
7 ##########################################
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
20 cherrypy.tree.mount(Root())
21 cherrypy.config.update({'environment': 'production',
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
30 ##########################################
35 LoadModule python_module /usr/lib/apache2/modules/mod_python.so
38 PythonPath "sys.path+['/path/to/my/application']"
39 SetHandler python-program
40 PythonHandler cherrypy._cpmodpy::handler
41 PythonOption cherrypy.setup myapp::setup_server
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
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
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)
80 mod
= __import__(atoms
[0], globals(), locals())
82 modname
, fname
= atoms
83 mod
= __import__(modname
, globals(), locals(), [fname
])
84 func
= getattr(mod
, fname
)
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
)
116 def cherrypy_cleanup(data
):
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
)
136 from mod_python
import apache
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()
153 # apache.mpm_query only became available in mod_python 3.1
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()
165 elif threaded
== 'off':
168 raise ValueError(bad_value
% "multithread")
170 forked
= options
.get('multiprocess', '').lower()
173 elif forked
== 'off':
176 raise ValueError(bad_value
% "multiprocess")
178 sn
= cherrypy
.tree
.script_name(req
.uri
or "/")
180 send_response(req
, '404 Not Found', [], '')
182 app
= cherrypy
.tree
.apps
[sn
]
186 reqproto
= req
.protocol
187 headers
= copyitems(req
.headers_in
)
188 rfile
= _ReadOnlyRequest(req
)
194 request
, response
= app
.get_serving(local
, remote
, scheme
,
196 request
.login
= req
.user
197 request
.multithread
= bool(threaded
)
198 request
.multiprocess
= bool(forked
)
202 # Run the CherryPy Request object and obtain the response
204 request
.run(method
, path
, qs
, reqproto
, headers
, rfile
)
206 except cherrypy
.InternalRedirect
:
207 ir
= sys
.exc_info()[1]
208 app
.release_serving()
212 if ir
.path
in redirections
:
213 raise RuntimeError("InternalRedirector visited the "
214 "same URL twice: %r" % ir
.path
)
216 # Add the *previous* path_info + qs to redirections.
219 redirections
.append(sn
+ path
+ qs
)
221 # Munge environment and try again.
227 send_response(req
, response
.status
, response
.header_list
,
228 response
.body
, response
.stream
)
230 app
.release_serving()
233 cherrypy
.log(tb
, 'MOD_PYTHON', severity
=logging
.ERROR
)
234 s
, h
, b
= bare_error()
235 send_response(req
, s
, h
, b
)
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
249 req
.headers_out
.add(header
, value
)
252 # Flush now so the status and headers are sent immediately.
256 if isinstance(body
, basestring
):
264 # --------------- Startup tools for CherryPy + mod_python --------------- #
271 def read_process(cmd
, args
=""):
272 fullcmd
= "%s %s" % (cmd
, args
)
273 pipein
, pipeout
= os
.popen4(fullcmd
)
275 firstline
= pipeout
.readline()
276 if (re
.search(ntob("(not recognized|No such file|not found)"), firstline
,
278 raise IOError('%s must be on your system path.' % cmd
)
279 output
= firstline
+ pipeout
.read()
285 class ModPythonServer(object):
288 # Apache2 server configuration file for running CherryPy with mod_python.
292 LoadModule python_module modules/mod_python.so
295 SetHandler python-program
296 PythonHandler %(handler)s
302 def __init__(self
, loc
="/", port
=80, opts
=None, apache_path
="apache",
303 handler
="cherrypy._cpmodpy::handler"):
307 self
.apache_path
= apache_path
308 self
.handler
= handler
311 opts
= "".join([" PythonOption %s %s\n" % (k
, v
)
312 for k
, v
in self
.opts
])
313 conf_data
= self
.template
% {"port": self
.port
,
316 "handler": self
.handler
,
319 mpconf
= os
.path
.join(os
.path
.dirname(__file__
), "cpmodpy.conf")
320 f
= open(mpconf
, 'wb')
326 response
= read_process(self
.apache_path
, "-k start -f %s" % mpconf
)
331 os
.popen("apache -k stop")