Coverage for src/signalk_cli/_cli.py: 100%

43 statements  

« prev     ^ index     » next       coverage.py v7.16.1, created at 2026-10-06 00:17 +0000

1"""Click helpers shared by the history and stream CLIs. Not part of the library API.""" 

2 

3import contextlib 

4import io 

5import logging 

6from collections.abc import Iterator 

7 

8import click 

9 

10from . import net 

11 

12logger = logging.getLogger("signalk_cli") 

13 

14 

15class _ClickHandler(logging.Handler): 

16 """Send library log messages to stderr as plain text, like the CLI's own messages.""" 

17 

18 def emit(self, record: logging.LogRecord) -> None: 

19 click.echo(self.format(record), err=True) 

20 

21 

22def host_option(f): 

23 return click.option( 

24 "--host", 

25 default=None, 

26 envvar="SIGNALK_HOST", 

27 help="SignalK server base URL. http:// added if scheme omitted. " 

28 "Discovered via mDNS if omitted.", 

29 )(f) 

30 

31 

32def bare_option(f): 

33 return click.option( 

34 "--bare", 

35 is_flag=True, 

36 help="Suppress all informational messages, outputting data only.", 

37 )(f) 

38 

39 

40@contextlib.contextmanager 

41def stderr_ctx(bare: bool) -> Iterator[None]: 

42 """Route library log messages to stderr for one command, or discard them all if bare.""" 

43 handler = _ClickHandler() 

44 previous_level = logger.level 

45 logger.addHandler(handler) 

46 logger.setLevel(logging.INFO) 

47 try: 

48 with ( 

49 contextlib.redirect_stderr(io.StringIO()) 

50 if bare 

51 else contextlib.nullcontext() 

52 ): 

53 yield 

54 finally: 

55 logger.removeHandler(handler) 

56 logger.setLevel(previous_level) 

57 

58 

59def resolve_host(host: str | None, no_cache: bool = False) -> str: 

60 """Return a normalised host URL, discovering via mDNS if none provided.""" 

61 if host: 

62 return net.normalise_host(host) 

63 if not no_cache: 

64 cached = net.get_cached_host() 

65 if cached: 

66 click.echo(f"Using cached host: {cached}", err=True) 

67 return cached 

68 if net.Zeroconf is None: 

69 raise click.UsageError( 

70 "No host specified and mDNS discovery unavailable (zeroconf not " 

71 "installed). Use --host or set SIGNALK_HOST." 

72 ) 

73 click.echo("No host specified — searching for SignalK via mDNS...", err=True) 

74 discovered = net.discover_host() 

75 if not discovered: 

76 raise click.UsageError( 

77 "No SignalK server found via mDNS. Use --host or set SIGNALK_HOST." 

78 ) 

79 click.echo(f"Discovered: {discovered}", err=True) 

80 if not no_cache: 

81 net.save_cached_host(discovered) 

82 return discovered