Skip to content

Commit 9dfb87e

Browse files
committed
fix(cli): clean error (not traceback) when device is ambiguous in main()
main() resolves the device via setup_environment() before dispatching to the subcommand, but that call lacked the try/except that do_login/do_logout/do_env already have -- so "fdp run" (needs_env=True) dumped a raw traceback on a multi-device ambiguity instead of the helpful "choose a device" message. Wrap it to print "Error: ..." to stderr and exit(1). Adds a CLI-level regression test.
1 parent a9811d3 commit 9dfb87e

2 files changed

Lines changed: 30 additions & 5 deletions

File tree

fdp/cli.py

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -320,11 +320,20 @@ def main(argv=None) -> None:
320320
# the FDP env and shouldn't require a device contributor to be
321321
# installed, so they opt out via `needs_env=False`.
322322
if getattr(args, "needs_env", True):
323-
setup_environment(
324-
device=args.default_device,
325-
bearer_token=args.bearer_token or None,
326-
auto_login=getattr(args, "auto_login", False),
327-
)
323+
# Device resolution can fail (e.g. no default chosen among several
324+
# registered tokamaks); present it as a clean message, not a traceback.
325+
try:
326+
setup_environment(
327+
device=args.default_device,
328+
bearer_token=args.bearer_token or None,
329+
auto_login=getattr(args, "auto_login", False),
330+
)
331+
except (ValueError, KeyError) as exc:
332+
print(f"Error: {exc}", file=sys.stderr)
333+
sys.exit(1)
334+
except auth.AuthError as exc:
335+
print(f"Login failed: {exc}", file=sys.stderr)
336+
sys.exit(1)
328337

329338
args.func(args)
330339

tests/test_device_resolution.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,22 @@ def test_two_devices_nothing_set_raises_with_all_three_hints(self):
128128
self.assertIn("FDP_DEFAULT_DEVICE", msg)
129129
self.assertIn("~/.fdp/config.toml", msg)
130130

131+
def test_fdp_run_multidevice_exits_cleanly_not_traceback(self):
132+
# `fdp run` resolves the device in main() before dispatching; a
133+
# multi-device ambiguity must surface as a clean Error + exit(1),
134+
# not an uncaught traceback.
135+
import io
136+
import contextlib
137+
from fdp import cli
138+
stderr = io.StringIO()
139+
with self.assertRaises(SystemExit) as ctx, \
140+
contextlib.redirect_stderr(stderr):
141+
cli.main(["run", "true"])
142+
self.assertEqual(ctx.exception.code, 1)
143+
err = stderr.getvalue()
144+
self.assertIn("Error:", err)
145+
self.assertIn("--default-device", err)
146+
131147
def test_env_var_selects_device(self):
132148
from fdp.environment import _resolve_device_handle
133149
os.environ["FDP_DEFAULT_DEVICE"] = "mast"

0 commit comments

Comments
 (0)