| |
| import collections |
| import os.path as osp |
| import sys |
| from argparse import ArgumentParser |
| from importlib import import_module |
|
|
| from addict import Dict |
|
|
|
|
| class ConfigDict(Dict): |
|
|
| def __missing__(self, name): |
| raise KeyError(name) |
|
|
| def __getattr__(self, name): |
| try: |
| value = super(ConfigDict, self).__getattr__(name) |
| except KeyError: |
| ex = AttributeError("'{}' object has no attribute '{}'".format( |
| self.__class__.__name__, name)) |
| except Exception as e: |
| ex = e |
| else: |
| return value |
| raise ex |
|
|
|
|
| def add_args(parser, cfg, prefix=''): |
| for k, v in cfg.items(): |
| if isinstance(v, str): |
| parser.add_argument('--' + prefix + k) |
| elif isinstance(v, int): |
| parser.add_argument('--' + prefix + k, type=int) |
| elif isinstance(v, float): |
| parser.add_argument('--' + prefix + k, type=float) |
| elif isinstance(v, bool): |
| parser.add_argument('--' + prefix + k, action='store_true') |
| elif isinstance(v, dict): |
| add_args(parser, v, k + '.') |
| elif isinstance(v, collections.Iterable): |
| parser.add_argument('--' + prefix + k, type=type(v[0]), nargs='+') |
| else: |
| print('connot parse key {} of type {}'.format(prefix + k, type(v))) |
| return parser |
|
|
|
|
| class Config(object): |
| """A facility for config and config files. |
| It supports common file formats as configs: python/json/yaml. The interface |
| is the same as a dict object and also allows access config values as |
| attributes. |
| Example: |
| >>> cfg = Config(dict(a=1, b=dict(b1=[0, 1]))) |
| >>> cfg.a |
| 1 |
| >>> cfg.b |
| {'b1': [0, 1]} |
| >>> cfg.b.b1 |
| [0, 1] |
| >>> cfg = Config.fromfile('tests/data/config/a.py') |
| >>> cfg.filename |
| "/home/kchen/projects/mmcv/tests/data/config/a.py" |
| >>> cfg.item4 |
| 'test' |
| >>> cfg |
| "Config [path: /home/kchen/projects/mmcv/tests/data/config/a.py]: " |
| "{'item1': [1, 2], 'item2': {'a': 0}, 'item3': True, 'item4': 'test'}" |
| """ |
|
|
| @staticmethod |
| def fromfile(filename): |
| filename = osp.abspath(osp.expanduser(filename)) |
| if filename.endswith('.py'): |
| module_name = osp.basename(filename)[:-3] |
| if '.' in module_name: |
| raise ValueError('Dots are not allowed in config file path.') |
| config_dir = osp.dirname(filename) |
| sys.path.insert(0, config_dir) |
| mod = import_module(module_name) |
| sys.path.pop(0) |
| cfg_dict = { |
| name: value |
| for name, value in mod.__dict__.items() |
| if not name.startswith('__') |
| } |
| elif filename.endswith(('.yml', '.yaml')): |
| from yaml import safe_load |
| cfg_dict = safe_load(open(filename, 'r')) |
| else: |
| raise IOError('Only py/yml/yaml type are supported now!') |
| return Config(cfg_dict, filename=filename) |
|
|
| @staticmethod |
| def auto_argparser(description=None): |
| """Generate argparser from config file automatically (experimental) |
| """ |
| partial_parser = ArgumentParser(description=description) |
| partial_parser.add_argument('config', help='config file path') |
| cfg_file = partial_parser.parse_known_args()[0].config |
| cfg = Config.fromfile(cfg_file) |
| parser = ArgumentParser(description=description) |
| parser.add_argument('config', help='config file path') |
| add_args(parser, cfg) |
| return parser, cfg |
|
|
| def __init__(self, cfg_dict=None, filename=None): |
| if cfg_dict is None: |
| cfg_dict = dict() |
| elif not isinstance(cfg_dict, dict): |
| raise TypeError('cfg_dict must be a dict, but got {}'.format( |
| type(cfg_dict))) |
|
|
| super(Config, self).__setattr__('_cfg_dict', ConfigDict(cfg_dict)) |
| super(Config, self).__setattr__('_filename', filename) |
| if filename: |
| with open(filename, 'r', encoding='utf-8') as f: |
| super(Config, self).__setattr__('_text', f.read()) |
| else: |
| super(Config, self).__setattr__('_text', '') |
|
|
| @property |
| def filename(self): |
| return self._filename |
|
|
| @property |
| def text(self): |
| return self._text |
|
|
| def __repr__(self): |
| return 'Config (path: {}): {}'.format(self.filename, |
| self._cfg_dict.__repr__()) |
|
|
| def __len__(self): |
| return len(self._cfg_dict) |
|
|
| def __getattr__(self, name): |
| return getattr(self._cfg_dict, name) |
|
|
| def __getitem__(self, name): |
| return self._cfg_dict.__getitem__(name) |
|
|
| def __setattr__(self, name, value): |
| if isinstance(value, dict): |
| value = ConfigDict(value) |
| self._cfg_dict.__setattr__(name, value) |
|
|
| def __setitem__(self, name, value): |
| if isinstance(value, dict): |
| value = ConfigDict(value) |
| self._cfg_dict.__setitem__(name, value) |
|
|
| def __iter__(self): |
| return iter(self._cfg_dict) |
|
|