1+ import copy
12import gc
23import operator
34import re
1314 _testcapi = None
1415
1516from test import support
16- from test .support import threading_helper , Py_GIL_DISABLED
17+ from test .support import import_helper , threading_helper , Py_GIL_DISABLED
1718from test .support .script_helper import assert_python_ok
1819
1920
@@ -198,14 +199,6 @@ def inner():
198199 tb = tb .tb_next
199200 return frames
200201
201- def test_locals (self ):
202- f , outer , inner = self .make_frames ()
203- outer_locals = outer .f_locals
204- self .assertIsInstance (outer_locals .pop ('inner' ), types .FunctionType )
205- self .assertEqual (outer_locals , {'x' : 5 , 'y' : 6 })
206- inner_locals = inner .f_locals
207- self .assertEqual (inner_locals , {'x' : 5 , 'z' : 7 })
208-
209202 def test_clear_locals (self ):
210203 # Test f_locals after clear() (issue #21897)
211204 f , outer , inner = self .make_frames ()
@@ -217,8 +210,8 @@ def test_clear_locals(self):
217210 def test_locals_clear_locals (self ):
218211 # Test f_locals before and after clear() (to exercise caching)
219212 f , outer , inner = self .make_frames ()
220- outer .f_locals
221- inner .f_locals
213+ self . assertNotEqual ( outer .f_locals , {})
214+ self . assertNotEqual ( inner .f_locals , {})
222215 outer .clear ()
223216 inner .clear ()
224217 self .assertEqual (outer .f_locals , {})
@@ -269,6 +262,177 @@ def inner():
269262 r"^<frame at 0x[0-9a-fA-F]+, file %s, line %d, code inner>$"
270263 % (file_repr , offset + 5 ))
271264
265+ class TestFrameLocals (unittest .TestCase ):
266+ def test_scope (self ):
267+ class A :
268+ x = 1
269+ sys ._getframe ().f_locals ['x' ] = 2
270+ sys ._getframe ().f_locals ['y' ] = 2
271+
272+ self .assertEqual (A .x , 2 )
273+ self .assertEqual (A .y , 2 )
274+
275+ def f ():
276+ x = 1
277+ sys ._getframe ().f_locals ['x' ] = 2
278+ sys ._getframe ().f_locals ['y' ] = 2
279+ self .assertEqual (x , 2 )
280+ self .assertEqual (locals ()['y' ], 2 )
281+ f ()
282+
283+ def test_closure (self ):
284+ x = 1
285+ y = 2
286+
287+ def f ():
288+ z = x + y
289+ d = sys ._getframe ().f_locals
290+ self .assertEqual (d ['x' ], 1 )
291+ self .assertEqual (d ['y' ], 2 )
292+ d ['x' ] = 2
293+ d ['y' ] = 3
294+
295+ f ()
296+ self .assertEqual (x , 2 )
297+ self .assertEqual (y , 3 )
298+
299+ def test_as_dict (self ):
300+ x = 1
301+ y = 2
302+ d = sys ._getframe ().f_locals
303+ # self, x, y, d
304+ self .assertEqual (len (d ), 4 )
305+ self .assertIs (d ['d' ], d )
306+ self .assertEqual (set (d .keys ()), set (['x' , 'y' , 'd' , 'self' ]))
307+ self .assertEqual (len (d .values ()), 4 )
308+ self .assertIn (1 , d .values ())
309+ self .assertEqual (len (d .items ()), 4 )
310+ self .assertIn (('x' , 1 ), d .items ())
311+ self .assertEqual (d .__getitem__ ('x' ), 1 )
312+ d .__setitem__ ('x' , 2 )
313+ self .assertEqual (d ['x' ], 2 )
314+ self .assertEqual (d .get ('x' ), 2 )
315+ self .assertIs (d .get ('non_exist' , None ), None )
316+ self .assertEqual (d .__len__ (), 4 )
317+ self .assertEqual (set ([key for key in d ]), set (['x' , 'y' , 'd' , 'self' ]))
318+ self .assertIn ('x' , d )
319+ self .assertTrue (d .__contains__ ('x' ))
320+
321+ self .assertEqual (reversed (d ), list (reversed (d .keys ())))
322+
323+ d .update ({'x' : 3 , 'z' : 4 })
324+ self .assertEqual (d ['x' ], 3 )
325+ self .assertEqual (d ['z' ], 4 )
326+
327+ with self .assertRaises (TypeError ):
328+ d .update ([1 , 2 ])
329+
330+ self .assertEqual (d .setdefault ('x' , 5 ), 3 )
331+ self .assertEqual (d .setdefault ('new' , 5 ), 5 )
332+ self .assertEqual (d ['new' ], 5 )
333+
334+ with self .assertRaises (KeyError ):
335+ d ['non_exist' ]
336+
337+ def test_as_number (self ):
338+ x = 1
339+ y = 2
340+ d = sys ._getframe ().f_locals
341+ self .assertIn ('z' , d | {'z' : 3 })
342+ d |= {'z' : 3 }
343+ self .assertEqual (d ['z' ], 3 )
344+ d |= {'y' : 3 }
345+ self .assertEqual (d ['y' ], 3 )
346+ with self .assertRaises (TypeError ):
347+ d |= 3
348+ with self .assertRaises (TypeError ):
349+ _ = d | [3 ]
350+
351+ def test_non_string_key (self ):
352+ d = sys ._getframe ().f_locals
353+ d [1 ] = 2
354+ self .assertEqual (d [1 ], 2 )
355+
356+ def test_write_with_hidden (self ):
357+ def f ():
358+ f_locals = [sys ._getframe ().f_locals for b in [0 ]][0 ]
359+ f_locals ['b' ] = 2
360+ f_locals ['c' ] = 3
361+ self .assertEqual (b , 2 )
362+ self .assertEqual (c , 3 )
363+ b = 0
364+ c = 0
365+ f ()
366+
367+ def test_repr (self ):
368+ x = 1
369+ # Introduce a reference cycle
370+ frame = sys ._getframe ()
371+ self .assertEqual (repr (frame .f_locals ), repr (dict (frame .f_locals )))
372+
373+ def test_delete (self ):
374+ x = 1
375+ d = sys ._getframe ().f_locals
376+ with self .assertRaises (TypeError ):
377+ del d ['x' ]
378+
379+ with self .assertRaises (AttributeError ):
380+ d .clear ()
381+
382+ with self .assertRaises (AttributeError ):
383+ d .pop ('x' )
384+
385+ @support .cpython_only
386+ def test_sizeof (self ):
387+ proxy = sys ._getframe ().f_locals
388+ support .check_sizeof (self , proxy , support .calcobjsize ("P" ))
389+
390+ def test_unsupport (self ):
391+ x = 1
392+ d = sys ._getframe ().f_locals
393+ with self .assertRaises (AttributeError ):
394+ d .copy ()
395+
396+ with self .assertRaises (TypeError ):
397+ copy .copy (d )
398+
399+ with self .assertRaises (TypeError ):
400+ copy .deepcopy (d )
401+
402+
403+ class TestFrameCApi (unittest .TestCase ):
404+ def test_basic (self ):
405+ x = 1
406+ ctypes = import_helper .import_module ('ctypes' )
407+ PyEval_GetFrameLocals = ctypes .pythonapi .PyEval_GetFrameLocals
408+ PyEval_GetFrameLocals .restype = ctypes .py_object
409+ frame_locals = PyEval_GetFrameLocals ()
410+ self .assertTrue (type (frame_locals ), dict )
411+ self .assertEqual (frame_locals ['x' ], 1 )
412+ frame_locals ['x' ] = 2
413+ self .assertEqual (x , 1 )
414+
415+ PyEval_GetFrameGlobals = ctypes .pythonapi .PyEval_GetFrameGlobals
416+ PyEval_GetFrameGlobals .restype = ctypes .py_object
417+ frame_globals = PyEval_GetFrameGlobals ()
418+ self .assertTrue (type (frame_globals ), dict )
419+ self .assertIs (frame_globals , globals ())
420+
421+ PyEval_GetFrameBuiltins = ctypes .pythonapi .PyEval_GetFrameBuiltins
422+ PyEval_GetFrameBuiltins .restype = ctypes .py_object
423+ frame_builtins = PyEval_GetFrameBuiltins ()
424+ self .assertEqual (frame_builtins , __builtins__ )
425+
426+ PyFrame_GetLocals = ctypes .pythonapi .PyFrame_GetLocals
427+ PyFrame_GetLocals .argtypes = [ctypes .py_object ]
428+ PyFrame_GetLocals .restype = ctypes .py_object
429+ frame = sys ._getframe ()
430+ f_locals = PyFrame_GetLocals (frame )
431+ self .assertTrue (f_locals ['x' ], 1 )
432+ f_locals ['x' ] = 2
433+ self .assertEqual (x , 2 )
434+
435+
272436class TestIncompleteFrameAreInvisible (unittest .TestCase ):
273437
274438 def test_issue95818 (self ):
0 commit comments