diff options
author | Robert Brewer <fumanchu@aminus.org> | 2006-08-25 21:25:56 +0000 |
---|---|---|
committer | Robert Brewer <fumanchu@aminus.org> | 2006-08-25 21:25:56 +0000 |
commit | a4b0f89d3adff5ce7931d81dc259fed0e9f091f5 (patch) | |
tree | fda90c2e4664674aa3d0724beed3b543a824c8ff | |
parent | 31bfe036ad8ee2d7222c67deb7b2019a878c239c (diff) | |
download | cherrypy-git-a4b0f89d3adff5ce7931d81dc259fed0e9f091f5.tar.gz |
More (final?) config overhaul work:
1. Removed cherrypy.config.get! Instead, you should directly inspect cherrypy.request, response, server, etc. Note that request.config.get still works fine.
2. a) cherrypy.log is now an instance of LogManager. It's still callable, but now is the object you inspect instead of calling config.get("log*"). b) cherrypy.log_access is now cherrypy.log.access.
3. All threads should now have access to default Request and Response objects, not just the main thread.
4. deadlock.timeout is now request.timeout.
5. Renamed config.py to _cpconfig.py; cherrypy.config is now an instance of _cpconfig.Config.
I still need to decide what to do about log_config (removed for now).
-rw-r--r-- | cherrypy/__init__.py | 138 | ||||
-rw-r--r-- | cherrypy/_cpconfig.py (renamed from cherrypy/config.py) | 167 | ||||
-rw-r--r-- | cherrypy/_cpengine.py | 17 | ||||
-rw-r--r-- | cherrypy/_cperror.py | 2 | ||||
-rw-r--r-- | cherrypy/_cprequest.py | 22 | ||||
-rw-r--r-- | cherrypy/_cpserver.py | 33 | ||||
-rw-r--r-- | cherrypy/_cptree.py | 17 | ||||
-rw-r--r-- | cherrypy/_cpwsgi.py | 23 | ||||
-rw-r--r-- | cherrypy/lib/caching.py | 4 | ||||
-rw-r--r-- | cherrypy/lib/sessions.py | 5 | ||||
-rw-r--r-- | cherrypy/test/benchmark.py | 10 | ||||
-rw-r--r-- | cherrypy/test/helper.py | 2 | ||||
-rw-r--r-- | cherrypy/test/test.py | 2 | ||||
-rw-r--r-- | cherrypy/test/test_caching.py | 8 | ||||
-rw-r--r-- | cherrypy/test/test_config.py | 34 | ||||
-rw-r--r-- | cherrypy/test/test_conn.py | 12 | ||||
-rw-r--r-- | cherrypy/test/test_core.py | 18 | ||||
-rw-r--r-- | cherrypy/test/test_objectmapping.py | 2 | ||||
-rw-r--r-- | cherrypy/test/test_states.py | 8 | ||||
-rw-r--r-- | cherrypy/test/test_tools.py | 2 | ||||
-rw-r--r-- | cherrypy/test/test_tutorials.py | 2 | ||||
-rw-r--r-- | cherrypy/tutorial/tut10_http_errors.py | 4 |
22 files changed, 273 insertions, 259 deletions
diff --git a/cherrypy/__init__.py b/cherrypy/__init__.py index 40b60e64..63ba4fc1 100644 --- a/cherrypy/__init__.py +++ b/cherrypy/__init__.py @@ -3,10 +3,12 @@ __version__ = '3.0.0alpha' import logging as _logging +import os as _os +_localdir = _os.path.dirname(__file__) from cherrypy._cperror import HTTPError, HTTPRedirect, InternalRedirect, NotFound from cherrypy._cperror import WrongConfigValue, TimeoutError -from cherrypy import config +error_page = {} from cherrypy import _cptools tools = _cptools.default_toolbox @@ -36,55 +38,56 @@ except ImportError: # in a thread-safe way. serving = _local() -# Bind dummy instances of default request/response -# (in the main thread only!) to help introspection. -serving.request = _cprequest.Request("localhost", "11111", "localhost") -serving.response = _cprequest.Response() - class _ThreadLocalProxy(object): - __slots__ = ['__attrname__', '__dict__'] + __slots__ = ['__attrname__', '_default_child', '__dict__'] - def __init__(self, attrname): + def __init__(self, attrname, default): self.__attrname__ = attrname + self._default_child = default + + def _get_child(self): + try: + return getattr(serving, self.__attrname__) + except AttributeError: + # Bind dummy instances of default objects to help introspection. + return self._default_child def __getattr__(self, name): - childobject = getattr(serving, self.__attrname__) - return getattr(childobject, name) + return getattr(self._get_child(), name) def __setattr__(self, name, value): - if name == "__attrname__": - object.__setattr__(self, "__attrname__", value) + if name in ("__attrname__", "_default_child"): + object.__setattr__(self, name, value) else: - childobject = getattr(serving, self.__attrname__) - setattr(childobject, name, value) + setattr(self._get_child(), name, value) def __delattr__(self, name): - childobject = getattr(serving, self.__attrname__) - delattr(childobject, name) + delattr(self._get_child(), name) def _get_dict(self): - childobject = getattr(serving, self.__attrname__) + childobject = self._get_child() d = childobject.__class__.__dict__.copy() d.update(childobject.__dict__) return d __dict__ = property(_get_dict) def __getitem__(self, key): - childobject = getattr(serving, self.__attrname__) - return childobject[key] + return self._get_child()[key] def __setitem__(self, key, value): - childobject = getattr(serving, self.__attrname__) - childobject[key] = value + self._get_child()[key] = value # Create request and response object (the same objects will be used # throughout the entire life of the webserver, but will redirect # to the "serving" object) -request = _ThreadLocalProxy('request') -response = _ThreadLocalProxy('response') +from cherrypy.lib import http as _http +request = _ThreadLocalProxy('request', + _cprequest.Request(_http.Host("localhost", 80), + _http.Host("localhost", 1111))) +response = _ThreadLocalProxy('response', _cprequest.Response()) # Create thread_data object as a thread-specific all-purpose storage thread_data = _local() @@ -102,56 +105,59 @@ def logtime(): return '%02d/%s/%04d:%02d:%02d:%02d' % ( now.day, month, now.year, now.hour, now.minute, now.second) -def log_access(): - """Default method for logging access""" - tmpl = '%(h)s %(l)s %(u)s [%(t)s] "%(r)s" %(s)s %(b)s "%(f)s" "%(a)s"' - s = tmpl % {'h': request.remote.name or request.remote.ip, - 'l': '-', - 'u': getattr(request, "login", None) or "-", - 't': logtime(), - 'r': request.request_line, - 's': response.status.split(" ", 1)[0], - 'b': response.headers.get('Content-Length', '') or "-", - 'f': request.headers.get('referer', ''), - 'a': request.headers.get('user-agent', ''), - } - try: - request.app.access_log.log(_logging.INFO, s) - except: - log(traceback=True) - _error_log = _logging.getLogger("cherrypy.error") _error_log.setLevel(_logging.DEBUG) - _access_log = _logging.getLogger("cherrypy.access") _access_log.setLevel(_logging.INFO) -def _log_message(msg, context = '', severity = _logging.DEBUG): - """Default method for logging messages (error log). + +class LogManager(object): - This is not just for errors! Applications may call this at any time to - log application-specific information. - """ + screen = True + error_file = _os.path.join(_os.getcwd(), _localdir, "error.log") + # Using an access file makes CP about 10% slower. + access_file = '' - try: - log = request.app.error_log - except AttributeError: - log = _error_log - log.log(severity, ' '.join((logtime(), context, msg))) - -def log(msg='', context='', severity=_logging.DEBUG, traceback=False): - """Syntactic sugar for writing to the (error) log. + def error(self, msg='', context='', severity=_logging.DEBUG, traceback=False): + """Write to the 'error' log. + + This is not just for errors! Applications may call this at any time + to log application-specific information. + """ + if traceback: + from cherrypy import _cperror + msg += _cperror.format_exc() + + try: + elog = request.app.error_log + except AttributeError: + elog = _error_log + elog.log(severity, ' '.join((logtime(), context, msg))) - This is not just for errors! Applications may call this at any time to - log application-specific information. - """ - if traceback: - from cherrypy import _cperror - msg += _cperror.format_exc() - logfunc = config.get('log.error.function', _log_message) - logfunc(msg, context, severity) - + def __call__(self, *args, **kwargs): + return self.error(*args, **kwargs) + + def access(self): + """Default method for logging access""" + tmpl = '%(h)s %(l)s %(u)s [%(t)s] "%(r)s" %(s)s %(b)s "%(f)s" "%(a)s"' + s = tmpl % {'h': request.remote.name or request.remote.ip, + 'l': '-', + 'u': getattr(request, "login", None) or "-", + 't': logtime(), + 'r': request.request_line, + 's': response.status.split(" ", 1)[0], + 'b': response.headers.get('Content-Length', '') or "-", + 'f': request.headers.get('referer', ''), + 'a': request.headers.get('user-agent', ''), + } + try: + request.app.access_log.log(_logging.INFO, s) + except: + self.error(traceback=True) + + +log = LogManager() # Helper functions for CP apps # @@ -229,3 +235,7 @@ def expose(func=None, alias=None): alias = func return expose_ + +# Set up config last so it can wrap other top-level objects +from cherrypy import _cpconfig +config = _cpconfig.Config() diff --git a/cherrypy/config.py b/cherrypy/_cpconfig.py index 01eb21a8..a317c85f 100644 --- a/cherrypy/config.py +++ b/cherrypy/_cpconfig.py @@ -28,8 +28,8 @@ and configuration data may apply to any of those three scopes: page handler (see next). -Usage ------ +Declaration +----------- Configuration data may be supplied as a Python dictionary, as a filename, or as an open file object. When you supply a filename or file, CherryPy @@ -60,12 +60,11 @@ Namespaces Configuration keys are separated into namespaces by the first "." in the key. Current namespaces: - autoreload: Controls the autoreload mechanism in cherrypy.engine. - These can only be declared in the global config. - deadlock: Controls the deadlock monitoring system in cherrypy.engine. + engine: Controls the 'application engine', including autoreload. These can only be declared in the global config. hooks: Declares additional request-processing functions. log: Configures the logging for each application. + These can only be declared in the global or / config. request: Adds attributes to each Request during the tool_up phase. response: Adds attributes to each Response during the tool_up phase. server: Controls the default HTTP server via cherrypy.server. @@ -82,7 +81,7 @@ import ConfigParser import logging as _logging _logfmt = _logging.Formatter("%(message)s") import os as _os -_localdir = _os.path.dirname(__file__) +import types import cherrypy @@ -122,70 +121,70 @@ def merge(base, other): default_conf = { - # Server config - 'server.socket_port': 8080, - 'server.socket_host': '', - 'server.socket_file': '', - 'server.socket_queue_size': 5, - 'server.socket_timeout': 10, - 'server.protocol_version': 'HTTP/1.1', - 'server.reverse_dns': False, - 'server.thread_pool': 10, - 'server.max_request_header_size': 500 * 1024, - 'server.instance': None, - - # Log config - 'log.to_screen': True, -## 'log.error.function': cherrypy._log_message, - 'log.error.file': _os.path.join(_os.getcwd(), _localdir, "error.log"), - # Using an access file makes CP about 10% slower. -## 'log.access.function': cherrypy.log_access, -## 'log.access.file': _os.path.join(_os.getcwd(), _localdir, "access.log"), - - # Process management - 'autoreload.on': True, - 'autoreload.frequency': 1, - 'deadlock.timeout': 300, - 'deadlock.poll_freq': 60, - - 'error_page.404': '', - 'tools.log_tracebacks.on': True, 'tools.log_headers.on': True, } -globalconf = default_conf.copy() - -def reset(): - globalconf.clear() - update(default_conf) - -def update(conf): - """Update globalconf from a dict, file or filename.""" - if isinstance(conf, basestring): - if conf not in cherrypy.engine.reload_files: - cherrypy.engine.reload_files.append(conf) - conf = _Parser().dict_from_file(conf) - elif hasattr(conf, 'read'): - conf = _Parser().dict_from_file(conf) - - if isinstance(conf.get("global", None), dict): - conf = conf["global"] +class Config(object): - if 'environment' in conf: - env = environments[conf['environment']] - for k in env: - if k not in conf: - conf[k] = env[k] + globalconf = default_conf.copy() - if 'tools.staticdir.dir' in conf: - conf['tools.staticdir.section'] = "global" + def reset(self): + """Reset self.globalconf to default values.""" + self.globalconf.clear() + self.update(default_conf) - globalconf.update(conf) + def update(self, conf): + """Update self.globalconf from a dict, file or filename.""" + if isinstance(conf, basestring): + if conf not in cherrypy.engine.reload_files: + cherrypy.engine.reload_files.append(conf) + conf = _Parser().dict_from_file(conf) + elif hasattr(conf, 'read'): + conf = _Parser().dict_from_file(conf) + + if isinstance(conf.get("global", None), dict): + conf = conf["global"] + + if 'environment' in conf: + env = environments[conf['environment']] + for k in env: + if k not in conf: + conf[k] = env[k] + + if 'tools.staticdir.dir' in conf: + conf['tools.staticdir.section'] = "global" + + self.globalconf.update(conf) + + _configure_builtin_logging(self.globalconf, cherrypy._error_log) + _configure_builtin_logging(self.globalconf, cherrypy._access_log, "log.access_file") + + # Override properties specified in config. + gconf = self.globalconf + for k in gconf: + atoms = k.split(".", 1) + namespace = atoms[0] + if namespace == "server": + setattr(cherrypy.server, atoms[1], gconf[k]) + elif namespace == "engine": + setattr(cherrypy.engine, atoms[1], gconf[k]) + elif namespace == "log": + setattr(cherrypy.log, atoms[1], gconf[k]) + elif namespace == "error_page": + cherrypy.error_page[int(atoms[1])] = gconf[k] - _configure_builtin_logging(globalconf, cherrypy._error_log) - _configure_builtin_logging(globalconf, cherrypy._access_log, "log.access.file") + def wrap(**kwargs): + """Decorator to set _cp_config on a handler using the given kwargs.""" + def wrapper(f): + if not hasattr(f, "_cp_config"): + f._cp_config = {} + f._cp_config.update(kwargs) + return f + return wrapper + wrap = staticmethod(wrap) + def _add_builtin_screen_handler(log): import sys @@ -202,7 +201,7 @@ def _add_builtin_file_handler(log, fname): h._cpbuiltin = "file" log.addHandler(h) -def _configure_builtin_logging(conf, log, filekey="log.error.file"): +def _configure_builtin_logging(conf, log, filekey="log.error_file"): """Create/destroy builtin log handlers as needed from conf.""" existing = dict([(getattr(x, "_cpbuiltin", None), x) @@ -230,28 +229,6 @@ def _configure_builtin_logging(conf, log, filekey="log.error.file"): h.close() log.handlers.remove(h) -def get(key, default=None): - """Return the config value corresponding to key, or default.""" - try: - conf = cherrypy.request.config - if conf is None: - conf = globalconf - except AttributeError: - # There's no request, so just use globalconf. - conf = globalconf - - return conf.get(key, default) - - -def wrap(**kwargs): - """Decorator to set _cp_config on a handler using the given kwargs.""" - def wrapper(f): - if not hasattr(f, "_cp_config"): - f._cp_config = {} - f._cp_config.update(kwargs) - return f - return wrapper - class _Parser(ConfigParser.ConfigParser): """Sub-class of ConfigParser that keeps the case of options and that raises @@ -303,26 +280,4 @@ class _Parser(ConfigParser.ConfigParser): self.read(file) return self.as_dict() - -def log_config(): - """Log engine configuration parameters.""" - cherrypy.log("Server parameters:", 'CONFIG') - - server_vars = [ - 'environment', - 'log.screen', - 'log.error.file', - 'server.protocol_version', - 'server.socket_host', - 'server.socket_port', - 'server.socket_file', - 'server.reverse_dns', - 'server.socket_queue_size', - 'server.thread_pool', - ] - - for var in server_vars: - cherrypy.log(" %s: %s" % (var, get(var)), 'CONFIG') - - del ConfigParser diff --git a/cherrypy/_cpengine.py b/cherrypy/_cpengine.py index 764199c7..eb91310d 100644 --- a/cherrypy/_cpengine.py +++ b/cherrypy/_cpengine.py @@ -42,8 +42,12 @@ except ValueError, _signal_exc: class Engine(object): """The application engine, which exposes a request interface to (HTTP) servers.""" + # Configurable attributes request_class = _cprequest.Request response_class = _cprequest.Response + deadlock_poll_freq = 60 + autoreload_on = False + autoreload_frequency = 1 def __init__(self): self.state = STOPPED @@ -66,14 +70,12 @@ class Engine(object): """Start the application engine.""" self.state = STARTING - conf = cherrypy.config.get - for func in self.on_start_engine_list: func() self.state = STARTED - freq = float(conf('deadlock.poll_freq', 60)) + freq = self.deadlock_poll_freq if freq > 0: self.monitor_thread = threading.Timer(freq, self.monitor) self.monitor_thread.start() @@ -84,16 +86,15 @@ class Engine(object): def block(self): """Block forever (wait for stop(), KeyboardInterrupt or SystemExit).""" try: - autoreload = cherrypy.config.get('autoreload.on', False) - if autoreload: + if self.autoreload_on: i = 0 - freq = cherrypy.config.get('autoreload.frequency', 1) + freq = self.autoreload_frequency while self.state != STOPPED: time.sleep(.1) # Autoreload - if autoreload: + if self.autoreload_on: i += .1 if i > freq: i = 0 @@ -218,7 +219,7 @@ class Engine(object): if self.state == STARTED: for req, resp in self.servings: resp.check_timeout() - freq = float(cherrypy.config.get('deadlock.poll_freq', 60)) + freq = self.deadlock_poll_freq self.monitor_thread = threading.Timer(freq, self.monitor) self.monitor_thread.start() diff --git a/cherrypy/_cperror.py b/cherrypy/_cperror.py index c3418434..bde44974 100644 --- a/cherrypy/_cperror.py +++ b/cherrypy/_cperror.py @@ -266,7 +266,7 @@ def get_error_page(status, **kwargs): kwargs[k] = _escape(kwargs[k]) template = _HTTPErrorTemplate - error_page_file = cherrypy.config.get('error_page.%s' % code, '') + error_page_file = cherrypy.request.error_page.get(code, '') if error_page_file: try: template = file(error_page_file, 'rb').read() diff --git a/cherrypy/_cprequest.py b/cherrypy/_cprequest.py index 0db28064..d4ed2752 100644 --- a/cherrypy/_cprequest.py +++ b/cherrypy/_cprequest.py @@ -281,7 +281,6 @@ class Request(object): methods_with_bodies = ("POST", "PUT") body = None body_read = False - max_body_size = 100 * 1024 * 1024 # Dispatch attributes dispatch = Dispatcher() @@ -300,6 +299,7 @@ class Request(object): hooks = HookMap(hookpoints) error_response = cherrypy.HTTPError(500).set_response + error_page = {} show_tracebacks = True throw_errors = False @@ -420,7 +420,7 @@ class Request(object): # HEAD requests MUST NOT return a message-body in the response. cherrypy.response.body = [] - log_access = cherrypy.config.get("log.access.function", cherrypy.log_access) + log_access = cherrypy.log.access if log_access: log_access() @@ -448,7 +448,7 @@ class Request(object): if self.process_request_body: # Prepare the SizeCheckWrapper for the request body - mbs = self.max_body_size + mbs = cherrypy.server.max_request_body_size if mbs > 0: self.rfile = http.SizeCheckWrapper(self.rfile, mbs) @@ -531,16 +531,18 @@ class Request(object): def tool_up(self): """Process self.config, populate self.toolmap and set up each tool.""" + self.error_page = cherrypy.error_page.copy() + # Get all 'tools.*' config entries as a {toolname: {k: v}} dict. self.toolmap = tm = {} reqconf = self.config for k in reqconf: - atoms = k.split(".", 2) + atoms = k.split(".", 1) namespace = atoms[0] if namespace == "tools": - toolname = atoms[1] + toolname, arg = atoms[1].split(".", 1) bucket = tm.setdefault(toolname, {}) - bucket[atoms[2]] = reqconf[k] + bucket[arg] = reqconf[k] elif namespace == "hooks": # Attach bare hooks declared in config. hookpoint = atoms[1] @@ -554,6 +556,8 @@ class Request(object): elif namespace == "response": # Override properties of the current response object. setattr(cherrypy.response, atoms[1], reqconf[k]) + elif namespace == "error_page": + self.error_page[int(atoms[1])] = reqconf[k] # Run tool._setup(conf) for each tool in the new toolmap. tools = cherrypy.tools @@ -687,6 +691,7 @@ class Response(object): simple_cookie = Cookie.SimpleCookie() body = Body() time = None + timeout = 300 timed_out = False stream = False @@ -744,12 +749,11 @@ class Response(object): h.append((name, value)) def check_timeout(self): - """If now > self.time + deadlock_timeout, set self.timed_out. + """If now > self.time + self.timeout, set self.timed_out. This purposefully sets a flag, rather than raising an error, so that a monitor thread can interrupt the Response thread. """ - timeout = float(cherrypy.config.get('deadlock.timeout', 300)) - if time.time() > self.time + timeout: + if time.time() > self.time + self.timeout: self.timed_out = True diff --git a/cherrypy/_cpserver.py b/cherrypy/_cpserver.py index 1e069d52..21fa82a3 100644 --- a/cherrypy/_cpserver.py +++ b/cherrypy/_cpserver.py @@ -11,6 +11,18 @@ from cherrypy.lib import attributes class Server(object): """Manager for a set of HTTP servers.""" + socket_port = 8080 + socket_host = '' + socket_file = '' + socket_queue_size = 5 + socket_timeout = 10 + protocol_version = 'HTTP/1.1' + reverse_dns = False + thread_pool = 10 + max_request_header_size = 500 * 1024 + max_request_body_size = 100 * 1024 * 1024 + instance = None + def __init__(self): self.httpservers = {} self.interrupt = None @@ -19,32 +31,31 @@ class Server(object): """Main function for quick starts. MUST be called from the main thread. This function works like CherryPy 2's server.start(). It loads and - starts an httpserver based on the given server object, if any, and - config entries. + starts an httpserver based on the given server object (if provided) + and attributes of self. """ - httpserver, bind_addr = self.httpserver_from_config(server) + httpserver, bind_addr = self.httpserver_from_self(server) self.httpservers[httpserver] = bind_addr self.start() - def httpserver_from_config(self, httpserver=None): - """Return a (httpserver, bind_addr) pair based on config settings.""" - conf = cherrypy.config.get + def httpserver_from_self(self, httpserver=None): + """Return a (httpserver, bind_addr) pair based on self attributes.""" if httpserver is None: - httpserver = conf('server.instance', None) + httpserver = self.instance if httpserver is None: from cherrypy import _cpwsgi httpserver = _cpwsgi.WSGIServer() if isinstance(httpserver, basestring): httpserver = attributes(httpserver)() - if conf('server.socket_port'): - host = conf('server.socket_host') - port = conf('server.socket_port') + if self.socket_port: + host = self.socket_host + port = self.socket_port if not host: host = 'localhost' return httpserver, (host, port) else: - return httpserver, conf('server.socket_file') + return httpserver, self.socket_file def start(self): """Start all registered HTTP servers.""" diff --git a/cherrypy/_cptree.py b/cherrypy/_cptree.py index ac719b57..f8402e2a 100644 --- a/cherrypy/_cptree.py +++ b/cherrypy/_cptree.py @@ -1,6 +1,6 @@ import logging -from cherrypy import config, _cpwsgi +from cherrypy import _cpconfig, _cpwsgi class Application(object): @@ -45,12 +45,12 @@ class Application(object): def merge(self, conf): """Merge the given config into self.config.""" - config.merge(self.conf, conf) + _cpconfig.merge(self.conf, conf) # Create log handlers as specified in config. rootconf = self.conf.get("/", {}) - config._configure_builtin_logging(rootconf, self.access_log, "log.access.file") - config._configure_builtin_logging(rootconf, self.error_log) + _cpconfig._configure_builtin_logging(rootconf, self.access_log, "log.access_file") + _cpconfig._configure_builtin_logging(rootconf, self.error_log) def guess_abs_path(self): """Guess the absolute URL from server.socket_host and script_name. @@ -61,12 +61,12 @@ class Application(object): However, outside of the request we must guess, hoping the deployer set socket_host and socket_port correctly. """ - port = int(config.get('server.socket_port', 80)) + port = cherrypy.server.socket_port if port in (443, 8443): scheme = "https://" else: scheme = "http://" - host = config.get('server.socket_host', '') + host = cherrypy.server.socket_host if port != 80: host += ":%s" % port return scheme + host + self.script_name @@ -81,6 +81,11 @@ class Tree: An instance of this class may also be used as a WSGI callable (WSGI application object), in which case it dispatches to all mounted apps. + + apps: a dict of the form {script name: application}, where "script name" + is a string declaring the URL mount point (no trailing slash), + and "application" is an instance of cherrypy.Application (or an + arbitrary WSGI callable if you happen to be using a WSGI server). """ def __init__(self): diff --git a/cherrypy/_cpwsgi.py b/cherrypy/_cpwsgi.py index cbb4f677..71a46476 100644 --- a/cherrypy/_cpwsgi.py +++ b/cherrypy/_cpwsgi.py @@ -120,8 +120,7 @@ def _wsgi_callable(environ, start_response, app): class CPHTTPRequest(_cpwsgiserver.HTTPRequest): def parse_request(self): - mhs = int(cherrypy.config.get('server.max_request_header_size', - 500 * 1024)) + mhs = cherrypy.server.max_request_header_size if mhs > 0: self.rfile = http.SizeCheckWrapper(self.rfile, mhs) @@ -135,7 +134,7 @@ class CPHTTPRequest(_cpwsgiserver.HTTPRequest): """Decode the 'chunked' transfer coding.""" if isinstance(self.rfile, http.SizeCheckWrapper): self.rfile = self.rfile.rfile - mbs = int(cherrypy.config.get('request.max_body_size', 100 * 1024 * 1024)) + mbs = cherrypy.server.max_request_body_size if mbs > 0: self.rfile = http.SizeCheckWrapper(self.rfile, mbs) try: @@ -164,23 +163,21 @@ class WSGIServer(_cpwsgiserver.CherryPyWSGIServer): ConnectionClass = CPHTTPConnection def __init__(self): - conf = cherrypy.config.get - - sockFile = conf('server.socket_file') + server = cherrypy.server + sockFile = server.socket_file if sockFile: bind_addr = sockFile else: - bind_addr = (conf('server.socket_host'), - conf('server.socket_port')) + bind_addr = (server.socket_host, server.socket_port) s = _cpwsgiserver.CherryPyWSGIServer # We could just pass cherrypy.tree, but by passing tree.apps, # we get correct SCRIPT_NAMEs as early as possible. s.__init__(self, bind_addr, cherrypy.tree.apps.items(), - conf('server.thread_pool'), - conf('server.socket_host'), - request_queue_size = conf('server.socket_queue_size'), - timeout = conf('server.socket_timeout'), + server.thread_pool, + server.socket_host, + request_queue_size = server.socket_queue_size, + timeout = server.socket_timeout, ) - s.protocol = conf('server.protocol_version', 'HTTP/1.1') + s.protocol = server.protocol_version diff --git a/cherrypy/lib/caching.py b/cherrypy/lib/caching.py index 778976f4..10e0b4d5 100644 --- a/cherrypy/lib/caching.py +++ b/cherrypy/lib/caching.py @@ -91,8 +91,8 @@ def init(cache_class=None): def get(): # Ignore POST, PUT, DELETE. # See http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.10. - invalid = cherrypy.config.get("tools.caching.invalid_methods", - ("POST", "PUT", "DELETE")) + invalid = cherrypy.request.config.get("tools.caching.invalid_methods", + ("POST", "PUT", "DELETE")) if cherrypy.request.method in invalid: cherrypy.request.cached = c = False else: diff --git a/cherrypy/lib/sessions.py b/cherrypy/lib/sessions.py index 30bbc74c..43b450a8 100644 --- a/cherrypy/lib/sessions.py +++ b/cherrypy/lib/sessions.py @@ -353,6 +353,9 @@ def close(): sess.release_lock() close.failsafe = True + +_def_session = RamSession() + def init(storage_type='ram', path=None, path_header=None, name='session_id', timeout=60, domain=None, secure=False, locking='implicit', clean_freq=5, **kwargs): @@ -369,7 +372,7 @@ def init(storage_type='ram', path=None, path_header=None, name='session_id', id = request.simple_cookie[name].value if not hasattr(cherrypy, "session"): - cherrypy.session = cherrypy._ThreadLocalProxy('session') + cherrypy.session = cherrypy._ThreadLocalProxy('session', _def_session) # Create and attach a new Session instance to cherrypy.request. # It will possess a reference to (and lock, and lazily load) diff --git a/cherrypy/test/benchmark.py b/cherrypy/test/benchmark.py index a8186651..b12bfc5d 100644 --- a/cherrypy/test/benchmark.py +++ b/cherrypy/test/benchmark.py @@ -65,7 +65,7 @@ cherrypy.config.update({ 'server.socket_host': 'localhost', 'server.socket_port': 8080, 'server.max_request_header_size': 0, - 'request.max_body_size': 0, + 'server.max_request_body_size': 0, }) appconf = { @@ -180,7 +180,7 @@ Finished 1000 requests self.concurrency = concurrency def args(self): - port = cherrypy.config.get('server.socket_port') + port = cherrypy.server.socket_port assert self.concurrency > 0 assert self.requests > 0 return ("-k -n %s -c %s http://localhost:%s%s" % @@ -240,17 +240,17 @@ def print_report(rows): def run_standard_benchmarks(): print print ("Client Thread Report (1000 requests, 14 byte response body, " - "%s server threads):" % cherrypy.config.get('server.thread_pool')) + "%s server threads):" % cherrypy.server.thread_pool) print_report(thread_report()) print print ("Client Thread Report (1000 requests, 14 bytes via staticdir, " - "%s server threads):" % cherrypy.config.get('server.thread_pool')) + "%s server threads):" % cherrypy.server.thread_pool) print_report(thread_report("%s/static/index.html" % SCRIPT_NAME)) print print ("Size Report (1000 requests, 50 client threads, " - "%s server threads):" % cherrypy.config.get('server.thread_pool')) + "%s server threads):" % cherrypy.server.thread_pool) print_report(size_report()) diff --git a/cherrypy/test/helper.py b/cherrypy/test/helper.py index 009249ec..1b67af70 100644 --- a/cherrypy/test/helper.py +++ b/cherrypy/test/helper.py @@ -154,7 +154,7 @@ def testmain(conf=None): def _test_main_thread(): try: - webtest.WebCase.PORT = cherrypy.config.get('server.socket_port') + webtest.WebCase.PORT = cherrypy.server.socket_port webtest.main() finally: thread.interrupt_main() diff --git a/cherrypy/test/test.py b/cherrypy/test/test.py index 54225cbd..f7461746 100644 --- a/cherrypy/test/test.py +++ b/cherrypy/test/test.py @@ -39,7 +39,7 @@ class TestHarness(object): print if isinstance(conf, basestring): - conf = cherrypy.config.dict_from_config_file(conf) + conf = cherrypy.config._Parser().dict_from_file(conf) baseconf = {'server.socket_host': '127.0.0.1', 'server.socket_port': self.port, 'server.thread_pool': 10, diff --git a/cherrypy/test/test_caching.py b/cherrypy/test/test_caching.py index 54665914..7b2405d3 100644 --- a/cherrypy/test/test_caching.py +++ b/cherrypy/test/test_caching.py @@ -120,7 +120,7 @@ class CacheTest(helper.CPWebCase): self.assertStatus("200 OK") # This also gives us a chance to test 0 expiry with no other headers self.assertHeader("Pragma", "no-cache") - if cherrypy.config.get('server.protocol_version') == "HTTP/1.1": + if cherrypy.server.protocol_version == "HTTP/1.1": self.assertHeader("Cache-Control", "no-cache") d = self.assertHeader("Date") self.assertHeader("Expires", d) @@ -129,7 +129,7 @@ class CacheTest(helper.CPWebCase): self.getPage("/expires/index.html") self.assertStatus("200 OK") self.assertHeader("Pragma", "no-cache") - if cherrypy.config.get('server.protocol_version') == "HTTP/1.1": + if cherrypy.server.protocol_version == "HTTP/1.1": self.assertHeader("Cache-Control", "no-cache") d = self.assertHeader("Date") self.assertHeader("Expires", d) @@ -138,7 +138,7 @@ class CacheTest(helper.CPWebCase): self.getPage("/expires/cacheable") self.assertStatus("200 OK") self.assertHeader("Pragma", "no-cache") - if cherrypy.config.get('server.protocol_version') == "HTTP/1.1": + if cherrypy.server.protocol_version == "HTTP/1.1": self.assertHeader("Cache-Control", "no-cache") d = self.assertHeader("Date") self.assertHeader("Expires", d) @@ -148,7 +148,7 @@ class CacheTest(helper.CPWebCase): # dynamic sets Cache-Control to private but it should be # overwritten here ... self.assertHeader("Pragma", "no-cache") - if cherrypy.config.get('server.protocol_version') == "HTTP/1.1": + if cherrypy.server.protocol_version == "HTTP/1.1": self.assertHeader("Cache-Control", "no-cache") d = self.assertHeader("Date") self.assertHeader("Expires", d) diff --git a/cherrypy/test/test_config.py b/cherrypy/test/test_config.py index b7fe3cc7..d6fab72f 100644 --- a/cherrypy/test/test_config.py +++ b/cherrypy/test/test_config.py @@ -1,4 +1,5 @@ """Tests for the CherryPy configuration system.""" + from cherrypy.test import test test.prefer_parent_path() @@ -14,7 +15,7 @@ def setup_server(): 'bar': 'that'} def index(self, key): - return cherrypy.config.get(key, "None") + return cherrypy.request.config.get(key, "None") index.exposed = True global_ = index xyz = index @@ -25,19 +26,19 @@ def setup_server(): 'baz': 'that2'} def index(self, key): - return cherrypy.config.get(key, "None") + return cherrypy.request.config.get(key, "None") index.exposed = True nex = index def bar(self, key): - return cherrypy.config.get(key, "None") + return cherrypy.request.config.get(key, "None") bar.exposed = True bar._cp_config = {'foo': 'this3', 'bax': 'this4'} class Another: def index(self, key): - return str(cherrypy.config.get(key, "None")) + return str(cherrypy.request.config.get(key, "None")) index.exposed = True root = Root() @@ -73,6 +74,31 @@ class ConfigTests(helper.CPWebCase): for path, key, expected in tests: self.getPage(path + "?key=" + key) self.assertBody(expected) + + def testExternalDispatch(self): + # 'cherrypy.request' should reference a default instance + cherrypy.request.app = cherrypy.tree.apps[""] + cherrypy.request.dispatch("/foo/bar") + expectedconf = { + # From CP defaults + 'tools.log_headers.on': False, + 'tools.log_tracebacks.on': True, + 'request.show_tracebacks': True, + 'log.screen': False, + 'environment': 'test_suite', + 'autoreload.on': False, + # From globalconf + 'luxuryyacht': 'throatwobblermangrove', + # From Root._cp_config + 'bar': 'that', + # From Foo._cp_config + 'baz': 'that2', + # From Foo.bar._cp_config + 'foo': 'this3', + 'bax': 'this4', + } + for k, v in expectedconf.iteritems(): + self.assertEqual(cherrypy.request.config[k], v) if __name__ == '__main__': diff --git a/cherrypy/test/test_conn.py b/cherrypy/test/test_conn.py index 75120324..af744b3a 100644 --- a/cherrypy/test/test_conn.py +++ b/cherrypy/test/test_conn.py @@ -38,7 +38,7 @@ def setup_server(): cherrypy.tree.mount(Root()) cherrypy.config.update({ - 'request.max_body_size': 100, + 'server.max_request_body_size': 100, 'environment': 'test_suite', }) @@ -48,7 +48,7 @@ from cherrypy.test import helper class ConnectionTests(helper.CPWebCase): def test_HTTP11(self): - if cherrypy.config.get('server.protocol_version') != "HTTP/1.1": + if cherrypy.server.protocol_version != "HTTP/1.1": print "skipped ", return @@ -103,7 +103,7 @@ class ConnectionTests(helper.CPWebCase): self.assertRaises(httplib.NotConnected, self.getPage, "/") def test_HTTP11_pipelining(self): - if cherrypy.config.get('server.protocol_version') != "HTTP/1.1": + if cherrypy.server.protocol_version != "HTTP/1.1": print "skipped ", return @@ -141,7 +141,7 @@ class ConnectionTests(helper.CPWebCase): conn.close() def test_100_Continue(self): - if cherrypy.config.get('server.protocol_version') != "HTTP/1.1": + if cherrypy.server.protocol_version != "HTTP/1.1": print "skipped ", return @@ -193,7 +193,7 @@ class ConnectionTests(helper.CPWebCase): self.assertBody("thanks for 'I am a small file' (text/plain)") def test_Chunked_Encoding(self): - if cherrypy.config.get('server.protocol_version') != "HTTP/1.1": + if cherrypy.server.protocol_version != "HTTP/1.1": print "skipped ", return @@ -220,7 +220,7 @@ class ConnectionTests(helper.CPWebCase): self.assertStatus('200 OK') self.assertBody("thanks for 'xx\r\nxxxxyyyyy' (application/x-json)") - # Try a chunked request that exceeds request.max_body_size. + # Try a chunked request that exceeds server.max_request_body_size. # Note that the delimiters and trailer are included. body = "5f\r\n" + ("x" * 95) + "\r\n0\r\n\r\n" conn.putrequest("POST", "/upload", skip_host=True) diff --git a/cherrypy/test/test_core.py b/cherrypy/test/test_core.py index ff11bfed..2b27e21e 100644 --- a/cherrypy/test/test_core.py +++ b/cherrypy/test/test_core.py @@ -219,7 +219,8 @@ def setup_server(): raise ValueError() # We support Python 2.3, but the @-deco syntax would look like this: - # @cherrypy.config.wrap(**{"response.stream": True}) + # @cherrypy.config.response.stream(True) + # @cherrypy.config.tools.static.on(True) def page_streamed(self): yield "word up" raise ValueError() @@ -370,13 +371,13 @@ def setup_server(): return u'Wrong login/password' cherrypy.config.update({ - 'log.error.file': log_file, + 'log.error_file': log_file, 'environment': 'test_suite', - 'request.max_body_size': 200, + 'server.max_request_body_size': 200, 'server.max_request_header_size': 500, }) appconf = { - '/': {'log.access.file': log_access_file}, + '/': {'log.access_file': log_access_file}, '/method': {'request.methods_with_bodies': ("POST", "PUT", "PROPFIND")}, } cherrypy.tree.mount(root, conf=appconf) @@ -584,7 +585,7 @@ class CoreRequestHandlingTest(helper.CPWebCase): self.getPage("/redirect/stringify", protocol="HTTP/1.0") self.assertStatus(200) self.assertBody("(['http://127.0.0.1:%s/'], 302)" % self.PORT) - if cherrypy.config.get('server.protocol_version') == "HTTP/1.1": + if cherrypy.server.protocol_version == "HTTP/1.1": self.getPage("/redirect/stringify", protocol="HTTP/1.1") self.assertStatus(200) self.assertBody("(['http://127.0.0.1:%s/'], 303)" % self.PORT) @@ -627,7 +628,6 @@ class CoreRequestHandlingTest(helper.CPWebCase): # Test custom error page. self.getPage("/error/custom") self.assertStatus(404) - self.assertEqual(len(self.body), 513) self.assertBody("Hello, world\r\n" + (" " * 499)) # Test error in custom error page (ticket #305). @@ -652,7 +652,7 @@ class CoreRequestHandlingTest(helper.CPWebCase): self.assertBody("[(2, 5), (7, 8)]") # Get a partial file. - if cherrypy.config.get('server.protocol_version') == "HTTP/1.1": + if cherrypy.server.protocol_version == "HTTP/1.1": self.getPage("/ranges/slice_file", [('Range', 'bytes=2-5')]) self.assertStatus(206) self.assertHeader("Content-Type", "text/html") @@ -688,7 +688,7 @@ llo, self.getPage("/ranges/slice_file", [('Range', 'bytes=2300-2900')]) self.assertStatus(416) self.assertHeader("Content-Range", "bytes */14") - elif cherrypy.config.get('server.protocol_version') == "HTTP/1.0": + elif cherrypy.server.protocol_version == "HTTP/1.0": # Test Range behavior with HTTP/1.0 request self.getPage("/ranges/slice_file", [('Range', 'bytes=2-5')]) self.assertStatus(200) @@ -759,7 +759,7 @@ llo, 'Expires', 'Location', 'Server']: self.assertEqual(hnames.count(key), 1) - if cherrypy.config.get('server.protocol_version') == "HTTP/1.1": + if cherrypy.server.protocol_version == "HTTP/1.1": # Test RFC-2047-encoded request and response header values self.getPage("/headers/ifmatch", [('If-Match', '=?utf-8?q?=E2=84=ABngstr=C3=B6m?=')]) diff --git a/cherrypy/test/test_objectmapping.py b/cherrypy/test/test_objectmapping.py index 33ad1468..666196ed 100644 --- a/cherrypy/test/test_objectmapping.py +++ b/cherrypy/test/test_objectmapping.py @@ -32,7 +32,7 @@ def setup_server(): return "not exposed" def confvalue(self): - return cherrypy.config.get("user") + return cherrypy.request.config.get("user") confvalue.exposed = True def mapped_func(self, ID=None): diff --git a/cherrypy/test/test_states.py b/cherrypy/test/test_states.py index dfb530a6..06491642 100644 --- a/cherrypy/test/test_states.py +++ b/cherrypy/test/test_states.py @@ -86,8 +86,8 @@ class ServerStateTests(helper.CPWebCase): self.assertEqual(cherrypy.engine.state, 1) if self.server_class: - host = cherrypy.config.get('server.socket_host') - port = cherrypy.config.get('server.socket_port') + host = cherrypy.server.socket_host + port = cherrypy.server.socket_port self.assertRaises(IOError, cherrypy._cpserver.check_port, host, port) # The db_connection should be running now @@ -231,8 +231,8 @@ class ServerStateTests(helper.CPWebCase): "test_states_demo.py") # Start the demo script in a new process - host = cherrypy.config.get("server.socket_host") - port = cherrypy.config.get("server.socket_port") + host = cherrypy.server.socket_host + port = cherrypy.server.socket_port cherrypy._cpserver.wait_for_free_port(host, port) os.spawnl(os.P_NOWAIT, sys.executable, sys.executable, demoscript, host, str(port)) diff --git a/cherrypy/test/test_tools.py b/cherrypy/test/test_tools.py index fdbf5159..3f80e1d2 100644 --- a/cherrypy/test/test_tools.py +++ b/cherrypy/test/test_tools.py @@ -94,7 +94,9 @@ def setup_server(): pipe._cp_config = {'hooks.before_request_body': pipe_body} # Multiple decorators; include kwargs just for fun. + # XXX Note that encode must run before gzip. def decorated_euro(self): + print cherrypy.request.hooks.callbacks yield u"Hello," yield u"world" yield europoundUnicode diff --git a/cherrypy/test/test_tutorials.py b/cherrypy/test/test_tutorials.py index a15e43a1..c3d7c552 100644 --- a/cherrypy/test/test_tutorials.py +++ b/cherrypy/test/test_tutorials.py @@ -36,7 +36,7 @@ def setup_server(): sessions.exposed = True def traceback_setting(): - return repr(cherrypy.config.get('request.show_tracebacks')) + return repr(cherrypy.request.show_tracebacks) traceback_setting.exposed = True class Dummy: diff --git a/cherrypy/tutorial/tut10_http_errors.py b/cherrypy/tutorial/tut10_http_errors.py index f6d29cf2..0ae31b89 100644 --- a/cherrypy/tutorial/tut10_http_errors.py +++ b/cherrypy/tutorial/tut10_http_errors.py @@ -22,7 +22,7 @@ class HTTPErrorDemo(object): def index(self): # display some links that will result in errors - tracebacks = cherrypy.config.get('request.show_tracebacks') + tracebacks = cherrypy.request.show_tracebacks if tracebacks: trace = 'off' else: @@ -48,7 +48,7 @@ class HTTPErrorDemo(object): def toggleTracebacks(self): # simple function to toggle tracebacks on and off - tracebacks = cherrypy.config.get('request.show_tracebacks') + tracebacks = cherrypy.request.show_tracebacks cherrypy.config.update({'request.show_tracebacks': not tracebacks}) # redirect back to the index |