Format: text/x-python
⬇ Download File (3.8 KB)
   1  """
   2  A static version of getattr.
   3  This is a backport of the Python 3 code with a little bit of additional
   4  information returned to enable Jedi to make decisions.
   5  """
   6  
   7  import types
   8  
   9  from jedi import debug
  10  
  11  _sentinel = object()
  12  
  13  
  14  def _check_instance(obj, attr):
  15      instance_dict = {}
  16      try:
  17          instance_dict = object.__getattribute__(obj, "__dict__")
  18      except AttributeError:
  19          pass
  20      return dict.get(instance_dict, attr, _sentinel)
  21  
  22  
  23  def _check_class(klass, attr):
  24      for entry in _static_getmro(klass):
  25          if _shadowed_dict(type(entry)) is _sentinel:
  26              try:
  27                  return entry.__dict__[attr]
  28              except KeyError:
  29                  pass
  30      return _sentinel
  31  
  32  
  33  def _is_type(obj):
  34      try:
  35          _static_getmro(obj)
  36      except TypeError:
  37          return False
  38      return True
  39  
  40  
  41  def _shadowed_dict(klass):
  42      dict_attr = type.__dict__["__dict__"]
  43      for entry in _static_getmro(klass):
  44          try:
  45              class_dict = dict_attr.__get__(entry)["__dict__"]
  46          except KeyError:
  47              pass
  48          else:
  49              if not (type(class_dict) is types.GetSetDescriptorType
  50                      and class_dict.__name__ == "__dict__"
  51                      and class_dict.__objclass__ is entry):
  52                  return class_dict
  53      return _sentinel
  54  
  55  
  56  def _static_getmro(klass):
  57      mro = type.__dict__['__mro__'].__get__(klass)
  58      if not isinstance(mro, (tuple, list)):
  59          # There are unfortunately no tests for this, I was not able to
  60          # reproduce this in pure Python. However should still solve the issue
  61          # raised in GH #1517.
  62          debug.warning('mro of %s returned %s, should be a tuple' % (klass, mro))
  63          return ()
  64      return mro
  65  
  66  
  67  def _safe_hasattr(obj, name):
  68      return _check_class(type(obj), name) is not _sentinel
  69  
  70  
  71  def _safe_is_data_descriptor(obj):
  72      return _safe_hasattr(obj, '__set__') or _safe_hasattr(obj, '__delete__')
  73  
  74  
  75  def getattr_static(obj, attr, default=_sentinel):
  76      """Retrieve attributes without triggering dynamic lookup via the
  77         descriptor protocol,  __getattr__ or __getattribute__.
  78  
  79         Note: this function may not be able to retrieve all attributes
  80         that getattr can fetch (like dynamically created attributes)
  81         and may find attributes that getattr can't (like descriptors
  82         that raise AttributeError). It can also return descriptor objects
  83         instead of instance members in some cases. See the
  84         documentation for details.
  85  
  86         Returns a tuple `(attr, is_get_descriptor)`. is_get_descripter means that
  87         the attribute is a descriptor that has a `__get__` attribute.
  88      """
  89      instance_result = _sentinel
  90      if not _is_type(obj):
  91          klass = type(obj)
  92          dict_attr = _shadowed_dict(klass)
  93          if (dict_attr is _sentinel or type(dict_attr) is types.MemberDescriptorType):
  94              instance_result = _check_instance(obj, attr)
  95      else:
  96          klass = obj
  97  
  98      klass_result = _check_class(klass, attr)
  99  
 100      if instance_result is not _sentinel and klass_result is not _sentinel:
 101          if _safe_hasattr(klass_result, '__get__') \
 102                  and _safe_is_data_descriptor(klass_result):
 103              # A get/set descriptor has priority over everything.
 104              return klass_result, True
 105  
 106      if instance_result is not _sentinel:
 107          return instance_result, False
 108      if klass_result is not _sentinel:
 109          return klass_result, _safe_hasattr(klass_result, '__get__')
 110  
 111      if obj is klass:
 112          # for types we check the metaclass too
 113          for entry in _static_getmro(type(klass)):
 114              if _shadowed_dict(type(entry)) is _sentinel:
 115                  try:
 116                      return entry.__dict__[attr], False
 117                  except KeyError:
 118                      pass
 119      if default is not _sentinel:
 120          return default, False
 121      raise AttributeError(attr)
 122