Mercurial > hgrepos > Python > libs > ConfigMix
view configmix/config.py @ 156:e2e8d21b4122
Adjust copyright to year 2019
| author | Franz Glasner <fzglas.hg@dom66.de> |
|---|---|
| date | Thu, 21 Feb 2019 22:16:05 +0100 |
| parents | a5339d39af5c |
| children | 2e66178a09d8 |
line wrap: on
line source
# -*- coding: utf-8 -*- #- # :Copyright: (c) 2015-2019, Franz Glasner. All rights reserved. # :License: 3-clause BSD. See LICENSE.txt for details. #- """The unified configuration dictionary with attribute support or variable substitution. """ from __future__ import division, absolute_import, print_function import warnings try: from collections import OrderedDict as ConfigurationBase except ImportError: try: from ordereddict import OrderedDict as ConfigurationBase except ImportError: ConfigurationBase = dict from .variables import lookup_varns, lookup_filter from .compat import u __all__ = ["Configuration"] _MARKER = object() class _AttributeDict(ConfigurationBase): def __getattr__(self, name): try: v = self[name] except KeyError: raise AttributeError("%s has no attribute %r" % (type(self), name)) else: # Wrap a dict into another dict with attribute access support if isinstance(v, dict): return _AttributeDict(v) else: return v class Configuration(_AttributeDict): """The configuration dictionary with attribute support or variable substitution. .. note:: When retriving by attribute names variables will *not* substituted. """ def getvar(self, varname, default=_MARKER): """Get a variable of the form ``[ns:][[key1.]key2.]name`` - including variables from other namespaces. No variable expansion is done and no filters are applied. """ varns, varname = self._split_ns(varname) try: if not varns: lookupfn = self._lookupvar else: lookupfn = lookup_varns(varns) varvalue = lookupfn(varname) except KeyError: if default is _MARKER: raise KeyError("Variable %r not found" % varname) else: return default else: return varvalue def getvar_s(self, varname, default=_MARKER): """Get a variable - including variables from other namespaces. `varname` is interpreted as in :meth:`.getvar`. But variables will be expanded recursively within the variable values and filters are applied. For more details see chapter :ref:`variable-expansion`. """ try: obj = self.getvar(varname) return self.substitute_variables_in_obj(obj) except KeyError: if default is _MARKER: raise else: return default def getintvar_s(self, varname, default=_MARKER): """Get a (possibly substituted) variable and coerce text to a number. """ s = self.getvar_s(varname, default) if isinstance(s, self._TEXTTYPE): return int(s, 0) else: return s def getboolvar_s(self, varname, default=_MARKER): """Get a (possibly substituted) variable and convert text to a boolean """ s = self.getvar_s(varname, default) if isinstance(s, self._TEXTTYPE): sl = s.strip().lower() if not sl in self._BOOL_CVT: raise ValueError("Not a boolean: %r" % s) return self._BOOL_CVT[sl] else: return s # Conversion of booleans _BOOL_CVT = {'1': True, 'yes': True, 'true': True, 'on': True, '0': False, 'no': False, 'false': False, 'off': False} def getfloatvar_s(self, varname, default=_MARKER): """Get a (possibly substituted) variable and convert text to a float """ s = self.getvar_s(varname, default) if isinstance(s, self._TEXTTYPE): return float(s) else: return s def _split_ns(self, s): nameparts = s.split(':', 1) if len(nameparts) == 1: return (None, s, ) else: return (nameparts[0], nameparts[1], ) def _split_filters(self, s): nameparts = s.split('|') if len(nameparts) == 1: return (s, [], ) else: return (nameparts[0].rstrip(), nameparts[1:], ) def _lookupvar(self, key, default=_MARKER): """Lookup a variable. If no default is given an unexisting `key` raises a `KeyError` else `default` is returned. """ parts = key.split('.') try: v = self[parts[0]] for p in parts[1:]: v = v[p] except TypeError: raise KeyError("Configuration variable %r not found (missing intermediate keys?)" % key) except KeyError: if default is _MARKER: raise KeyError("Configuration variable %r not found" % key) else: return default return v # Speed _TEXTTYPE = type(u("")) def substitute_variables_in_obj(self, obj): """Recursively expand variables in the object tree `obj`.""" if isinstance(obj, self._TEXTTYPE): # a string - really replace the value return self.expand_variable(obj) elif isinstance(obj, list): return [self.substitute_variables_in_obj(i) for i in obj] elif isinstance(obj, tuple): tmp = [self.substitute_variables_in_obj(i) for i in obj] return type(obj)(tmp) elif isinstance(obj, dict): newdict = type(obj)() for k in obj: newdict[k] = self.substitute_variables_in_obj(obj[k]) return newdict elif isinstance(obj, set): newset = type(obj)() for i in obj: newset.add(self.substitute_variables_in_obj(i)) else: return obj # Speed _STARTTOK = u(b"{{") _ENDTOK = u(b"}}") def expand_variable(self, s): """Expand variables in a single string""" start = s.find(self._STARTTOK, 0) while start != -1: end = s.find(self._ENDTOK, start) if end < 0: return s varname, filters = self._split_filters(s[start+2:end]) try: varvalue = self._apply_filters(filters, self.getvar_s(varname)) except KeyError: warnings.warn("Cannot expand variable %r in string " "%r" % (varname, s, ), UserWarning, stacklevel=1) raise if varvalue is None: varvalue = u("") replaced = u(b"{0}{1}").format(s[:start], varvalue) s = u(b"{0}{1}").format(replaced, s[end+2:]) # don't re-evaluate because `self.getvar_s()` expands already start = s.find(self._STARTTOK, len(replaced)) return s def _apply_filters(self, filters, value): for name in filters: try: filterfn = lookup_filter(name) except KeyError: # # Convert to NameError because we find a missing filters # a very serious error. # raise NameError("Filter %r not found" % name) else: value = filterfn(self, value) return value
