From 60692889ae3a705720ca9f5e485925881c6eba48 Mon Sep 17 00:00:00 2001
From: ShaharNaveh <50263213+ShaharNaveh@users.noreply.github.com>
Date: Tue, 3 Mar 2026 09:32:51 +0100
Subject: [PATCH 1/3] Update `test_frame.py` from 3.14.3
---
Lib/test/test_frame.py | 800 +++++++++++++++++++++++++++++++++++++++++
1 file changed, 800 insertions(+)
create mode 100644 Lib/test/test_frame.py
diff --git a/Lib/test/test_frame.py b/Lib/test/test_frame.py
new file mode 100644
index 000000000..18ade18d1
--- /dev/null
+++ b/Lib/test/test_frame.py
@@ -0,0 +1,800 @@
+import copy
+import operator
+import re
+import sys
+import textwrap
+import threading
+import unittest
+import weakref
+try:
+ import _testcapi
+except ImportError:
+ _testcapi = None
+
+from collections.abc import Mapping
+from test import support
+from test.support import import_helper, threading_helper
+from test.support.script_helper import assert_python_ok
+from test import mapping_tests
+
+
+class ClearTest(unittest.TestCase):
+ """
+ Tests for frame.clear().
+ """
+
+ def inner(self, x=5, **kwargs):
+ 1/0
+
+ def outer(self, **kwargs):
+ try:
+ self.inner(**kwargs)
+ except ZeroDivisionError as e:
+ exc = e
+ return exc
+
+ def clear_traceback_frames(self, tb):
+ """
+ Clear all frames in a traceback.
+ """
+ while tb is not None:
+ tb.tb_frame.clear()
+ tb = tb.tb_next
+
+ def test_clear_locals(self):
+ class C:
+ pass
+ c = C()
+ wr = weakref.ref(c)
+ exc = self.outer(c=c)
+ del c
+ support.gc_collect()
+ # A reference to c is held through the frames
+ self.assertIsNot(None, wr())
+ self.clear_traceback_frames(exc.__traceback__)
+ support.gc_collect()
+ # The reference was released by .clear()
+ self.assertIs(None, wr())
+
+ def test_clear_locals_after_f_locals_access(self):
+ # see gh-113939
+ class C:
+ pass
+
+ wr = None
+ def inner():
+ nonlocal wr
+ c = C()
+ wr = weakref.ref(c)
+ 1/0
+
+ try:
+ inner()
+ except ZeroDivisionError as exc:
+ support.gc_collect()
+ self.assertIsNotNone(wr())
+ exc.__traceback__.tb_next.tb_frame.clear()
+ support.gc_collect()
+ self.assertIsNone(wr())
+
+ def test_clear_does_not_clear_specials(self):
+ class C:
+ pass
+ c = C()
+ exc = self.outer(c=c)
+ del c
+ f = exc.__traceback__.tb_frame
+ f.clear()
+ self.assertIsNot(f.f_code, None)
+ self.assertIsNot(f.f_locals, None)
+ self.assertIsNot(f.f_builtins, None)
+ self.assertIsNot(f.f_globals, None)
+
+ def test_clear_generator(self):
+ endly = False
+ def g():
+ nonlocal endly
+ try:
+ yield
+ self.inner()
+ finally:
+ endly = True
+ gen = g()
+ next(gen)
+ self.assertFalse(endly)
+
+ # Cannot clear a suspended frame
+ with self.assertRaisesRegex(RuntimeError, r'suspended frame'):
+ gen.gi_frame.clear()
+ self.assertFalse(endly)
+
+ def test_clear_executing(self):
+ # Attempting to clear an executing frame is forbidden.
+ try:
+ 1/0
+ except ZeroDivisionError as e:
+ f = e.__traceback__.tb_frame
+ with self.assertRaises(RuntimeError):
+ f.clear()
+ with self.assertRaises(RuntimeError):
+ f.f_back.clear()
+
+ def test_clear_executing_generator(self):
+ # Attempting to clear an executing generator frame is forbidden.
+ endly = False
+ def g():
+ nonlocal endly
+ try:
+ 1/0
+ except ZeroDivisionError as e:
+ f = e.__traceback__.tb_frame
+ with self.assertRaises(RuntimeError):
+ f.clear()
+ with self.assertRaises(RuntimeError):
+ f.f_back.clear()
+ yield f
+ finally:
+ endly = True
+ gen = g()
+ f = next(gen)
+ self.assertFalse(endly)
+ # Cannot clear a suspended frame
+ with self.assertRaisesRegex(RuntimeError, 'suspended frame'):
+ f.clear()
+ self.assertFalse(endly)
+
+ def test_lineno_with_tracing(self):
+ def record_line():
+ f = sys._getframe(1)
+ lines.append(f.f_lineno-f.f_code.co_firstlineno)
+
+ def test(trace):
+ record_line()
+ if trace:
+ sys._getframe(0).f_trace = True
+ record_line()
+ record_line()
+
+ expected_lines = [1, 4, 5]
+ lines = []
+ test(False)
+ self.assertEqual(lines, expected_lines)
+ lines = []
+ test(True)
+ self.assertEqual(lines, expected_lines)
+
+ @support.cpython_only
+ def test_clear_refcycles(self):
+ # .clear() doesn't leave any refcycle behind
+ with support.disable_gc():
+ class C:
+ pass
+ c = C()
+ wr = weakref.ref(c)
+ exc = self.outer(c=c)
+ del c
+ self.assertIsNot(None, wr())
+ self.clear_traceback_frames(exc.__traceback__)
+ self.assertIs(None, wr())
+
+
+class FrameAttrsTest(unittest.TestCase):
+
+ def make_frames(self):
+ def outer():
+ x = 5
+ y = 6
+ def inner():
+ z = x + 2
+ 1/0
+ t = 9
+ return inner()
+ try:
+ outer()
+ except ZeroDivisionError as e:
+ tb = e.__traceback__
+ frames = []
+ while tb:
+ frames.append(tb.tb_frame)
+ tb = tb.tb_next
+ return frames
+
+ def test_clear_locals(self):
+ # Test f_locals after clear() (issue #21897)
+ f, outer, inner = self.make_frames()
+ outer.clear()
+ inner.clear()
+ self.assertEqual(outer.f_locals, {})
+ self.assertEqual(inner.f_locals, {})
+
+ def test_locals_clear_locals(self):
+ # Test f_locals before and after clear() (to exercise caching)
+ f, outer, inner = self.make_frames()
+ self.assertNotEqual(outer.f_locals, {})
+ self.assertNotEqual(inner.f_locals, {})
+ outer.clear()
+ inner.clear()
+ self.assertEqual(outer.f_locals, {})
+ self.assertEqual(inner.f_locals, {})
+
+ def test_f_lineno_del_segfault(self):
+ f, _, _ = self.make_frames()
+ with self.assertRaises(AttributeError):
+ del f.f_lineno
+
+ def test_f_generator(self):
+ # Test f_generator in different contexts.
+
+ def t0():
+ def nested():
+ frame = sys._getframe()
+ return frame.f_generator
+
+ def gen():
+ yield nested()
+
+ g = gen()
+ try:
+ return next(g)
+ finally:
+ g.close()
+
+ def t1():
+ frame = sys._getframe()
+ return frame.f_generator
+
+ def t2():
+ frame = sys._getframe()
+ yield frame.f_generator
+
+ async def t3():
+ frame = sys._getframe()
+ return frame.f_generator
+
+ # For regular functions f_generator is None
+ self.assertIsNone(t0())
+ self.assertIsNone(t1())
+
+ # For generators f_generator is equal to self
+ g = t2()
+ try:
+ frame_g = next(g)
+ self.assertIs(g, frame_g)
+ finally:
+ g.close()
+
+ # Ditto for coroutines
+ c = t3()
+ try:
+ c.send(None)
+ except StopIteration as ex:
+ self.assertIs(ex.value, c)
+ else:
+ raise AssertionError('coroutine did not exit')
+
+
+class ReprTest(unittest.TestCase):
+ """
+ Tests for repr(frame).
+ """
+
+ def test_repr(self):
+ def outer():
+ x = 5
+ y = 6
+ def inner():
+ z = x + 2
+ 1/0
+ t = 9
+ return inner()
+
+ offset = outer.__code__.co_firstlineno
+ try:
+ outer()
+ except ZeroDivisionError as e:
+ tb = e.__traceback__
+ frames = []
+ while tb:
+ frames.append(tb.tb_frame)
+ tb = tb.tb_next
+ else:
+ self.fail("should have raised")
+
+ f_this, f_outer, f_inner = frames
+ file_repr = re.escape(repr(__file__))
+ self.assertRegex(repr(f_this),
+ r"^$"
+ % (file_repr, offset + 23))
+ self.assertRegex(repr(f_outer),
+ r"^$"
+ % (file_repr, offset + 7))
+ self.assertRegex(repr(f_inner),
+ r"^$"
+ % (file_repr, offset + 5))
+
+class TestFrameLocals(unittest.TestCase):
+ def test_scope(self):
+ class A:
+ x = 1
+ sys._getframe().f_locals['x'] = 2
+ sys._getframe().f_locals['y'] = 2
+
+ self.assertEqual(A.x, 2)
+ self.assertEqual(A.y, 2)
+
+ def f():
+ x = 1
+ sys._getframe().f_locals['x'] = 2
+ sys._getframe().f_locals['y'] = 2
+ self.assertEqual(x, 2)
+ self.assertEqual(locals()['y'], 2)
+ f()
+
+ def test_closure(self):
+ x = 1
+ y = 2
+
+ def f():
+ z = x + y
+ d = sys._getframe().f_locals
+ self.assertEqual(d['x'], 1)
+ self.assertEqual(d['y'], 2)
+ d['x'] = 2
+ d['y'] = 3
+
+ f()
+ self.assertEqual(x, 2)
+ self.assertEqual(y, 3)
+
+ def test_closure_with_inline_comprehension(self):
+ lambda: k
+ k = 1
+ lst = [locals() for k in [0]]
+ self.assertEqual(lst[0]['k'], 0)
+
+ def test_as_dict(self):
+ x = 1
+ y = 2
+ d = sys._getframe().f_locals
+ # self, x, y, d
+ self.assertEqual(len(d), 4)
+ self.assertIs(d['d'], d)
+ self.assertEqual(set(d.keys()), set(['x', 'y', 'd', 'self']))
+ self.assertEqual(len(d.values()), 4)
+ self.assertIn(1, d.values())
+ self.assertEqual(len(d.items()), 4)
+ self.assertIn(('x', 1), d.items())
+ self.assertEqual(d.__getitem__('x'), 1)
+ d.__setitem__('x', 2)
+ self.assertEqual(d['x'], 2)
+ self.assertEqual(d.get('x'), 2)
+ self.assertIs(d.get('non_exist', None), None)
+ self.assertEqual(d.__len__(), 4)
+ self.assertEqual(set([key for key in d]), set(['x', 'y', 'd', 'self']))
+ self.assertIn('x', d)
+ self.assertTrue(d.__contains__('x'))
+
+ self.assertEqual(reversed(d), list(reversed(d.keys())))
+
+ d.update({'x': 3, 'z': 4})
+ self.assertEqual(d['x'], 3)
+ self.assertEqual(d['z'], 4)
+
+ with self.assertRaises(TypeError):
+ d.update([1, 2])
+
+ self.assertEqual(d.setdefault('x', 5), 3)
+ self.assertEqual(d.setdefault('new', 5), 5)
+ self.assertEqual(d['new'], 5)
+
+ with self.assertRaises(KeyError):
+ d['non_exist']
+
+ def test_as_number(self):
+ x = 1
+ y = 2
+ d = sys._getframe().f_locals
+ self.assertIn('z', d | {'z': 3})
+ d |= {'z': 3}
+ self.assertEqual(d['z'], 3)
+ d |= {'y': 3}
+ self.assertEqual(d['y'], 3)
+ with self.assertRaises(TypeError):
+ d |= 3
+ with self.assertRaises(TypeError):
+ _ = d | [3]
+
+ def test_non_string_key(self):
+ d = sys._getframe().f_locals
+ d[1] = 2
+ self.assertEqual(d[1], 2)
+
+ def test_write_with_hidden(self):
+ def f():
+ f_locals = [sys._getframe().f_locals for b in [0]][0]
+ f_locals['b'] = 2
+ f_locals['c'] = 3
+ self.assertEqual(b, 2)
+ self.assertEqual(c, 3)
+ b = 0
+ c = 0
+ f()
+
+ def test_local_objects(self):
+ o = object()
+ k = '.'.join(['a', 'b', 'c'])
+ f_locals = sys._getframe().f_locals
+ f_locals['o'] = f_locals['k']
+ self.assertEqual(o, 'a.b.c')
+
+ def test_copy(self):
+ x = 0
+ d = sys._getframe().f_locals
+ d_copy = d.copy()
+ self.assertIsInstance(d_copy, dict)
+ self.assertEqual(d_copy['x'], 0)
+ d_copy['x'] = 1
+ self.assertEqual(x, 0)
+
+ def test_update_with_self(self):
+ def f():
+ f_locals = sys._getframe().f_locals
+ f_locals.update(f_locals)
+ f_locals.update(f_locals)
+ f_locals.update(f_locals)
+ f()
+
+ def test_repr(self):
+ x = 1
+ # Introduce a reference cycle
+ frame = sys._getframe()
+ self.assertEqual(repr(frame.f_locals), repr(dict(frame.f_locals)))
+
+ def test_delete(self):
+ x = 1
+ d = sys._getframe().f_locals
+
+ # This needs to be tested before f_extra_locals is created
+ with self.assertRaisesRegex(KeyError, 'non_exist'):
+ del d['non_exist']
+
+ with self.assertRaises(KeyError):
+ d.pop('non_exist')
+
+ with self.assertRaisesRegex(ValueError, 'local variables'):
+ del d['x']
+
+ with self.assertRaises(AttributeError):
+ d.clear()
+
+ with self.assertRaises(ValueError):
+ d.pop('x')
+
+ with self.assertRaises(ValueError):
+ d.pop('x', None)
+
+ # 'm', 'n' is stored in f_extra_locals
+ d['m'] = 1
+ d['n'] = 1
+
+ with self.assertRaises(KeyError):
+ d.pop('non_exist')
+
+ del d['m']
+ self.assertEqual(d.pop('n'), 1)
+
+ self.assertNotIn('m', d)
+ self.assertNotIn('n', d)
+
+ self.assertEqual(d.pop('n', 2), 2)
+
+ @support.cpython_only
+ def test_sizeof(self):
+ proxy = sys._getframe().f_locals
+ support.check_sizeof(self, proxy, support.calcobjsize("P"))
+
+ def test_unsupport(self):
+ x = 1
+ d = sys._getframe().f_locals
+ with self.assertRaises(TypeError):
+ copy.copy(d)
+
+ with self.assertRaises(TypeError):
+ copy.deepcopy(d)
+
+ def test_is_mapping(self):
+ x = 1
+ d = sys._getframe().f_locals
+ self.assertIsInstance(d, Mapping)
+ match d:
+ case {"x": value}:
+ self.assertEqual(value, 1)
+ kind = "mapping"
+ case _:
+ kind = "other"
+ self.assertEqual(kind, "mapping")
+
+ def _x_stringlikes(self):
+ class StringSubclass(str):
+ pass
+
+ class ImpostorX:
+ def __hash__(self):
+ return hash('x')
+
+ def __eq__(self, other):
+ return other == 'x'
+
+ return StringSubclass('x'), ImpostorX(), 'x'
+
+ def test_proxy_key_stringlikes_overwrite(self):
+ def f(obj):
+ x = 1
+ proxy = sys._getframe().f_locals
+ proxy[obj] = 2
+ return (
+ list(proxy.keys()),
+ dict(proxy),
+ proxy
+ )
+
+ for obj in self._x_stringlikes():
+ with self.subTest(cls=type(obj).__name__):
+
+ keys_snapshot, proxy_snapshot, proxy = f(obj)
+ expected_keys = ['obj', 'x', 'proxy']
+ expected_dict = {'obj': 'x', 'x': 2, 'proxy': proxy}
+ self.assertEqual(proxy.keys(), expected_keys)
+ self.assertEqual(proxy, expected_dict)
+ self.assertEqual(keys_snapshot, expected_keys)
+ self.assertEqual(proxy_snapshot, expected_dict)
+
+ def test_proxy_key_stringlikes_ftrst_write(self):
+ def f(obj):
+ proxy = sys._getframe().f_locals
+ proxy[obj] = 2
+ self.assertEqual(x, 2)
+ x = 1
+
+ for obj in self._x_stringlikes():
+ with self.subTest(cls=type(obj).__name__):
+ f(obj)
+
+ def test_proxy_key_unhashables(self):
+ class StringSubclass(str):
+ __hash__ = None
+
+ class ObjectSubclass:
+ __hash__ = None
+
+ proxy = sys._getframe().f_locals
+
+ for obj in StringSubclass('x'), ObjectSubclass():
+ with self.subTest(cls=type(obj).__name__):
+ with self.assertRaises(TypeError):
+ proxy[obj]
+ with self.assertRaises(TypeError):
+ proxy[obj] = 0
+
+ def test_constructor(self):
+ FrameLocalsProxy = type([sys._getframe().f_locals
+ for x in range(1)][0])
+ self.assertEqual(FrameLocalsProxy.__name__, 'FrameLocalsProxy')
+
+ def make_frame():
+ x = 1
+ y = 2
+ return sys._getframe()
+
+ proxy = FrameLocalsProxy(make_frame())
+ self.assertEqual(proxy, {'x': 1, 'y': 2})
+
+ # constructor expects 1 frame argument
+ with self.assertRaises(TypeError):
+ FrameLocalsProxy() # no arguments
+ with self.assertRaises(TypeError):
+ FrameLocalsProxy(123) # wrong type
+ with self.assertRaises(TypeError):
+ FrameLocalsProxy(frame=sys._getframe()) # no keyword arguments
+
+ def test_overwrite_locals(self):
+ # Verify we do not crash if we overwrite a local passed as an argument
+ # from an ancestor in the call stack.
+ def f():
+ xs = [1, 2, 3]
+ ys = [4, 5, 6]
+ return g(xs)
+
+ def g(xs):
+ f = sys._getframe()
+ f.f_back.f_locals["xs"] = None
+ f.f_back.f_locals["ys"] = None
+ return xs[1]
+
+ self.assertEqual(f(), 2)
+
+
+class FrameLocalsProxyMappingTests(mapping_tests.TestHashMappingProtocol):
+ """Test that FrameLocalsProxy behaves like a Mapping (with exceptions)"""
+
+ def _f(*args, **kwargs):
+ def _f():
+ return sys._getframe().f_locals
+ return _f()
+ type2test = _f
+
+ @unittest.skipIf(True, 'Locals proxies for different frames never compare as equal')
+ def test_constructor(self):
+ pass
+
+ @unittest.skipIf(True, 'Unlike a mapping: del proxy[key] fails')
+ def test_write(self):
+ pass
+
+ @unittest.skipIf(True, 'Unlike a mapping: no proxy.popitem')
+ def test_popitem(self):
+ pass
+
+ @unittest.skipIf(True, 'Unlike a mapping: no proxy.pop')
+ def test_pop(self):
+ pass
+
+ @unittest.skipIf(True, 'Unlike a mapping: no proxy.clear')
+ def test_clear(self):
+ pass
+
+ @unittest.skipIf(True, 'Unlike a mapping: no proxy.fromkeys')
+ def test_fromkeys(self):
+ pass
+
+ # no del
+ def test_getitem(self):
+ mapping_tests.BasicTestMappingProtocol.test_getitem(self)
+ d = self._full_mapping({'a': 1, 'b': 2})
+ self.assertEqual(d['a'], 1)
+ self.assertEqual(d['b'], 2)
+ d['c'] = 3
+ d['a'] = 4
+ self.assertEqual(d['c'], 3)
+ self.assertEqual(d['a'], 4)
+
+ @unittest.skipIf(True, 'Unlike a mapping: no proxy.update')
+ def test_update(self):
+ pass
+
+ # proxy.copy returns a regular dict
+ def test_copy(self):
+ d = self._full_mapping({1:1, 2:2, 3:3})
+ self.assertEqual(d.copy(), {1:1, 2:2, 3:3})
+ d = self._empty_mapping()
+ self.assertEqual(d.copy(), d)
+ self.assertRaises(TypeError, d.copy, None)
+
+ self.assertIsInstance(d.copy(), dict)
+
+ @unittest.skipIf(True, 'Locals proxies for different frames never compare as equal')
+ def test_eq(self):
+ pass
+
+
+class TestFrameCApi(unittest.TestCase):
+ def test_basic(self):
+ x = 1
+ ctypes = import_helper.import_module('ctypes')
+ PyEval_GetFrameLocals = ctypes.pythonapi.PyEval_GetFrameLocals
+ PyEval_GetFrameLocals.restype = ctypes.py_object
+ frame_locals = PyEval_GetFrameLocals()
+ self.assertTrue(type(frame_locals), dict)
+ self.assertEqual(frame_locals['x'], 1)
+ frame_locals['x'] = 2
+ self.assertEqual(x, 1)
+
+ PyEval_GetFrameGlobals = ctypes.pythonapi.PyEval_GetFrameGlobals
+ PyEval_GetFrameGlobals.restype = ctypes.py_object
+ frame_globals = PyEval_GetFrameGlobals()
+ self.assertTrue(type(frame_globals), dict)
+ self.assertIs(frame_globals, globals())
+
+ PyEval_GetFrameBuiltins = ctypes.pythonapi.PyEval_GetFrameBuiltins
+ PyEval_GetFrameBuiltins.restype = ctypes.py_object
+ frame_builtins = PyEval_GetFrameBuiltins()
+ self.assertEqual(frame_builtins, __builtins__)
+
+ PyFrame_GetLocals = ctypes.pythonapi.PyFrame_GetLocals
+ PyFrame_GetLocals.argtypes = [ctypes.py_object]
+ PyFrame_GetLocals.restype = ctypes.py_object
+ frame = sys._getframe()
+ f_locals = PyFrame_GetLocals(frame)
+ self.assertTrue(f_locals['x'], 1)
+ f_locals['x'] = 2
+ self.assertEqual(x, 2)
+
+
+class TestIncompleteFrameAreInvisible(unittest.TestCase):
+
+ def test_issue95818(self):
+ # See GH-95818 for details
+ code = textwrap.dedent(f"""
+ import gc
+
+ gc.set_threshold(1,1,1)
+ class GCHello:
+ def __del__(self):
+ print("Destroyed from gc")
+
+ def gen():
+ yield
+
+ fd = open({__file__!r})
+ l = [fd, GCHello()]
+ l.append(l)
+ del fd
+ del l
+ gen()
+ """)
+ assert_python_ok("-c", code)
+
+ @support.cpython_only
+ @threading_helper.requires_working_threading()
+ def test_sneaky_frame_object_teardown(self):
+
+ class SneakyDel:
+ def __del__(self):
+ """
+ Stash a reference to the entire stack for walking later.
+
+ It may look crazy, but you'd be surprised how common this is
+ when using a test runner (like pytest). The typical recipe is:
+ ResourceWarning + -Werror + a custom sys.unraisablehook.
+ """
+ nonlocal sneaky_frame_object
+ sneaky_frame_object = sys._getframe()
+
+ class SneakyThread(threading.Thread):
+ """
+ A separate thread isn't needed to make this code crash, but it does
+ make crashes more consistent, since it means sneaky_frame_object is
+ backed by freed memory after the thread completes!
+ """
+
+ def run(self):
+ """Run SneakyDel.__del__ as this frame is popped."""
+ ref = SneakyDel()
+
+ sneaky_frame_object = None
+ t = SneakyThread()
+ t.start()
+ t.join()
+ # sneaky_frame_object can be anything, really, but it's crucial that
+ # SneakyThread.run's frame isn't anywhere on the stack while it's being
+ # torn down:
+ self.assertIsNotNone(sneaky_frame_object)
+ while sneaky_frame_object is not None:
+ self.assertIsNot(
+ sneaky_frame_object.f_code, SneakyThread.run.__code__
+ )
+ sneaky_frame_object = sneaky_frame_object.f_back
+
+ def test_entry_frames_are_invisible_during_teardown(self):
+ class C:
+ """A weakref'able class."""
+
+ def f():
+ """Try to find globals and locals as this frame is being cleared."""
+ ref = C()
+ # Ignore the fact that exec(C()) is a nonsense callback. We're only
+ # using exec here because it tries to access the current frame's
+ # globals and locals. If it's trying to get those from a shim frame,
+ # we'll crash before raising:
+ return weakref.ref(ref, exec)
+
+ with support.catch_unraisable_exception() as catcher:
+ # Call from C, so there is a shim frame directly above f:
+ weak = operator.call(f) # BOOM!
+ # Cool, we didn't crash. Check that the callback actually happened:
+ self.assertIs(catcher.unraisable.exc_type, TypeError)
+ self.assertIsNone(weak())
+
+
+if __name__ == "__main__":
+ unittest.main()
From b1314b2bef116aea65e095910adbf7620e0ff60b Mon Sep 17 00:00:00 2001
From: ShaharNaveh <50263213+ShaharNaveh@users.noreply.github.com>
Date: Tue, 3 Mar 2026 13:20:00 +0100
Subject: [PATCH 2/3] Mark failing tests
---
Lib/test/test_frame.py | 13 +++++++++++++
1 file changed, 13 insertions(+)
diff --git a/Lib/test/test_frame.py b/Lib/test/test_frame.py
index 18ade18d1..5198d24b6 100644
--- a/Lib/test/test_frame.py
+++ b/Lib/test/test_frame.py
@@ -217,6 +217,7 @@ class FrameAttrsTest(unittest.TestCase):
self.assertEqual(outer.f_locals, {})
self.assertEqual(inner.f_locals, {})
+ @unittest.expectedFailure # TODO: RUSTPYTHON; TypeError: can't delete f_lineno attribute
def test_f_lineno_del_segfault(self):
f, _, _ = self.make_frames()
with self.assertRaises(AttributeError):
@@ -278,6 +279,7 @@ class ReprTest(unittest.TestCase):
Tests for repr(frame).
"""
+ @unittest.expectedFailure # TODO: RUSTPYTHON
def test_repr(self):
def outer():
x = 5
@@ -313,6 +315,7 @@ class ReprTest(unittest.TestCase):
% (file_repr, offset + 5))
class TestFrameLocals(unittest.TestCase):
+ @unittest.expectedFailure # TODO: RUSTPYTHON
def test_scope(self):
class A:
x = 1
@@ -330,6 +333,7 @@ class TestFrameLocals(unittest.TestCase):
self.assertEqual(locals()['y'], 2)
f()
+ @unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: 1 != 2
def test_closure(self):
x = 1
y = 2
@@ -352,6 +356,7 @@ class TestFrameLocals(unittest.TestCase):
lst = [locals() for k in [0]]
self.assertEqual(lst[0]['k'], 0)
+ @unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: 3 != 4
def test_as_dict(self):
x = 1
y = 2
@@ -409,6 +414,7 @@ class TestFrameLocals(unittest.TestCase):
d[1] = 2
self.assertEqual(d[1], 2)
+ @unittest.expectedFailure # TODO: RUSTPYTHON; UnboundLocalError: local variable 'b' referenced before assignment
def test_write_with_hidden(self):
def f():
f_locals = [sys._getframe().f_locals for b in [0]][0]
@@ -420,6 +426,7 @@ class TestFrameLocals(unittest.TestCase):
c = 0
f()
+ @unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: