dsh-labscript 0.2.6 → 0.2.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,20 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.2.7
4
+
5
+ - Fix: labs_wait no longer fails with "value is not lossless JSON". Tool results must be
6
+ pure JSON; a completed wait could carry an undefined hint field, which the DSH host
7
+ rejects when recording the result (the run itself was fine). labs_status pending
8
+ progress and the aborted-wait result are cleaned the same way.
9
+ - Fix: non-ASCII arguments (e.g. a Chinese note) no longer break the bridge. Python's
10
+ stdin on Windows uses the locale encoding (GBK); the bridge now reads stdin as UTF-8.
11
+ Such a note used to submit the run and then fail with UnicodeEncodeError in the event
12
+ log, which made a successful engage look failed.
13
+ - Fix: a failed event-log write can no longer turn a successful action into an error; it
14
+ is reported on stderr and the action result is returned as usual.
15
+ - Tests: Unicode note end-to-end through the bridge process, event-log failure
16
+ isolation, and a lossless-JSON check on tool results.
17
+
3
18
  ## 0.2.6
4
19
 
5
20
  - Fix: labs_data rendered only counts. It now prints selected first-shot globals
package/README.md CHANGED
@@ -125,8 +125,8 @@ is not a physics simulation.
125
125
  ## Development
126
126
 
127
127
  npm install # only runtime dependency: yaml
128
- npm test # node --test, 39 tests (fake bridge, no python needed)
129
- python python/tests/run_tests.py # 20 tests (needs numpy + h5py)
128
+ npm test # node --test, 40 tests (fake bridge, no python needed)
129
+ python python/tests/run_tests.py # 22 tests (needs numpy + h5py)
130
130
  LABSCRIPT_PYTHON=/path/to/python3 node scripts/demo.mjs # end-to-end mock demo
131
131
 
132
132
  A fresh clone must run `npm install` before `npm test`; the CI workflow does this.
package/lib/tools.js CHANGED
@@ -133,6 +133,7 @@ async function pendingProgress(runtime, exec) {
133
133
  note: pending.note,
134
134
  elapsed_s: pending.submitted_at ? Math.round((Date.now() - Date.parse(pending.submitted_at)) / 1000) : undefined,
135
135
  }
136
+ for (const key of Object.keys(base)) if (base[key] === undefined) delete base[key]
136
137
  if (!pending.run_folder) return base
137
138
  try {
138
139
  const progress = await runtime.runBridge('progress', {
@@ -350,7 +351,7 @@ export function registerTools(ctx, runtime) {
350
351
  runtime.state.waitCancelledByAbort = false
351
352
  return {
352
353
  run_folder: runFolder,
353
- expected_n_shots: expected,
354
+ ...(expected !== undefined ? { expected_n_shots: expected } : {}),
354
355
  complete: false,
355
356
  timed_out: false,
356
357
  aborted: true,
@@ -376,6 +377,7 @@ export function registerTools(ctx, runtime) {
376
377
  mapped_run_folder: waited.mapped_run_folder,
377
378
  folder_exists: waited.folder_exists,
378
379
  }
380
+ for (const key of Object.keys(out)) if (out[key] === undefined) delete out[key]
379
381
  if (waited.complete && runtime.state.lastRun && runtime.state.lastRun.run_folder === runFolder) {
380
382
  runtime.setLastRun(Object.assign({}, runtime.state.lastRun, {
381
383
  completed_at: new Date().toISOString(),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-labscript",
3
- "version": "0.2.6",
3
+ "version": "0.2.7",
4
4
  "description": "Control labscript-suite experiments from DeepSeek Harness: runmanager globals and scans, submits and waits, YAML fixed-operation protocols with postprocess write-back, panel snapshots, a built-in mock backend and a read-only doctor.",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
package/python/bridge.py CHANGED
@@ -612,10 +612,13 @@ def snapshot_panel(backend, config, tool, args, touched=None):
612
612
 
613
613
 
614
614
  def append_event(config, event):
615
- folder = history_dir(config)
616
- path = os.path.join(folder, "events.jsonl")
617
- with open(path, "a", encoding="utf-8") as handle:
618
- handle.write(json.dumps(jsonable(event), ensure_ascii=False) + "\n")
615
+ try:
616
+ folder = history_dir(config)
617
+ path = os.path.join(folder, "events.jsonl")
618
+ with open(path, "a", encoding="utf-8") as handle:
619
+ handle.write(json.dumps(jsonable(event), ensure_ascii=False) + "\n")
620
+ except Exception as exc:
621
+ sys.stderr.write("append_event failed: %s\n" % (exc,))
619
622
 
620
623
 
621
624
  def load_snapshot(config, name):
@@ -1530,6 +1533,10 @@ HANDLERS = {
1530
1533
 
1531
1534
 
1532
1535
  def main():
1536
+ try:
1537
+ sys.stdin.reconfigure(encoding="utf-8")
1538
+ except Exception:
1539
+ pass
1533
1540
  try:
1534
1541
  request = json.load(sys.stdin)
1535
1542
  except Exception as exc:
@@ -432,6 +432,41 @@ def test_data_keys_and_scan_summary():
432
432
  shutil.rmtree(tmp, ignore_errors=True)
433
433
 
434
434
 
435
+ def test_engage_with_unicode_note():
436
+ import subprocess
437
+ tmp = tempfile.mkdtemp(prefix="dsh-labscript-unicode-")
438
+ try:
439
+ cfg = make_config(tmp)
440
+ request = {"cmd": "engage", "config": cfg, "args": {"note": "中文备注:测试"}}
441
+ env = dict((k, v) for k, v in os.environ.items() if k not in ("PYTHONUTF8", "PYTHONIOENCODING"))
442
+ proc = subprocess.run(
443
+ [sys.executable, os.path.join(os.path.dirname(HERE), "bridge.py")],
444
+ input=json.dumps(request).encode("utf-8"),
445
+ stdout=subprocess.PIPE,
446
+ stderr=subprocess.PIPE,
447
+ env=env,
448
+ )
449
+ payload = json.loads(proc.stdout.decode("utf-8"))
450
+ assert payload.get("ok") is True, payload
451
+ assert payload["data"]["run_folder"], payload["data"]
452
+ with open(os.path.join(tmp, "history", "events.jsonl"), "r", encoding="utf-8") as handle:
453
+ assert "中文备注" in handle.read()
454
+ finally:
455
+ shutil.rmtree(tmp, ignore_errors=True)
456
+
457
+
458
+ def test_append_event_never_raises():
459
+ tmp = tempfile.mkdtemp(prefix="dsh-labscript-events-")
460
+ try:
461
+ blocked = os.path.join(tmp, "blocked")
462
+ with open(blocked, "w", encoding="utf-8") as handle:
463
+ handle.write("not a directory")
464
+ cfg = {"panelHistoryDir": os.path.join(blocked, "history")}
465
+ bridge.append_event(cfg, {"tool": "x", "note": "中文"})
466
+ finally:
467
+ shutil.rmtree(tmp, ignore_errors=True)
468
+
469
+
435
470
  def main():
436
471
  tests = [value for name, value in sorted(globals().items()) if name.startswith("test_")]
437
472
  failures = 0