Skip to content

Commit

Permalink
Merge pull request #555 from OpenCOMPES/config_renaming
Browse files Browse the repository at this point in the history
use user platformdir also for user config
  • Loading branch information
rettigl authored Jan 20, 2025
2 parents 975a506 + d283e93 commit 8ff0040
Show file tree
Hide file tree
Showing 3 changed files with 38 additions and 22 deletions.
4 changes: 2 additions & 2 deletions docs/user_guide/config.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ The config module contains a mechanism to collect configuration parameters from
It will load an (optional) provided config file, or alternatively use a passed python dictionary as initial config dictionary, and subsequently look for the following additional config files to load:

* ``folder_config``: A config file of name :file:`sed_config.yaml` in the current working directory. This is mostly intended to pass calibration parameters of the workflow between different notebook instances.
* ``user_config``: A config file provided by the user, stored as :file:`.sed/config.yaml` in the current user's home directly. This is intended to give a user the option for individual configuration modifications of system settings.
* ``system_config``: A config file provided by the system administrator, stored as :file:`/etc/sed/config.yaml` on Linux-based systems, and :file:`%ALLUSERSPROFILE%/sed/config.yaml` on Windows. This should provide all necessary default parameters for using the sed processor with a given setup. For an example for an mpes setup, see :ref:`example_config`
* ``user_config``: A config file provided by the user, stored as :file:`.config/sed/config_v1.yaml` in the current user's home directly. This is intended to give a user the option for individual configuration modifications of system settings.
* ``system_config``: A config file provided by the system administrator, stored as :file:`/etc/sed/config_v1.yaml` on Linux-based systems, and :file:`%ALLUSERSPROFILE%/sed/config_v1.yaml` on Windows. This should provide all necessary default parameters for using the sed processor with a given setup. For an example for an mpes setup, see :ref:`example_config`
* ``default_config``: The default configuration shipped with the package. Typically, all parameters here should be overwritten by any of the other configuration files.

The config mechanism returns the combined dictionary, and reports the loaded configuration files. In order to disable or overwrite any of the configuration files, they can be also given as optional parameters (path to a file, or python dictionary).
Expand Down
37 changes: 20 additions & 17 deletions src/sed/core/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@
package_dir = os.path.dirname(find_spec("sed").origin)

USER_CONFIG_PATH = user_config_path(appname="sed", appauthor="OpenCOMPES", ensure_exists=True)
SYSTEM_CONFIG_PATH = (
Path(os.environ["ALLUSERSPROFILE"]).joinpath("sed")
if platform.system() == "Windows"
else Path("/etc/").joinpath("sed")
)
ENV_DIR = Path(".env")

# Configure logging
Expand Down Expand Up @@ -50,11 +55,11 @@ def parse_config(
user_config (dict | str, optional): user-based config dictionary
or file path. The loaded dictionary is completed with the user-based values,
taking preference over system and default values.
Defaults to the file ".sed/config.yaml" in the current user's home directory.
Defaults to the file ".config/sed/config_v1.yaml" in the current user's home directory.
system_config (dict | str, optional): system-wide config dictionary
or file path. The loaded dictionary is completed with the system-wide values,
taking preference over default values. Defaults to the file "/etc/sed/config.yaml"
on linux, and "%ALLUSERSPROFILE%/sed/config.yaml" on windows.
taking preference over default values. Defaults to the file "/etc/sed/config_v1.yaml"
on linux, and "%ALLUSERSPROFILE%/sed/config_v1.yaml" on windows.
default_config (dict | str, optional): default config dictionary
or file path. The loaded dictionary is completed with the default values.
Defaults to *package_dir*/config/default.yaml".
Expand Down Expand Up @@ -94,9 +99,7 @@ def parse_config(
user_dict = copy.deepcopy(user_config)
else:
if user_config is None:
user_config = str(
Path.home().joinpath(".sed").joinpath("config.yaml"),
)
user_config = str(USER_CONFIG_PATH.joinpath("config_v1.yaml"))
if Path(user_config).exists():
user_dict = load_config(user_config)
if verbose:
Expand All @@ -107,14 +110,7 @@ def parse_config(
system_dict = copy.deepcopy(system_config)
else:
if system_config is None:
if platform.system() in ["Linux", "Darwin"]:
system_config = str(
Path("/etc/").joinpath("sed").joinpath("config.yaml"),
)
elif platform.system() == "Windows":
system_config = str(
Path(os.environ["ALLUSERSPROFILE"]).joinpath("sed").joinpath("config.yaml"),
)
system_config = str(SYSTEM_CONFIG_PATH.joinpath("config_v1.yaml"))
if Path(system_config).exists():
system_dict = load_config(system_config)
if verbose:
Expand Down Expand Up @@ -282,31 +278,38 @@ def read_env_var(var_name: str) -> str | None:
1. OS environment variables
2. .env file in current directory
3. .env file in user config directory
4. .env file in system config directory
Args:
var_name (str): Name of the environment variable to read
Returns:
str | None: Value of the environment variable or None if not found
"""
# First check OS environment variables
# 1. check OS environment variables
value = os.getenv(var_name)
if value is not None:
logger.debug(f"Found {var_name} in OS environment variables")
return value

# Then check .env in current directory
# 2. check .env in current directory
local_vars = _parse_env_file(ENV_DIR)
if var_name in local_vars:
logger.debug(f"Found {var_name} in ./.env file")
return local_vars[var_name]

# Finally check .env in user config directory
# 3. check .env in user config directory
user_vars = _parse_env_file(USER_CONFIG_PATH / ".env")
if var_name in user_vars:
logger.debug(f"Found {var_name} in user config .env file")
return user_vars[var_name]

# 4. check .env in system config directory
system_vars = _parse_env_file(SYSTEM_CONFIG_PATH / ".env")
if var_name in system_vars:
logger.debug(f"Found {var_name} in system config .env file")
return system_vars[var_name]

logger.debug(f"Environment variable {var_name} not found in any location")
return None

Expand Down
19 changes: 16 additions & 3 deletions tests/test_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -285,18 +285,27 @@ def test_env_var_precedence(mock_env_file, tmp_path, monkeypatch): # noqa: ARG0
# Create local .env directory if it doesn't exist
local_env_dir = tmp_path / "local"
local_env_dir.mkdir(exist_ok=True)
system_env_dir = tmp_path / "system"
system_env_dir.mkdir(exist_ok=True)
monkeypatch.setattr("sed.core.config.ENV_DIR", local_env_dir / ".env")
monkeypatch.setattr("sed.core.config.SYSTEM_CONFIG_PATH", system_env_dir)

# Set up test values in different locations
os.environ["TEST_VAR"] = "os_value"

# Save to user config first (lowest precedence)
# Save to system config first (4th precedence)
with open(system_env_dir / ".env", "w") as f:
f.write("TEST_VAR=system_value\n")

# Save to user config first (3rd precedence)
save_env_var("TEST_VAR", "user_value")

# Create local .env file (medium precedence)
# Create local .env file (2nd precedence)
with open(local_env_dir / ".env", "w") as f:
f.write("TEST_VAR=local_value\n")

assert read_env_var("TEST_VAR") == "os_value"

# Remove from OS env to test other precedence levels
monkeypatch.delenv("TEST_VAR", raising=False)
assert read_env_var("TEST_VAR") == "local_value"
Expand All @@ -305,8 +314,12 @@ def test_env_var_precedence(mock_env_file, tmp_path, monkeypatch): # noqa: ARG0
(local_env_dir / ".env").unlink()
assert read_env_var("TEST_VAR") == "user_value"

# Remove user config and should get None
# Remove user config and should get system value
(mock_env_file / ".env").unlink()
assert read_env_var("TEST_VAR") == "system_value"

# Remove system config and should get None
(system_env_dir / ".env").unlink()
assert read_env_var("TEST_VAR") is None


Expand Down

0 comments on commit 8ff0040

Please sign in to comment.