Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/jedi/api/file_name.py: 10%

114 statements  

« prev     ^ index     » next       coverage.py v7.4.4, created at 2024-04-20 06:09 +0000

1import os 

2 

3from jedi.api import classes 

4from jedi.api.strings import StringName, get_quote_ending 

5from jedi.api.helpers import match 

6from jedi.inference.helpers import get_str_or_none 

7 

8 

9class PathName(StringName): 

10 api_type = 'path' 

11 

12 

13def complete_file_name(inference_state, module_context, start_leaf, quote, string, 

14 like_name, signatures_callback, code_lines, position, fuzzy): 

15 # First we want to find out what can actually be changed as a name. 

16 like_name_length = len(os.path.basename(string)) 

17 

18 addition = _get_string_additions(module_context, start_leaf) 

19 if string.startswith('~'): 

20 string = os.path.expanduser(string) 

21 if addition is None: 

22 return 

23 string = addition + string 

24 

25 # Here we use basename again, because if strings are added like 

26 # `'foo' + 'bar`, it should complete to `foobar/`. 

27 must_start_with = os.path.basename(string) 

28 string = os.path.dirname(string) 

29 

30 sigs = signatures_callback(*position) 

31 is_in_os_path_join = sigs and all(s.full_name == 'os.path.join' for s in sigs) 

32 if is_in_os_path_join: 

33 to_be_added = _add_os_path_join(module_context, start_leaf, sigs[0].bracket_start) 

34 if to_be_added is None: 

35 is_in_os_path_join = False 

36 else: 

37 string = to_be_added + string 

38 base_path = os.path.join(inference_state.project.path, string) 

39 try: 

40 listed = sorted(os.scandir(base_path), key=lambda e: e.name) 

41 # OSError: [Errno 36] File name too long: '...' 

42 except (FileNotFoundError, OSError): 

43 return 

44 quote_ending = get_quote_ending(quote, code_lines, position) 

45 for entry in listed: 

46 name = entry.name 

47 if match(name, must_start_with, fuzzy=fuzzy): 

48 if is_in_os_path_join or not entry.is_dir(): 

49 name += quote_ending 

50 else: 

51 name += os.path.sep 

52 

53 yield classes.Completion( 

54 inference_state, 

55 PathName(inference_state, name[len(must_start_with) - like_name_length:]), 

56 stack=None, 

57 like_name_length=like_name_length, 

58 is_fuzzy=fuzzy, 

59 ) 

60 

61 

62def _get_string_additions(module_context, start_leaf): 

63 def iterate_nodes(): 

64 node = addition.parent 

65 was_addition = True 

66 for child_node in reversed(node.children[:node.children.index(addition)]): 

67 if was_addition: 

68 was_addition = False 

69 yield child_node 

70 continue 

71 

72 if child_node != '+': 

73 break 

74 was_addition = True 

75 

76 addition = start_leaf.get_previous_leaf() 

77 if addition != '+': 

78 return '' 

79 context = module_context.create_context(start_leaf) 

80 return _add_strings(context, reversed(list(iterate_nodes()))) 

81 

82 

83def _add_strings(context, nodes, add_slash=False): 

84 string = '' 

85 first = True 

86 for child_node in nodes: 

87 values = context.infer_node(child_node) 

88 if len(values) != 1: 

89 return None 

90 c, = values 

91 s = get_str_or_none(c) 

92 if s is None: 

93 return None 

94 if not first and add_slash: 

95 string += os.path.sep 

96 string += s 

97 first = False 

98 return string 

99 

100 

101def _add_os_path_join(module_context, start_leaf, bracket_start): 

102 def check(maybe_bracket, nodes): 

103 if maybe_bracket.start_pos != bracket_start: 

104 return None 

105 

106 if not nodes: 

107 return '' 

108 context = module_context.create_context(nodes[0]) 

109 return _add_strings(context, nodes, add_slash=True) or '' 

110 

111 if start_leaf.type == 'error_leaf': 

112 # Unfinished string literal, like `join('` 

113 value_node = start_leaf.parent 

114 index = value_node.children.index(start_leaf) 

115 if index > 0: 

116 error_node = value_node.children[index - 1] 

117 if error_node.type == 'error_node' and len(error_node.children) >= 2: 

118 index = -2 

119 if error_node.children[-1].type == 'arglist': 

120 arglist_nodes = error_node.children[-1].children 

121 index -= 1 

122 else: 

123 arglist_nodes = [] 

124 

125 return check(error_node.children[index + 1], arglist_nodes[::2]) 

126 return None 

127 

128 # Maybe an arglist or some weird error case. Therefore checked below. 

129 searched_node_child = start_leaf 

130 while searched_node_child.parent is not None \ 

131 and searched_node_child.parent.type not in ('arglist', 'trailer', 'error_node'): 

132 searched_node_child = searched_node_child.parent 

133 

134 if searched_node_child.get_first_leaf() is not start_leaf: 

135 return None 

136 searched_node = searched_node_child.parent 

137 if searched_node is None: 

138 return None 

139 

140 index = searched_node.children.index(searched_node_child) 

141 arglist_nodes = searched_node.children[:index] 

142 if searched_node.type == 'arglist': 

143 trailer = searched_node.parent 

144 if trailer.type == 'error_node': 

145 trailer_index = trailer.children.index(searched_node) 

146 assert trailer_index >= 2 

147 assert trailer.children[trailer_index - 1] == '(' 

148 return check(trailer.children[trailer_index - 1], arglist_nodes[::2]) 

149 elif trailer.type == 'trailer': 

150 return check(trailer.children[0], arglist_nodes[::2]) 

151 elif searched_node.type == 'trailer': 

152 return check(searched_node.children[0], []) 

153 elif searched_node.type == 'error_node': 

154 # Stuff like `join(""` 

155 return check(arglist_nodes[-1], [])