my_getattr_static.py 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  1. import types
  2. from pure_eval.utils import of_type, CannotEval
  3. _sentinel = object()
  4. def _static_getmro(klass):
  5. return type.__dict__['__mro__'].__get__(klass)
  6. def _check_instance(obj, attr):
  7. instance_dict = {}
  8. try:
  9. instance_dict = object.__getattribute__(obj, "__dict__")
  10. except AttributeError:
  11. pass
  12. return dict.get(instance_dict, attr, _sentinel)
  13. def _check_class(klass, attr):
  14. for entry in _static_getmro(klass):
  15. if _shadowed_dict(type(entry)) is _sentinel:
  16. try:
  17. return entry.__dict__[attr]
  18. except KeyError:
  19. pass
  20. else:
  21. break
  22. return _sentinel
  23. def _is_type(obj):
  24. try:
  25. _static_getmro(obj)
  26. except TypeError:
  27. return False
  28. return True
  29. def _shadowed_dict(klass):
  30. dict_attr = type.__dict__["__dict__"]
  31. for entry in _static_getmro(klass):
  32. try:
  33. class_dict = dict_attr.__get__(entry)["__dict__"]
  34. except KeyError:
  35. pass
  36. else:
  37. if not (type(class_dict) is types.GetSetDescriptorType and
  38. class_dict.__name__ == "__dict__" and
  39. class_dict.__objclass__ is entry):
  40. return class_dict
  41. return _sentinel
  42. def getattr_static(obj, attr):
  43. """Retrieve attributes without triggering dynamic lookup via the
  44. descriptor protocol, __getattr__ or __getattribute__.
  45. Note: this function may not be able to retrieve all attributes
  46. that getattr can fetch (like dynamically created attributes)
  47. and may find attributes that getattr can't (like descriptors
  48. that raise AttributeError). It can also return descriptor objects
  49. instead of instance members in some cases. See the
  50. documentation for details.
  51. """
  52. instance_result = _sentinel
  53. if not _is_type(obj):
  54. klass = type(obj)
  55. dict_attr = _shadowed_dict(klass)
  56. if (dict_attr is _sentinel or
  57. type(dict_attr) is types.MemberDescriptorType):
  58. instance_result = _check_instance(obj, attr)
  59. else:
  60. raise CannotEval
  61. else:
  62. klass = obj
  63. klass_result = _check_class(klass, attr)
  64. if instance_result is not _sentinel and klass_result is not _sentinel:
  65. if _check_class(type(klass_result), "__get__") is not _sentinel and (
  66. _check_class(type(klass_result), "__set__") is not _sentinel
  67. or _check_class(type(klass_result), "__delete__") is not _sentinel
  68. ):
  69. return _resolve_descriptor(klass_result, obj, klass)
  70. if instance_result is not _sentinel:
  71. return instance_result
  72. if klass_result is not _sentinel:
  73. get = _check_class(type(klass_result), '__get__')
  74. if get is _sentinel:
  75. return klass_result
  76. else:
  77. if obj is klass:
  78. instance = None
  79. else:
  80. instance = obj
  81. return _resolve_descriptor(klass_result, instance, klass)
  82. if obj is klass:
  83. # for types we check the metaclass too
  84. for entry in _static_getmro(type(klass)):
  85. if _shadowed_dict(type(entry)) is _sentinel:
  86. try:
  87. result = entry.__dict__[attr]
  88. get = _check_class(type(result), '__get__')
  89. if get is not _sentinel:
  90. raise CannotEval
  91. return result
  92. except KeyError:
  93. pass
  94. raise CannotEval
  95. class _foo:
  96. __slots__ = ['foo']
  97. method = lambda: 0
  98. slot_descriptor = _foo.foo
  99. wrapper_descriptor = str.__dict__['__add__']
  100. method_descriptor = str.__dict__['startswith']
  101. user_method_descriptor = _foo.__dict__['method']
  102. safe_descriptors_raw = [
  103. slot_descriptor,
  104. wrapper_descriptor,
  105. method_descriptor,
  106. user_method_descriptor,
  107. ]
  108. safe_descriptor_types = list(map(type, safe_descriptors_raw))
  109. def _resolve_descriptor(d, instance, owner):
  110. try:
  111. return type(of_type(d, *safe_descriptor_types)).__get__(d, instance, owner)
  112. except AttributeError as e:
  113. raise CannotEval from e