Package cherrypy :: Module _cpmodpy
[hide private]
[frames] | no frames]

Source Code for Module cherrypy._cpmodpy

  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