Commits

Robert Brewer committed d1f84ea

Fix for #356 (formalize server.environment as a set of config defaults). New server.logFileNotFound boolean.

  • Participants
  • Parent commits 953b47a

Comments (0)

Files changed (9)

cherrypy/_cperror.py

         if cherrypy.config.get('server.logTracebacks', True):
             cherrypy.log(tb)
         
-        defaultOn = (cherrypy.config.get('server.environment') == 'development')
-        if not cherrypy.config.get('server.showTracebacks', defaultOn):
+        if not cherrypy.config.get('server.showTracebacks', False):
             tb = None
         
         # In all cases, finalize will be called after this method,

cherrypy/_cphttptools.py

         
         # Failure in _cpOnError, error filter, or finalize.
         # Bypass them all.
-        defaultOn = (cherrypy.config.get('server.environment') == 'development')
-        if cherrypy.config.get('server.showTracebacks', defaultOn):
+        if cherrypy.config.get('server.showTracebacks', False):
             body = self.dbltrace % (_cputil.formatExc(exc),
                                     _cputil.formatExc())
         else:

cherrypy/_cpserver.py

         # our own webserver, and therefore could do Very Bad Things when
         # autoreload calls sys.exit.
         if serverClass is not None:
-            defaultOn = (conf("server.environment") == "development")
-            if conf('autoreload.on', defaultOn):
+            if conf('autoreload.on', False):
                 try:
                     autoreload.main(self._start)
                 except KeyboardInterrupt:

cherrypy/_cpwsgi.py

     except:
         tb = _cputil.formatExc()
         cherrypy.log(tb)
-        defaultOn = (cherrypy.config.get('server.environment') == 'development')
-        if not cherrypy.config.get("server.showTracebacks", defaultOn):
+        if not cherrypy.config.get("server.showTracebacks", False):
             tb = ""
         s, h, b = _cputil.bareError(tb)
         exc = sys.exc_info()

cherrypy/config.py

     'server.socketHost': '',
     'server.socketFile': '',
     'server.socketQueueSize': 5,
-    
-    'server.environment': 'development',
     'server.protocolVersion': 'HTTP/1.0',
     'server.logToScreen': True,
     'server.logFile': '',
     'server.reverseDNS': False,
     'server.threadPool': 0,
+    'server.environment': "development",
     }
 
+environments = {
+    "development": {
+        'autoreload.on': True,
+        'logDebugInfoFilter.on': True,
+        'server.logFileNotFound': True,
+        'server.showTracebacks': True,
+        },
+    "staging": {
+        'autoreload.on': False,
+        'logDebugInfoFilter.on': False,
+        'server.logFileNotFound': False,
+        'server.showTracebacks': False,
+        },
+    "production": {
+        'autoreload.on': False,
+        'logDebugInfoFilter.on': False,
+        'server.logFileNotFound': False,
+        'server.showTracebacks': False,
+        },
+    }
+
+def update(updateMap=None, file=None, overwrite=True):
+    """Update configMap from a dictionary or a config file.
+    
+    If overwrite is False then the update will not modify values
+    already defined in the configMap.
+    """
+    if updateMap is None:
+        updateMap = {}
+    
+    if file:
+        if file not in autoreload.reloadFiles:
+            autoreload.reloadFiles.append(file)
+        updateMap = updateMap.copy()
+        updateMap.update(dict_from_config_file(file))
+    
+    # Load new conf into cherrypy.configMap
+    for section, valueMap in updateMap.iteritems():
+        # Handle shortcut syntax for "global" section
+        #   example: update({'server.socketPort': 80})
+        if not isinstance(valueMap, dict):
+            valueMap = {section: valueMap}
+            section = 'global'
+        
+        bucket = configMap.setdefault(section, {})
+        if overwrite:
+            bucket.update(valueMap)
+        else:
+            for key, value in valueMap.iteritems():
+                bucket.setdefault(key, value)
+
 def reset(useDefaults=True):
     """Clear configuration and restore defaults"""
     configMap.clear()
     if useDefaults:
-        configMap["global"] = defaultGlobal.copy()
+        update(defaultGlobal)
 reset()
 
-def update(updateMap=None, file=None, override=True):
-    """Update configMap from a dictionary or a config file
-    If override is True then the update will not modify values already defined
-    in the configMap.
-    """
-    if updateMap:
-        for section, valueMap in updateMap.iteritems():
-            if not isinstance(valueMap, dict):
-                # Shortcut syntax
-                #   ex: update({'server.socketPort': 80})
-                valueMap = {section: valueMap}
-                section = 'global'
-            sectionMap = configMap.setdefault(section, {})
-            if override:
-                sectionMap.update(valueMap)
-            else:
-                for key, value in valueMap.iteritems():
-                    sectionMap.setdefault(key, value)
-    if file:
-        if file not in autoreload.reloadFiles:
-            autoreload.reloadFiles.append(file)
-        _load(file, override)
-
 def get(key, defaultValue=None, returnSection=False, path = None):
     """Return the configuration value corresponding to key
     If specified, return defaultValue on lookup failure. If returnSection is
         
         try:
             result = configMap[path][key]
+            break
         except KeyError:
-            if path == "global":
-                result = defaultValue
-            elif path == "/":
-                path = "global"
-                continue
-            else:
-                path = path[:path.rfind("/")]
-                continue
-        break
+            pass
+        
+        try:
+            # Check for a server.environment entry at this node.
+            env = configMap[path]["server.environment"]
+            result = environments[env][key]
+            break
+        except KeyError:
+            pass
+        
+        if path == "global":
+            result = defaultValue
+            break
+        
+        # Move one node up the tree and try again.
+        if path == "/":
+            path = "global"
+        else:
+            path = path[:path.rfind("/")]
     
     if returnSection:
         return path
     return result
 
 
-def _load(configFile, override=True):
-    """Merge an INI file into configMap
-    If override is false, preserve values already in the configMap.
-    """
-    
-    conf = dict_from_config_file(configFile)
-    
-    # Load new conf into cherrypy.configMap
-    for section, options in conf.iteritems():
-        bucket = configMap.setdefault(section, {})
-        for key, value in options.iteritems():
-            if override:
-                bucket[key] = value
-            else:
-                bucket.setdefault(key, value)
-
-
 def outputConfigMap():
     """Log server configuration parameters"""
     cherrypy.log("Server parameters:", 'CONFIG')

cherrypy/lib/cptools.py

     try:
         stat = os.stat(path)
     except OSError:
-        if cherrypy.config.get('server.environment') == 'development':
+        if cherrypy.config.get('server.logFileNotFound', False):
             cherrypy.log("    NOT FOUND file: %s" % path, "DEBUG")
         raise cherrypy.NotFound()
     

cherrypy/lib/filter/logdebuginfofilter.py

         cherrypy.request.startBuilTime = time.time()
     
     def beforeFinalize(self):
-        if cherrypy.config.get('server.environment') == 'development':
-            # In "dev" environment, log everything by default
-            defaultOn = True
-        else:
-            defaultOn = False
-        
-        if not cherrypy.config.get('logDebugInfoFilter.on', defaultOn):
+        if not cherrypy.config.get('logDebugInfoFilter.on', False):
             return
         
         mimelist = cherrypy.config.get('logDebugInfoFilter.mimeTypeList', ['text/html'])

cherrypy/test/test_config.py

+"""
+Copyright (c) 2005, CherryPy Team (team@cherrypy.org)
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification, 
+are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright notice, 
+      this list of conditions and the following disclaimer.
+    * Redistributions in binary form must reproduce the above copyright notice, 
+      this list of conditions and the following disclaimer in the documentation 
+      and/or other materials provided with the distribution.
+    * Neither the name of the CherryPy Team nor the names of its contributors 
+      may be used to endorse or promote products derived from this software 
+      without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE 
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+"""
+
+"""Tests for the CherryPy configuration system."""
+
+import cherrypy
+
+class Root:
+    def index(self, key):
+        return cherrypy.config.get(key, "None")
+    index.exposed = True
+    _global = index
+    xyz = index
+
+class Foo:
+    def index(self, key):
+        return cherrypy.config.get(key, "None")
+    index.exposed = True
+    bar = index
+    nex = index
+
+class Env:
+    def index(self, key):
+        return str(cherrypy.config.get(key, "None"))
+    index.exposed = True
+    prod = index
+
+cherrypy.root = Root()
+cherrypy.root.foo = Foo()
+cherrypy.root.env = Env()
+cherrypy.config.update({
+    'global': {'server.logToScreen': False,
+               'server.environment': 'production',
+               'server.showTracebacks': True,
+               'server.protocolVersion': "HTTP/1.1",
+               },
+    '/': {
+        'foo': 'this',
+        'bar': 'that',
+        },
+    '/foo': {
+        'foo': 'this2',
+        'baz': 'that2',
+        },
+    '/foo/bar': {
+        'foo': 'this3',
+        'bax': 'this4',
+        },
+    '/env': {'server.environment': 'development'},
+    '/env/prod': {'server.environment': 'production'},
+})
+
+# Shortcut syntax--should get put in the "global" bucket
+cherrypy.config.update({'luxuryyacht': 'throatwobblermangrove'})
+
+import helper
+
+class ConfigTests(helper.CPWebCase):
+    
+    def testConfig(self):
+        tests = [
+            ('*',        'luxuryyacht', 'throatwobblermangrove'),
+            ('/',        'nex', 'None'),
+            ('/',        'foo', 'this'),
+            ('/',        'bar', 'that'),
+            ('/xyz',     'foo', 'this'),
+            ('/foo/',    'foo', 'this2'),
+            ('/foo/',    'bar', 'that'),
+            ('/foo/',    'bax', 'None'),
+            ('/foo/bar', 'baz', 'that2'),
+            ('/foo/nex', 'baz', 'that2'),
+        ]
+        for path, key, expected in tests:
+            self.getPage(path + "?key=" + key)
+            self.assertBody(expected)
+    
+    def testEnvironments(self):
+        for key, val in cherrypy.config.environments['development'].iteritems():
+            self.getPage("/env/?key=" + key)
+            # The dev environment will have logdebuginfo data
+            self.assertEqual(self.body.split("\n")[0], str(val))
+        for key, val in cherrypy.config.environments['production'].iteritems():
+            self.getPage("/env/prod/?key=" + key)
+            self.assertBody(str(val))
+
+
+if __name__ == '__main__':
+    helper.testmain()

cherrypy/test/test_core.py

                'server.showTracebacks': True,
                'server.protocolVersion': "HTTP/1.1",
                },
-    '/': {
-        'foo': 'this',
-        'bar': 'that',
-    },
-    '/foo': {
-        'foo': 'this2',
-        'baz': 'that2',
-    },
-    '/foo/bar': {
-        'foo': 'this3',
-        'bax': 'this4',
-    },
     '/flatten': {
         'server.logFile': logFile,
         'server.logAccessFile': logAccessFile,
     },
 })
 
-# Shortcut syntax--should get put in the "global" bucket
-cherrypy.config.update({'luxuryyacht': 'throatwobblermangrove'})
 
 import helper
 
 class CoreRequestHandlingTest(helper.CPWebCase):
     
-    def testConfig(self):
-        tests = [
-            ('global',   'luxuryyacht', 'throatwobblermangrove'),
-            ('/',        'nex', None   ),
-            ('/',        'foo', 'this' ),
-            ('/',        'bar', 'that' ),
-            ('/xyz',     'foo', 'this' ),
-            ('/foo',     'foo', 'this2'),
-            ('/foo',     'bar', 'that' ),
-            ('/foo',     'bax', None   ),
-            ('/foo/bar', 'baz', 'that2'),
-            ('/foo/nex', 'baz', 'that2'),
-        ]
-        for path, key, expected in tests:
-            from cherrypy import _cphttptools
-            cherrypy.serving.request = r = _cphttptools.Request("", "", "")
-            r.objectPath = r.path = path
-            result = cherrypy.config.get(key, None)
-            self.assertEqual(result, expected)
-    
     def testParams(self):
         self.getPage("/params/?thing=a")
         self.assertBody("'a'")