Format: text/x-python
⬇ Download File (18.5 KB)
   1  import inspect
   2  import types
   3  import traceback
   4  import sys
   5  import operator as op
   6  from collections import namedtuple
   7  import warnings
   8  import re
   9  import builtins
  10  import typing
  11  from pathlib import Path
  12  from typing import Optional, Tuple
  13  
  14  from jedi.inference.compiled.getattr_static import getattr_static
  15  
  16  ALLOWED_GETITEM_TYPES = (str, list, tuple, bytes, bytearray, dict)
  17  
  18  MethodDescriptorType = type(str.replace)
  19  # These are not considered classes and access is granted even though they have
  20  # a __class__ attribute.
  21  NOT_CLASS_TYPES = (
  22      types.BuiltinFunctionType,
  23      types.CodeType,
  24      types.FrameType,
  25      types.FunctionType,
  26      types.GeneratorType,
  27      types.GetSetDescriptorType,
  28      types.LambdaType,
  29      types.MemberDescriptorType,
  30      types.MethodType,
  31      types.ModuleType,
  32      types.TracebackType,
  33      MethodDescriptorType,
  34      types.MappingProxyType,
  35      types.SimpleNamespace,
  36      types.DynamicClassAttribute,
  37  )
  38  
  39  # Those types don't exist in typing.
  40  MethodDescriptorType = type(str.replace)
  41  WrapperDescriptorType = type(set.__iter__)
  42  # `object.__subclasshook__` is an already executed descriptor.
  43  object_class_dict = type.__dict__["__dict__"].__get__(object)  # type: ignore[index]
  44  ClassMethodDescriptorType = type(object_class_dict['__subclasshook__'])
  45  
  46  _sentinel = object()
  47  
  48  # Maps Python syntax to the operator module.
  49  COMPARISON_OPERATORS = {
  50      '==': op.eq,
  51      '!=': op.ne,
  52      'is': op.is_,
  53      'is not': op.is_not,
  54      '<': op.lt,
  55      '<=': op.le,
  56      '>': op.gt,
  57      '>=': op.ge,
  58  }
  59  
  60  _OPERATORS = {
  61      '+': op.add,
  62      '-': op.sub,
  63  }
  64  _OPERATORS.update(COMPARISON_OPERATORS)
  65  
  66  ALLOWED_DESCRIPTOR_ACCESS = (
  67      types.FunctionType,
  68      types.GetSetDescriptorType,
  69      types.MemberDescriptorType,
  70      MethodDescriptorType,
  71      WrapperDescriptorType,
  72      ClassMethodDescriptorType,
  73      staticmethod,
  74      classmethod,
  75  )
  76  
  77  
  78  def safe_getattr(obj, name, default=_sentinel):
  79      try:
  80          attr, is_get_descriptor = getattr_static(obj, name)
  81      except AttributeError:
  82          if default is _sentinel:
  83              raise
  84          return default
  85      else:
  86          if isinstance(attr, ALLOWED_DESCRIPTOR_ACCESS):
  87              # In case of descriptors that have get methods we cannot return
  88              # it's value, because that would mean code execution.
  89              # Since it's an isinstance call, code execution is still possible,
  90              # but this is not really a security feature, but much more of a
  91              # safety feature. Code execution is basically always possible when
  92              # a module is imported. This is here so people don't shoot
  93              # themselves in the foot.
  94              return getattr(obj, name)
  95      return attr
  96  
  97  
  98  SignatureParam = namedtuple(
  99      'SignatureParam',
 100      'name has_default default default_string has_annotation annotation annotation_string kind_name'
 101  )
 102  
 103  
 104  def shorten_repr(func):
 105      def wrapper(self):
 106          r = func(self)
 107          if len(r) > 50:
 108              r = r[:50] + '..'
 109          return r
 110      return wrapper
 111  
 112  
 113  def create_access(inference_state, obj):
 114      return inference_state.compiled_subprocess.get_or_create_access_handle(obj)
 115  
 116  
 117  def load_module(inference_state, dotted_name, sys_path):
 118      temp, sys.path = sys.path, sys_path
 119      try:
 120          __import__(dotted_name)
 121      except ImportError:
 122          # If a module is "corrupt" or not really a Python module or whatever.
 123          warnings.warn(
 124              "Module %s not importable in path %s." % (dotted_name, sys_path),
 125              UserWarning,
 126              stacklevel=2,
 127          )
 128          return None
 129      except Exception:
 130          # Since __import__ pretty much makes code execution possible, just
 131          # catch any error here and print it.
 132          warnings.warn(
 133              "Cannot import:\n%s" % traceback.format_exc(), UserWarning, stacklevel=2
 134          )
 135          return None
 136      finally:
 137          sys.path = temp
 138  
 139      # Just access the cache after import, because of #59 as well as the very
 140      # complicated import structure of Python.
 141      module = sys.modules[dotted_name]
 142      return create_access_path(inference_state, module)
 143  
 144  
 145  class AccessPath:
 146      def __init__(self, accesses):
 147          self.accesses = accesses
 148  
 149  
 150  def create_access_path(inference_state, obj) -> AccessPath:
 151      access = create_access(inference_state, obj)
 152      return AccessPath(access.get_access_path_tuples())
 153  
 154  
 155  def get_api_type(obj):
 156      if inspect.isclass(obj):
 157          return 'class'
 158      elif inspect.ismodule(obj):
 159          return 'module'
 160      elif inspect.isbuiltin(obj) or inspect.ismethod(obj) \
 161              or inspect.ismethoddescriptor(obj) or inspect.isfunction(obj):
 162          return 'function'
 163      # Everything else...
 164      return 'instance'
 165  
 166  
 167  class DirectObjectAccess:
 168      def __init__(self, inference_state, obj):
 169          self._inference_state = inference_state
 170          self._obj = obj
 171  
 172      def __repr__(self):
 173          return '%s(%s)' % (self.__class__.__name__, self.get_repr())
 174  
 175      def _create_access(self, obj):
 176          return create_access(self._inference_state, obj)
 177  
 178      def _create_access_path(self, obj) -> AccessPath:
 179          return create_access_path(self._inference_state, obj)
 180  
 181      def py__bool__(self):
 182          return bool(self._obj)
 183  
 184      def py__file__(self) -> Optional[Path]:
 185          try:
 186              return Path(self._obj.__file__)
 187          except AttributeError:
 188              return None
 189  
 190      def py__doc__(self):
 191          return inspect.getdoc(self._obj) or ''
 192  
 193      def py__name__(self):
 194          if not _is_class_instance(self._obj) or \
 195                  inspect.ismethoddescriptor(self._obj):  # slots
 196              cls = self._obj
 197          else:
 198              try:
 199                  cls = self._obj.__class__
 200              except AttributeError:
 201                  # happens with numpy.core.umath._UFUNC_API (you get it
 202                  # automatically by doing `import numpy`.
 203                  return None
 204  
 205          try:
 206              return cls.__name__
 207          except AttributeError:
 208              return None
 209  
 210      def py__mro__accesses(self):
 211          return tuple(self._create_access_path(cls) for cls in self._obj.__mro__[1:])
 212  
 213      def py__getitem__all_values(self):
 214          if isinstance(self._obj, dict):
 215              return [self._create_access_path(v) for v in self._obj.values()]
 216          if isinstance(self._obj, (list, tuple)):
 217              return [self._create_access_path(v) for v in self._obj]
 218  
 219          if self.is_instance():
 220              cls = DirectObjectAccess(self._inference_state, self._obj.__class__)
 221              return cls.py__getitem__all_values()
 222  
 223          try:
 224              getitem = self._obj.__getitem__
 225          except AttributeError:
 226              pass
 227          else:
 228              annotation = DirectObjectAccess(self._inference_state, getitem).get_return_annotation()
 229              if annotation is not None:
 230                  return [annotation]
 231          return None
 232  
 233      def py__simple_getitem__(self, index, *, safe=True):
 234          if safe and type(self._obj) not in ALLOWED_GETITEM_TYPES:
 235              # Get rid of side effects, we won't call custom `__getitem__`s.
 236              return None
 237  
 238          return self._create_access_path(self._obj[index])
 239  
 240      def py__iter__list(self):
 241          try:
 242              iter_method = self._obj.__iter__
 243          except AttributeError:
 244              return None
 245          else:
 246              p = DirectObjectAccess(self._inference_state, iter_method).get_return_annotation()
 247              if p is not None:
 248                  return [p]
 249  
 250          if type(self._obj) not in ALLOWED_GETITEM_TYPES:
 251              # Get rid of side effects, we won't call custom `__getitem__`s.
 252              return []
 253  
 254          lst = []
 255          for i, part in enumerate(self._obj):
 256              if i > 20:
 257                  # Should not go crazy with large iterators
 258                  break
 259              lst.append(self._create_access_path(part))
 260          return lst
 261  
 262      def py__class__(self):
 263          return self._create_access_path(self._obj.__class__)
 264  
 265      def py__bases__(self):
 266          return [self._create_access_path(base) for base in self._obj.__bases__]
 267  
 268      def py__path__(self):
 269          paths = getattr(self._obj, '__path__', None)
 270          # Avoid some weird hacks that would just fail, because they cannot be
 271          # used by pickle.
 272          if not isinstance(paths, list) \
 273                  or not all(isinstance(p, str) for p in paths):
 274              return None
 275          return paths
 276  
 277      @shorten_repr
 278      def get_repr(self):
 279          if inspect.ismodule(self._obj):
 280              return repr(self._obj)
 281          # Try to avoid execution of the property.
 282          if safe_getattr(self._obj, '__module__', default='') == 'builtins':
 283              return repr(self._obj)
 284  
 285          type_ = type(self._obj)
 286          if type_ == type:
 287              return type.__repr__(self._obj)
 288  
 289          if safe_getattr(type_, '__module__', default='') == 'builtins':
 290              # Allow direct execution of repr for builtins.
 291              return repr(self._obj)
 292          return object.__repr__(self._obj)
 293  
 294      def is_class(self):
 295          return inspect.isclass(self._obj)
 296  
 297      def is_function(self):
 298          return inspect.isfunction(self._obj) or inspect.ismethod(self._obj)
 299  
 300      def is_module(self):
 301          return inspect.ismodule(self._obj)
 302  
 303      def is_instance(self):
 304          return _is_class_instance(self._obj)
 305  
 306      def ismethoddescriptor(self):
 307          return inspect.ismethoddescriptor(self._obj)
 308  
 309      def get_qualified_names(self):
 310          def try_to_get_name(obj):
 311              return getattr(obj, '__qualname__', getattr(obj, '__name__', None))
 312  
 313          if self.is_module():
 314              return ()
 315          name = try_to_get_name(self._obj)
 316          if name is None:
 317              name = try_to_get_name(type(self._obj))
 318              if name is None:
 319                  return ()
 320          return tuple(name.split('.'))
 321  
 322      def dir(self):
 323          return dir(self._obj)
 324  
 325      def has_iter(self):
 326          try:
 327              iter(self._obj)
 328              return True
 329          except TypeError:
 330              return False
 331  
 332      def is_allowed_getattr(self, name, safe=True) -> Tuple[bool, bool, Optional[AccessPath]]:
 333          # TODO this API is ugly.
 334          try:
 335              attr, is_get_descriptor = getattr_static(self._obj, name)
 336          except AttributeError:
 337              if not safe:
 338                  # Unsafe is mostly used to check for __getattr__/__getattribute__.
 339                  # getattr_static works for properties, but the underscore methods
 340                  # are just ignored (because it's safer and avoids more code
 341                  # execution). See also GH #1378.
 342  
 343                  # Avoid warnings, see comment in the next function.
 344                  with warnings.catch_warnings(record=True):
 345                      warnings.simplefilter("always")
 346                      try:
 347                          return hasattr(self._obj, name), False, None
 348                      except Exception:
 349                          # Obviously has an attribute (probably a property) that
 350                          # gets executed, so just avoid all exceptions here.
 351                          pass
 352              return False, False, None
 353          else:
 354              if is_get_descriptor and type(attr) not in ALLOWED_DESCRIPTOR_ACCESS:
 355                  if isinstance(attr, property):
 356                      if hasattr(attr.fget, '__annotations__'):
 357                          a = DirectObjectAccess(self._inference_state, attr.fget)
 358                          return True, True, a.get_return_annotation()
 359                  # In case of descriptors that have get methods we cannot return
 360                  # it's value, because that would mean code execution.
 361                  return True, True, None
 362          return True, False, None
 363  
 364      def getattr_paths(self, name, default=_sentinel):
 365          try:
 366              # Make sure no warnings are printed here, this is autocompletion,
 367              # warnings should not be shown. See also GH #1383.
 368              with warnings.catch_warnings(record=True):
 369                  warnings.simplefilter("always")
 370                  return_obj = getattr(self._obj, name)
 371          except Exception as e:
 372              if default is _sentinel:
 373                  if isinstance(e, AttributeError):
 374                      # Happens e.g. in properties of
 375                      # PyQt4.QtGui.QStyleOptionComboBox.currentText
 376                      # -> just set it to None
 377                      raise
 378                  # Just in case anything happens, return an AttributeError. It
 379                  # should not crash.
 380                  raise AttributeError
 381              return_obj = default
 382          access = self._create_access(return_obj)
 383          if inspect.ismodule(return_obj):
 384              return [access]
 385  
 386          try:
 387              module = return_obj.__module__
 388          except AttributeError:
 389              pass
 390          else:
 391              if module is not None and isinstance(module, str):
 392                  try:
 393                      __import__(module)
 394                      # For some modules like _sqlite3, the __module__ for classes is
 395                      # different, in this case it's sqlite3. So we have to try to
 396                      # load that "original" module, because it's not loaded yet. If
 397                      # we don't do that, we don't really have a "parent" module and
 398                      # we would fall back to builtins.
 399                  except ImportError:
 400                      pass
 401  
 402          module = inspect.getmodule(return_obj)
 403          if module is None:
 404              module = inspect.getmodule(type(return_obj))
 405              if module is None:
 406                  module = builtins
 407          return [self._create_access(module), access]
 408  
 409      def get_safe_value(self):
 410          if type(self._obj) in (bool, bytes, float, int, str, slice) or self._obj is None:
 411              return self._obj
 412          raise ValueError("Object is type %s and not simple" % type(self._obj))
 413  
 414      def get_api_type(self):
 415          return get_api_type(self._obj)
 416  
 417      def get_array_type(self):
 418          if isinstance(self._obj, dict):
 419              return 'dict'
 420          return None
 421  
 422      def get_key_paths(self):
 423          def iter_partial_keys():
 424              # We could use list(keys()), but that might take a lot more memory.
 425              for (i, k) in enumerate(self._obj.keys()):
 426                  # Limit key listing at some point. This is artificial, but this
 427                  # way we don't get stalled because of slow completions
 428                  if i > 50:
 429                      break
 430                  yield k
 431  
 432          return [self._create_access_path(k) for k in iter_partial_keys()]
 433  
 434      def get_access_path_tuples(self):
 435          accesses = [create_access(self._inference_state, o) for o in self._get_objects_path()]
 436          return [(access.py__name__(), access) for access in accesses]
 437  
 438      def _get_objects_path(self):
 439          def get():
 440              obj = self._obj
 441              yield obj
 442              try:
 443                  obj = obj.__objclass__
 444              except AttributeError:
 445                  pass
 446              else:
 447                  yield obj
 448  
 449              try:
 450                  # Returns a dotted string path.
 451                  imp_plz = obj.__module__
 452              except AttributeError:
 453                  # Unfortunately in some cases like `int` there's no __module__
 454                  if not inspect.ismodule(obj):
 455                      yield builtins
 456              else:
 457                  if imp_plz is None:
 458                      # Happens for example in `(_ for _ in []).send.__module__`.
 459                      yield builtins
 460                  else:
 461                      try:
 462                          yield sys.modules[imp_plz]
 463                      except KeyError:
 464                          # __module__ can be something arbitrary that doesn't exist.
 465                          yield builtins
 466  
 467          return list(reversed(list(get())))
 468  
 469      def execute_operation(self, other_access_handle, operator):
 470          other_access = other_access_handle.access
 471          op = _OPERATORS[operator]
 472          return self._create_access_path(op(self._obj, other_access._obj))
 473  
 474      def get_annotation_name_and_args(self):
 475          """
 476          Returns Tuple[Optional[str], Tuple[AccessPath, ...]]
 477          """
 478          name = None
 479          args = ()
 480          if safe_getattr(self._obj, '__module__', default='') == 'typing':
 481              m = re.match(r'typing.(\w+)\[', repr(self._obj))
 482              if m is not None:
 483                  name = m.group(1)
 484  
 485                  import typing
 486                  if sys.version_info >= (3, 8):
 487                      args = typing.get_args(self._obj)
 488                  else:
 489                      args = safe_getattr(self._obj, '__args__', default=None)
 490          return name, tuple(self._create_access_path(arg) for arg in args)
 491  
 492      def needs_type_completions(self):
 493          return inspect.isclass(self._obj) and self._obj != type
 494  
 495      def _annotation_to_str(self, annotation):
 496          return inspect.formatannotation(annotation)
 497  
 498      def get_signature_params(self):
 499          return [
 500              SignatureParam(
 501                  name=p.name,
 502                  has_default=p.default is not p.empty,
 503                  default=self._create_access_path(p.default),
 504                  default_string=repr(p.default),
 505                  has_annotation=p.annotation is not p.empty,
 506                  annotation=self._create_access_path(p.annotation),
 507                  annotation_string=self._annotation_to_str(p.annotation),
 508                  kind_name=str(p.kind)
 509              ) for p in self._get_signature().parameters.values()
 510          ]
 511  
 512      def _get_signature(self):
 513          obj = self._obj
 514          try:
 515              return inspect.signature(obj)
 516          except (RuntimeError, TypeError):
 517              # Reading the code of the function in Python 3.6 implies there are
 518              # at least these errors that might occur if something is wrong with
 519              # the signature. In that case we just want a simple escape for now.
 520              raise ValueError
 521  
 522      def get_return_annotation(self) -> Optional[AccessPath]:
 523          try:
 524              o = self._obj.__annotations__.get('return')
 525          except AttributeError:
 526              return None
 527  
 528          if o is None:
 529              return None
 530  
 531          try:
 532              o = typing.get_type_hints(self._obj).get('return')
 533          except Exception:
 534              pass
 535  
 536          return self._create_access_path(o)
 537  
 538      def negate(self):
 539          return self._create_access_path(-self._obj)
 540  
 541      def get_dir_infos(self):
 542          """
 543          Used to return a couple of infos that are needed when accessing the sub
 544          objects of an objects
 545          """
 546          tuples = dict(
 547              (name, self.is_allowed_getattr(name))
 548              for name in self.dir()
 549          )
 550          return self.needs_type_completions(), tuples
 551  
 552  
 553  def _is_class_instance(obj):
 554      """Like inspect.* methods."""
 555      try:
 556          cls = obj.__class__
 557      except AttributeError:
 558          return False
 559      else:
 560          # The isinstance check for cls is just there so issubclass doesn't
 561          # raise an exception.
 562          return cls != type and isinstance(cls, type) and not issubclass(cls, NOT_CLASS_TYPES)
 563