Files
ETPlot/gallery/config.py
T
2026-04-22 13:48:23 +02:00

165 lines
5.0 KiB
Python

"""
Configuration Management for Scientific Gallery Generator
This module provides dataclasses for managing configuration, including
defaults for gallery generation settings.
"""
from dataclasses import dataclass, field
from pathlib import Path
from typing import List, Dict, Any, Union
import yaml
@dataclass
class GalleryDefaults:
"""Default values for gallery generation."""
png_dpi: int = 400
plot_root: str = "gallery"
cache_enabled: bool = True
inherit_from_parent: bool = True
@dataclass
class GallerySource:
"""Represents a single data source for the gallery."""
name: str
path: Union[str, Path]
def __post_init__(self):
"""Convert path to Path object if needed."""
if isinstance(self.path, str):
self.path = Path(self.path)
@dataclass
class GalleryConfig:
"""
Main configuration for gallery generation.
Can be created programmatically or loaded from YAML.
"""
web_folder: Union[str, Path]
sources: List[Union[GallerySource, Dict[str, Any]]] = field(
default_factory=list
)
png_dpi: int = GalleryDefaults.png_dpi
plot_root: str = GalleryDefaults.plot_root
cache_enabled: bool = GalleryDefaults.cache_enabled
inherit_from_parent: bool = GalleryDefaults.inherit_from_parent
backup_folder: str = ""
def __post_init__(self):
"""Normalize and validate configuration."""
# Convert web_folder to Path
if isinstance(self.web_folder, str):
self.web_folder = Path(self.web_folder)
# Convert sources to GallerySource objects if they're dicts
normalized_sources = []
for source in self.sources:
if isinstance(source, dict):
source = GallerySource(**source)
elif not isinstance(source, GallerySource):
raise TypeError(
f"Source must be dict or GallerySource, "
f"got {type(source)}"
)
normalized_sources.append(source)
self.sources = normalized_sources
@classmethod
def from_yaml(cls, yaml_file: Union[str, Path]) -> "GalleryConfig":
"""
Load configuration from a YAML file.
Args:
yaml_file: Path to the YAML configuration file
Returns:
GalleryConfig instance with loaded settings
Raises:
FileNotFoundError: If the YAML file doesn't exist
yaml.YAMLError: If the YAML file is malformed
"""
yaml_path = Path(yaml_file)
if not yaml_path.exists():
raise FileNotFoundError(f"Config file not found: {yaml_file}")
with open(yaml_path, "r") as f:
data = yaml.safe_load(f)
if data is None:
data = {}
# Extract relevant sections
web_folder = data.get("paths", {}).get("web_folder")
if not web_folder:
raise ValueError(
"web_folder must be specified in config under paths"
)
gallery_cfg = data.get("gallery", {})
sources_data = data.get("sources", [])
# Build sources list
sources = [
{"name": s["name"], "path": s["path"]} for s in sources_data
]
return cls(
web_folder=web_folder,
sources=sources,
png_dpi=gallery_cfg.get("png_dpi", GalleryDefaults.png_dpi),
plot_root=gallery_cfg.get(
"plot_root", GalleryDefaults.plot_root
),
cache_enabled=data.get("metadata", {}).get(
"cache_enabled", GalleryDefaults.cache_enabled
),
inherit_from_parent=data.get("metadata", {}).get(
"inherit_from_parent", GalleryDefaults.inherit_from_parent
),
backup_folder=gallery_cfg.get("backup_folder", ""),
)
def to_yaml(self, yaml_file: Union[str, Path]) -> None:
"""
Save the current configuration to a YAML file.
Args:
yaml_file: Path where to save the YAML configuration
Raises:
IOError: If unable to write to the specified file
"""
yaml_path = Path(yaml_file)
data = {
"paths": {
"work_dir": str(Path.cwd()),
"web_folder": str(self.web_folder),
},
"gallery": {
"plot_root": self.plot_root,
"png_dpi": self.png_dpi,
"backup_folder": self.backup_folder,
},
"ui": {
"max_recent_plots": 20,
"search_debounce_ms": 300,
},
"metadata": {
"cache_enabled": self.cache_enabled,
"inherit_from_parent": self.inherit_from_parent,
"supported_formats": [".yaml", ".yml", ".json"],
},
"sources": [
{"name": s.name, "path": str(s.path)} for s in self.sources
],
}
with open(yaml_path, "w") as f:
yaml.dump(data, f, default_flow_style=False)