@@ -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