Skip to content

Commit 154f14c

Browse files
committed
Test marshalling of the NaN constants the compiler can produce
1 parent b3dea86 commit 154f14c

1 file changed

Lines changed: 81 additions & 0 deletions

File tree

‎Lib/test/test_marshal.py‎

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1299,6 +1299,87 @@ def ref(index):
12991299
new = marshal.loads(expected)
13001300
self.check_same_structure(new, sample, True)
13011301

1302+
@support.cpython_only
1303+
def test_nan_constants_in_code(self):
1304+
# Python has no literal for NaN, but constant folding produces one
1305+
# from arithmetic on 1e999 (inf). Such NaNs end up as a constant
1306+
# of the code object, in a tuple constant, or in a frozenset
1307+
# constant (the only kind of set the compiler creates, from a set
1308+
# display used as the right operand of "in"). Every fold creates
1309+
# a new object, so the compiler cannot produce a NaN which is an
1310+
# element of a set and is also referenced elsewhere; the ordering
1311+
# of such elements (test_nan_in_set_order) is not observable here.
1312+
# What is observable is that the code can be marshalled, and that
1313+
# the output, so the .pyc file, is the same in every process.
1314+
sources = {
1315+
'const': 'x = 1e999 - 1e999',
1316+
'tuple': 'x = (1e999 - 1e999, 2.0)',
1317+
'repeat': 'x = (1e999 - 1e999,) * 2',
1318+
'set': 'x in {1e999 - 1e999, 1e999 - 1e999, 2.0}',
1319+
'complex': 'x in {(1e999 - 1e999) * 1j, (1e999 - 1e999) * 2j}',
1320+
'tuples': 'x in {(1e999 - 1e999,) * 2, (1e999 - 1e999,) * 2}',
1321+
'nested': 'def f(x):\n return x in {1e999 - 1e999, 2j}',
1322+
}
1323+
def nan_places(code):
1324+
# Where the NaN constants of a code object are, with the
1325+
# elements of sets in any order.
1326+
places = []
1327+
def visit(x, where):
1328+
if isinstance(x, (float, complex)):
1329+
if x != x:
1330+
places.append((where, repr(x)))
1331+
elif isinstance(x, (tuple, frozenset)):
1332+
for y in sorted(x, key=repr):
1333+
visit(y, type(x).__name__)
1334+
elif isinstance(x, types.CodeType):
1335+
for y in x.co_consts:
1336+
visit(y, 'consts')
1337+
visit(code, None)
1338+
return places
1339+
expected = {
1340+
'const': [('consts', 'nan')],
1341+
'tuple': [('tuple', 'nan')],
1342+
'repeat': [('tuple', 'nan'), ('tuple', 'nan')],
1343+
'set': [('frozenset', 'nan'), ('frozenset', 'nan')],
1344+
'complex': [('frozenset', '(nan+nanj)'),
1345+
('frozenset', '(nan+nanj)')],
1346+
'tuples': [('tuple', 'nan')] * 4,
1347+
'nested': [('frozenset', 'nan')],
1348+
}
1349+
dumps = {}
1350+
for name, source in sources.items():
1351+
code = compile(source, '<marshal test>', 'exec')
1352+
self.assertEqual(nan_places(code), expected[name], name)
1353+
for version in range(marshal.version + 1):
1354+
data = marshal.dumps(code, version)
1355+
dumps[name, version] = data
1356+
new = marshal.loads(data)
1357+
self.assertEqual(nan_places(new), expected[name], name)
1358+
self.assertEqual(new.co_code, code.co_code)
1359+
# Compiling again gives other NaN objects at other
1360+
# addresses, and the same bytes.
1361+
code2 = compile(source, '<marshal test>', 'exec')
1362+
self.assertEqual(marshal.dumps(code2, version), data)
1363+
1364+
# The same bytes in other processes.
1365+
script = textwrap.dedent("""
1366+
import marshal, sys
1367+
sources = %r
1368+
for name, source in sources.items():
1369+
code = compile(source, '<marshal test>', 'exec')
1370+
for version in range(marshal.version + 1):
1371+
print(name, version, marshal.dumps(code, version).hex())
1372+
""") % sources
1373+
for seed in '1', '2', 'random':
1374+
with self.subTest(hashseed=seed):
1375+
rc, out, err = assert_python_ok('-c', script,
1376+
PYTHONHASHSEED=seed)
1377+
for line in out.decode().splitlines():
1378+
name, version, data = line.split()
1379+
self.assertEqual(bytes.fromhex(data),
1380+
dumps[name, int(version)],
1381+
(name, version))
1382+
13021383
def check_same_structure(self, new, sample, shared):
13031384
# Both object graphs have the same values and, if shared is true,
13041385
# share the same objects. Elements of sets are matched in any

0 commit comments

Comments
 (0)