1"""Allow bash-completion for argparse with argcomplete if installed.

2

3 Needs argcomplete>=0.5.6 for python 3.2/3.3 (older versions fail

4 to find the magic string, so _ARGCOMPLETE env. var is never set, and

5 this does not need special code).

6

7 Function try_argcomplete(parser) should be called directly before

8 the call to ArgumentParser.parse_args().

9

10 The filescompleter is what you normally would use on the positional

11 arguments specification, in order to get "dirname/" after "dirn<TAB>"

12 instead of the default "dirname ":

13

14 optparser.add_argument(Config._file_or_dir, nargs='*').completer=filescompleter

15

16 Other, application specific, completers should go in the file

17 doing the add_argument calls as they need to be specified as .completer

18 attributes as well. (If argcomplete is not installed, the function the

19 attribute points to will not be used).

20

21 SPEEDUP

22=======

23

24 The generic argcomplete script for bash-completion

25(/etc/bash_completion.d/python-argcomplete.sh)

26 uses a python program to determine startup script generated by pip.

27 You can speed up completion somewhat by changing this script to include

28 # PYTHON_ARGCOMPLETE_OK

29 so the python-argcomplete-check-easy-install-script does not

30 need to be called to find the entry point of the code and see if that is

31 marked with PYTHON_ARGCOMPLETE_OK.

32

33 INSTALL/DEBUGGING

34=================

35

36 To include this support in another application that has setup.py generated

37 scripts:

38

39- Add the line:

40 # PYTHON_ARGCOMPLETE_OK

41 near the top of the main python entry point.

42

43- Include in the file calling parse_args():

44 from _argcomplete import try_argcomplete, filescompleter

45 Call try_argcomplete just before parse_args(), and optionally add

46 filescompleter to the positional arguments' add_argument().

47

48 If things do not work right away:

49

50- Switch on argcomplete debugging with (also helpful when doing custom

51 completers):

52 export _ARC_DEBUG=1

53

54- Run:

55 python-argcomplete-check-easy-install-script $(which appname)

56 echo $?

57 will echo 0 if the magic line has been found, 1 if not.

58

59- Sometimes it helps to find early on errors using:

60 _ARGCOMPLETE=1 _ARC_DEBUG=1 appname

61 which should throw a KeyError: 'COMPLINE' (which is properly set by the

62 global argcomplete script).

63"""

64 import argparse

65 import os

66 import sys

67 from glob import glob

68 from typing import Any

69 from typing import List

70 from typing import Optional

71

72

73 class FastFilesCompleter:

74 """Fast file completer class."""

75

76 def __init__(self, directories: bool = True) -> None:

77 self.directories = directories

78

79 def __call__(self, prefix: str, **kwargs: Any) -> List[str]:

80 # Only called on non option completions.

81 if os.path.sep in prefix[1:]:

82 prefix_dir = len(os.path.dirname(prefix) + os.path.sep)

83 else:

84 prefix_dir = 0

85 completion = []

86 globbed = []

87 if "*" not in prefix and "?" not in prefix:

88 # We are on unix, otherwise no bash.

89 if not prefix or prefix[-1] == os.path.sep:

90 globbed.extend(glob(prefix + ".*"))

91 prefix += "*"

92 globbed.extend(glob(prefix))

93 for x in sorted(globbed):

94 if os.path.isdir(x):

95 x += "/"

96 # Append stripping the prefix (like bash, not like compgen).

97 completion.append(x[prefix_dir:])

98 return completion

99

100

101 if os.environ.get("_ARGCOMPLETE"):

102 try:

103 import argcomplete.completers

104 except ImportError:

105 sys.exit(-1)

106 filescompleter: Optional[FastFilesCompleter] = FastFilesCompleter()

107

108 def try_argcomplete(parser: argparse.ArgumentParser) -> None:

109 argcomplete.autocomplete(parser, always_complete_options=False)

110

111 else:

112

113 def try_argcomplete(parser: argparse.ArgumentParser) -> None:

114 pass

115

116 filescompleter = None