@@ -112,6 +112,100 @@ def iscoroutinefunction(fn):
112112 def make_async_decorator (tracer , fn , * params , ** kw_params ):
113113 return fn
114114
115+ # static version of getattr backported from Python 3.7
116+ try :
117+ from inspect import getattr_static
118+ except ImportError :
119+ import types
120+
121+ _sentinel = object ()
122+
123+ def _static_getmro (klass ):
124+ return type .__dict__ ['__mro__' ].__get__ (klass )
125+
126+ def _check_instance (obj , attr ):
127+ instance_dict = {}
128+ try :
129+ instance_dict = object .__getattribute__ (obj , "__dict__" )
130+ except AttributeError :
131+ pass
132+ return dict .get (instance_dict , attr , _sentinel )
133+
134+ def _check_class (klass , attr ):
135+ for entry in _static_getmro (klass ):
136+ if _shadowed_dict (type (entry )) is _sentinel :
137+ try :
138+ return entry .__dict__ [attr ]
139+ except KeyError :
140+ pass
141+ return _sentinel
142+
143+ def _is_type (obj ):
144+ try :
145+ _static_getmro (obj )
146+ except TypeError :
147+ return False
148+ return True
149+
150+ def _shadowed_dict (klass ):
151+ dict_attr = type .__dict__ ["__dict__" ]
152+ for entry in _static_getmro (klass ):
153+ try :
154+ class_dict = dict_attr .__get__ (entry )["__dict__" ]
155+ except KeyError :
156+ pass
157+ else :
158+ if not (type (class_dict ) is types .GetSetDescriptorType and # noqa: E721,E261,W504
159+ class_dict .__name__ == "__dict__" and # noqa: E261,W504
160+ class_dict .__objclass__ is entry ):
161+ return class_dict
162+ return _sentinel
163+
164+ def getattr_static (obj , attr , default = _sentinel ):
165+ """Retrieve attributes without triggering dynamic lookup via the
166+ descriptor protocol, __getattr__ or __getattribute__.
167+
168+ Note: this function may not be able to retrieve all attributes
169+ that getattr can fetch (like dynamically created attributes)
170+ and may find attributes that getattr can't (like descriptors
171+ that raise AttributeError). It can also return descriptor objects
172+ instead of instance members in some cases. See the
173+ documentation for details.
174+ """
175+ instance_result = _sentinel
176+ if not _is_type (obj ):
177+ klass = type (obj )
178+ dict_attr = _shadowed_dict (klass )
179+ if (dict_attr is _sentinel or # noqa: E261,E721,W504
180+ type (dict_attr ) is types .MemberDescriptorType ):
181+ instance_result = _check_instance (obj , attr )
182+ else :
183+ klass = obj
184+
185+ klass_result = _check_class (klass , attr )
186+
187+ if instance_result is not _sentinel and klass_result is not _sentinel :
188+ if (_check_class (type (klass_result ), '__get__' ) is not _sentinel and # noqa: W504,E261,E721
189+ _check_class (type (klass_result ), '__set__' ) is not _sentinel ):
190+ return klass_result
191+
192+ if instance_result is not _sentinel :
193+ return instance_result
194+ if klass_result is not _sentinel :
195+ return klass_result
196+
197+ if obj is klass :
198+ # for types we check the metaclass too
199+ for entry in _static_getmro (type (klass )):
200+ if _shadowed_dict (type (entry )) is _sentinel :
201+ try :
202+ return entry .__dict__ [attr ]
203+ except KeyError :
204+ pass
205+ if default is not _sentinel :
206+ return default
207+ raise AttributeError (attr )
208+
115209
116210# DEV: There is `six.u()` which does something similar, but doesn't have the guard around `hasattr(s, 'decode')`
117211def to_unicode (s ):
0 commit comments