"""Environment utilities."""
from __future__ import annotations
import contextlib
import os
import tempfile
from collections.abc import Iterator
from pathlib import Path
from ..constants import (
PYSTOW_HOME_ENVVAR,
PYSTOW_NAME_DEFAULT,
PYSTOW_NAME_ENVVAR,
PYSTOW_USE_APPDIRS,
)
__all__ = [
"get_base",
"get_home",
"get_name",
"getenv_path",
"mkdir",
"mock_envvar",
"mock_home",
"use_appdirs",
]
[docs]
def mkdir(path: Path, ensure_exists: bool = True) -> None:
"""Make a directory (or parent directory if a file is given) if flagged with ``ensure_exists``.
:param path: The path to a directory
:param ensure_exists: Should the directories leading to the path be created if they
don't already exist?
"""
if ensure_exists:
path.mkdir(exist_ok=True, parents=True)
[docs]
@contextlib.contextmanager
def mock_envvar(envvar: str, value: str) -> Iterator[None]:
"""Mock the environment variable then delete it after the test is over.
:param envvar: The environment variable to mock
:param value: The value to temporarily put in the environment variable during this
mock.
:yield: None, since this just mocks the environment variable for the time being.
"""
original_value = os.environ.get(envvar)
os.environ[envvar] = value
yield
if original_value is None:
del os.environ[envvar]
else:
os.environ[envvar] = original_value
[docs]
@contextlib.contextmanager
def mock_home() -> Iterator[Path]:
"""Mock the PyStow home environment variable, yields the directory name.
:yield: The path to the temporary directory.
"""
with tempfile.TemporaryDirectory() as directory:
with mock_envvar(PYSTOW_HOME_ENVVAR, directory):
yield Path(directory)
[docs]
def getenv_path(envvar: str, default: Path, ensure_exists: bool = True) -> Path:
"""Get an environment variable representing a path, or use the default.
:param envvar: The environmental variable name to check
:param default: The default path to return if the environmental variable is not set
:param ensure_exists: Should the directories leading to the path be created if they
don't already exist?
:returns: A path either specified by the environmental variable or by the default.
"""
rv = Path(os.getenv(envvar, default=default)).expanduser()
mkdir(rv, ensure_exists=ensure_exists)
return rv
[docs]
def use_appdirs() -> bool:
"""Check if X Desktop Group (XDG) compatibility is requested.
:returns: If the :data:`PYSTOW_USE_APPDIRS` is set to ``true`` in the environment.
"""
return os.getenv(PYSTOW_USE_APPDIRS) in {"true", "True"}
[docs]
def get_home(ensure_exists: bool = True) -> Path:
"""Get the PyStow home directory.
:param ensure_exists: If true, ensures the directory is created
:returns: A path object representing the pystow home directory, as one of:
1. :data:`PYSTOW_HOME_ENVVAR` environment variable or
2. The user data directory defined by :mod:`appdirs` or :mod:`platformdirs` if
the :data:`PYSTOW_USE_APPDIRS` environment variable is set to ``true`` or
3. The default directory constructed in the user's home directory plus what's
returned by :func:`get_name`.
"""
if use_appdirs():
try:
from platformdirs import user_data_dir
except ImportError:
from appdirs import user_data_dir
default = Path(user_data_dir())
else:
default = Path.home() / get_name()
return getenv_path(PYSTOW_HOME_ENVVAR, default, ensure_exists=ensure_exists)
[docs]
def get_base(key: str, ensure_exists: bool = True) -> Path:
"""Get the base directory for a module.
:param key: The name of the module. No funny characters. The envvar <key>_HOME where
key is uppercased is checked first before using the default home directory.
:param ensure_exists: Should all directories be created automatically? Defaults to
true.
:returns: The path to the given
:raises ValueError: if the key is invalid (e.g., has a dot in it)
"""
if "." in key:
raise ValueError(f"The module should not have a dot in it: {key}")
envvar = f"{key.upper()}_HOME"
if use_appdirs():
try:
from platformdirs import user_data_dir
except ImportError:
from appdirs import user_data_dir
default = Path(user_data_dir(appname=key))
else:
default = get_home(ensure_exists=False) / key
return getenv_path(envvar, default, ensure_exists=ensure_exists)
[docs]
def get_name() -> str:
"""Get the PyStow home directory name.
:returns: The name of the pystow home directory, either loaded from the
:data:`PYSTOW_NAME_ENVVAR`` environment variable or given by the default value
:data:`PYSTOW_NAME_DEFAULT`.
"""
return os.getenv(PYSTOW_NAME_ENVVAR, default=PYSTOW_NAME_DEFAULT)