5
from cherrypy._cpcompat import iteritems, copykeys, builtins
7
8
class Checker(object):
8
9
"""A checker for CherryPy sites and their mounted applications.
10
on: set this to False to turn off the checker completely.
12
11
When this object is called at engine startup, it executes each
13
of its own methods whose names start with "check_". If you wish
12
of its own methods whose names start with ``check_``. If you wish
14
13
to disable selected checks, simply add a line in your global
15
config which sets the appropriate method to False:
18
checker.check_skipped_app_config = False
20
You may also dynamically add or replace check_* methods in this way.
14
config which sets the appropriate method to False::
17
checker.check_skipped_app_config = False
19
You may also dynamically add or replace ``check_*`` methods in this way.
23
"""If True (the default), run all checks; if False, turn off all checks."""
25
26
def __init__(self):
26
27
self._populate_known_types()
34
35
for name in dir(self):
35
36
if name.startswith("check_"):
36
37
method = getattr(self, name)
37
if method and callable(method):
38
if method and hasattr(method, '__call__'):
40
41
warnings.formatwarning = oldformatwarning
46
47
# This value should be set inside _cpconfig.
47
48
global_config_contained_paths = False
50
def check_app_config_entries_dont_start_with_script_name(self):
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):
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:
64
"The application mounted at %r has config " \
65
"entries that start with its script name: %r" % (sn, key))
67
def check_site_config_entries_in_app_config(self):
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):
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."):
79
msg.append("[%s] %s = %s" % (section, key, value))
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))
49
88
def check_skipped_app_config(self):
50
for sn, app in cherrypy.tree.apps.iteritems():
89
"""Check for mounted Applications that have no config."""
90
for sn, app in cherrypy.tree.apps.items():
51
91
if not isinstance(app, cherrypy.Application):
61
101
warnings.warn(msg)
104
def check_app_config_brackets(self):
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):
111
for key in app.config.keys():
112
if key.startswith("[") or key.endswith("]"):
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))
64
119
def check_static_paths(self):
120
"""Check Application config for incorrect static paths."""
65
121
# Use the dummy Request object in the main thread.
66
122
request = cherrypy.request
67
for sn, app in cherrypy.tree.apps.iteritems():
123
for sn, app in cherrypy.tree.apps.items():
68
124
if not isinstance(app, cherrypy.Application):
131
187
def _compat(self, config):
132
188
"""Process config and warn on each obsolete or deprecated entry."""
133
for section, conf in config.iteritems():
189
for section, conf in config.items():
134
190
if isinstance(conf, dict):
135
for k, v in conf.iteritems():
191
for k, v in conf.items():
136
192
if k in self.obsolete:
137
193
warnings.warn("%r is obsolete. Use %r instead.\n"
138
194
"section: [%s]" %
152
208
def check_compatibility(self):
153
209
"""Process config and warn on each obsolete or deprecated entry."""
154
210
self._compat(cherrypy.config)
155
for sn, app in cherrypy.tree.apps.iteritems():
211
for sn, app in cherrypy.tree.apps.items():
156
212
if not isinstance(app, cherrypy.Application):
158
214
self._compat(app.config)
165
221
def _known_ns(self, app):
167
ns.extend(app.toolboxes.keys())
168
ns.extend(app.namespaces.keys())
169
ns.extend(app.request_class.namespaces.keys())
170
ns.extend(cherrypy.config.namespaces.keys())
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))
171
227
ns += self.extra_config_namespaces
173
for section, conf in app.config.iteritems():
229
for section, conf in app.config.items():
174
230
is_path_section = section.startswith("/")
175
231
if is_path_section and isinstance(conf, dict):
176
for k, v in conf.iteritems():
232
for k, v in conf.items():
177
233
atoms = k.split(".")
178
234
if len(atoms) > 1:
179
235
if atoms[0] not in ns:
198
254
def check_config_namespaces(self):
199
255
"""Process config and warn on each unknown config namespace."""
200
for sn, app in cherrypy.tree.apps.iteritems():
256
for sn, app in cherrypy.tree.apps.items():
201
257
if not isinstance(app, cherrypy.Application):
203
259
self._known_ns(app)
210
266
known_config_types = {}
212
268
def _populate_known_types(self):
214
builtins = [x for x in vars(__builtin__).values()
215
if type(x) is type(str)]
269
b = [x for x in vars(builtins).values()
270
if type(x) is type(str)]
217
272
def traverse(obj, namespace):
218
273
for name in dir(obj):
274
# Hack for 3.2's warning about body_params
275
if name == 'body_params':
219
277
vtype = type(getattr(obj, name, None))
220
if vtype in builtins:
221
279
self.known_config_types[namespace + "." + name] = vtype
223
281
traverse(cherrypy.request, "request")
230
288
msg = ("The config entry %r in section %r is of type %r, "
231
289
"which does not match the expected type %r.")
233
for section, conf in config.iteritems():
291
for section, conf in config.items():
234
292
if isinstance(conf, dict):
235
for k, v in conf.iteritems():
293
for k, v in conf.items():
236
294
if v is not None:
237
295
expected_type = self.known_config_types.get(k, None)
251
309
def check_config_types(self):
252
310
"""Assert that config values are of the same type as default values."""
253
311
self._known_types(cherrypy.config)
254
for sn, app in cherrypy.tree.apps.iteritems():
312
for sn, app in cherrypy.tree.apps.items():
255
313
if not isinstance(app, cherrypy.Application):
257
315
self._known_types(app.config)
262
320
def check_localhost(self):
263
321
"""Warn if any socket_host is 'localhost'. See #711."""
264
for k, v in cherrypy.config.iteritems():
322
for k, v in cherrypy.config.items():
265
323
if k == 'server.socket_host' and v == 'localhost':
266
324
warnings.warn("The use of 'localhost' as a socket host can "
267
325
"cause problems on newer systems, since 'localhost' can "