Source code for so101_mvbench.settings
"""Runtime settings: where user data lives.
Replaces the thesis workspace's ``ws_utils.get_workspace_root()``. A public user
has no "workspace" -- they have a data directory they choose. Everything the
package *ships* (USD assets, scene configs, eval.json) is resolved relative to
``__file__`` instead and never goes through here.
Usage::
from so101_mvbench.settings import SETTINGS
root = SETTINGS.require_datasets_root() # fails loud with the env var name
out = SETTINGS.outputs_root / "my_run"
Override per process::
SO101_BENCH_DATA_ROOT=/mnt/data so101-train ...
Override in a test::
Settings.from_env(data_root=tmp_path)
"""
from __future__ import annotations
import os
from dataclasses import dataclass
from pathlib import Path
__version__ = "1.0.0"
ENV_DATA_ROOT = "SO101_BENCH_DATA_ROOT"
ENV_DATASETS = "SO101_BENCH_DATASETS"
ENV_OUTPUTS = "SO101_BENCH_OUTPUTS"
ENV_LOG_FILE = "SO101_BENCH_LOG_FILE"
[docs]
@dataclass(frozen=True, slots=True)
class Settings:
"""Where user data lives. Immutable; build a new one to change it."""
data_root: Path
datasets_root: Path
outputs_root: Path
log_file: Path | None
[docs]
@classmethod
def from_env(cls, **overrides: Path | str | None) -> Settings:
"""Build from environment variables, with keyword overrides taking priority."""
def pick(key: str, env: str, default: Path | None) -> Path | None:
if key in overrides:
val = overrides[key]
return Path(val) if val is not None else None
raw = os.environ.get(env)
return Path(raw).expanduser() if raw else default
data_root = pick("data_root", ENV_DATA_ROOT, Path.cwd())
assert data_root is not None # data_root has a non-None default
return cls(
data_root=data_root,
datasets_root=pick("datasets_root", ENV_DATASETS, data_root / "datasets"),
outputs_root=pick("outputs_root", ENV_OUTPUTS, data_root / "outputs"),
log_file=pick("log_file", ENV_LOG_FILE, None),
)
[docs]
def require_datasets_root(self) -> Path:
"""Return ``datasets_root``, or fail with the env var the user needs to set."""
if not self.datasets_root.is_dir():
raise FileNotFoundError(
f"datasets root not found: {self.datasets_root}\n"
f"Set {ENV_DATASETS} (or {ENV_DATA_ROOT}) to point at your data directory."
)
return self.datasets_root
[docs]
def require_outputs_root(self) -> Path:
"""Return ``outputs_root``, creating it if absent (training writes here)."""
self.outputs_root.mkdir(parents=True, exist_ok=True)
return self.outputs_root
SETTINGS: Settings = Settings.from_env()
if __name__ == "__main__": # ponytail: inline self-check, `python -m so101_mvbench.settings`
s = Settings.from_env(data_root="/tmp/x")
assert s.datasets_root == Path("/tmp/x/datasets"), s
assert s.outputs_root == Path("/tmp/x/outputs"), s
assert s.log_file is None
s2 = Settings.from_env(data_root="/tmp/x", datasets_root="/elsewhere")
assert s2.datasets_root == Path("/elsewhere"), s2
try:
Settings.from_env(data_root="/nonexistent").require_datasets_root()
except FileNotFoundError as exc:
assert ENV_DATASETS in str(exc)
else:
raise AssertionError("require_datasets_root must fail loud on a missing dir")
print("OK settings")