1"""Tests for the CherryPy configuration system.""" 2 3import io 4import os 5import sys 6import unittest 7 8import six 9 10import cherrypy 11 12from cherrypy.test import helper 13 14 15localDir = os.path.join(os.getcwd(), os.path.dirname(__file__)) 16 17 18def StringIOFromNative(x): 19 return io.StringIO(six.text_type(x)) 20 21 22def setup_server(): 23 24 @cherrypy.config(foo='this', bar='that') 25 class Root: 26 27 def __init__(self): 28 cherrypy.config.namespaces['db'] = self.db_namespace 29 30 def db_namespace(self, k, v): 31 if k == 'scheme': 32 self.db = v 33 34 @cherrypy.expose(alias=('global_', 'xyz')) 35 def index(self, key): 36 return cherrypy.request.config.get(key, 'None') 37 38 @cherrypy.expose 39 def repr(self, key): 40 return repr(cherrypy.request.config.get(key, None)) 41 42 @cherrypy.expose 43 def dbscheme(self): 44 return self.db 45 46 @cherrypy.expose 47 @cherrypy.config(**{'request.body.attempt_charsets': ['utf-16']}) 48 def plain(self, x): 49 return x 50 51 favicon_ico = cherrypy.tools.staticfile.handler( 52 filename=os.path.join(localDir, '../favicon.ico')) 53 54 @cherrypy.config(foo='this2', baz='that2') 55 class Foo: 56 57 @cherrypy.expose 58 def index(self, key): 59 return cherrypy.request.config.get(key, 'None') 60 nex = index 61 62 @cherrypy.expose 63 @cherrypy.config(**{'response.headers.X-silly': 'sillyval'}) 64 def silly(self): 65 return 'Hello world' 66 67 # Test the expose and config decorators 68 @cherrypy.config(foo='this3', **{'bax': 'this4'}) 69 @cherrypy.expose 70 def bar(self, key): 71 return repr(cherrypy.request.config.get(key, None)) 72 73 class Another: 74 75 @cherrypy.expose 76 def index(self, key): 77 return str(cherrypy.request.config.get(key, 'None')) 78 79 def raw_namespace(key, value): 80 if key == 'input.map': 81 handler = cherrypy.request.handler 82 83 def wrapper(): 84 params = cherrypy.request.params 85 for name, coercer in list(value.items()): 86 try: 87 params[name] = coercer(params[name]) 88 except KeyError: 89 pass 90 return handler() 91 cherrypy.request.handler = wrapper 92 elif key == 'output': 93 handler = cherrypy.request.handler 94 95 def wrapper(): 96 # 'value' is a type (like int or str). 97 return value(handler()) 98 cherrypy.request.handler = wrapper 99 100 @cherrypy.config(**{'raw.output': repr}) 101 class Raw: 102 103 @cherrypy.expose 104 @cherrypy.config(**{'raw.input.map': {'num': int}}) 105 def incr(self, num): 106 return num + 1 107 108 if not six.PY3: 109 thing3 = "thing3: unicode('test', errors='ignore')" 110 else: 111 thing3 = '' 112 113 ioconf = StringIOFromNative(""" 114[/] 115neg: -1234 116filename: os.path.join(sys.prefix, "hello.py") 117thing1: cherrypy.lib.httputil.response_codes[404] 118thing2: __import__('cherrypy.tutorial', globals(), locals(), ['']).thing2 119%s 120complex: 3+2j 121mul: 6*3 122ones: "11" 123twos: "22" 124stradd: %%(ones)s + %%(twos)s + "33" 125 126[/favicon.ico] 127tools.staticfile.filename = %r 128""" % (thing3, os.path.join(localDir, 'static/dirback.jpg'))) 129 130 root = Root() 131 root.foo = Foo() 132 root.raw = Raw() 133 app = cherrypy.tree.mount(root, config=ioconf) 134 app.request_class.namespaces['raw'] = raw_namespace 135 136 cherrypy.tree.mount(Another(), '/another') 137 cherrypy.config.update({'luxuryyacht': 'throatwobblermangrove', 138 'db.scheme': r'sqlite///memory', 139 }) 140 141 142# Client-side code # 143 144 145class ConfigTests(helper.CPWebCase): 146 setup_server = staticmethod(setup_server) 147 148 def testConfig(self): 149 tests = [ 150 ('/', 'nex', 'None'), 151 ('/', 'foo', 'this'), 152 ('/', 'bar', 'that'), 153 ('/xyz', 'foo', 'this'), 154 ('/foo/', 'foo', 'this2'), 155 ('/foo/', 'bar', 'that'), 156 ('/foo/', 'bax', 'None'), 157 ('/foo/bar', 'baz', "'that2'"), 158 ('/foo/nex', 'baz', 'that2'), 159 # If 'foo' == 'this', then the mount point '/another' leaks into 160 # '/'. 161 ('/another/', 'foo', 'None'), 162 ] 163 for path, key, expected in tests: 164 self.getPage(path + '?key=' + key) 165 self.assertBody(expected) 166 167 expectedconf = { 168 # From CP defaults 169 'tools.log_headers.on': False, 170 'tools.log_tracebacks.on': True, 171 'request.show_tracebacks': True, 172 'log.screen': False, 173 'environment': 'test_suite', 174 'engine.autoreload.on': False, 175 # From global config 176 'luxuryyacht': 'throatwobblermangrove', 177 # From Root._cp_config 178 'bar': 'that', 179 # From Foo._cp_config 180 'baz': 'that2', 181 # From Foo.bar._cp_config 182 'foo': 'this3', 183 'bax': 'this4', 184 } 185 for key, expected in expectedconf.items(): 186 self.getPage('/foo/bar?key=' + key) 187 self.assertBody(repr(expected)) 188 189 def testUnrepr(self): 190 self.getPage('/repr?key=neg') 191 self.assertBody('-1234') 192 193 self.getPage('/repr?key=filename') 194 self.assertBody(repr(os.path.join(sys.prefix, 'hello.py'))) 195 196 self.getPage('/repr?key=thing1') 197 self.assertBody(repr(cherrypy.lib.httputil.response_codes[404])) 198 199 if not getattr(cherrypy.server, 'using_apache', False): 200 # The object ID's won't match up when using Apache, since the 201 # server and client are running in different processes. 202 self.getPage('/repr?key=thing2') 203 from cherrypy.tutorial import thing2 204 self.assertBody(repr(thing2)) 205 206 if not six.PY3: 207 self.getPage('/repr?key=thing3') 208 self.assertBody(repr(six.text_type('test'))) 209 210 self.getPage('/repr?key=complex') 211 self.assertBody('(3+2j)') 212 213 self.getPage('/repr?key=mul') 214 self.assertBody('18') 215 216 self.getPage('/repr?key=stradd') 217 self.assertBody(repr('112233')) 218 219 def testRespNamespaces(self): 220 self.getPage('/foo/silly') 221 self.assertHeader('X-silly', 'sillyval') 222 self.assertBody('Hello world') 223 224 def testCustomNamespaces(self): 225 self.getPage('/raw/incr?num=12') 226 self.assertBody('13') 227 228 self.getPage('/dbscheme') 229 self.assertBody(r'sqlite///memory') 230 231 def testHandlerToolConfigOverride(self): 232 # Assert that config overrides tool constructor args. Above, we set 233 # the favicon in the page handler to be '../favicon.ico', 234 # but then overrode it in config to be './static/dirback.jpg'. 235 self.getPage('/favicon.ico') 236 self.assertBody(open(os.path.join(localDir, 'static/dirback.jpg'), 237 'rb').read()) 238 239 def test_request_body_namespace(self): 240 self.getPage('/plain', method='POST', headers=[ 241 ('Content-Type', 'application/x-www-form-urlencoded'), 242 ('Content-Length', '13')], 243 body=b'\xff\xfex\x00=\xff\xfea\x00b\x00c\x00') 244 self.assertBody('abc') 245 246 247class VariableSubstitutionTests(unittest.TestCase): 248 setup_server = staticmethod(setup_server) 249 250 def test_config(self): 251 from textwrap import dedent 252 253 # variable substitution with [DEFAULT] 254 conf = dedent(""" 255 [DEFAULT] 256 dir = "/some/dir" 257 my.dir = %(dir)s + "/sub" 258 259 [my] 260 my.dir = %(dir)s + "/my/dir" 261 my.dir2 = %(my.dir)s + '/dir2' 262 263 """) 264 265 fp = StringIOFromNative(conf) 266 267 cherrypy.config.update(fp) 268 self.assertEqual(cherrypy.config['my']['my.dir'], '/some/dir/my/dir') 269 self.assertEqual(cherrypy.config['my'] 270 ['my.dir2'], '/some/dir/my/dir/dir2') 271 272 273class CallablesInConfigTest(unittest.TestCase): 274 setup_server = staticmethod(setup_server) 275 276 def test_call_with_literal_dict(self): 277 from textwrap import dedent 278 conf = dedent(""" 279 [my] 280 value = dict(**{'foo': 'bar'}) 281 """) 282 fp = StringIOFromNative(conf) 283 cherrypy.config.update(fp) 284 self.assertEqual(cherrypy.config['my']['value'], {'foo': 'bar'}) 285 286 def test_call_with_kwargs(self): 287 from textwrap import dedent 288 conf = dedent(""" 289 [my] 290 value = dict(foo="buzz", **cherrypy._test_dict) 291 """) 292 test_dict = { 293 'foo': 'bar', 294 'bar': 'foo', 295 'fizz': 'buzz' 296 } 297 cherrypy._test_dict = test_dict 298 fp = StringIOFromNative(conf) 299 cherrypy.config.update(fp) 300 test_dict['foo'] = 'buzz' 301 self.assertEqual(cherrypy.config['my']['value']['foo'], 'buzz') 302 self.assertEqual(cherrypy.config['my']['value'], test_dict) 303 del cherrypy._test_dict 304