STAR
Cal STAR — firmware, DAQ server, and engine design
Loading...
Searching...
No Matches
check_toml_no_underscore_ints.py
Go to the documentation of this file.
1#!/usr/bin/env python3
2"""
3Fail if any config TOML uses `_` digit-separators in numbers (e.g. `port = 5_006`).
4
5Those are valid TOML, but the minimal C++ config parsers in the DAQ services use
6std::stoul/stoi/stod, which stop at the underscore — so `5_006` parses as `5`, a
7privileged port, and daq_bridge crash-loops on bind (EACCES). @iarna/toml's
8stringify emits these separators for every integer >= 1000, so a GUI "Save Config"
9would reintroduce them; the writer now strips them (routes/config.ts), and this
10check guards the committed files against any other source (hand edits, other tools).
11
12Scans daq-server/config/*.toml. Quote- and comment-aware so a string value like
13"weird_1_2_name" is not flagged. Exit 1 (with file:line) on any violation.
14
15Run: python3 test/check_toml_no_underscore_ints.py (from daq-server/ or repo root)
16"""
17
18from __future__ import annotations
19
20import glob
21import os
22import re
23import sys
24
25UNDERSCORE_BETWEEN_DIGITS = re.compile(r"[0-9]_[0-9]")
26
27
28def strip_strings_and_comment(line: str) -> str:
29 """Blank out quoted spans (basic \" and literal ') and any trailing comment,
30 so only the structural/numeric part of the line remains for scanning."""
31 out = []
32 quote = None # '"' or "'"
33 for ch in line:
34 if quote:
35 out.append(" ") # keep column alignment, hide string content
36 if ch == quote:
37 quote = None
38 continue
39 if ch in ('"', "'"):
40 quote = ch
41 out.append(" ")
42 continue
43 if ch == "#": # start of an unquoted comment — ignore the rest
44 break
45 out.append(ch)
46 return "".join(out)
47
48
49def scan_file(path: str) -> list[tuple[int, str]]:
50 violations: list[tuple[int, str]] = []
51 with open(path, "r", encoding="utf-8", errors="replace") as f:
52 for i, raw in enumerate(f, start=1):
53 scannable = strip_strings_and_comment(raw)
54 if UNDERSCORE_BETWEEN_DIGITS.search(scannable):
55 violations.append((i, raw.rstrip("\n")))
56 return violations
57
58
59def main() -> int:
60 here = os.path.dirname(os.path.abspath(__file__))
61 daq_root = os.path.dirname(here) # daq-server/
62 config_dir = os.path.join(daq_root, "config")
63 # config/*.toml plus the committed config profiles (config/profiles/*.toml — the source of truth;
64 # config.toml itself is a git-ignored generated artifact).
65 files = sorted(
66 glob.glob(os.path.join(config_dir, "*.toml"))
67 + glob.glob(os.path.join(config_dir, "profiles", "*.toml"))
68 )
69 if not files:
70 print(f"[check_toml] no TOML files found in {config_dir}", flush=True)
71 return 0
72
73 total = 0
74 for path in files:
75 for line_no, text in scan_file(path):
76 rel = os.path.relpath(path, daq_root)
77 print(f"{rel}:{line_no}: underscore digit-separator in number → {text.strip()}",
78 flush=True)
79 total += 1
80
81 if total:
82 print(
83 f"\n[check_toml] {total} underscore-separated number(s) found. The C++ config "
84 "parsers (std::stoul) truncate these — write plain integers (5006, not 5_006).",
85 flush=True,
86 )
87 return 1
88 print(f"[check_toml] OK — {len(files)} config TOML file(s) have no underscore-separated numbers.",
89 flush=True)
90 return 0
91
92
93if __name__ == "__main__":
94 sys.exit(main())
list[tuple[int, str]] scan_file(str path)