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