diff --git a/conservancy_beancount/config.py b/conservancy_beancount/config.py index 55b48d1ab8c13329f436233c3d28e63e70022bf8..d03ea59915da20b518d4bcf4a3e96d3531621a9b 100644 --- a/conservancy_beancount/config.py +++ b/conservancy_beancount/config.py @@ -69,6 +69,11 @@ class RTCredentials(NamedTuple): class Config: + _ENVIRON_DEFAULT_PATHS = { + 'XDG_CACHE_HOME': Path('.cache'), + 'XDG_CONFIG_HOME': Path('.config'), + } + def _dir_or_none(self, path: Path) -> Optional[Path]: try: path.mkdir(exist_ok=True) @@ -77,21 +82,29 @@ class Config: else: return path - def cache_dir_path(self, name: str='conservancy_beancount') -> Optional[Path]: + def _path_from_environ(self, key: str, default: Optional[Path]=None) -> Path: try: - cache_root = Path(os.environ['XDG_CACHE_HOME']) + retval = Path(os.environ[key]) except (KeyError, ValueError): ok = False else: # Per the spec, non-absolute paths should be ignored. - ok = cache_root.is_absolute() + ok = retval.is_absolute() if not ok: - cache_root = Path.home() / '.cache' + retval = default or (Path.home() / self._ENVIRON_DEFAULT_PATHS[key]) + return retval + + def cache_dir_path(self, name: str='conservancy_beancount') -> Optional[Path]: + cache_root = self._path_from_environ('XDG_CACHE_HOME') return ( self._dir_or_none(cache_root) and self._dir_or_none(cache_root / name) ) + def config_file_path(self, name: str='conservancy_beancount') -> Path: + config_root = self._path_from_environ('XDG_CONFIG_HOME') + return Path(config_root, name, 'config.ini') + def payment_threshold(self) -> decimal.Decimal: return decimal.Decimal(0) diff --git a/tests/test_config.py b/tests/test_config.py index 0b45aae0802070e0b3c6f06e8b6b48c4f7261a2b..8dce12d196d2f2d7bb539c58f286fbe353d545da 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -19,6 +19,8 @@ import decimal import os import re +from pathlib import Path + import pytest from . import testutil @@ -294,3 +296,24 @@ def test_payment_threshold(): threshold = config_mod.Config().payment_threshold() assert threshold == 0 assert isinstance(threshold, (int, decimal.Decimal)) + +@pytest.mark.parametrize('config_path', [ + None, + '', + 'nonexistent/relative/path', +]) +def test_config_file_path(config_path): + expected = Path('~/.config/conservancy_beancount/config.ini').expanduser() + with update_environ(XDG_CONFIG_HOME=config_path): + config = config_mod.Config() + assert config.config_file_path() == expected + +def test_config_file_path_respects_xdg_config_home(): + with update_environ(XDG_CONFIG_HOME='/etc'): + config = config_mod.Config() + assert config.config_file_path() == Path('/etc/conservancy_beancount/config.ini') + +def test_config_file_path_with_subdir(): + expected = Path('~/.config/conftest/config.ini').expanduser() + config = config_mod.Config() + assert config.config_file_path('conftest') == expected