Index: third_party/cherrypy/lib/reprconf.py |
=================================================================== |
--- third_party/cherrypy/lib/reprconf.py (revision 0) |
+++ third_party/cherrypy/lib/reprconf.py (revision 0) |
@@ -0,0 +1,485 @@ |
+"""Generic configuration system using unrepr. |
+ |
+Configuration data may be supplied as a Python dictionary, as a filename, |
+or as an open file object. When you supply a filename or file, Python's |
+builtin ConfigParser is used (with some extensions). |
+ |
+Namespaces |
+---------- |
+ |
+Configuration keys are separated into namespaces by the first "." in the key. |
+ |
+The only key that cannot exist in a namespace is the "environment" entry. |
+This special entry 'imports' other config entries from a template stored in |
+the Config.environments dict. |
+ |
+You can define your own namespaces to be called when new config is merged |
+by adding a named handler to Config.namespaces. The name can be any string, |
+and the handler must be either a callable or a context manager. |
+""" |
+ |
+try: |
+ # Python 3.0+ |
+ from configparser import ConfigParser |
+except ImportError: |
+ from ConfigParser import ConfigParser |
+ |
+try: |
+ set |
+except NameError: |
+ from sets import Set as set |
+ |
+try: |
+ basestring |
+except NameError: |
+ basestring = str |
+ |
+try: |
+ # Python 3 |
+ import builtins |
+except ImportError: |
+ # Python 2 |
+ import __builtin__ as builtins |
+ |
+import operator as _operator |
+import sys |
+ |
+def as_dict(config): |
+ """Return a dict from 'config' whether it is a dict, file, or filename.""" |
+ if isinstance(config, basestring): |
+ config = Parser().dict_from_file(config) |
+ elif hasattr(config, 'read'): |
+ config = Parser().dict_from_file(config) |
+ return config |
+ |
+ |
+class NamespaceSet(dict): |
+ """A dict of config namespace names and handlers. |
+ |
+ Each config entry should begin with a namespace name; the corresponding |
+ namespace handler will be called once for each config entry in that |
+ namespace, and will be passed two arguments: the config key (with the |
+ namespace removed) and the config value. |
+ |
+ Namespace handlers may be any Python callable; they may also be |
+ Python 2.5-style 'context managers', in which case their __enter__ |
+ method should return a callable to be used as the handler. |
+ See cherrypy.tools (the Toolbox class) for an example. |
+ """ |
+ |
+ def __call__(self, config): |
+ """Iterate through config and pass it to each namespace handler. |
+ |
+ config |
+ A flat dict, where keys use dots to separate |
+ namespaces, and values are arbitrary. |
+ |
+ The first name in each config key is used to look up the corresponding |
+ namespace handler. For example, a config entry of {'tools.gzip.on': v} |
+ will call the 'tools' namespace handler with the args: ('gzip.on', v) |
+ """ |
+ # Separate the given config into namespaces |
+ ns_confs = {} |
+ for k in config: |
+ if "." in k: |
+ ns, name = k.split(".", 1) |
+ bucket = ns_confs.setdefault(ns, {}) |
+ bucket[name] = config[k] |
+ |
+ # I chose __enter__ and __exit__ so someday this could be |
+ # rewritten using Python 2.5's 'with' statement: |
+ # for ns, handler in self.iteritems(): |
+ # with handler as callable: |
+ # for k, v in ns_confs.get(ns, {}).iteritems(): |
+ # callable(k, v) |
+ for ns, handler in self.items(): |
+ exit = getattr(handler, "__exit__", None) |
+ if exit: |
+ callable = handler.__enter__() |
+ no_exc = True |
+ try: |
+ try: |
+ for k, v in ns_confs.get(ns, {}).items(): |
+ callable(k, v) |
+ except: |
+ # The exceptional case is handled here |
+ no_exc = False |
+ if exit is None: |
+ raise |
+ if not exit(*sys.exc_info()): |
+ raise |
+ # The exception is swallowed if exit() returns true |
+ finally: |
+ # The normal and non-local-goto cases are handled here |
+ if no_exc and exit: |
+ exit(None, None, None) |
+ else: |
+ for k, v in ns_confs.get(ns, {}).items(): |
+ handler(k, v) |
+ |
+ def __repr__(self): |
+ return "%s.%s(%s)" % (self.__module__, self.__class__.__name__, |
+ dict.__repr__(self)) |
+ |
+ def __copy__(self): |
+ newobj = self.__class__() |
+ newobj.update(self) |
+ return newobj |
+ copy = __copy__ |
+ |
+ |
+class Config(dict): |
+ """A dict-like set of configuration data, with defaults and namespaces. |
+ |
+ May take a file, filename, or dict. |
+ """ |
+ |
+ defaults = {} |
+ environments = {} |
+ namespaces = NamespaceSet() |
+ |
+ def __init__(self, file=None, **kwargs): |
+ self.reset() |
+ if file is not None: |
+ self.update(file) |
+ if kwargs: |
+ self.update(kwargs) |
+ |
+ def reset(self): |
+ """Reset self to default values.""" |
+ self.clear() |
+ dict.update(self, self.defaults) |
+ |
+ def update(self, config): |
+ """Update self from a dict, file or filename.""" |
+ if isinstance(config, basestring): |
+ # Filename |
+ config = Parser().dict_from_file(config) |
+ elif hasattr(config, 'read'): |
+ # Open file object |
+ config = Parser().dict_from_file(config) |
+ else: |
+ config = config.copy() |
+ self._apply(config) |
+ |
+ def _apply(self, config): |
+ """Update self from a dict.""" |
+ which_env = config.get('environment') |
+ if which_env: |
+ env = self.environments[which_env] |
+ for k in env: |
+ if k not in config: |
+ config[k] = env[k] |
+ |
+ dict.update(self, config) |
+ self.namespaces(config) |
+ |
+ def __setitem__(self, k, v): |
+ dict.__setitem__(self, k, v) |
+ self.namespaces({k: v}) |
+ |
+ |
+class Parser(ConfigParser): |
+ """Sub-class of ConfigParser that keeps the case of options and that |
+ raises an exception if the file cannot be read. |
+ """ |
+ |
+ def optionxform(self, optionstr): |
+ return optionstr |
+ |
+ def read(self, filenames): |
+ if isinstance(filenames, basestring): |
+ filenames = [filenames] |
+ for filename in filenames: |
+ # try: |
+ # fp = open(filename) |
+ # except IOError: |
+ # continue |
+ fp = open(filename) |
+ try: |
+ self._read(fp, filename) |
+ finally: |
+ fp.close() |
+ |
+ def as_dict(self, raw=False, vars=None): |
+ """Convert an INI file to a dictionary""" |
+ # Load INI file into a dict |
+ result = {} |
+ for section in self.sections(): |
+ if section not in result: |
+ result[section] = {} |
+ for option in self.options(section): |
+ value = self.get(section, option, raw=raw, vars=vars) |
+ try: |
+ value = unrepr(value) |
+ except Exception: |
+ x = sys.exc_info()[1] |
+ msg = ("Config error in section: %r, option: %r, " |
+ "value: %r. Config values must be valid Python." % |
+ (section, option, value)) |
+ raise ValueError(msg, x.__class__.__name__, x.args) |
+ result[section][option] = value |
+ return result |
+ |
+ def dict_from_file(self, file): |
+ if hasattr(file, 'read'): |
+ self.readfp(file) |
+ else: |
+ self.read(file) |
+ return self.as_dict() |
+ |
+ |
+# public domain "unrepr" implementation, found on the web and then improved. |
+ |
+ |
+class _Builder2: |
+ |
+ def build(self, o): |
+ m = getattr(self, 'build_' + o.__class__.__name__, None) |
+ if m is None: |
+ raise TypeError("unrepr does not recognize %s" % |
+ repr(o.__class__.__name__)) |
+ return m(o) |
+ |
+ def astnode(self, s): |
+ """Return a Python2 ast Node compiled from a string.""" |
+ try: |
+ import compiler |
+ except ImportError: |
+ # Fallback to eval when compiler package is not available, |
+ # e.g. IronPython 1.0. |
+ return eval(s) |
+ |
+ p = compiler.parse("__tempvalue__ = " + s) |
+ return p.getChildren()[1].getChildren()[0].getChildren()[1] |
+ |
+ def build_Subscript(self, o): |
+ expr, flags, subs = o.getChildren() |
+ expr = self.build(expr) |
+ subs = self.build(subs) |
+ return expr[subs] |
+ |
+ def build_CallFunc(self, o): |
+ children = map(self.build, o.getChildren()) |
+ callee = children.pop(0) |
+ kwargs = children.pop() or {} |
+ starargs = children.pop() or () |
+ args = tuple(children) + tuple(starargs) |
+ return callee(*args, **kwargs) |
+ |
+ def build_List(self, o): |
+ return map(self.build, o.getChildren()) |
+ |
+ def build_Const(self, o): |
+ return o.value |
+ |
+ def build_Dict(self, o): |
+ d = {} |
+ i = iter(map(self.build, o.getChildren())) |
+ for el in i: |
+ d[el] = i.next() |
+ return d |
+ |
+ def build_Tuple(self, o): |
+ return tuple(self.build_List(o)) |
+ |
+ def build_Name(self, o): |
+ name = o.name |
+ if name == 'None': |
+ return None |
+ if name == 'True': |
+ return True |
+ if name == 'False': |
+ return False |
+ |
+ # See if the Name is a package or module. If it is, import it. |
+ try: |
+ return modules(name) |
+ except ImportError: |
+ pass |
+ |
+ # See if the Name is in builtins. |
+ try: |
+ return getattr(builtins, name) |
+ except AttributeError: |
+ pass |
+ |
+ raise TypeError("unrepr could not resolve the name %s" % repr(name)) |
+ |
+ def build_Add(self, o): |
+ left, right = map(self.build, o.getChildren()) |
+ return left + right |
+ |
+ def build_Mul(self, o): |
+ left, right = map(self.build, o.getChildren()) |
+ return left * right |
+ |
+ def build_Getattr(self, o): |
+ parent = self.build(o.expr) |
+ return getattr(parent, o.attrname) |
+ |
+ def build_NoneType(self, o): |
+ return None |
+ |
+ def build_UnarySub(self, o): |
+ return -self.build(o.getChildren()[0]) |
+ |
+ def build_UnaryAdd(self, o): |
+ return self.build(o.getChildren()[0]) |
+ |
+ |
+class _Builder3: |
+ |
+ def build(self, o): |
+ m = getattr(self, 'build_' + o.__class__.__name__, None) |
+ if m is None: |
+ raise TypeError("unrepr does not recognize %s" % |
+ repr(o.__class__.__name__)) |
+ return m(o) |
+ |
+ def astnode(self, s): |
+ """Return a Python3 ast Node compiled from a string.""" |
+ try: |
+ import ast |
+ except ImportError: |
+ # Fallback to eval when ast package is not available, |
+ # e.g. IronPython 1.0. |
+ return eval(s) |
+ |
+ p = ast.parse("__tempvalue__ = " + s) |
+ return p.body[0].value |
+ |
+ def build_Subscript(self, o): |
+ return self.build(o.value)[self.build(o.slice)] |
+ |
+ def build_Index(self, o): |
+ return self.build(o.value) |
+ |
+ def build_Call(self, o): |
+ callee = self.build(o.func) |
+ |
+ if o.args is None: |
+ args = () |
+ else: |
+ args = tuple([self.build(a) for a in o.args]) |
+ |
+ if o.starargs is None: |
+ starargs = () |
+ else: |
+ starargs = self.build(o.starargs) |
+ |
+ if o.kwargs is None: |
+ kwargs = {} |
+ else: |
+ kwargs = self.build(o.kwargs) |
+ |
+ return callee(*(args + starargs), **kwargs) |
+ |
+ def build_List(self, o): |
+ return list(map(self.build, o.elts)) |
+ |
+ def build_Str(self, o): |
+ return o.s |
+ |
+ def build_Num(self, o): |
+ return o.n |
+ |
+ def build_Dict(self, o): |
+ return dict([(self.build(k), self.build(v)) |
+ for k, v in zip(o.keys, o.values)]) |
+ |
+ def build_Tuple(self, o): |
+ return tuple(self.build_List(o)) |
+ |
+ def build_Name(self, o): |
+ name = o.id |
+ if name == 'None': |
+ return None |
+ if name == 'True': |
+ return True |
+ if name == 'False': |
+ return False |
+ |
+ # See if the Name is a package or module. If it is, import it. |
+ try: |
+ return modules(name) |
+ except ImportError: |
+ pass |
+ |
+ # See if the Name is in builtins. |
+ try: |
+ import builtins |
+ return getattr(builtins, name) |
+ except AttributeError: |
+ pass |
+ |
+ raise TypeError("unrepr could not resolve the name %s" % repr(name)) |
+ |
+ def build_UnaryOp(self, o): |
+ op, operand = map(self.build, [o.op, o.operand]) |
+ return op(operand) |
+ |
+ def build_BinOp(self, o): |
+ left, op, right = map(self.build, [o.left, o.op, o.right]) |
+ return op(left, right) |
+ |
+ def build_Add(self, o): |
+ return _operator.add |
+ |
+ def build_Mult(self, o): |
+ return _operator.mul |
+ |
+ def build_USub(self, o): |
+ return _operator.neg |
+ |
+ def build_Attribute(self, o): |
+ parent = self.build(o.value) |
+ return getattr(parent, o.attr) |
+ |
+ def build_NoneType(self, o): |
+ return None |
+ |
+ |
+def unrepr(s): |
+ """Return a Python object compiled from a string.""" |
+ if not s: |
+ return s |
+ if sys.version_info < (3, 0): |
+ b = _Builder2() |
+ else: |
+ b = _Builder3() |
+ obj = b.astnode(s) |
+ return b.build(obj) |
+ |
+ |
+def modules(modulePath): |
+ """Load a module and retrieve a reference to that module.""" |
+ try: |
+ mod = sys.modules[modulePath] |
+ if mod is None: |
+ raise KeyError() |
+ except KeyError: |
+ # The last [''] is important. |
+ mod = __import__(modulePath, globals(), locals(), ['']) |
+ return mod |
+ |
+def attributes(full_attribute_name): |
+ """Load a module and retrieve an attribute of that module.""" |
+ |
+ # Parse out the path, module, and attribute |
+ last_dot = full_attribute_name.rfind(".") |
+ attr_name = full_attribute_name[last_dot + 1:] |
+ mod_path = full_attribute_name[:last_dot] |
+ |
+ mod = modules(mod_path) |
+ # Let an AttributeError propagate outward. |
+ try: |
+ attr = getattr(mod, attr_name) |
+ except AttributeError: |
+ raise AttributeError("'%s' object has no attribute '%s'" |
+ % (mod_path, attr_name)) |
+ |
+ # Return a reference to the attribute. |
+ return attr |
+ |
+ |
Property changes on: third_party/cherrypy/lib/reprconf.py |
___________________________________________________________________ |
Added: svn:eol-style |
+ LF |