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