fork download
  1. import sys
  2. import warnings
  3. from gc import get_referrers
  4. from weakref import WeakKeyDictionary
  5. from types import FunctionType, CodeType, CellType
  6.  
  7. referrer_types = {
  8. CodeType: [FunctionType],
  9. FunctionType: [dict, CellType],
  10. CellType: [tuple],
  11. tuple: [FunctionType],
  12. dict: [type]
  13. }
  14.  
  15. def get_class(obj):
  16. if next_types := referrer_types.get(type(obj)):
  17. for referrer in get_referrers(obj):
  18. if issubclass(referrer_type := type(referrer), type):
  19. return referrer
  20. if referrer_type in next_types and (cls := get_class(referrer)):
  21. return cls
  22.  
  23. def make_protector(action):
  24. def decorator(func):
  25. def wrapper(*args, **kwargs):
  26. func_code = func.__code__
  27. if func_code not in class_of:
  28. class_of[func_code] = get_class(func_code)
  29. caller_code = sys._getframe(1).f_code
  30. if caller_code not in class_of:
  31. class_of[caller_code] = get_class(caller_code)
  32. if not (class_of[caller_code] and
  33. class_of[func_code] in class_of[caller_code].mro()):
  34. action(func.__qualname__)
  35. return func(*args, **kwargs)
  36. class_of = WeakKeyDictionary()
  37. return wrapper
  38. return decorator
  39.  
  40. @make_protector
  41. def protected(name):
  42. warnings.warn(f'{name} is protected.', stacklevel=3)
  43.  
  44. @make_protector
  45. def private(name):
  46. raise Exception(f'{name} is private.')
  47.  
  48. class C:
  49. def A_factory(self):
  50. class A:
  51. @protected
  52. def protected_func(self):
  53. print('protected is running')
  54. self.private_func()
  55.  
  56. @private
  57. def private_func(self):
  58. print('private is running')
  59.  
  60. return A
  61.  
  62. a = C().A_factory()
  63.  
  64. class B(a):
  65. def foo(self):
  66. super().private_func()
  67.  
  68. b = B()
  69. b.foo() # Runs without complaint because of inheritance
  70. b.protected_func() # Runs with protected warning
  71. b.private_func() # Raises Exception
Runtime error #stdin #stdout #stderr 0.19s 26112KB
stdin
Standard input is empty
stdout
private is running
protected is running
private is running
stderr
./prog.py:70: UserWarning: C.A_factory.<locals>.A.protected_func is protected.
Traceback (most recent call last):
  File "./prog.py", line 71, in <module>
  File "./prog.py", line 34, in wrapper
  File "./prog.py", line 46, in private
Exception: C.A_factory.<locals>.A.private_func is private.