Coverage for ase / cli / completion.py: 77.55%

49 statements  

« prev     ^ index     » next       coverage.py v7.13.5, created at 2026-03-30 08:22 +0000

1# fmt: off 

2 

3"""TAB-completion sub-command and update helper funtion. 

4 

5Run this when ever options are changed:: 

6 

7 python3 -m ase.cli.completion 

8""" 

9 

10import sys 

11from pathlib import Path 

12 

13# Path of the complete.py script: 

14path = Path(__file__).with_name('complete.py') 

15 

16 

17class CLICommand: 

18 """Add tab-completion for Bash. 

19 

20 Will show the command that needs to be added to your '~/.bashrc file. 

21 """ 

22 cmd = f'complete -o default -C "{sys.executable} {path}" ase' 

23 

24 @staticmethod 

25 def add_arguments(parser): 

26 pass 

27 

28 @staticmethod 

29 def run(args): 

30 cmd = CLICommand.cmd 

31 print(cmd) 

32 

33 

34def update(path: Path, 

35 subcommands: list[tuple[str, str]], 

36 test: bool = False) -> None: 

37 """Update commands dict in complete.py. 

38 

39 Use test=True to test that no changes are needed. 

40 

41 Refactor with care! This function is also used by GPAW. 

42 """ 

43 

44 import textwrap 

45 from importlib import import_module 

46 

47 dct: dict[str, list[str]] = {} 

48 

49 class Subparser: 

50 def __init__(self, command): 

51 self.command = command 

52 dct[command] = [] 

53 

54 def add_argument(self, *args, **kwargs): 

55 dct[command].extend(arg for arg in args 

56 if arg.startswith('-')) 

57 

58 def add_argument_group(self, name): 

59 return self 

60 

61 def add_mutually_exclusive_group(self, required=False): 

62 return self 

63 

64 for command, module_name in subcommands: 

65 module = import_module(module_name) 

66 module.CLICommand.add_arguments(Subparser(command)) 

67 

68 txt = 'commands = {' 

69 for command, opts in sorted(dct.items()): 

70 txt += "\n '" + command + "':\n [" 

71 if opts: 

72 txt += '\n'.join(textwrap.wrap("'" + "', '".join(opts) + "'],", 

73 width=65, 

74 break_on_hyphens=False, 

75 subsequent_indent=' ')) 

76 else: 

77 txt += '],' 

78 txt = txt[:-1] + '}\n' 

79 

80 with path.open() as fd: 

81 lines = fd.readlines() 

82 

83 a = lines.index('# Beginning of computer generated data:\n') 

84 b = lines.index('# End of computer generated data\n') 

85 

86 if test: 

87 if ''.join(lines[a + 1:b]) != txt: 

88 raise ValueError( 

89 'Please update ase/cli/complete.py using ' 

90 '"python3 -m ase.cli.completion".') 

91 else: 

92 lines[a + 1:b] = [txt] 

93 new = path.with_name('complete.py.new') 

94 with new.open('w') as fd: 

95 print(''.join(lines), end='', file=fd) 

96 new.rename(path) 

97 path.chmod(0o775) 

98 

99 

100if __name__ == '__main__': 

101 from ase.cli.main import commands 

102 update(path, commands)