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