1 from jedi import debug 2 from jedi.inference.base_value import ValueSet, \ 3 NO_VALUES 4 from jedi.inference.utils import to_list 5 from jedi.inference.gradual.stub_value import StubModuleValue 6 from jedi.inference.gradual.typeshed import try_to_load_stub_cached 7 from jedi.inference.value.decorator import Decoratee 8 9 10 def _stub_to_python_value_set(stub_value, ignore_compiled=False): 11 stub_module_context = stub_value.get_root_context() 12 if not stub_module_context.is_stub(): 13 return ValueSet([stub_value]) 14 15 decorates = None 16 if isinstance(stub_value, Decoratee): 17 decorates = stub_value._original_value 18 19 was_instance = stub_value.is_instance() 20 if was_instance: 21 arguments = getattr(stub_value, '_arguments', None) 22 stub_value = stub_value.py__class__() 23 24 qualified_names = stub_value.get_qualified_names() 25 if qualified_names is None: 26 return NO_VALUES 27 28 was_bound_method = stub_value.is_bound_method() 29 if was_bound_method: 30 # Infer the object first. We can infer the method later. 31 method_name = qualified_names[-1] 32 qualified_names = qualified_names[:-1] 33 was_instance = True 34 arguments = None 35 36 values = _infer_from_stub(stub_module_context, qualified_names, ignore_compiled) 37 if was_instance: 38 values = ValueSet.from_sets( 39 c.execute_with_values() if arguments is None else c.execute(arguments) 40 for c in values 41 if c.is_class() 42 ) 43 if was_bound_method: 44 # Now that the instance has been properly created, we can simply get 45 # the method. 46 values = values.py__getattribute__(method_name) 47 if decorates is not None: 48 values = ValueSet(Decoratee(v, decorates) for v in values) 49 return values 50 51 52 def _infer_from_stub(stub_module_context, qualified_names, ignore_compiled): 53 from jedi.inference.compiled.mixed import MixedObject 54 stub_module = stub_module_context.get_value() 55 assert isinstance(stub_module, (StubModuleValue, MixedObject)), stub_module_context 56 non_stubs = stub_module.non_stub_value_set 57 if ignore_compiled: 58 non_stubs = non_stubs.filter(lambda c: not c.is_compiled()) 59 for name in qualified_names: 60 non_stubs = non_stubs.py__getattribute__(name) 61 return non_stubs 62 63 64 @to_list 65 def _try_stub_to_python_names(names, prefer_stub_to_compiled=False): 66 for name in names: 67 module_context = name.get_root_context() 68 if not module_context.is_stub(): 69 yield name 70 continue 71 72 if name.api_type == 'module': 73 values = convert_values(name.infer(), ignore_compiled=prefer_stub_to_compiled) 74 if values: 75 for v in values: 76 yield v.name 77 continue 78 else: 79 v = name.get_defining_qualified_value() 80 if v is not None: 81 converted = _stub_to_python_value_set(v, ignore_compiled=prefer_stub_to_compiled) 82 if converted: 83 converted_names = converted.goto(name.get_public_name()) 84 if converted_names: 85 for n in converted_names: 86 if n.get_root_context().is_stub(): 87 # If it's a stub again, it means we're going in 88 # a circle. Probably some imports make it a 89 # stub again. 90 yield name 91 else: 92 yield n 93 continue 94 yield name 95 96 97 def _load_stub_module(module): 98 if module.is_stub(): 99 return module 100 return try_to_load_stub_cached( 101 module.inference_state, 102 import_names=module.string_names, 103 python_value_set=ValueSet([module]), 104 parent_module_value=None, 105 sys_path=module.inference_state.get_sys_path(), 106 ) 107 108 109 @to_list 110 def _python_to_stub_names(names, fallback_to_python=False): 111 for name in names: 112 module_context = name.get_root_context() 113 if module_context.is_stub(): 114 yield name 115 continue 116 117 if name.api_type == 'module': 118 found_name = False 119 for n in name.goto(): 120 if n.api_type == 'module': 121 values = convert_values(n.infer(), only_stubs=True) 122 for v in values: 123 yield v.name 124 found_name = True 125 else: 126 for x in _python_to_stub_names([n], fallback_to_python=fallback_to_python): 127 yield x 128 found_name = True 129 if found_name: 130 continue 131 else: 132 v = name.get_defining_qualified_value() 133 if v is not None: 134 converted = to_stub(v) 135 if converted: 136 converted_names = converted.goto(name.get_public_name()) 137 if converted_names: 138 yield from converted_names 139 continue 140 if fallback_to_python: 141 # This is the part where if we haven't found anything, just return 142 # the stub name. 143 yield name 144 145 146 def convert_names(names, only_stubs=False, prefer_stubs=False, prefer_stub_to_compiled=True): 147 if only_stubs and prefer_stubs: 148 raise ValueError("You cannot use both of only_stubs and prefer_stubs.") 149 150 with debug.increase_indent_cm('convert names'): 151 if only_stubs or prefer_stubs: 152 return _python_to_stub_names(names, fallback_to_python=prefer_stubs) 153 else: 154 return _try_stub_to_python_names( 155 names, prefer_stub_to_compiled=prefer_stub_to_compiled) 156 157 158 def convert_values(values, only_stubs=False, prefer_stubs=False, ignore_compiled=True): 159 assert not (only_stubs and prefer_stubs) 160 with debug.increase_indent_cm('convert values'): 161 if only_stubs or prefer_stubs: 162 return ValueSet.from_sets( 163 to_stub(value) 164 or (ValueSet({value}) if prefer_stubs else NO_VALUES) 165 for value in values 166 ) 167 else: 168 return ValueSet.from_sets( 169 _stub_to_python_value_set(stub_value, ignore_compiled=ignore_compiled) 170 or ValueSet({stub_value}) 171 for stub_value in values 172 ) 173 174 175 def to_stub(value): 176 if value.is_stub(): 177 return ValueSet([value]) 178 179 was_instance = value.is_instance() 180 if was_instance: 181 value = value.py__class__() 182 183 qualified_names = value.get_qualified_names() 184 stub_module = _load_stub_module(value.get_root_context().get_value()) 185 if stub_module is None or qualified_names is None: 186 return NO_VALUES 187 188 was_bound_method = value.is_bound_method() 189 if was_bound_method: 190 # Infer the object first. We can infer the method later. 191 method_name = qualified_names[-1] 192 qualified_names = qualified_names[:-1] 193 was_instance = True 194 195 stub_values = ValueSet([stub_module]) 196 for name in qualified_names: 197 stub_values = stub_values.py__getattribute__(name) 198 199 if was_instance: 200 stub_values = ValueSet.from_sets( 201 c.execute_with_values() 202 for c in stub_values 203 if c.is_class() 204 ) 205 if was_bound_method: 206 # Now that the instance has been properly created, we can simply get 207 # the method. 208 stub_values = stub_values.py__getattribute__(method_name) 209 return stub_values 210