Skip to content

Configuration Layer

The Configuration Layer manages application settings, dependency injection, and module registration.


Configuration Components

Settings

Settings

Settings(config_file: Optional[Path] = None)

Application settings manager with singleton pattern.

Loads configuration from YAML file and provides access to settings throughout the application. Only one instance exists per application.

Attributes:

Name Type Description
config_file Path

Path to configuration YAML file

_settings Dict[str, Any]

Loaded settings dictionary

Methods:

Name Description
get

Get setting value by dot-notation key

set

Set setting value by dot-notation key

get_section

Get entire settings section

reload

Reload settings from file

save

Save current settings to YAML file

get_all

Get all settings

Initialize settings.

Parameters:

Name Type Description Default
config_file Optional[Path]

Path to YAML configuration file. Defaults to 'config/settings.yaml'.

None
Source code in src/infrastructure/config/settings.py
def __init__(self, config_file: Optional[Path] = None):
    """
    Initialize settings.

    Parameters
    ----------
    config_file : Optional[Path], default=None
        Path to YAML configuration file.
        Defaults to 'config/settings.yaml'.
    """
    if self._initialized:
        return

    if config_file is None:
        config_file = Path("config/settings.yaml")

    self.config_file = config_file
    self._settings: Dict[str, Any] = {}
    self._load_settings()
    self._initialized = True

Functions

__new__
__new__(config_file: Optional[Path] = None)

Singleton implementation.

Parameters:

Name Type Description Default
config_file Optional[Path]

Path to config file. Only used on first instantiation.

None
Source code in src/infrastructure/config/settings.py
def __new__(cls, config_file: Optional[Path] = None):
    """
    Singleton implementation.

    Parameters
    ----------
    config_file : Optional[Path], default=None
        Path to config file. Only used on first instantiation.
    """
    if cls._instance is None:
        cls._instance = super(Settings, cls).__new__(cls)
        cls._instance._initialized = False
    return cls._instance
get
get(key: str, default: Any = None) -> Any

Get setting value by dot-notation key.

Parameters:

Name Type Description Default
key str

Setting key in dot notation (e.g., 'cache.max_size')

required
default Any

Default value if key not found

None

Returns:

Type Description
Any

Setting value or default

Source code in src/infrastructure/config/settings.py
def get(self, key: str, default: Any = None) -> Any:
    """
    Get setting value by dot-notation key.

    Parameters
    ----------
    key : str
        Setting key in dot notation (e.g., 'cache.max_size')
    default : Any, default=None
        Default value if key not found

    Returns
    -------
    Any
        Setting value or default
    """
    keys = key.split(".")
    value = self._settings

    for k in keys:
        if isinstance(value, dict) and k in value:
            value = value[k]
        else:
            return default

    return value
set
set(key: str, value: Any) -> None

Set setting value by dot-notation key.

Parameters:

Name Type Description Default
key str

Setting key in dot notation

required
value Any

Value to set

required
Source code in src/infrastructure/config/settings.py
def set(self, key: str, value: Any) -> None:
    """
    Set setting value by dot-notation key.

    Parameters
    ----------
    key : str
        Setting key in dot notation
    value : Any
        Value to set
    """
    keys = key.split(".")
    settings = self._settings

    # Navigate to nested dict
    for k in keys[:-1]:
        if k not in settings:
            settings[k] = {}
        settings = settings[k]

    # Set value
    settings[keys[-1]] = value

    logger.debug(f"Setting updated: {key} = {value}")
get_section
get_section(section: str) -> Dict[str, Any]

Get entire settings section.

Parameters:

Name Type Description Default
section str

Section name (e.g., 'cache')

required

Returns:

Type Description
Dict[str, Any]

Section dictionary or empty dict if not found

Source code in src/infrastructure/config/settings.py
def get_section(self, section: str) -> Dict[str, Any]:
    """
    Get entire settings section.

    Parameters
    ----------
    section : str
        Section name (e.g., 'cache')

    Returns
    -------
    Dict[str, Any]
        Section dictionary or empty dict if not found
    """
    return self._settings.get(section, {})
reload
reload() -> None

Reload settings from file.

Source code in src/infrastructure/config/settings.py
def reload(self) -> None:
    """Reload settings from file."""
    logger.info("Reloading settings")
    self._load_settings()
save
save(output_file: Optional[Path] = None) -> None

Save current settings to YAML file.

Parameters:

Name Type Description Default
output_file Optional[Path]

Output file path. If None, uses original config_file.

None
Source code in src/infrastructure/config/settings.py
def save(self, output_file: Optional[Path] = None) -> None:
    """
    Save current settings to YAML file.

    Parameters
    ----------
    output_file : Optional[Path], default=None
        Output file path. If None, uses original config_file.
    """
    output_file = output_file or self.config_file

    try:
        output_file.parent.mkdir(parents=True, exist_ok=True)

        with open(output_file, "w", encoding="utf-8") as f:
            yaml.dump(
                self._settings, f, default_flow_style=False, allow_unicode=True
            )

        logger.info(f"Settings saved to {output_file}")

    except Exception as e:
        logger.error(f"Failed to save settings: {e}")
        raise
get_all
get_all() -> Dict[str, Any]

Get all settings.

Returns:

Type Description
Dict[str, Any]

Complete settings dictionary.

Source code in src/infrastructure/config/settings.py
def get_all(self) -> Dict[str, Any]:
    """
    Get all settings.

    Returns
    -------
    Dict[str, Any]
        Complete settings dictionary.
    """
    return self._settings.copy()

DIContainer

DIContainer

DIContainer()

Dependency injection container.

Manages singleton and factory registrations for application dependencies. Supports lazy initialization and dependency resolution.

Attributes:

Name Type Description
_singletons Dict[str, Any]

Registered singleton instances

_factories Dict[str, Callable[[], Any]]

Registered factory functions

_types Dict[str, Type]

Registered types for lazy instantiation

Methods:

Name Description
register_singleton

Register a singleton instance

register_factory

Register a factory function

register_type

Register a type for lazy instantiation

resolve

Resolve dependency by name

is_registered

Check if dependency is registered

get_registered_names

Get list of registered dependency names

clear

Clear all registrations

unregister

Unregister a dependency

Initialize DI container.

Source code in src/infrastructure/config/dependency_injection.py
def __init__(self):
    """Initialize DI container."""
    self._singletons: Dict[str, Any] = {}
    self._factories: Dict[str, Callable[[], Any]] = {}
    self._types: Dict[str, Type] = {}

    logger.info("Initialized DIContainer")

Functions

register_singleton
register_singleton(name: str, instance: Any) -> None

Register a singleton instance.

Parameters:

Name Type Description Default
name str

Dependency name

required
instance Any

Instance to register

required
Source code in src/infrastructure/config/dependency_injection.py
def register_singleton(self, name: str, instance: Any) -> None:
    """
    Register a singleton instance.

    Parameters
    ----------
    name : str
        Dependency name
    instance : Any
        Instance to register
    """
    self._singletons[name] = instance

    logger.debug(
        f"Registered singleton: {name}", extra={"type": type(instance).__name__}
    )
register_factory
register_factory(name: str, factory: Callable[[], Any]) -> None

Register a factory function.

Factory is called each time dependency is resolved.

Parameters:

Name Type Description Default
name str

Dependency name

required
factory Callable[[], Any]

Factory function that creates instances

required
Source code in src/infrastructure/config/dependency_injection.py
def register_factory(self, name: str, factory: Callable[[], Any]) -> None:
    """
    Register a factory function.

    Factory is called each time dependency is resolved.

    Parameters
    ----------
    name : str
        Dependency name
    factory : Callable[[], Any]
        Factory function that creates instances
    """
    self._factories[name] = factory

    logger.debug(f"Registered factory: {name}")
register_type
register_type(name: str, cls: Type) -> None

Register a type for lazy instantiation.

Type is instantiated (with no-arg constructor) when first resolved.

Parameters:

Name Type Description Default
name str

Dependency name

required
cls Type

Class to instantiate

required
Source code in src/infrastructure/config/dependency_injection.py
def register_type(self, name: str, cls: Type) -> None:
    """
    Register a type for lazy instantiation.

    Type is instantiated (with no-arg constructor) when first resolved.

    Parameters
    ----------
    name : str
        Dependency name
    cls : Type
        Class to instantiate
    """
    self._types[name] = cls

    logger.debug(f"Registered type: {name}", extra={"class": cls.__name__})
resolve
resolve(name: str) -> Any

Resolve dependency by name.

Resolution order: singleton, factory, type.

Parameters:

Name Type Description Default
name str

Dependency name

required

Returns:

Type Description
Any

Resolved dependency instance

Raises:

Type Description
ValueError

If dependency not found

Source code in src/infrastructure/config/dependency_injection.py
def resolve(self, name: str) -> Any:
    """
    Resolve dependency by name.

    Resolution order: singleton, factory, type.

    Parameters
    ----------
    name : str
        Dependency name

    Returns
    -------
    Any
        Resolved dependency instance

    Raises
    ------
    ValueError
        If dependency not found
    """
    # Try singleton first
    if name in self._singletons:
        return self._singletons[name]

    # Try factory
    if name in self._factories:
        instance = self._factories[name]()
        logger.debug(f"Created instance from factory: {name}")
        return instance

    # Try type (lazy singleton)
    if name in self._types:
        cls = self._types[name]
        instance = cls()
        # Cache as singleton
        self._singletons[name] = instance
        logger.debug(
            f"Created instance from type: {name}", extra={"class": cls.__name__}
        )
        return instance

    # Not found
    available = self.get_registered_names()
    raise ValueError(f"Dependency not found: '{name}'. " f"Available: {available}")
is_registered
is_registered(name: str) -> bool

Check if dependency is registered.

Parameters:

Name Type Description Default
name str

Dependency name.

required

Returns:

Type Description
bool

True if registered.

Source code in src/infrastructure/config/dependency_injection.py
def is_registered(self, name: str) -> bool:
    """
    Check if dependency is registered.

    Parameters
    ----------
    name : str
        Dependency name.

    Returns
    -------
    bool
        True if registered.
    """
    return (
        name in self._singletons or name in self._factories or name in self._types
    )
get_registered_names
get_registered_names() -> list[str]

Get list of registered dependency names.

Returns:

Type Description
list[str]

List of dependency names.

Source code in src/infrastructure/config/dependency_injection.py
def get_registered_names(self) -> list[str]:
    """
    Get list of registered dependency names.

    Returns
    -------
    list[str]
        List of dependency names.
    """
    return list(
        set(self._singletons.keys())
        | set(self._factories.keys())
        | set(self._types.keys())
    )
clear
clear() -> None

Clear all registrations.

Source code in src/infrastructure/config/dependency_injection.py
def clear(self) -> None:
    """Clear all registrations."""
    count = len(self.get_registered_names())

    self._singletons.clear()
    self._factories.clear()
    self._types.clear()

    logger.info(f"Cleared DIContainer: {count} registrations removed")
unregister
unregister(name: str) -> bool

Unregister a dependency.

Parameters:

Name Type Description Default
name str

Dependency name.

required

Returns:

Type Description
bool

True if unregistered, False if not found.

Source code in src/infrastructure/config/dependency_injection.py
def unregister(self, name: str) -> bool:
    """
    Unregister a dependency.

    Parameters
    ----------
    name : str
        Dependency name.

    Returns
    -------
    bool
        True if unregistered, False if not found.
    """
    removed = False

    if name in self._singletons:
        del self._singletons[name]
        removed = True

    if name in self._factories:
        del self._factories[name]
        removed = True

    if name in self._types:
        del self._types[name]
        removed = True

    if removed:
        logger.debug(f"Unregistered dependency: {name}")

    return removed

AnalysisRegistry

AnalysisRegistry

AnalysisRegistry(analyses_dir: Optional[Path] = None)

Registry for analysis configurations.

Loads analysis definitions from JSON files and provides access to analysis metadata, parameters, and settings.

Attributes:

Name Type Description
analyses_dir Path

Directory containing analysis JSON files

_analyses Dict[str, Dict[str, Any]]

Loaded analysis configurations

Methods:

Name Description
get_analysis

Get analysis configuration by ID

get_all_analyses

Get all registered analyses

get_analyses_by_use_case

Get all analyses for a specific Use Case

get_analysis_ids

Get list of all analysis IDs

get_use_cases

Get list of unique Use Cases

analysis_exists

Check if analysis exists

get_analysis_plot_type

Get plot type for analysis

reload

Reload all analysis configurations

get_stats

Get registry statistics

Initialize analysis registry.

Parameters:

Name Type Description Default
analyses_dir Optional[Path]

Directory containing analysis JSON files. Defaults to 'config/analyses/'.

None
Source code in src/infrastructure/config/analysis_registry.py
def __init__(self, analyses_dir: Optional[Path] = None):
    """
    Initialize analysis registry.

    Parameters
    ----------
    analyses_dir : Optional[Path], default=None
        Directory containing analysis JSON files.
        Defaults to 'config/analyses/'.
    """
    if analyses_dir is None:
        analyses_dir = Path("config/analyses")

    self.analyses_dir = analyses_dir
    self._analyses: Dict[str, Dict[str, Any]] = {}
    self._load_analyses()

Functions

get_analysis
get_analysis(analysis_id: str) -> Optional[Dict[str, Any]]

Get analysis configuration by ID.

Parameters:

Name Type Description Default
analysis_id str

Analysis identifier (e.g., 'UC1_1')

required

Returns:

Type Description
Optional[Dict[str, Any]]

Analysis configuration or None if not found

Source code in src/infrastructure/config/analysis_registry.py
def get_analysis(self, analysis_id: str) -> Optional[Dict[str, Any]]:
    """
    Get analysis configuration by ID.

    Parameters
    ----------
    analysis_id : str
        Analysis identifier (e.g., 'UC1_1')

    Returns
    -------
    Optional[Dict[str, Any]]
        Analysis configuration or None if not found
    """
    analysis = self._analyses.get(analysis_id)

    if analysis is None:
        logger.warning(f"Analysis not found: {analysis_id}")

    return analysis
get_all_analyses
get_all_analyses() -> Dict[str, Dict[str, Any]]

Get all registered analyses.

Returns:

Type Description
Dict[str, Dict[str, Any]]

Dictionary mapping analysis IDs to configurations.

Source code in src/infrastructure/config/analysis_registry.py
def get_all_analyses(self) -> Dict[str, Dict[str, Any]]:
    """
    Get all registered analyses.

    Returns
    -------
    Dict[str, Dict[str, Any]]
        Dictionary mapping analysis IDs to configurations.
    """
    return self._analyses.copy()
get_analyses_by_use_case
get_analyses_by_use_case(use_case: str) -> List[Dict[str, Any]]

Get all analyses for a specific Use Case.

Parameters:

Name Type Description Default
use_case str

Use Case identifier (e.g., 'UC1')

required

Returns:

Type Description
List[Dict[str, Any]]

List of analysis configurations

Source code in src/infrastructure/config/analysis_registry.py
def get_analyses_by_use_case(self, use_case: str) -> List[Dict[str, Any]]:
    """
    Get all analyses for a specific Use Case.

    Parameters
    ----------
    use_case : str
        Use Case identifier (e.g., 'UC1')

    Returns
    -------
    List[Dict[str, Any]]
        List of analysis configurations
    """
    return [
        analysis
        for analysis_id, analysis in self._analyses.items()
        if analysis_id.startswith(use_case)
    ]
get_analysis_ids
get_analysis_ids() -> List[str]

Get list of all analysis IDs.

Returns:

Type Description
List[str]

List of analysis identifiers.

Source code in src/infrastructure/config/analysis_registry.py
def get_analysis_ids(self) -> List[str]:
    """
    Get list of all analysis IDs.

    Returns
    -------
    List[str]
        List of analysis identifiers.
    """
    return list(self._analyses.keys())
get_use_cases
get_use_cases() -> List[str]

Get list of unique Use Cases.

Returns:

Type Description
List[str]

List of Use Case identifiers

Source code in src/infrastructure/config/analysis_registry.py
def get_use_cases(self) -> List[str]:
    """
    Get list of unique Use Cases.

    Returns
    -------
    List[str]
        List of Use Case identifiers
    """
    use_cases = set()

    for analysis_id in self._analyses.keys():
        # Extract UC prefix (e.g., 'UC1' from 'UC1_1')
        if "_" in analysis_id:
            uc = analysis_id.split("_")[0]
            use_cases.add(uc)

    return sorted(list(use_cases))
analysis_exists
analysis_exists(analysis_id: str) -> bool

Check if analysis exists.

Parameters:

Name Type Description Default
analysis_id str

Analysis identifier.

required

Returns:

Type Description
bool

True if analysis is registered.

Source code in src/infrastructure/config/analysis_registry.py
def analysis_exists(self, analysis_id: str) -> bool:
    """
    Check if analysis exists.

    Parameters
    ----------
    analysis_id : str
        Analysis identifier.

    Returns
    -------
    bool
        True if analysis is registered.
    """
    return analysis_id in self._analyses
get_analysis_plot_type
get_analysis_plot_type(analysis_id: str) -> Optional[str]

Get plot type for analysis.

Parameters:

Name Type Description Default
analysis_id str

Analysis identifier

required

Returns:

Type Description
Optional[str]

Plot type (e.g., 'heatmap', 'bar_chart') or None

Source code in src/infrastructure/config/analysis_registry.py
def get_analysis_plot_type(self, analysis_id: str) -> Optional[str]:
    """
    Get plot type for analysis.

    Parameters
    ----------
    analysis_id : str
        Analysis identifier

    Returns
    -------
    Optional[str]
        Plot type (e.g., 'heatmap', 'bar_chart') or None
    """
    analysis = self.get_analysis(analysis_id)

    if analysis is None:
        return None

    return analysis.get("plot_type")
reload
reload() -> None

Reload all analysis configurations from files.

Source code in src/infrastructure/config/analysis_registry.py
def reload(self) -> None:
    """Reload all analysis configurations from files."""
    logger.info("Reloading analysis configurations")
    self._analyses.clear()
    self._load_analyses()
get_stats
get_stats() -> Dict[str, Any]

Get registry statistics.

Returns:

Type Description
Dict[str, Any]

Statistics including total analyses, use cases, etc.

Source code in src/infrastructure/config/analysis_registry.py
def get_stats(self) -> Dict[str, Any]:
    """
    Get registry statistics.

    Returns
    -------
    Dict[str, Any]
        Statistics including total analyses, use cases, etc.
    """
    use_cases = self.get_use_cases()
    use_case_counts = {
        uc: len(self.get_analyses_by_use_case(uc)) for uc in use_cases
    }

    return {
        "total_analyses": len(self._analyses),
        "total_use_cases": len(use_cases),
        "use_cases": use_cases,
        "analyses_per_use_case": use_case_counts,
    }

DatabaseConfig

DatabaseConfig

DatabaseConfig(config_file: Optional[Path] = None)

Database configuration manager.

Manages paths and settings for all four databases: BioRemPP, KEGG, HADEG, and ToxCSM.

Attributes:

Name Type Description
config_file Path

Path to database config YAML file

_config Dict[str, Dict[str, str]]

Loaded database configurations

Methods:

Name Description
get_database_path

Get database file path

get_database_encoding

Get database file encoding

get_database_separator

Get database CSV separator

get_all_database_paths

Get all database paths

validate_paths

Validate that all database files exist

get_database_info

Get complete database configuration

get_available_databases

Get list of available database names

Initialize database configuration.

Parameters:

Name Type Description Default
config_file Optional[Path]

Path to database config YAML file. Defaults to 'config/databases.yaml'.

None
Source code in src/infrastructure/config/database_config.py
def __init__(self, config_file: Optional[Path] = None):
    """
    Initialize database configuration.

    Parameters
    ----------
    config_file : Optional[Path], default=None
        Path to database config YAML file.
        Defaults to 'config/databases.yaml'.
    """
    if config_file is None:
        config_file = Path("config/databases.yaml")

    self.config_file = config_file
    self._config: Dict[str, Dict[str, str]] = {}
    self._load_config()

Functions

get_database_path
get_database_path(database_name: str) -> Path

Get database file path.

Parameters:

Name Type Description Default
database_name str

Database name ('biorempp', 'kegg', 'hadeg', 'toxcsm')

required

Returns:

Type Description
Path

Path to database file

Raises:

Type Description
ValueError

If database name is unknown

Source code in src/infrastructure/config/database_config.py
def get_database_path(self, database_name: str) -> Path:
    """
    Get database file path.

    Parameters
    ----------
    database_name : str
        Database name ('biorempp', 'kegg', 'hadeg', 'toxcsm')

    Returns
    -------
    Path
        Path to database file

    Raises
    ------
    ValueError
        If database name is unknown
    """
    if database_name not in self._config:
        available = list(self._config.keys())
        raise ValueError(
            f"Unknown database: '{database_name}'. " f"Available: {available}"
        )

    filepath = self._config[database_name]["filepath"]
    return Path(filepath)
get_database_encoding
get_database_encoding(database_name: str, default: str = 'utf-8') -> str

Get database file encoding.

Parameters:

Name Type Description Default
database_name str

Database name.

required
default str

Default encoding if not specified.

'utf-8'

Returns:

Type Description
str

File encoding.

Source code in src/infrastructure/config/database_config.py
def get_database_encoding(self, database_name: str, default: str = "utf-8") -> str:
    """
    Get database file encoding.

    Parameters
    ----------
    database_name : str
        Database name.
    default : str, default='utf-8'
        Default encoding if not specified.

    Returns
    -------
    str
        File encoding.
    """
    if database_name not in self._config:
        return default

    return self._config[database_name].get("encoding", default)
get_database_separator
get_database_separator(database_name: str, default: str = ';') -> str

Get database CSV separator.

Parameters:

Name Type Description Default
database_name str

Database name.

required
default str

Default separator if not specified.

';'

Returns:

Type Description
str

CSV separator.

Source code in src/infrastructure/config/database_config.py
def get_database_separator(self, database_name: str, default: str = ";") -> str:
    """
    Get database CSV separator.

    Parameters
    ----------
    database_name : str
        Database name.
    default : str, default=';'
        Default separator if not specified.

    Returns
    -------
    str
        CSV separator.
    """
    if database_name not in self._config:
        return default

    return self._config[database_name].get("separator", default)
get_all_database_paths
get_all_database_paths() -> Dict[str, Path]

Get all database paths.

Returns:

Type Description
Dict[str, Path]

Dictionary mapping database names to paths

Source code in src/infrastructure/config/database_config.py
def get_all_database_paths(self) -> Dict[str, Path]:
    """
    Get all database paths.

    Returns
    -------
    Dict[str, Path]
        Dictionary mapping database names to paths
    """
    return {name: Path(cfg["filepath"]) for name, cfg in self._config.items()}
validate_paths
validate_paths() -> Dict[str, bool]

Validate that all database files exist.

Returns:

Type Description
Dict[str, bool]

Dictionary mapping database names to existence status

Source code in src/infrastructure/config/database_config.py
def validate_paths(self) -> Dict[str, bool]:
    """
    Validate that all database files exist.

    Returns
    -------
    Dict[str, bool]
        Dictionary mapping database names to existence status
    """
    status = {}

    for name, path in self.get_all_database_paths().items():
        exists = path.exists()
        status[name] = exists

        if not exists:
            logger.warning(f"Database file not found: {name} ({path})")

    return status
get_database_info
get_database_info(database_name: str) -> Dict[str, str]

Get complete database configuration.

Parameters:

Name Type Description Default
database_name str

Database name.

required

Returns:

Type Description
Dict[str, str]

Database configuration dictionary.

Raises:

Type Description
ValueError

If database name is unknown.

Source code in src/infrastructure/config/database_config.py
def get_database_info(self, database_name: str) -> Dict[str, str]:
    """
    Get complete database configuration.

    Parameters
    ----------
    database_name : str
        Database name.

    Returns
    -------
    Dict[str, str]
        Database configuration dictionary.

    Raises
    ------
    ValueError
        If database name is unknown.
    """
    if database_name not in self._config:
        available = list(self._config.keys())
        raise ValueError(
            f"Unknown database: '{database_name}'. " f"Available: {available}"
        )

    return self._config[database_name].copy()
get_available_databases
get_available_databases() -> list[str]

Get list of available database names.

Returns:

Type Description
list[str]

List of database names.

Source code in src/infrastructure/config/database_config.py
def get_available_databases(self) -> list[str]:
    """
    Get list of available database names.

    Returns
    -------
    list[str]
        List of database names.
    """
    return list(self._config.keys())