Benjamin-eecs/openrsi-commit-runtime-assets
0168
1diff --git a/confuse.py b/confuse.py2index a9c2f96..0460499 1006443--- a/confuse.py4+++ b/confuse.py5@@ -17,6 +17,8 @@6 """7 from __future__ import division, absolute_import, print_function8 9+import argparse10+import optparse11 import platform12 import os13 import pkgutil14@@ -60,6 +62,21 @@ def iter_first(sequence):15 raise ValueError()16 17 18+def namespace_to_dict(obj):19+ """If obj is argparse.Namespace or optparse.Values we'll return20+ a dict representation of it, else return the original object.21+22+ Redefine this method if using other parsers.23+24+ :param obj: *25+ :return:26+ :rtype: dict or *27+ """28+ if isinstance(obj, (argparse.Namespace, optparse.Values)):29+ return vars(obj)30+ return obj31+32+33 # Exceptions.34 35 class ConfigError(Exception):36@@ -243,15 +260,86 @@ class ConfigView(object):37 def __contains__(self, key):38 return self[key].exists()39 40- def set_args(self, namespace):41+ @classmethod42+ def _build_namespace_dict(cls, obj, dots=False):43+ """Recursively replaces all argparse.Namespace and optparse.Values44+ with dicts and drops any keys with None values.45+46+ Additionally, if dots is True, will expand any dot delimited47+ keys.48+49+ :param obj: Namespace, Values, or dict to iterate over. Other50+ values will simply be returned.51+ :type obj: argparse.Namespace or optparse.Values or dict or *52+ :param dots: If True, any properties on obj that contain dots (.)53+ will be broken down into child dictionaries.54+ :return: A new dictionary or the value passed if obj was not a55+ dict, Namespace, or Values.56+ :rtype: dict or *57+ """58+ # We expect our root object to be a dict, but it may come in as59+ # a namespace60+ obj = namespace_to_dict(obj)61+ # We only deal with dictionaries62+ if not isinstance(obj, dict):63+ return obj64+65+ # Get keys iterator66+ keys = obj.keys() if PY3 else obj.iterkeys()67+ if dots:68+ # Dots needs sorted keys to prevent parents from69+ # clobbering children70+ keys = sorted(list(keys))71+72+ output = {}73+ for key in keys:74+ value = obj[key]75+ if value is None: # Avoid unset options.76+ continue77+78+ save_to = output79+ result = cls._build_namespace_dict(value, dots)80+ if dots:81+ # Split keys by dots as this signifies nesting82+ split = key.split('.')83+ if len(split) > 1:84+ # The last index will be the key we assign result to85+ key = split.pop()86+ # Build the dict tree if needed and change where87+ # we're saving to88+ for child_key in split:89+ if child_key in save_to and \90+ isinstance(save_to[child_key], dict):91+ save_to = save_to[child_key]92+ else:93+ # Clobber or create94+ save_to[child_key] = {}95+ save_to = save_to[child_key]96+97+ # Save98+ if key in save_to:99+ save_to[key].update(result)100+ else:101+ save_to[key] = result102+ return output103+104+ def set_args(self, namespace, dots=False):105 """Overlay parsed command-line arguments, generated by a library106 like argparse or optparse, onto this view's value.107+108+ :param namespace: Dictionary or Namespace to overlay this config with.109+ Supports nested Dictionaries and Namespaces.110+ :type namespace: dict or Namespace111+ :param dots: If True, any properties on namespace that contain dots (.)112+ will be broken down into child dictionaries.113+ :Example:114+115+ {'foo.bar': 'car'}116+ # Will be turned into117+ {'foo': {'bar': 'car'}}118+ :type dots: bool119 """120- args = {}121- for key, value in namespace.__dict__.items():122- if value is not None: # Avoid unset options.123- args[key] = value124- self.set(args)125+ self.set(self._build_namespace_dict(namespace, dots))126 127 # Magical conversions. These special methods make it possible to use128 # View objects somewhat transparently in certain circumstances. For129diff --git a/docs/index.rst b/docs/index.rst130index c900a37..a3c7e14 100644131--- a/docs/index.rst132+++ b/docs/index.rst133@@ -167,6 +167,24 @@ consider this argparse script::134 This will allow the user to override the configured value for key135 ``foo`` by passing ``--foo <something>`` on the command line.136 137+Overriding nested values can be accomplished by passing `dots=True` and138+have dot-delimited properties on the incoming object.::139+140+ parser.add_argument('--bar', help='nested parameter', dest='foo.bar')141+ args = parser.parse_args() # args looks like: {'foo.bar': 'value'}142+ config.set_args(args, dots=True)143+ print(config['foo']['bar'].get())144+145+`parse_args` works with generic dictionaries too.::146+147+ args = {148+ 'foo': {149+ 'bar': 1150+ }151+ }152+ config.set_args(args, dots=True)153+ print(config['foo']['bar'].get())154+155 .. _argparse: http://docs.python.org/dev/library/argparse.html156 .. _parse_args: http://docs.python.org/library/argparse.html#the-parse-args-method157 .. _optparse: http://docs.python.org/library/optparse.html158diff --git a/example/__init__.py b/example/__init__.py159index 0717181..33b4306 100644160--- a/example/__init__.py161+++ b/example/__init__.py162@@ -23,7 +23,7 @@ def main():163 parser = argparse.ArgumentParser(description='example Confuse program')164 parser.add_argument('--library', '-l', dest='library', metavar='LIBPATH',165 help='library database file')166- parser.add_argument('--directory', '-d', dest='directory',167+ parser.add_argument('--directory', '-d', dest='paths.directory',168 metavar='DIRECTORY',169 help='destination music directory')170 parser.add_argument('--verbose', '-v', dest='verbose', action='store_true',171 