fork download
  1. import gc
  2. import types
  3. import inspect
  4. from typing import Optional
  5. from weakref import WeakKeyDictionary
  6.  
  7. def proxy0(data):
  8. def proxy1(): return data
  9. return proxy1
  10.  
  11. _CELLTYPE = type(proxy0(None).__closure__[0])
  12.  
  13. def replace_all_refs(org_obj, new_obj):
  14. gc.collect()
  15.  
  16. hit = False
  17. for referrer in gc.get_referrers(org_obj):
  18. if isinstance(referrer, types.FrameType):
  19. continue
  20. if isinstance(referrer, dict):
  21. cls = None
  22. if '__dict__' in referrer and '__weakref__' in referrer:
  23. for cls in gc.get_referrers(referrer):
  24. if inspect.isclass(cls) and cls.__dict__ == referrer:
  25. break
  26. for key, value in referrer.items():
  27. if value is org_obj:
  28. hit = True
  29. value = new_obj
  30. referrer[key] = value
  31. if cls:
  32. setattr(cls, key, new_obj)
  33. if key is org_obj:
  34. hit = True
  35. del referrer[key]
  36. referrer[new_obj] = value
  37. elif isinstance(referrer, list):
  38. for i, value in enumerate(referrer):
  39. if value is org_obj:
  40. hit = True
  41. referrer[i] = new_obj
  42. elif isinstance(referrer, set):
  43. referrer.remove(org_obj)
  44. referrer.add(new_obj)
  45. hit = True
  46. elif isinstance(referrer, (tuple, frozenset,)):
  47. new_tuple = []
  48. for obj in referrer:
  49. if obj is org_obj:
  50. new_tuple.append(new_obj)
  51. else:
  52. new_tuple.append(obj)
  53. replace_all_refs(referrer, type(referrer)(new_tuple))
  54. elif isinstance(referrer, _CELLTYPE):
  55. def proxy0(data):
  56. def proxy1(): return data
  57. return proxy1
  58. proxy = proxy0(new_obj)
  59. newcell = proxy.__closure__[0]
  60. replace_all_refs(referrer, newcell)
  61. elif isinstance(referrer, types.FunctionType):
  62. localsmap = {}
  63. for key in ['__code__', '__globals__', '__name__',
  64. '__defaults__', '__closure__']:
  65. orgattr = getattr(referrer, key)
  66. if orgattr is org_obj:
  67. localsmap[key[2:-2]] = new_obj
  68. else:
  69. localsmap[key[2:-2]] = orgattr
  70. localsmap['argdefs'] = localsmap['defaults']
  71. del localsmap['defaults']
  72. newfn = types.FunctionType(**localsmap)
  73. replace_all_refs(referrer, newfn)
  74. if hit is False:
  75. raise AttributeError("Object '%r' not found" % org_obj)
  76. return org_obj
  77.  
  78. class DeferredStrProperty:
  79. instances = WeakKeyDictionary()
  80.  
  81. def __init__(self, attribute):
  82. self.attribute = attribute
  83.  
  84. def __get__(self, obj, objtype=None):
  85. if obj is None:
  86. return vars(objtype)[self.attribute]
  87. if (deferred_str := self.instances.get(obj)) is None:
  88. return vars(obj)[self.attribute]
  89. return deferred_str
  90.  
  91. def __set__(self, obj, value):
  92. if (deferred_str := self.instances.get(obj)) is None:
  93. vars(obj)[self.attribute] = value
  94. else:
  95. replace_all_refs(deferred_str, value)
  96. del self.instances[obj]
  97.  
  98. class DeferredStr(str):
  99. def __new__(cls, instance, attribute):
  100. if not isinstance(getattr(type(instance), attribute), DeferredStrProperty):
  101. setattr(type(instance), attribute, DeferredStrProperty(attribute))
  102. DeferredStrProperty.instances[instance] = s = super().__new__(cls, 'x')
  103. return s
  104.  
  105. class Test:
  106. value: Optional[str] = None
  107.  
  108. instance = Test()
  109. instance2 = Test()
  110. deferred_str = DeferredStr(instance, "value")
  111. print("2" + deferred_str)
  112. print("".join((deferred_str,)))
  113. print(instance.value)
  114. print('some time later...')
  115. instance2.value = '3'
  116. instance.value = '1'
  117. print(instance2.value)
  118. print(deferred_str)
  119. print(deferred_str + "2")
  120. print("".join(deferred_str))
  121. print("2" + deferred_str)
  122. print("".join((deferred_str,)))
  123.  
Success #stdin #stdout 0.04s 10856KB
stdin
Standard input is empty
stdout
2x
x
x
some time later...
3
1
12
1
21
1