Coverage for src/bayernwerk_client/completion.py: 100%

39 statements  

« prev     ^ index     » next       coverage.py v7.15.4, created at 2026-08-17 13:07 +0000

1"""Shell completion for the `bayernwerk` CLI, powered by `argcomplete`. 

2 

3`bayernwerk completion <shell>` prints the activation snippet for that 

4shell - generated on the fly from the actual argparse structure 

5(`build_parser()`/`register()` in `cli.py` and the per-service `cli.py` 

6modules), so it's always in sync with whatever commands actually exist. 

7No hand-written/hardcoded completion script to keep updated. 

8 

9`bayernwerk completion install` writes an `eval "$(bayernwerk completion 

10<shell>)"` line into the appropriate shell rc file (idempotent - checks 

11for the marker comment first). 

12""" 

13 

14from __future__ import annotations 

15 

16import argparse 

17import os 

18import sys 

19from pathlib import Path 

20 

21import argcomplete 

22 

23_MARKER = "# bayernwerk shell completion" 

24_EVAL_SHELL_RC_FILES = { 

25 "bash": Path.home() / ".bashrc", 

26 "zsh": Path.home() / ".zshrc", 

27} 

28 

29 

30def _detect_shell() -> str: 

31 return Path(os.environ.get("SHELL", "/bin/bash")).name 

32 

33 

34def cmd_bash(args: argparse.Namespace) -> None: 

35 print(argcomplete.shellcode(["bayernwerk"], shell="bash")) 

36 

37 

38def cmd_zsh(args: argparse.Namespace) -> None: 

39 print(argcomplete.shellcode(["bayernwerk"], shell="zsh")) 

40 

41 

42def cmd_fish(args: argparse.Namespace) -> None: 

43 print(argcomplete.shellcode(["bayernwerk"], shell="fish")) 

44 

45 

46def cmd_install(args: argparse.Namespace) -> None: 

47 shell = args.shell or _detect_shell() 

48 if shell not in _EVAL_SHELL_RC_FILES: 

49 print( 

50 f"Automatische Installation fuer '{shell}' wird nicht unterstuetzt (nur bash/zsh).\n" 

51 f"Fuehre 'bayernwerk completion {shell}' aus und binde die Ausgabe manuell " 

52 "in deine Shell-Konfiguration ein.", 

53 file=sys.stderr, 

54 ) 

55 raise SystemExit(1) 

56 

57 rc_path = _EVAL_SHELL_RC_FILES[shell] 

58 existing = rc_path.read_text() if rc_path.exists() else "" 

59 if _MARKER in existing: 

60 print(f"Completion ist bereits in {rc_path} eingetragen.") 

61 return 

62 

63 line = f'eval "$(bayernwerk completion {shell})"' 

64 with rc_path.open("a") as f: 

65 f.write(f"\n{_MARKER}\n{line}\n") 

66 print(f"Completion-Zeile zu {rc_path} hinzugefuegt. Neue Shell starten oder 'source {rc_path}' ausfuehren.") 

67 

68 

69def register(subparsers: argparse._SubParsersAction) -> None: 

70 """Add the `completion` command (and its own sub-subcommands) to `subparsers`.""" 

71 comp_parser = subparsers.add_parser("completion", help="Shell-Autovervollstaendigung generieren/installieren") 

72 comp_sub = comp_parser.add_subparsers(dest="completion_command", required=True) 

73 

74 comp_sub.add_parser( 

75 "bash", help="Bash-Completion-Skript ausgeben", description="Bash-Completion-Skript ausgeben" 

76 ).set_defaults(func=cmd_bash) 

77 comp_sub.add_parser( 

78 "zsh", help="Zsh-Completion-Skript ausgeben", description="Zsh-Completion-Skript ausgeben" 

79 ).set_defaults(func=cmd_zsh) 

80 comp_sub.add_parser( 

81 "fish", help="Fish-Completion-Skript ausgeben", description="Fish-Completion-Skript ausgeben" 

82 ).set_defaults(func=cmd_fish) 

83 

84 p = comp_sub.add_parser( 

85 "install", 

86 help="Completion automatisch in die Shell-Konfiguration eintragen (bash/zsh)", 

87 description="Completion automatisch in die Shell-Konfiguration eintragen (bash/zsh)", 

88 ) 

89 p.add_argument("--shell", choices=["bash", "zsh"], help="Shell explizit angeben (Default: aus $SHELL erkannt)") 

90 p.set_defaults(func=cmd_install)