@@ -2085,13 +2085,16 @@ def describe(obj):
20852085def compile_and_describe (source , use_ast ):
20862086 """Compile source; return the code object and what depends on identity.
20872087
2088- If use_ast is true, compile an AST in which the set displays are
2089- frozenset constants.
2088+ If use_ast is true, compile an AST in which the set displays of
2089+ constants are frozenset constants.
20902090 """
20912091 if use_ast :
20922092 class Transformer (ast .NodeTransformer ):
20932093 def visit_Set (self , node ):
2094- value = frozenset (ast .literal_eval (node ))
2094+ try :
2095+ value = frozenset (ast .literal_eval (node ))
2096+ except ValueError :
2097+ return node
20952098 return ast .copy_location (ast .Constant (value ), node )
20962099 source = Transformer ().visit (ast .parse (source ))
20972100 # The marshal format also depends on reference counts (gh-156504),
@@ -2158,10 +2161,6 @@ class Case:
21582161 # and the number of distinct objects among them.
21592162 frozensets_and_slices : int = 0
21602163 distinct : int = 0
2161- # Compile an AST with frozenset constants. In the free-threaded
2162- # build, the constants of code compiled from an AST are also
2163- # interned.
2164- use_ast : bool = False
21652164
21662165 @dataclasses .dataclass (frozen = True )
21672166 class Job :
@@ -2351,30 +2350,24 @@ def f(self):
23512350 self.ID_a = 1
23522351 """ ,
23532352 strings = ['ID_a' ]),
2354- 'ast_constants' : Case (
2355- source = """
2356- def f(x):
2357- return "f", x in {"ID_a", ("ID_b", "ID_c")}
2358- def g(x):
2359- return "g", x in {"ID_a", ("ID_b", "ID_c")}
2360- """ ,
2361- strings = ['ID_a' , 'ID_b' , 'ID_c' ],
2362- frozensets_and_slices = 2 , distinct = 1 ,
2363- use_ast = True ),
23642353 }
23652354
23662355 @classmethod
23672356 def jobs (cls , tag ):
2368- # A Job for each of the cases. The prefix depends on tag: a
2369- # process compiles a source with given strings only once.
2357+ # Two Jobs for each of the cases: compiled from the source and
2358+ # from an AST. The prefix depends on tag and on use_ast: a process
2359+ # compiles a source with given strings only once.
23702360 jobs = []
23712361 for name , case in cls .CASES .items ():
2372- prefix = f'gh129724{ tag } _{ name } '
2373- jobs .append (cls .Job (
2374- key = f'{ tag } :{ name } ' ,
2375- source = textwrap .dedent (case .source ).replace ('ID' , prefix ),
2376- strings = [s .replace ('ID' , prefix ) for s in case .strings ],
2377- use_ast = case .use_ast ))
2362+ source = textwrap .dedent (case .source )
2363+ for use_ast in False , True :
2364+ kind = 'ast' if use_ast else 'str'
2365+ prefix = f'gh129724{ tag } { kind } _{ name } '
2366+ jobs .append (cls .Job (
2367+ key = f'{ tag } :{ kind } :{ name } ' ,
2368+ source = source .replace ('ID' , prefix ),
2369+ strings = [s .replace ('ID' , prefix ) for s in case .strings ],
2370+ use_ast = use_ast ))
23782371 return jobs
23792372
23802373 @classmethod
@@ -2439,9 +2432,11 @@ def check_child(self, mode):
24392432 def test_fresh (self ):
24402433 # equal constants are the same object
24412434 for name , case in self .CASES .items ():
2442- with self .subTest (name ):
2443- self .assertEqual (self .fresh [f'c:{ name } ' ]['shared' ],
2444- [case .frozensets_and_slices , case .distinct ])
2435+ expected = [case .frozensets_and_slices , case .distinct ]
2436+ for kind in 'str' , 'ast' :
2437+ with self .subTest (name , kind = kind ):
2438+ self .assertEqual (self .fresh [f'c:{ kind } :{ name } ' ]['shared' ],
2439+ expected )
24452440
24462441 def test_interned (self ):
24472442 self .check_child ('interned' )
@@ -2478,6 +2473,22 @@ def test_same_process_interned(self):
24782473 code , result = compile_and_describe (job .source , job .use_ast )
24792474 self .check (job .key , result )
24802475
2476+ @support .cpython_only
2477+ @unittest .skipUnless (support .Py_GIL_DISABLED ,
2478+ 'requires the free-threaded build' )
2479+ def test_constants_not_immortal (self ):
2480+ # compile() does not replace the constants by the immortal ones
2481+ # of the code objects which were created before.
2482+ source = 'x = (129724.5, 129724.25)'
2483+ exec (source , {})
2484+ for use_ast in False , True :
2485+ with self .subTest (use_ast = use_ast ):
2486+ code = compile (ast .parse (source ) if use_ast else source ,
2487+ '<test>' , 'exec' )
2488+ const , = [const for const in code .co_consts
2489+ if isinstance (const , tuple )]
2490+ self .assertFalse (sys ._is_immortal (const ))
2491+
24812492 def test_names_tuples (self ):
24822493 # Code objects with equal names share one tuple, and a constant
24832494 # equal to the names is that same tuple, whatever was compiled
0 commit comments