1 from jedi.inference.cache import inference_state_method_cache 2 from jedi.inference.base_value import ValueSet, NO_VALUES, Value, \ 3 iterator_to_value_set, LazyValueWrapper, ValueWrapper 4 from jedi.inference.compiled import builtin_from_name 5 from jedi.inference.value.klass import ClassFilter 6 from jedi.inference.value.klass import ClassMixin 7 from jedi.inference.utils import to_list 8 from jedi.inference.names import AbstractNameDefinition, ValueName 9 from jedi.inference.context import ClassContext 10 from jedi.inference.gradual.generics import TupleGenericManager 11 12 13 class _BoundTypeVarName(AbstractNameDefinition): 14 """ 15 This type var was bound to a certain type, e.g. int. 16 """ 17 def __init__(self, type_var, value_set): 18 self._type_var = type_var 19 self.parent_context = type_var.parent_context 20 self._value_set = value_set 21 22 def infer(self): 23 def iter_(): 24 for value in self._value_set: 25 # Replace any with the constraints if they are there. 26 from jedi.inference.gradual.typing import AnyClass 27 if isinstance(value, AnyClass): 28 yield from self._type_var.constraints 29 else: 30 yield value 31 return ValueSet(iter_()) 32 33 def py__name__(self): 34 return self._type_var.py__name__() 35 36 def __repr__(self): 37 return '<%s %s -> %s>' % (self.__class__.__name__, self.py__name__(), self._value_set) 38 39 40 class _TypeVarFilter: 41 """ 42 A filter for all given variables in a class. 43 44 A = TypeVar('A') 45 B = TypeVar('B') 46 class Foo(Mapping[A, B]): 47 ... 48 49 In this example we would have two type vars given: A and B 50 """ 51 def __init__(self, generics, type_vars): 52 self._generics = generics 53 self._type_vars = type_vars 54 55 def get(self, name): 56 for i, type_var in enumerate(self._type_vars): 57 if type_var.py__name__() == name: 58 try: 59 return [_BoundTypeVarName(type_var, self._generics[i])] 60 except IndexError: 61 return [type_var.name] 62 return [] 63 64 def values(self): 65 # The values are not relevant. If it's not searched exactly, the type 66 # vars are just global and should be looked up as that. 67 return [] 68 69 70 class _AnnotatedClassContext(ClassContext): 71 def get_filters(self, *args, **kwargs): 72 filters = super().get_filters( 73 *args, **kwargs 74 ) 75 yield from filters 76 77 # The type vars can only be looked up if it's a global search and 78 # not a direct lookup on the class. 79 yield self._value.get_type_var_filter() 80 81 82 class DefineGenericBaseClass(LazyValueWrapper): 83 def __init__(self, generics_manager): 84 self._generics_manager = generics_manager 85 86 def _create_instance_with_generics(self, generics_manager): 87 raise NotImplementedError 88 89 @inference_state_method_cache() 90 def get_generics(self): 91 return self._generics_manager.to_tuple() 92 93 def define_generics(self, type_var_dict): 94 from jedi.inference.gradual.type_var import TypeVar 95 changed = False 96 new_generics = [] 97 for generic_set in self.get_generics(): 98 values = NO_VALUES 99 for generic in generic_set: 100 if isinstance(generic, (DefineGenericBaseClass, TypeVar)): 101 result = generic.define_generics(type_var_dict) 102 values |= result 103 if result != ValueSet({generic}): 104 changed = True 105 else: 106 values |= ValueSet([generic]) 107 new_generics.append(values) 108 109 if not changed: 110 # There might not be any type vars that change. In that case just 111 # return itself, because it does not make sense to potentially lose 112 # cached results. 113 return ValueSet([self]) 114 115 return ValueSet([self._create_instance_with_generics( 116 TupleGenericManager(tuple(new_generics)) 117 )]) 118 119 def is_same_class(self, other): 120 if not isinstance(other, DefineGenericBaseClass): 121 return False 122 123 if self.tree_node != other.tree_node: 124 # TODO not sure if this is nice. 125 return False 126 given_params1 = self.get_generics() 127 given_params2 = other.get_generics() 128 129 if len(given_params1) != len(given_params2): 130 # If the amount of type vars doesn't match, the class doesn't 131 # match. 132 return False 133 134 # Now compare generics 135 return all( 136 any( 137 # TODO why is this ordering the correct one? 138 cls2.is_same_class(cls1) 139 # TODO I'm still not sure gather_annotation_classes is a good 140 # idea. They are essentially here to avoid comparing Tuple <=> 141 # tuple and instead compare tuple <=> tuple, but at the moment 142 # the whole `is_same_class` and `is_sub_class` matching is just 143 # not in the best shape. 144 for cls1 in class_set1.gather_annotation_classes() 145 for cls2 in class_set2.gather_annotation_classes() 146 ) for class_set1, class_set2 in zip(given_params1, given_params2) 147 ) 148 149 def get_signatures(self): 150 return [] 151 152 def __repr__(self): 153 return '<%s: %s%s>' % ( 154 self.__class__.__name__, 155 self._wrapped_value, 156 list(self.get_generics()), 157 ) 158 159 160 class GenericClass(DefineGenericBaseClass, ClassMixin): 161 """ 162 A class that is defined with generics, might be something simple like: 163 164 class Foo(Generic[T]): ... 165 my_foo_int_cls = Foo[int] 166 """ 167 def __init__(self, class_value, generics_manager): 168 super().__init__(generics_manager) 169 self._class_value = class_value 170 171 def _get_wrapped_value(self): 172 return self._class_value 173 174 def get_type_hint(self, add_class_info=True): 175 n = self.py__name__() 176 # Not sure if this is the best way to do this, but all of these types 177 # are a bit special in that they have type aliases and other ways to 178 # become lower case. It's probably better to make them upper case, 179 # because that's what you can use in annotations. 180 n = dict(list="List", dict="Dict", set="Set", tuple="Tuple").get(n, n) 181 s = n + self._generics_manager.get_type_hint() 182 if add_class_info: 183 return 'Type[%s]' % s 184 return s 185 186 def get_type_var_filter(self): 187 return _TypeVarFilter(self.get_generics(), self.list_type_vars()) 188 189 def py__call__(self, arguments): 190 instance, = super().py__call__(arguments) 191 return ValueSet([_GenericInstanceWrapper(instance)]) 192 193 def _as_context(self): 194 return _AnnotatedClassContext(self) 195 196 @to_list 197 def py__bases__(self): 198 for base in self._wrapped_value.py__bases__(): 199 yield _LazyGenericBaseClass(self, base, self._generics_manager) 200 201 def _create_instance_with_generics(self, generics_manager): 202 return GenericClass(self._class_value, generics_manager) 203 204 def is_sub_class_of(self, class_value): 205 if super().is_sub_class_of(class_value): 206 return True 207 return self._class_value.is_sub_class_of(class_value) 208 209 def with_generics(self, generics_tuple): 210 return self._class_value.with_generics(generics_tuple) 211 212 def infer_type_vars(self, value_set): 213 # Circular 214 from jedi.inference.gradual.annotation import merge_pairwise_generics, merge_type_var_dicts 215 216 annotation_name = self.py__name__() 217 type_var_dict = {} 218 if annotation_name == 'Iterable': 219 annotation_generics = self.get_generics() 220 if annotation_generics: 221 return annotation_generics[0].infer_type_vars( 222 value_set.merge_types_of_iterate(), 223 ) 224 else: 225 # Note: we need to handle the MRO _in order_, so we need to extract 226 # the elements from the set first, then handle them, even if we put 227 # them back in a set afterwards. 228 for py_class in value_set: 229 if py_class.is_instance() and not py_class.is_compiled(): 230 py_class = py_class.get_annotated_class_object() 231 else: 232 continue 233 234 if py_class.api_type != 'class': 235 # Functions & modules don't have an MRO and we're not 236 # expecting a Callable (those are handled separately within 237 # TypingClassValueWithIndex). 238 continue 239 240 for parent_class in py_class.py__mro__(): 241 class_name = parent_class.py__name__() 242 if annotation_name == class_name: 243 merge_type_var_dicts( 244 type_var_dict, 245 merge_pairwise_generics(self, parent_class), 246 ) 247 break 248 249 return type_var_dict 250 251 252 class _LazyGenericBaseClass: 253 def __init__(self, class_value, lazy_base_class, generics_manager): 254 self._class_value = class_value 255 self._lazy_base_class = lazy_base_class 256 self._generics_manager = generics_manager 257 258 @iterator_to_value_set 259 def infer(self): 260 for base in self._lazy_base_class.infer(): 261 if isinstance(base, GenericClass): 262 # Here we have to recalculate the given types. 263 yield GenericClass.create_cached( 264 base.inference_state, 265 base._wrapped_value, 266 TupleGenericManager(tuple(self._remap_type_vars(base))), 267 ) 268 else: 269 if base.is_class_mixin(): 270 # This case basically allows classes like `class Foo(List)` 271 # to be used like `Foo[int]`. The generics are not 272 # necessary and can be used later. 273 yield GenericClass.create_cached( 274 base.inference_state, 275 base, 276 self._generics_manager, 277 ) 278 else: 279 yield base 280 281 def _remap_type_vars(self, base): 282 from jedi.inference.gradual.type_var import TypeVar 283 filter = self._class_value.get_type_var_filter() 284 for type_var_set in base.get_generics(): 285 new = NO_VALUES 286 for type_var in type_var_set: 287 if isinstance(type_var, TypeVar): 288 names = filter.get(type_var.py__name__()) 289 new |= ValueSet.from_sets( 290 name.infer() for name in names 291 ) 292 else: 293 # Mostly will be type vars, except if in some cases 294 # a concrete type will already be there. In that 295 # case just add it to the value set. 296 new |= ValueSet([type_var]) 297 yield new 298 299 def __repr__(self): 300 return '<%s: %s>' % (self.__class__.__name__, self._lazy_base_class) 301 302 303 class _GenericInstanceWrapper(ValueWrapper): 304 def py__stop_iteration_returns(self): 305 for cls in self._wrapped_value.class_value.py__mro__(): 306 if cls.py__name__() == 'Generator': 307 generics = cls.get_generics() 308 try: 309 return generics[2].execute_annotation() 310 except IndexError: 311 pass 312 elif cls.py__name__() == 'Iterator': 313 return ValueSet([builtin_from_name(self.inference_state, 'None')]) 314 return self._wrapped_value.py__stop_iteration_returns() 315 316 def get_type_hint(self, add_class_info=True): 317 return self._wrapped_value.class_value.get_type_hint(add_class_info=False) 318 319 320 class _PseudoTreeNameClass(Value): 321 """ 322 In typeshed, some classes are defined like this: 323 324 Tuple: _SpecialForm = ... 325 326 Now this is not a real class, therefore we have to do some workarounds like 327 this class. Essentially this class makes it possible to goto that `Tuple` 328 name, without affecting anything else negatively. 329 """ 330 api_type = 'class' 331 332 def __init__(self, parent_context, tree_name): 333 super().__init__( 334 parent_context.inference_state, 335 parent_context 336 ) 337 self._tree_name = tree_name 338 339 @property 340 def tree_node(self): 341 return self._tree_name 342 343 def get_filters(self, *args, **kwargs): 344 # TODO this is obviously wrong. Is it though? 345 class EmptyFilter(ClassFilter): 346 def __init__(self): 347 pass 348 349 def get(self, name, **kwargs): 350 return [] 351 352 def values(self, **kwargs): 353 return [] 354 355 yield EmptyFilter() 356 357 def py__class__(self): 358 # This might not be 100% correct, but it is good enough. The details of 359 # the typing library are not really an issue for Jedi. 360 return builtin_from_name(self.inference_state, 'type') 361 362 @property 363 def name(self): 364 return ValueName(self, self._tree_name) 365 366 def get_qualified_names(self): 367 return (self._tree_name.value,) 368 369 def __repr__(self): 370 return '%s(%s)' % (self.__class__.__name__, self._tree_name.value) 371 372 373 class BaseTypingValue(LazyValueWrapper): 374 def __init__(self, parent_context, tree_name): 375 self.inference_state = parent_context.inference_state 376 self.parent_context = parent_context 377 self._tree_name = tree_name 378 379 @property 380 def name(self): 381 return ValueName(self, self._tree_name) 382 383 def _get_wrapped_value(self): 384 return _PseudoTreeNameClass(self.parent_context, self._tree_name) 385 386 def get_signatures(self): 387 return self._wrapped_value.get_signatures() 388 389 def __repr__(self): 390 return '%s(%s)' % (self.__class__.__name__, self._tree_name.value) 391 392 393 class BaseTypingClassWithGenerics(DefineGenericBaseClass): 394 def __init__(self, parent_context, tree_name, generics_manager): 395 super().__init__(generics_manager) 396 self.inference_state = parent_context.inference_state 397 self.parent_context = parent_context 398 self._tree_name = tree_name 399 400 def _get_wrapped_value(self): 401 return _PseudoTreeNameClass(self.parent_context, self._tree_name) 402 403 def __repr__(self): 404 return '%s(%s%s)' % (self.__class__.__name__, self._tree_name.value, 405 self._generics_manager) 406 407 408 class BaseTypingInstance(LazyValueWrapper): 409 def __init__(self, parent_context, class_value, tree_name, generics_manager): 410 self.inference_state = class_value.inference_state 411 self.parent_context = parent_context 412 self._class_value = class_value 413 self._tree_name = tree_name 414 self._generics_manager = generics_manager 415 416 def py__class__(self): 417 return self._class_value 418 419 def get_annotated_class_object(self): 420 return self._class_value 421 422 def get_qualified_names(self): 423 return (self.py__name__(),) 424 425 @property 426 def name(self): 427 return ValueName(self, self._tree_name) 428 429 def _get_wrapped_value(self): 430 object_, = builtin_from_name(self.inference_state, 'object').execute_annotation() 431 return object_ 432 433 def __repr__(self): 434 return '<%s: %s>' % (self.__class__.__name__, self._generics_manager) 435