2025-04-16 22:12:19 +02:00

149 lines
3.6 KiB
Python

#!/usr/bin/env python
#
# Copyright 2021, Heidelberg University Clinic
#
# File author(s): Sebastian Lobentanzer
# ...
#
# Distributed under MIT licence, see the file `LICENSE`.
#
"""
Module data directory, including:
* The BioLink database schema
* The default config files
"""
from typing import Any, Optional
import os
import warnings
import yaml
import appdirs
__all__ = ["module_data", "module_data_path", "read_config", "config", "reset"]
_USER_CONFIG_DIR = appdirs.user_config_dir("biocypher", "saezlab")
_USER_CONFIG_FILE = os.path.join(_USER_CONFIG_DIR, "conf.yaml")
class MyLoader(yaml.SafeLoader):
def construct_scalar(self, node):
# Check if the scalar contains double quotes and an escape sequence
value = super().construct_scalar(node)
q = bool(node.style == '"')
b = bool("\\" in value.encode("unicode_escape").decode("utf-8"))
if q and b:
warnings.warn(
(
"Double quotes detected in YAML configuration scalar: "
f"{value.encode('unicode_escape')}. "
"These allow escape sequences and may cause problems, for "
"instance with the Neo4j admin import files (e.g. '\\t'). "
"Make sure you wanted to do this, and use single quotes "
"whenever possible."
),
category=UserWarning,
)
return value
def module_data_path(name: str) -> str:
"""
Absolute path to a YAML file shipped with the module.
"""
here = os.path.dirname(os.path.abspath(__file__))
return os.path.join(here, f"{name}.yaml")
def module_data(name: str) -> Any:
"""
Retrieve the contents of a YAML file shipped with this module.
"""
path = module_data_path(name)
return _read_yaml(path)
def _read_yaml(path: str) -> Optional[dict]:
if os.path.exists(path):
with open(path, "r") as fp:
return yaml.load(fp.read(), Loader=MyLoader)
def read_config() -> dict:
"""
Read the module config.
Read and merge the built-in default, the user level and directory level
configuration, with the later taking precendence over the former.
TODO explain path configuration
"""
defaults = module_data("biocypher_config")
user = _read_yaml(_USER_CONFIG_FILE) or {}
# TODO account for .yml?
local = (
_read_yaml("biocypher_config.yaml")
or _read_yaml("config/biocypher_config.yaml")
or {}
)
for key in defaults:
value = (
local[key] if key in local else user[key] if key in user else None
)
if value is not None:
if isinstance(
defaults[key], str
): # first level config (like title)
defaults[key] = value
else:
defaults[key].update(value)
return defaults
def config(*args, **kwargs) -> Optional[Any]:
"""
Set or get module config parameters.
"""
if args and kwargs:
raise ValueError(
"Setting and getting values in the same call is not allowed.",
)
if args:
result = tuple(globals()["_config"].get(key, None) for key in args)
return result[0] if len(result) == 1 else result
for key, value in kwargs.items():
globals()["_config"][key].update(value)
def reset():
"""
Reload configuration from the config files.
"""
globals()["_config"] = read_config()
reset()
def update_from_file(path: str):
"""
Update the module configuration from a YAML file.
"""
config(**_read_yaml(path))