From a45bb02606b92fc67c16caffef0101b5c7c21463 Mon Sep 17 00:00:00 2001 From: kapkekes Date: Sat, 22 Nov 2025 14:07:24 +0300 Subject: [PATCH 1/2] feat: check both for `context.yml` and `context.yaml` in `get_default_context_path` --- annet/lib.py | 20 +++++++++- docs/usage/config.rst | 3 ++ tests/annet/test_context_extensions.py | 54 ++++++++++++++++++++++++++ 3 files changed, 75 insertions(+), 2 deletions(-) create mode 100644 tests/annet/test_context_extensions.py diff --git a/annet/lib.py b/annet/lib.py index 72e4d9ff..954a0073 100644 --- a/annet/lib.py +++ b/annet/lib.py @@ -39,7 +39,7 @@ _HOMEDIR_PATH: Optional[str] = None # defaults to ~/.annet _TEMPLATE_CONTEXT_PATH: Optional[str] = None # defaults to annet/configs/context.yml -_DEFAULT_CONTEXT_PATH: Optional[str] = None # defaults to ~/.annet/context.yml +_DEFAULT_CONTEXT_PATH: Optional[str] = None # defaults to ~/.annet/context.yml, checks for ~/.annet/context.yaml def get_homedir_path() -> str: @@ -53,6 +53,21 @@ def set_homedir_path(path: str) -> None: _HOMEDIR_PATH = path +def add_context_extension(path: str, default: str, *checks: str) -> str: + existing = [] + expanded = os.path.expanduser(path) + if os.path.exists(p := expanded + default): + existing.append(p) + for check in checks: + if os.path.exists(p := expanded + check): + existing.append(p) + if not existing: + return path + default + elif len(existing) == 1: + return existing[0] + raise ValueError(f"Multiple context files found: {existing}; either delete all but one, or merge them") + + def get_template_context_path() -> str: if _TEMPLATE_CONTEXT_PATH is None: set_template_context_path(str(Path(sys.modules["annet"].__file__).parent / "configs/context.yml")) @@ -66,7 +81,8 @@ def set_template_context_path(path: str) -> None: def get_default_context_path() -> str: if _DEFAULT_CONTEXT_PATH is None: - set_default_context_path("~/.annet/context.yml") + path = add_context_extension("~/.annet/context", ".yml", ".yaml") + set_default_context_path(path) return _DEFAULT_CONTEXT_PATH diff --git a/docs/usage/config.rst b/docs/usage/config.rst index f22e9785..c177b9b3 100644 --- a/docs/usage/config.rst +++ b/docs/usage/config.rst @@ -5,8 +5,11 @@ The path to the configuration file is searched in following order: * ``ANN_CONTEXT_CONFIG_PATH`` env. * ``~/.annet/context.yml``. +* ``~/.annet/context.yaml``. * ``annet/configs/context.yml``. +.. warning:: The simultaneous existence of ``~/.annet/context.yml`` and ``~/.annet/context.yaml`` is considered an error. + Config example: .. code-block:: yaml diff --git a/tests/annet/test_context_extensions.py b/tests/annet/test_context_extensions.py new file mode 100644 index 00000000..7f3a1656 --- /dev/null +++ b/tests/annet/test_context_extensions.py @@ -0,0 +1,54 @@ +import os + +import pytest +from annet.lib import add_context_extension + + +@pytest.fixture +def mock_home_directory(tmp_path, monkeypatch): + def mock_expanduser(path): + if path.startswith("~"): + return str(tmp_path) + path[1:] + return path + + monkeypatch.setattr(os.path, "expanduser", mock_expanduser) + return tmp_path + + +def test_add_context_extension_no_files_exist(tmp_path): + base_path = str(tmp_path / "context") + result = add_context_extension(base_path, ".yml", ".yaml") + assert result == base_path + ".yml" + + +def test_add_context_extension_default_exists(tmp_path): + base_path = tmp_path / "context" + default_path = base_path.with_suffix(".yml") + default_path.touch() + + result = add_context_extension(base_path, ".yml", ".yaml") + assert result == str(default_path) + + +def test_add_context_extension_check_exists(tmp_path): + base_path = tmp_path / "context" + check_file = base_path.with_suffix(".yml") + check_file.touch() + + result = add_context_extension(base_path, ".yml", ".json", ".yaml", ".toml") + assert result == str(check_file) + + +def test_add_context_extension_multiple_files(tmp_path): + base_path = tmp_path / "context" + yml_file = base_path.with_suffix(".yml") + yaml_file = base_path.with_suffix(".yaml") + yml_file.touch() + yaml_file.touch() + + with pytest.raises(ValueError) as exc_info: + add_context_extension(base_path, ".yml", ".yaml") + + assert "Multiple context files found" in str(exc_info.value) + assert str(yml_file) in str(exc_info.value) + assert str(yaml_file) in str(exc_info.value) From 1884c9e25392ede091121d4f40a179d75512c526 Mon Sep 17 00:00:00 2001 From: kapkekes Date: Sat, 22 Nov 2025 14:16:31 +0300 Subject: [PATCH 2/2] add `formatted_output` --- annet/lib.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/annet/lib.py b/annet/lib.py index 954a0073..925d094b 100644 --- a/annet/lib.py +++ b/annet/lib.py @@ -65,7 +65,9 @@ def add_context_extension(path: str, default: str, *checks: str) -> str: return path + default elif len(existing) == 1: return existing[0] - raise ValueError(f"Multiple context files found: {existing}; either delete all but one, or merge them") + exc = ValueError(f"Multiple context files found: {existing}; either delete all but one, or merge them") + setattr(exc, "formatted_output", str(exc)) + raise exc def get_template_context_path() -> str: