diff --git a/httpie/config.py b/httpie/config.py index d1b7ae75..78adf13b 100644 --- a/httpie/config.py +++ b/httpie/config.py @@ -8,25 +8,51 @@ from httpie import __version__ from httpie.compat import is_windows +ENV_XDG_CONFIG_HOME = 'XDG_CONFIG_HOME' +ENV_HTTPIE_CONFIG_DIR = 'HTTPIE_CONFIG_DIR' +DEFAULT_CONFIG_DIRNAME = 'httpie' +DEFAULT_RELATIVE_XDG_CONFIG_HOME = Path('.config') +DEFAULT_RELATIVE_LEGACY_CONFIG_DIR = Path('.httpie') +DEFAULT_WINDOWS_CONFIG_DIR = Path( + os.path.expandvars('%APPDATA%')) / DEFAULT_CONFIG_DIRNAME + + def get_default_config_dir() -> Path: - """Return the path to the httpie configuration directory. + """ + Return the path to the httpie configuration directory. This directory isn't guaranteed to exist, and nor are any of its - ancestors. + ancestors (only the legacy ~/.httpie, if returned, is guaranteed to exist). + + XDG Base Directory Specification support: + + + + $XDG_CONFIG_HOME is supported; $XDG_CONFIG_DIRS is not + """ - env_config_dir = os.environ.get('HTTPIE_CONFIG_DIR') + # 1. explicitly set through env + env_config_dir = os.environ.get(ENV_HTTPIE_CONFIG_DIR) if env_config_dir: return Path(env_config_dir) + + # 2. Windows if is_windows: - return Path(os.path.expandvars(r'%APPDATA%\httpie')) - legacy_config_dir = os.path.expanduser('~/.httpie') - if os.path.exists(legacy_config_dir): - return Path(legacy_config_dir) - xdg_config_dir = os.environ.get('XDG_CONFIG_HOME') - if not xdg_config_dir or not os.path.isabs(xdg_config_dir): - xdg_config_dir = os.path.expanduser('~/.config') - httpie_config_dir = os.path.join(xdg_config_dir, 'httpie') - return Path(httpie_config_dir) + return DEFAULT_WINDOWS_CONFIG_DIR + + home_dir = Path.home() + + # 3. legacy ~/.httpie + legacy_config_dir = home_dir / DEFAULT_RELATIVE_LEGACY_CONFIG_DIR + if legacy_config_dir.exists(): + return legacy_config_dir + + # 4. XDG + xdg_config_home_dir = os.environ.get( + ENV_XDG_CONFIG_HOME, # 4.1. explicit + home_dir / DEFAULT_RELATIVE_XDG_CONFIG_HOME # 4.2. default + ) + return Path(xdg_config_home_dir) / DEFAULT_CONFIG_DIRNAME DEFAULT_CONFIG_DIR = get_default_config_dir() diff --git a/tests/test_config.py b/tests/test_config.py index 3612234d..32106c79 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -1,10 +1,14 @@ -import os.path from pathlib import Path import pytest +from _pytest.monkeypatch import MonkeyPatch from httpie.compat import is_windows -from httpie.config import Config, get_default_config_dir +from httpie.config import ( + Config, DEFAULT_CONFIG_DIRNAME, DEFAULT_RELATIVE_LEGACY_CONFIG_DIR, + DEFAULT_RELATIVE_XDG_CONFIG_HOME, DEFAULT_WINDOWS_CONFIG_DIR, + ENV_HTTPIE_CONFIG_DIR, ENV_XDG_CONFIG_HOME, get_default_config_dir, +) from utils import HTTP_OK, MockEnvironment, http @@ -53,38 +57,48 @@ def test_default_options_overwrite(httpbin): } -@pytest.mark.skipif(is_windows, reason='XDG_CONFIG_HOME is only supported on *nix') -def test_config_file_location_xdg(monkeypatch, tmp_path): - monkeypatch.delenv('HTTPIE_CONFIG_DIR', raising=False) - home = tmp_path.absolute().as_posix() - monkeypatch.setenv('HOME', home) - xdg_config_home = tmp_path.joinpath("different_config") - os.mkdir(xdg_config_home) - monkeypatch.setenv('XDG_CONFIG_HOME', xdg_config_home.absolute().as_posix()) - assert get_default_config_dir() == xdg_config_home.joinpath('httpie') - monkeypatch.delenv('XDG_CONFIG_HOME') - # NB: this should be true even though .config doesn't exist. - assert get_default_config_dir() == tmp_path.joinpath('.config', 'httpie') - monkeypatch.setenv('XDG_CONFIG_HOME', 'some/nonabsolute/path') - assert get_default_config_dir() == tmp_path.joinpath('.config', 'httpie') +@pytest.mark.skipif(is_windows, reason='XDG_CONFIG_HOME needs *nix') +def test_explicit_xdg_config_home(monkeypatch: MonkeyPatch, tmp_path: Path): + home_dir = tmp_path + monkeypatch.delenv(ENV_HTTPIE_CONFIG_DIR, raising=False) + monkeypatch.setenv('HOME', home_dir) + custom_xdg_config_home = home_dir / 'custom_xdg_config_home' + monkeypatch.setenv(ENV_XDG_CONFIG_HOME, str(custom_xdg_config_home)) + expected_config_dir = custom_xdg_config_home / DEFAULT_CONFIG_DIRNAME + assert get_default_config_dir() == expected_config_dir -@pytest.mark.skipif(is_windows, reason='legacy config file location is only checked on *nix') -def test_config_file_location_legacy(monkeypatch, tmp_path): - monkeypatch.delenv('HTTPIE_CONFIG_DIR', raising=False) - home = tmp_path.absolute().as_posix() - monkeypatch.setenv('HOME', home) - os.mkdir(tmp_path.joinpath('.httpie')) - assert get_default_config_dir() == tmp_path.joinpath('.httpie') +@pytest.mark.skipif(is_windows, reason='XDG_CONFIG_HOME needs *nix') +def test_default_xdg_config_home(monkeypatch: MonkeyPatch, tmp_path: Path): + home_dir = tmp_path + monkeypatch.delenv(ENV_HTTPIE_CONFIG_DIR, raising=False) + monkeypatch.delenv(ENV_XDG_CONFIG_HOME, raising=False) + monkeypatch.setenv('HOME', home_dir) + expected_config_dir = ( + home_dir + / DEFAULT_RELATIVE_XDG_CONFIG_HOME + / DEFAULT_CONFIG_DIRNAME + ) + assert get_default_config_dir() == expected_config_dir + + +@pytest.mark.skipif(is_windows, reason='legacy config dir needs *nix') +def test_legacy_config_dir(monkeypatch: MonkeyPatch, tmp_path: Path): + home_dir = tmp_path + monkeypatch.delenv(ENV_HTTPIE_CONFIG_DIR, raising=False) + monkeypatch.setenv('HOME', home_dir) + legacy_config_dir = home_dir / DEFAULT_RELATIVE_LEGACY_CONFIG_DIR + legacy_config_dir.mkdir() + assert get_default_config_dir() == legacy_config_dir + + +def test_custom_config_dir(monkeypatch: MonkeyPatch, tmp_path: Path): + httpie_config_dir = tmp_path / 'custom/directory' + monkeypatch.setenv(ENV_HTTPIE_CONFIG_DIR, str(httpie_config_dir)) + assert get_default_config_dir() == httpie_config_dir @pytest.mark.skipif(not is_windows, reason='windows-only') -def test_config_file_location_windows(monkeypatch): - monkeypatch.delenv('HTTPIE_CONFIG_DIR', raising=False) - assert get_default_config_dir() == Path(os.path.expandvars(r'%APPDATA%\httpie')) - - -def test_config_file_location_custom(monkeypatch, tmp_path): - httpie_config_dir = tmp_path.joinpath('custom', 'directory') - monkeypatch.setenv('HTTPIE_CONFIG_DIR', str(httpie_config_dir.absolute())) - assert get_default_config_dir() == httpie_config_dir +def test_windows_config_dir(monkeypatch: MonkeyPatch): + monkeypatch.delenv(ENV_HTTPIE_CONFIG_DIR, raising=False) + assert get_default_config_dir() == DEFAULT_WINDOWS_CONFIG_DIR