(self)
| 237 | self.assertIn(gc.collect(), (2, 3)) |
| 238 | |
| 239 | def test_function_tp_clear_leaves_consistent_state(self): |
| 240 | # https://github.com/python/cpython/issues/91636 |
| 241 | code = """if 1: |
| 242 | |
| 243 | import gc |
| 244 | import weakref |
| 245 | |
| 246 | class LateFin: |
| 247 | __slots__ = ('ref',) |
| 248 | |
| 249 | def __del__(self): |
| 250 | |
| 251 | # 8. Now `latefin`'s finalizer is called. Here we |
| 252 | # obtain a reference to `func`, which is currently |
| 253 | # undergoing `tp_clear`. |
| 254 | global func |
| 255 | func = self.ref() |
| 256 | |
| 257 | class Cyclic(tuple): |
| 258 | __slots__ = () |
| 259 | |
| 260 | # 4. The finalizers of all garbage objects are called. In |
| 261 | # this case this is only us as `func` doesn't have a |
| 262 | # finalizer. |
| 263 | def __del__(self): |
| 264 | |
| 265 | # 5. Create a weakref to `func` now. In previous |
| 266 | # versions of Python, this would avoid having it |
| 267 | # cleared by the garbage collector before calling |
| 268 | # the finalizers. Now, weakrefs get cleared after |
| 269 | # calling finalizers. |
| 270 | self[1].ref = weakref.ref(self[0]) |
| 271 | |
| 272 | # 6. Drop the global reference to `latefin`. The only |
| 273 | # remaining reference is the one we have. |
| 274 | global latefin |
| 275 | del latefin |
| 276 | |
| 277 | # 7. Now `func` is `tp_clear`-ed. This drops the last |
| 278 | # reference to `Cyclic`, which gets `tp_dealloc`-ed. |
| 279 | # This drops the last reference to `latefin`. |
| 280 | |
| 281 | latefin = LateFin() |
| 282 | def func(): |
| 283 | pass |
| 284 | cyc = tuple.__new__(Cyclic, (func, latefin)) |
| 285 | |
| 286 | # 1. Create a reference cycle of `cyc` and `func`. |
| 287 | func.__module__ = cyc |
| 288 | |
| 289 | # 2. Make the cycle unreachable, but keep the global reference |
| 290 | # to `latefin` so that it isn't detected as garbage. This |
| 291 | # way its finalizer will not be called immediately. |
| 292 | del func, cyc |
| 293 | |
| 294 | # 3. Invoke garbage collection, |
| 295 | # which will find `cyc` and `func` as garbage. |
| 296 | gc.collect() |
nothing calls this directly
no test coverage detected