@@ -2104,13 +2104,16 @@ def describe(obj):
21042104def compile_and_describe (source , use_ast ):
21052105 """Compile source; return the code object and what depends on identity.
21062106
2107- If use_ast is true, compile an AST in which the set displays are
2108- frozenset constants.
2107+ If use_ast is true, compile an AST in which the set displays of
2108+ constants are frozenset constants.
21092109 """
21102110 if use_ast :
21112111 class Transformer (ast .NodeTransformer ):
21122112 def visit_Set (self , node ):
2113- value = frozenset (ast .literal_eval (node ))
2113+ try :
2114+ value = frozenset (ast .literal_eval (node ))
2115+ except ValueError :
2116+ return node
21142117 return ast .copy_location (ast .Constant (value ), node )
21152118 source = Transformer ().visit (ast .parse (source ))
21162119 # The marshal format also depends on reference counts (gh-156504),
@@ -2177,10 +2180,6 @@ class Case:
21772180 # and the number of distinct objects among them.
21782181 frozensets_and_slices : int = 0
21792182 distinct : int = 0
2180- # Compile an AST with frozenset constants. In the free-threaded
2181- # build, the constants of code compiled from an AST are also
2182- # interned.
2183- use_ast : bool = False
21842183
21852184 @dataclasses .dataclass (frozen = True )
21862185 class Job :
@@ -2370,30 +2369,24 @@ def f(self):
23702369 self.ID_a = 1
23712370 """ ,
23722371 strings = ['ID_a' ]),
2373- 'ast_constants' : Case (
2374- source = """
2375- def f(x):
2376- return "f", x in {"ID_a", ("ID_b", "ID_c")}
2377- def g(x):
2378- return "g", x in {"ID_a", ("ID_b", "ID_c")}
2379- """ ,
2380- strings = ['ID_a' , 'ID_b' , 'ID_c' ],
2381- frozensets_and_slices = 2 , distinct = 1 ,
2382- use_ast = True ),
23832372 }
23842373
23852374 @classmethod
23862375 def jobs (cls , tag ):
2387- # A Job for each of the cases. The prefix depends on tag: a
2388- # process compiles a source with given strings only once.
2376+ # Two Jobs for each of the cases: compiled from the source and
2377+ # from an AST. The prefix depends on tag and on use_ast: a process
2378+ # compiles a source with given strings only once.
23892379 jobs = []
23902380 for name , case in cls .CASES .items ():
2391- prefix = f'gh129724{ tag } _{ name } '
2392- jobs .append (cls .Job (
2393- key = f'{ tag } :{ name } ' ,
2394- source = textwrap .dedent (case .source ).replace ('ID' , prefix ),
2395- strings = [s .replace ('ID' , prefix ) for s in case .strings ],
2396- use_ast = case .use_ast ))
2381+ source = textwrap .dedent (case .source )
2382+ for use_ast in False , True :
2383+ kind = 'ast' if use_ast else 'str'
2384+ prefix = f'gh129724{ tag } { kind } _{ name } '
2385+ jobs .append (cls .Job (
2386+ key = f'{ tag } :{ kind } :{ name } ' ,
2387+ source = source .replace ('ID' , prefix ),
2388+ strings = [s .replace ('ID' , prefix ) for s in case .strings ],
2389+ use_ast = use_ast ))
23972390 return jobs
23982391
23992392 @classmethod
@@ -2458,9 +2451,11 @@ def check_child(self, mode):
24582451 def test_fresh (self ):
24592452 # equal constants are the same object
24602453 for name , case in self .CASES .items ():
2461- with self .subTest (name ):
2462- self .assertEqual (self .fresh [f'c:{ name } ' ]['shared' ],
2463- [case .frozensets_and_slices , case .distinct ])
2454+ expected = [case .frozensets_and_slices , case .distinct ]
2455+ for kind in 'str' , 'ast' :
2456+ with self .subTest (name , kind = kind ):
2457+ self .assertEqual (self .fresh [f'c:{ kind } :{ name } ' ]['shared' ],
2458+ expected )
24642459
24652460 def test_interned (self ):
24662461 self .check_child ('interned' )
@@ -2497,6 +2492,22 @@ def test_same_process_interned(self):
24972492 code , result = compile_and_describe (job .source , job .use_ast )
24982493 self .check (job .key , result )
24992494
2495+ @support .cpython_only
2496+ @unittest .skipUnless (support .Py_GIL_DISABLED ,
2497+ 'requires the free-threaded build' )
2498+ def test_constants_not_immortal (self ):
2499+ # compile() does not replace the constants by the immortal ones
2500+ # of the code objects which were created before.
2501+ source = 'x = (129724.5, 129724.25)'
2502+ exec (source , {})
2503+ for use_ast in False , True :
2504+ with self .subTest (use_ast = use_ast ):
2505+ code = compile (ast .parse (source ) if use_ast else source ,
2506+ '<test>' , 'exec' )
2507+ const , = [const for const in code .co_consts
2508+ if isinstance (const , tuple )]
2509+ self .assertFalse (sys ._is_immortal (const ))
2510+
25002511 def test_names_tuples (self ):
25012512 # Code objects with equal names share one tuple, and a constant
25022513 # equal to the names is that same tuple, whatever was compiled
0 commit comments