Skip to content

Instantly share code, notes, and snippets.

@IanChen83
Last active March 11, 2018 08:37
Show Gist options
  • Save IanChen83/cef3bd90e22f1b55ee2fabad148b2243 to your computer and use it in GitHub Desktop.
Save IanChen83/cef3bd90e22f1b55ee2fabad148b2243 to your computer and use it in GitHub Desktop.
A python class that handles configurations easily
__all__ = ('Config', )
class Config:
def __init__(self, **kwargs):
self._options = set()
for key, value in kwargs.items():
setattr(self, key, value)
def __setitem__(self, key, value):
if key.endswith('.') or key.startswith('.'):
raise KeyError('Key should not start/end with "."')
domain, *key = key.split('.', 1)
if key:
if not hasattr(self, domain):
setattr(self, domain, self.__class__())
getattr(self, domain)[key[0]] = value
else:
setattr(self, domain, value)
def __getitem__(self, key):
if key.endswith('.') or key.startswith('.'):
raise KeyError('Key should not start/end with "."')
key, *next_keys = key.split('.')
if key not in self._options:
raise KeyError(f'Key {key} not found.')
if next_keys:
return getattr(self, key)['.'.join(next_keys)]
return getattr(self, key)
def __delattr__(self, key):
if key == '_options':
raise ValueError('Cannot delete "_options" attribute')
super().__delattr__(key)
self._options.remove(key)
def __setattr__(self, key, value):
if key == '_options':
if not hasattr(self, key):
super().__setattr__(key, value)
return
if key not in self._options:
self._options.add(key)
if isinstance(value, dict):
value = self.__class__(**value)
super().__setattr__(key, value)
def to_dict(self):
ret = {}
for key in self._options:
value = getattr(self, key)
if isinstance(value, self.__class__):
value = value.to_dict()
if not value:
continue
ret[key] = value
return ret
@classmethod
def from_dict(cls, d):
return cls(**d)
def __iter__(self):
return iter(self._options)
def _to_str(self, ret, prefix=''):
if prefix:
prefix = prefix + '.'
domains = []
for key in self._options:
value = getattr(self, key)
if isinstance(value, self.__class__):
domains.append((key, value))
else:
space = ' ' * prefix.count('.')
ret.append(f' {space}{prefix}{key} = {value}')
for key, value in domains:
value._to_str(ret, f'{prefix}{key}') # pylint: disable=protected-access
if ret[-1] != '':
ret.append('')
def __str__(self):
ret = ['Configurations:']
self._to_str(ret)
return '\n'.join(ret)
def _main():
import ast
from argparse import ArgumentParser
from functools import reduce
import yaml
options = {}
def get_literal(v):
if v == 'False' or v == 'false':
return False
elif v == 'True' or v == 'true':
return True
try:
x = ast.literal_eval(v)
return x
except (SyntaxError, ValueError):
return v
def reducer(prev, value):
if prev:
options[prev] = get_literal(value)
return None
if '=' in value:
prev, *value = value.split('=')
options[prev] = get_literal('='.join(value))
return None
return value
parser = ArgumentParser()
parser.add_argument('config', help='Config file')
parser.add_argument(
'options',
help='Overwritten Options in the format of D1.D2.KEY=VALUE',
nargs='*')
args = parser.parse_args()
reduce(reducer, args.options, {})
with open(args.config) as f:
config = Config.from_dict(yaml.load(f))
for key, value in options.items():
config[key] = value
print(config)
if __name__ == '__main__':
_main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment