config.py 5.17 KB
Newer Older
Kai Chen's avatar
Kai Chen committed
1
# Copyright (c) Open-MMLab. All rights reserved.
Kai Chen's avatar
Kai Chen committed
2
import os.path as osp
lizz's avatar
lizz committed
3
import shutil
Kai Chen's avatar
Kai Chen committed
4
import sys
lizz's avatar
lizz committed
5
import tempfile
Kai Chen's avatar
Kai Chen committed
6
7
8
9
10
from argparse import ArgumentParser
from importlib import import_module

from addict import Dict

11
from .misc import collections_abc
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
from .path import check_file_exist


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

Kai Chen's avatar
Kai Chen committed
32
33
34
35
36
37
38
39
40
41
42
43
44

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 + '.')
45
        elif isinstance(v, collections_abc.Iterable):
Kai Chen's avatar
Kai Chen committed
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
            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))
81
        check_file_exist(filename)
Kai Chen's avatar
Kai Chen committed
82
        if filename.endswith('.py'):
lizz's avatar
lizz committed
83
84
85
86
87
88
89
90
91
92
93
            with tempfile.TemporaryDirectory() as temp_config_dir:
                shutil.copyfile(filename,
                                osp.join(temp_config_dir, '_tempconfig.py'))
                sys.path.insert(0, temp_config_dir)
                mod = import_module('_tempconfig')
                sys.path.pop(0)
                cfg_dict = {
                    name: value
                    for name, value in mod.__dict__.items()
                    if not name.startswith('__')
                }
wangg12's avatar
wangg12 committed
94
        elif filename.endswith(('.yml', '.yaml', '.json')):
Kai Chen's avatar
Kai Chen committed
95
96
97
            import mmcv
            cfg_dict = mmcv.load(filename)
        else:
wangg12's avatar
wangg12 committed
98
            raise IOError('Only py/yml/yaml/json type are supported now!')
Kai Chen's avatar
Kai Chen committed
99
100
101
102
103
104
105
106
107
        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
108
        cfg = Config.fromfile(cfg_file)
Kai Chen's avatar
Kai Chen committed
109
110
111
112
113
114
115
116
117
118
119
120
        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)))

121
        super(Config, self).__setattr__('_cfg_dict', ConfigDict(cfg_dict))
Kai Chen's avatar
Kai Chen committed
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
        super(Config, self).__setattr__('_filename', filename)
        if filename:
            with open(filename, 'r') 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):
138
        return 'Config (path: {}): {}'.format(self.filename,
Kai Chen's avatar
Kai Chen committed
139
140
141
142
143
144
145
146
147
148
149
150
151
                                              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):
152
            value = ConfigDict(value)
Kai Chen's avatar
Kai Chen committed
153
154
155
156
        self._cfg_dict.__setattr__(name, value)

    def __setitem__(self, name, value):
        if isinstance(value, dict):
157
            value = ConfigDict(value)
Kai Chen's avatar
Kai Chen committed
158
159
160
161
        self._cfg_dict.__setitem__(name, value)

    def __iter__(self):
        return iter(self._cfg_dict)