From 9e1f2068bb12eb5570dbdfb8da413f3c9632f2e7 Mon Sep 17 00:00:00 2001
From: Thomas Kluyver <thomas@kluyver.me.uk>
Date: Mon, 19 Dec 2022 17:16:16 +0000
Subject: [PATCH] Add function to update metadata.yml with file locking

---
 src/cal_tools/tools.py | 30 ++++++++++++++++++++++++++++++
 1 file changed, 30 insertions(+)

diff --git a/src/cal_tools/tools.py b/src/cal_tools/tools.py
index 2d15866ac..ee54ffa82 100644
--- a/src/cal_tools/tools.py
+++ b/src/cal_tools/tools.py
@@ -4,6 +4,7 @@ import os
 import re
 import zlib
 from collections import OrderedDict
+from fcntl import lockf, LOCK_EX, LOCK_UN
 from glob import glob
 from multiprocessing.pool import ThreadPool
 from os import environ, listdir, path
@@ -811,6 +812,35 @@ def module_index_to_qm(index: int, total_modules: int = 16):
     return f"Q{quad+1}M{mod+1}"
 
 
+def recursive_update(target: dict, source: dict):
+    for k, v2 in source.items():
+        v1 = target.get(k, None)
+        if isinstance(v1, dict) and isinstance(v2, dict):
+            recursive_update(v1, v2)
+        else:
+            target[k] = v2
+
+
+def update_metadata(folder, changes: dict):
+    yaml_fn = Path(folder) / "calibration_metadata.yml"
+    try:
+        f = yaml_fn.open('x')
+    except FileExistsError:
+        f = yaml_fn.open('r+')
+
+    with f:
+        lockf(f, LOCK_EX)
+        d = yaml.safe_load(f)
+        if d is None:
+            d = {}
+
+        recursive_update(d, changes)
+
+        f.seek(0)
+        yaml.safe_dump(d, f)
+        f.truncate()
+
+
 class CalibrationMetadata(dict):
     """Convenience class: dictionary stored in metadata YAML file
 
-- 
GitLab