Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/jedi/api/strings.py: 25%
63 statements
« prev ^ index » next coverage.py v7.2.2, created at 2023-03-26 06:07 +0000
« prev ^ index » next coverage.py v7.2.2, created at 2023-03-26 06:07 +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"]`.
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
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
17_sentinel = object()
20class StringName(AbstractArbitraryName):
21 api_type = 'string'
22 is_value_name = False
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()
30 cut_end_quote = ''
31 if string:
32 cut_end_quote = get_quote_ending(string, code_lines, position, invert_result=True)
34 if bracket_leaf == '[':
35 if string is None and leaf is not bracket_leaf:
36 string = cut_value_at_position(leaf, position)
38 context = module_context.create_context(bracket_leaf)
39 before_bracket_leaf = bracket_leaf.get_previous_leaf()
40 if before_bracket_leaf.type in ('atom', 'trailer', 'name'):
41 values = infer_call_of_leaf(context, before_bracket_leaf)
42 return list(_completions_for_dicts(
43 module_context.inference_state,
44 values,
45 '' if string is None else string,
46 cut_end_quote,
47 fuzzy=fuzzy,
48 ))
49 return []
52def _completions_for_dicts(inference_state, dicts, literal_string, cut_end_quote, fuzzy):
53 for dict_key in sorted(_get_python_keys(dicts), key=lambda x: repr(x)):
54 dict_key_str = _create_repr_string(literal_string, dict_key)
55 if dict_key_str.startswith(literal_string):
56 name = StringName(inference_state, dict_key_str[:-len(cut_end_quote) or None])
57 yield Completion(
58 inference_state,
59 name,
60 stack=None,
61 like_name_length=len(literal_string),
62 is_fuzzy=fuzzy
63 )
66def _create_repr_string(literal_string, dict_key):
67 if not isinstance(dict_key, (str, bytes)) or not literal_string:
68 return repr(dict_key)
70 r = repr(dict_key)
71 prefix, quote = _get_string_prefix_and_quote(literal_string)
72 if quote is None:
73 return r
74 if quote == r[0]:
75 return prefix + r
76 return prefix + quote + r[1:-1] + quote
79def _get_python_keys(dicts):
80 for dct in dicts:
81 if dct.array_type == 'dict':
82 for key in dct.get_key_values():
83 dict_key = key.get_safe_value(default=_sentinel)
84 if dict_key is not _sentinel:
85 yield dict_key
88def _get_string_prefix_and_quote(string):
89 match = re.match(r'(\w*)("""|\'{3}|"|\')', string)
90 if match is None:
91 return None, None
92 return match.group(1), match.group(2)
95def _matches_quote_at_position(code_lines, quote, position):
96 string = code_lines[position[0] - 1][position[1]:position[1] + len(quote)]
97 return string == quote
100def get_quote_ending(string, code_lines, position, invert_result=False):
101 _, quote = _get_string_prefix_and_quote(string)
102 if quote is None:
103 return ''
105 # Add a quote only if it's not already there.
106 if _matches_quote_at_position(code_lines, quote, position) != invert_result:
107 return ''
108 return quote