Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New ConfigManager, Repo, and Worktree #19

Merged
merged 6 commits into from
Oct 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
"""Fixture setup"""

__all__ = [
'cfgman',
'baregitrepo',
'gitrepo',
'verify_pristine_gitconfig_global',
]


from datalad_core.tests.fixtures import (
# function-scope temporary, bare Git repo
baregitrepo,
# function-scope config manager
cfgman,
# function-scope temporary Git repo
gitrepo,
# verify no test leave contaminated config behind
verify_pristine_gitconfig_global,
)
90 changes: 90 additions & 0 deletions datalad_core/config/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
"""Configuration management

This module provides the standard facilities for configuration management,
query, and update. It is built on `datasalad.settings
<https://datasalad.readthedocs.io/latest/generated/datasalad.settings.html>`__.

The key piece is the :class:`ConfigManager` that supports querying for
configuration settings across multiple sources. It also offers a context
manager to temporarily override particular configuration items.

With the method :meth:`ConfigManager.for_dataset`, a manager instance tailored
to a specific dataset can be created. A "dataset" in this context is any
Git repository, with or without an initialized annex, and with or without
initialization as a DataLad dataset. Such an instance will share all sources
of the original manager instance, and supports additional sources for the
scopes of repository-local Git configuration, and branch-committed DataLad
configuration.

Usage
-----

Basic usage is more or less identical to that of the ``datasalad.settings``.
Importantly, and unlike the legacy DataLad approach, no instance of
:class:`~datalad_core.config.ConfigManager` is provided. Instead, if and when
such a common instance it needed, it must be obtained by calling
:func:`get_manager`. This is typically done first by a respective "entrypoint"
implementation. Subsequent calls will return the same instance.

The same pattern is applied to obtain a common instance of
:class:`ImplementationDefaults` via :func:`get_defaults`. This instance
can be used to register defaults for any and all configuration settings
supported by a particular implementation. This can be done on importing
the respective modules.


.. currentmodule:: datalad_core.config
.. autosummary::
:toctree: generated

ConfigItem
ConfigManager
GitConfig
SystemGitConfig
GlobalGitConfig
LocalGitConfig
DataladBranchConfig
WorktreeGitConfig
GitEnvironment
ImplementationDefaults
UnsetValue
get_defaults
get_manager
"""

__all__ = [
'ConfigItem',
'ConfigManager',
'GitConfig',
'SystemGitConfig',
'GlobalGitConfig',
'LocalGitConfig',
'DataladBranchConfig',
'WorktreeGitConfig',
'GitEnvironment',
'ImplementationDefaults',
'UnsetValue',
'get_defaults',
'get_manager',
]

from datasalad.settings import UnsetValue

from .defaults import (
ImplementationDefaults,
get_defaults,
)
from .git import (
DataladBranchConfig,
GitConfig,
GlobalGitConfig,
LocalGitConfig,
SystemGitConfig,
WorktreeGitConfig,
)
from .gitenv import GitEnvironment
from .item import ConfigItem
from .manager import (
ConfigManager,
get_manager,
)
66 changes: 66 additions & 0 deletions datalad_core/config/defaults.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
from __future__ import annotations

from datasalad.settings import (
Defaults,
)
from datasalad.settings import (
UnsetValue as Unset,
)

from datalad_core.config.item import ConfigItem as Item


class ImplementationDefaults(Defaults):
"""Source for registering implementation defaults of settings

This is a in-memory only source that is populated by any
implementations that want to expose their configuration
options."""

def __str__(self):
return 'ImplementationDefaults'


__the_defaults: ImplementationDefaults | None = None


def get_defaults() -> ImplementationDefaults:
"""Return a a process-unique `ImplementationDefault` instance

This function can be used obtain a :class:`ImplementationDefaults`
instance for setting and/or getting defaults for settings.
"""
global __the_defaults # noqa: PLW0603
if __the_defaults is None:
__the_defaults = ImplementationDefaults()
register_defaults_gitcfg(__the_defaults)
return __the_defaults


def register_defaults_gitcfg(defaults: ImplementationDefaults) -> None:
for k, v in _gitcfg.items():
defaults[k] = v


def anything2bool(val):
if val == '':
return False
if hasattr(val, 'lower'):
val = val.lower()
if val in {'off', 'no', 'false', '0'} or not bool(val):
return False
if (
val in {'on', 'yes', 'true', True}
or (hasattr(val, 'isdigit') and val.isdigit() and int(val))
or isinstance(val, int)
and val
):
return True
msg = f'Cannot interpret {val!r} as a boolean'
raise ValueError(msg)


_gitcfg = {
'core.bare': Item(Unset, coercer=anything2bool),
'extensions.worktreeConfig': Item(Unset, coercer=anything2bool),
}
Loading