import sys
import warnings
from gc import get_referrers
from weakref import WeakKeyDictionary
from types import FunctionType, CodeType, CellType
referrer_types = {
CodeType: [FunctionType],
FunctionType: [dict, CellType],
CellType: [tuple],
tuple: [FunctionType],
dict: [type]
}
def get_class(obj):
if next_types := referrer_types.get(type(obj)):
for referrer in get_referrers(obj):
if issubclass(referrer_type := type(referrer), type):
return referrer
if referrer_type in next_types and (cls := get_class(referrer)):
return cls
def make_protector(action):
def decorator(func):
def wrapper(*args, **kwargs):
func_code = func.__code__
if func_code not in class_of:
class_of[func_code] = get_class(func_code)
caller_code = sys._getframe(1).f_code
if caller_code not in class_of:
class_of[caller_code] = get_class(caller_code)
if not (class_of[caller_code] and
class_of[func_code] in class_of[caller_code].mro()):
action(func.__qualname__)
return func(*args, **kwargs)
class_of = WeakKeyDictionary()
return wrapper
return decorator
@make_protector
def protected(name):
warnings.warn(f'{name} is protected.', stacklevel=3)
@make_protector
def private(name):
raise Exception(f'{name} is private.')
class C:
def A_factory(self):
class A:
@protected
def protected_func(self):
print('protected is running')
self.private_func()
@private
def private_func(self):
print('private is running')
return A
a = C().A_factory()
class B(a):
def foo(self):
super().private_func()
b = B()
b.foo() # Runs without complaint because of inheritance
b.protected_func() # Runs with protected warning
b.private_func() # Raises Exception