diff --git a/README.md b/README.md index 7235520..bcfb4bd 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,10 @@ # xdg -`xdg` is a tiny Python module which provides the variables defined by the [XDG -Base Directory Specification][spec], to save you from duplicating the same -snippet of logic in every Python utility you write that deals with user cache, -configuration, or data files. It has no external dependencies. +`xdg` is a Python module which provides functions to return paths to the +directories defined by the [XDG Base Directory Specification][spec], to save you +from duplicating the same snippet of logic in every Python utility you write +that deals with user cache, configuration, or data files. It has no external +dependencies. ## Installation @@ -21,23 +22,30 @@ Alternatively, since `xdg` is only a single file you may prefer to just copy ## Usage ```python -from xdg import (XDG_CACHE_HOME, XDG_CONFIG_DIRS, XDG_CONFIG_HOME, - XDG_DATA_DIRS, XDG_DATA_HOME, XDG_RUNTIME_DIR) +from xdg import ( + xdg_cache_home, + xdg_config_dirs, + xdg_config_home, + xdg_data_dirs, + xdg_data_home, + xdg_runtime_dir, +) ``` -`XDG_CACHE_HOME`, `XDG_CONFIG_HOME`, and `XDG_DATA_HOME` are [`pathlib.Path` -objects][path] containing the value of the environment variable of the same -name, or the default defined in the specification if the environment variable is -unset or empty. +`xdg_cache_home()`, `xdg_config_home()`, and `xdg_data_home()` return +[`pathlib.Path` objects][path] containing the value of the environment variable +named `XDG_CACHE_HOME`, `XDG_CONFIG_HOME`, and `XDG_DATA_HOME` respectively, or +the default defined in the specification if the environment variable is unset or +empty. -`XDG_CONFIG_DIRS` and `XDG_DATA_DIRS` are lists of `pathlib.Path` objects -containing the value of the environment variable of the same name split on -colons, or the default defined in the specification if the environment variable -is unset or empty. +`xdg_config_dirs()` and `xdg_data_dirs()` return a list of `pathlib.Path` +objects containing the value, split on colons, of the environment variable named +`XDG_CONFIG_DIRS` and `XDG_DATA_DIRS` respectively, or the default defined in +the specification if the environment variable is unset or empty. -`XDG_RUNTIME_DIR` is a `pathlib.Path` object containing the value of the -environment variable of the same name, or `None` if the environment variable is -unset. +`xdg_runtime_dir()` returns a `pathlib.Path` object containing the value of the +`XDG_RUNTIME_DIR` environment variable, or `None` if the environment variable is +not set. ## Copyright diff --git a/src/xdg/__init__.py b/src/xdg/__init__.py index 4a912b4..3e46422 100644 --- a/src/xdg/__init__.py +++ b/src/xdg/__init__.py @@ -16,18 +16,20 @@ """XDG Base Directory Specification variables. -XDG_CACHE_HOME, XDG_CONFIG_HOME, and XDG_DATA_HOME are pathlib.Path -objects containing the value of the environment variable of the same -name, or the default defined in the specification if the environment -variable is unset or empty. - -XDG_CONFIG_DIRS and XDG_DATA_DIRS are lists of pathlib.Path objects -containing the value of the environment variable of the same name split -on colons, or the default defined in the specification if the -environment variable is unset or empty. - -XDG_RUNTIME_DIR is a pathlib.Path object containing the value of the -environment variable of the same name, or None if the environment +xdg_cache_home(), xdg_config_home(), and xdg_data_home() return +pathlib.Path objects containing the value of the environment variable +named XDG_CACHE_HOME, XDG_CONFIG_HOME, and XDG_DATA_HOME respectively, +or the default defined in the specification if the environment variable +is unset or empty. + +xdg_config_dirs() and xdg_data_dirs() return a list of pathlib.Path +objects containing the value, split on colons, of the environment +variable named XDG_CONFIG_DIRS and XDG_DATA_DIRS respectively, or the +default defined in the specification if the environment variable is +unset or empty. + +xdg_runtime_dir() returns a pathlib.Path object containing the value of +the XDG_RUNTIME_DIR environment variable, or None if the environment variable is not set. """ @@ -39,6 +41,12 @@ from typing import List, Optional __all__ = [ + "xdg_cache_home", + "xdg_config_dirs", + "xdg_config_home", + "xdg_data_dirs", + "xdg_data_home", + "xdg_runtime_dir", "XDG_CACHE_HOME", "XDG_CONFIG_DIRS", "XDG_CONFIG_HOME", @@ -47,7 +55,10 @@ "XDG_RUNTIME_DIR", ] -HOME = Path(os.path.expandvars("$HOME")) + +def _home_dir() -> Path: + """Return a Path corresponding to the user's home directory.""" + return Path(os.path.expandvars("$HOME")) def _path_from_env(variable: str, default: Path) -> Path: @@ -105,20 +116,51 @@ def _paths_from_env(variable: str, default: List[Path]) -> List[Path]: return default -XDG_CACHE_HOME = _path_from_env("XDG_CACHE_HOME", HOME / ".cache") +def xdg_cache_home() -> Path: + """Return a Path corresponding to XDG_CACHE_HOME.""" + return _path_from_env("XDG_CACHE_HOME", _home_dir() / ".cache") -XDG_CONFIG_DIRS = _paths_from_env("XDG_CONFIG_DIRS", [Path("/etc/xdg")]) -XDG_CONFIG_HOME = _path_from_env("XDG_CONFIG_HOME", HOME / ".config") +def xdg_config_dirs() -> List[Path]: + """Return a list of Paths corresponding to XDG_CONFIG_DIRS.""" + return _paths_from_env("XDG_CONFIG_DIRS", [Path("/etc/xdg")]) -XDG_DATA_DIRS = _paths_from_env( - "XDG_DATA_DIRS", - [Path(path) for path in "/usr/local/share/:/usr/share/".split(":")], -) -XDG_DATA_HOME = _path_from_env("XDG_DATA_HOME", HOME / ".local" / "share") +def xdg_config_home() -> Path: + """Return a Path corresponding to XDG_CONFIG_HOME.""" + return _path_from_env("XDG_CONFIG_HOME", _home_dir() / ".config") + + +def xdg_data_dirs() -> List[Path]: + """Return a list of Paths corresponding to XDG_DATA_DIRS.""" + return _paths_from_env( + "XDG_DATA_DIRS", + [Path(path) for path in "/usr/local/share/:/usr/share/".split(":")], + ) + -try: - XDG_RUNTIME_DIR: Optional[Path] = Path(os.environ["XDG_RUNTIME_DIR"]) -except KeyError: - XDG_RUNTIME_DIR = None +def xdg_data_home() -> Path: + """Return a Path corresponding to XDG_DATA_HOME.""" + return _path_from_env("XDG_DATA_HOME", _home_dir() / ".local" / "share") + + +def xdg_runtime_dir() -> Optional[Path]: + """Return a Path corresponding to XDG_RUNTIME_DIR. + + If the XDG_RUNTIME_DIR environment variable is not set, None will be + returned as per the specification. + + """ + try: + return Path(os.environ["XDG_RUNTIME_DIR"]) + except KeyError: + return None + + +# The following variables are deprecated, but remain for backward compatibility. +XDG_CACHE_HOME = xdg_cache_home() +XDG_CONFIG_DIRS = xdg_config_dirs() +XDG_CONFIG_HOME = xdg_config_home() +XDG_DATA_DIRS = xdg_data_dirs() +XDG_DATA_HOME = xdg_data_home() +XDG_RUNTIME_DIR = xdg_runtime_dir() diff --git a/test/test_xdg.py b/test/test_xdg.py index 8d6bbee..2e9ad9d 100644 --- a/test/test_xdg.py +++ b/test/test_xdg.py @@ -1,213 +1,130 @@ """Test suite for xdg.""" -# pylint: disable=import-outside-toplevel -# pylint: disable=no-self-use -# pylint: disable=redefined-outer-name -# pylint: disable=unused-argument - import os -import sys from pathlib import Path -from typing import TYPE_CHECKING, Callable -import pytest +from _pytest.monkeypatch import MonkeyPatch -if TYPE_CHECKING: - from _pytest.monkeypatch import MonkeyPatch +import xdg HOME_DIR = Path("/homedir") -@pytest.fixture # type: ignore -def unimport() -> None: - """Ensure xdg is absent from sys.modules.""" - try: - del sys.modules["xdg"] - except KeyError: - pass - - -class TestXdgCacheHome: - """Tests for XDG_CACHE_HOME.""" - - def test_unset( - self, monkeypatch: "MonkeyPatch", unimport: Callable - ) -> None: - """Test when XDG_CACHE_HOME is unset.""" - monkeypatch.delenv("XDG_CACHE_HOME", raising=False) - monkeypatch.setenv("HOME", os.fspath(HOME_DIR)) - from xdg import XDG_CACHE_HOME - - assert XDG_CACHE_HOME == HOME_DIR / ".cache" - - def test_empty( - self, monkeypatch: "MonkeyPatch", unimport: Callable - ) -> None: - """Test when XDG_CACHE_HOME is empty.""" - monkeypatch.setenv("HOME", os.fspath(HOME_DIR)) - monkeypatch.setenv("XDG_CACHE_HOME", "") - from xdg import XDG_CACHE_HOME - - assert XDG_CACHE_HOME == HOME_DIR / ".cache" - - def test_set(self, monkeypatch: "MonkeyPatch", unimport: Callable) -> None: - """Test when XDG_CACHE_HOME is set.""" - monkeypatch.setenv("XDG_CACHE_HOME", "/xdg_cache_home") - from xdg import XDG_CACHE_HOME - - assert XDG_CACHE_HOME == Path("/xdg_cache_home") - - -class TestXdgConfigDirs: - """Tests for XDG_CONFIG_DIRS.""" - - def test_unset( - self, monkeypatch: "MonkeyPatch", unimport: Callable - ) -> None: - """Test when XDG_CONFIG_DIRS is unset.""" - monkeypatch.delenv("XDG_CONFIG_DIRS", raising=False) - from xdg import XDG_CONFIG_DIRS - - assert XDG_CONFIG_DIRS == [Path("/etc/xdg")] - - def test_empty( - self, monkeypatch: "MonkeyPatch", unimport: Callable - ) -> None: - """Test when XDG_CONFIG_DIRS is empty.""" - monkeypatch.setenv("XDG_CONFIG_DIRS", "") - from xdg import XDG_CONFIG_DIRS - - assert XDG_CONFIG_DIRS == [Path("/etc/xdg")] +def test_xdg_cache_home_unset(monkeypatch: MonkeyPatch) -> None: + """Test xdg_cache_home when XDG_CACHE_HOME is unset.""" + monkeypatch.delenv("XDG_CACHE_HOME", raising=False) + monkeypatch.setenv("HOME", os.fspath(HOME_DIR)) + assert xdg.xdg_cache_home() == HOME_DIR / ".cache" - def test_set(self, monkeypatch: "MonkeyPatch", unimport: Callable) -> None: - """Test when XDG_CONFIG_DIRS is set.""" - monkeypatch.setenv("XDG_CONFIG_DIRS", "/first:/sec/ond") - from xdg import XDG_CONFIG_DIRS - assert XDG_CONFIG_DIRS == [Path("/first"), Path("/sec/ond")] +def test_xdg_cache_home_empty(monkeypatch: MonkeyPatch) -> None: + """Test xdg_cache_home when XDG_CACHE_HOME is empty.""" + monkeypatch.setenv("HOME", os.fspath(HOME_DIR)) + monkeypatch.setenv("XDG_CACHE_HOME", "") + assert xdg.xdg_cache_home() == HOME_DIR / ".cache" -class TestXdgConfigHome: - """Tests for XDG_CONFIG_HOME.""" +def test_xdg_cache_home_set(monkeypatch: MonkeyPatch) -> None: + """Test xdg_cache_home when XDG_CACHE_HOME is set.""" + monkeypatch.setenv("XDG_CACHE_HOME", "/xdg_cache_home") + assert xdg.xdg_cache_home() == Path("/xdg_cache_home") - def test_unset( - self, monkeypatch: "MonkeyPatch", unimport: Callable - ) -> None: - """Test when XDG_CONFIG_HOME is unset.""" - monkeypatch.delenv("XDG_CONFIG_HOME", raising=False) - monkeypatch.setenv("HOME", os.fspath(HOME_DIR)) - from xdg import XDG_CONFIG_HOME - assert XDG_CONFIG_HOME == HOME_DIR / ".config" +def test_xdg_config_dirs_unset(monkeypatch: MonkeyPatch) -> None: + """Test xdg_config_dirs when XDG_CONFIG_DIRS is unset.""" + monkeypatch.delenv("XDG_CONFIG_DIRS", raising=False) + assert xdg.xdg_config_dirs() == [Path("/etc/xdg")] - def test_empty( - self, monkeypatch: "MonkeyPatch", unimport: Callable - ) -> None: - """Test when XDG_CONFIG_HOME is empty.""" - monkeypatch.setenv("HOME", os.fspath(HOME_DIR)) - monkeypatch.setenv("XDG_CONFIG_HOME", "") - from xdg import XDG_CONFIG_HOME - assert XDG_CONFIG_HOME == HOME_DIR / ".config" +def test_xdg_config_dirs_empty(monkeypatch: MonkeyPatch) -> None: + """Test xdg_config_dirs when XDG_CONFIG_DIRS is empty.""" + monkeypatch.setenv("XDG_CONFIG_DIRS", "") + assert xdg.xdg_config_dirs() == [Path("/etc/xdg")] - def test_set(self, monkeypatch: "MonkeyPatch", unimport: Callable) -> None: - """Test when XDG_CONFIG_HOME is set.""" - monkeypatch.setenv("XDG_CONFIG_HOME", "/xdg_config_home") - from xdg import XDG_CONFIG_HOME - assert XDG_CONFIG_HOME == Path("/xdg_config_home") +def test_xdg_config_dirs_set(monkeypatch: MonkeyPatch) -> None: + """Test xdg_config_dirs when XDG_CONFIG_DIRS is set.""" + monkeypatch.setenv("XDG_CONFIG_DIRS", "/first:/sec/ond") + assert xdg.xdg_config_dirs() == [Path("/first"), Path("/sec/ond")] -class TestXdgDataDirs: - """Tests for XDG_DATA_DIRS.""" +def test_xdg_config_home_unset(monkeypatch: MonkeyPatch) -> None: + """Test xdg_config_home when XDG_CONFIG_HOME is unset.""" + monkeypatch.delenv("XDG_CONFIG_HOME", raising=False) + monkeypatch.setenv("HOME", os.fspath(HOME_DIR)) + assert xdg.xdg_config_home() == HOME_DIR / ".config" - def test_unset( - self, monkeypatch: "MonkeyPatch", unimport: Callable - ) -> None: - """Test when XDG_DATA_DIRS is unset.""" - monkeypatch.delenv("XDG_DATA_DIRS", raising=False) - from xdg import XDG_DATA_DIRS - assert XDG_DATA_DIRS == [ - Path("/usr/local/share/"), - Path("/usr/share/"), - ] +def test_xdg_config_home_empty(monkeypatch: MonkeyPatch) -> None: + """Test xdg_config_home when XDG_CONFIG_HOME is empty.""" + monkeypatch.setenv("HOME", os.fspath(HOME_DIR)) + monkeypatch.setenv("XDG_CONFIG_HOME", "") + assert xdg.xdg_config_home() == HOME_DIR / ".config" - def test_empty( - self, monkeypatch: "MonkeyPatch", unimport: Callable - ) -> None: - """Test when XDG_DATA_DIRS is empty.""" - monkeypatch.setenv("XDG_DATA_DIRS", "") - from xdg import XDG_DATA_DIRS - assert XDG_DATA_DIRS == [ - Path("/usr/local/share/"), - Path("/usr/share/"), - ] +def test_xdg_config_home_set(monkeypatch: MonkeyPatch) -> None: + """Test xdg_config_home when XDG_CONFIG_HOME is set.""" + monkeypatch.setenv("XDG_CONFIG_HOME", "/xdg_config_home") + assert xdg.xdg_config_home() == Path("/xdg_config_home") - def test_set(self, monkeypatch: "MonkeyPatch", unimport: Callable) -> None: - """Test when XDG_DATA_DIRS is set.""" - monkeypatch.setenv("XDG_DATA_DIRS", "/first/:/sec/ond/") - from xdg import XDG_DATA_DIRS - assert XDG_DATA_DIRS == [Path("/first/"), Path("/sec/ond/")] +def test_xdg_data_dirs_unset(monkeypatch: MonkeyPatch) -> None: + """Test xdg_data_dirs when XDG_DATA_DIRS is unset.""" + monkeypatch.delenv("XDG_DATA_DIRS", raising=False) + assert xdg.xdg_data_dirs() == [ + Path("/usr/local/share/"), + Path("/usr/share/"), + ] -class TestXdgDataHome: - """Tests for XDG_DATA_HOME.""" +def test_xdg_data_dirs_empty(monkeypatch: MonkeyPatch) -> None: + """Test xdg_data_dirs when XDG_DATA_DIRS is empty.""" + monkeypatch.setenv("XDG_DATA_DIRS", "") + assert xdg.xdg_data_dirs() == [ + Path("/usr/local/share/"), + Path("/usr/share/"), + ] - def test_unset( - self, monkeypatch: "MonkeyPatch", unimport: Callable - ) -> None: - """Test when XDG_DATA_HOME is unset.""" - monkeypatch.delenv("XDG_DATA_HOME", raising=False) - monkeypatch.setenv("HOME", os.fspath(HOME_DIR)) - from xdg import XDG_DATA_HOME - assert XDG_DATA_HOME == HOME_DIR / ".local" / "share" +def test_xdg_data_dirs_set(monkeypatch: MonkeyPatch) -> None: + """Test xdg_data_dirs when XDG_DATA_DIRS is set.""" + monkeypatch.setenv("XDG_DATA_DIRS", "/first/:/sec/ond/") + assert xdg.xdg_data_dirs() == [Path("/first/"), Path("/sec/ond/")] - def test_empty( - self, monkeypatch: "MonkeyPatch", unimport: Callable - ) -> None: - """Test when XDG_DATA_HOME is empty.""" - monkeypatch.setenv("HOME", os.fspath(HOME_DIR)) - monkeypatch.setenv("XDG_DATA_HOME", "") - from xdg import XDG_DATA_HOME - assert XDG_DATA_HOME == HOME_DIR / ".local" / "share" +def test_xdg_data_home_unset(monkeypatch: MonkeyPatch) -> None: + """Test xdg_data_home when XDG_DATA_HOME is unset.""" + monkeypatch.delenv("XDG_DATA_HOME", raising=False) + monkeypatch.setenv("HOME", os.fspath(HOME_DIR)) + assert xdg.xdg_data_home() == HOME_DIR / ".local" / "share" - def test_set(self, monkeypatch: "MonkeyPatch", unimport: Callable) -> None: - """Test when XDG_DATA_HOME is set.""" - monkeypatch.setenv("XDG_DATA_HOME", "/xdg_data_home") - from xdg import XDG_DATA_HOME - assert XDG_DATA_HOME == Path("/xdg_data_home") +def test_xdg_data_home_empty(monkeypatch: MonkeyPatch) -> None: + """Test xdg_data_home when XDG_DATA_HOME is empty.""" + monkeypatch.setenv("HOME", os.fspath(HOME_DIR)) + monkeypatch.setenv("XDG_DATA_HOME", "") + assert xdg.xdg_data_home() == HOME_DIR / ".local" / "share" -class TestXdgRuntimeDir: - """Tests for XDG_RUNTIME_DIR.""" +def test_xdg_data_home_set(monkeypatch: MonkeyPatch) -> None: + """Test xdg_data_home when XDG_DATA_HOME is set.""" + monkeypatch.setenv("XDG_DATA_HOME", "/xdg_data_home") + assert xdg.xdg_data_home() == Path("/xdg_data_home") - def test_unset( - self, monkeypatch: "MonkeyPatch", unimport: Callable - ) -> None: - """Test when XDG_RUNTIME_DIR is unset.""" - monkeypatch.delenv("XDG_RUNTIME_DIR", raising=False) - from xdg import XDG_RUNTIME_DIR - assert XDG_RUNTIME_DIR is None +def test_xdg_runtime_dir_unset(monkeypatch: MonkeyPatch) -> None: + """Test xdg_runtime_dir when XDG_RUNTIME_DIR is unset.""" + monkeypatch.delenv("XDG_RUNTIME_DIR", raising=False) + assert xdg.xdg_runtime_dir() is None - def test_empty( - self, monkeypatch: "MonkeyPatch", unimport: Callable - ) -> None: - """Test when XDG_RUNTIME_DIR is empty.""" - monkeypatch.setenv("XDG_RUNTIME_DIR", "") - from xdg import XDG_RUNTIME_DIR - assert XDG_RUNTIME_DIR == Path("") +def test_xdg_runtime_dir_empty(monkeypatch: MonkeyPatch) -> None: + """Test xdg_runtime_dir when XDG_RUNTIME_DIR is empty.""" + monkeypatch.setenv("XDG_RUNTIME_DIR", "") + assert xdg.xdg_runtime_dir() == Path("") - def test_set(self, monkeypatch: "MonkeyPatch", unimport: Callable) -> None: - """Test when XDG_RUNTIME_DIR is set.""" - monkeypatch.setenv("XDG_RUNTIME_DIR", "/xdg_runtime_dir") - from xdg import XDG_RUNTIME_DIR - assert XDG_RUNTIME_DIR == Path("/xdg_runtime_dir") +def test_xdg_runtime_dir_set(monkeypatch: MonkeyPatch) -> None: + """Test xdg_runtime_dir when XDG_RUNTIME_DIR is set.""" + monkeypatch.setenv("XDG_RUNTIME_DIR", "/xdg_runtime_dir") + assert xdg.xdg_runtime_dir() == Path("/xdg_runtime_dir")