1 import copy 2 import sys 3 import re 4 import os 5 from itertools import chain 6 from contextlib import contextmanager 7 8 from parso.python import tree 9 10 11 def is_stdlib_path(path): 12 # Python standard library paths look like this: 13 # /usr/lib/python3.9/... 14 # TODO The implementation below is probably incorrect and not complete. 15 parts = path.parts 16 if 'dist-packages' in parts or 'site-packages' in parts: 17 return False 18 19 base_path = os.path.join(sys.prefix, 'lib', 'python') 20 return bool(re.match(re.escape(base_path) + r'\d.\d', str(path))) 21 22 23 def deep_ast_copy(obj): 24 """ 25 Much, much faster than copy.deepcopy, but just for parser tree nodes. 26 """ 27 # If it's already in the cache, just return it. 28 new_obj = copy.copy(obj) 29 30 # Copy children 31 new_children = [] 32 for child in obj.children: 33 if isinstance(child, tree.Leaf): 34 new_child = copy.copy(child) 35 new_child.parent = new_obj 36 else: 37 new_child = deep_ast_copy(child) 38 new_child.parent = new_obj 39 new_children.append(new_child) 40 new_obj.children = new_children 41 42 return new_obj 43 44 45 def infer_call_of_leaf(context, leaf, cut_own_trailer=False): 46 """ 47 Creates a "call" node that consist of all ``trailer`` and ``power`` 48 objects. E.g. if you call it with ``append``:: 49 50 list([]).append(3) or None 51 52 You would get a node with the content ``list([]).append`` back. 53 54 This generates a copy of the original ast node. 55 56 If you're using the leaf, e.g. the bracket `)` it will return ``list([])``. 57 58 We use this function for two purposes. Given an expression ``bar.foo``, 59 we may want to 60 - infer the type of ``foo`` to offer completions after foo 61 - infer the type of ``bar`` to be able to jump to the definition of foo 62 The option ``cut_own_trailer`` must be set to true for the second purpose. 63 """ 64 trailer = leaf.parent 65 if trailer.type == 'fstring': 66 from jedi.inference import compiled 67 return compiled.get_string_value_set(context.inference_state) 68 69 # The leaf may not be the last or first child, because there exist three 70 # different trailers: `( x )`, `[ x ]` and `.x`. In the first two examples 71 # we should not match anything more than x. 72 if trailer.type != 'trailer' or leaf not in (trailer.children[0], trailer.children[-1]): 73 if leaf == ':': 74 # Basically happens with foo[:] when the cursor is on the colon 75 from jedi.inference.base_value import NO_VALUES 76 return NO_VALUES 77 if trailer.type == 'atom': 78 return context.infer_node(trailer) 79 return context.infer_node(leaf) 80 81 power = trailer.parent 82 index = power.children.index(trailer) 83 if cut_own_trailer: 84 cut = index 85 else: 86 cut = index + 1 87 88 if power.type == 'error_node': 89 start = index 90 while True: 91 start -= 1 92 base = power.children[start] 93 if base.type != 'trailer': 94 break 95 trailers = power.children[start + 1:cut] 96 else: 97 base = power.children[0] 98 trailers = power.children[1:cut] 99 100 if base == 'await': 101 base = trailers[0] 102 trailers = trailers[1:] 103 104 values = context.infer_node(base) 105 from jedi.inference.syntax_tree import infer_trailer 106 for trailer in trailers: 107 values = infer_trailer(context, values, trailer) 108 return values 109 110 111 def get_names_of_node(node): 112 try: 113 children = node.children 114 except AttributeError: 115 if node.type == 'name': 116 return [node] 117 else: 118 return [] 119 else: 120 return list(chain.from_iterable(get_names_of_node(c) for c in children)) 121 122 123 def is_string(value): 124 return value.is_compiled() and isinstance(value.get_safe_value(default=None), str) 125 126 127 def is_literal(value): 128 return is_number(value) or is_string(value) 129 130 131 def _get_safe_value_or_none(value, accept): 132 value = value.get_safe_value(default=None) 133 if isinstance(value, accept): 134 return value 135 136 137 def get_int_or_none(value): 138 return _get_safe_value_or_none(value, int) 139 140 141 def get_str_or_none(value): 142 return _get_safe_value_or_none(value, str) 143 144 145 def is_number(value): 146 return _get_safe_value_or_none(value, (int, float)) is not None 147 148 149 class SimpleGetItemNotFound(Exception): 150 pass 151 152 153 @contextmanager 154 def reraise_getitem_errors(*exception_classes): 155 try: 156 yield 157 except exception_classes as e: 158 raise SimpleGetItemNotFound(e) 159 160 161 def parse_dotted_names(nodes, is_import_from, until_node=None): 162 level = 0 163 names = [] 164 for node in nodes[1:]: 165 if node in ('.', '...'): 166 if not names: 167 level += len(node.value) 168 elif node.type == 'dotted_name': 169 for n in node.children[::2]: 170 names.append(n) 171 if n is until_node: 172 break 173 else: 174 continue 175 break 176 elif node.type == 'name': 177 names.append(node) 178 if node is until_node: 179 break 180 elif node == ',': 181 if not is_import_from: 182 names = [] 183 else: 184 # Here if the keyword `import` comes along it stops checking 185 # for names. 186 break 187 return level, names 188 189 190 def values_from_qualified_names(inference_state, *names): 191 return inference_state.import_module(names[:-1]).py__getattribute__(names[-1]) 192 193 194 def is_big_annoying_library(context): 195 string_names = context.get_root_context().string_names 196 if string_names is None: 197 return False 198 199 # Especially pandas and tensorflow are huge complicated Python libraries 200 # that get even slower than they already are when Jedi tries to undrstand 201 # dynamic features like decorators, ifs and other stuff. 202 return string_names[0] in ('pandas', 'numpy', 'tensorflow', 'matplotlib') 203