Format: text/x-python
⬇ Download File (20.8 KB)
   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)
 627