Coverage for src / marimushka / config.py: 100%
39 statements
« prev ^ index » next coverage.py v7.13.4, created at 2026-02-28 17:41 +0000
« prev ^ index » next coverage.py v7.13.4, created at 2026-02-28 17:41 +0000
1"""Configuration management for marimushka.
3This module provides configuration loading and validation from TOML files.
4"""
6import tomllib
7from pathlib import Path
8from typing import Any
11class MarimushkaConfig:
12 """Configuration for marimushka.
14 This class encapsulates all configuration options with sensible defaults.
16 Attributes:
17 output: Output directory for exported files.
18 template: Path to Jinja2 template file.
19 notebooks: Directory containing static notebooks.
20 apps: Directory containing app notebooks.
21 notebooks_wasm: Directory containing interactive notebooks.
22 sandbox: Whether to run exports in sandbox mode.
23 parallel: Whether to export notebooks in parallel.
24 max_workers: Maximum number of parallel workers.
25 timeout: Timeout in seconds for each export.
26 audit_log: Optional path to audit log file.
27 audit_enabled: Whether audit logging is enabled.
28 max_file_size_mb: Maximum file size in MB for templates/notebooks.
29 file_permissions: Default file permissions (octal).
31 """
33 def __init__(
34 self,
35 output: str = "_site",
36 template: str | None = None,
37 notebooks: str = "notebooks",
38 apps: str = "apps",
39 notebooks_wasm: str = "notebooks_wasm",
40 sandbox: bool = True,
41 parallel: bool = True,
42 max_workers: int = 4,
43 timeout: int = 300,
44 audit_log: str | None = None,
45 audit_enabled: bool = True,
46 max_file_size_mb: int = 10,
47 file_permissions: int = 0o644,
48 ) -> None:
49 """Initialize configuration with defaults.
51 Args:
52 output: Output directory. Defaults to "_site".
53 template: Template path. Defaults to None (uses built-in).
54 notebooks: Notebooks directory. Defaults to "notebooks".
55 apps: Apps directory. Defaults to "apps".
56 notebooks_wasm: Interactive notebooks directory. Defaults to "notebooks_wasm".
57 sandbox: Use sandbox mode. Defaults to True.
58 parallel: Use parallel export. Defaults to True.
59 max_workers: Max parallel workers. Defaults to 4.
60 timeout: Export timeout. Defaults to 300.
61 audit_log: Audit log file path. Defaults to None.
62 audit_enabled: Enable audit logging. Defaults to True.
63 max_file_size_mb: Max file size in MB. Defaults to 10.
64 file_permissions: File permissions. Defaults to 0o644.
66 """
67 self.output = output
68 self.template = template
69 self.notebooks = notebooks
70 self.apps = apps
71 self.notebooks_wasm = notebooks_wasm
72 self.sandbox = sandbox
73 self.parallel = parallel
74 self.max_workers = max_workers
75 self.timeout = timeout
76 self.audit_log = audit_log
77 self.audit_enabled = audit_enabled
78 self.max_file_size_mb = max_file_size_mb
79 self.file_permissions = file_permissions
81 @classmethod
82 def from_file(cls, config_path: Path) -> "MarimushkaConfig":
83 """Load configuration from a TOML file.
85 Args:
86 config_path: Path to the configuration file.
88 Returns:
89 A MarimushkaConfig instance with loaded settings.
91 Raises:
92 FileNotFoundError: If config file doesn't exist.
93 ValueError: If config file is invalid.
95 """
96 if not config_path.exists():
97 raise FileNotFoundError(f"Config file not found: {config_path}") # noqa: TRY003
99 try:
100 with config_path.open("rb") as f:
101 config_data = tomllib.load(f)
102 except Exception as e:
103 raise ValueError(f"Failed to parse config file: {e}") from e # noqa: TRY003
105 # Extract marimushka section
106 marimushka_config = config_data.get("marimushka", {})
108 # Handle security subsection
109 security_config = marimushka_config.get("security", {})
111 return cls(
112 output=marimushka_config.get("output", "_site"),
113 template=marimushka_config.get("template"),
114 notebooks=marimushka_config.get("notebooks", "notebooks"),
115 apps=marimushka_config.get("apps", "apps"),
116 notebooks_wasm=marimushka_config.get("notebooks_wasm", "notebooks_wasm"),
117 sandbox=marimushka_config.get("sandbox", True),
118 parallel=marimushka_config.get("parallel", True),
119 max_workers=marimushka_config.get("max_workers", 4),
120 timeout=marimushka_config.get("timeout", 300),
121 audit_log=security_config.get("audit_log"),
122 audit_enabled=security_config.get("audit_enabled", True),
123 max_file_size_mb=security_config.get("max_file_size_mb", 10),
124 file_permissions=int(str(security_config.get("file_permissions", "0o644")), 8),
125 )
127 @classmethod
128 def from_file_or_defaults(cls, config_path: Path | None = None) -> "MarimushkaConfig":
129 """Load configuration from file if it exists, otherwise use defaults.
131 Args:
132 config_path: Optional path to config file. If None, looks for
133 .marimushka.toml in current directory.
135 Returns:
136 A MarimushkaConfig instance.
138 """
139 if config_path is None:
140 config_path = Path(".marimushka.toml")
142 if config_path.exists():
143 return cls.from_file(config_path)
145 return cls()
147 def to_dict(self) -> dict[str, Any]:
148 """Convert configuration to dictionary.
150 Returns:
151 Dictionary representation of configuration.
153 """
154 return {
155 "output": self.output,
156 "template": self.template,
157 "notebooks": self.notebooks,
158 "apps": self.apps,
159 "notebooks_wasm": self.notebooks_wasm,
160 "sandbox": self.sandbox,
161 "parallel": self.parallel,
162 "max_workers": self.max_workers,
163 "timeout": self.timeout,
164 "security": {
165 "audit_log": self.audit_log,
166 "audit_enabled": self.audit_enabled,
167 "max_file_size_mb": self.max_file_size_mb,
168 "file_permissions": oct(self.file_permissions),
169 },
170 }