Source code for datalad_core.config.gitenv

from __future__ import annotations

from contextlib import contextmanager
from typing import (
    TYPE_CHECKING,
)

if TYPE_CHECKING:
    from collections.abc import (
        Collection,
        Generator,
        Hashable,
    )

from datasalad.settings import (
    Setting,
    UnsetValue,
    WritableMultivalueSource,
)

from datalad_core.config.item import ConfigItem
from datalad_core.config.utils import (
    get_gitconfig_items_from_env,
    set_gitconfig_items_in_env,
)


[docs] class GitEnvironment(WritableMultivalueSource): """Source for Git's environment variable based ``command`` scope This class provides an interface to get and set (multivalued) configuration items in the process environment via ``GIT_CONFIG_KEY/VALUE`` variables. This mechanism is provided by ``git config``, hence any such manipulations directly affect all git-based child processes, too. This implementation is intentionally stateless to avoid any syncronicity issues from multiple agent manipulating the environment during the runtime of a process. The method :meth:`overrides` provides a context manager for setting temporary configuration. """ item_type = ConfigItem def __str__(self) -> str: return self.__class__.__name__ def _reinit(self): """Does nothing""" def _load(self) -> None: """Does nothing All accessors inspect the process environment directly. """ def _get_item(self, key: Hashable) -> Setting: val = get_gitconfig_items_from_env()[str(key)] if isinstance(val, tuple): return self.item_type(val[-1]) return self.item_type(val) def _set_item(self, key: Hashable, value: Setting) -> None: env = get_gitconfig_items_from_env() env[str(key)] = str(value.value) set_gitconfig_items_in_env(env) def _del_item(self, key: Hashable) -> None: env = get_gitconfig_items_from_env() del env[str(key)] set_gitconfig_items_in_env(env) def _get_keys(self) -> Collection: return get_gitconfig_items_from_env().keys() def _getall( self, key: Hashable, ) -> tuple[Setting, ...]: val = get_gitconfig_items_from_env()[str(key)] vals = val if isinstance(val, tuple) else (val,) return tuple(self.item_type(v) for v in vals) def _setall(self, key: Hashable, values: tuple[Setting, ...]) -> None: key_str = str(key) prepped_values = tuple(str(v.value) for v in values) env = get_gitconfig_items_from_env() env[key_str] = prepped_values set_gitconfig_items_in_env(env)
[docs] @contextmanager def overrides( self, overrides: dict[Hashable, Setting | tuple[Setting, ...]], ) -> Generator[None]: """Context manager to temporarily set configuration overrides""" restore: dict[Hashable, tuple[Setting, ...]] = {} for k, v in overrides.items(): restore[k] = self.getall(k, self.item_type(UnsetValue)) if isinstance(v, tuple): self.setall(k, v) else: self[k] = v try: yield finally: # NOTE: coverage is falsely reported as absent for PY3.9 # https://github.com/nedbat/coveragepy/issues/1292 # we disable the coverage error report below. To convince # yourself that this is covered, just introduce a syntax # error for k, vals in restore.items(): # pragma: no cover if len(vals) == 1 and vals[0].pristine_value is UnsetValue: del self[k] break self.setall(k, vals)