Download Install Tutorial Docs FAQ Tools WikiLicense Team IRC Planet Involvement Shop Book

root/trunk/cherrypy/_cpmodpy.py

Revision 1989 (checked in by fumanchu, 4 weeks ago)

Moved all bus plugins onto the engine object itself, and fixed a couple bus details along the way.

  • Property svn:eol-style set to native
Line 
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 # ------------------------------ Request-handling
68
69
70
71 def setup(req):
72     from mod_python import apache
73    
74     # Run any setup function defined by a "PythonOption cherrypy.setup" directive.
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         # On Windows, req.server is required or the msg will vanish. See
108         # http://www.modpython.org/pipermail/mod_python/2003-October/014291.html.
109         # Also, "When server is not specified...LogLevel does not apply..."
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         # apache.register_cleanup wasn't available until 3.1.4.
119         apache.register_cleanup(cherrypy_cleanup)
120     except AttributeError:
121         req.server.register_cleanup(req, cherrypy_cleanup)
122
123
124 class _ReadOnlyRequest:
125     expose = ('read', 'readline', 'readlines')
126     def __init__(self, req):
127         for method in self.expose:
128             self.__dict__[method] = getattr(req, method)
129
130
131 recursive = False
132
133 _isSetUp = False
134 def handler(req):
135     from mod_python import apache
136     try:
137         global _isSetUp
138         if not _isSetUp:
139             setup(req)
140             _isSetUp = True
141        
142         # Obtain a Request object from CherryPy
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             # apache.mpm_query only became available in mod_python 3.1
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                     # Run the CherryPy Request object and obtain the response
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                                 # Add the *previous* path_info + qs to redirections.
215                                 if qs:
216                                     qs = "?" + qs
217                                 redirections.append(sn + path + qs)
218                        
219                         # Munge environment and try again.
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
237 def send_response(req, status, headers, body, stream=False):
238     # Set response status
239     req.status = int(status[:3])
240    
241     # Set response headers
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         # Flush now so the status and headers are sent immediately.
251         req.flush()
252    
253     # Set response body
254     if isinstance(body, basestring):
255         req.write(body)
256     else:
257         for seg in body:
258             req.write(seg)
259
260
261
262 # --------------- Startup tools for CherryPy + mod_python --------------- #
263
264
265 import os
266 import re
267
268
269 def read_process(cmd, args=""):
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
282 class ModPythonServer(object):
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    
307     def start(self):
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    
327     def stop(self):
328         os.popen("apache -k stop")
329         self.ready = False
330
Note: See TracBrowser for help on using the browser.

Hosted by WebFaction

Log in as guest/cpguest to create tickets