Skip to content

Commit

Permalink
- Refactored core and deprecated tests using pytest.
Browse files Browse the repository at this point in the history
- Reorganized subcommand-related tests into new module.
- Other minor test improvements.
  • Loading branch information
mauvilsa committed Jun 5, 2023
1 parent 540f91f commit 61804fb
Show file tree
Hide file tree
Showing 19 changed files with 1,812 additions and 1,744 deletions.
27 changes: 0 additions & 27 deletions jsonargparse_tests/base.py

This file was deleted.

50 changes: 45 additions & 5 deletions jsonargparse_tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,19 @@
import pytest

from jsonargparse import ArgumentParser
from jsonargparse.optionals import docstring_parser_support, jsonschema_support
from jsonargparse.optionals import (
docstring_parser_support,
fsspec_support,
jsonschema_support,
set_docstring_parse_options,
url_support,
)

if docstring_parser_support:
from docstring_parser import DocstringStyle

set_docstring_parse_options(style=DocstringStyle.GOOGLE)


is_cpython = platform.python_implementation() == "CPython"
is_posix = os.name == "posix"
Expand All @@ -26,17 +38,33 @@
reason="only supported in posix systems",
)


skip_if_jsonschema_unavailable = pytest.mark.skipif(
not jsonschema_support,
reason="jsonschema package is required",
)

skip_if_fsspec_unavailable = pytest.mark.skipif(
not fsspec_support,
reason="fsspec package is required",
)

skip_if_docstring_parser_unavailable = pytest.mark.skipif(
not docstring_parser_support,
reason="docstring-parser package is required",
)

responses_available = find_spec("responses") is not None
skip_if_requests_unavailable = pytest.mark.skipif(
not url_support,
reason="requests package is required",
)

responses_available = bool(find_spec("responses"))

skip_if_responses_unavailable = pytest.mark.skipif(
not responses_available,
reason="responses package is required",
)

if responses_available:
import responses
Expand All @@ -61,7 +89,18 @@ def subparser() -> ArgumentParser:


@pytest.fixture
def tmp_cwd(tmpdir):
def example_parser() -> ArgumentParser:
parser = ArgumentParser(prog="app", exit_on_error=False)
group_1 = parser.add_argument_group("Group 1", name="group1")
group_1.add_argument("--bool", type=bool, default=True)
group_2 = parser.add_argument_group("Group 2")
group_2.add_argument("--nums.val1", type=int, default=1)
group_2.add_argument("--nums.val2", type=float, default=2.0)
return parser


@pytest.fixture
def tmp_cwd(tmpdir) -> Iterator[Path]:
with tmpdir.as_cwd():
yield Path(tmpdir)

Expand All @@ -83,7 +122,7 @@ def logger() -> logging.Logger:


@contextmanager
def capture_logs(logger: logging.Logger):
def capture_logs(logger: logging.Logger) -> Iterator[StringIO]:
with ExitStack() as stack:
captured = StringIO()
for handler in logger.handlers:
Expand All @@ -94,7 +133,8 @@ def capture_logs(logger: logging.Logger):

def get_parser_help(parser: ArgumentParser) -> str:
out = StringIO()
parser.print_help(out)
with patch.dict(os.environ, {"COLUMNS": "200"}):
parser.print_help(out)
return out.getvalue()


Expand Down
69 changes: 62 additions & 7 deletions jsonargparse_tests/test_actions.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from pathlib import Path

import pytest

from jsonargparse import (
Expand All @@ -6,10 +8,57 @@
ActionYesNo,
ArgumentError,
ArgumentParser,
Namespace,
strip_meta,
)
from jsonargparse_tests.conftest import get_parser_help

# ActionConfigFile tests


def test_action_config_file(parser, tmp_cwd):
rel_yaml_file = Path("subdir", "config.yaml")
abs_yaml_file = (tmp_cwd / rel_yaml_file).resolve()
abs_yaml_file.parent.mkdir()
abs_yaml_file.write_text("val: yaml\n")

parser.add_argument("--cfg", action=ActionConfigFile)
parser.add_argument("--val")

cfg = parser.parse_args([f"--cfg={abs_yaml_file}", f"--cfg={rel_yaml_file}", "--cfg", "val: arg"])
assert 3 == len(cfg.cfg)
assert "arg" == cfg.val
assert str(abs_yaml_file) == cfg.cfg[0].absolute
assert str(rel_yaml_file) == cfg.cfg[1].relative
assert None is cfg.cfg[2]


def test_action_config_file_set_defaults_error(parser):
parser.add_argument("--cfg", action=ActionConfigFile)
with pytest.raises(ValueError) as ctx:
parser.set_defaults(cfg="config.yaml")
ctx.match("does not accept a default, use default_config_files")


def test_action_config_file_add_argument_default_error(parser):
with pytest.raises(ValueError) as ctx:
parser.add_argument("--cfg", default="config.yaml", action=ActionConfigFile)
ctx.match("does not accept a default, use default_config_files")


def test_action_config_file_nested_error(parser):
with pytest.raises(ValueError) as ctx:
parser.add_argument("--nested.cfg", action=ActionConfigFile)
ctx.match("ActionConfigFile must be a top level option")


def test_action_config_file_argument_errors(parser, tmp_cwd):
parser.add_argument("--cfg", action=ActionConfigFile)
pytest.raises(ArgumentError, lambda: parser.parse_args(["--cfg", '"""']))
pytest.raises(ArgumentError, lambda: parser.parse_args(["--cfg=not-exist"]))
pytest.raises(ArgumentError, lambda: parser.parse_args(["--cfg", '{"k":"v"}']))


# ActionYesNo tests


Expand Down Expand Up @@ -72,19 +121,19 @@ def test_yes_no_action_prefixes(parser):
def test_yes_no_action_invalid_yes_prefix(parser):
with pytest.raises(ValueError) as ctx:
parser.add_argument("--arg", action=ActionYesNo(yes_prefix="yes_"))
assert 'Expected option string to start with "--yes_"' in str(ctx.value)
ctx.match('Expected option string to start with "--yes_"')


def test_yes_no_action_invalid_no_prefix(parser):
with pytest.raises(ValueError) as ctx:
parser.add_argument("--arg", nargs="?", action=ActionYesNo(no_prefix=None))
assert "no_prefix=None only supports nargs=1" in str(ctx.value)
ctx.match("no_prefix=None only supports nargs=1")


def test_yes_no_action_invalid_positional(parser):
with pytest.raises(ValueError) as ctx:
parser.add_argument("pos", action=ActionYesNo)
assert "not intended for positional" in str(ctx.value)
ctx.match("not intended for positional")


def test_yes_no_action_parse_env(parser):
Expand All @@ -98,7 +147,13 @@ def test_yes_no_action_parse_env(parser):
assert False is parser.parse_env({"APP_DEFAULT_TRUE": "no"}).default_true


# ArgumentParser tests
def test_yes_no_action_move_to_subparser(parser, subparser):
subparser.add_argument("--g.key", default=False, action=ActionYesNo)
parser.add_argument("--subparser", action=ActionParser(parser=subparser))
assert parser.parse_args([]) == Namespace(subparser=Namespace(g=Namespace(key=False)))


# ActionParser tests


@pytest.fixture(scope="module")
Expand Down Expand Up @@ -212,7 +267,7 @@ def test_action_parser_required_argument(parser, subparser):
assert "1" == parser.parse_args(["--op2.op1=1"]).op2.op1
with pytest.raises(ArgumentError) as ctx:
parser.parse_args([])
assert '"op2.op1" is required' in str(ctx.value)
ctx.match('"op2.op1" is required')


def test_action_parser_init_failures(parser, subparser):
Expand All @@ -227,13 +282,13 @@ def test_action_parser_init_failures(parser, subparser):
def test_action_parser_failure_add_parser_to_self(parser):
with pytest.raises(ValueError) as ctx:
parser.add_argument("--subparser", action=ActionParser(parser=parser))
assert "cannot be added as a subparser of itself" in str(ctx.value)
ctx.match("cannot be added as a subparser of itself")


def test_action_parser_failure_only_single_optional(parser, subparser):
with pytest.raises(ValueError) as ctx:
parser.add_argument("-b", "--bad", action=ActionParser(subparser))
assert "only accepts a single optional" in str(ctx.value)
ctx.match("only accepts a single optional")


def test_action_parser_conflict_subparser_key(parser, subparser):
Expand Down
Loading

0 comments on commit 61804fb

Please sign in to comment.