test_finalization.py 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538
  1. """
  2. Tests for object finalization semantics, as outlined in PEP 442.
  3. """
  4. import contextlib
  5. import gc
  6. import unittest
  7. import weakref
  8. try:
  9. from _testcapi import with_tp_del
  10. except ImportError:
  11. def with_tp_del(cls):
  12. class C(object):
  13. def __new__(cls, *args, **kwargs):
  14. raise TypeError('requires _testcapi.with_tp_del')
  15. return C
  16. try:
  17. from _testcapi import without_gc
  18. except ImportError:
  19. def without_gc(cls):
  20. class C:
  21. def __new__(cls, *args, **kwargs):
  22. raise TypeError('requires _testcapi.without_gc')
  23. return C
  24. from test import support
  25. class NonGCSimpleBase:
  26. """
  27. The base class for all the objects under test, equipped with various
  28. testing features.
  29. """
  30. survivors = []
  31. del_calls = []
  32. tp_del_calls = []
  33. errors = []
  34. _cleaning = False
  35. __slots__ = ()
  36. @classmethod
  37. def _cleanup(cls):
  38. cls.survivors.clear()
  39. cls.errors.clear()
  40. gc.garbage.clear()
  41. gc.collect()
  42. cls.del_calls.clear()
  43. cls.tp_del_calls.clear()
  44. @classmethod
  45. @contextlib.contextmanager
  46. def test(cls):
  47. """
  48. A context manager to use around all finalization tests.
  49. """
  50. with support.disable_gc():
  51. cls.del_calls.clear()
  52. cls.tp_del_calls.clear()
  53. NonGCSimpleBase._cleaning = False
  54. try:
  55. yield
  56. if cls.errors:
  57. raise cls.errors[0]
  58. finally:
  59. NonGCSimpleBase._cleaning = True
  60. cls._cleanup()
  61. def check_sanity(self):
  62. """
  63. Check the object is sane (non-broken).
  64. """
  65. def __del__(self):
  66. """
  67. PEP 442 finalizer. Record that this was called, check the
  68. object is in a sane state, and invoke a side effect.
  69. """
  70. try:
  71. if not self._cleaning:
  72. self.del_calls.append(id(self))
  73. self.check_sanity()
  74. self.side_effect()
  75. except Exception as e:
  76. self.errors.append(e)
  77. def side_effect(self):
  78. """
  79. A side effect called on destruction.
  80. """
  81. class SimpleBase(NonGCSimpleBase):
  82. def __init__(self):
  83. self.id_ = id(self)
  84. def check_sanity(self):
  85. assert self.id_ == id(self)
  86. @without_gc
  87. class NonGC(NonGCSimpleBase):
  88. __slots__ = ()
  89. @without_gc
  90. class NonGCResurrector(NonGCSimpleBase):
  91. __slots__ = ()
  92. def side_effect(self):
  93. """
  94. Resurrect self by storing self in a class-wide list.
  95. """
  96. self.survivors.append(self)
  97. class Simple(SimpleBase):
  98. pass
  99. # Can't inherit from NonGCResurrector, in case importing without_gc fails.
  100. class SimpleResurrector(SimpleBase):
  101. def side_effect(self):
  102. """
  103. Resurrect self by storing self in a class-wide list.
  104. """
  105. self.survivors.append(self)
  106. class TestBase:
  107. def setUp(self):
  108. self.old_garbage = gc.garbage[:]
  109. gc.garbage[:] = []
  110. def tearDown(self):
  111. # None of the tests here should put anything in gc.garbage
  112. try:
  113. self.assertEqual(gc.garbage, [])
  114. finally:
  115. del self.old_garbage
  116. gc.collect()
  117. def assert_del_calls(self, ids):
  118. self.assertEqual(sorted(SimpleBase.del_calls), sorted(ids))
  119. def assert_tp_del_calls(self, ids):
  120. self.assertEqual(sorted(SimpleBase.tp_del_calls), sorted(ids))
  121. def assert_survivors(self, ids):
  122. self.assertEqual(sorted(id(x) for x in SimpleBase.survivors), sorted(ids))
  123. def assert_garbage(self, ids):
  124. self.assertEqual(sorted(id(x) for x in gc.garbage), sorted(ids))
  125. def clear_survivors(self):
  126. SimpleBase.survivors.clear()
  127. class SimpleFinalizationTest(TestBase, unittest.TestCase):
  128. """
  129. Test finalization without refcycles.
  130. """
  131. def test_simple(self):
  132. with SimpleBase.test():
  133. s = Simple()
  134. ids = [id(s)]
  135. wr = weakref.ref(s)
  136. del s
  137. gc.collect()
  138. self.assert_del_calls(ids)
  139. self.assert_survivors([])
  140. self.assertIs(wr(), None)
  141. gc.collect()
  142. self.assert_del_calls(ids)
  143. self.assert_survivors([])
  144. def test_simple_resurrect(self):
  145. with SimpleBase.test():
  146. s = SimpleResurrector()
  147. ids = [id(s)]
  148. wr = weakref.ref(s)
  149. del s
  150. gc.collect()
  151. self.assert_del_calls(ids)
  152. self.assert_survivors(ids)
  153. self.assertIsNot(wr(), None)
  154. self.clear_survivors()
  155. gc.collect()
  156. self.assert_del_calls(ids)
  157. self.assert_survivors([])
  158. self.assertIs(wr(), None)
  159. @support.cpython_only
  160. def test_non_gc(self):
  161. with SimpleBase.test():
  162. s = NonGC()
  163. self.assertFalse(gc.is_tracked(s))
  164. ids = [id(s)]
  165. del s
  166. gc.collect()
  167. self.assert_del_calls(ids)
  168. self.assert_survivors([])
  169. gc.collect()
  170. self.assert_del_calls(ids)
  171. self.assert_survivors([])
  172. @support.cpython_only
  173. def test_non_gc_resurrect(self):
  174. with SimpleBase.test():
  175. s = NonGCResurrector()
  176. self.assertFalse(gc.is_tracked(s))
  177. ids = [id(s)]
  178. del s
  179. gc.collect()
  180. self.assert_del_calls(ids)
  181. self.assert_survivors(ids)
  182. self.clear_survivors()
  183. gc.collect()
  184. self.assert_del_calls(ids * 2)
  185. self.assert_survivors(ids)
  186. class SelfCycleBase:
  187. def __init__(self):
  188. super().__init__()
  189. self.ref = self
  190. def check_sanity(self):
  191. super().check_sanity()
  192. assert self.ref is self
  193. class SimpleSelfCycle(SelfCycleBase, Simple):
  194. pass
  195. class SelfCycleResurrector(SelfCycleBase, SimpleResurrector):
  196. pass
  197. class SuicidalSelfCycle(SelfCycleBase, Simple):
  198. def side_effect(self):
  199. """
  200. Explicitly break the reference cycle.
  201. """
  202. self.ref = None
  203. class SelfCycleFinalizationTest(TestBase, unittest.TestCase):
  204. """
  205. Test finalization of an object having a single cyclic reference to
  206. itself.
  207. """
  208. def test_simple(self):
  209. with SimpleBase.test():
  210. s = SimpleSelfCycle()
  211. ids = [id(s)]
  212. wr = weakref.ref(s)
  213. del s
  214. gc.collect()
  215. self.assert_del_calls(ids)
  216. self.assert_survivors([])
  217. self.assertIs(wr(), None)
  218. gc.collect()
  219. self.assert_del_calls(ids)
  220. self.assert_survivors([])
  221. def test_simple_resurrect(self):
  222. # Test that __del__ can resurrect the object being finalized.
  223. with SimpleBase.test():
  224. s = SelfCycleResurrector()
  225. ids = [id(s)]
  226. wr = weakref.ref(s)
  227. del s
  228. gc.collect()
  229. self.assert_del_calls(ids)
  230. self.assert_survivors(ids)
  231. # XXX is this desirable?
  232. self.assertIs(wr(), None)
  233. # When trying to destroy the object a second time, __del__
  234. # isn't called anymore (and the object isn't resurrected).
  235. self.clear_survivors()
  236. gc.collect()
  237. self.assert_del_calls(ids)
  238. self.assert_survivors([])
  239. self.assertIs(wr(), None)
  240. def test_simple_suicide(self):
  241. # Test the GC is able to deal with an object that kills its last
  242. # reference during __del__.
  243. with SimpleBase.test():
  244. s = SuicidalSelfCycle()
  245. ids = [id(s)]
  246. wr = weakref.ref(s)
  247. del s
  248. gc.collect()
  249. self.assert_del_calls(ids)
  250. self.assert_survivors([])
  251. self.assertIs(wr(), None)
  252. gc.collect()
  253. self.assert_del_calls(ids)
  254. self.assert_survivors([])
  255. self.assertIs(wr(), None)
  256. class ChainedBase:
  257. def chain(self, left):
  258. self.suicided = False
  259. self.left = left
  260. left.right = self
  261. def check_sanity(self):
  262. super().check_sanity()
  263. if self.suicided:
  264. assert self.left is None
  265. assert self.right is None
  266. else:
  267. left = self.left
  268. if left.suicided:
  269. assert left.right is None
  270. else:
  271. assert left.right is self
  272. right = self.right
  273. if right.suicided:
  274. assert right.left is None
  275. else:
  276. assert right.left is self
  277. class SimpleChained(ChainedBase, Simple):
  278. pass
  279. class ChainedResurrector(ChainedBase, SimpleResurrector):
  280. pass
  281. class SuicidalChained(ChainedBase, Simple):
  282. def side_effect(self):
  283. """
  284. Explicitly break the reference cycle.
  285. """
  286. self.suicided = True
  287. self.left = None
  288. self.right = None
  289. class CycleChainFinalizationTest(TestBase, unittest.TestCase):
  290. """
  291. Test finalization of a cyclic chain. These tests are similar in
  292. spirit to the self-cycle tests above, but the collectable object
  293. graph isn't trivial anymore.
  294. """
  295. def build_chain(self, classes):
  296. nodes = [cls() for cls in classes]
  297. for i in range(len(nodes)):
  298. nodes[i].chain(nodes[i-1])
  299. return nodes
  300. def check_non_resurrecting_chain(self, classes):
  301. N = len(classes)
  302. with SimpleBase.test():
  303. nodes = self.build_chain(classes)
  304. ids = [id(s) for s in nodes]
  305. wrs = [weakref.ref(s) for s in nodes]
  306. del nodes
  307. gc.collect()
  308. self.assert_del_calls(ids)
  309. self.assert_survivors([])
  310. self.assertEqual([wr() for wr in wrs], [None] * N)
  311. gc.collect()
  312. self.assert_del_calls(ids)
  313. def check_resurrecting_chain(self, classes):
  314. N = len(classes)
  315. with SimpleBase.test():
  316. nodes = self.build_chain(classes)
  317. N = len(nodes)
  318. ids = [id(s) for s in nodes]
  319. survivor_ids = [id(s) for s in nodes if isinstance(s, SimpleResurrector)]
  320. wrs = [weakref.ref(s) for s in nodes]
  321. del nodes
  322. gc.collect()
  323. self.assert_del_calls(ids)
  324. self.assert_survivors(survivor_ids)
  325. # XXX desirable?
  326. self.assertEqual([wr() for wr in wrs], [None] * N)
  327. self.clear_survivors()
  328. gc.collect()
  329. self.assert_del_calls(ids)
  330. self.assert_survivors([])
  331. def test_homogenous(self):
  332. self.check_non_resurrecting_chain([SimpleChained] * 3)
  333. def test_homogenous_resurrect(self):
  334. self.check_resurrecting_chain([ChainedResurrector] * 3)
  335. def test_homogenous_suicidal(self):
  336. self.check_non_resurrecting_chain([SuicidalChained] * 3)
  337. def test_heterogenous_suicidal_one(self):
  338. self.check_non_resurrecting_chain([SuicidalChained, SimpleChained] * 2)
  339. def test_heterogenous_suicidal_two(self):
  340. self.check_non_resurrecting_chain(
  341. [SuicidalChained] * 2 + [SimpleChained] * 2)
  342. def test_heterogenous_resurrect_one(self):
  343. self.check_resurrecting_chain([ChainedResurrector, SimpleChained] * 2)
  344. def test_heterogenous_resurrect_two(self):
  345. self.check_resurrecting_chain(
  346. [ChainedResurrector, SimpleChained, SuicidalChained] * 2)
  347. def test_heterogenous_resurrect_three(self):
  348. self.check_resurrecting_chain(
  349. [ChainedResurrector] * 2 + [SimpleChained] * 2 + [SuicidalChained] * 2)
  350. # NOTE: the tp_del slot isn't automatically inherited, so we have to call
  351. # with_tp_del() for each instantiated class.
  352. class LegacyBase(SimpleBase):
  353. def __del__(self):
  354. try:
  355. # Do not invoke side_effect here, since we are now exercising
  356. # the tp_del slot.
  357. if not self._cleaning:
  358. self.del_calls.append(id(self))
  359. self.check_sanity()
  360. except Exception as e:
  361. self.errors.append(e)
  362. def __tp_del__(self):
  363. """
  364. Legacy (pre-PEP 442) finalizer, mapped to a tp_del slot.
  365. """
  366. try:
  367. if not self._cleaning:
  368. self.tp_del_calls.append(id(self))
  369. self.check_sanity()
  370. self.side_effect()
  371. except Exception as e:
  372. self.errors.append(e)
  373. @with_tp_del
  374. class Legacy(LegacyBase):
  375. pass
  376. @with_tp_del
  377. class LegacyResurrector(LegacyBase):
  378. def side_effect(self):
  379. """
  380. Resurrect self by storing self in a class-wide list.
  381. """
  382. self.survivors.append(self)
  383. @with_tp_del
  384. class LegacySelfCycle(SelfCycleBase, LegacyBase):
  385. pass
  386. @support.cpython_only
  387. class LegacyFinalizationTest(TestBase, unittest.TestCase):
  388. """
  389. Test finalization of objects with a tp_del.
  390. """
  391. def tearDown(self):
  392. # These tests need to clean up a bit more, since they create
  393. # uncollectable objects.
  394. gc.garbage.clear()
  395. gc.collect()
  396. super().tearDown()
  397. def test_legacy(self):
  398. with SimpleBase.test():
  399. s = Legacy()
  400. ids = [id(s)]
  401. wr = weakref.ref(s)
  402. del s
  403. gc.collect()
  404. self.assert_del_calls(ids)
  405. self.assert_tp_del_calls(ids)
  406. self.assert_survivors([])
  407. self.assertIs(wr(), None)
  408. gc.collect()
  409. self.assert_del_calls(ids)
  410. self.assert_tp_del_calls(ids)
  411. def test_legacy_resurrect(self):
  412. with SimpleBase.test():
  413. s = LegacyResurrector()
  414. ids = [id(s)]
  415. wr = weakref.ref(s)
  416. del s
  417. gc.collect()
  418. self.assert_del_calls(ids)
  419. self.assert_tp_del_calls(ids)
  420. self.assert_survivors(ids)
  421. # weakrefs are cleared before tp_del is called.
  422. self.assertIs(wr(), None)
  423. self.clear_survivors()
  424. gc.collect()
  425. self.assert_del_calls(ids)
  426. self.assert_tp_del_calls(ids * 2)
  427. self.assert_survivors(ids)
  428. self.assertIs(wr(), None)
  429. def test_legacy_self_cycle(self):
  430. # Self-cycles with legacy finalizers end up in gc.garbage.
  431. with SimpleBase.test():
  432. s = LegacySelfCycle()
  433. ids = [id(s)]
  434. wr = weakref.ref(s)
  435. del s
  436. gc.collect()
  437. self.assert_del_calls([])
  438. self.assert_tp_del_calls([])
  439. self.assert_survivors([])
  440. self.assert_garbage(ids)
  441. self.assertIsNot(wr(), None)
  442. # Break the cycle to allow collection
  443. gc.garbage[0].ref = None
  444. self.assert_garbage([])
  445. self.assertIs(wr(), None)
  446. if __name__ == "__main__":
  447. unittest.main()