diff options
| author | ianb <devnull@localhost> | 2005-05-17 03:06:22 +0000 |
|---|---|---|
| committer | ianb <devnull@localhost> | 2005-05-17 03:06:22 +0000 |
| commit | ed51c888895583a856f40ce951c0c131a26f9ca3 (patch) | |
| tree | 8f56068ec37a20406eab36c1f4a86ff233a5ddb8 /paste | |
| parent | a3ce49f606ece7bfc639a7d27fa6a4c50b0b8b37 (diff) | |
| download | paste-ed51c888895583a856f40ce951c0c131a26f9ca3.tar.gz | |
A bunch of pylint-inspired changes; all minor code cleanup
Diffstat (limited to 'paste')
| -rw-r--r-- | paste/app_setup.py | 14 | ||||
| -rw-r--r-- | paste/cgitb_catcher.py | 11 | ||||
| -rw-r--r-- | paste/configmiddleware.py | 1 | ||||
| -rw-r--r-- | paste/errormiddleware.py | 23 | ||||
| -rw-r--r-- | paste/gzipper.py | 1 | ||||
| -rw-r--r-- | paste/httpexceptions.py | 2 | ||||
| -rw-r--r-- | paste/lint.py | 117 | ||||
| -rw-r--r-- | paste/pycgiwrapper.py | 9 | ||||
| -rw-r--r-- | paste/pyconfig.py | 14 | ||||
| -rw-r--r-- | paste/recursive.py | 10 | ||||
| -rw-r--r-- | paste/reloader.py | 21 | ||||
| -rwxr-xr-x | paste/server.py | 1 | ||||
| -rw-r--r-- | paste/tests/fixture.py | 21 | ||||
| -rw-r--r-- | paste/threaded.py | 436 | ||||
| -rw-r--r-- | paste/urlparser.py | 3 | ||||
| -rw-r--r-- | paste/util/threadedprint.py | 32 | ||||
| -rw-r--r-- | paste/wdg_validate.py | 32 | ||||
| -rw-r--r-- | paste/wsgilib.py | 10 |
18 files changed, 174 insertions, 584 deletions
diff --git a/paste/app_setup.py b/paste/app_setup.py index 007b9d5..a0b6047 100644 --- a/paste/app_setup.py +++ b/paste/app_setup.py @@ -49,7 +49,8 @@ def find_template_info(args): except ImportError, e: raise InvalidCommand( 'No template exists by the name %r (%s)' % (template_name, e)) - return (server_conf_fn, template_name, os.path.dirname(template_mod.__file__), template_mod) + return (server_conf_fn, template_name, + os.path.dirname(template_mod.__file__), template_mod) def find_template_option(args): copy = args[:] @@ -102,6 +103,7 @@ class CommandRunner(object): self.commands = {} self.command_aliases = {} self.register_standard_commands() + self.server_conf_fn = None def run(self, argv, template_name, template_dir, template_module, server_conf_fn): @@ -259,9 +261,11 @@ class CommandList(Command): if self.args and not fnmatch.fnmatch(name, self.args[0]): continue if not self.options.verbose: - print '%s: %s\n' % (name, self.template_description().splitlines()[0]) + print '%s: %s\n' % ( + name, self.template_description().splitlines()[0]) else: - return '%s: %s\n' % (self.name, self.template_description()) + return '%s: %s\n' % ( + self.name, self.template_description()) # @@: for verbosity >= 2 we should give lots of metadata any = True if not any: @@ -346,8 +350,8 @@ class CommandCreate(Command): template_options['absolute_parent'] = os.path.dirname( os.path.abspath(output_dir)) template_options['template_name'] = self.template_name - self.copy_dir(file_dir, output_dir, template_options, self.options.verbose, - self.options.simulate) + self.copy_dir(file_dir, output_dir, template_options, + self.options.verbose, self.options.simulate) def copy_dir(self, *args, **kw): copy_dir(*args, **kw) diff --git a/paste/cgitb_catcher.py b/paste/cgitb_catcher.py index 732f7e1..b417583 100644 --- a/paste/cgitb_catcher.py +++ b/paste/cgitb_catcher.py @@ -9,7 +9,6 @@ documentation for more: import cgitb from cStringIO import StringIO import sys -import traceback class DummyFile(object): pass @@ -24,7 +23,7 @@ def middleware(application, **kw): return started[0] try: - app_iter = application(environ, start_response) + app_iter = application(environ, detect_start_response) return catching_iter(app_iter) except: if not started: @@ -40,12 +39,12 @@ def middleware(application, **kw): hook(*sys.exc_info()) return [dummy_file.getvalue()] - def catching_iter(iter): - if not iter: + def catching_iter(app_iter): + if not app_iter: raise StopIteration try: - for v in iter: - yield iter + for v in app_iter: + yield v except: exc = sys.exc_info() dummy_file = StringIO() diff --git a/paste/configmiddleware.py b/paste/configmiddleware.py index bf4768a..6c64d6c 100644 --- a/paste/configmiddleware.py +++ b/paste/configmiddleware.py @@ -1,4 +1,3 @@ -from paste.util import threadinglocal from paste import CONFIG from paste import wsgilib diff --git a/paste/errormiddleware.py b/paste/errormiddleware.py index d5c2b02..f3ef73d 100644 --- a/paste/errormiddleware.py +++ b/paste/errormiddleware.py @@ -73,26 +73,25 @@ class ErrorMiddleware(object): start_response('500 Internal Server Error', [('content-type', 'text/html')]) # @@: it would be nice to deal with bad content types here - dummy_file = StringIO() response = self.exception_handler(sys.exc_info(), environ) return [response] - def catching_iter(self, iter, environ): + def catching_iter(self, app_iter, environ): __traceback_supplement__ = Supplement, self, environ - if not iter: + if not app_iter: raise StopIteration error_on_close = False try: - for v in iter: + for v in app_iter: yield v - if hasattr(iter, 'close'): + if hasattr(app_iter, 'close'): error_on_close = True - iter.close() + app_iter.close() except: response = self.exception_handler(sys.exc_info(), environ) - if not error_on_close and hasattr(iter, 'close'): + if not error_on_close and hasattr(app_iter, 'close'): try: - iter.close() + app_iter.close() except: close_response = self.exception_handler( sys.exc_info(), environ) @@ -200,9 +199,9 @@ def handle_exception(exc_info, conf, error_stream, html=True): error_stream.write(extra_data) return return_error -def send_report(reporter, exc_data, html=True): +def send_report(rep, exc_data, html=True): try: - reporter.report(exc_data) + rep.report(exc_data) except: output = StringIO() traceback.print_exc(file=output) @@ -212,11 +211,11 @@ def send_report(reporter, exc_data, html=True): <pre>%s</pre> </p>""" % ( - cgi.escape(str(reporter)), output.getvalue()) + cgi.escape(str(rep)), output.getvalue()) else: return ( "Additionally an error occurred while sending the " - "%s report:\n%s" % (str(reporter), output.getvalue())) + "%s report:\n%s" % (str(rep), output.getvalue())) else: return '' diff --git a/paste/gzipper.py b/paste/gzipper.py index 3bf42bb..50e9e48 100644 --- a/paste/gzipper.py +++ b/paste/gzipper.py @@ -5,7 +5,6 @@ Gzip-encodes the response. """ import gzip -from cStringIO import StringIO import wsgilib class GzipOutput(object): diff --git a/paste/httpexceptions.py b/paste/httpexceptions.py index 7540a17..01d68f5 100644 --- a/paste/httpexceptions.py +++ b/paste/httpexceptions.py @@ -218,7 +218,7 @@ def middleware(application): return start_response(status, headers, exc_info) try: - return application(environ, start_response) + return application(environ, checked_start_response) except HTTPException, e: if app_started: # They've already started the response, so we can't diff --git a/paste/lint.py b/paste/lint.py index 0eac637..a52a227 100644 --- a/paste/lint.py +++ b/paste/lint.py @@ -24,7 +24,8 @@ def middleware(application): start_response_started = [] def start_response_wrapper(*args, **kw): - assert len(args) == 2 or len(args) == 3, "Invalid number of arguments: %s" % args + assert len(args) == 2 or len(args) == 3, ( + "Invalid number of arguments: %s" % args) assert not kw, "No keyword arguments allowed" status = args[0] headers = args[1] @@ -47,8 +48,8 @@ def middleware(application): iterator = application(environ, start_response_wrapper) assert start_response_started, ( "The application returned, but did not call start_response()") - assert iterator is not None and iterator != False, \ - "The application must return an iterator, if only an empty list" + assert iterator is not None and iterator != False, ( + "The application must return an iterator, if only an empty list") check_iterator(iterator) @@ -121,7 +122,7 @@ class WriteWrapper: class PartialIteratorWrapper: def __init__(self, wsgi_iterator): - self.iterator = iterator + self.iterator = wsgi_iterator def __iter__(self): # We want to make sure __iter__ is called @@ -138,8 +139,8 @@ class IteratorWrapper: return self def next(self): - assert not self.closed, \ - "Iterator read after closed" + assert not self.closed, ( + "Iterator read after closed") return self.iterator.next() def close(self): @@ -149,90 +150,104 @@ class IteratorWrapper: def __del__(self): if not self.closed: - sys.stderr.write("Iterator garbage collected without being closed") - assert self.closed, \ - "Iterator garbage collected without being closed" + sys.stderr.write( + "Iterator garbage collected without being closed") + assert self.closed, ( + "Iterator garbage collected without being closed") def check_environ(environ): - assert type(environ) is DictType, \ - "Environment is not of the right type: %r (environment: %r)" % (type(environ), environ) + assert type(environ) is DictType, ( + "Environment is not of the right type: %r (environment: %r)" + % (type(environ), environ)) for key in ['REQUEST_METHOD', 'SERVER_NAME', 'SERVER_PORT', 'wsgi.version', 'wsgi.input', 'wsgi.errors', 'wsgi.multithread', 'wsgi.multiprocess', 'wsgi.run_once']: - assert environ.has_key(key), \ - "Environment missing required key: %r" % key + assert environ.has_key(key), ( + "Environment missing required key: %r" % key) for key in environ.keys(): if '.' in key: # Extension, we don't care about its type continue - assert type(environ[key]) is StringType, \ - "Environmental variable %s is not a string: %r (value: %r)" % (type(environ[key]), environ[key]) + assert type(environ[key]) is StringType, ( + "Environmental variable %s is not a string: %r (value: %r)" + % (type(environ[key]), environ[key])) - assert type(environ['wsgi.version']) is TupleType, \ - "wsgi.version should be a tuple (%r)" % environ['wsgi.version'] - assert environ['wsgi.url_scheme'] in ('http', 'https'), \ - "wsgi.url_scheme unknown: %r" % environ['wsgi.url_scheme'] + assert type(environ['wsgi.version']) is TupleType, ( + "wsgi.version should be a tuple (%r)" % environ['wsgi.version']) + assert environ['wsgi.url_scheme'] in ('http', 'https'), ( + "wsgi.url_scheme unknown: %r" % environ['wsgi.url_scheme']) check_input(environ['wsgi.input']) check_errors(environ['wsgi.errors']) # @@: these need filling out: - assert environ['REQUEST_METHOD'] in ('GET', 'HEAD', 'POST'), "Unknown REQUEST_METHOD: %r" % environ['REQUEST_METHOD'] + assert environ['REQUEST_METHOD'] in ('GET', 'HEAD', 'POST'), ( + "Unknown REQUEST_METHOD: %r" % environ['REQUEST_METHOD']) assert (not environ.get('SCRIPT_NAME') - or environ['SCRIPT_NAME'].startswith('/')), \ - "SCRIPT_NAME doesn't start with /: %r" % environ['SCRIPT_NAME'] + or environ['SCRIPT_NAME'].startswith('/')), ( + "SCRIPT_NAME doesn't start with /: %r" % environ['SCRIPT_NAME']) assert (not environ.get('PATH_INFO') - or environ['PATH_INFO'].startswith('/')), \ - "PATH_INFO doesn't start with /: %s" % environ['PATH_INFO'] + or environ['PATH_INFO'].startswith('/')), ( + "PATH_INFO doesn't start with /: %s" % environ['PATH_INFO']) if environ.get('CONTENT_LENGTH'): - assert int(environ['CONTENT_LENGTH']) >= 0, "Invalid CONTENT_LENGTH: %r" % environ['CONTENT_LENGTH'] + assert int(environ['CONTENT_LENGTH']) >= 0, ( + "Invalid CONTENT_LENGTH: %r" % environ['CONTENT_LENGTH']) if not environ.get('SCRIPT_NAME'): - assert environ.has_key('PATH_INFO'), \ - "One of SCRIPT_NAME or PATH_INFO are required (PATH_INFO should at least be '/' if SCRIPT_NAME is empty)" - assert environ.get('SCRIPT_NAME') != '/', \ - "SCRIPT_NAME cannot be '/'; it should instead be '', and PATH_INFO should be '/'" + assert environ.has_key('PATH_INFO'), ( + "One of SCRIPT_NAME or PATH_INFO are required (PATH_INFO " + "should at least be '/' if SCRIPT_NAME is empty)") + assert environ.get('SCRIPT_NAME') != '/', ( + "SCRIPT_NAME cannot be '/'; it should instead be '', and " + "PATH_INFO should be '/'") def check_input(wsgi_input): for attr in ['read', 'readline', 'readlines', '__iter__']: - assert hasattr(wsgi_input, attr), \ - "wsgi.input (%r) doesn't have the attribute %s" % (wsgi_input, attr) + assert hasattr(wsgi_input, attr), ( + "wsgi.input (%r) doesn't have the attribute %s" + % (wsgi_input, attr)) def check_errors(wsgi_errors): for attr in ['flush', 'write', 'writelines']: - assert hasattr(wsgi_errors, attr), \ - "wsgi.errors (%r) doesn't have the attributes %s" % (wsgi_errors, attr) + assert hasattr(wsgi_errors, attr), ( + "wsgi.errors (%r) doesn't have the attribute %s" + % (wsgi_errors, attr)) def check_status(status): - assert type(status) is StringType, \ - "Status must be a string (not %r)" % status + assert type(status) is StringType, ( + "Status must be a string (not %r)" % status) # Implicitly check that we can turn it into an integer: status_int = int(status.split(None, 1)[0]) assert status_int >= 100, "Status code is invalid: %r" % status_int def check_headers(headers): - assert type(headers) is ListType, \ - "Headers (%r) must be of type list: %r" % (headers, type(headers)) + assert type(headers) is ListType, ( + "Headers (%r) must be of type list: %r" + % (headers, type(headers))) header_names = {} for item in headers: - assert type(item) is TupleType, \ - "Individual headers (%r) must be of type tuple: %r" % (item, type(item)) + assert type(item) is TupleType, ( + "Individual headers (%r) must be of type tuple: %r" + % (item, type(item))) assert len(item) == 2 name, value = item - assert name.lower() != 'status', \ - "The Status header cannot be used; it conflicts with CGI script, and HTTP status is not given through headers (value: %r)." % value + assert name.lower() != 'status', ( + "The Status header cannot be used; it conflicts with CGI " + "script, and HTTP status is not given through headers " + "(value: %r)." % value) header_names[name.lower()] = None - assert '\n' not in name and ':' not in name, \ - "Header names may not contain ':' or '\\n': %r" % name + assert '\n' not in name and ':' not in name, ( + "Header names may not contain ':' or '\\n': %r" % name) assert header_re.search(name), "Bad header name: %r" % name - assert not name.endswith('-') and not name.endswith('_'), \ - "Names may not end in '-' or '_': %r" % name - assert not bad_header_value_re.search(value), \ - "Bad header value: %r (bad char: %r)" % (value, bad_header_value_re.search(value).group(0)) + assert not name.endswith('-') and not name.endswith('_'), ( + "Names may not end in '-' or '_': %r" % name) + assert not bad_header_value_re.search(value), ( + "Bad header value: %r (bad char: %r)" + % (value, bad_header_value_re.search(value).group(0))) def check_content_type(status, headers): code = int(status.split(None, 1)[0]) @@ -248,12 +263,14 @@ def check_content_type(status, headers): assert 0, "No Content-Type header found in headers (%s)" % headers def check_exc_info(exc_info): - assert not exc_info or type(exc_info) is type(()), "exc_info (%r) is not a tuple: %r" % (exc_info, type(exc_info)) + assert not exc_info or type(exc_info) is type(()), ( + "exc_info (%r) is not a tuple: %r" % (exc_info, type(exc_info))) # More exc_info checks? def check_iterator(iterator): # Technically a string is legal, which is why it's a really bad # idea, because it may cause the response to be returned # character-by-character - assert not isinstance(iterator, str), \ - "You should not return a string as your application iterator, instead return a single-item list containing that string." + assert not isinstance(iterator, str), ( + "You should not return a string as your application iterator, " + "instead return a single-item list containing that string.") diff --git a/paste/pycgiwrapper.py b/paste/pycgiwrapper.py index eca2102..f95ad50 100644 --- a/paste/pycgiwrapper.py +++ b/paste/pycgiwrapper.py @@ -66,7 +66,7 @@ class CGIWrapper(object): else: del message['status'] headers = message.items() - writer = start_response(status, headers) + start_response(status, headers) return [body] suffix_info = [t for t in imp.get_suffixes() if t[0] == '.py'][0] @@ -74,8 +74,8 @@ class CGIWrapper(object): def run_script(self): f = open(self.cgi_filename, self.suffix_info[1]) try: - mod = imp.load_module('__main__', f, self.cgi_filename, - self.suffix_info) + imp.load_module('__main__', f, self.cgi_filename, + self.suffix_info) except SystemExit: pass f.close() @@ -144,7 +144,8 @@ _real_FieldStorage = cgi.FieldStorage class FieldStorageWrapper(_real_FieldStorage): def __init__(self, fp=None, headers=None, outerboundary="", - environ=os.environ, keep_blank_values=0, strict_parsing=0): + environ=os.environ, keep_blank_values=0, + strict_parsing=0): if fp is None: # @@: Should I look for sys.stdin too? # Or should I be replacing sys.stdin entirely? diff --git a/paste/pyconfig.py b/paste/pyconfig.py index d798f6e..d0192c6 100644 --- a/paste/pyconfig.py +++ b/paste/pyconfig.py @@ -141,8 +141,13 @@ class Config(UserDict.DictMixin): self.namespaces.insert(0, d) def load_commandline(self, *args, **kw): + if 'default' in kw: + default = kw['default'] + del kw['default'] + else: + default = False options, args = parse_commandline(*args, **kw) - self.load_dict(options) + self.load_dict(options, default=default) return args def update_sys_path(self): @@ -181,7 +186,7 @@ def update_sys_path(paths, verbose): print 'Adding %s to path' % path sys.path.insert(0, path) -def parse_commandline(items, bool_options, aliases={}, default=False): +def parse_commandline(items, bool_options, aliases={}): """ Parses options from the command line. bool_options take no arguments, everything else is supposed to take arguments. aliases @@ -268,8 +273,7 @@ def convert_commandline(value): try: return int(value) except ValueError: - pass - return value + return value class DispatchingConfig(object): @@ -342,7 +346,7 @@ class DispatchingConfig(object): self._process_configs.append(conf) def pop_process_config(self, conf=None): - self._pop_from(self._process_config, conf) + self._pop_from(self._process_configs, conf) def __getattr__(self, attr): conf = self.current_conf() diff --git a/paste/recursive.py b/paste/recursive.py index 5c370b8..68bc380 100644 --- a/paste/recursive.py +++ b/paste/recursive.py @@ -50,7 +50,9 @@ class Recursive(object): environ['paste.recursive.previous_environ'] = self.previous_environ base_path = self.original_environ.get('SCRIPT_NAME') if path.startswith('/'): - assert path.startswith(base_path), "You can only forward requests to resources under the path %r (not %r)" % (base_path, path) + assert path.startswith(base_path), ( + "You can only forward requests to resources under the " + "path %r (not %r)" % (base_path, path)) path = path[len(base_path)+1:] assert not path.startswith('/') path_info = '/' + path @@ -66,13 +68,11 @@ class Recursive(object): class Forwarder(Recursive): def activate(self, environ): - environ['wsgi.errors'].write('Forwarding to %r\n' % (environ['SCRIPT_NAME'] + environ['PATH_INFO'])) return self.application(environ, self.start_response) class Includer(Recursive): def activate(self, environ): - environ['wsgi.errors'].write('Including %r\n' % (environ['SCRIPT_NAME'] + environ['PATH_INFO'])) response = IncludedResponse def start_response(status, headers, exc_info=None): if exc_info: @@ -104,7 +104,9 @@ class IncludedResponse(object): self.output = None def write(self): - assert self.output is not None, "This response has already been closed and no further data can be written." + assert self.output is not None, ( + "This response has already been closed and no further data " + "can be written.") self.output.write() def __str__(self): diff --git a/paste/reloader.py b/paste/reloader.py index 72148e1..f2d75ba 100644 --- a/paste/reloader.py +++ b/paste/reloader.py @@ -63,7 +63,7 @@ class Monitor: def check_reload(self): filenames = self.extra_files[:] - for name, module in sys.modules.items(): + for module in sys.modules.values(): try: filenames.append(module.__file__) except AttributeError: @@ -95,22 +95,3 @@ class Monitor: watch_file = classinstancemethod(watch_file) watch_file = Monitor.watch_file - -def awake_select(addrs): - """ - When using socket.select(), processes will not quit. By connecting - to the addresses locally we move the code out of the select, where - it can terminate properly. - - (For now, KeyboardInterrupt seems to terminate these selects - properly) - """ - - for addr in addrs: - sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - try: - sock.connect(addr) - sock.close() - except: - pass - diff --git a/paste/server.py b/paste/server.py index 0d147ec..b018384 100755 --- a/paste/server.py +++ b/paste/server.py @@ -50,7 +50,6 @@ if os.path.dirname(here) != paste_path: 'out of %s\n' % (here, paste_path)) from paste import reloader -from paste import wsgilib from paste import CONFIG from paste.util import plugin from paste import pyconfig diff --git a/paste/tests/fixture.py b/paste/tests/fixture.py index a6245e2..45b22a1 100644 --- a/paste/tests/fixture.py +++ b/paste/tests/fixture.py @@ -5,14 +5,13 @@ import mimetypes import time import cgi import os -import warnings import webbrowser +import smtplib from Cookie import SimpleCookie try: from cStringIO import StringIO except ImportError: from StringIO import StringIO -import types import re #from py.test.collect import Module, PyCollector from paste.util import thirdparty @@ -225,7 +224,8 @@ class FakeFile(object): def open(self, mode): if mode == 'r' or mode == 'rb': if self.content is None: - raise IOError("[FakeFS] No such file or directory: %r" % filename) + raise IOError("[FakeFS] No such file or directory: %r" + % self.filename) return ReaderFile(self) elif mode == 'w' or mode == 'wb': return WriterFile(self) @@ -234,8 +234,8 @@ class FakeFile(object): class ReaderFile(object): - def __init__(self, file): - self.file = file + def __init__(self, fp): + self.file = fp self.stream = StringIO(self.file.content) self.open = True @@ -249,8 +249,8 @@ class ReaderFile(object): class WriterFile(object): - def __init__(self, file): - self.file = file + def __init__(self, fp): + self.file = fp self.stream = StringIO() self.open = True @@ -317,6 +317,8 @@ class TestApp(object): environ['CONTENT_LENGTH'] = str(len(params)) environ['REQUEST_METHOD'] = 'POST' environ['wsgi.input'] = StringIO(params) + for header, value in headers.items(): + environ['HTTP_%s' % header.replace('-', '_').upper()] = value req = TestRequest(url, environ) return self.do_request(req, status=status) @@ -353,8 +355,9 @@ class TestApp(object): if len(file_info) == 2: # It only has a filename filename = file_info[2] - if self.conf.get('test_file_path'): - filename = os.path.join(self.conf['test_file_path'], filename) + if self.config.get('test_file_path'): + filename = os.path.join(self.config['test_file_path'], + filename) f = open(filename, 'rb') content = f.read() f.close() diff --git a/paste/threaded.py b/paste/threaded.py deleted file mode 100644 index 6abbd32..0000000 --- a/paste/threaded.py +++ /dev/null @@ -1,436 +0,0 @@ -# Note, this is totally incomplete and untested -import threading -import marshal -import Queue -import select -import socket -import errno -import logging -import atexit -import BaseHTTPServer - -__version__ = '0.1' - -logger = logging.getLogger('wsgiserver.threaded') -errorLog = logging.getLogger('wsgiserver.apperrors') - -intLength = len(marshal.dumps(int(1))) - -server = None - -class NotEnoughDataError(Exception): - pass - -class ProtocolError(Exception): - pass - -class ThreadedWSGIServer(object): - - def __init__(self, application): - self.application = application - threadCount = self.setting('StartServerThreads') - self._maxServerThreads = self.setting('MaxServerThreads') - self._minServerThreads = self.setting('MinServerThreads') - self._threadPool = [] - self._threadCount = 0 - self._threadUseCounter = [] - self._requestQueue = Queue.Queue(self._maxServerThreads * 2) - self._addr = {} - # @@: Should load persistently - self._requestID = 0 - - logger.info('Creating %i threads' % threadCount) - for i in range(threadCount): - self.spawnThread() - - #self.recordPID() @@: ? - - self._socketHandlers = {} - self._handlerCache = {} - self._sockets = {} - - self.addSocketHandlers() - self.running = True - atexit.register(self.awakeSelect) - atexit.register(self.shutdown) - self.readyForRequests() - - def addSocketHandler(self, handlerClass, serverAddress=None): - """ - Adds a socket handler for `serverAddress`, which is typically - a tuple ``(host, port)``. - """ - if serverAddress is None: - serverAddress = self.address(handlerClass.defaultServerAddress()) - self._socketHandlers[serverAddress] = handlerClass - self._handlerCache[serverAddress] = [] - sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) - sock.bind(serverAddress) - sock.listen(1024) - self._sockets[serverAddress] = sock - logger.info('Listening on: %s' % serverAddress) - - def readyForRequests(self): - logger.info('Ready for requests') - - def spawnThread(self): - """ - Create a new worker thread; threads run the `threadLoop` - method. - """ - t = threading.Thread(target=self.threadLoop) - t.processing = False - self._threadPool.append(t) - self._threadCount += 1 - t.start() - logger.info('New thread spawned, threadcount=%s' % - self._threadCount) - - def absorbThread(self, count=1): - """ - Absorb a thread. - """ - for i in range(count): - self._requestQueue.put(None) - self._threadCount -= 1 - for t in self._threadPool: - if not t.isAlive(): - rv = i.join() - self._threadPool.remove(i) - logger.info('Thread absorbed, threadcount=%s' % - len(self.threadPool)) - - def threadLoop(self): - self.initThread() - t = threading.currentThread() - t.processing = False - try: - while 1: - try: - handler = self._requestQueue.get() - # Non means time to quit - if handler is None: - break - t.processing = True - try: - handler.handleRequest() - except: - logger.exception() - handler.close() - t.processing = False - except Queue.Empty: - pass - finally: - self.delThread() - - def initThread(self): - pass - - def delThread(self): - pass - - def awakeSelect(self): - """ - The ``select()`` in `mainloop` is blocking, so when - we shut down we have to make a connect to unblock it. - Here's where we do that, called `shutDown`. - """ - - for addr in self._sockets.keys(): - sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - try: - sock.connect(addr) - sock.close() - except: - pass - - def shutdown(self): - self.running = False - - def run(self, timeout=1): - while 1: - if not self.running: - return - - try: - input, output, exc = select.select( - self._sockets.values(), [], [], timeout) - except select.error, v: - if v[0] == errno.EINTR or v[0] == 0: - break - else: - raise - - for sock in input: - self._requestID += 1 - client, addr = sock.accept() - serverAddress = sock.getsockname() - try: - handler = self._handlerCache[serverAddress].pop() - except IndexError: - handler = self._socketHandlers[serverAddress](self, serverAddress) - handler.activate(client, self._requestID) - self._requestQueue.put(handler) - -class Handler(object): - - def __init__(self, server, serverAddress): - self.server = server - self.serverAddress = serverAddress - - def active(self, sock, requestID): - self.requestID = requestID - self.sock = sock - - def close(self): - self.sock = None - self.server._handlerCache[self._serverAddress].append(self) - - def handleRequest(self): - raise NotImplementedError - -class ModWebKitHandler(Handler): - - protocolName = 'webkit' - - def receiveDict(self): - """ - Utility function to receive a marshalled dictionary from - the socket. Returns None if the request was empty. - """ - chunk = '' - missing = intLength - while missing > 0: - block = self.sock.recv(missing) - if not block: - self.sock.close() - if len(chunk) == 0: - # We probably awakened due to awakeSelect being called. - return None - else: - # We got a partial request -- something went wrong. - raise NotEnoughDataError, 'received only %d of %d bytes when receiving dictLength' % (len(chunk), intLength) - chunk += block - missing = intLength - len(chunk) - try: - dictLength = loads(chunk) - except ValueError: - logger.warn('bad marshal data for webkit adapter interface; ' - 'you can only connect to %s via an adapter, like ' - 'mod_webkit or wkcgi, not with a browser' - % self._serverAddress[1]) - raise - if type(dictLength) != type(1): - self.sock.close() - raise ProtocolError, "Invalid AppServer protocol" - chunk = '' - missing = dictLength - while missing > 0: - block = self.sock.recv(missing) - if not block: - self.sock.close() - raise NotEnoughDataError, 'received only %d of %d bytes when receiving dict' % (len(chunk), dictLength) - chunk += block - missing = dictLength - len(chunk) - return loads(chunk) - - def defaultServerAddress(cls): - return ('127.0.0.1', 8086) - defaultServerAddress = classmethod(defaultServerAddress) - - def handleRequest(self): - data = [] - environ = self.receiveDict() - if not environ: - return - if environ.get('REQUEST_URI'): - requestURI = environ['REQUEST_URI'] - else: - requestURI = environ.get('SCRIPT_NAME', '') + environ.get('PATH_INFO', '') - query = environ.get('QUERY_STRING') - if query: - requestURI += '?' + query - environ['wsgi.input'] = self.sock.makefile('rb', 8012) - environ['wsgi.errors'] = LoggingError(errorLog) - environ['wsgi.version'] = '1.0' - environ['wsgi.multithread'] = True - environ['wsgi.multiprocess'] = False - output = WebKitStreamOut(self.sock) - - def start(status, headers): - output.write('Status: %s\n' % status) - for header, value in headers.items(): - assert '\n' not in value and '\r' not in value, \ - "Headers cannot contain newlines (%s: %r)" \ - % (header, value) - assert ':' not in header, \ - "Headers should not container ':' (%r)" % header - output.write('%s: %s\n' % (key, value)) - return output.write - - try: - result = self.server.application(environ, start) - if result: - try: - for data in result: - output.write(data) - finally: - if hasattr(result, 'close'): - result.close() - except: - errorLog.exception() - - output.close() - try: - self.sock.shutdown(1) - self.sock.close() - except: - # @@: Why the except:? - pass - -class LoggingError(object): - - def __init__(self, logger): - self.logger = logger - - def flush(self): - pass - - def write(self, s): - self.logger.error(s) - - def writelines(self, seq): - for s in seq: - self.write(s) - -class WebKitStreamOut(object): - - def __init__(self, sock): - self.sock = sock - - def write(self, s): - self.sock.send(s) - -############################################################ -## HTTP -############################################################ - - -class HTTPHandler(BaseHTTPServer.BaseHTTPRequestHandler): - """Handles incoming requests. Recreated with every request. - Abstract base class. - """ - - ## This sends certain CGI variables. These are some that - ## should be sent, but aren't: - ## SERVER_ADDR - ## SERVER_PORT - ## SERVER_SOFTWARE - ## SERVER_NAME - ## HTTP_CONNECTION - ## SERVER_PROTOCOL - ## HTTP_KEEP_ALIVE - - ## These I don't think are needed: - ## DOCUMENT_ROOT - ## PATH_TRANSLATED - ## GATEWAY_INTERFACE - ## PATH - ## SERVER_SIGNATURE - ## SCRIPT_FILENAME (?) - ## SERVER_ADMIN (?) - - server_version = 'WSGIServer/%s' % __version__ - - def handleRequest(self): - """ - Actually performs the request, creating the environment and - calling self.doTransaction(env, myInput) to perform the - response. - """ - self.server_version = 'Webware/0.1' - env = {} - if self.headers.has_key('Content-Type'): - env['CONTENT_TYPE'] = self.headers['Content-Type'] - del self.headers['Content-Type'] - self.headersToEnviron(self.headers, env) - env['REMOTE_ADDR'], env['REMOTE_PORT'] = map(str, self.client_address) - env['REQUEST_METHOD'] = self.command - path = self.path - if path.find('?') != -1: - # @@: should REQUEST_URI include QUERY_STRING? - env['REQUEST_URI'], env['QUERY_STRING'] = path.split('?', 1) - else: - env['REQUEST_URI'] = path - env['QUERY_STRING'] = '' - env['PATH_INFO'] = env['REQUEST_URI'] - env['SCRIPT_NAME'] = '' - myInput = '' - if self.headers.has_key('Content-Length'): - myInput = self.rfile.read(int(self.headers['Content-Length'])) - self.doTransaction(env, myInput) - - do_GET = do_POST = do_HEAD = handleRequest - # These methods are used in WebDAV requests: - do_OPTIONS = do_PUT = do_DELETE = handleRequest - do_MKCOL = do_COPY = do_MOVE = handleRequest - do_PROPFIND = handleRequest - - def headersToEnviron(self, headers, env): - """Use a simple heuristic to convert all the headers to - environmental variables...""" - for header, value in headers.items(): - env['HTTP_%s' % (header.upper().replace('-', '_'))] = value - return env - - def processResponse(self, data): - """ - Takes a string (like what a CGI script would print) and - sends the actual HTTP response (response code, headers, body). - """ - s = StringIO(data) - headers = mimetools.Message(s) - self.sendStatus(headers) - self.sendHeaders(headers) - self.sendBody(s) - - def sendStatus(self, status): - status = str(status) - pos = status.find(' ') - if pos == -1: - code = int(status) - message = '' - else: - code = int(status[:pos]) - message = status[pos:].strip() - self.send_response(code, message) - - def sendHeaders(self, headers): - for header, value in headers.items(): - self.send_header(header, value) - self.end_headers() - - def sendBody(self, bodyFile): - self.wfile.write(bodyFile.read()) - bodyFile.close() - - def log_message(self, format, *args): - self.server.logMessage(format % args) - - def log_request(self, *args, **kw): - pass - - -class HTTPHandler(BaseHTTPServer.BaseHTTPRequestHandler): - - protocolName = 'http' - - def defaultServerAddress(cls): - # @@: 127.0.0.1 isn't very useful - return ('127.0.0.1', 80) - defaultServerAddress = classmethod(defaultServerAddress) - - def handleRequest(self): - baseHandler = BaseHTTPHandler(req, None, self) - diff --git a/paste/urlparser.py b/paste/urlparser.py index 89a1fca..4e2eaf1 100644 --- a/paste/urlparser.py +++ b/paste/urlparser.py @@ -175,7 +175,8 @@ class URLParser(object): environ, status, ''' - <p>The resource has moved to <a href="%s">%s</a>. You should be redirected automatically.</p>''' % (url, url)) + <p>The resource has moved to <a href="%s">%s</a>. You + should be redirected automatically.</p>''' % (url, url)) start_response(status, headers + [('Location', url)]) return [body] diff --git a/paste/util/threadedprint.py b/paste/util/threadedprint.py index a9ff7fd..eb57a01 100644 --- a/paste/util/threadedprint.py +++ b/paste/util/threadedprint.py @@ -65,8 +65,8 @@ class PrintCatcher(filemixin.FileMixin): def __init__(self, default=None, factory=None, paramwriter=None): assert len(filter(lambda x: x is not None, - [default, factory, paramwriter])) <= 1, \ - "You can only provide one of default, factory, or paramwriter" + [default, factory, paramwriter])) <= 1, ( + "You can only provide one of default, factory, or paramwriter") if default: self._defaultfunc = self._writedefault elif factory: @@ -99,7 +99,9 @@ class PrintCatcher(filemixin.FileMixin): self._paramwriter(name, v) def _writeerror(self, name, v): - assert 0, "There is no PrintCatcher output stream for the thread %r" % name + assert False, ( + "There is no PrintCatcher output stream for the thread %r" + % name) def register(self, catcher, name=None, currentThread=threading.currentThread): @@ -111,7 +113,8 @@ class PrintCatcher(filemixin.FileMixin): currentThread=threading.currentThread): if name is None: name = currentThread().getName() - assert self._catchers.has_key(name), "There is no PrintCatcher catcher for the thread %r" % name + assert self._catchers.has_key(name), ( + "There is no PrintCatcher catcher for the thread %r" % name) del self._catchers[name] _printcatcher = None @@ -134,7 +137,9 @@ def uninstall(): deregister = not_installed_error def not_installed_error(*args, **kw): - assert 0, "threadedprint has not yet been installed (call threadedprint.install())" + assert False, ( + "threadedprint has not yet been installed (call " + "threadedprint.install())") register = deregister = not_installed_error @@ -142,8 +147,8 @@ class StdinCatcher(filemixin.FileMixin): def __init__(self, default=None, factory=None, paramwriter=None): assert len(filter(lambda x: x is not None, - [default, factory, paramwriter])) <= 1, \ - "You can only provide one of default, factory, or paramwriter" + [default, factory, paramwriter])) <= 1, ( + "You can only provide one of default, factory, or paramwriter") if default: self._defaultfunc = self._readdefault elif factory: @@ -161,7 +166,7 @@ class StdinCatcher(filemixin.FileMixin): name = currentThread().getName() catchers = self._catchers if not catchers.has_key(name): - self._defaultfunc(name, v, size) + self._defaultfunc(name, size) else: catcher = catchers[name] catcher.read(size) @@ -176,7 +181,9 @@ class StdinCatcher(filemixin.FileMixin): self._paramreader(name, size) def _readerror(self, name, size): - assert 0, "There is no StdinCatcher output stream for the thread %r" % name + assert False, ( + "There is no StdinCatcher output stream for the thread %r" + % name) def register(self, catcher, name=None, currentThread=threading.currentThread): @@ -188,7 +195,8 @@ class StdinCatcher(filemixin.FileMixin): currentThread=threading.currentThread): if name is None: name = currentThread().getName() - assert self._catchers.has_key(name), "There is no StdinCatcher catcher for the thread %r" % name + assert self._catchers.has_key(name), ( + "There is no StdinCatcher catcher for the thread %r" % name) del self._catchers[name] _stdincatcher = None @@ -210,4 +218,6 @@ def uninstall(): register_stdin = deregister_stdin = not_installed_error_stdin def not_installed_error_stdin(*args, **kw): - assert 0, "threadedprint has not yet been installed for stdin (call threadedprint.install_stdin())" + assert False, ( + "threadedprint has not yet been installed for stdin (call " + "threadedprint.install_stdin())") diff --git a/paste/wdg_validate.py b/paste/wdg_validate.py index a7903d1..ad8ee46 100644 --- a/paste/wdg_validate.py +++ b/paste/wdg_validate.py @@ -27,10 +27,12 @@ class WDGValidateMiddleware(object): def __call__(self, environ, start_response): output = StringIO() response = [] + def writer_start_response(status, headers, exc_info=None): response.extend((status, headers)) start_response(status, headers, exc_info) return output.write + app_iter = self.app(environ, writer_start_response) try: for s in app_iter: @@ -51,7 +53,13 @@ class WDGValidateMiddleware(object): ops.append('--xml') # @@: Should capture encoding too conf = environ['paste.config'] - wdg_path = conf.get('wdg_path', 'validate') + html_errors = self.call_wdg_validate( + conf.get('wdg_path', 'validate'), ops, page) + if not html_errors: + return [page] + return self.add_error(page, html_errors) + + def call_wdg_validate(self, wdg_path, ops, page): proc = subprocess.Popen([wdg_path] + ops, shell=False, close_fds=True, @@ -60,16 +68,16 @@ class WDGValidateMiddleware(object): stderr=subprocess.STDOUT) stdout = proc.communicate(page)[0] proc.wait() - if not stdout: - return [page] - add_text = '<pre style="background-color: #ffd; color: #600; border: 1px solid #000;">%s</pre>' % cgi.escape(stdout) - match = self._end_body_regex.search(page) + return stdout + + def add_error(self, html_page, html_errors): + add_text = ('<pre style="background-color: #ffd; color: #600; ' + 'border: 1px solid #000;">%s</pre>' + % cgi.escape(html_errors)) + match = self._end_body_regex.search(html_page) if match: - page = page[:match.start()] + add_text + page[match.end():] + return [html_page[:match.start()] + + add_text + + html_page[match.end():]] else: - page += add_text - return [page] - - - - + return [html_page + add_text] diff --git a/paste/wsgilib.py b/paste/wsgilib.py index 3bf5a6b..9537e9d 100644 --- a/paste/wsgilib.py +++ b/paste/wsgilib.py @@ -118,10 +118,10 @@ def construct_url(environ, with_query_string=True, with_path_info=True): if environ['wsgi.url_scheme'] == 'https': if environ['SERVER_PORT'] != '443': - url += ':' + environ['SERVER_PORT'] + url += ':' + environ['SERVER_PORT'] else: if environ['SERVER_PORT'] != '80': - url += ':' + environ['SERVER_PORT'] + url += ':' + environ['SERVER_PORT'] url += environ.get('SCRIPT_NAME','') if with_path_info: @@ -197,8 +197,8 @@ def send_file(filename): class _FileIter: - def __init__(self, file, blocksize=4096): - self.file = file + def __init__(self, fp, blocksize=4096): + self.file = fp self.blocksize = blocksize def __iter__(self): @@ -223,7 +223,7 @@ def has_header(headers, name): return True return False -def header_value(headers, name, collapse=False): +def header_value(headers, name): """ Returns the header's value, or None if no such header. If a header appears more than once, all the values of the headers |
