Package cherrypy :: Module _cpchecker
[hide private]
[frames] | no frames]

Source Code for Module cherrypy._cpchecker

  1  import os 
  2  import warnings 
  3   
  4  import cherrypy 
  5  from cherrypy._cpcompat import iteritems, copykeys, builtins 
  6   
  7   
8 -class Checker(object):
9 """A checker for CherryPy sites and their mounted applications. 10 11 When this object is called at engine startup, it executes each 12 of its own methods whose names start with ``check_``. If you wish 13 to disable selected checks, simply add a line in your global 14 config which sets the appropriate method to False:: 15 16 [global] 17 checker.check_skipped_app_config = False 18 19 You may also dynamically add or replace ``check_*`` methods in this way. 20 """ 21 22 on = True 23 """If True (the default), run all checks; if False, turn off all checks.""" 24 25
26 - def __init__(self):
28
29 - def __call__(self):
30 """Run all check_* methods.""" 31 if self.on: 32 oldformatwarning = warnings.formatwarning 33 warnings.formatwarning = self.formatwarning 34 try: 35 for name in dir(self): 36 if name.startswith("check_"): 37 method = getattr(self, name) 38 if method and hasattr(method, '__call__'): 39 method() 40 finally: 41 warnings.formatwarning = oldformatwarning
42
43 - def formatwarning(self, message, category, filename, lineno, line=None):
44 """Function to format a warning.""" 45 return "CherryPy Checker:\n%s\n\n" % message
46 47 # This value should be set inside _cpconfig. 48 global_config_contained_paths = False 49
51 """Check for Application config with sections that repeat script_name.""" 52 for sn, app in cherrypy.tree.apps.items(): 53 if not isinstance(app, cherrypy.Application): 54 continue 55 if not app.config: 56 continue 57 if sn == '': 58 continue 59 sn_atoms = sn.strip("/").split("/") 60 for key in app.config.keys(): 61 key_atoms = key.strip("/").split("/") 62 if key_atoms[:len(sn_atoms)] == sn_atoms: 63 warnings.warn( 64 "The application mounted at %r has config " \ 65 "entries that start with its script name: %r" % (sn, key))
66
68 """Check for mounted Applications that have site-scoped config.""" 69 for sn, app in iteritems(cherrypy.tree.apps): 70 if not isinstance(app, cherrypy.Application): 71 continue 72 73 msg = [] 74 for section, entries in iteritems(app.config): 75 if section.startswith('/'): 76 for key, value in iteritems(entries): 77 for n in ("engine.", "server.", "tree.", "checker."): 78 if key.startswith(n): 79 msg.append("[%s] %s = %s" % (section, key, value)) 80 if msg: 81 msg.insert(0, 82 "The application mounted at %r contains the following " 83 "config entries, which are only allowed in site-wide " 84 "config. Move them to a [global] section and pass them " 85 "to cherrypy.config.update() instead of tree.mount()." % sn) 86 warnings.warn(os.linesep.join(msg))
87
88 - def check_skipped_app_config(self):
89 """Check for mounted Applications that have no config.""" 90 for sn, app in cherrypy.tree.apps.items(): 91 if not isinstance(app, cherrypy.Application): 92 continue 93 if not app.config: 94 msg = "The Application mounted at %r has an empty config." % sn 95 if self.global_config_contained_paths: 96 msg += (" It looks like the config you passed to " 97 "cherrypy.config.update() contains application-" 98 "specific sections. You must explicitly pass " 99 "application config via " 100 "cherrypy.tree.mount(..., config=app_config)") 101 warnings.warn(msg) 102 return
103
105 """Check for Application config with extraneous brackets in section names.""" 106 for sn, app in cherrypy.tree.apps.items(): 107 if not isinstance(app, cherrypy.Application): 108 continue 109 if not app.config: 110 continue 111 for key in app.config.keys(): 112 if key.startswith("[") or key.endswith("]"): 113 warnings.warn( 114 "The application mounted at %r has config " \ 115 "section names with extraneous brackets: %r. " 116 "Config *files* need brackets; config *dicts* " 117 "(e.g. passed to tree.mount) do not." % (sn, key))
118
119 - def check_static_paths(self):
120 """Check Application config for incorrect static paths.""" 121 # Use the dummy Request object in the main thread. 122 request = cherrypy.request 123 for sn, app in cherrypy.tree.apps.items(): 124 if not isinstance(app, cherrypy.Application): 125 continue 126 request.app = app 127 for section in app.config: 128 # get_resource will populate request.config 129 request.get_resource(section + "/dummy.html") 130 conf = request.config.get 131 132 if conf("tools.staticdir.on", False): 133 msg = "" 134 root = conf("tools.staticdir.root") 135 dir = conf("tools.staticdir.dir") 136 if dir is None: 137 msg = "tools.staticdir.dir is not set." 138 else: 139 fulldir = "" 140 if os.path.isabs(dir): 141 fulldir = dir 142 if root: 143 msg = ("dir is an absolute path, even " 144 "though a root is provided.") 145 testdir = os.path.join(root, dir[1:]) 146 if os.path.exists(testdir): 147 msg += ("\nIf you meant to serve the " 148 "filesystem folder at %r, remove " 149 "the leading slash from dir." % testdir) 150 else: 151 if not root: 152 msg = "dir is a relative path and no root provided." 153 else: 154 fulldir = os.path.join(root, dir) 155 if not os.path.isabs(fulldir): 156 msg = "%r is not an absolute path." % fulldir 157 158 if fulldir and not os.path.exists(fulldir): 159 if msg: 160 msg += "\n" 161 msg += ("%r (root + dir) is not an existing " 162 "filesystem path." % fulldir) 163 164 if msg: 165 warnings.warn("%s\nsection: [%s]\nroot: %r\ndir: %r" 166 % (msg, section, root, dir))
167 168 169 # -------------------------- Compatibility -------------------------- # 170 171 obsolete = { 172 'server.default_content_type': 'tools.response_headers.headers', 173 'log_access_file': 'log.access_file', 174 'log_config_options': None, 175 'log_file': 'log.error_file', 176 'log_file_not_found': None, 177 'log_request_headers': 'tools.log_headers.on', 178 'log_to_screen': 'log.screen', 179 'show_tracebacks': 'request.show_tracebacks', 180 'throw_errors': 'request.throw_errors', 181 'profiler.on': ('cherrypy.tree.mount(profiler.make_app(' 182 'cherrypy.Application(Root())))'), 183 } 184 185 deprecated = {} 186
187 - def _compat(self, config):
188 """Process config and warn on each obsolete or deprecated entry.""" 189 for section, conf in config.items(): 190 if isinstance(conf, dict): 191 for k, v in conf.items(): 192 if k in self.obsolete: 193 warnings.warn("%r is obsolete. Use %r instead.\n" 194 "section: [%s]" % 195 (k, self.obsolete[k], section)) 196 elif k in self.deprecated: 197 warnings.warn("%r is deprecated. Use %r instead.\n" 198 "section: [%s]" % 199 (k, self.deprecated[k], section)) 200 else: 201 if section in self.obsolete: 202 warnings.warn("%r is obsolete. Use %r instead." 203 % (section, self.obsolete[section])) 204 elif section in self.deprecated: 205 warnings.warn("%r is deprecated. Use %r instead." 206 % (section, self.deprecated[section]))
207
208 - def check_compatibility(self):
209 """Process config and warn on each obsolete or deprecated entry.""" 210 self._compat(cherrypy.config) 211 for sn, app in cherrypy.tree.apps.items(): 212 if not isinstance(app, cherrypy.Application): 213 continue 214 self._compat(app.config)
215 216 217 # ------------------------ Known Namespaces ------------------------ # 218 219 extra_config_namespaces = [] 220
221 - def _known_ns(self, app):
222 ns = ["wsgi"] 223 ns.extend(copykeys(app.toolboxes)) 224 ns.extend(copykeys(app.namespaces)) 225 ns.extend(copykeys(app.request_class.namespaces)) 226 ns.extend(copykeys(cherrypy.config.namespaces)) 227 ns += self.extra_config_namespaces 228 229 for section, conf in app.config.items(): 230 is_path_section = section.startswith("/") 231 if is_path_section and isinstance(conf, dict): 232 for k, v in conf.items(): 233 atoms = k.split(".") 234 if len(atoms) > 1: 235 if atoms[0] not in ns: 236 # Spit out a special warning if a known 237 # namespace is preceded by "cherrypy." 238 if (atoms[0] == "cherrypy" and atoms[1] in ns): 239 msg = ("The config entry %r is invalid; " 240 "try %r instead.\nsection: [%s]" 241 % (k, ".".join(atoms[1:]), section)) 242 else: 243 msg = ("The config entry %r is invalid, because " 244 "the %r config namespace is unknown.\n" 245 "section: [%s]" % (k, atoms[0], section)) 246 warnings.warn(msg) 247 elif atoms[0] == "tools": 248 if atoms[1] not in dir(cherrypy.tools): 249 msg = ("The config entry %r may be invalid, " 250 "because the %r tool was not found.\n" 251 "section: [%s]" % (k, atoms[1], section)) 252 warnings.warn(msg)
253
254 - def check_config_namespaces(self):
255 """Process config and warn on each unknown config namespace.""" 256 for sn, app in cherrypy.tree.apps.items(): 257 if not isinstance(app, cherrypy.Application): 258 continue 259 self._known_ns(app)
260 261 262 263 264 # -------------------------- Config Types -------------------------- # 265 266 known_config_types = {} 267
268 - def _populate_known_types(self):
269 b = [x for x in vars(builtins).values() 270 if type(x) is type(str)] 271 272 def traverse(obj, namespace): 273 for name in dir(obj): 274 # Hack for 3.2's warning about body_params 275 if name == 'body_params': 276 continue 277 vtype = type(getattr(obj, name, None)) 278 if vtype in b: 279 self.known_config_types[namespace + "." + name] = vtype
280 281 traverse(cherrypy.request, "request") 282 traverse(cherrypy.response, "response") 283 traverse(cherrypy.server, "server") 284 traverse(cherrypy.engine, "engine") 285 traverse(cherrypy.log, "log")
286
287 - def _known_types(self, config):
288 msg = ("The config entry %r in section %r is of type %r, " 289 "which does not match the expected type %r.") 290 291 for section, conf in config.items(): 292 if isinstance(conf, dict): 293 for k, v in conf.items(): 294 if v is not None: 295 expected_type = self.known_config_types.get(k, None) 296 vtype = type(v) 297 if expected_type and vtype != expected_type: 298 warnings.warn(msg % (k, section, vtype.__name__, 299 expected_type.__name__)) 300 else: 301 k, v = section, conf 302 if v is not None: 303 expected_type = self.known_config_types.get(k, None) 304 vtype = type(v) 305 if expected_type and vtype != expected_type: 306 warnings.warn(msg % (k, section, vtype.__name__, 307 expected_type.__name__))
308
309 - def check_config_types(self):
310 """Assert that config values are of the same type as default values.""" 311 self._known_types(cherrypy.config) 312 for sn, app in cherrypy.tree.apps.items(): 313 if not isinstance(app, cherrypy.Application): 314 continue 315 self._known_types(app.config)
316 317 318 # -------------------- Specific config warnings -------------------- # 319
320 - def check_localhost(self):
321 """Warn if any socket_host is 'localhost'. See #711.""" 322 for k, v in cherrypy.config.items(): 323 if k == 'server.socket_host' and v == 'localhost': 324 warnings.warn("The use of 'localhost' as a socket host can " 325 "cause problems on newer systems, since 'localhost' can " 326 "map to either an IPv4 or an IPv6 address. You should " 327 "use '127.0.0.1' or '[::1]' instead.")
328