]> crepu.dev Git - config.git/blame - djavu-asus/elpy/rpc-venv/lib/python3.11/site-packages/jedi/inference/value/klass.py
Archivo de configuraciĆ³n de la terminal
[config.git] / djavu-asus / elpy / rpc-venv / lib / python3.11 / site-packages / jedi / inference / value / klass.py
CommitLineData
53e6db90
DC
1"""
2Like described in the :mod:`parso.python.tree` module,
3there's a need for an ast like module to represent the states of parsed
4modules.
5
6But now there are also structures in Python that need a little bit more than
7that. An ``Instance`` for example is only a ``Class`` before it is
8instantiated. This class represents these cases.
9
10So, why is there also a ``Class`` class here? Well, there are decorators and
11they change classes in Python 3.
12
13Representation modules also define "magic methods". Those methods look like
14``py__foo__`` and are typically mappable to the Python equivalents ``__call__``
15and others. Here's a list:
16
17====================================== ========================================
18**Method** **Description**
19-------------------------------------- ----------------------------------------
20py__call__(arguments: Array) On callable objects, returns types.
21py__bool__() Returns True/False/None; None means that
22 there's no certainty.
23py__bases__() Returns a list of base classes.
24py__iter__() Returns a generator of a set of types.
25py__class__() Returns the class of an instance.
26py__simple_getitem__(index: int/str) Returns a a set of types of the index.
27 Can raise an IndexError/KeyError.
28py__getitem__(indexes: ValueSet) Returns a a set of types of the index.
29py__file__() Only on modules. Returns None if does
30 not exist.
31py__package__() -> List[str] Only on modules. For the import system.
32py__path__() Only on modules. For the import system.
33py__get__(call_object) Only on instances. Simulates
34 descriptors.
35py__doc__() Returns the docstring for a value.
36====================================== ========================================
37
38"""
39from jedi import debug
40from jedi.parser_utils import get_cached_parent_scope, expr_is_dotted, \
41 function_is_property
42from jedi.inference.cache import inference_state_method_cache, CachedMetaClass, \
43 inference_state_method_generator_cache
44from jedi.inference import compiled
45from jedi.inference.lazy_value import LazyKnownValues, LazyTreeValue
46from jedi.inference.filters import ParserTreeFilter
47from jedi.inference.names import TreeNameDefinition, ValueName
48from jedi.inference.arguments import unpack_arglist, ValuesArguments
49from jedi.inference.base_value import ValueSet, iterator_to_value_set, \
50 NO_VALUES
51from jedi.inference.context import ClassContext
52from jedi.inference.value.function import FunctionAndClassBase
53from jedi.inference.gradual.generics import LazyGenericManager, TupleGenericManager
54from jedi.plugins import plugin_manager
55
56
57class 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
93class 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
132class 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
322class 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 []