milieux.config

  1from dataclasses import dataclass, field
  2from pathlib import Path
  3from typing import Annotated, Optional
  4
  5from fancy_dataclass import ConfigDataclass, TOMLDataclass
  6from typing_extensions import Doc
  7
  8from milieux import PKG_NAME
  9from milieux.errors import ConfigNotFoundError
 10from milieux.utils import resolve_path
 11
 12
 13#################
 14# DEFAULT PATHS #
 15#################
 16
 17def user_dir() -> Path:
 18    """Gets the path to the user's directory where configs, etc. will be stored."""
 19    return Path.home() / f'.{PKG_NAME}'
 20
 21def user_default_config_path() -> Path:
 22    """Gets the default path to the user's config file."""
 23    return user_dir() / 'config.toml'
 24
 25def user_default_base_dir() -> Path:
 26    """Gets the default path to the user's base workspace directory."""
 27    return user_dir() / 'workspace'
 28
 29# global variable storing the config path
 30_CONFIG_PATH: Optional[Path] = None
 31
 32def get_config_path() -> Path:
 33    """Gets the global configuration path."""
 34    return _CONFIG_PATH or user_default_config_path()
 35
 36def set_config_path(cfg_path: Path) -> None:
 37    """Sets the global configuration path."""
 38    global _CONFIG_PATH
 39    _CONFIG_PATH = cfg_path
 40
 41
 42##########
 43# CONFIG #
 44##########
 45
 46@dataclass
 47class PipConfig(TOMLDataclass):
 48    """Configurations for pip."""
 49    index_url: Annotated[
 50        Optional[str],
 51        Doc('URL for PyPI index')
 52    ] = None
 53
 54
 55@dataclass
 56class Config(ConfigDataclass, TOMLDataclass):  # type: ignore[misc]
 57    """Configurations for milieux."""
 58    base_dir: Annotated[
 59        str,
 60        Doc('base directory (by default, everything will be installed here)')
 61    ] = field(default_factory=lambda: str(user_default_base_dir()))
 62    env_dir: Annotated[
 63        str,
 64        Doc('directory for virtual environments')
 65    ] = 'envs'
 66    distro_dir: Annotated[
 67        str,
 68        Doc('directory for distros (Python requirements files)')
 69    ] = 'distros'
 70    pip: PipConfig = field(default_factory=PipConfig)
 71
 72    @property
 73    def base_dir_path(self) -> Path:
 74        """Gets the path to the base workspace directory."""
 75        return Path(self.base_dir)
 76
 77    @property
 78    def env_dir_path(self) -> Path:
 79        """Gets the path to the environment directory."""
 80        return resolve_path(self.env_dir, Path(self.base_dir))
 81
 82    @property
 83    def distro_dir_path(self) -> Path:
 84        """Gets the path to the distro directory."""
 85        return resolve_path(self.distro_dir, Path(self.base_dir))
 86
 87
 88def get_config() -> Config:
 89    """Gets the current configurations.
 90    If none are set, loads them from the user's default config path."""
 91    cfg = Config.get_config()
 92    if cfg is None:
 93        config_path = user_default_config_path()
 94        try:
 95            cfg = Config.load_config(config_path)
 96        except FileNotFoundError as e:
 97            raise ConfigNotFoundError(f'Could not load configs from {config_path}') from e
 98        set_config_path(config_path)
 99        cfg.update_config()
100    return cfg
101
102def update_command_with_index_url(cmd: list[str]) -> None:
103    """Given a `uv` command (list of arguments), if the globally configured `index_url` is set, adds a corresponding `--index-url` argument, in-place."""
104    cfg = get_config()
105    if (index_url := cfg.pip.index_url):
106        cmd.extend(['--index-url', index_url])
def user_dir() -> pathlib.Path:
18def user_dir() -> Path:
19    """Gets the path to the user's directory where configs, etc. will be stored."""
20    return Path.home() / f'.{PKG_NAME}'

Gets the path to the user's directory where configs, etc. will be stored.

def user_default_config_path() -> pathlib.Path:
22def user_default_config_path() -> Path:
23    """Gets the default path to the user's config file."""
24    return user_dir() / 'config.toml'

Gets the default path to the user's config file.

def user_default_base_dir() -> pathlib.Path:
26def user_default_base_dir() -> Path:
27    """Gets the default path to the user's base workspace directory."""
28    return user_dir() / 'workspace'

Gets the default path to the user's base workspace directory.

def get_config_path() -> pathlib.Path:
33def get_config_path() -> Path:
34    """Gets the global configuration path."""
35    return _CONFIG_PATH or user_default_config_path()

Gets the global configuration path.

def set_config_path(cfg_path: pathlib.Path) -> None:
37def set_config_path(cfg_path: Path) -> None:
38    """Sets the global configuration path."""
39    global _CONFIG_PATH
40    _CONFIG_PATH = cfg_path

Sets the global configuration path.

@dataclass
class PipConfig(fancy_dataclass.toml.TOMLDataclass):
47@dataclass
48class PipConfig(TOMLDataclass):
49    """Configurations for pip."""
50    index_url: Annotated[
51        Optional[str],
52        Doc('URL for PyPI index')
53    ] = None

Configurations for pip.

PipConfig( index_url: Annotated[Optional[str], Doc('URL for PyPI index')] = None)
index_url: Annotated[Optional[str], Doc('URL for PyPI index')] = None
@dataclass
class Config(fancy_dataclass.config.ConfigDataclass, fancy_dataclass.toml.TOMLDataclass):
56@dataclass
57class Config(ConfigDataclass, TOMLDataclass):  # type: ignore[misc]
58    """Configurations for milieux."""
59    base_dir: Annotated[
60        str,
61        Doc('base directory (by default, everything will be installed here)')
62    ] = field(default_factory=lambda: str(user_default_base_dir()))
63    env_dir: Annotated[
64        str,
65        Doc('directory for virtual environments')
66    ] = 'envs'
67    distro_dir: Annotated[
68        str,
69        Doc('directory for distros (Python requirements files)')
70    ] = 'distros'
71    pip: PipConfig = field(default_factory=PipConfig)
72
73    @property
74    def base_dir_path(self) -> Path:
75        """Gets the path to the base workspace directory."""
76        return Path(self.base_dir)
77
78    @property
79    def env_dir_path(self) -> Path:
80        """Gets the path to the environment directory."""
81        return resolve_path(self.env_dir, Path(self.base_dir))
82
83    @property
84    def distro_dir_path(self) -> Path:
85        """Gets the path to the distro directory."""
86        return resolve_path(self.distro_dir, Path(self.base_dir))

Configurations for milieux.

Config( base_dir: typing.Annotated[str, Doc('base directory (by default, everything will be installed here)')] = <factory>, env_dir: typing.Annotated[str, Doc('directory for virtual environments')] = 'envs', distro_dir: typing.Annotated[str, Doc('directory for distros (Python requirements files)')] = 'distros', pip: PipConfig = <factory>)
base_dir: typing.Annotated[str, Doc('base directory (by default, everything will be installed here)')]
env_dir: typing.Annotated[str, Doc('directory for virtual environments')] = 'envs'
distro_dir: typing.Annotated[str, Doc('directory for distros (Python requirements files)')] = 'distros'
pip: PipConfig
base_dir_path: pathlib.Path
73    @property
74    def base_dir_path(self) -> Path:
75        """Gets the path to the base workspace directory."""
76        return Path(self.base_dir)

Gets the path to the base workspace directory.

env_dir_path: pathlib.Path
78    @property
79    def env_dir_path(self) -> Path:
80        """Gets the path to the environment directory."""
81        return resolve_path(self.env_dir, Path(self.base_dir))

Gets the path to the environment directory.

distro_dir_path: pathlib.Path
83    @property
84    def distro_dir_path(self) -> Path:
85        """Gets the path to the distro directory."""
86        return resolve_path(self.distro_dir, Path(self.base_dir))

Gets the path to the distro directory.

def get_config() -> Config:
 89def get_config() -> Config:
 90    """Gets the current configurations.
 91    If none are set, loads them from the user's default config path."""
 92    cfg = Config.get_config()
 93    if cfg is None:
 94        config_path = user_default_config_path()
 95        try:
 96            cfg = Config.load_config(config_path)
 97        except FileNotFoundError as e:
 98            raise ConfigNotFoundError(f'Could not load configs from {config_path}') from e
 99        set_config_path(config_path)
100        cfg.update_config()
101    return cfg

Gets the current configurations. If none are set, loads them from the user's default config path.

def update_command_with_index_url(cmd: list[str]) -> None:
103def update_command_with_index_url(cmd: list[str]) -> None:
104    """Given a `uv` command (list of arguments), if the globally configured `index_url` is set, adds a corresponding `--index-url` argument, in-place."""
105    cfg = get_config()
106    if (index_url := cfg.pip.index_url):
107        cmd.extend(['--index-url', index_url])

Given a uv command (list of arguments), if the globally configured index_url is set, adds a corresponding --index-url argument, in-place.