diff --git a/README.rst b/README.rst index cabb001c..d597302d 100644 --- a/README.rst +++ b/README.rst @@ -1691,8 +1691,11 @@ but you can create it manually. Config file directory --------------------- -The default location of the configuration file is ``~/.httpie/config.json`` -(or ``%APPDATA%\httpie\config.json`` on Windows). +The default location of the configuration file on most platforms is +``$XDG_CONFIG_HOME/httpie/config.json`` (defaulting to +``~/.config/httpie/config.json``). For backwards compatibility, if the directory +``~/.httpie`` exists, the configuration file there will be used instead. On +Windows, the config file is located at ``%APPDATA%\httpie\config.json``. The config directory can be changed by setting the ``$HTTPIE_CONFIG_DIR`` environment variable: diff --git a/httpie/config.py b/httpie/config.py index 82193743..d1b7ae75 100644 --- a/httpie/config.py +++ b/httpie/config.py @@ -8,11 +8,28 @@ from httpie import __version__ from httpie.compat import is_windows -DEFAULT_CONFIG_DIR = Path(os.environ.get( - 'HTTPIE_CONFIG_DIR', - os.path.expanduser('~/.httpie') if not is_windows else - os.path.expandvars(r'%APPDATA%\\httpie') -)) +def get_default_config_dir() -> Path: + """Return the path to the httpie configuration directory. + + This directory isn't guaranteed to exist, and nor are any of its + ancestors. + """ + env_config_dir = os.environ.get('HTTPIE_CONFIG_DIR') + if env_config_dir: + return Path(env_config_dir) + 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) + + +DEFAULT_CONFIG_DIR = get_default_config_dir() class ConfigFileError(Exception): diff --git a/tests/test_config.py b/tests/test_config.py index 0a33ef68..3612234d 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -1,7 +1,10 @@ +import os.path +from pathlib import Path + import pytest from httpie.compat import is_windows -from httpie.config import Config +from httpie.config import Config, get_default_config_dir from utils import HTTP_OK, MockEnvironment, http @@ -48,3 +51,40 @@ def test_default_options_overwrite(httpbin): assert r.json['json'] == { "foo": "bar" } + + +@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='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(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