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

root/branches/598-sendall/cherrypy/_cpmodpy.py

Revision 1830 (checked in by fumanchu, 1 year ago)

Fix for #751 (logging: Python file objects are not thread-safe). Whew. Finally got all the bus operations where I want em:

  1. There's a new EXITING state
  2. bus.block() waits for EXITING now instead of STOPPED, and also wait for all threads to exit, so the main thread doesn't exit (and run atexit callbacks) too early.
  3. bus.exit() no longer calls sys.exit. Instead, exit is used to signal the main thread to unblock and terminate normally. This means some callers of stop need(ed) to be changed to call exit instead.
  4. bus.block() no longer takes a state arg; it's now only for use by the main thread. Call bus.wait(state) to wait for other states.
  • 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     # Turn off the builtin signal handlers, which can interact
26     # in strange ways with the Apache process.
27     cherrypy.engine.SIGHUP = None
28     cherrypy.engine.SIGTERM = None
29     
30     # You must start the engine in a non-blocking fashion
31     # so that mod_python can proceed
32     cherrypy.engine.start()
33
34 ##########################################
35 # mod_python settings for apache2
36 # This should reside in your httpd.conf
37 # or a file that will be loaded at
38 # apache startup
39 ##########################################
40
41 # Start
42 DocumentRoot "/"
43 Listen 8080
44 LoadModule python_module /usr/lib/apache2/modules/mod_python.so
45
46 <Location "/">
47         PythonPath "sys.path+['/path/to/my/application']"
48         SetHandler python-program
49         PythonHandler cherrypy._cpmodpy::handler
50         PythonOption cherrypy.setup myapp::setup_server
51         PythonDebug On
52 </Location>
53 # End
54
55 The actual path to your mod_python.so is dependent on your
56 environment. In this case we suppose a global mod_python
57 installation on a Linux distribution such as Ubuntu.
58
59 We do set the PythonPath configuration setting so that
60 your application can be found by from the user running
61 the apache2 instance. Of course if your application
62 resides in the global site-package this won't be needed.
63
64 Then restart apache2 and access http://127.0.0.1:8080
65 """
66
67 import StringIO
68
69 import cherrypy
70 from cherrypy._cperror import format_exc, bare_error
71 from cherrypy.lib import http
72
73
74
75 # ------------------------------ Request-handling
76
77
78 def setup(req):
79     # Run any setup function defined by a "PythonOption cherrypy.setup" directive.
80     options = req.get_options()
81     if 'cherrypy.setup' in options:
82         modname, fname = options['cherrypy.setup'].split('::')
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     cherrypy.server.unsubscribe()
93     cherrypy.engine.start()
94    
95     def cherrypy_cleanup(data):
96         cherrypy.engine.exit()
97     try:
98         from mod_python import apache
99         # apache.register_cleanup wasn't available until 3.1.4.
100         apache.register_cleanup(cherrypy_cleanup)
101     except AttributeError:
102         req.server.register_cleanup(req, cherrypy_cleanup)
103
104
105 class _ReadOnlyRequest:
106     expose = ('read', 'readline', 'readlines')
107     def __init__(self, req):
108         for method in self.expose:
109             self.__dict__[method] = getattr(req, method)
110
111
112 recursive = False
113
114 _isSetUp = False
115 def handler(req):
116     from mod_python import apache
117     try:
118         global _isSetUp
119         if not _isSetUp:
120             setup(req)
121             _isSetUp = True
122        
123         # Obtain a Request object from CherryPy
124         local = req.connection.local_addr
125         local = http.Host(local[0], local[1], req.connection.local_host or "")
126         remote = req.connection.remote_addr
127         remote = http.Host(remote[0], remote[1], req.connection.remote_host or "")
128        
129         scheme = req.parsed_uri[0] or 'http'
130         req.get_basic_auth_pw()
131        
132         try:
133             # apache.mpm_query only became available in mod_python 3.1
134             q = apache.mpm_query
135             threaded = q(apache.AP_MPMQ_IS_THREADED)
136             forked = q(apache.AP_MPMQ_IS_FORKED)
137         except AttributeError:
138             bad_value = ("You must provide a PythonOption '%s', "
139                          "either 'on' or 'off', when running a version "
140                          "of mod_python < 3.1")
141            
142             threaded = options.get('multithread', '').lower()
143             if threaded == 'on':
144                 threaded = True
145             elif threaded == 'off':
146                 threaded = False
147             else:
148                 raise ValueError(bad_value % "multithread")
149            
150             forked = options.get('multiprocess', '').lower()
151             if forked == 'on':
152                 forked = True
153             elif forked == 'off':
154                 forked = False
155             else:
156                 raise ValueError(bad_value % "multiprocess")
157        
158         sn = cherrypy.tree.script_name(req.uri or "/")
159         if sn is None:
160             send_response(req, '404 Not Found', [], '')
161         else:
162             app = cherrypy.tree.apps[sn]
163             method = req.method
164             path = req.uri
165             qs = req.args or ""
166             reqproto = req.protocol
167             headers = req.headers_in.items()
168             rfile = _ReadOnlyRequest(req)
169             prev = None
170            
171             try:
172                 redirections = []
173                 while True:
174                     request, response = app.get_serving(local, remote, scheme,
175                                                         "HTTP/1.1")
176                     request.login = req.user
177                     request.multithread = bool(threaded)
178                     request.multiprocess = bool(forked)
179                     request.app = app
180                     request.prev = prev
181                    
182                     # Run the CherryPy Request object and obtain the response
183                     try:
184                         request.run(method, path, qs, reqproto, headers, rfile)
185                         break
186                     except cherrypy.InternalRedirect, ir:
187                         app.release_serving()
188                         prev = request
189                        
190                         if not recursive:
191                             if ir.path in redirections:
192                                 raise RuntimeError("InternalRedirector visited the "
193                                                    "same URL twice: %r" % ir.path)
194                             else:
195                                 # Add the *previous* path_info + qs to redirections.
196                                 if qs:
197                                     qs = "?" + qs
198                                 redirections.append(sn + path + qs)
199                        
200                         # Munge environment and try again.
201                         method = "GET"
202                         path = ir.path
203                         qs = ir.query_string
204                         rfile = StringIO.StringIO()
205                
206                 send_response(req, response.status, response.header_list,
207                               response.body, response.stream)
208             finally:
209                 app.release_serving()
210     except:
211         tb = format_exc()
212         cherrypy.log(tb)
213         s, h, b = bare_error()
214         send_response(req, s, h, b)
215     return apache.OK
216
217
218 def send_response(req, status, headers, body, stream=False):
219     # Set response status
220     req.status = int(status[:3])
221    
222     # Set response headers
223     req.content_type = "text/plain"
224     for header, value in headers:
225         if header.lower() == 'content-type':
226             req.content_type = value
227             continue
228         req.headers_out.add(header, value)
229    
230     if stream:
231         # Flush now so the status and headers are sent immediately.
232         req.flush()
233    
234     # Set response body
235     if isinstance(body, basestring):
236         req.write(body)
237     else:
238         for seg in body:
239             req.write(seg)
240
241
242
243 # --------------- Startup tools for CherryPy + mod_python --------------- #
244
245
246 import os
247 import re
248
249
250 def read_process(cmd, args=""):
251     pipein, pipeout = os.popen4("%s %s" % (cmd, args))
252     try:
253         firstline = pipeout.readline()
254         if (re.search(r"(not recognized|No such file|not found)", firstline,
255                       re.IGNORECASE)):
256             raise IOError('%s must be on your system path.' % cmd)
257         output = firstline + pipeout.read()
258     finally:
259         pipeout.close()
260     return output
261
262
263 class ModPythonServer(object):
264    
265     template = """
266 # Apache2 server configuration file for running CherryPy with mod_python.
267
268 DocumentRoot "/"
269 Listen %(port)s
270 LoadModule python_module modules/mod_python.so
271
272 <Location %(loc)s>
273     SetHandler python-program
274     PythonHandler %(handler)s
275     PythonDebug On
276 %(opts)s
277 </Location>
278 """
279    
280     def __init__(self, loc="/", port=80, opts=None, apache_path="apache",
281                  handler="cherrypy._cpmodpy::handler"):
282         self.loc = loc
283         self.port = port
284         self.opts = opts
285         self.apache_path = apache_path
286         self.handler = handler
287    
288     def start(self):
289         opts = "".join(["    PythonOption %s %s\n" % (k, v)
290                         for k, v in self.opts])
291         conf_data = self.template % {"port": self.port,
292                                      "loc": self.loc,
293                                      "opts": opts,
294                                      "handler": self.handler,
295                                      }
296        
297         mpconf = os.path.join(os.path.dirname(__file__), "cpmodpy.conf")
298         f = open(mpconf, 'wb')
299         try:
300             f.write(conf_data)
301         finally:
302             f.close()
303        
304         response = read_process(self.apache_path, "-k start -f %s" % mpconf)
305         self.ready = True
306         return response
307    
308     def stop(self):
309         os.popen("apache -k stop")
310         self.ready = False
311
Note: See TracBrowser for help on using the browser.

Hosted by WebFaction

Log in as guest/cpguest to create tickets