]>
Commit | Line | Data |
---|---|---|
53e6db90 DC |
1 | """ |
2 | Imitate the parser representation. | |
3 | """ | |
4 | import re | |
5 | from functools import partial | |
6 | from inspect import Parameter | |
7 | from pathlib import Path | |
8 | from typing import Optional | |
9 | ||
10 | from jedi import debug | |
11 | from jedi.inference.utils import to_list | |
12 | from jedi.cache import memoize_method | |
13 | from jedi.inference.filters import AbstractFilter | |
14 | from jedi.inference.names import AbstractNameDefinition, ValueNameMixin, \ | |
15 | ParamNameInterface | |
16 | from jedi.inference.base_value import Value, ValueSet, NO_VALUES | |
17 | from jedi.inference.lazy_value import LazyKnownValue | |
18 | from jedi.inference.compiled.access import _sentinel | |
19 | from jedi.inference.cache import inference_state_function_cache | |
20 | from jedi.inference.helpers import reraise_getitem_errors | |
21 | from jedi.inference.signature import BuiltinSignature | |
22 | from jedi.inference.context import CompiledContext, CompiledModuleContext | |
23 | ||
24 | ||
25 | class CheckAttribute: | |
26 | """Raises :exc:`AttributeError` if the attribute X is not available.""" | |
27 | def __init__(self, check_name=None): | |
28 | # Remove the py in front of e.g. py__call__. | |
29 | self.check_name = check_name | |
30 | ||
31 | def __call__(self, func): | |
32 | self.func = func | |
33 | if self.check_name is None: | |
34 | self.check_name = func.__name__[2:] | |
35 | return self | |
36 | ||
37 | def __get__(self, instance, owner): | |
38 | if instance is None: | |
39 | return self | |
40 | ||
41 | # This might raise an AttributeError. That's wanted. | |
42 | instance.access_handle.getattr_paths(self.check_name) | |
43 | return partial(self.func, instance) | |
44 | ||
45 | ||
46 | class CompiledValue(Value): | |
47 | def __init__(self, inference_state, access_handle, parent_context=None): | |
48 | super().__init__(inference_state, parent_context) | |
49 | self.access_handle = access_handle | |
50 | ||
51 | def py__call__(self, arguments): | |
52 | return_annotation = self.access_handle.get_return_annotation() | |
53 | if return_annotation is not None: | |
54 | return create_from_access_path( | |
55 | self.inference_state, | |
56 | return_annotation | |
57 | ).execute_annotation() | |
58 | ||
59 | try: | |
60 | self.access_handle.getattr_paths('__call__') | |
61 | except AttributeError: | |
62 | return super().py__call__(arguments) | |
63 | else: | |
64 | if self.access_handle.is_class(): | |
65 | from jedi.inference.value import CompiledInstance | |
66 | return ValueSet([ | |
67 | CompiledInstance(self.inference_state, self.parent_context, self, arguments) | |
68 | ]) | |
69 | else: | |
70 | return ValueSet(self._execute_function(arguments)) | |
71 | ||
72 | @CheckAttribute() | |
73 | def py__class__(self): | |
74 | return create_from_access_path(self.inference_state, self.access_handle.py__class__()) | |
75 | ||
76 | @CheckAttribute() | |
77 | def py__mro__(self): | |
78 | return (self,) + tuple( | |
79 | create_from_access_path(self.inference_state, access) | |
80 | for access in self.access_handle.py__mro__accesses() | |
81 | ) | |
82 | ||
83 | @CheckAttribute() | |
84 | def py__bases__(self): | |
85 | return tuple( | |
86 | create_from_access_path(self.inference_state, access) | |
87 | for access in self.access_handle.py__bases__() | |
88 | ) | |
89 | ||
90 | def get_qualified_names(self): | |
91 | return self.access_handle.get_qualified_names() | |
92 | ||
93 | def py__bool__(self): | |
94 | return self.access_handle.py__bool__() | |
95 | ||
96 | def is_class(self): | |
97 | return self.access_handle.is_class() | |
98 | ||
99 | def is_function(self): | |
100 | return self.access_handle.is_function() | |
101 | ||
102 | def is_module(self): | |
103 | return self.access_handle.is_module() | |
104 | ||
105 | def is_compiled(self): | |
106 | return True | |
107 | ||
108 | def is_stub(self): | |
109 | return False | |
110 | ||
111 | def is_instance(self): | |
112 | return self.access_handle.is_instance() | |
113 | ||
114 | def py__doc__(self): | |
115 | return self.access_handle.py__doc__() | |
116 | ||
117 | @to_list | |
118 | def get_param_names(self): | |
119 | try: | |
120 | signature_params = self.access_handle.get_signature_params() | |
121 | except ValueError: # Has no signature | |
122 | params_str, ret = self._parse_function_doc() | |
123 | if not params_str: | |
124 | tokens = [] | |
125 | else: | |
126 | tokens = params_str.split(',') | |
127 | if self.access_handle.ismethoddescriptor(): | |
128 | tokens.insert(0, 'self') | |
129 | for p in tokens: | |
130 | name, _, default = p.strip().partition('=') | |
131 | yield UnresolvableParamName(self, name, default) | |
132 | else: | |
133 | for signature_param in signature_params: | |
134 | yield SignatureParamName(self, signature_param) | |
135 | ||
136 | def get_signatures(self): | |
137 | _, return_string = self._parse_function_doc() | |
138 | return [BuiltinSignature(self, return_string)] | |
139 | ||
140 | def __repr__(self): | |
141 | return '<%s: %s>' % (self.__class__.__name__, self.access_handle.get_repr()) | |
142 | ||
143 | @memoize_method | |
144 | def _parse_function_doc(self): | |
145 | doc = self.py__doc__() | |
146 | if doc is None: | |
147 | return '', '' | |
148 | ||
149 | return _parse_function_doc(doc) | |
150 | ||
151 | @property | |
152 | def api_type(self): | |
153 | return self.access_handle.get_api_type() | |
154 | ||
155 | def get_filters(self, is_instance=False, origin_scope=None): | |
156 | yield self._ensure_one_filter(is_instance) | |
157 | ||
158 | @memoize_method | |
159 | def _ensure_one_filter(self, is_instance): | |
160 | return CompiledValueFilter(self.inference_state, self, is_instance) | |
161 | ||
162 | def py__simple_getitem__(self, index): | |
163 | with reraise_getitem_errors(IndexError, KeyError, TypeError): | |
164 | try: | |
165 | access = self.access_handle.py__simple_getitem__( | |
166 | index, | |
167 | safe=not self.inference_state.allow_unsafe_executions | |
168 | ) | |
169 | except AttributeError: | |
170 | return super().py__simple_getitem__(index) | |
171 | if access is None: | |
172 | return super().py__simple_getitem__(index) | |
173 | ||
174 | return ValueSet([create_from_access_path(self.inference_state, access)]) | |
175 | ||
176 | def py__getitem__(self, index_value_set, contextualized_node): | |
177 | all_access_paths = self.access_handle.py__getitem__all_values() | |
178 | if all_access_paths is None: | |
179 | # This means basically that no __getitem__ has been defined on this | |
180 | # object. | |
181 | return super().py__getitem__(index_value_set, contextualized_node) | |
182 | return ValueSet( | |
183 | create_from_access_path(self.inference_state, access) | |
184 | for access in all_access_paths | |
185 | ) | |
186 | ||
187 | def py__iter__(self, contextualized_node=None): | |
188 | if not self.access_handle.has_iter(): | |
189 | yield from super().py__iter__(contextualized_node) | |
190 | ||
191 | access_path_list = self.access_handle.py__iter__list() | |
192 | if access_path_list is None: | |
193 | # There is no __iter__ method on this object. | |
194 | return | |
195 | ||
196 | for access in access_path_list: | |
197 | yield LazyKnownValue(create_from_access_path(self.inference_state, access)) | |
198 | ||
199 | def py__name__(self): | |
200 | return self.access_handle.py__name__() | |
201 | ||
202 | @property | |
203 | def name(self): | |
204 | name = self.py__name__() | |
205 | if name is None: | |
206 | name = self.access_handle.get_repr() | |
207 | return CompiledValueName(self, name) | |
208 | ||
209 | def _execute_function(self, params): | |
210 | from jedi.inference import docstrings | |
211 | from jedi.inference.compiled import builtin_from_name | |
212 | if self.api_type != 'function': | |
213 | return | |
214 | ||
215 | for name in self._parse_function_doc()[1].split(): | |
216 | try: | |
217 | # TODO wtf is this? this is exactly the same as the thing | |
218 | # below. It uses getattr as well. | |
219 | self.inference_state.builtins_module.access_handle.getattr_paths(name) | |
220 | except AttributeError: | |
221 | continue | |
222 | else: | |
223 | bltn_obj = builtin_from_name(self.inference_state, name) | |
224 | yield from self.inference_state.execute(bltn_obj, params) | |
225 | yield from docstrings.infer_return_types(self) | |
226 | ||
227 | def get_safe_value(self, default=_sentinel): | |
228 | try: | |
229 | return self.access_handle.get_safe_value() | |
230 | except ValueError: | |
231 | if default == _sentinel: | |
232 | raise | |
233 | return default | |
234 | ||
235 | def execute_operation(self, other, operator): | |
236 | try: | |
237 | return ValueSet([create_from_access_path( | |
238 | self.inference_state, | |
239 | self.access_handle.execute_operation(other.access_handle, operator) | |
240 | )]) | |
241 | except TypeError: | |
242 | return NO_VALUES | |
243 | ||
244 | def execute_annotation(self): | |
245 | if self.access_handle.get_repr() == 'None': | |
246 | # None as an annotation doesn't need to be executed. | |
247 | return ValueSet([self]) | |
248 | ||
249 | name, args = self.access_handle.get_annotation_name_and_args() | |
250 | arguments = [ | |
251 | ValueSet([create_from_access_path(self.inference_state, path)]) | |
252 | for path in args | |
253 | ] | |
254 | if name == 'Union': | |
255 | return ValueSet.from_sets(arg.execute_annotation() for arg in arguments) | |
256 | elif name: | |
257 | # While with_generics only exists on very specific objects, we | |
258 | # should probably be fine, because we control all the typing | |
259 | # objects. | |
260 | return ValueSet([ | |
261 | v.with_generics(arguments) | |
262 | for v in self.inference_state.typing_module.py__getattribute__(name) | |
263 | ]).execute_annotation() | |
264 | return super().execute_annotation() | |
265 | ||
266 | def negate(self): | |
267 | return create_from_access_path(self.inference_state, self.access_handle.negate()) | |
268 | ||
269 | def get_metaclasses(self): | |
270 | return NO_VALUES | |
271 | ||
272 | def _as_context(self): | |
273 | return CompiledContext(self) | |
274 | ||
275 | @property | |
276 | def array_type(self): | |
277 | return self.access_handle.get_array_type() | |
278 | ||
279 | def get_key_values(self): | |
280 | return [ | |
281 | create_from_access_path(self.inference_state, k) | |
282 | for k in self.access_handle.get_key_paths() | |
283 | ] | |
284 | ||
285 | def get_type_hint(self, add_class_info=True): | |
286 | if self.access_handle.get_repr() in ('None', "<class 'NoneType'>"): | |
287 | return 'None' | |
288 | return None | |
289 | ||
290 | ||
291 | class CompiledModule(CompiledValue): | |
292 | file_io = None # For modules | |
293 | ||
294 | def _as_context(self): | |
295 | return CompiledModuleContext(self) | |
296 | ||
297 | def py__path__(self): | |
298 | return self.access_handle.py__path__() | |
299 | ||
300 | def is_package(self): | |
301 | return self.py__path__() is not None | |
302 | ||
303 | @property | |
304 | def string_names(self): | |
305 | # For modules | |
306 | name = self.py__name__() | |
307 | if name is None: | |
308 | return () | |
309 | return tuple(name.split('.')) | |
310 | ||
311 | def py__file__(self) -> Optional[Path]: | |
312 | return self.access_handle.py__file__() # type: ignore[no-any-return] | |
313 | ||
314 | ||
315 | class CompiledName(AbstractNameDefinition): | |
316 | def __init__(self, inference_state, parent_value, name, is_descriptor): | |
317 | self._inference_state = inference_state | |
318 | self.parent_context = parent_value.as_context() | |
319 | self._parent_value = parent_value | |
320 | self.string_name = name | |
321 | self.is_descriptor = is_descriptor | |
322 | ||
323 | def py__doc__(self): | |
324 | return self.infer_compiled_value().py__doc__() | |
325 | ||
326 | def _get_qualified_names(self): | |
327 | parent_qualified_names = self.parent_context.get_qualified_names() | |
328 | if parent_qualified_names is None: | |
329 | return None | |
330 | return parent_qualified_names + (self.string_name,) | |
331 | ||
332 | def get_defining_qualified_value(self): | |
333 | context = self.parent_context | |
334 | if context.is_module() or context.is_class(): | |
335 | return self.parent_context.get_value() # Might be None | |
336 | ||
337 | return None | |
338 | ||
339 | def __repr__(self): | |
340 | try: | |
341 | name = self.parent_context.name # __name__ is not defined all the time | |
342 | except AttributeError: | |
343 | name = None | |
344 | return '<%s: (%s).%s>' % (self.__class__.__name__, name, self.string_name) | |
345 | ||
346 | @property | |
347 | def api_type(self): | |
348 | if self.is_descriptor: | |
349 | # In case of properties we want to avoid executions as much as | |
350 | # possible. Since the api_type can be wrong for other reasons | |
351 | # anyway, we just return instance here. | |
352 | return "instance" | |
353 | return self.infer_compiled_value().api_type | |
354 | ||
355 | def infer(self): | |
356 | return ValueSet([self.infer_compiled_value()]) | |
357 | ||
358 | @memoize_method | |
359 | def infer_compiled_value(self): | |
360 | return create_from_name(self._inference_state, self._parent_value, self.string_name) | |
361 | ||
362 | ||
363 | class SignatureParamName(ParamNameInterface, AbstractNameDefinition): | |
364 | def __init__(self, compiled_value, signature_param): | |
365 | self.parent_context = compiled_value.parent_context | |
366 | self._signature_param = signature_param | |
367 | ||
368 | @property | |
369 | def string_name(self): | |
370 | return self._signature_param.name | |
371 | ||
372 | def to_string(self): | |
373 | s = self._kind_string() + self.string_name | |
374 | if self._signature_param.has_annotation: | |
375 | s += ': ' + self._signature_param.annotation_string | |
376 | if self._signature_param.has_default: | |
377 | s += '=' + self._signature_param.default_string | |
378 | return s | |
379 | ||
380 | def get_kind(self): | |
381 | return getattr(Parameter, self._signature_param.kind_name) | |
382 | ||
383 | def infer(self): | |
384 | p = self._signature_param | |
385 | inference_state = self.parent_context.inference_state | |
386 | values = NO_VALUES | |
387 | if p.has_default: | |
388 | values = ValueSet([create_from_access_path(inference_state, p.default)]) | |
389 | if p.has_annotation: | |
390 | annotation = create_from_access_path(inference_state, p.annotation) | |
391 | values |= annotation.execute_with_values() | |
392 | return values | |
393 | ||
394 | ||
395 | class UnresolvableParamName(ParamNameInterface, AbstractNameDefinition): | |
396 | def __init__(self, compiled_value, name, default): | |
397 | self.parent_context = compiled_value.parent_context | |
398 | self.string_name = name | |
399 | self._default = default | |
400 | ||
401 | def get_kind(self): | |
402 | return Parameter.POSITIONAL_ONLY | |
403 | ||
404 | def to_string(self): | |
405 | string = self.string_name | |
406 | if self._default: | |
407 | string += '=' + self._default | |
408 | return string | |
409 | ||
410 | def infer(self): | |
411 | return NO_VALUES | |
412 | ||
413 | ||
414 | class CompiledValueName(ValueNameMixin, AbstractNameDefinition): | |
415 | def __init__(self, value, name): | |
416 | self.string_name = name | |
417 | self._value = value | |
418 | self.parent_context = value.parent_context | |
419 | ||
420 | ||
421 | class EmptyCompiledName(AbstractNameDefinition): | |
422 | """ | |
423 | Accessing some names will raise an exception. To avoid not having any | |
424 | completions, just give Jedi the option to return this object. It infers to | |
425 | nothing. | |
426 | """ | |
427 | def __init__(self, inference_state, name): | |
428 | self.parent_context = inference_state.builtins_module | |
429 | self.string_name = name | |
430 | ||
431 | def infer(self): | |
432 | return NO_VALUES | |
433 | ||
434 | ||
435 | class CompiledValueFilter(AbstractFilter): | |
436 | def __init__(self, inference_state, compiled_value, is_instance=False): | |
437 | self._inference_state = inference_state | |
438 | self.compiled_value = compiled_value | |
439 | self.is_instance = is_instance | |
440 | ||
441 | def get(self, name): | |
442 | access_handle = self.compiled_value.access_handle | |
443 | safe = not self._inference_state.allow_unsafe_executions | |
444 | return self._get( | |
445 | name, | |
446 | lambda name: access_handle.is_allowed_getattr(name, safe=safe), | |
447 | lambda name: name in access_handle.dir(), | |
448 | check_has_attribute=True | |
449 | ) | |
450 | ||
451 | def _get(self, name, allowed_getattr_callback, in_dir_callback, check_has_attribute=False): | |
452 | """ | |
453 | To remove quite a few access calls we introduced the callback here. | |
454 | """ | |
455 | has_attribute, is_descriptor, property_return_annotation = allowed_getattr_callback( | |
456 | name, | |
457 | ) | |
458 | if property_return_annotation is not None: | |
459 | values = create_from_access_path( | |
460 | self._inference_state, | |
461 | property_return_annotation | |
462 | ).execute_annotation() | |
463 | if values: | |
464 | return [CompiledValueName(v, name) for v in values] | |
465 | ||
466 | if check_has_attribute and not has_attribute: | |
467 | return [] | |
468 | ||
469 | if (is_descriptor or not has_attribute) \ | |
470 | and not self._inference_state.allow_unsafe_executions: | |
471 | return [self._get_cached_name(name, is_empty=True)] | |
472 | ||
473 | if self.is_instance and not in_dir_callback(name): | |
474 | return [] | |
475 | return [self._get_cached_name(name, is_descriptor=is_descriptor)] | |
476 | ||
477 | @memoize_method | |
478 | def _get_cached_name(self, name, is_empty=False, *, is_descriptor=False): | |
479 | if is_empty: | |
480 | return EmptyCompiledName(self._inference_state, name) | |
481 | else: | |
482 | return self._create_name(name, is_descriptor=is_descriptor) | |
483 | ||
484 | def values(self): | |
485 | from jedi.inference.compiled import builtin_from_name | |
486 | names = [] | |
487 | needs_type_completions, dir_infos = self.compiled_value.access_handle.get_dir_infos() | |
488 | # We could use `safe=False` here as well, especially as a parameter to | |
489 | # get_dir_infos. But this would lead to a lot of property executions | |
490 | # that are probably not wanted. The drawback for this is that we | |
491 | # have a different name for `get` and `values`. For `get` we always | |
492 | # execute. | |
493 | for name in dir_infos: | |
494 | names += self._get( | |
495 | name, | |
496 | lambda name: dir_infos[name], | |
497 | lambda name: name in dir_infos, | |
498 | ) | |
499 | ||
500 | # ``dir`` doesn't include the type names. | |
501 | if not self.is_instance and needs_type_completions: | |
502 | for filter in builtin_from_name(self._inference_state, 'type').get_filters(): | |
503 | names += filter.values() | |
504 | return names | |
505 | ||
506 | def _create_name(self, name, is_descriptor): | |
507 | return CompiledName( | |
508 | self._inference_state, | |
509 | self.compiled_value, | |
510 | name, | |
511 | is_descriptor, | |
512 | ) | |
513 | ||
514 | def __repr__(self): | |
515 | return "<%s: %s>" % (self.__class__.__name__, self.compiled_value) | |
516 | ||
517 | ||
518 | docstr_defaults = { | |
519 | 'floating point number': 'float', | |
520 | 'character': 'str', | |
521 | 'integer': 'int', | |
522 | 'dictionary': 'dict', | |
523 | 'string': 'str', | |
524 | } | |
525 | ||
526 | ||
527 | def _parse_function_doc(doc): | |
528 | """ | |
529 | Takes a function and returns the params and return value as a tuple. | |
530 | This is nothing more than a docstring parser. | |
531 | ||
532 | TODO docstrings like utime(path, (atime, mtime)) and a(b [, b]) -> None | |
533 | TODO docstrings like 'tuple of integers' | |
534 | """ | |
535 | # parse round parentheses: def func(a, (b,c)) | |
536 | try: | |
537 | count = 0 | |
538 | start = doc.index('(') | |
539 | for i, s in enumerate(doc[start:]): | |
540 | if s == '(': | |
541 | count += 1 | |
542 | elif s == ')': | |
543 | count -= 1 | |
544 | if count == 0: | |
545 | end = start + i | |
546 | break | |
547 | param_str = doc[start + 1:end] | |
548 | except (ValueError, UnboundLocalError): | |
549 | # ValueError for doc.index | |
550 | # UnboundLocalError for undefined end in last line | |
551 | debug.dbg('no brackets found - no param') | |
552 | end = 0 | |
553 | param_str = '' | |
554 | else: | |
555 | # remove square brackets, that show an optional param ( = None) | |
556 | def change_options(m): | |
557 | args = m.group(1).split(',') | |
558 | for i, a in enumerate(args): | |
559 | if a and '=' not in a: | |
560 | args[i] += '=None' | |
561 | return ','.join(args) | |
562 | ||
563 | while True: | |
564 | param_str, changes = re.subn(r' ?\[([^\[\]]+)\]', | |
565 | change_options, param_str) | |
566 | if changes == 0: | |
567 | break | |
568 | param_str = param_str.replace('-', '_') # see: isinstance.__doc__ | |
569 | ||
570 | # parse return value | |
571 | r = re.search('-[>-]* ', doc[end:end + 7]) | |
572 | if r is None: | |
573 | ret = '' | |
574 | else: | |
575 | index = end + r.end() | |
576 | # get result type, which can contain newlines | |
577 | pattern = re.compile(r'(,\n|[^\n-])+') | |
578 | ret_str = pattern.match(doc, index).group(0).strip() | |
579 | # New object -> object() | |
580 | ret_str = re.sub(r'[nN]ew (.*)', r'\1()', ret_str) | |
581 | ||
582 | ret = docstr_defaults.get(ret_str, ret_str) | |
583 | ||
584 | return param_str, ret | |
585 | ||
586 | ||
587 | def create_from_name(inference_state, compiled_value, name): | |
588 | access_paths = compiled_value.access_handle.getattr_paths(name, default=None) | |
589 | ||
590 | value = None | |
591 | for access_path in access_paths: | |
592 | value = create_cached_compiled_value( | |
593 | inference_state, | |
594 | access_path, | |
595 | parent_context=None if value is None else value.as_context(), | |
596 | ) | |
597 | return value | |
598 | ||
599 | ||
600 | def _normalize_create_args(func): | |
601 | """The cache doesn't care about keyword vs. normal args.""" | |
602 | def wrapper(inference_state, obj, parent_context=None): | |
603 | return func(inference_state, obj, parent_context) | |
604 | return wrapper | |
605 | ||
606 | ||
607 | def create_from_access_path(inference_state, access_path): | |
608 | value = None | |
609 | for name, access in access_path.accesses: | |
610 | value = create_cached_compiled_value( | |
611 | inference_state, | |
612 | access, | |
613 | parent_context=None if value is None else value.as_context() | |
614 | ) | |
615 | return value | |
616 | ||
617 | ||
618 | @_normalize_create_args | |
619 | @inference_state_function_cache() | |
620 | def create_cached_compiled_value(inference_state, access_handle, parent_context): | |
621 | assert not isinstance(parent_context, CompiledValue) | |
622 | if parent_context is None: | |
623 | cls = CompiledModule | |
624 | else: | |
625 | cls = CompiledValue | |
626 | return cls(inference_state, access_handle, parent_context) |