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

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

69 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 

7 

8 

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

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

11 kwargs["universal_newlines"] = True 

12 try: 

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

14 except subprocess.CalledProcessError: 

15 return [] 

16 

17 

18class BaseCompleter: 

19 """ 

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

21 """ 

22 

23 def __call__( 

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

25 ) -> None: 

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

27 

28 

29class ChoicesCompleter(BaseCompleter): 

30 def __init__(self, choices): 

31 self.choices = choices 

32 

33 def _convert(self, choice): 

34 if not isinstance(choice, str): 

35 choice = str(choice) 

36 return choice 

37 

38 def __call__(self, **kwargs): 

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

40 

41 

42EnvironCompleter = ChoicesCompleter(os.environ) 

43 

44 

45class FilesCompleter(BaseCompleter): 

46 """ 

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

48 """ 

49 

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

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

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

53 allowednames = [allowednames] 

54 

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

56 self.directories = directories 

57 

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

59 completion = [] 

60 if self.allowednames: 

61 if self.directories: 

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

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

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

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

66 files = _call( 

67 ["bash", "-c", "bind; compgen -A directory -- '{p}'".format(p=prefix)], stderr=subprocess.DEVNULL 

68 ) 

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

70 for x in self.allowednames: 

71 completion += _call( 

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

73 stderr=subprocess.DEVNULL, 

74 ) 

75 else: 

76 completion += _call( 

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

78 ) 

79 anticomp = _call( 

80 ["bash", "-c", "bind; compgen -A directory -- '{p}'".format(p=prefix)], stderr=subprocess.DEVNULL 

81 ) 

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

83 

84 if self.directories: 

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

86 return completion 

87 

88 

89class _FilteredFilesCompleter(BaseCompleter): 

90 def __init__(self, predicate): 

91 """ 

92 Create the completer 

93 

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

95 accepts it or rejects it. 

96 """ 

97 assert predicate, "Expected a callable predicate" 

98 self.predicate = predicate 

99 

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

101 """ 

102 Provide completions on prefix 

103 """ 

104 target_dir = os.path.dirname(prefix) 

105 try: 

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

107 except Exception: 

108 return # empty iterator 

109 incomplete_part = os.path.basename(prefix) 

110 # Iterate on target_dir entries and filter on given predicate 

111 for name in names: 

112 if not name.startswith(incomplete_part): 

113 continue 

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

115 if not self.predicate(candidate): 

116 continue 

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

118 

119 

120class DirectoriesCompleter(_FilteredFilesCompleter): 

121 def __init__(self): 

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

123 

124 

125class SuppressCompleter(BaseCompleter): 

126 """ 

127 A completer used to suppress the completion of specific arguments 

128 """ 

129 

130 def __init__(self): 

131 pass 

132 

133 def suppress(self): 

134 """ 

135 Decide if the completion should be suppressed 

136 """ 

137 return True