PK ;^W7K[( ( paste/modpython.py"""WSGI Paste wrapper for mod_python. Requires Python 2.2 or greater.
Example httpd.conf section for a Paste app with an ini file::
SetHandler python-program
PythonHandler paste.modpython
PythonOption paste.ini /some/location/your/pasteconfig.ini
Or if you want to load a WSGI application under /your/homedir in the module
``startup`` and the WSGI app is ``app``::
SetHandler python-program
PythonHandler paste.modpython
PythonPath "['/virtual/project/directory'] + sys.path"
PythonOption wsgi.application startup::app
If you'd like to use a virtual installation, make sure to add it in the path
like so::
SetHandler python-program
PythonHandler paste.modpython
PythonPath "['/virtual/project/directory', '/virtual/lib/python2.4/'] + sys.path"
PythonOption paste.ini /virtual/project/directory/pasteconfig.ini
Some WSGI implementations assume that the SCRIPT_NAME environ variable will
always be equal to "the root URL of the app"; Apache probably won't act as
you expect in that case. You can add another PythonOption directive to tell
modpython_gateway to force that behavior:
PythonOption SCRIPT_NAME /mcontrol
Some WSGI applications need to be cleaned up when Apache exits. You can
register a cleanup handler with yet another PythonOption directive:
PythonOption wsgi.cleanup module::function
The module.function will be called with no arguments on server shutdown,
once for each child process or thread.
This module highly based on Robert Brewer's, here:
http://projects.amor.org/misc/svn/modpython_gateway.py
"""
import traceback
try:
from mod_python import apache
except:
pass
from paste.deploy import loadapp
class InputWrapper(object):
def __init__(self, req):
self.req = req
def close(self):
pass
def read(self, size=-1):
return self.req.read(size)
def readline(self, size=-1):
return self.req.readline(size)
def readlines(self, hint=-1):
return self.req.readlines(hint)
def __iter__(self):
line = self.readline()
while line:
yield line
# Notice this won't prefetch the next line; it only
# gets called if the generator is resumed.
line = self.readline()
class ErrorWrapper(object):
def __init__(self, req):
self.req = req
def flush(self):
pass
def write(self, msg):
self.req.log_error(msg)
def writelines(self, seq):
self.write(''.join(seq))
bad_value = ("You must provide a PythonOption '%s', either 'on' or 'off', "
"when running a version of mod_python < 3.1")
class Handler(object):
def __init__(self, req):
self.started = False
options = req.get_options()
# Threading and forking
try:
q = apache.mpm_query
threaded = q(apache.AP_MPMQ_IS_THREADED)
forked = q(apache.AP_MPMQ_IS_FORKED)
except AttributeError:
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")
env = self.environ = dict(apache.build_cgi_env(req))
if 'SCRIPT_NAME' in options:
# Override SCRIPT_NAME and PATH_INFO if requested.
env['SCRIPT_NAME'] = options['SCRIPT_NAME']
env['PATH_INFO'] = req.uri[len(options['SCRIPT_NAME']):]
else:
env['SCRIPT_NAME'] = ''
env['PATH_INFO'] = req.uri
env['wsgi.input'] = InputWrapper(req)
env['wsgi.errors'] = ErrorWrapper(req)
env['wsgi.version'] = (1, 0)
env['wsgi.run_once'] = False
if env.get("HTTPS") in ('yes', 'on', '1'):
env['wsgi.url_scheme'] = 'https'
else:
env['wsgi.url_scheme'] = 'http'
env['wsgi.multithread'] = threaded
env['wsgi.multiprocess'] = forked
self.request = req
def run(self, application):
try:
result = application(self.environ, self.start_response)
for data in result:
self.write(data)
if not self.started:
self.request.set_content_length(0)
if hasattr(result, 'close'):
result.close()
except:
traceback.print_exc(None, self.environ['wsgi.errors'])
if not self.started:
self.request.status = 500
self.request.content_type = 'text/plain'
data = "A server error occurred. Please contact the administrator."
self.request.set_content_length(len(data))
self.request.write(data)
def start_response(self, status, headers, exc_info=None):
if exc_info:
try:
if self.started:
raise exc_info[0], exc_info[1], exc_info[2]
finally:
exc_info = None
self.request.status = int(status[:3])
for key, val in headers:
if key.lower() == 'content-length':
self.request.set_content_length(int(val))
elif key.lower() == 'content-type':
self.request.content_type = val
else:
self.request.headers_out.add(key, val)
return self.write
def write(self, data):
if not self.started:
self.started = True
self.request.write(data)
startup = None
cleanup = None
wsgiapps = {}
def handler(req):
options = req.get_options()
# Run a startup function if requested.
global startup
if 'wsgi.startup' in options and not startup:
func = options['wsgi.startup']
if func:
module_name, object_str = func.split('::', 1)
module = __import__(module_name, globals(), locals(), [''])
startup = apache.resolve_object(module, object_str)
startup(req)
# Register a cleanup function if requested.
global cleanup
if 'wsgi.cleanup' in options and not cleanup:
func = options['wsgi.cleanup']
if func:
module_name, object_str = func.split('::', 1)
module = __import__(module_name, globals(), locals(), [''])
cleanup = apache.resolve_object(module, object_str)
def cleaner(data):
cleanup()
try:
# apache.register_cleanup wasn't available until 3.1.4.
apache.register_cleanup(cleaner)
except AttributeError:
req.server.register_cleanup(req, cleaner)
# Import the wsgi 'application' callable and pass it to Handler.run
global wsgiapps
appini = options.get('paste.ini')
app = None
if appini:
if appini not in wsgiapps:
wsgiapps[appini] = loadapp("config:%s" % appini)
app = wsgiapps[appini]
# Import the wsgi 'application' callable and pass it to Handler.run
appwsgi = options.get('wsgi.application')
if appwsgi and not appini:
modname, objname = appwsgi.split('::', 1)
module = __import__(modname, globals(), locals(), [''])
app = getattr(module, objname)
Handler(req).run(app)
# status was set in Handler; always return apache.OK
return apache.OK
PK ;^W7o2=Ȣ# # paste/cgiapp.py# (c) 2005 Ian Bicking and contributors; written for Paste (http://pythonpaste.org)
# Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php
"""
Application that runs a CGI script.
"""
import os
import subprocess
try:
import select
except ImportError:
select = None
from paste.util import converters
__all__ = ['CGIError', 'CGIApplication']
class CGIError(Exception):
"""
Raised when the CGI script can't be found or doesn't
act like a proper CGI script.
"""
class CGIApplication(object):
"""
This object acts as a proxy to a CGI application. You pass in the
script path (``script``), an optional path to search for the
script (if the name isn't absolute) (``path``). If you don't give
a path, then ``$PATH`` will be used.
"""
def __init__(self,
global_conf,
script,
path=None,
include_os_environ=True,
query_string=None):
if global_conf:
raise NotImplemented(
"global_conf is no longer supported for CGIApplication "
"(use make_cgi_application); please pass None instead")
self.script_filename = script
if path is None:
path = os.environ.get('PATH', '').split(':')
self.path = path
if '?' in script:
assert query_string is None, (
"You cannot have '?' in your script name (%r) and also "
"give a query_string (%r)" % (script, query_string))
script, query_string = script.split('?', 1)
if os.path.abspath(script) != script:
# relative path
for path_dir in self.path:
if os.path.exists(os.path.join(path_dir, script)):
self.script = os.path.join(path_dir, script)
break
else:
raise CGIError(
"Script %r not found in path %r"
% (script, self.path))
else:
self.script = script
self.include_os_environ = include_os_environ
self.query_string = query_string
def __call__(self, environ, start_response):
if 'REQUEST_URI' not in environ:
environ['REQUEST_URI'] = (
environ.get('SCRIPT_NAME', '')
+ environ.get('PATH_INFO', ''))
if self.include_os_environ:
cgi_environ = os.environ.copy()
else:
cgi_environ = {}
for name in environ:
# Should unicode values be encoded?
if (name.upper() == name
and isinstance(environ[name], str)):
cgi_environ[name] = environ[name]
if self.query_string is not None:
old = cgi_environ.get('QUERY_STRING', '')
if old:
old += '&'
cgi_environ['QUERY_STRING'] = old + self.query_string
cgi_environ['SCRIPT_FILENAME'] = self.script
proc = subprocess.Popen(
[self.script],
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
env=cgi_environ,
cwd=os.path.dirname(self.script),
)
writer = CGIWriter(environ, start_response)
if select:
proc_communicate(
proc,
stdin=StdinReader.from_environ(environ),
stdout=writer,
stderr=environ['wsgi.errors'])
else:
stdout, stderr = proc.communicate(StdinReader.from_environ(environ).read())
if stderr:
environ['wsgi.errors'].write(stderr)
writer(stdout)
if not writer.headers_finished:
start_response(writer.status, writer.headers)
return []
class CGIWriter(object):
def __init__(self, environ, start_response):
self.environ = environ
self.start_response = start_response
self.status = '200 OK'
self.headers = []
self.headers_finished = False
self.writer = None
self.buffer = ''
def write(self, data):
if self.headers_finished:
self.writer(data)
return
self.buffer += data
while '\n' in self.buffer:
if '\r\n' in self.buffer:
line1, self.buffer = self.buffer.split('\r\n', 1)
else:
line1, self.buffer = self.buffer.split('\n', 1)
if not line1:
self.headers_finished = True
self.writer = self.start_response(
self.status, self.headers)
self.writer(self.buffer)
del self.buffer
del self.headers
del self.status
break
elif ':' not in line1:
raise CGIError(
"Bad header line: %r" % line1)
else:
name, value = line1.split(':', 1)
value = value.lstrip()
name = name.strip()
if name.lower() == 'status':
self.status = value
else:
self.headers.append((name, value))
class StdinReader(object):
def __init__(self, stdin, content_length):
self.stdin = stdin
self.content_length = content_length
def from_environ(cls, environ):
length = environ.get('CONTENT_LENGTH')
if length:
length = int(length)
else:
length = 0
return cls(environ['wsgi.input'], length)
from_environ = classmethod(from_environ)
def read(self, size=None):
if not self.content_length:
return ''
if size is None:
text = self.stdin.read(self.content_length)
else:
text = self.stdin.read(min(self.content_length, size))
self.content_length -= len(text)
return text
def proc_communicate(proc, stdin=None, stdout=None, stderr=None):
"""
Run the given process, piping input/output/errors to the given
file-like objects (which need not be actual file objects, unlike
the arguments passed to Popen). Wait for process to terminate.
Note: this is taken from the posix version of
subprocess.Popen.communicate, but made more general through the
use of file-like objects.
"""
read_set = []
write_set = []
input_buffer = ''
trans_nl = proc.universal_newlines and hasattr(open, 'newlines')
if proc.stdin:
# Flush stdio buffer. This might block, if the user has
# been writing to .stdin in an uncontrolled fashion.
proc.stdin.flush()
if input:
write_set.append(proc.stdin)
else:
proc.stdin.close()
else:
assert stdin is None
if proc.stdout:
read_set.append(proc.stdout)
else:
assert stdout is None
if proc.stderr:
read_set.append(proc.stderr)
else:
assert stderr is None
while read_set or write_set:
rlist, wlist, xlist = select.select(read_set, write_set, [])
if proc.stdin in wlist:
# When select has indicated that the file is writable,
# we can write up to PIPE_BUF bytes without risk
# blocking. POSIX defines PIPE_BUF >= 512
next, input_buffer = input_buffer, ''
next_len = 512-len(next)
if next_len:
next += stdin.read(next_len)
if not next:
proc.stdin.close()
write_set.remove(proc.stdin)
else:
bytes_written = os.write(proc.stdin.fileno(), next)
if bytes_written < len(next):
input_buffer = next[bytes_written:]
if proc.stdout in rlist:
data = os.read(proc.stdout.fileno(), 1024)
if data == "":
proc.stdout.close()
read_set.remove(proc.stdout)
if trans_nl:
data = proc._translate_newlines(data)
stdout.write(data)
if proc.stderr in rlist:
data = os.read(proc.stderr.fileno(), 1024)
if data == "":
proc.stderr.close()
read_set.remove(proc.stderr)
if trans_nl:
data = proc._translate_newlines(data)
stderr.write(data)
proc.wait()
def make_cgi_application(global_conf, script, path=None, include_os_environ=None,
query_string=None):
"""
This object acts as a proxy to a CGI application. You pass in the
script path (``script``), an optional path to search for the
script (if the name isn't absolute) (``path``). If you don't give
a path, then ``$PATH`` will be used.
"""
if path is None:
path = global_conf.get('path') or global_conf.get('PATH')
include_os_environ = converters.asbool(include_os_environ)
return CGIApplication(
script, path=path, include_os_environ=include_os_environ,
query_string=query_string)
PK ;^W7Z paste/reloader.py# (c) 2005 Ian Bicking and contributors; written for Paste (http://pythonpaste.org)
# Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php
"""
A file monitor and server restarter.
Use this like:
..code-block:: Python
import reloader
reloader.install()
Then make sure your server is installed with a shell script like::
err=3
while test "$err" -eq 3 ; do
python server.py
err="$?"
done
or is run from this .bat file (if you use Windows)::
@echo off
:repeat
python server.py
if %errorlevel% == 3 goto repeat
or run a monitoring process in Python (``paster serve --reload`` does
this). Use the watch_file(filename) function to cause a
reload/restart for other other non-Python files (e.g., configuration
files).
"""
import os
import sys
import time
import threading
from paste.util.classinstance import classinstancemethod
def install(poll_interval=1):
"""
Install the reloading monitor.
On some platforms server threads may not terminate when the main
thread does, causing ports to remain open/locked. The
``raise_keyboard_interrupt`` option creates a unignorable signal
which causes the whole application to shut-down (rudely).
"""
mon = Monitor(poll_interval=poll_interval)
t = threading.Thread(target=mon.periodic_reload)
t.setDaemon(True)
t.start()
class Monitor(object):
instances = []
global_extra_files = []
def __init__(self, poll_interval):
self.module_mtimes = {}
self.keep_running = True
self.poll_interval = poll_interval
self.extra_files = self.global_extra_files[:]
self.instances.append(self)
def periodic_reload(self):
while 1:
if not self.check_reload():
# use os._exit() here and not sys.exit() since within a
# thread sys.exit() just closes the given thread and
# won't kill the process; note os._exit does not call
# any atexit callbacks, nor does it do finally blocks,
# flush open files, etc. In otherwords, it is rude.
os._exit(3)
break
time.sleep(self.poll_interval)
def check_reload(self):
filenames = self.extra_files[:]
for module in sys.modules.values():
try:
filenames.append(module.__file__)
except AttributeError:
continue
for filename in filenames:
try:
stat = os.stat(filename)
if stat:
mtime = stat.st_mtime
else:
mtime = 0
except (OSError, IOError):
continue
if filename.endswith('.pyc') and os.path.exists(filename[:-1]):
mtime = max(os.stat(filename[:-1]).st_mtime, mtime)
if not self.module_mtimes.has_key(filename):
self.module_mtimes[filename] = mtime
elif self.module_mtimes[filename] < mtime:
print >> sys.stderr, (
"%s changed; reloading..." % filename)
return False
return True
def watch_file(self, cls, filename):
filename = os.path.abspath(filename)
if self is None:
for instance in cls.instances:
instance.watch_file(filename)
cls.global_extra_files.append(filename)
else:
self.extra_files.append(filename)
watch_file = classinstancemethod(watch_file)
watch_file = Monitor.watch_file
PK -68
a6 6 paste/fileapp.pyc;
#Gc @ s d Z d k Z d k Z d k Z d k Z d k Z d k Td k Td Z d d Z d d d g Z
d e f d YZ d e f d YZ
d
e f d YZ d e f d YZ d S(
s
This module handles sending static content such as in-memory data or
files. At this time it has cache helpers and understands the
if-modified-since request header.
N( s *i i s DataApps FileApps ArchiveStorec B sb t Z d Z d d f Z e e d Z d Z e d Z d Z d Z d Z
d Z RS(
s
Returns an application that will send content in a single chunk,
this application has support for setting cache-control and for
responding to conditional (or HEAD) requests.
Constructor Arguments:
``content`` the content being sent to the client
``headers`` the headers to send /w the response
The remaining ``kwargs`` correspond to headers, where the
underscore is replaced with a dash. These values are only
added to the headers if they are not already provided; thus,
they can be used for default values. Examples include, but
are not limited to:
``content_type``
``content_encoding``
``content_location``
``cache_control()``
This method provides validated construction of the ``Cache-Control``
header as well as providing for automated filling out of the
``EXPIRES`` header for HTTP/1.0 clients.
``set_content()``
This method provides a mechanism to set the content after the
application has been constructed. This method does things
like changing ``Last-Modified`` and ``Content-Length`` headers.
s GETs HEADc K s
t | t t t f p t t | _ t | _ t | _ d | _
| t j o
| | _ n | p g | _ x9 | i
D]+ \ } } t | } | i | i | q~ Wt i | i d t t | i o t i | i n | t j o | i | n d S( Ni s bytes( s
isinstances headerss types Nones lists AssertionErrors selfs expiress contents content_lengths
last_modifieds allowed_methodss kwargss itemss ks vs
get_headers headers updates
ACCEPT_RANGESs Trues CONTENT_TYPEs set_content( s selfs contents headerss allowed_methodss kwargss headers vs k( ( s1 build/bdist.darwin-8.0.1-x86/egg/paste/fileapp.pys __init__; s" #
c K s' t i | i | p t | _ | Sd S( N( s
CACHE_CONTROLs applys selfs headerss kwargss Nones expires( s selfs kwargs( ( s1 build/bdist.darwin-8.0.1-x86/egg/paste/fileapp.pys
cache_controlN s c C sv | t j p t | t j o t i | _ n
| | _ | | _ t | | _ t i | i
d | i | Sd S( Ns time( s contents Nones AssertionErrors
last_modifieds times selfs lens content_lengths
LAST_MODIFIEDs updates headers( s selfs contents
last_modified( ( s1 build/bdist.darwin-8.0.1-x86/egg/paste/fileapp.pys set_contentR s
c K s t i | i | | Sd S( N( s CONTENT_DISPOSITIONs applys selfs headerss kwargs( s selfs kwargs( ( s1 build/bdist.darwin-8.0.1-x86/egg/paste/fileapp.pys content_disposition] s c C sp | d i } | | i j o<