Skip to content

Commit

Permalink
Use .config/audb.yaml as user config file (#474)
Browse files Browse the repository at this point in the history
* Use .config/audb.yaml as user config file

* Update docs

* Add missing test file

* DEBUG

* DEBUG

* Try to fix under Windows

* DEBUG

* DEBUG

* DEBUG

* DEBUG

* Try to fix Windows tests

* Fix docstring

* Another docstring fix

* Add return types

* Simplify docstring of load_config()

* Move all tests to test_config.py

* Removed no longer needed helper function
  • Loading branch information
hagenw authored Dec 6, 2024
1 parent 110e380 commit 0442f55
Show file tree
Hide file tree
Showing 6 changed files with 99 additions and 17 deletions.
42 changes: 29 additions & 13 deletions audb/core/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,16 @@
import audeer

from audb.core.define import CONFIG_FILE
from audb.core.define import DEPRECATED_USER_CONFIG_FILE
from audb.core.define import USER_CONFIG_FILE
from audb.core.repository import Repository


def load_configuration_file(config_file: str):
CWD = audeer.script_dir()
global_config_file = os.path.join(CWD, CONFIG_FILE)


def load_configuration_file(config_file: str) -> dict:
r"""Read configuration from YAML file.
Args:
Expand Down Expand Up @@ -58,21 +63,31 @@ def load_configuration_file(config_file: str):
return config


# Read in configuration from global and user file
root = os.path.dirname(os.path.realpath(__file__))
global_config_file = os.path.join(root, CONFIG_FILE)
user_config_file = audeer.path(USER_CONFIG_FILE)
global_config = load_configuration_file(global_config_file)
user_config = load_configuration_file(user_config_file)
global_config.update(user_config)
def load_config() -> dict:
"""Read configuration from configuration files.
User config values take precedence over global config values
when the same setting exists in both files.
"""
# Global config
config = load_configuration_file(global_config_file)
# User config
if os.path.exists(audeer.path(USER_CONFIG_FILE)):
user_config_file = audeer.path(USER_CONFIG_FILE)
else:
user_config_file = audeer.path(DEPRECATED_USER_CONFIG_FILE)
user_config = load_configuration_file(user_config_file)
config.update(user_config)
return config


class config:
"""Get/set configuration values for the :mod:`audb` module.
The configuration values are read in during module import
from the :ref:`configuration file <configuration>`
:file:`~/.audb.yaml`.
:file:`~/.config/audb.yaml`.
You can change the configuration values after import,
by setting the attributes directly.
The :ref:`caching <caching>` related configuration values
Expand All @@ -87,12 +102,13 @@ class config:
"""

CACHE_ROOT = global_config["cache_root"]
_config = load_config()

CACHE_ROOT = _config["cache_root"]
r"""Default user cache folder."""

REPOSITORIES = [
Repository(r["name"], r["host"], r["backend"])
for r in global_config["repositories"]
Repository(r["name"], r["host"], r["backend"]) for r in _config["repositories"]
]
r"""Repositories, will be iterated in given order.
Expand All @@ -106,5 +122,5 @@ class config:
"""

SHARED_CACHE_ROOT = global_config["shared_cache_root"]
SHARED_CACHE_ROOT = _config["shared_cache_root"]
r"""Shared cache folder."""
3 changes: 2 additions & 1 deletion audb/core/define.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@

# Configuration files
CONFIG_FILE = os.path.join("etc", "audb.yaml")
USER_CONFIG_FILE = "~/.audb.yaml"
USER_CONFIG_FILE = "~/.config/audb.yaml"
DEPRECATED_USER_CONFIG_FILE = "~/.audb.yaml"

# Database
DB = "db"
Expand Down
2 changes: 1 addition & 1 deletion docs/caching.rst
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ There are four ways to change the default locations:

4. System wide by
using the :ref:`configuration file <configuration>`
:file:`~/.audb.yaml`
:file:`~/.config/audb.yaml`

Note,
1. overwrites all other methods,
Expand Down
2 changes: 1 addition & 1 deletion docs/configuration.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
Configuration
-------------

:mod:`audb` can be configured with a :file:`~/.audb.yaml` file.
:mod:`audb` can be configured with a :file:`~/.config/audb.yaml` file.
The configuration file is read during import
and will overwrite the default settings.
The default settings are:
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ classifiers = [
requires-python = '>=3.9'
dependencies = [
'audbackend[all] >=2.2.1',
'audeer >=2.1.0',
'audeer >=2.2.0',
'audformat >=1.2.0',
'audiofile >=1.0.0',
'audobject >=0.5.0',
Expand Down
65 changes: 65 additions & 0 deletions tests/test_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,45 @@
import audb


@pytest.fixture()
def config_files(tmpdir, request):
"""Provide user config files.
The config file at ``.config/audb.yaml``
sets ``cache_root`` to ``~/user1``.
The config file at ``.audb.yam``
sets ``cache_root`` to ``~/user2``.
Args:
tmpdir: tmpdir fixture.
The tmpdir is used as the home folder
for storing user config files
request: request fixture
for selecting which user config file to create.
``"default"`` will create a file at ``tmpdir/.config/audb.yaml``,
``"deprecated"`` will create a file at ``tmpdir/.audb.yaml``,
``"both"`` will create both files
"""
home = audeer.mkdir(tmpdir)
current_user_config_file = audb.core.define.USER_CONFIG_FILE
current_deprecated_user_config_file = audb.core.define.DEPRECATED_USER_CONFIG_FILE
audb.core.config.USER_CONFIG_FILE = audeer.path(home, ".config", "audb.yaml")
audb.core.config.DEPRECATED_USER_CONFIG_FILE = audeer.path(home, ".audb.yaml")
if request.param in ["both", "default"]:
audeer.mkdir(home, ".config")
with open(audeer.path(home, ".config", "audb.yaml"), "w") as fp:
fp.write("cache_root: ~/user1\n")
if request.param in ["both", "deprecated"]:
with open(audeer.path(home, ".audb.yaml"), "w") as fp:
fp.write("cache_root: ~/user2\n")

yield

audb.core.config.USER_CONFIG_FILE = current_user_config_file
audb.core.config.DEPRECATED_USER_CONFIG_FILE = current_deprecated_user_config_file


def test_config_file(tmpdir):
root = audeer.mkdir(tmpdir)

Expand Down Expand Up @@ -87,6 +126,32 @@ def test_config_file(tmpdir):
}


@pytest.mark.parametrize(
"config_files, expected",
[
("both", "~/user1"),
("default", "~/user1"),
("deprecated", "~/user2"),
],
indirect=["config_files"],
)
def test_deprecated_config_file(config_files, expected):
"""Test loading of user configuration files.
It especially checks,
if the default configuration file
overwrites a deprecated user config file.
Args:
config_files: config_files fixture
expected: expected value for ``cache_root``
config entry
"""
config = audb.core.config.load_config()
assert config["cache_root"] == expected


def test_empty_config_file(tmp_path):
"""Test loading an empty config file."""
empty_config = tmp_path / "empty.yaml"
Expand Down

0 comments on commit 0442f55

Please sign in to comment.