Skip to content

Commit 152f81f

Browse files
committed
Fix LabThings decorators and Python 3.9 unions
1 parent 4a74d77 commit 152f81f

File tree

16 files changed

+170
-160
lines changed

16 files changed

+170
-160
lines changed

pi-deployment/api/main.py

Lines changed: 23 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
import logging
1010
import os
1111
from threading import Event
12-
from typing import Any, cast
12+
from typing import Any, Optional, cast
1313

1414
import uvicorn
1515
import yaml
@@ -322,7 +322,7 @@ def capabilities() -> CapabilitiesResponse:
322322
@app.get("/api/control/flow/state", response_model=FlowState)
323323
def flow_state_legacy() -> FlowState:
324324
"""Legacy endpoint - calls Flow controller directly."""
325-
flow: FlowWeb | None = CONTROLLERS.get("flow")
325+
flow: Optional[FlowWeb] = CONTROLLERS.get("flow")
326326
if flow is None:
327327
raise HTTPException(status_code=503, detail="Flow controller unavailable")
328328

@@ -351,7 +351,7 @@ def flow_state_legacy() -> FlowState:
351351
@app.post("/api/control/flow/set_pressure")
352352
def flow_set_pressure_legacy(req: FlowSetPressureRequest):
353353
"""Legacy endpoint - calls Flow controller directly."""
354-
flow: FlowWeb | None = CONTROLLERS.get("flow")
354+
flow: Optional[FlowWeb] = CONTROLLERS.get("flow")
355355
if flow is None:
356356
raise HTTPException(status_code=503, detail="Flow controller unavailable")
357357
ok = flow.set_pressure(req.index, req.pressure_mbar)
@@ -362,7 +362,7 @@ def flow_set_pressure_legacy(req: FlowSetPressureRequest):
362362
@app.post("/api/control/flow/set_flow")
363363
def flow_set_flow_legacy(req: FlowSetFlowRequest):
364364
"""Legacy endpoint - calls Flow controller directly."""
365-
flow: FlowWeb | None = CONTROLLERS.get("flow")
365+
flow: Optional[FlowWeb] = CONTROLLERS.get("flow")
366366
if flow is None:
367367
raise HTTPException(status_code=503, detail="Flow controller unavailable")
368368
ok = flow.set_flow(req.index, req.flow_ul_hr)
@@ -373,7 +373,7 @@ def flow_set_flow_legacy(req: FlowSetFlowRequest):
373373
@app.post("/api/control/flow/set_mode")
374374
def flow_set_mode_legacy(req: FlowSetModeRequest):
375375
"""Legacy endpoint - calls Flow controller directly."""
376-
flow: FlowWeb | None = CONTROLLERS.get("flow")
376+
flow: Optional[FlowWeb] = CONTROLLERS.get("flow")
377377
if flow is None:
378378
raise HTTPException(status_code=503, detail="Flow controller unavailable")
379379
from config import CONTROL_MODE_UI_TO_FIRMWARE
@@ -387,7 +387,7 @@ def flow_set_mode_legacy(req: FlowSetModeRequest):
387387
@app.post("/api/control/flow/set_pi_consts")
388388
def flow_set_pi_legacy(req: FlowSetPIRequest):
389389
"""Legacy endpoint - calls Flow controller directly."""
390-
flow: FlowWeb | None = CONTROLLERS.get("flow")
390+
flow: Optional[FlowWeb] = CONTROLLERS.get("flow")
391391
if flow is None:
392392
raise HTTPException(status_code=503, detail="Flow controller unavailable")
393393
ok = flow.set_flow_pi_consts(req.index, [req.p, req.i])
@@ -398,7 +398,7 @@ def flow_set_pi_legacy(req: FlowSetPIRequest):
398398
@app.get("/api/control/heater/state", response_model=HeaterState)
399399
def heater_state_legacy():
400400
"""Legacy endpoint - calls Heater controllers directly."""
401-
heaters: list[heater_web] | None = CONTROLLERS.get("heaters")
401+
heaters: Optional[list[heater_web]] = CONTROLLERS.get("heaters")
402402
if heaters is None:
403403
raise HTTPException(status_code=503, detail="Heaters unavailable")
404404

@@ -425,7 +425,7 @@ def heater_state_legacy():
425425
@app.post("/api/control/heater/set_temp")
426426
def heater_set_temp_legacy(req: HeaterSetTempRequest):
427427
"""Legacy endpoint - calls Heater controller directly."""
428-
heaters: list[heater_web] | None = CONTROLLERS.get("heaters")
428+
heaters: Optional[list[heater_web]] = CONTROLLERS.get("heaters")
429429
if heaters is None or req.index >= len(heaters):
430430
raise HTTPException(status_code=503, detail="Heaters unavailable")
431431
heaters[req.index].set_temp(req.temp_c)
@@ -434,7 +434,7 @@ def heater_set_temp_legacy(req: HeaterSetTempRequest):
434434
@app.post("/api/control/heater/pid")
435435
def heater_set_pid_legacy(req: HeaterSetPidRequest):
436436
"""Legacy endpoint - calls Heater controller directly."""
437-
heaters: list[heater_web] | None = CONTROLLERS.get("heaters")
437+
heaters: Optional[list[heater_web]] = CONTROLLERS.get("heaters")
438438
if heaters is None or req.index >= len(heaters):
439439
raise HTTPException(status_code=503, detail="Heaters unavailable")
440440
heaters[req.index].set_pid_running(1 if req.enabled else 0)
@@ -444,7 +444,7 @@ def heater_set_pid_legacy(req: HeaterSetPidRequest):
444444
@app.post("/api/control/heater/stir")
445445
def heater_set_stir_legacy(req: HeaterSetStirRequest):
446446
"""Legacy endpoint - calls Heater controller directly."""
447-
heaters: list[heater_web] | None = CONTROLLERS.get("heaters")
447+
heaters: Optional[list[heater_web]] = CONTROLLERS.get("heaters")
448448
if heaters is None or req.index >= len(heaters):
449449
raise HTTPException(status_code=503, detail="Heaters unavailable")
450450
heaters[req.index].set_stir_running(1 if req.enabled else 0)
@@ -454,7 +454,7 @@ def heater_set_stir_legacy(req: HeaterSetStirRequest):
454454
@app.post("/api/control/heater/power_limit")
455455
def heater_set_power_limit_legacy(req: HeaterSetPowerLimitRequest):
456456
"""Legacy endpoint - calls Heater controller directly."""
457-
heaters: list[heater_web] | None = CONTROLLERS.get("heaters")
457+
heaters: Optional[list[heater_web]] = CONTROLLERS.get("heaters")
458458
if heaters is None or req.index >= len(heaters):
459459
raise HTTPException(status_code=503, detail="Heaters unavailable")
460460
heaters[req.index].set_heat_power_limit_pc(req.power_limit_pc)
@@ -463,7 +463,7 @@ def heater_set_power_limit_legacy(req: HeaterSetPowerLimitRequest):
463463
@app.post("/api/control/heater/autotune")
464464
def heater_set_autotune_legacy(req: HeaterSetAutotuneRequest):
465465
"""Legacy endpoint - calls Heater controller directly."""
466-
heaters: list[heater_web] | None = CONTROLLERS.get("heaters")
466+
heaters: Optional[list[heater_web]] = CONTROLLERS.get("heaters")
467467
if heaters is None or req.index >= len(heaters):
468468
raise HTTPException(status_code=503, detail="Heaters unavailable")
469469
heaters[req.index].autotune_target_temp = req.temp_c
@@ -473,7 +473,7 @@ def heater_set_autotune_legacy(req: HeaterSetAutotuneRequest):
473473
@app.get("/api/streams/camera/snapshot")
474474
def camera_snapshot_legacy():
475475
"""Legacy endpoint - uses Camera controller directly (snapshot is synchronous)."""
476-
cam: Camera | None = CONTROLLERS.get("camera")
476+
cam: Optional[Camera] = CONTROLLERS.get("camera")
477477
if cam is None:
478478
raise HTTPException(status_code=503, detail="Camera unavailable")
479479
try:
@@ -491,7 +491,7 @@ def camera_snapshot_legacy():
491491
@app.post("/api/control/camera/set_resolution")
492492
def camera_set_resolution_legacy(req: CameraResolutionRequest):
493493
"""Legacy endpoint - calls Camera controller directly."""
494-
cam: Camera | None = CONTROLLERS.get("camera")
494+
cam: Optional[Camera] = CONTROLLERS.get("camera")
495495
if cam is None:
496496
raise HTTPException(status_code=503, detail="Camera unavailable")
497497
params: dict[str, Any] = {}
@@ -506,7 +506,7 @@ def camera_set_resolution_legacy(req: CameraResolutionRequest):
506506
@app.post("/api/control/camera/set_snapshot_resolution")
507507
def camera_set_snapshot_resolution_legacy(req: CameraSnapshotResolutionRequest):
508508
"""Legacy endpoint - calls Camera controller directly."""
509-
cam: Camera | None = CONTROLLERS.get("camera")
509+
cam: Optional[Camera] = CONTROLLERS.get("camera")
510510
if cam is None:
511511
raise HTTPException(status_code=503, detail="Camera unavailable")
512512
params: dict[str, Any] = {"mode": req.mode}
@@ -519,7 +519,7 @@ def camera_set_snapshot_resolution_legacy(req: CameraSnapshotResolutionRequest):
519519
@app.post("/api/control/camera/roi")
520520
def camera_set_roi_legacy(req: CameraROIRequest):
521521
"""Legacy endpoint - calls Camera controller directly."""
522-
cam: Camera | None = CONTROLLERS.get("camera")
522+
cam: Optional[Camera] = CONTROLLERS.get("camera")
523523
if cam is None:
524524
raise HTTPException(status_code=503, detail="Camera unavailable")
525525
cam.on_roi({"cmd": CMD_SET, "parameters": {"x": req.x, "y": req.y, "w": req.w, "h": req.h}})
@@ -528,7 +528,7 @@ def camera_set_roi_legacy(req: CameraROIRequest):
528528
@app.post("/api/control/camera/roi/clear")
529529
def camera_clear_roi_legacy():
530530
"""Legacy endpoint - calls Camera controller directly."""
531-
cam: Camera | None = CONTROLLERS.get("camera")
531+
cam: Optional[Camera] = CONTROLLERS.get("camera")
532532
if cam is None:
533533
raise HTTPException(status_code=503, detail="Camera unavailable")
534534
cam.on_roi({"cmd": CMD_CLEAR, "parameters": {}})
@@ -537,7 +537,7 @@ def camera_clear_roi_legacy():
537537
@app.get("/api/control/camera/state", response_model=CameraState)
538538
def camera_state_legacy() -> CameraState:
539539
"""Return current camera state for remote UI clients."""
540-
cam: Camera | None = CONTROLLERS.get("camera")
540+
cam: Optional[Camera] = CONTROLLERS.get("camera")
541541
if cam is None:
542542
raise HTTPException(status_code=503, detail="Camera unavailable")
543543
roi_tuple = cam.get_roi()
@@ -562,7 +562,7 @@ def camera_state_legacy() -> CameraState:
562562
@app.post("/api/control/camera/select")
563563
def camera_select_legacy(req: CameraSelectRequest):
564564
"""Select camera backend (legacy endpoint)."""
565-
cam: Camera | None = CONTROLLERS.get("camera")
565+
cam: Optional[Camera] = CONTROLLERS.get("camera")
566566
if cam is None or cam.strobe_cam is None:
567567
raise HTTPException(status_code=503, detail="Camera unavailable")
568568
if cam.thread is not None and cam.thread.is_alive():
@@ -590,7 +590,7 @@ def camera_select_legacy(req: CameraSelectRequest):
590590
@app.post("/api/control/strobe/enable")
591591
def strobe_enable_legacy(req: StrobeEnableRequest):
592592
"""Legacy endpoint - calls Camera controller directly."""
593-
cam: Camera | None = CONTROLLERS.get("camera")
593+
cam: Optional[Camera] = CONTROLLERS.get("camera")
594594
if cam is None:
595595
raise HTTPException(status_code=503, detail="Camera unavailable")
596596
cam.on_strobe({"cmd": CMD_ENABLE, "parameters": {"on": 1 if req.on else 0}})
@@ -599,7 +599,7 @@ def strobe_enable_legacy(req: StrobeEnableRequest):
599599
@app.post("/api/control/strobe/hold")
600600
def strobe_hold_legacy(req: StrobeEnableRequest):
601601
"""Legacy endpoint - calls Camera controller directly."""
602-
cam: Camera | None = CONTROLLERS.get("camera")
602+
cam: Optional[Camera] = CONTROLLERS.get("camera")
603603
if cam is None:
604604
raise HTTPException(status_code=503, detail="Camera unavailable")
605605
cam.on_strobe({"cmd": CMD_HOLD, "parameters": {"on": 1 if req.on else 0}})
@@ -608,7 +608,7 @@ def strobe_hold_legacy(req: StrobeEnableRequest):
608608
@app.post("/api/control/strobe/timing")
609609
def strobe_timing_legacy(req: StrobeTimingRequest):
610610
"""Legacy endpoint - calls Camera controller directly."""
611-
cam: Camera | None = CONTROLLERS.get("camera")
611+
cam: Optional[Camera] = CONTROLLERS.get("camera")
612612
if cam is None:
613613
raise HTTPException(status_code=503, detail="Camera unavailable")
614614
params = {"period_ns": int(req.period_ns)}
@@ -620,7 +620,7 @@ def strobe_timing_legacy(req: StrobeTimingRequest):
620620
@app.get("/api/control/strobe/state", response_model=StrobeState)
621621
def strobe_state_legacy() -> StrobeState:
622622
"""Return current strobe state for remote UI clients."""
623-
cam: Camera | None = CONTROLLERS.get("camera")
623+
cam: Optional[Camera] = CONTROLLERS.get("camera")
624624
if cam is None:
625625
raise HTTPException(status_code=503, detail="Camera unavailable")
626626
cam.update_strobe_data()

pi-deployment/api/main_old.py

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
import logging
1212
import os
1313
from threading import Event
14-
from typing import Any, cast
14+
from typing import Any, Optional, cast
1515

1616
import uvicorn
1717
import yaml
@@ -300,7 +300,7 @@ def set_channels(config: ChannelConfig) -> ChannelConfigResponse:
300300

301301
@app.get("/api/control/flow/state", response_model=FlowState)
302302
def flow_state() -> FlowState:
303-
flow: FlowWeb | None = CONTROLLERS.get("flow")
303+
flow: Optional[FlowWeb] = CONTROLLERS.get("flow")
304304
if flow is None:
305305
raise HTTPException(status_code=503, detail="Flow controller unavailable")
306306

@@ -328,7 +328,7 @@ def flow_state() -> FlowState:
328328

329329
@app.post("/api/control/flow/set_pressure")
330330
def flow_set_pressure(req: FlowSetPressureRequest):
331-
flow: FlowWeb | None = CONTROLLERS.get("flow")
331+
flow: Optional[FlowWeb] = CONTROLLERS.get("flow")
332332
if flow is None:
333333
raise HTTPException(status_code=503, detail="Flow controller unavailable")
334334
ok = flow.set_pressure(req.index, req.pressure_mbar)
@@ -338,7 +338,7 @@ def flow_set_pressure(req: FlowSetPressureRequest):
338338

339339
@app.post("/api/control/flow/set_flow")
340340
def flow_set_flow(req: FlowSetFlowRequest):
341-
flow: FlowWeb | None = CONTROLLERS.get("flow")
341+
flow: Optional[FlowWeb] = CONTROLLERS.get("flow")
342342
if flow is None:
343343
raise HTTPException(status_code=503, detail="Flow controller unavailable")
344344
ok = flow.set_flow(req.index, req.flow_ul_hr)
@@ -348,7 +348,7 @@ def flow_set_flow(req: FlowSetFlowRequest):
348348

349349
@app.post("/api/control/flow/set_mode")
350350
def flow_set_mode(req: FlowSetModeRequest):
351-
flow: FlowWeb | None = CONTROLLERS.get("flow")
351+
flow: Optional[FlowWeb] = CONTROLLERS.get("flow")
352352
if flow is None:
353353
raise HTTPException(status_code=503, detail="Flow controller unavailable")
354354
firmware_mode = CONTROL_MODE_UI_TO_FIRMWARE.get(req.mode_ui, 0)
@@ -359,7 +359,7 @@ def flow_set_mode(req: FlowSetModeRequest):
359359

360360
@app.post("/api/control/flow/set_pi_consts")
361361
def flow_set_pi(req: FlowSetPIRequest):
362-
flow: FlowWeb | None = CONTROLLERS.get("flow")
362+
flow: Optional[FlowWeb] = CONTROLLERS.get("flow")
363363
if flow is None:
364364
raise HTTPException(status_code=503, detail="Flow controller unavailable")
365365
ok = flow.set_flow_pi_consts(req.index, [req.p, req.i])
@@ -373,7 +373,7 @@ def flow_set_pi(req: FlowSetPIRequest):
373373

374374
@app.get("/api/control/heater/state", response_model=HeaterState)
375375
def heater_state():
376-
heaters: list[heater_web] | None = CONTROLLERS.get("heaters")
376+
heaters: Optional[list[heater_web]] = CONTROLLERS.get("heaters")
377377
if heaters is None:
378378
raise HTTPException(status_code=503, detail="Heaters unavailable")
379379
items: list[HeaterStateItem] = []
@@ -393,15 +393,15 @@ def heater_state():
393393

394394
@app.post("/api/control/heater/set_temp")
395395
def heater_set_temp(req: HeaterSetTempRequest):
396-
heaters: list[heater_web] | None = CONTROLLERS.get("heaters")
396+
heaters: Optional[list[heater_web]] = CONTROLLERS.get("heaters")
397397
if heaters is None or req.index >= len(heaters):
398398
raise HTTPException(status_code=503, detail="Heaters unavailable")
399399
heaters[req.index].set_temp(req.temp_c)
400400
return {"ok": True}
401401

402402
@app.post("/api/control/heater/pid")
403403
def heater_set_pid(req: HeaterSetPidRequest):
404-
heaters: list[heater_web] | None = CONTROLLERS.get("heaters")
404+
heaters: Optional[list[heater_web]] = CONTROLLERS.get("heaters")
405405
if heaters is None or req.index >= len(heaters):
406406
raise HTTPException(status_code=503, detail="Heaters unavailable")
407407
heaters[req.index].set_pid_running(1 if req.enabled else 0)
@@ -410,7 +410,7 @@ def heater_set_pid(req: HeaterSetPidRequest):
410410

411411
@app.post("/api/control/heater/stir")
412412
def heater_set_stir(req: HeaterSetStirRequest):
413-
heaters: list[heater_web] | None = CONTROLLERS.get("heaters")
413+
heaters: Optional[list[heater_web]] = CONTROLLERS.get("heaters")
414414
if heaters is None or req.index >= len(heaters):
415415
raise HTTPException(status_code=503, detail="Heaters unavailable")
416416
heaters[req.index].set_stir_running(1 if req.enabled else 0)
@@ -423,7 +423,7 @@ def heater_set_stir(req: HeaterSetStirRequest):
423423

424424
@app.get("/api/streams/camera/snapshot")
425425
def camera_snapshot():
426-
cam: Camera | None = CONTROLLERS.get("camera")
426+
cam: Optional[Camera] = CONTROLLERS.get("camera")
427427
if cam is None:
428428
raise HTTPException(status_code=503, detail="Camera unavailable")
429429
if cam.thread is None or not cam.thread.is_alive():
@@ -435,7 +435,7 @@ def camera_snapshot():
435435

436436
@app.post("/api/control/camera/set_resolution")
437437
def camera_set_resolution(req: CameraResolutionRequest):
438-
cam: Camera | None = CONTROLLERS.get("camera")
438+
cam: Optional[Camera] = CONTROLLERS.get("camera")
439439
if cam is None:
440440
raise HTTPException(status_code=503, detail="Camera unavailable")
441441
params: dict[str, Any] = {}
@@ -449,7 +449,7 @@ def camera_set_resolution(req: CameraResolutionRequest):
449449

450450
@app.post("/api/control/camera/set_snapshot_resolution")
451451
def camera_set_snapshot_resolution(req: CameraSnapshotResolutionRequest):
452-
cam: Camera | None = CONTROLLERS.get("camera")
452+
cam: Optional[Camera] = CONTROLLERS.get("camera")
453453
if cam is None:
454454
raise HTTPException(status_code=503, detail="Camera unavailable")
455455
params: dict[str, Any] = {"mode": req.mode}
@@ -461,39 +461,39 @@ def camera_set_snapshot_resolution(req: CameraSnapshotResolutionRequest):
461461

462462
@app.post("/api/control/camera/roi")
463463
def camera_set_roi(req: CameraROIRequest):
464-
cam: Camera | None = CONTROLLERS.get("camera")
464+
cam: Optional[Camera] = CONTROLLERS.get("camera")
465465
if cam is None:
466466
raise HTTPException(status_code=503, detail="Camera unavailable")
467467
cam.on_roi({"cmd": CMD_SET, "parameters": {"x": req.x, "y": req.y, "w": req.w, "h": req.h}})
468468
return {"ok": True}
469469

470470
@app.post("/api/control/camera/roi/clear")
471471
def camera_clear_roi():
472-
cam: Camera | None = CONTROLLERS.get("camera")
472+
cam: Optional[Camera] = CONTROLLERS.get("camera")
473473
if cam is None:
474474
raise HTTPException(status_code=503, detail="Camera unavailable")
475475
cam.on_roi({"cmd": CMD_CLEAR, "parameters": {}})
476476
return {"ok": True}
477477

478478
@app.post("/api/control/strobe/enable")
479479
def strobe_enable(req: StrobeEnableRequest):
480-
cam: Camera | None = CONTROLLERS.get("camera")
480+
cam: Optional[Camera] = CONTROLLERS.get("camera")
481481
if cam is None:
482482
raise HTTPException(status_code=503, detail="Camera unavailable")
483483
cam.on_strobe({"cmd": CMD_ENABLE, "parameters": {"on": 1 if req.on else 0}})
484484
return {"ok": True}
485485

486486
@app.post("/api/control/strobe/hold")
487487
def strobe_hold(req: StrobeEnableRequest):
488-
cam: Camera | None = CONTROLLERS.get("camera")
488+
cam: Optional[Camera] = CONTROLLERS.get("camera")
489489
if cam is None:
490490
raise HTTPException(status_code=503, detail="Camera unavailable")
491491
cam.on_strobe({"cmd": CMD_HOLD, "parameters": {"on": 1 if req.on else 0}})
492492
return {"ok": True}
493493

494494
@app.post("/api/control/strobe/timing")
495495
def strobe_timing(req: StrobeTimingRequest):
496-
cam: Camera | None = CONTROLLERS.get("camera")
496+
cam: Optional[Camera] = CONTROLLERS.get("camera")
497497
if cam is None:
498498
raise HTTPException(status_code=503, detail="Camera unavailable")
499499
params = {"period_ns": int(req.period_ns)}

pi-deployment/api/streams.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,7 @@ def _get_calibration(self, topic: str, idx: int) -> float:
186186

187187
# Capture support
188188
def start_capture(
189-
self, topics: List[str], channels: Dict[str, List[int]], path: str | None = None
189+
self, topics: List[str], channels: Dict[str, List[int]], path: Optional[str] = None
190190
):
191191
if self.capture.enabled:
192192
self.stop_capture()

0 commit comments

Comments
 (0)