Skip to content
Merged
1 change: 1 addition & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ def launch_http_server(directory):
tests_require = [
"black",
"mock",
"toml",
"PyYAML",
"pytest",
"pytest-cov",
Expand Down
175 changes: 173 additions & 2 deletions tests/test_configargparse.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,9 @@
import types
import unittest
from unittest import mock
import textwrap

from io import StringIO
from io import BytesIO, StringIO

if sys.version_info >= (3, 10):
OPTIONAL_ARGS_STRING = "options"
Expand Down Expand Up @@ -60,7 +61,6 @@ def captured_output():


class TestCase(unittest.TestCase):

def initParser(self, *args, **kwargs):
p = configargparse.ArgParser(*args, **kwargs)
self.parser = replace_error_method(p)
Expand Down Expand Up @@ -1749,6 +1749,177 @@ def testYAMLConfigFileParser_w_ArgumentParser_parsed_values(self):
assert args.level == 35


class TestTomlConfigParser(unittest.TestCase):
def write_toml_file(self, content, obj=StringIO):
f = obj()
dedent = lambda x: x
if isinstance(content, str):
dedent = textwrap.dedent
f.write(dedent(content))
f.seek(0)
return f

def test_section(self):
f = self.write_toml_file(
"""
[section]
key1 = 'toml1'
key2 = 'toml2'
"""
)
parser = configargparse.TomlConfigParser(["section"])
self.assertEqual(parser.parse(f), {"key1": "toml1", "key2": "toml2"})

def test_no_sections(self):
f = self.write_toml_file(
"""
[section]
key1 = 'toml1'
key2 = 'toml2'
"""
)
parser = configargparse.TomlConfigParser([])
self.assertEqual(parser.parse(f), {})

def test_empty_section(self):
f = self.write_toml_file("[section]")
parser = configargparse.TomlConfigParser(["section"])
self.assertEqual(parser.parse(f), {})

def test_empty_file(self):
f = self.write_toml_file("")
parser = configargparse.TomlConfigParser(["section"])
self.assertEqual(parser.parse(f), {})

@unittest.expectedFailure # Ints should be strings
def test_advanced(self):
f = self.write_toml_file(
"""
[tool.section]
key1 = "toml1"
key2 = [1, 2, 3]
"""
)
parser = configargparse.TomlConfigParser(["tool.section"])
self.assertEqual(parser.parse(f), {"key1": "toml1", "key2": ["1", "2", "3"]})

def test_fails_binary_read(self):
f = self.write_toml_file(
b"""[tool.section]\nkey1 = "toml1"
""",
obj=BytesIO,
)
parser = configargparse.TomlConfigParser(["tool.section"])
with self.assertRaises(configargparse.ConfigFileParserException):
parser.parse(f)


class TestCompositeConfigParser(unittest.TestCase):
def setUp(self):
self.tmpdir = tempfile.TemporaryDirectory()
self.old_cwd = os.getcwd()
os.chdir(self.tmpdir.name)
self.parser = configargparse.ArgParser(
description="test",
default_config_files=["config.yaml", "config.toml", "config.ini"],
config_file_parser_class=configargparse.CompositeConfigParser(
[
configargparse.IniConfigParser(["section"], False),
configargparse.TomlConfigParser(["section"]),
configargparse.YAMLConfigFileParser,
]
),
)
self.parser.add_argument("--config", is_config_file=True)
self.parser.add_argument("--key1", type=str)
self.parser.add_argument("--key2", type=str)

def tearDown(self):
os.chdir(self.old_cwd)
self.tmpdir.cleanup()

def write_yaml_file(self, content="[section]\nkey1: yaml1\nkey2: yaml2"):
with open("config.yaml", "w") as f:
f.write(content)

def write_ini_file(
self, content="[section]\nkey1=ini1\nkey2=ini2\n; this is an ini comment"
):
with open("config.ini", "w") as f:
f.write(content)

def write_toml_file(
self,
content="[section]\nkey1 = 'toml1'\nkey2 = 'toml2'\nspecial.key = { a = 1 }\n# this is a toml comment",
):
with open("config.toml", "w") as f:
f.write(content)

def write_toml_file_extra(self):
self.write_toml_file()
with open("config.toml", "a") as f:
f.write("\nkey3 = 'toml3'\nkey4 = 'toml4'")

def test_plain(self):
self.assertEqual(
vars(self.parser.parse_args([])),
{"config": None, "key1": None, "key2": None},
)

@unittest.expectedFailure # unknown fields should be ignored
def test_with_all_configs(self):
self.write_yaml_file()
self.write_ini_file()
self.write_toml_file()
self.assertEqual(
vars(self.parser.parse_args([])),
{"config": None, "key1": "ini1", "key2": "ini2"},
)

@unittest.expectedFailure # unknown fields should be ignored
def test_with_all_configs_override(self):
self.write_yaml_file()
self.write_ini_file()
self.write_toml_file()
self.assertEqual(
vars(self.parser.parse_args(["--key1", "val1"])),
{"config": None, "key1": "val1", "key2": "ini2"},
)

def test_missing_primary_config(self):
self.write_yaml_file()
self.assertEqual(
vars(self.parser.parse_args([])),
{"config": None, "key1": "yaml1", "key2": "yaml2"},
)

@unittest.expectedFailure # unknown fields should be ignored
def test_toml(self):
self.write_yaml_file()
self.write_toml_file()
self.assertEqual(
vars(self.parser.parse_args([])),
{"config": None, "key1": "toml1", "key2": "toml2"},
)

@unittest.expectedFailure # unknown fields should be ignored
def test_override_primary_config(self):
self.write_yaml_file()
self.write_ini_file()
self.write_toml_file()
self.assertEqual(
vars(self.parser.parse_args(["--config", "config.yaml"])),
{"config": "config.yaml", "key1": "yaml1", "key2": "yaml2"},
)

def test_toml_extra(self):
self.write_yaml_file()
self.write_ini_file()
self.write_toml_file_extra()
with self.assertRaises(SystemExit):
self.parser.parse_args([])


################################################################################
# since configargparse should work as a drop-in replacement for argparse
# in all situations, run argparse unittests on configargparse by modifying
Expand Down