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

65 statements  

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

1""" 

2This module is here for string completions. This means mostly stuff where 

3strings are returned, like `foo = dict(bar=3); foo["ba` would complete to 

4`"bar"]`. 

5 

6It however does the same for numbers. The difference between string completions 

7and other completions is mostly that this module doesn't return defined 

8names in a module, but pretty much an arbitrary string. 

9""" 

10import re 

11 

12from jedi.inference.names import AbstractArbitraryName 

13from jedi.inference.helpers import infer_call_of_leaf 

14from jedi.api.classes import Completion 

15from jedi.parser_utils import cut_value_at_position 

16 

17_sentinel = object() 

18 

19 

20class StringName(AbstractArbitraryName): 

21 api_type = 'string' 

22 is_value_name = False 

23 

24 

25def complete_dict(module_context, code_lines, leaf, position, string, fuzzy): 

26 bracket_leaf = leaf 

27 if bracket_leaf != '[': 

28 bracket_leaf = leaf.get_previous_leaf() 

29 

30 cut_end_quote = '' 

31 if string: 

32 cut_end_quote = get_quote_ending(string, code_lines, position, invert_result=True) 

33 

34 if bracket_leaf == '[': 

35 if string is None and leaf is not bracket_leaf: 

36 string = cut_value_at_position(leaf, position) 

37 

38 context = module_context.create_context(bracket_leaf) 

39 

40 before_node = before_bracket_leaf = bracket_leaf.get_previous_leaf() 

41 if before_node in (')', ']', '}'): 

42 before_node = before_node.parent 

43 if before_node.type in ('atom', 'trailer', 'name'): 

44 values = infer_call_of_leaf(context, before_bracket_leaf) 

45 return list(_completions_for_dicts( 

46 module_context.inference_state, 

47 values, 

48 '' if string is None else string, 

49 cut_end_quote, 

50 fuzzy=fuzzy, 

51 )) 

52 return [] 

53 

54 

55def _completions_for_dicts(inference_state, dicts, literal_string, cut_end_quote, fuzzy): 

56 for dict_key in sorted(_get_python_keys(dicts), key=lambda x: repr(x)): 

57 dict_key_str = _create_repr_string(literal_string, dict_key) 

58 if dict_key_str.startswith(literal_string): 

59 name = StringName(inference_state, dict_key_str[:-len(cut_end_quote) or None]) 

60 yield Completion( 

61 inference_state, 

62 name, 

63 stack=None, 

64 like_name_length=len(literal_string), 

65 is_fuzzy=fuzzy 

66 ) 

67 

68 

69def _create_repr_string(literal_string, dict_key): 

70 if not isinstance(dict_key, (str, bytes)) or not literal_string: 

71 return repr(dict_key) 

72 

73 r = repr(dict_key) 

74 prefix, quote = _get_string_prefix_and_quote(literal_string) 

75 if quote is None: 

76 return r 

77 if quote == r[0]: 

78 return prefix + r 

79 return prefix + quote + r[1:-1] + quote 

80 

81 

82def _get_python_keys(dicts): 

83 for dct in dicts: 

84 if dct.array_type == 'dict': 

85 for key in dct.get_key_values(): 

86 dict_key = key.get_safe_value(default=_sentinel) 

87 if dict_key is not _sentinel: 

88 yield dict_key 

89 

90 

91def _get_string_prefix_and_quote(string): 

92 match = re.match(r'(\w*)("""|\'{3}|"|\')', string) 

93 if match is None: 

94 return None, None 

95 return match.group(1), match.group(2) 

96 

97 

98def _matches_quote_at_position(code_lines, quote, position): 

99 string = code_lines[position[0] - 1][position[1]:position[1] + len(quote)] 

100 return string == quote 

101 

102 

103def get_quote_ending(string, code_lines, position, invert_result=False): 

104 _, quote = _get_string_prefix_and_quote(string) 

105 if quote is None: 

106 return '' 

107 

108 # Add a quote only if it's not already there. 

109 if _matches_quote_at_position(code_lines, quote, position) != invert_result: 

110 return '' 

111 return quote