File tree Expand file tree Collapse file tree 1 file changed +40
-0
lines changed Expand file tree Collapse file tree 1 file changed +40
-0
lines changed Original file line number Diff line number Diff line change 2
2
import re
3
3
import sys
4
4
import textwrap
5
+ import threading
5
6
import types
6
7
import unittest
7
8
import weakref
@@ -325,6 +326,45 @@ def f():
325
326
if old_enabled :
326
327
gc .enable ()
327
328
329
+ @support .cpython_only
330
+ def test_sneaky_frame_object_teardown (self ):
331
+
332
+ class SneakyDel :
333
+ def __del__ (self ):
334
+ """
335
+ Stash a reference to the entire stack for walking later.
336
+
337
+ It may look crazy, but you'd be surprised how common this is
338
+ when using a test runner (like pytest). The typical recipe is:
339
+ ResourceWarning + -Werror + a custom sys.unraisablehook.
340
+ """
341
+ nonlocal sneaky_frame_object
342
+ sneaky_frame_object = sys ._getframe ()
343
+
344
+ class SneakyThread (threading .Thread ):
345
+ """
346
+ A separate thread isn't needed to make this code crash, but it does
347
+ make crashes more consistent, since it means sneaky_frame_object is
348
+ backed by freed memory after the thread completes!
349
+ """
350
+
351
+ def run (self ):
352
+ """Run SneakyDel.__del__ as this frame is popped."""
353
+ ref = SneakyDel ()
354
+
355
+ sneaky_frame_object = None
356
+ t = SneakyThread ()
357
+ t .start ()
358
+ t .join ()
359
+ # sneaky_frame_object can be anything, really, but it's crucial that
360
+ # SneakyThread.run's frame isn't anywhere on the stack while it's being
361
+ # torn down:
362
+ self .assertIsNotNone (sneaky_frame_object )
363
+ while sneaky_frame_object is not None :
364
+ self .assertIsNot (
365
+ sneaky_frame_object .f_code , SneakyThread .run .__code__
366
+ )
367
+ sneaky_frame_object = sneaky_frame_object .f_back
328
368
329
369
if __name__ == "__main__" :
330
370
unittest .main ()
You can’t perform that action at this time.
0 commit comments