Index: third_party/cherrypy/_cpmodpy.py |
=================================================================== |
--- third_party/cherrypy/_cpmodpy.py (revision 0) |
+++ third_party/cherrypy/_cpmodpy.py (revision 0) |
@@ -0,0 +1,344 @@ |
+"""Native adapter for serving CherryPy via mod_python |
+ |
+Basic usage: |
+ |
+########################################## |
+# Application in a module called myapp.py |
+########################################## |
+ |
+import cherrypy |
+ |
+class Root: |
+ @cherrypy.expose |
+ def index(self): |
+ return 'Hi there, Ho there, Hey there' |
+ |
+ |
+# We will use this method from the mod_python configuration |
+# as the entry point to our application |
+def setup_server(): |
+ cherrypy.tree.mount(Root()) |
+ cherrypy.config.update({'environment': 'production', |
+ 'log.screen': False, |
+ 'show_tracebacks': False}) |
+ |
+########################################## |
+# mod_python settings for apache2 |
+# This should reside in your httpd.conf |
+# or a file that will be loaded at |
+# apache startup |
+########################################## |
+ |
+# Start |
+DocumentRoot "/" |
+Listen 8080 |
+LoadModule python_module /usr/lib/apache2/modules/mod_python.so |
+ |
+<Location "/"> |
+ PythonPath "sys.path+['/path/to/my/application']" |
+ SetHandler python-program |
+ PythonHandler cherrypy._cpmodpy::handler |
+ PythonOption cherrypy.setup myapp::setup_server |
+ PythonDebug On |
+</Location> |
+# End |
+ |
+The actual path to your mod_python.so is dependent on your |
+environment. In this case we suppose a global mod_python |
+installation on a Linux distribution such as Ubuntu. |
+ |
+We do set the PythonPath configuration setting so that |
+your application can be found by from the user running |
+the apache2 instance. Of course if your application |
+resides in the global site-package this won't be needed. |
+ |
+Then restart apache2 and access http://127.0.0.1:8080 |
+""" |
+ |
+import logging |
+import sys |
+ |
+import cherrypy |
+from cherrypy._cpcompat import BytesIO, copyitems, ntob |
+from cherrypy._cperror import format_exc, bare_error |
+from cherrypy.lib import httputil |
+ |
+ |
+# ------------------------------ Request-handling |
+ |
+ |
+ |
+def setup(req): |
+ from mod_python import apache |
+ |
+ # Run any setup functions defined by a "PythonOption cherrypy.setup" directive. |
+ options = req.get_options() |
+ if 'cherrypy.setup' in options: |
+ for function in options['cherrypy.setup'].split(): |
+ atoms = function.split('::', 1) |
+ if len(atoms) == 1: |
+ mod = __import__(atoms[0], globals(), locals()) |
+ else: |
+ modname, fname = atoms |
+ mod = __import__(modname, globals(), locals(), [fname]) |
+ func = getattr(mod, fname) |
+ func() |
+ |
+ cherrypy.config.update({'log.screen': False, |
+ "tools.ignore_headers.on": True, |
+ "tools.ignore_headers.headers": ['Range'], |
+ }) |
+ |
+ engine = cherrypy.engine |
+ if hasattr(engine, "signal_handler"): |
+ engine.signal_handler.unsubscribe() |
+ if hasattr(engine, "console_control_handler"): |
+ engine.console_control_handler.unsubscribe() |
+ engine.autoreload.unsubscribe() |
+ cherrypy.server.unsubscribe() |
+ |
+ def _log(msg, level): |
+ newlevel = apache.APLOG_ERR |
+ if logging.DEBUG >= level: |
+ newlevel = apache.APLOG_DEBUG |
+ elif logging.INFO >= level: |
+ newlevel = apache.APLOG_INFO |
+ elif logging.WARNING >= level: |
+ newlevel = apache.APLOG_WARNING |
+ # On Windows, req.server is required or the msg will vanish. See |
+ # http://www.modpython.org/pipermail/mod_python/2003-October/014291.html. |
+ # Also, "When server is not specified...LogLevel does not apply..." |
+ apache.log_error(msg, newlevel, req.server) |
+ engine.subscribe('log', _log) |
+ |
+ engine.start() |
+ |
+ def cherrypy_cleanup(data): |
+ engine.exit() |
+ try: |
+ # apache.register_cleanup wasn't available until 3.1.4. |
+ apache.register_cleanup(cherrypy_cleanup) |
+ except AttributeError: |
+ req.server.register_cleanup(req, cherrypy_cleanup) |
+ |
+ |
+class _ReadOnlyRequest: |
+ expose = ('read', 'readline', 'readlines') |
+ def __init__(self, req): |
+ for method in self.expose: |
+ self.__dict__[method] = getattr(req, method) |
+ |
+ |
+recursive = False |
+ |
+_isSetUp = False |
+def handler(req): |
+ from mod_python import apache |
+ try: |
+ global _isSetUp |
+ if not _isSetUp: |
+ setup(req) |
+ _isSetUp = True |
+ |
+ # Obtain a Request object from CherryPy |
+ local = req.connection.local_addr |
+ local = httputil.Host(local[0], local[1], req.connection.local_host or "") |
+ remote = req.connection.remote_addr |
+ remote = httputil.Host(remote[0], remote[1], req.connection.remote_host or "") |
+ |
+ scheme = req.parsed_uri[0] or 'http' |
+ req.get_basic_auth_pw() |
+ |
+ try: |
+ # apache.mpm_query only became available in mod_python 3.1 |
+ q = apache.mpm_query |
+ threaded = q(apache.AP_MPMQ_IS_THREADED) |
+ forked = q(apache.AP_MPMQ_IS_FORKED) |
+ except AttributeError: |
+ bad_value = ("You must provide a PythonOption '%s', " |
+ "either 'on' or 'off', when running a version " |
+ "of mod_python < 3.1") |
+ |
+ threaded = options.get('multithread', '').lower() |
+ if threaded == 'on': |
+ threaded = True |
+ elif threaded == 'off': |
+ threaded = False |
+ else: |
+ raise ValueError(bad_value % "multithread") |
+ |
+ forked = options.get('multiprocess', '').lower() |
+ if forked == 'on': |
+ forked = True |
+ elif forked == 'off': |
+ forked = False |
+ else: |
+ raise ValueError(bad_value % "multiprocess") |
+ |
+ sn = cherrypy.tree.script_name(req.uri or "/") |
+ if sn is None: |
+ send_response(req, '404 Not Found', [], '') |
+ else: |
+ app = cherrypy.tree.apps[sn] |
+ method = req.method |
+ path = req.uri |
+ qs = req.args or "" |
+ reqproto = req.protocol |
+ headers = copyitems(req.headers_in) |
+ rfile = _ReadOnlyRequest(req) |
+ prev = None |
+ |
+ try: |
+ redirections = [] |
+ while True: |
+ request, response = app.get_serving(local, remote, scheme, |
+ "HTTP/1.1") |
+ request.login = req.user |
+ request.multithread = bool(threaded) |
+ request.multiprocess = bool(forked) |
+ request.app = app |
+ request.prev = prev |
+ |
+ # Run the CherryPy Request object and obtain the response |
+ try: |
+ request.run(method, path, qs, reqproto, headers, rfile) |
+ break |
+ except cherrypy.InternalRedirect: |
+ ir = sys.exc_info()[1] |
+ app.release_serving() |
+ prev = request |
+ |
+ if not recursive: |
+ if ir.path in redirections: |
+ raise RuntimeError("InternalRedirector visited the " |
+ "same URL twice: %r" % ir.path) |
+ else: |
+ # Add the *previous* path_info + qs to redirections. |
+ if qs: |
+ qs = "?" + qs |
+ redirections.append(sn + path + qs) |
+ |
+ # Munge environment and try again. |
+ method = "GET" |
+ path = ir.path |
+ qs = ir.query_string |
+ rfile = BytesIO() |
+ |
+ send_response(req, response.output_status, response.header_list, |
+ response.body, response.stream) |
+ finally: |
+ app.release_serving() |
+ except: |
+ tb = format_exc() |
+ cherrypy.log(tb, 'MOD_PYTHON', severity=logging.ERROR) |
+ s, h, b = bare_error() |
+ send_response(req, s, h, b) |
+ return apache.OK |
+ |
+ |
+def send_response(req, status, headers, body, stream=False): |
+ # Set response status |
+ req.status = int(status[:3]) |
+ |
+ # Set response headers |
+ req.content_type = "text/plain" |
+ for header, value in headers: |
+ if header.lower() == 'content-type': |
+ req.content_type = value |
+ continue |
+ req.headers_out.add(header, value) |
+ |
+ if stream: |
+ # Flush now so the status and headers are sent immediately. |
+ req.flush() |
+ |
+ # Set response body |
+ if isinstance(body, basestring): |
+ req.write(body) |
+ else: |
+ for seg in body: |
+ req.write(seg) |
+ |
+ |
+ |
+# --------------- Startup tools for CherryPy + mod_python --------------- # |
+ |
+ |
+import os |
+import re |
+try: |
+ import subprocess |
+ def popen(fullcmd): |
+ p = subprocess.Popen(fullcmd, shell=True, |
+ stdout=subprocess.PIPE, stderr=subprocess.STDOUT, |
+ close_fds=True) |
+ return p.stdout |
+except ImportError: |
+ def popen(fullcmd): |
+ pipein, pipeout = os.popen4(fullcmd) |
+ return pipeout |
+ |
+ |
+def read_process(cmd, args=""): |
+ fullcmd = "%s %s" % (cmd, args) |
+ pipeout = popen(fullcmd) |
+ try: |
+ firstline = pipeout.readline() |
+ if (re.search(ntob("(not recognized|No such file|not found)"), firstline, |
+ re.IGNORECASE)): |
+ raise IOError('%s must be on your system path.' % cmd) |
+ output = firstline + pipeout.read() |
+ finally: |
+ pipeout.close() |
+ return output |
+ |
+ |
+class ModPythonServer(object): |
+ |
+ template = """ |
+# Apache2 server configuration file for running CherryPy with mod_python. |
+ |
+DocumentRoot "/" |
+Listen %(port)s |
+LoadModule python_module modules/mod_python.so |
+ |
+<Location %(loc)s> |
+ SetHandler python-program |
+ PythonHandler %(handler)s |
+ PythonDebug On |
+%(opts)s |
+</Location> |
+""" |
+ |
+ def __init__(self, loc="/", port=80, opts=None, apache_path="apache", |
+ handler="cherrypy._cpmodpy::handler"): |
+ self.loc = loc |
+ self.port = port |
+ self.opts = opts |
+ self.apache_path = apache_path |
+ self.handler = handler |
+ |
+ def start(self): |
+ opts = "".join([" PythonOption %s %s\n" % (k, v) |
+ for k, v in self.opts]) |
+ conf_data = self.template % {"port": self.port, |
+ "loc": self.loc, |
+ "opts": opts, |
+ "handler": self.handler, |
+ } |
+ |
+ mpconf = os.path.join(os.path.dirname(__file__), "cpmodpy.conf") |
+ f = open(mpconf, 'wb') |
+ try: |
+ f.write(conf_data) |
+ finally: |
+ f.close() |
+ |
+ response = read_process(self.apache_path, "-k start -f %s" % mpconf) |
+ self.ready = True |
+ return response |
+ |
+ def stop(self): |
+ os.popen("apache -k stop") |
+ self.ready = False |
+ |
Property changes on: third_party/cherrypy/_cpmodpy.py |
___________________________________________________________________ |
Added: svn:eol-style |
+ LF |