1"""
2Imitate the parser representation.
3"""
4import re
5from functools import partial
6from inspect import Parameter
7from pathlib import Path
8from typing import Optional
9
10from jedi import debug
11from jedi.inference.utils import to_list
12from jedi.cache import memoize_method
13from jedi.inference.filters import AbstractFilter
14from jedi.inference.names import AbstractNameDefinition, ValueNameMixin, \
15 ParamNameInterface
16from jedi.inference.base_value import Value, ValueSet, NO_VALUES
17from jedi.inference.lazy_value import LazyKnownValue
18from jedi.inference.compiled.access import _sentinel
19from jedi.inference.cache import inference_state_function_cache
20from jedi.inference.helpers import reraise_getitem_errors
21from jedi.inference.signature import BuiltinSignature
22from jedi.inference.context import CompiledContext, CompiledModuleContext
23
24
25class 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
46class 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(arguments.context)
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, context):
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(
256 arg.execute_annotation(context)
257 for arg in arguments)
258 elif name:
259 # While with_generics only exists on very specific objects, we
260 # should probably be fine, because we control all the typing
261 # objects.
262 return ValueSet([
263 v.with_generics(arguments)
264 for v in self.inference_state.typing_module.py__getattribute__(name)
265 ]).execute_annotation(context)
266 return super().execute_annotation(context)
267
268 def negate(self):
269 return create_from_access_path(self.inference_state, self.access_handle.negate())
270
271 def get_metaclasses(self):
272 return NO_VALUES
273
274 def _as_context(self):
275 return CompiledContext(self)
276
277 @property
278 def array_type(self):
279 return self.access_handle.get_array_type()
280
281 def get_key_values(self):
282 return [
283 create_from_access_path(self.inference_state, k)
284 for k in self.access_handle.get_key_paths()
285 ]
286
287 def get_type_hint(self, add_class_info=True):
288 if self.access_handle.get_repr() in ('None', "<class 'NoneType'>"):
289 return 'None'
290 return None
291
292
293class CompiledModule(CompiledValue):
294 file_io = None # For modules
295
296 def _as_context(self):
297 return CompiledModuleContext(self)
298
299 def py__path__(self):
300 return self.access_handle.py__path__()
301
302 def is_package(self):
303 return self.py__path__() is not None
304
305 @property
306 def string_names(self):
307 # For modules
308 name = self.py__name__()
309 if name is None:
310 return ()
311 return tuple(name.split('.'))
312
313 def py__file__(self) -> Optional[Path]:
314 return self.access_handle.py__file__() # type: ignore[no-any-return]
315
316
317class CompiledName(AbstractNameDefinition):
318 def __init__(self, inference_state, parent_value, name, is_descriptor):
319 self._inference_state = inference_state
320 self.parent_context = parent_value.as_context()
321 self._parent_value = parent_value
322 self.string_name = name
323 self.is_descriptor = is_descriptor
324
325 def py__doc__(self):
326 return self.infer_compiled_value().py__doc__()
327
328 def _get_qualified_names(self):
329 parent_qualified_names = self.parent_context.get_qualified_names()
330 if parent_qualified_names is None:
331 return None
332 return parent_qualified_names + (self.string_name,)
333
334 def get_defining_qualified_value(self):
335 context = self.parent_context
336 if context.is_module() or context.is_class():
337 return self.parent_context.get_value() # Might be None
338
339 return None
340
341 def __repr__(self):
342 try:
343 name = self.parent_context.name # __name__ is not defined all the time
344 except AttributeError:
345 name = None
346 return '<%s: (%s).%s>' % (self.__class__.__name__, name, self.string_name)
347
348 @property
349 def api_type(self):
350 if self.is_descriptor:
351 # In case of properties we want to avoid executions as much as
352 # possible. Since the api_type can be wrong for other reasons
353 # anyway, we just return instance here.
354 return "instance"
355 return self.infer_compiled_value().api_type
356
357 def infer(self):
358 return ValueSet([self.infer_compiled_value()])
359
360 @memoize_method
361 def infer_compiled_value(self):
362 return create_from_name(self._inference_state, self._parent_value, self.string_name)
363
364
365class SignatureParamName(ParamNameInterface, AbstractNameDefinition):
366 def __init__(self, compiled_value, signature_param):
367 self.parent_context = compiled_value.parent_context
368 self._signature_param = signature_param
369
370 @property
371 def string_name(self):
372 return self._signature_param.name
373
374 def to_string(self):
375 s = self._kind_string() + self.string_name
376 if self._signature_param.has_annotation:
377 s += ': ' + self._signature_param.annotation_string
378 if self._signature_param.has_default:
379 s += '=' + self._signature_param.default_string
380 return s
381
382 def get_kind(self):
383 return getattr(Parameter, self._signature_param.kind_name)
384
385 def infer(self):
386 p = self._signature_param
387 inference_state = self.parent_context.inference_state
388 values = NO_VALUES
389 if p.has_default:
390 values = ValueSet([create_from_access_path(inference_state, p.default)])
391 if p.has_annotation:
392 annotation = create_from_access_path(inference_state, p.annotation)
393 values |= annotation.execute_with_values()
394 return values
395
396
397class UnresolvableParamName(ParamNameInterface, AbstractNameDefinition):
398 def __init__(self, compiled_value, name, default):
399 self.parent_context = compiled_value.parent_context
400 self.string_name = name
401 self._default = default
402
403 def get_kind(self):
404 return Parameter.POSITIONAL_ONLY
405
406 def to_string(self):
407 string = self.string_name
408 if self._default:
409 string += '=' + self._default
410 return string
411
412 def infer(self):
413 return NO_VALUES
414
415
416class CompiledValueName(ValueNameMixin, AbstractNameDefinition):
417 def __init__(self, value, name):
418 self.string_name = name
419 self._value = value
420 self.parent_context = value.parent_context
421
422
423class EmptyCompiledName(AbstractNameDefinition):
424 """
425 Accessing some names will raise an exception. To avoid not having any
426 completions, just give Jedi the option to return this object. It infers to
427 nothing.
428 """
429 def __init__(self, inference_state, name):
430 self.parent_context = inference_state.builtins_module
431 self.string_name = name
432
433 def infer(self):
434 return NO_VALUES
435
436
437class CompiledValueFilter(AbstractFilter):
438 def __init__(self, inference_state, compiled_value, is_instance=False):
439 self._inference_state = inference_state
440 self.compiled_value = compiled_value
441 self.is_instance = is_instance
442
443 def get(self, name):
444 access_handle = self.compiled_value.access_handle
445 safe = not self._inference_state.allow_unsafe_executions
446 return self._get(
447 name,
448 lambda name: access_handle.is_allowed_getattr(name, safe=safe),
449 lambda name: name in access_handle.dir(),
450 check_has_attribute=True
451 )
452
453 def _get(self, name, allowed_getattr_callback, in_dir_callback, check_has_attribute=False):
454 """
455 To remove quite a few access calls we introduced the callback here.
456 """
457 has_attribute, is_descriptor, property_return_annotation = allowed_getattr_callback(
458 name,
459 )
460 if property_return_annotation is not None:
461 values = create_from_access_path(
462 self._inference_state,
463 property_return_annotation
464 ).execute_annotation(None)
465 if values:
466 return [CompiledValueName(v, name) for v in values]
467
468 if check_has_attribute and not has_attribute:
469 return []
470
471 if (is_descriptor or not has_attribute) \
472 and not self._inference_state.allow_unsafe_executions:
473 return [self._get_cached_name(name, is_empty=True)]
474
475 if self.is_instance and not in_dir_callback(name):
476 return []
477 return [self._get_cached_name(name, is_descriptor=is_descriptor)]
478
479 @memoize_method
480 def _get_cached_name(self, name, is_empty=False, *, is_descriptor=False):
481 if is_empty:
482 return EmptyCompiledName(self._inference_state, name)
483 else:
484 return self._create_name(name, is_descriptor=is_descriptor)
485
486 def values(self):
487 from jedi.inference.compiled import builtin_from_name
488 names = []
489 needs_type_completions, dir_infos = self.compiled_value.access_handle.get_dir_infos()
490 # We could use `safe=False` here as well, especially as a parameter to
491 # get_dir_infos. But this would lead to a lot of property executions
492 # that are probably not wanted. The drawback for this is that we
493 # have a different name for `get` and `values`. For `get` we always
494 # execute.
495 for name in dir_infos:
496 names += self._get(
497 name,
498 lambda name: dir_infos[name],
499 lambda name: name in dir_infos,
500 )
501
502 # ``dir`` doesn't include the type names.
503 if not self.is_instance and needs_type_completions:
504 for filter in builtin_from_name(self._inference_state, 'type').get_filters():
505 names += filter.values()
506 return names
507
508 def _create_name(self, name, is_descriptor):
509 return CompiledName(
510 self._inference_state,
511 self.compiled_value,
512 name,
513 is_descriptor,
514 )
515
516 def __repr__(self):
517 return "<%s: %s>" % (self.__class__.__name__, self.compiled_value)
518
519
520docstr_defaults = {
521 'floating point number': 'float',
522 'character': 'str',
523 'integer': 'int',
524 'dictionary': 'dict',
525 'string': 'str',
526}
527
528
529def _parse_function_doc(doc):
530 """
531 Takes a function and returns the params and return value as a tuple.
532 This is nothing more than a docstring parser.
533
534 TODO docstrings like utime(path, (atime, mtime)) and a(b [, b]) -> None
535 TODO docstrings like 'tuple of integers'
536 """
537 # parse round parentheses: def func(a, (b,c))
538 try:
539 count = 0
540 start = doc.index('(')
541 for i, s in enumerate(doc[start:]):
542 if s == '(':
543 count += 1
544 elif s == ')':
545 count -= 1
546 if count == 0:
547 end = start + i
548 break
549 param_str = doc[start + 1:end]
550 except (ValueError, UnboundLocalError):
551 # ValueError for doc.index
552 # UnboundLocalError for undefined end in last line
553 debug.dbg('no brackets found - no param')
554 end = 0
555 param_str = ''
556 else:
557 # remove square brackets, that show an optional param ( = None)
558 def change_options(m):
559 args = m.group(1).split(',')
560 for i, a in enumerate(args):
561 if a and '=' not in a:
562 args[i] += '=None'
563 return ','.join(args)
564
565 while True:
566 param_str, changes = re.subn(r' ?\[([^\[\]]+)\]',
567 change_options, param_str)
568 if changes == 0:
569 break
570 param_str = param_str.replace('-', '_') # see: isinstance.__doc__
571
572 # parse return value
573 r = re.search('-[>-]* ', doc[end:end + 7])
574 if r is None:
575 ret = ''
576 else:
577 index = end + r.end()
578 # get result type, which can contain newlines
579 pattern = re.compile(r'(,\n|[^\n-])+')
580 ret_str = pattern.match(doc, index).group(0).strip()
581 # New object -> object()
582 ret_str = re.sub(r'[nN]ew (.*)', r'\1()', ret_str)
583
584 ret = docstr_defaults.get(ret_str, ret_str)
585
586 return param_str, ret
587
588
589def create_from_name(inference_state, compiled_value, name):
590 access_paths = compiled_value.access_handle.getattr_paths(name, default=None)
591
592 value = None
593 for access_path in access_paths:
594 value = create_cached_compiled_value(
595 inference_state,
596 access_path,
597 parent_context=None if value is None else value.as_context(), # type: ignore # TODO
598 )
599 return value
600
601
602def _normalize_create_args(func):
603 """The cache doesn't care about keyword vs. normal args."""
604 def wrapper(inference_state, obj, parent_context=None):
605 return func(inference_state, obj, parent_context)
606 return wrapper
607
608
609def create_from_access_path(inference_state, access_path):
610 value = None
611 for name, access in access_path.accesses:
612 value = create_cached_compiled_value(
613 inference_state,
614 access,
615 parent_context=None if value is None else value.as_context() # type: ignore # TODO
616 )
617 return value
618
619
620@_normalize_create_args
621@inference_state_function_cache()
622def create_cached_compiled_value(inference_state, access_handle, parent_context):
623 assert not isinstance(parent_context, CompiledValue)
624 if parent_context is None:
625 cls = CompiledModule
626 else:
627 cls = CompiledValue
628 return cls(inference_state, access_handle, parent_context)