Skip to content

Commit 69b8521

Browse files
Add frozendict support to str.maketrans() and type()
1 parent 5944a53 commit 69b8521

5 files changed

Lines changed: 38 additions & 6 deletions

File tree

Lib/test/test_builtin.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2993,6 +2993,12 @@ def test_type_doc(self):
29932993
A.__doc__ = doc
29942994
self.assertEqual(A.__doc__, doc)
29952995

2996+
def test_type_frozendict(self):
2997+
A = type('A', (), frozendict({'x': 4, 'y': 2}))
2998+
self.assertEqual(A.x, 4)
2999+
self.assertEqual(A.y, 2)
3000+
self.assertEqual(A.__name__, 'A')
3001+
29963002
def test_bad_args(self):
29973003
with self.assertRaises(TypeError):
29983004
type()

Lib/test/test_str.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -454,6 +454,13 @@ def test_maketrans_translate(self):
454454
self.assertEqual("[a\xe9]".translate(str.maketrans({'a': '<\u20ac>'})),
455455
"[<\u20ac>\xe9]")
456456

457+
# with frozendict
458+
tbl = self.type2test.maketrans(frozendict({'s': 'S', 'T': 't'}))
459+
self.assertEqual(tbl, {ord('s'): 'S', ord('T'): 't'})
460+
self.assertEqual('sTan'.translate(tbl), 'Stan')
461+
tbl = self.type2test.maketrans(frozendict({'a': None, 'b': '<i>'}))
462+
self.checkequalnofix('<i><i><i>c', 'abababc', 'translate', tbl)
463+
457464
# invalid Unicode characters
458465
invalid_char = 0x10ffff+1
459466
for before in "a\xe9\u20ac\U0010ffff":
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
:meth:`str.maketrans` and :func:`type` now accept :class:`frozendict`.

Objects/typeobject.c

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4872,9 +4872,21 @@ type_new_get_slots(type_new_ctx *ctx, PyObject *dict)
48724872
static PyTypeObject*
48734873
type_new_init(type_new_ctx *ctx)
48744874
{
4875-
PyObject *dict = PyDict_Copy(ctx->orig_dict);
4876-
if (dict == NULL) {
4877-
goto error;
4875+
PyObject *dict;
4876+
if (PyFrozenDict_Check(ctx->orig_dict)) {
4877+
dict = PyDict_New();
4878+
if (dict == NULL) {
4879+
goto error;
4880+
}
4881+
if (PyDict_Merge(dict, ctx->orig_dict, 1) < 0) {
4882+
goto error;
4883+
}
4884+
}
4885+
else {
4886+
dict = PyDict_Copy(ctx->orig_dict);
4887+
if (dict == NULL) {
4888+
goto error;
4889+
}
48784890
}
48794891

48804892
if (type_new_get_slots(ctx, dict) < 0) {
@@ -5037,13 +5049,19 @@ type_new(PyTypeObject *metatype, PyObject *args, PyObject *kwds)
50375049

50385050
/* Parse arguments: (name, bases, dict) */
50395051
PyObject *name, *bases, *orig_dict;
5040-
if (!PyArg_ParseTuple(args, "UO!O!:type.__new__",
5052+
if (!PyArg_ParseTuple(args, "UO!O:type.__new__",
50415053
&name,
50425054
&PyTuple_Type, &bases,
5043-
&PyDict_Type, &orig_dict))
5055+
&orig_dict))
50445056
{
50455057
return NULL;
50465058
}
5059+
if (!PyAnyDict_Check(orig_dict)) {
5060+
PyErr_Format(PyExc_TypeError,
5061+
"type.__new__() argument 3 must be dict, not %T",
5062+
orig_dict);
5063+
return NULL;
5064+
}
50475065

50485066
type_new_ctx ctx = {
50495067
.metatype = metatype,

Objects/unicodeobject.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13149,7 +13149,7 @@ unicode_maketrans_impl(PyObject *x, PyObject *y, PyObject *z)
1314913149
const void *data;
1315013150

1315113151
/* x must be a dict */
13152-
if (!PyDict_CheckExact(x)) {
13152+
if (!PyAnyDict_CheckExact(x)) {
1315313153
PyErr_SetString(PyExc_TypeError, "if you give only one argument "
1315413154
"to maketrans it must be a dict");
1315513155
goto err;

0 commit comments

Comments
 (0)