Source code for rgpycrumbs.eon._mlflow.log_params

"""Logging of eOn configuration parameters to MLflow.

Performs a merge between eon-schema L0 catalog defaults and user
``config.ini`` overrides, then logs every parameter to MLflow for full
provenance tracking.

.. versionadded:: 1.1.0
.. versionchanged:: 1.9.x
    Prefer ``eon-schema`` hydrate/catalog; eon-akmc ``ConfigClass`` is a
    one-release fallback only.
"""

from __future__ import annotations

import configparser
from pathlib import Path

import mlflow


[docs] def log_config_ini( conf_ini: Path = Path("config.ini"), *, w_artifact: bool = True, track_overrides: bool = False, ): """Log the hydrated eOn configuration to MLflow. Args: conf_ini: Path to the eOn ``config.ini`` file. w_artifact: Whether to log the config file as an MLflow artifact. track_overrides: Whether to separately log parameters that the user explicitly set (values present in the INI, not only defaults). .. versionadded:: 1.1.0 """ conf_ini = Path(conf_ini) try: _log_via_eon_schema( conf_ini, w_artifact=w_artifact, track_overrides=track_overrides ) except ImportError: _log_via_eon_config_fallback( conf_ini, w_artifact=w_artifact, track_overrides=track_overrides )
[docs] def _log_via_eon_schema( conf_ini: Path, *, w_artifact: bool, track_overrides: bool, ) -> None: from eon_schema.config import hydrate_ini, read_ini user: dict[str, dict[str, str]] = {} if conf_ini.exists(): user = read_ini(conf_ini) hydrated = hydrate_ini(user) for section_name, options in hydrated.items(): for key_name, val in options.items(): full_key = f"{section_name}/{key_name}" mlflow.log_param(full_key, val) if track_overrides and conf_ini.exists(): if key_name in user.get(section_name, {}): mlflow.log_param(f"Overrides/{full_key}", val) if conf_ini.exists(): mlflow.set_tag("config.overridden_sections", ", ".join(user.keys())) mlflow.set_tag("config.hydrate_backend", "eon-schema") if w_artifact and conf_ini.exists(): mlflow.log_artifact(str(conf_ini.absolute()), "inputs")
[docs] def _log_via_eon_config_fallback( conf_ini: Path, *, w_artifact: bool, track_overrides: bool, ) -> None: """Legacy path: full eon-akmc ConfigClass (one-release fallback).""" from rgpycrumbs._aux import _import_from_parent_env eon_config = _import_from_parent_env("eon.config") if eon_config is None: raise ImportError( "log_config_ini needs eon-schema>=0.2 (preferred) or eon-akmc " "(eon.config) available for fallback.\n" " pip install 'rgpycrumbs[eon]'\n" " # or: pip install 'eon-schema>=0.2'" ) econf = eon_config.ConfigClass() hydrated_parser = configparser.ConfigParser() user_parser = configparser.ConfigParser() for section in econf.format: if not hydrated_parser.has_section(section.name): hydrated_parser.add_section(section.name) for config_key in section.keys: hydrated_parser.set(section.name, config_key.name, str(config_key.default)) if conf_ini.exists(): user_parser.read(str(conf_ini.absolute())) hydrated_parser.read(str(conf_ini.absolute())) type_getters = { "int": hydrated_parser.getint, "float": hydrated_parser.getfloat, "boolean": hydrated_parser.getboolean, "string": hydrated_parser.get, } for section in econf.format: section_name = section.name for config_key in section.keys: key_name = config_key.name full_key = f"{section_name}/{key_name}" getter = type_getters.get(config_key.kind, hydrated_parser.get) try: val = getter(section_name, key_name) mlflow.log_param(full_key, val) if track_overrides: if user_parser.has_option(section_name, key_name): mlflow.log_param(f"Overrides/{full_key}", val) except (ValueError, configparser.Error): raw_val = hydrated_parser.get(section_name, key_name) mlflow.log_param(full_key, raw_val) if track_overrides: if user_parser.has_option(section_name, key_name): mlflow.log_param(f"Overrides/{full_key}", raw_val) if conf_ini.exists(): overridden_sections = ", ".join(user_parser.sections()) mlflow.set_tag("config.overridden_sections", overridden_sections) mlflow.set_tag("config.hydrate_backend", "eon-akmc-fallback") if w_artifact and conf_ini.exists(): mlflow.log_artifact(str(conf_ini.absolute()), "inputs")