Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/argcomplete/completers.py: 39%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

70 statements  

1# Copyright 2012-2023, Andrey Kislyuk and argcomplete contributors. 

2# Licensed under the Apache License. See https://github.com/kislyuk/argcomplete for more info. 

3 

4import argparse 

5import os 

6import subprocess 

7from shlex import quote 

8 

9 

10def _call(*args, **kwargs): 

11 # TODO: replace "universal_newlines" with "text" once 3.6 support is dropped 

12 kwargs["universal_newlines"] = True 

13 try: 

14 return subprocess.check_output(*args, **kwargs).splitlines() 

15 except subprocess.CalledProcessError: 

16 return [] 

17 

18 

19class BaseCompleter: 

20 """ 

21 This is the base class that all argcomplete completers should subclass. 

22 """ 

23 

24 def __call__( 

25 self, *, prefix: str, action: argparse.Action, parser: argparse.ArgumentParser, parsed_args: argparse.Namespace 

26 ) -> None: 

27 raise NotImplementedError("This method should be implemented by a subclass.") 

28 

29 

30class ChoicesCompleter(BaseCompleter): 

31 def __init__(self, choices): 

32 self.choices = choices 

33 

34 def _convert(self, choice): 

35 if not isinstance(choice, str): 

36 choice = str(choice) 

37 return choice 

38 

39 def __call__(self, **kwargs): 

40 return (self._convert(c) for c in self.choices) 

41 

42 

43EnvironCompleter = ChoicesCompleter(os.environ) 

44 

45 

46class FilesCompleter(BaseCompleter): 

47 """ 

48 File completer class, optionally takes a list of allowed extensions 

49 """ 

50 

51 def __init__(self, allowednames=(), directories=True): 

52 # Fix if someone passes in a string instead of a list 

53 if isinstance(allowednames, (str, bytes)): 

54 allowednames = [allowednames] 

55 

56 self.allowednames = [x.lstrip("*").lstrip(".") for x in allowednames] 

57 self.directories = directories 

58 

59 def __call__(self, prefix, **kwargs): 

60 completion = [] 

61 if self.allowednames: 

62 if self.directories: 

63 # Using 'bind' in this and the following commands is a workaround to a bug in bash 

64 # that was fixed in bash 5.3 but affects older versions. Environment variables are not treated 

65 # correctly in older versions and calling bind makes them available. For details, see 

66 # https://savannah.gnu.org/support/index.php?111125 

67 files = _call( 

68 ["bash", "-c", "bind; compgen -A directory -- {p}".format(p=quote(prefix))], 

69 stderr=subprocess.DEVNULL, 

70 ) 

71 completion += [f + "/" for f in files] 

72 for x in self.allowednames: 

73 completion += _call( 

74 ["bash", "-c", "bind; compgen -A file -X '!*.{0}' -- {p}".format(x, p=quote(prefix))], 

75 stderr=subprocess.DEVNULL, 

76 ) 

77 else: 

78 completion += _call( 

79 ["bash", "-c", "bind; compgen -A file -- {p}".format(p=quote(prefix))], stderr=subprocess.DEVNULL 

80 ) 

81 anticomp = _call( 

82 ["bash", "-c", "bind; compgen -A directory -- {p}".format(p=quote(prefix))], 

83 stderr=subprocess.DEVNULL, 

84 ) 

85 completion = list(set(completion) - set(anticomp)) 

86 

87 if self.directories: 

88 completion += [f + "/" for f in anticomp] 

89 return completion 

90 

91 

92class _FilteredFilesCompleter(BaseCompleter): 

93 def __init__(self, predicate): 

94 """ 

95 Create the completer 

96 

97 A predicate accepts as its only argument a candidate path and either 

98 accepts it or rejects it. 

99 """ 

100 assert predicate, "Expected a callable predicate" 

101 self.predicate = predicate 

102 

103 def __call__(self, prefix, **kwargs): 

104 """ 

105 Provide completions on prefix 

106 """ 

107 target_dir = os.path.dirname(prefix) 

108 try: 

109 names = os.listdir(target_dir or ".") 

110 except Exception: 

111 return # empty iterator 

112 incomplete_part = os.path.basename(prefix) 

113 # Iterate on target_dir entries and filter on given predicate 

114 for name in names: 

115 if not name.startswith(incomplete_part): 

116 continue 

117 candidate = os.path.join(target_dir, name) 

118 if not self.predicate(candidate): 

119 continue 

120 yield candidate + "/" if os.path.isdir(candidate) else candidate 

121 

122 

123class DirectoriesCompleter(_FilteredFilesCompleter): 

124 def __init__(self): 

125 _FilteredFilesCompleter.__init__(self, predicate=os.path.isdir) 

126 

127 

128class SuppressCompleter(BaseCompleter): 

129 """ 

130 A completer used to suppress the completion of specific arguments 

131 """ 

132 

133 def __init__(self): 

134 pass 

135 

136 def suppress(self): 

137 """ 

138 Decide if the completion should be suppressed 

139 """ 

140 return True