AiOS / mmcv /tests /test_utils /test_config.py
ttxskk
update
d7e58f0
raw
history blame
21.7 kB
# Copyright (c) OpenMMLab. All rights reserved.
import argparse
import copy
import json
import os
import os.path as osp
import shutil
import tempfile
from pathlib import Path
import pytest
import yaml
from mmcv import Config, ConfigDict, DictAction, dump, load
data_path = osp.join(osp.dirname(osp.dirname(__file__)), 'data')
def test_construct():
cfg = Config()
assert cfg.filename is None
assert cfg.text == ''
assert len(cfg) == 0
assert cfg._cfg_dict == {}
with pytest.raises(TypeError):
Config([0, 1])
cfg_dict = dict(item1=[1, 2], item2=dict(a=0), item3=True, item4='test')
# test a.py
cfg_file = osp.join(data_path, 'config/a.py')
cfg_file_path = Path(cfg_file)
file_list = [cfg_file, cfg_file_path]
for item in file_list:
cfg = Config(cfg_dict, filename=item)
assert isinstance(cfg, Config)
assert isinstance(cfg.filename, str) and cfg.filename == str(item)
assert cfg.text == open(item).read()
assert cfg.dump() == cfg.pretty_text
with tempfile.TemporaryDirectory() as temp_config_dir:
dump_file = osp.join(temp_config_dir, 'a.py')
cfg.dump(dump_file)
assert cfg.dump() == open(dump_file).read()
assert Config.fromfile(dump_file)
# test b.json
cfg_file = osp.join(data_path, 'config/b.json')
cfg = Config(cfg_dict, filename=cfg_file)
assert isinstance(cfg, Config)
assert cfg.filename == cfg_file
assert cfg.text == open(cfg_file).read()
assert cfg.dump() == json.dumps(cfg_dict)
with tempfile.TemporaryDirectory() as temp_config_dir:
dump_file = osp.join(temp_config_dir, 'b.json')
cfg.dump(dump_file)
assert cfg.dump() == open(dump_file).read()
assert Config.fromfile(dump_file)
# test c.yaml
cfg_file = osp.join(data_path, 'config/c.yaml')
cfg = Config(cfg_dict, filename=cfg_file)
assert isinstance(cfg, Config)
assert cfg.filename == cfg_file
assert cfg.text == open(cfg_file).read()
assert cfg.dump() == yaml.dump(cfg_dict)
with tempfile.TemporaryDirectory() as temp_config_dir:
dump_file = osp.join(temp_config_dir, 'c.yaml')
cfg.dump(dump_file)
assert cfg.dump() == open(dump_file).read()
assert Config.fromfile(dump_file)
# test h.py
cfg_file = osp.join(data_path, 'config/h.py')
path = osp.join(osp.dirname(__file__), 'data', 'config')
# the value of osp.dirname(__file__) may be `D:\a\xxx` in windows
# environment. When dumping the cfg_dict to file, `D:\a\xxx` will be
# converted to `D:\x07\xxx` and it will cause unexpected result when
# checking whether `D:\a\xxx` equals to `D:\x07\xxx`. Therefore, we forcely
# convert a string representation of the path with forward slashes (/)
path = Path(path).as_posix()
cfg_dict = dict(item1='h.py', item2=path, item3='abc_h')
cfg = Config(cfg_dict, filename=cfg_file)
assert isinstance(cfg, Config)
assert cfg.filename == cfg_file
assert cfg.text == open(cfg_file).read()
assert cfg.dump() == cfg.pretty_text
with tempfile.TemporaryDirectory() as temp_config_dir:
dump_file = osp.join(temp_config_dir, 'h.py')
cfg.dump(dump_file)
assert cfg.dump() == open(dump_file).read()
assert Config.fromfile(dump_file)
assert Config.fromfile(dump_file)['item1'] == cfg_dict['item1']
assert Config.fromfile(dump_file)['item2'] == cfg_dict['item2']
assert Config.fromfile(dump_file)['item3'] == cfg_dict['item3']
# test no use_predefined_variable
cfg_dict = dict(
item1='{{fileBasename}}',
item2='{{ fileDirname}}',
item3='abc_{{ fileBasenameNoExtension }}')
assert Config.fromfile(cfg_file, False)
assert Config.fromfile(cfg_file, False)['item1'] == cfg_dict['item1']
assert Config.fromfile(cfg_file, False)['item2'] == cfg_dict['item2']
assert Config.fromfile(cfg_file, False)['item3'] == cfg_dict['item3']
# test p.yaml
cfg_file = osp.join(data_path, 'config/p.yaml')
cfg_dict = dict(item1=osp.join(osp.dirname(__file__), 'data', 'config'))
cfg = Config(cfg_dict, filename=cfg_file)
assert isinstance(cfg, Config)
assert cfg.filename == cfg_file
assert cfg.text == open(cfg_file).read()
assert cfg.dump() == yaml.dump(cfg_dict)
with tempfile.TemporaryDirectory() as temp_config_dir:
dump_file = osp.join(temp_config_dir, 'p.yaml')
cfg.dump(dump_file)
assert cfg.dump() == open(dump_file).read()
assert Config.fromfile(dump_file)
assert Config.fromfile(dump_file)['item1'] == cfg_dict['item1']
# test no use_predefined_variable
assert Config.fromfile(cfg_file, False)
assert Config.fromfile(cfg_file, False)['item1'] == '{{ fileDirname }}'
# test o.json
cfg_file = osp.join(data_path, 'config/o.json')
cfg_dict = dict(item1=osp.join(osp.dirname(__file__), 'data', 'config'))
cfg = Config(cfg_dict, filename=cfg_file)
assert isinstance(cfg, Config)
assert cfg.filename == cfg_file
assert cfg.text == open(cfg_file).read()
assert cfg.dump() == json.dumps(cfg_dict)
with tempfile.TemporaryDirectory() as temp_config_dir:
dump_file = osp.join(temp_config_dir, 'o.json')
cfg.dump(dump_file)
assert cfg.dump() == open(dump_file).read()
assert Config.fromfile(dump_file)
assert Config.fromfile(dump_file)['item1'] == cfg_dict['item1']
# test no use_predefined_variable
assert Config.fromfile(cfg_file, False)
assert Config.fromfile(cfg_file, False)['item1'] == '{{ fileDirname }}'
def test_fromfile():
for filename in ['a.py', 'a.b.py', 'b.json', 'c.yaml']:
cfg_file = osp.join(data_path, 'config', filename)
cfg_file_path = Path(cfg_file)
file_list = [cfg_file, cfg_file_path]
for item in file_list:
cfg = Config.fromfile(item)
assert isinstance(cfg, Config)
assert isinstance(cfg.filename, str) and cfg.filename == str(item)
assert cfg.text == osp.abspath(osp.expanduser(item)) + '\n' + \
open(item).read()
# test custom_imports for Config.fromfile
cfg_file = osp.join(data_path, 'config', 'q.py')
imported_file = osp.join(data_path, 'config', 'r.py')
target_pkg = osp.join(osp.dirname(__file__), 'r.py')
# Since the imported config will be regarded as a tmp file
# it should be copied to the directory at the same level
shutil.copy(imported_file, target_pkg)
Config.fromfile(cfg_file, import_custom_modules=True)
assert os.environ.pop('TEST_VALUE') == 'test'
os.remove(target_pkg)
with pytest.raises(FileNotFoundError):
Config.fromfile('no_such_file.py')
with pytest.raises(IOError):
Config.fromfile(osp.join(data_path, 'color.jpg'))
def test_fromstring():
for filename in ['a.py', 'a.b.py', 'b.json', 'c.yaml']:
cfg_file = osp.join(data_path, 'config', filename)
file_format = osp.splitext(filename)[-1]
in_cfg = Config.fromfile(cfg_file)
out_cfg = Config.fromstring(in_cfg.pretty_text, '.py')
assert in_cfg._cfg_dict == out_cfg._cfg_dict
cfg_str = open(cfg_file).read()
out_cfg = Config.fromstring(cfg_str, file_format)
assert in_cfg._cfg_dict == out_cfg._cfg_dict
# test pretty_text only supports py file format
cfg_file = osp.join(data_path, 'config', 'b.json')
in_cfg = Config.fromfile(cfg_file)
with pytest.raises(Exception):
Config.fromstring(in_cfg.pretty_text, '.json')
# test file format error
cfg_str = open(cfg_file).read()
with pytest.raises(Exception):
Config.fromstring(cfg_str, '.py')
def test_merge_from_base():
cfg_file = osp.join(data_path, 'config/d.py')
cfg = Config.fromfile(cfg_file)
assert isinstance(cfg, Config)
assert cfg.filename == cfg_file
base_cfg_file = osp.join(data_path, 'config/base.py')
merge_text = osp.abspath(osp.expanduser(base_cfg_file)) + '\n' + \
open(base_cfg_file).read()
merge_text += '\n' + osp.abspath(osp.expanduser(cfg_file)) + '\n' + \
open(cfg_file).read()
assert cfg.text == merge_text
assert cfg.item1 == [2, 3]
assert cfg.item2.a == 1
assert cfg.item3 is False
assert cfg.item4 == 'test_base'
with pytest.raises(TypeError):
Config.fromfile(osp.join(data_path, 'config/e.py'))
def test_merge_from_multiple_bases():
cfg_file = osp.join(data_path, 'config/l.py')
cfg = Config.fromfile(cfg_file)
assert isinstance(cfg, Config)
assert cfg.filename == cfg_file
# cfg.field
assert cfg.item1 == [1, 2]
assert cfg.item2.a == 0
assert cfg.item3 is False
assert cfg.item4 == 'test'
assert cfg.item5 == dict(a=0, b=1)
assert cfg.item6 == [dict(a=0), dict(b=1)]
assert cfg.item7 == dict(a=[0, 1, 2], b=dict(c=[3.1, 4.2, 5.3]))
with pytest.raises(KeyError):
Config.fromfile(osp.join(data_path, 'config/m.py'))
def test_base_variables():
for file in ['t.py', 't.json', 't.yaml']:
cfg_file = osp.join(data_path, f'config/{file}')
cfg = Config.fromfile(cfg_file)
assert isinstance(cfg, Config)
assert cfg.filename == cfg_file
# cfg.field
assert cfg.item1 == [1, 2]
assert cfg.item2.a == 0
assert cfg.item3 is False
assert cfg.item4 == 'test'
assert cfg.item5 == dict(a=0, b=1)
assert cfg.item6 == [dict(a=0), dict(b=1)]
assert cfg.item7 == dict(a=[0, 1, 2], b=dict(c=[3.1, 4.2, 5.3]))
assert cfg.item8 == file
assert cfg.item9 == dict(a=0)
assert cfg.item10 == [3.1, 4.2, 5.3]
# test nested base
for file in ['u.py', 'u.json', 'u.yaml']:
cfg_file = osp.join(data_path, f'config/{file}')
cfg = Config.fromfile(cfg_file)
assert isinstance(cfg, Config)
assert cfg.filename == cfg_file
# cfg.field
assert cfg.base == '_base_.item8'
assert cfg.item1 == [1, 2]
assert cfg.item2.a == 0
assert cfg.item3 is False
assert cfg.item4 == 'test'
assert cfg.item5 == dict(a=0, b=1)
assert cfg.item6 == [dict(a=0), dict(b=1)]
assert cfg.item7 == dict(a=[0, 1, 2], b=dict(c=[3.1, 4.2, 5.3]))
assert cfg.item8 == 't.py'
assert cfg.item9 == dict(a=0)
assert cfg.item10 == [3.1, 4.2, 5.3]
assert cfg.item11 == 't.py'
assert cfg.item12 == dict(a=0)
assert cfg.item13 == [3.1, 4.2, 5.3]
assert cfg.item14 == [1, 2]
assert cfg.item15 == dict(
a=dict(b=dict(a=0)),
b=[False],
c=['test'],
d=[[{
'e': 0
}], [{
'a': 0
}, {
'b': 1
}]],
e=[1, 2])
# test reference assignment for py
cfg_file = osp.join(data_path, 'config/v.py')
cfg = Config.fromfile(cfg_file)
assert isinstance(cfg, Config)
assert cfg.filename == cfg_file
assert cfg.item21 == 't.py'
assert cfg.item22 == 't.py'
assert cfg.item23 == [3.1, 4.2, 5.3]
assert cfg.item24 == [3.1, 4.2, 5.3]
assert cfg.item25 == dict(
a=dict(b=[3.1, 4.2, 5.3]),
b=[[3.1, 4.2, 5.3]],
c=[[{
'e': 't.py'
}], [{
'a': 0
}, {
'b': 1
}]],
e='t.py')
def test_merge_recursive_bases():
cfg_file = osp.join(data_path, 'config/f.py')
cfg = Config.fromfile(cfg_file)
assert isinstance(cfg, Config)
assert cfg.filename == cfg_file
# cfg.field
assert cfg.item1 == [2, 3]
assert cfg.item2.a == 1
assert cfg.item3 is False
assert cfg.item4 == 'test_recursive_bases'
def test_merge_from_dict():
cfg_file = osp.join(data_path, 'config/a.py')
cfg = Config.fromfile(cfg_file)
input_options = {'item2.a': 1, 'item2.b': 0.1, 'item3': False}
cfg.merge_from_dict(input_options)
assert cfg.item2 == dict(a=1, b=0.1)
assert cfg.item3 is False
cfg_file = osp.join(data_path, 'config/s.py')
cfg = Config.fromfile(cfg_file)
# Allow list keys
input_options = {'item.0.a': 1, 'item.1.b': 1}
cfg.merge_from_dict(input_options, allow_list_keys=True)
assert cfg.item == [{'a': 1}, {'b': 1, 'c': 0}]
# allow_list_keys is False
input_options = {'item.0.a': 1, 'item.1.b': 1}
with pytest.raises(TypeError):
cfg.merge_from_dict(input_options, allow_list_keys=False)
# Overflowed index number
input_options = {'item.2.a': 1}
with pytest.raises(KeyError):
cfg.merge_from_dict(input_options, allow_list_keys=True)
def test_merge_delete():
cfg_file = osp.join(data_path, 'config/delete.py')
cfg = Config.fromfile(cfg_file)
# cfg.field
assert cfg.item1 == dict(a=0)
assert cfg.item2 == dict(a=0, b=0)
assert cfg.item3 is True
assert cfg.item4 == 'test'
assert '_delete_' not in cfg.item2
# related issue: https://github.com/open-mmlab/mmcv/issues/1570
assert type(cfg.item1) == ConfigDict
assert type(cfg.item2) == ConfigDict
def test_merge_intermediate_variable():
cfg_file = osp.join(data_path, 'config/i_child.py')
cfg = Config.fromfile(cfg_file)
# cfg.field
assert cfg.item1 == [1, 2]
assert cfg.item2 == dict(a=0)
assert cfg.item3 is True
assert cfg.item4 == 'test'
assert cfg.item_cfg == dict(b=2)
assert cfg.item5 == dict(cfg=dict(b=1))
assert cfg.item6 == dict(cfg=dict(b=2))
def test_fromfile_in_config():
cfg_file = osp.join(data_path, 'config/code.py')
cfg = Config.fromfile(cfg_file)
# cfg.field
assert cfg.cfg.item1 == [1, 2]
assert cfg.cfg.item2 == dict(a=0)
assert cfg.cfg.item3 is True
assert cfg.cfg.item4 == 'test'
assert cfg.item5 == 1
def test_dict():
cfg_dict = dict(item1=[1, 2], item2=dict(a=0), item3=True, item4='test')
for filename in ['a.py', 'b.json', 'c.yaml']:
cfg_file = osp.join(data_path, 'config', filename)
cfg = Config.fromfile(cfg_file)
# len(cfg)
assert len(cfg) == 4
# cfg.keys()
assert set(cfg.keys()) == set(cfg_dict.keys())
assert set(cfg._cfg_dict.keys()) == set(cfg_dict.keys())
# cfg.values()
for value in cfg.values():
assert value in cfg_dict.values()
# cfg.items()
for name, value in cfg.items():
assert name in cfg_dict
assert value in cfg_dict.values()
# cfg.field
assert cfg.item1 == cfg_dict['item1']
assert cfg.item2 == cfg_dict['item2']
assert cfg.item2.a == 0
assert cfg.item3 == cfg_dict['item3']
assert cfg.item4 == cfg_dict['item4']
with pytest.raises(AttributeError):
cfg.not_exist
# field in cfg, cfg[field], cfg.get()
for name in ['item1', 'item2', 'item3', 'item4']:
assert name in cfg
assert cfg[name] == cfg_dict[name]
assert cfg.get(name) == cfg_dict[name]
assert cfg.get('not_exist') is None
assert cfg.get('not_exist', 0) == 0
with pytest.raises(KeyError):
cfg['not_exist']
assert 'item1' in cfg
assert 'not_exist' not in cfg
# cfg.update()
cfg.update(dict(item1=0))
assert cfg.item1 == 0
cfg.update(dict(item2=dict(a=1)))
assert cfg.item2.a == 1
@pytest.mark.parametrize('file', ['a.json', 'b.py', 'c.yaml', 'd.yml', None])
def test_dump(file):
# config loaded from dict
cfg_dict = dict(item1=[1, 2], item2=dict(a=0), item3=True, item4='test')
cfg = Config(cfg_dict=cfg_dict)
assert cfg.item1 == cfg_dict['item1']
assert cfg.item2 == cfg_dict['item2']
assert cfg.item3 == cfg_dict['item3']
assert cfg.item4 == cfg_dict['item4']
assert cfg._filename is None
if file is not None:
# dump without a filename argument is only returning pretty_text.
with tempfile.TemporaryDirectory() as temp_config_dir:
cfg_file = osp.join(temp_config_dir, file)
cfg.dump(cfg_file)
dumped_cfg = Config.fromfile(cfg_file)
assert dumped_cfg._cfg_dict == cfg._cfg_dict
else:
assert cfg.dump() == cfg.pretty_text
# The key of json must be a string, so key `1` will be converted to `'1'`.
def compare_json_cfg(ori_cfg, dumped_json_cfg):
for key, value in ori_cfg.items():
assert str(key) in dumped_json_cfg
if not isinstance(value, dict):
assert ori_cfg[key] == dumped_json_cfg[str(key)]
else:
compare_json_cfg(value, dumped_json_cfg[str(key)])
# config loaded from file
cfg_file = osp.join(data_path, 'config/n.py')
cfg = Config.fromfile(cfg_file)
if file is not None:
with tempfile.TemporaryDirectory() as temp_config_dir:
cfg_file = osp.join(temp_config_dir, file)
cfg.dump(cfg_file)
dumped_cfg = Config.fromfile(cfg_file)
if not file.endswith('.json'):
assert dumped_cfg._cfg_dict == cfg._cfg_dict
else:
compare_json_cfg(cfg._cfg_dict, dumped_cfg._cfg_dict)
else:
assert cfg.dump() == cfg.pretty_text
def test_setattr():
cfg = Config()
cfg.item1 = [1, 2]
cfg.item2 = {'a': 0}
cfg['item5'] = {'a': {'b': None}}
assert cfg._cfg_dict['item1'] == [1, 2]
assert cfg.item1 == [1, 2]
assert cfg._cfg_dict['item2'] == {'a': 0}
assert cfg.item2.a == 0
assert cfg._cfg_dict['item5'] == {'a': {'b': None}}
assert cfg.item5.a.b is None
def test_pretty_text():
cfg_file = osp.join(data_path, 'config/l.py')
cfg = Config.fromfile(cfg_file)
with tempfile.TemporaryDirectory() as temp_config_dir:
text_cfg_filename = osp.join(temp_config_dir, '_text_config.py')
with open(text_cfg_filename, 'w') as f:
f.write(cfg.pretty_text)
text_cfg = Config.fromfile(text_cfg_filename)
assert text_cfg._cfg_dict == cfg._cfg_dict
def test_dict_action():
parser = argparse.ArgumentParser(description='Train a detector')
parser.add_argument(
'--options', nargs='+', action=DictAction, help='custom options')
# Nested brackets
args = parser.parse_args(
['--options', 'item2.a=a,b', 'item2.b=[(a,b), [1,2], false]'])
out_dict = {'item2.a': ['a', 'b'], 'item2.b': [('a', 'b'), [1, 2], False]}
assert args.options == out_dict
# Single Nested brackets
args = parser.parse_args(['--options', 'item2.a=[[1]]'])
out_dict = {'item2.a': [[1]]}
assert args.options == out_dict
# Imbalance bracket
with pytest.raises(AssertionError):
parser.parse_args(['--options', 'item2.a=[(a,b), [1,2], false'])
# Normal values
args = parser.parse_args([
'--options', 'item2.a=1', 'item2.b=0.1', 'item2.c=x', 'item3=false',
'item4=none', 'item5=None'
])
out_dict = {
'item2.a': 1,
'item2.b': 0.1,
'item2.c': 'x',
'item3': False,
'item4': 'none',
'item5': None,
}
assert args.options == out_dict
cfg_file = osp.join(data_path, 'config/a.py')
cfg = Config.fromfile(cfg_file)
cfg.merge_from_dict(args.options)
assert cfg.item2 == dict(a=1, b=0.1, c='x')
assert cfg.item3 is False
def test_reserved_key():
cfg_file = osp.join(data_path, 'config/g.py')
with pytest.raises(KeyError):
Config.fromfile(cfg_file)
def test_syntax_error():
# the name can not be used to open the file a second time in windows,
# so `delete` should be set as `False` and we need to manually remove it
# more details can be found at https://github.com/open-mmlab/mmcv/pull/1077
temp_cfg_file = tempfile.NamedTemporaryFile(suffix='.py', delete=False)
temp_cfg_path = temp_cfg_file.name
# write a file with syntax error
with open(temp_cfg_path, 'w') as f:
f.write('a=0b=dict(c=1)')
with pytest.raises(
SyntaxError, match='There are syntax errors in config file'):
Config.fromfile(temp_cfg_path)
temp_cfg_file.close()
os.remove(temp_cfg_path)
def test_pickle_support():
cfg_file = osp.join(data_path, 'config/n.py')
cfg = Config.fromfile(cfg_file)
with tempfile.TemporaryDirectory() as temp_config_dir:
pkl_cfg_filename = osp.join(temp_config_dir, '_pickle.pkl')
dump(cfg, pkl_cfg_filename)
pkl_cfg = load(pkl_cfg_filename)
assert pkl_cfg._cfg_dict == cfg._cfg_dict
def test_deprecation():
deprecated_cfg_files = [
osp.join(data_path, 'config/deprecated.py'),
osp.join(data_path, 'config/deprecated_as_base.py')
]
for cfg_file in deprecated_cfg_files:
with pytest.warns(DeprecationWarning):
cfg = Config.fromfile(cfg_file)
assert cfg.item1 == 'expected'
def test_deepcopy():
cfg_file = osp.join(data_path, 'config/n.py')
cfg = Config.fromfile(cfg_file)
new_cfg = copy.deepcopy(cfg)
assert isinstance(new_cfg, Config)
assert new_cfg._cfg_dict == cfg._cfg_dict
assert new_cfg._cfg_dict is not cfg._cfg_dict
assert new_cfg._filename == cfg._filename
assert new_cfg._text == cfg._text
def test_copy():
cfg_file = osp.join(data_path, 'config/n.py')
cfg = Config.fromfile(cfg_file)
new_cfg = copy.copy(cfg)
assert isinstance(new_cfg, Config)
assert new_cfg is not cfg
assert new_cfg._cfg_dict is cfg._cfg_dict
assert new_cfg._filename == cfg._filename
assert new_cfg._text == cfg._text