"Fossies" - the Fresh Open Source Software Archive

Member "cheetah3-3.2.6.post2/Cheetah/SettingsManager.py" (20 Apr 2021, 10207 Bytes) of package /linux/www/cheetah3-3.2.6.post2.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Python source code syntax highlighting (style: standard) with prefixed line numbers. Alternatively you can here view or download the uninterpreted source code file. For more information about "SettingsManager.py" see the Fossies "Dox" file reference documentation and the last Fossies "Diffs" side-by-side code changes report: 3-3.1.0_vs_3-3.2.0.

    1 import os.path
    2 import copy as copyModule
    3 try:
    4     from ConfigParser import ConfigParser
    5 except ImportError:  # PY3
    6     from configparser import ConfigParser
    7 import re
    8 from tokenize import Number
    9 import types
   10 try:
   11     from StringIO import StringIO
   12 except ImportError:
   13     from io import StringIO
   14 from Cheetah.compat import PY2
   15 
   16 
   17 numberRE = re.compile(Number)
   18 complexNumberRE = re.compile(
   19     '[\(]*' + Number + r'[ \t]*\+[ \t]*' + Number + '[\)]*')
   20 
   21 ##################################################
   22 # FUNCTIONS ##
   23 
   24 
   25 def mergeNestedDictionaries(dict1, dict2, copy=False, deepcopy=False):
   26     """Recursively merge the values of dict2 into dict1.
   27 
   28     This little function is very handy for selectively overriding settings in a
   29     settings dictionary that has a nested structure.
   30     """
   31 
   32     if copy:
   33         dict1 = copyModule.copy(dict1)
   34     elif deepcopy:
   35         dict1 = copyModule.deepcopy(dict1)
   36 
   37     for key, val in dict2.items():
   38         if key in dict1 and isinstance(val, dict) \
   39                 and isinstance(dict1[key], dict):
   40             dict1[key] = mergeNestedDictionaries(dict1[key], val)
   41         else:
   42             dict1[key] = val
   43     return dict1
   44 
   45 
   46 def stringIsNumber(S):
   47     """Return True if theString represents a Python number, False otherwise.
   48     This also works for complex numbers and numbers with +/- in front."""
   49 
   50     S = S.strip()
   51 
   52     if S[0] in '-+' and len(S) > 1:
   53         S = S[1:].strip()
   54 
   55     match = complexNumberRE.match(S)
   56     if not match:
   57         match = numberRE.match(S)
   58     if not match or (match.end() != len(S)):
   59         return False
   60     else:
   61         return True
   62 
   63 
   64 def convStringToNum(theString):
   65     """
   66     Convert a string representation of a Python number to the Python version
   67     """
   68     if not stringIsNumber(theString):
   69         raise Error(theString + ' cannot be converted to a Python number')
   70     return eval(theString, {}, {})
   71 
   72 
   73 class Error(Exception):
   74     pass
   75 
   76 
   77 class NoDefault(object):
   78     pass
   79 
   80 
   81 class ConfigParserCaseSensitive(ConfigParser):
   82     """A case sensitive version of the standard Python ConfigParser."""
   83 
   84     def optionxform(self, optionstr):
   85         """Don't change the case as is done in the default implemenation."""
   86         return optionstr
   87 
   88 
   89 class _SettingsCollector(object):
   90     """An abstract base class that provides the methods SettingsManager uses to
   91     collect settings from config files and strings.
   92 
   93     This class only collects settings, it doesn't modify
   94     the _settings dictionary of SettingsManager instances in any way.
   95     """
   96     _ConfigParserClass = ConfigParserCaseSensitive
   97 
   98     def readSettingsFromModule(self, mod, ignoreUnderscored=True):
   99         """Returns all settings from a Python module.
  100         """
  101         S = {}
  102         attrs = vars(mod)
  103         for k, v in attrs.items():
  104             if (ignoreUnderscored and k.startswith('_')):
  105                 continue
  106             else:
  107                 S[k] = v
  108         return S
  109 
  110     def readSettingsFromPySrcStr(self, theString):
  111         """Return a dictionary of the settings in a Python src string."""
  112 
  113         globalsDict = {'True': (1 == 1),
  114                        'False': (0 == 1),
  115                        }
  116         newSettings = {'self': self}
  117         exec((theString + os.linesep), globalsDict, newSettings)
  118         del newSettings['self']
  119         module = types.ModuleType('temp_settings_module')
  120         module.__dict__.update(newSettings)
  121         return self.readSettingsFromModule(module)
  122 
  123     def readSettingsFromConfigFileObj(self, inFile, convert=True):
  124         """Return the settings from a config file that uses the syntax accepted by
  125         Python's standard ConfigParser module (like Windows .ini files).
  126 
  127         NOTE:
  128         this method maintains case unlike the ConfigParser module, unless this
  129         class was initialized with the 'caseSensitive' keyword set to False.
  130 
  131         All setting values are initially parsed as strings. However, If the
  132         'convert' arg is True this method will do the following value
  133         conversions:
  134 
  135         * all Python numeric literals will be coverted from string to number
  136 
  137         * The string 'None' will be converted to the Python value None
  138 
  139         * The string 'True' will be converted to a Python truth value
  140 
  141         * The string 'False' will be converted to a Python false value
  142 
  143         * Any string starting with 'python:' will be treated
  144           as a Python literal or expression that needs to be eval'd.
  145           This approach is useful for declaring lists and dictionaries.
  146 
  147         If a config section titled 'Globals' is present the options defined
  148         under it will be treated as top-level settings.
  149         """
  150 
  151         p = self._ConfigParserClass()
  152         if PY2:
  153             p.readfp(inFile)
  154         else:
  155             p.read_file(inFile)
  156         sects = p.sections()
  157         newSettings = {}
  158 
  159         sects = p.sections()
  160         newSettings = {}
  161 
  162         for s in sects:
  163             newSettings[s] = {}
  164             for o in p.options(s):
  165                 if o != '__name__':
  166                     newSettings[s][o] = p.get(s, o)
  167 
  168         # loop through new settings -> deal with global settings, numbers,
  169         # booleans and None ++ also deal with 'importSettings' commands
  170 
  171         for sect, subDict in list(newSettings.items()):
  172             for key, val in list(subDict.items()):
  173                 if convert:
  174                     if val.lower().startswith('python:'):
  175                         subDict[key] = eval(val[7:], {}, {})
  176                     if val.lower() == 'none':
  177                         subDict[key] = None
  178                     if val.lower() == 'true':
  179                         subDict[key] = True
  180                     if val.lower() == 'false':
  181                         subDict[key] = False
  182                     if stringIsNumber(val):
  183                         subDict[key] = convStringToNum(val)
  184 
  185                 # now deal with any 'importSettings' commands
  186                 if key.lower() == 'importsettings':
  187                     if val.find(';') < 0:
  188                         importedSettings = self.readSettingsFromPySrcFile(val)
  189                     else:
  190                         path = val.split(';')[0]
  191                         rest = ''.join(val.split(';')[1:]).strip()
  192                         parentDict = self.readSettingsFromPySrcFile(path)  # noqa: E501,F841
  193                         importedSettings = eval('parentDict["' + rest + '"]')
  194 
  195                     subDict.update(mergeNestedDictionaries(subDict,
  196                                                            importedSettings))
  197 
  198             if sect.lower() == 'globals':
  199                 newSettings.update(newSettings[sect])
  200                 del newSettings[sect]
  201 
  202         return newSettings
  203 
  204 
  205 class SettingsManager(_SettingsCollector):
  206     """A mixin class that provides facilities for managing application settings.
  207 
  208     SettingsManager is designed to work well with nested settings dictionaries
  209     of any depth.
  210     """
  211 
  212     def __init__(self):
  213         super(SettingsManager, self).__init__()
  214         self._settings = {}
  215         self._initializeSettings()
  216 
  217     def _defaultSettings(self):
  218         return {}
  219 
  220     def _initializeSettings(self):
  221         """A hook that allows for complex setting initialization sequences
  222         that involve references to 'self' or other settings.  For example:
  223               self._settings['myCalcVal'] = self._settings['someVal'] * 15
  224         This method should be called by the class' __init__() method
  225         when needed.
  226         The dummy implementation should be reimplemented by subclasses.
  227         """
  228         pass
  229 
  230     # core post startup methods
  231 
  232     def setting(self, name, default=NoDefault):
  233         """
  234         Get a setting from self._settings, with or without a default value
  235         """
  236 
  237         if default is NoDefault:
  238             return self._settings[name]
  239         else:
  240             return self._settings.get(name, default)
  241 
  242     def hasSetting(self, key):
  243         """True/False"""
  244         return key in self._settings
  245 
  246     def setSetting(self, name, value):
  247         """Set a setting in self._settings."""
  248         self._settings[name] = value
  249 
  250     def settings(self):
  251         """Return a reference to the settings dictionary"""
  252         return self._settings
  253 
  254     def copySettings(self):
  255         """Returns a shallow copy of the settings dictionary"""
  256         return copyModule.copy(self._settings)
  257 
  258     def deepcopySettings(self):
  259         """Returns a deep copy of the settings dictionary"""
  260         return copyModule.deepcopy(self._settings)
  261 
  262     def updateSettings(self, newSettings, merge=True):
  263         """
  264         Update the settings with a selective merge or a complete overwrite
  265         """
  266         if merge:
  267             mergeNestedDictionaries(self._settings, newSettings)
  268         else:
  269             self._settings.update(newSettings)
  270 
  271     # source specific update methods
  272 
  273     def updateSettingsFromPySrcStr(self, theString, merge=True):
  274         """Update the settings from a code in a Python src string."""
  275 
  276         newSettings = self.readSettingsFromPySrcStr(theString)
  277         self.updateSettings(newSettings,
  278                             merge=newSettings.get('mergeSettings', merge))
  279 
  280     def updateSettingsFromConfigFileObj(self, inFile,
  281                                         convert=True, merge=True):
  282         """See the docstring for .updateSettingsFromConfigFile()
  283 
  284         The caller of this method is responsible for closing the inFile file
  285         object."""
  286 
  287         newSettings = self.readSettingsFromConfigFileObj(inFile,
  288                                                          convert=convert)
  289         self.updateSettings(newSettings,
  290                             merge=newSettings.get('mergeSettings', merge))
  291 
  292     def updateSettingsFromConfigStr(self, configStr, convert=True, merge=True):
  293         """See the docstring for .updateSettingsFromConfigFile()
  294         """
  295 
  296         configStr = '[globals]\n' + configStr
  297         inFile = StringIO(configStr)
  298         newSettings = self.readSettingsFromConfigFileObj(inFile,
  299                                                          convert=convert)
  300         self.updateSettings(newSettings,
  301                             merge=newSettings.get('mergeSettings', merge))