diff options
author | Robert Brewer <fumanchu@aminus.org> | 2006-12-28 20:34:24 +0000 |
---|---|---|
committer | Robert Brewer <fumanchu@aminus.org> | 2006-12-28 20:34:24 +0000 |
commit | 4afcf1bed1fce0314fd87d27776a48343f462f03 (patch) | |
tree | 887b0b6c505dc9d6abf42c87bbf9d1ee403c8397 | |
parent | bce6251732ebe9bb1d750a31c610ff0d501507a7 (diff) | |
download | cherrypy-git-4afcf1bed1fce0314fd87d27776a48343f462f03.tar.gz |
Moved checker back to cherrypy.checker from engine, and also added a new check_config_types method. There's also a new checker config namespace, so you can turn off the checker with "checker.on = False", or turn off specific methods with "checker.check_method_foo = None".
-rw-r--r-- | cherrypy/__init__.py | 3 | ||||
-rw-r--r-- | cherrypy/_cpchecker.py | 117 | ||||
-rw-r--r-- | cherrypy/_cpconfig.py | 12 | ||||
-rw-r--r-- | cherrypy/_cpengine.py | 6 | ||||
-rw-r--r-- | cherrypy/test/checkerdemo.py | 2 |
5 files changed, 111 insertions, 29 deletions
diff --git a/cherrypy/__init__.py b/cherrypy/__init__.py index 264925d5..58f61bc6 100644 --- a/cherrypy/__init__.py +++ b/cherrypy/__init__.py @@ -269,3 +269,6 @@ from cherrypy import _cpconfig # Use _global_conf_alias so quickstart can use 'config' as an arg # without shadowing cherrypy.config. config = _global_conf_alias = _cpconfig.Config() + +from cherrypy import _cpchecker +checker = _cpchecker.Checker() diff --git a/cherrypy/_cpchecker.py b/cherrypy/_cpchecker.py index 104f7db2..8fed2fb7 100644 --- a/cherrypy/_cpchecker.py +++ b/cherrypy/_cpchecker.py @@ -6,24 +6,32 @@ import cherrypy class Checker(object): - global_config_contained_paths = False + on = True + + def __init__(self): + self._populate_known_types() def __call__(self): - oldformatwarning = warnings.formatwarning - warnings.formatwarning = self.formatwarning - try: - for name in dir(self): - if name.startswith("check_"): - method = getattr(self, name) - if method and callable(method): - method() - finally: - warnings.formatwarning = oldformatwarning + """Run all check_* methods.""" + if self.on: + oldformatwarning = warnings.formatwarning + warnings.formatwarning = self.formatwarning + try: + for name in dir(self): + if name.startswith("check_"): + method = getattr(self, name) + if method and callable(method): + method() + finally: + warnings.formatwarning = oldformatwarning def formatwarning(self, message, category, filename, lineno): """Function to format a warning.""" return "CherryPy Checker:\n%s\n\n" % message + # This value should be set inside _cpconfig. + global_config_contained_paths = False + def check_skipped_app_config(self): for sn, app in cherrypy.tree.apps.iteritems(): if not app.config: @@ -58,11 +66,13 @@ class Checker(object): if os.path.isabs(dir): fulldir = dir if root: - msg = "dir is an absolute path, even though a root is provided." + msg = ("dir is an absolute path, even " + "though a root is provided.") testdir = os.path.join(root, dir[1:]) if os.path.exists(testdir): - msg += ("\nIf you meant to serve the filesystem folder at %r, " - "remove the leading slash from dir." % testdir) + msg += ("\nIf you meant to serve the " + "filesystem folder at %r, remove " + "the leading slash from dir." % testdir) else: if not root: msg = "dir is a relative path and no root provided." @@ -74,12 +84,16 @@ class Checker(object): if fulldir and not os.path.exists(fulldir): if msg: msg += "\n" - msg += "%r (root + dir) is not an existing filesystem path." % fulldir + msg += ("%r (root + dir) is not an existing " + "filesystem path." % fulldir) if msg: warnings.warn("%s\nsection: [%s]\nroot: %r\ndir: %r" % (msg, section, root, dir)) + + # -------------------------- Compatibility -------------------------- # + obsolete = { 'server.default_content_type': 'tools.response_headers.headers', 'log_access_file': 'log.access_file', @@ -90,7 +104,8 @@ class Checker(object): 'log_to_screen': 'log.screen', 'show_tracebacks': 'request.show_tracebacks', 'throw_errors': 'request.throw_errors', - 'profiler.on': 'cherrypy.tree.mount(profiler.make_app(cherrypy.Application(Root())))', + 'profiler.on': ('cherrypy.tree.mount(profiler.make_app(' + 'cherrypy.Application(Root())))'), } deprecated = {} @@ -102,10 +117,12 @@ class Checker(object): for k, v in conf.iteritems(): if k in self.obsolete: warnings.warn("%r is obsolete. Use %r instead.\n" - "section: [%s]" % (k, self.obsolete[k], section)) + "section: [%s]" % + (k, self.obsolete[k], section)) elif k in self.deprecated: warnings.warn("%r is deprecated. Use %r instead.\n" - "section: [%s]" % (k, self.deprecated[k], section)) + "section: [%s]" % + (k, self.deprecated[k], section)) else: if section in self.obsolete: warnings.warn("%r is obsolete. Use %r instead." @@ -120,8 +137,13 @@ class Checker(object): for sn, app in cherrypy.tree.apps.iteritems(): self._compat(app.config) - known_config_namespaces = ["engine", "hooks", "log", "request", - "response", "server", "tools", "wsgi"] + + # ------------------------ Known Namespaces ------------------------ # + + known_config_namespaces = ["checker", "engine", "hooks", "log", + "request", "response", "server", + "tools", + "wsgi"] def _known_ns(self, config): for section, conf in config.iteritems(): @@ -131,7 +153,8 @@ class Checker(object): atoms = k.split(".") if len(atoms) > 1: if atoms[0] not in self.known_config_namespaces: - if atoms[0] == "cherrypy" and atoms[1] in self.known_config_namespaces: + if (atoms[0] == "cherrypy" and + atoms[1] in self.known_config_namespaces): msg = ("The config entry %r is invalid; " "try %r instead.\nsection: [%s]" % (k, ".".join(atoms[1:]), section)) @@ -145,3 +168,55 @@ class Checker(object): """Process config and warn on each unknown config namespace.""" for sn, app in cherrypy.tree.apps.iteritems(): self._known_ns(app.config) + + + # -------------------------- Config Types -------------------------- # + + known_config_types = {} + + def _populate_known_types(self): + import __builtin__ + builtins = [x for x in vars(__builtin__).values() + if type(x) is type(str)] + + def traverse(obj, namespace): + for name in dir(obj): + vtype = type(getattr(obj, name, None)) + if vtype in builtins: + self.known_config_types[namespace + "." + name] = vtype + + traverse(cherrypy.request, "request") + traverse(cherrypy.response, "response") + traverse(cherrypy.server, "server") + traverse(cherrypy.engine, "engine") + traverse(cherrypy.log, "log") + + def _known_types(self, config): + msg = ("The config entry %r in section %r is of type %r, " + "which does not match the expected type %r.") + + for section, conf in config.iteritems(): + if isinstance(conf, dict): + for k, v in conf.iteritems(): + if v is not None: + expected_type = self.known_config_types.get(k, None) + vtype = type(v) + if expected_type and vtype != expected_type: + warnings.warn(msg % (k, section, vtype.__name__, + expected_type.__name__)) + else: + k, v = section, conf + if v is not None: + expected_type = self.known_config_types.get(k, None) + vtype = type(v) + if expected_type and vtype != expected_type: + warnings.warn(msg % (k, section, vtype.__name__, + expected_type.__name__)) + + def check_config_types(self): + """Assert that config values are of the same type as default values.""" + self._known_types(cherrypy.config) + for sn, app in cherrypy.tree.apps.iteritems(): + self._known_types(app.config) + + diff --git a/cherrypy/_cpconfig.py b/cherrypy/_cpconfig.py index 2c9a84e4..fb2101c3 100644 --- a/cherrypy/_cpconfig.py +++ b/cherrypy/_cpconfig.py @@ -69,6 +69,9 @@ Current namespaces: server: Controls the default HTTP server via cherrypy.server. These can only be declared in the global config. tools: Runs and configures additional request-processing packages. + checker: Controls the 'checker', which looks for common errors in + app state (including config) when the engine starts. + Global config only. The only key that does not exist in a namespace is the "environment" entry. This special entry 'imports' other config entries from a template stored in @@ -92,20 +95,20 @@ import cherrypy environments = { "staging": { 'engine.autoreload_on': False, - 'engine.checker': None, + 'checker.on': False, 'tools.log_headers.on': False, 'request.show_tracebacks': False, }, "production": { 'engine.autoreload_on': False, - 'engine.checker': None, + 'checker.on': False, 'tools.log_headers.on': False, 'request.show_tracebacks': False, 'log.screen': False, }, "test_suite": { 'engine.autoreload_on': False, - 'engine.checker': None, + 'checker.on': False, 'tools.log_headers.on': False, 'request.show_tracebacks': True, 'log.screen': False, @@ -203,6 +206,7 @@ class Config(dict): namespaces = {"server": lambda k, v: setattr(cherrypy.server, k, v), "engine": lambda k, v: setattr(cherrypy.engine, k, v), "log": lambda k, v: setattr(cherrypy.log, k, v), + "checker": lambda k, v: setattr(cherrypy.checker, k, v), } def __init__(self): @@ -228,7 +232,7 @@ class Config(dict): if isinstance(config.get("global", None), dict): if len(config) > 1: - cherrypy.engine.checker.global_config_contained_paths = True + cherrypy.checker.global_config_contained_paths = True config = config["global"] if 'environment' in config: diff --git a/cherrypy/_cpengine.py b/cherrypy/_cpengine.py index 114a27e0..efb5ee31 100644 --- a/cherrypy/_cpengine.py +++ b/cherrypy/_cpengine.py @@ -9,7 +9,7 @@ import threading import time import cherrypy -from cherrypy import _cprequest, _cpchecker +from cherrypy import _cprequest # Use a flag to indicate the state of the application engine. STOPPED = 0 @@ -52,7 +52,6 @@ class Engine(object): autoreload_on = True autoreload_frequency = 1 autoreload_match = ".*" - checker = _cpchecker.Checker() def __init__(self): self.state = STOPPED @@ -75,8 +74,7 @@ class Engine(object): """Start the application engine.""" self.state = STARTING - if self.checker: - self.checker() + cherrypy.checker() for func in self.on_start_engine_list: func() diff --git a/cherrypy/test/checkerdemo.py b/cherrypy/test/checkerdemo.py index 37fc2901..fdc229a5 100644 --- a/cherrypy/test/checkerdemo.py +++ b/cherrypy/test/checkerdemo.py @@ -33,5 +33,7 @@ if __name__ == '__main__': '/unknown': {'toobles.gzip.on': True}, # Warn special on cherrypy.<known ns>.* '/cpknown': {'cherrypy.tools.encode.on': True}, + # Warn on mismatched types + '/conftype': {'request.show_tracebacks': 14}, } cherrypy.quickstart(Root(), config=conf) |