refactor(config): add set_config_value for non-merge replacement
This commit is contained in:
1 parent
fb05409403
commit
d124a7c896
3 files changed
+78
-19
No files matched your search
@@ -8,9 +8,8 @@ from typing import Any, Literal, Optional
|
||||
from fastapi import APIRouter, HTTPException
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
from .. import config as _config_mod
|
||||
from ..clients import G1Unavailable
|
||||
from ..config import get_config, update_config
|
||||
from ..config import get_config, set_config_value
|
||||
from ..geometry import normalize_quaternion
|
||||
from ..timing import Stopwatch
|
||||
from . import _deps
|
||||
@@ -29,21 +28,6 @@ def _validate_name(name: str) -> None:
|
||||
)
|
||||
|
||||
|
||||
def _replace_named_poses(poses: dict[str, Any]) -> None:
|
||||
"""Wholesale replace pose_3d.named_poses.
|
||||
update_config does a recursive deep-merge so a dict patch can't remove
|
||||
keys from a nested dict. We bypass it by mutating the in-memory state
|
||||
directly, then call update_config with an empty patch to persist."""
|
||||
with _config_mod._LOCK:
|
||||
if _config_mod._STATE["config"] is None:
|
||||
_config_mod.load_config()
|
||||
cfg = _config_mod._STATE["config"]
|
||||
if "pose_3d" not in cfg or not isinstance(cfg["pose_3d"], dict):
|
||||
cfg["pose_3d"] = {}
|
||||
cfg["pose_3d"]["named_poses"] = poses
|
||||
update_config({}) # persist to disk
|
||||
|
||||
|
||||
class WristPose(BaseModel):
|
||||
position: list[float]
|
||||
quaternion: list[float]
|
||||
@@ -141,7 +125,7 @@ def put_pose(name: str, req: NamedPose) -> dict[str, Any]:
|
||||
cfg = get_config().get("pose_3d", {}) or {}
|
||||
poses = dict(cfg.get("named_poses") or {})
|
||||
poses[name] = pose
|
||||
_replace_named_poses(poses)
|
||||
set_config_value(["pose_3d", "named_poses"], poses)
|
||||
return {"name": name, "pose": pose}
|
||||
|
||||
|
||||
@@ -153,5 +137,5 @@ def delete_pose(name: str) -> dict[str, Any]:
|
||||
if name not in poses:
|
||||
raise HTTPException(status_code=404, detail=f"pose '{name}' non trovata")
|
||||
del poses[name]
|
||||
_replace_named_poses(poses)
|
||||
set_config_value(["pose_3d", "named_poses"], poses)
|
||||
return {"deleted": name}
|
||||
@@ -63,6 +63,36 @@ def update_config(patch: dict[str, Any]) -> dict[str, Any]:
|
||||
return deepcopy(merged)
|
||||
|
||||
|
||||
def set_config_value(path: list[str], value: Any) -> dict[str, Any]:
|
||||
"""Set a value at a nested path in config, REPLACING the existing value
|
||||
(unlike update_config which deep-merges). Useful when you need to wholesale
|
||||
replace a dict (e.g., to remove keys not present in the new value).
|
||||
|
||||
Example:
|
||||
set_config_value(["pose_3d", "named_poses"], {"a": {...}, "b": {...}})
|
||||
|
||||
Walks the path creating intermediate dicts if missing. Persists to disk.
|
||||
Returns the new full config (deep-copied)."""
|
||||
if not path:
|
||||
raise ValueError("path must be a non-empty list of keys")
|
||||
with _LOCK:
|
||||
if _STATE["config"] is None:
|
||||
load_config()
|
||||
cfg = _STATE["config"]
|
||||
node = cfg
|
||||
for key in path[:-1]:
|
||||
child = node.get(key)
|
||||
if not isinstance(child, dict):
|
||||
child = {}
|
||||
node[key] = child
|
||||
node = child
|
||||
node[path[-1]] = value
|
||||
if _STATE["path"]:
|
||||
with open(_STATE["path"], "w") as f:
|
||||
yaml.safe_dump(cfg, f, sort_keys=False)
|
||||
return deepcopy(cfg)
|
||||
|
||||
|
||||
def _deep_merge(base: dict[str, Any], patch: dict[str, Any]) -> dict[str, Any]:
|
||||
out = deepcopy(base)
|
||||
for k, v in patch.items():
|
||||
|
||||
Reference in new issue
Block a user