Skip to content

Commit b027839

Browse files
committed
Add frozendict support to _PyCode_ConstantKey
1 parent 37d8189 commit b027839

2 files changed

Lines changed: 71 additions & 0 deletions

File tree

Lib/test/test_compile.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -776,6 +776,13 @@ def check_constant(self, func, expected):
776776
self.fail("unable to find constant %r in %r"
777777
% (expected, func.__code__.co_consts))
778778

779+
@staticmethod
780+
def _frozen_dict_consts(*consts):
781+
"""Use AST to make frozendict constants since it has no literal syntax"""
782+
m = ast.Interactive([ast.Expr(ast.Constant(c)) for c in consts])
783+
ast.fix_missing_locations(m)
784+
return compile(m, "<test>", "single")
785+
779786
# Merging equal constants is not a strict requirement for the Python
780787
# semantics, it's a more an implementation detail.
781788
@support.cpython_only
@@ -820,6 +827,21 @@ def check_same_constant(const):
820827
self.check_constant(f1, frozenset({0}))
821828
self.assertTrue(f1(0))
822829

830+
# two identical frozendicts merge into one constant
831+
c = self._frozen_dict_consts(frozendict({0: 1}), frozendict({0: 1}))
832+
self.assertEqual(c.co_consts, (frozendict({0: 1}),))
833+
834+
# empty frozendicts also merge
835+
c = self._frozen_dict_consts(frozendict(), frozendict())
836+
self.assertEqual(c.co_consts, (frozendict(),))
837+
838+
# frozendicts containing a nested frozendict value merge
839+
c = self._frozen_dict_consts(
840+
frozendict({0: frozendict({1: 2})}),
841+
frozendict({0: frozendict({1: 2})}),
842+
)
843+
self.assertEqual(c.co_consts, (frozendict({0: frozendict({1: 2})}),))
844+
823845
# Merging equal co_linetable is not a strict requirement
824846
# for the Python semantics, it's a more an implementation detail.
825847
@support.cpython_only
@@ -1033,6 +1055,16 @@ def check_different_constants(const1, const2):
10331055
self.assertTrue(f1(0))
10341056
self.assertTrue(f2(0.0))
10351057

1058+
# frozendicts with type-distinct keys must not merge (0 vs 0.0)
1059+
c = self._frozen_dict_consts(frozendict({0: 1}), frozendict({0.0: 1}))
1060+
self.assertEqual(c.co_consts, (frozendict({0: 1}), frozendict({0.0: 1})))
1061+
self.assertIsNot(c.co_consts[0], c.co_consts[1])
1062+
1063+
# frozendicts with type-distinct values must not merge (1 vs 1.0)
1064+
c = self._frozen_dict_consts(frozendict({0: 1}), frozendict({0: 1.0}))
1065+
self.assertEqual(c.co_consts, (frozendict({0: 1}), frozendict({0: 1.0})))
1066+
self.assertIsNot(c.co_consts[0], c.co_consts[1])
1067+
10361068
def test_path_like_objects(self):
10371069
# An implicit test for PyUnicode_FSDecoder().
10381070
compile("42", FakePath("test_compile_pathlike"), "single")

Objects/codeobject.c

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3039,6 +3039,45 @@ _PyCode_ConstantKey(PyObject *op)
30393039
Py_DECREF(set);
30403040
return key;
30413041
}
3042+
else if (PyFrozenDict_CheckExact(op)) {
3043+
PyObject *dict = PyDict_New();
3044+
if (dict == NULL) {
3045+
return NULL;
3046+
}
3047+
3048+
Py_ssize_t pos = 0;
3049+
PyObject *k_obj, *v_obj;
3050+
while (PyDict_Next(op, &pos, &k_obj, &v_obj)) {
3051+
PyObject *k_key = _PyCode_ConstantKey(k_obj);
3052+
if (k_key == NULL) {
3053+
Py_DECREF(dict);
3054+
return NULL;
3055+
}
3056+
PyObject *v_key = _PyCode_ConstantKey(v_obj);
3057+
if (v_key == NULL) {
3058+
Py_DECREF(k_key);
3059+
Py_DECREF(dict);
3060+
return NULL;
3061+
}
3062+
int res = PyDict_SetItem(dict, k_key, v_key);
3063+
Py_DECREF(k_key);
3064+
Py_DECREF(v_key);
3065+
if (res < 0) {
3066+
Py_DECREF(dict);
3067+
return NULL;
3068+
}
3069+
}
3070+
3071+
PyObject *fdict = PyFrozenDict_New(dict);
3072+
Py_DECREF(dict);
3073+
if (fdict == NULL) {
3074+
return NULL;
3075+
}
3076+
3077+
key = _PyTuple_FromPair(fdict, op);
3078+
Py_DECREF(fdict);
3079+
return key;
3080+
}
30423081
else if (PySlice_Check(op)) {
30433082
PySliceObject *slice = (PySliceObject *)op;
30443083
PyObject *start_key = NULL;

0 commit comments

Comments
 (0)