mirror of
https://github.com/SickGear/SickGear.git
synced 2024-12-03 18:03:37 +00:00
cec4ed573d
Switched out sqlite3 libs in favour of SQLAlchemy v0.9, will gradually migrate dialects and scheme to be fully SQLAlchemy compliant for using there ORM with sessions instead of direct. Fixed getEpisode function to stop making unrequired scene number conversions on already converted data thats available now from cache.
353 lines
11 KiB
Python
353 lines
11 KiB
Python
"""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()
|
|
cmd_not_found = re.search(
|
|
ntob("(not recognized|No such file|not found)"),
|
|
firstline,
|
|
re.IGNORECASE
|
|
)
|
|
if cmd_not_found:
|
|
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
|