You cannot select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
	
	
		
			141 lines
		
	
	
		
			4.1 KiB
		
	
	
	
		
			Python
		
	
			
		
		
	
	
			141 lines
		
	
	
		
			4.1 KiB
		
	
	
	
		
			Python
		
	
import types
 | 
						|
 | 
						|
from pure_eval.utils import of_type, CannotEval
 | 
						|
 | 
						|
_sentinel = object()
 | 
						|
 | 
						|
 | 
						|
def _static_getmro(klass):
 | 
						|
    return type.__dict__['__mro__'].__get__(klass)
 | 
						|
 | 
						|
 | 
						|
def _check_instance(obj, attr):
 | 
						|
    instance_dict = {}
 | 
						|
    try:
 | 
						|
        instance_dict = object.__getattribute__(obj, "__dict__")
 | 
						|
    except AttributeError:
 | 
						|
        pass
 | 
						|
    return dict.get(instance_dict, attr, _sentinel)
 | 
						|
 | 
						|
 | 
						|
def _check_class(klass, attr):
 | 
						|
    for entry in _static_getmro(klass):
 | 
						|
        if _shadowed_dict(type(entry)) is _sentinel:
 | 
						|
            try:
 | 
						|
                return entry.__dict__[attr]
 | 
						|
            except KeyError:
 | 
						|
                pass
 | 
						|
        else:
 | 
						|
            break
 | 
						|
    return _sentinel
 | 
						|
 | 
						|
 | 
						|
def _is_type(obj):
 | 
						|
    try:
 | 
						|
        _static_getmro(obj)
 | 
						|
    except TypeError:
 | 
						|
        return False
 | 
						|
    return True
 | 
						|
 | 
						|
 | 
						|
def _shadowed_dict(klass):
 | 
						|
    dict_attr = type.__dict__["__dict__"]
 | 
						|
    for entry in _static_getmro(klass):
 | 
						|
        try:
 | 
						|
            class_dict = dict_attr.__get__(entry)["__dict__"]
 | 
						|
        except KeyError:
 | 
						|
            pass
 | 
						|
        else:
 | 
						|
            if not (type(class_dict) is types.GetSetDescriptorType and
 | 
						|
                    class_dict.__name__ == "__dict__" and
 | 
						|
                    class_dict.__objclass__ is entry):
 | 
						|
                return class_dict
 | 
						|
    return _sentinel
 | 
						|
 | 
						|
 | 
						|
def getattr_static(obj, attr):
 | 
						|
    """Retrieve attributes without triggering dynamic lookup via the
 | 
						|
       descriptor protocol,  __getattr__ or __getattribute__.
 | 
						|
 | 
						|
       Note: this function may not be able to retrieve all attributes
 | 
						|
       that getattr can fetch (like dynamically created attributes)
 | 
						|
       and may find attributes that getattr can't (like descriptors
 | 
						|
       that raise AttributeError). It can also return descriptor objects
 | 
						|
       instead of instance members in some cases. See the
 | 
						|
       documentation for details.
 | 
						|
    """
 | 
						|
    instance_result = _sentinel
 | 
						|
    if not _is_type(obj):
 | 
						|
        klass = type(obj)
 | 
						|
        dict_attr = _shadowed_dict(klass)
 | 
						|
        if (dict_attr is _sentinel or
 | 
						|
                type(dict_attr) is types.MemberDescriptorType):
 | 
						|
            instance_result = _check_instance(obj, attr)
 | 
						|
        else:
 | 
						|
            raise CannotEval
 | 
						|
    else:
 | 
						|
        klass = obj
 | 
						|
 | 
						|
    klass_result = _check_class(klass, attr)
 | 
						|
 | 
						|
    if instance_result is not _sentinel and klass_result is not _sentinel:
 | 
						|
        if _check_class(type(klass_result), "__get__") is not _sentinel and (
 | 
						|
            _check_class(type(klass_result), "__set__") is not _sentinel
 | 
						|
            or _check_class(type(klass_result), "__delete__") is not _sentinel
 | 
						|
        ):
 | 
						|
            return _resolve_descriptor(klass_result, obj, klass)
 | 
						|
 | 
						|
    if instance_result is not _sentinel:
 | 
						|
        return instance_result
 | 
						|
    if klass_result is not _sentinel:
 | 
						|
        get = _check_class(type(klass_result), '__get__')
 | 
						|
        if get is _sentinel:
 | 
						|
            return klass_result
 | 
						|
        else:
 | 
						|
            if obj is klass:
 | 
						|
                instance = None
 | 
						|
            else:
 | 
						|
                instance = obj
 | 
						|
            return _resolve_descriptor(klass_result, instance, klass)
 | 
						|
 | 
						|
    if obj is klass:
 | 
						|
        # for types we check the metaclass too
 | 
						|
        for entry in _static_getmro(type(klass)):
 | 
						|
            if _shadowed_dict(type(entry)) is _sentinel:
 | 
						|
                try:
 | 
						|
                    result = entry.__dict__[attr]
 | 
						|
                    get = _check_class(type(result), '__get__')
 | 
						|
                    if get is not _sentinel:
 | 
						|
                        raise CannotEval
 | 
						|
                    return result
 | 
						|
                except KeyError:
 | 
						|
                    pass
 | 
						|
    raise CannotEval
 | 
						|
 | 
						|
 | 
						|
class _foo:
 | 
						|
    __slots__ = ['foo']
 | 
						|
    method = lambda: 0
 | 
						|
 | 
						|
 | 
						|
slot_descriptor = _foo.foo
 | 
						|
wrapper_descriptor = str.__dict__['__add__']
 | 
						|
method_descriptor = str.__dict__['startswith']
 | 
						|
user_method_descriptor = _foo.__dict__['method']
 | 
						|
 | 
						|
safe_descriptors_raw = [
 | 
						|
    slot_descriptor,
 | 
						|
    wrapper_descriptor,
 | 
						|
    method_descriptor,
 | 
						|
    user_method_descriptor,
 | 
						|
]
 | 
						|
 | 
						|
safe_descriptor_types = list(map(type, safe_descriptors_raw))
 | 
						|
 | 
						|
 | 
						|
def _resolve_descriptor(d, instance, owner):
 | 
						|
    try:
 | 
						|
        return type(of_type(d, *safe_descriptor_types)).__get__(d, instance, owner)
 | 
						|
    except AttributeError as e:
 | 
						|
        raise CannotEval from e
 |