1 """ 2 Like described in the :mod:`parso.python.tree` module, 3 there's a need for an ast like module to represent the states of parsed 4 modules. 5 6 But now there are also structures in Python that need a little bit more than 7 that. An ``Instance`` for example is only a ``Class`` before it is 8 instantiated. This class represents these cases. 9 10 So, why is there also a ``Class`` class here? Well, there are decorators and 11 they change classes in Python 3. 12 13 Representation modules also define "magic methods". Those methods look like 14 ``py__foo__`` and are typically mappable to the Python equivalents ``__call__`` 15 and others. Here's a list: 16 17 ====================================== ======================================== 18 **Method** **Description** 19 -------------------------------------- ---------------------------------------- 20 py__call__(arguments: Array) On callable objects, returns types. 21 py__bool__() Returns True/False/None; None means that 22 there's no certainty. 23 py__bases__() Returns a list of base classes. 24 py__iter__() Returns a generator of a set of types. 25 py__class__() Returns the class of an instance. 26 py__simple_getitem__(index: int/str) Returns a a set of types of the index. 27 Can raise an IndexError/KeyError. 28 py__getitem__(indexes: ValueSet) Returns a a set of types of the index. 29 py__file__() Only on modules. Returns None if does 30 not exist. 31 py__package__() -> List[str] Only on modules. For the import system. 32 py__path__() Only on modules. For the import system. 33 py__get__(call_object) Only on instances. Simulates 34 descriptors. 35 py__doc__() Returns the docstring for a value. 36 ====================================== ======================================== 37 38 """ 39 from jedi import debug 40 from jedi.parser_utils import get_cached_parent_scope, expr_is_dotted, \ 41 function_is_property 42 from jedi.inference.cache import inference_state_method_cache, CachedMetaClass, \ 43 inference_state_method_generator_cache 44 from jedi.inference import compiled 45 from jedi.inference.lazy_value import LazyKnownValues, LazyTreeValue 46 from jedi.inference.filters import ParserTreeFilter 47 from jedi.inference.names import TreeNameDefinition, ValueName 48 from jedi.inference.arguments import unpack_arglist, ValuesArguments 49 from jedi.inference.base_value import ValueSet, iterator_to_value_set, \ 50 NO_VALUES 51 from jedi.inference.context import ClassContext 52 from jedi.inference.value.function import FunctionAndClassBase 53 from jedi.inference.gradual.generics import LazyGenericManager, TupleGenericManager 54 from jedi.plugins import plugin_manager 55 56 57 class ClassName(TreeNameDefinition): 58 def __init__(self, class_value, tree_name, name_context, apply_decorators): 59 super().__init__(name_context, tree_name) 60 self._apply_decorators = apply_decorators 61 self._class_value = class_value 62 63 @iterator_to_value_set 64 def infer(self): 65 # We're using a different value to infer, so we cannot call super(). 66 from jedi.inference.syntax_tree import tree_name_to_values 67 inferred = tree_name_to_values( 68 self.parent_context.inference_state, self.parent_context, self.tree_name) 69 70 for result_value in inferred: 71 if self._apply_decorators: 72 yield from result_value.py__get__(instance=None, class_value=self._class_value) 73 else: 74 yield result_value 75 76 @property 77 def api_type(self): 78 type_ = super().api_type 79 if type_ == 'function': 80 definition = self.tree_name.get_definition() 81 if definition is None: 82 return type_ 83 if function_is_property(definition): 84 # This essentially checks if there is an @property before 85 # the function. @property could be something different, but 86 # any programmer that redefines property as something that 87 # is not really a property anymore, should be shot. (i.e. 88 # this is a heuristic). 89 return 'property' 90 return type_ 91 92 93 class ClassFilter(ParserTreeFilter): 94 def __init__(self, class_value, node_context=None, until_position=None, 95 origin_scope=None, is_instance=False): 96 super().__init__( 97 class_value.as_context(), node_context, 98 until_position=until_position, 99 origin_scope=origin_scope, 100 ) 101 self._class_value = class_value 102 self._is_instance = is_instance 103 104 def _convert_names(self, names): 105 return [ 106 ClassName( 107 class_value=self._class_value, 108 tree_name=name, 109 name_context=self._node_context, 110 apply_decorators=not self._is_instance, 111 ) for name in names 112 ] 113 114 def _equals_origin_scope(self): 115 node = self._origin_scope 116 while node is not None: 117 if node == self._parser_scope or node == self.parent_context: 118 return True 119 node = get_cached_parent_scope(self._parso_cache_node, node) 120 return False 121 122 def _access_possible(self, name): 123 # Filter for name mangling of private variables like __foo 124 return not name.value.startswith('__') or name.value.endswith('__') \ 125 or self._equals_origin_scope() 126 127 def _filter(self, names): 128 names = super()._filter(names) 129 return [name for name in names if self._access_possible(name)] 130 131 132 class ClassMixin: 133 def is_class(self): 134 return True 135 136 def is_class_mixin(self): 137 return True 138 139 def py__call__(self, arguments): 140 from jedi.inference.value import TreeInstance 141 142 from jedi.inference.gradual.typing import TypedDict 143 if self.is_typeddict(): 144 return ValueSet([TypedDict(self)]) 145 return ValueSet([TreeInstance(self.inference_state, self.parent_context, self, arguments)]) 146 147 def py__class__(self): 148 return compiled.builtin_from_name(self.inference_state, 'type') 149 150 @property 151 def name(self): 152 return ValueName(self, self.tree_node.name) 153 154 def py__name__(self): 155 return self.name.string_name 156 157 @inference_state_method_generator_cache() 158 def py__mro__(self): 159 mro = [self] 160 yield self 161 # TODO Do a proper mro resolution. Currently we are just listing 162 # classes. However, it's a complicated algorithm. 163 for lazy_cls in self.py__bases__(): 164 # TODO there's multiple different mro paths possible if this yields 165 # multiple possibilities. Could be changed to be more correct. 166 for cls in lazy_cls.infer(): 167 # TODO detect for TypeError: duplicate base class str, 168 # e.g. `class X(str, str): pass` 169 try: 170 mro_method = cls.py__mro__ 171 except AttributeError: 172 # TODO add a TypeError like: 173 """ 174 >>> class Y(lambda: test): pass 175 Traceback (most recent call last): 176 File "<stdin>", line 1, in <module> 177 TypeError: function() argument 1 must be code, not str 178 >>> class Y(1): pass 179 Traceback (most recent call last): 180 File "<stdin>", line 1, in <module> 181 TypeError: int() takes at most 2 arguments (3 given) 182 """ 183 debug.warning('Super class of %s is not a class: %s', self, cls) 184 else: 185 for cls_new in mro_method(): 186 if cls_new not in mro: 187 mro.append(cls_new) 188 yield cls_new 189 190 def get_filters(self, origin_scope=None, is_instance=False, 191 include_metaclasses=True, include_type_when_class=True): 192 if include_metaclasses: 193 metaclasses = self.get_metaclasses() 194 if metaclasses: 195 yield from self.get_metaclass_filters(metaclasses, is_instance) 196 197 for cls in self.py__mro__(): 198 if cls.is_compiled(): 199 yield from cls.get_filters(is_instance=is_instance) 200 else: 201 yield ClassFilter( 202 self, node_context=cls.as_context(), 203 origin_scope=origin_scope, 204 is_instance=is_instance 205 ) 206 if not is_instance and include_type_when_class: 207 from jedi.inference.compiled import builtin_from_name 208 type_ = builtin_from_name(self.inference_state, 'type') 209 assert isinstance(type_, ClassValue) 210 if type_ != self: 211 # We are not using execute_with_values here, because the 212 # plugin function for type would get executed instead of an 213 # instance creation. 214 args = ValuesArguments([]) 215 for instance in type_.py__call__(args): 216 instance_filters = instance.get_filters() 217 # Filter out self filters 218 next(instance_filters, None) 219 next(instance_filters, None) 220 x = next(instance_filters, None) 221 assert x is not None 222 yield x 223 224 def get_signatures(self): 225 # Since calling staticmethod without a function is illegal, the Jedi 226 # plugin doesn't return anything. Therefore call directly and get what 227 # we want: An instance of staticmethod. 228 metaclasses = self.get_metaclasses() 229 if metaclasses: 230 sigs = self.get_metaclass_signatures(metaclasses) 231 if sigs: 232 return sigs 233 args = ValuesArguments([]) 234 init_funcs = self.py__call__(args).py__getattribute__('__init__') 235 return [sig.bind(self) for sig in init_funcs.get_signatures()] 236 237 def _as_context(self): 238 return ClassContext(self) 239 240 def get_type_hint(self, add_class_info=True): 241 if add_class_info: 242 return 'Type[%s]' % self.py__name__() 243 return self.py__name__() 244 245 @inference_state_method_cache(default=False) 246 def is_typeddict(self): 247 # TODO Do a proper mro resolution. Currently we are just listing 248 # classes. However, it's a complicated algorithm. 249 from jedi.inference.gradual.typing import TypedDictClass 250 for lazy_cls in self.py__bases__(): 251 if not isinstance(lazy_cls, LazyTreeValue): 252 return False 253 tree_node = lazy_cls.data 254 # Only resolve simple classes, stuff like Iterable[str] are more 255 # intensive to resolve and if generics are involved, we know it's 256 # not a TypedDict. 257 if not expr_is_dotted(tree_node): 258 return False 259 260 for cls in lazy_cls.infer(): 261 if isinstance(cls, TypedDictClass): 262 return True 263 try: 264 method = cls.is_typeddict 265 except AttributeError: 266 # We're only dealing with simple classes, so just returning 267 # here should be fine. This only happens with e.g. compiled 268 # classes. 269 return False 270 else: 271 if method(): 272 return True 273 return False 274 275 def py__getitem__(self, index_value_set, contextualized_node): 276 from jedi.inference.gradual.base import GenericClass 277 if not index_value_set: 278 debug.warning('Class indexes inferred to nothing. Returning class instead') 279 return ValueSet([self]) 280 return ValueSet( 281 GenericClass( 282 self, 283 LazyGenericManager( 284 context_of_index=contextualized_node.context, 285 index_value=index_value, 286 ) 287 ) 288 for index_value in index_value_set 289 ) 290 291 def with_generics(self, generics_tuple): 292 from jedi.inference.gradual.base import GenericClass 293 return GenericClass( 294 self, 295 TupleGenericManager(generics_tuple) 296 ) 297 298 def define_generics(self, type_var_dict): 299 from jedi.inference.gradual.base import GenericClass 300 301 def remap_type_vars(): 302 """ 303 The TypeVars in the resulting classes have sometimes different names 304 and we need to check for that, e.g. a signature can be: 305 306 def iter(iterable: Iterable[_T]) -> Iterator[_T]: ... 307 308 However, the iterator is defined as Iterator[_T_co], which means it has 309 a different type var name. 310 """ 311 for type_var in self.list_type_vars(): 312 yield type_var_dict.get(type_var.py__name__(), NO_VALUES) 313 314 if type_var_dict: 315 return ValueSet([GenericClass( 316 self, 317 TupleGenericManager(tuple(remap_type_vars())) 318 )]) 319 return ValueSet({self}) 320 321 322 class ClassValue(ClassMixin, FunctionAndClassBase, metaclass=CachedMetaClass): 323 api_type = 'class' 324 325 @inference_state_method_cache() 326 def list_type_vars(self): 327 found = [] 328 arglist = self.tree_node.get_super_arglist() 329 if arglist is None: 330 return [] 331 332 for stars, node in unpack_arglist(arglist): 333 if stars: 334 continue # These are not relevant for this search. 335 336 from jedi.inference.gradual.annotation import find_unknown_type_vars 337 for type_var in find_unknown_type_vars(self.parent_context, node): 338 if type_var not in found: 339 # The order matters and it's therefore a list. 340 found.append(type_var) 341 return found 342 343 def _get_bases_arguments(self): 344 arglist = self.tree_node.get_super_arglist() 345 if arglist: 346 from jedi.inference import arguments 347 return arguments.TreeArguments(self.inference_state, self.parent_context, arglist) 348 return None 349 350 @inference_state_method_cache(default=()) 351 def py__bases__(self): 352 args = self._get_bases_arguments() 353 if args is not None: 354 lst = [value for key, value in args.unpack() if key is None] 355 if lst: 356 return lst 357 358 if self.py__name__() == 'object' \ 359 and self.parent_context.is_builtins_module(): 360 return [] 361 return [LazyKnownValues( 362 self.inference_state.builtins_module.py__getattribute__('object') 363 )] 364 365 @plugin_manager.decorate() 366 def get_metaclass_filters(self, metaclasses, is_instance): 367 debug.warning('Unprocessed metaclass %s', metaclasses) 368 return [] 369 370 @inference_state_method_cache(default=NO_VALUES) 371 def get_metaclasses(self): 372 args = self._get_bases_arguments() 373 if args is not None: 374 m = [value for key, value in args.unpack() if key == 'metaclass'] 375 metaclasses = ValueSet.from_sets(lazy_value.infer() for lazy_value in m) 376 metaclasses = ValueSet(m for m in metaclasses if m.is_class()) 377 if metaclasses: 378 return metaclasses 379 380 for lazy_base in self.py__bases__(): 381 for value in lazy_base.infer(): 382 if value.is_class(): 383 values = value.get_metaclasses() 384 if values: 385 return values 386 return NO_VALUES 387 388 @plugin_manager.decorate() 389 def get_metaclass_signatures(self, metaclasses): 390 return [] 391