Skip to content

Commit 462d6e6

Browse files
gh-104231: Make str() and repr() always returning str, and bytes() -- bytes
1 parent f6afa42 commit 462d6e6

File tree

7 files changed

+50
-23
lines changed

7 files changed

+50
-23
lines changed

Lib/test/test_bytes.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1062,7 +1062,7 @@ def __bytes__(self):
10621062
self.assertEqual(BytesSubclass(StrWithBytes(OtherBytesSubclass(b'abc'))),
10631063
BytesSubclass(b'abc'))
10641064
# Issue #24731
1065-
self.assertTypedEqual(bytes(WithBytes(BytesSubclass(b'abc'))), BytesSubclass(b'abc'))
1065+
self.assertTypedEqual(bytes(WithBytes(BytesSubclass(b'abc'))), b'abc')
10661066
self.assertTypedEqual(BytesSubclass(WithBytes(BytesSubclass(b'abc'))),
10671067
BytesSubclass(b'abc'))
10681068
self.assertTypedEqual(BytesSubclass(WithBytes(OtherBytesSubclass(b'abc'))),
@@ -1078,8 +1078,7 @@ def __bytes__(self):
10781078
self.assertTypedEqual(bytes(BytesWithBytes(b'abc')), b'abc')
10791079
self.assertTypedEqual(BytesSubclass(BytesWithBytes(b'abc')),
10801080
BytesSubclass(b'abc'))
1081-
self.assertTypedEqual(bytes(BytesWithBytes(BytesSubclass(b'abc'))),
1082-
BytesSubclass(b'abc'))
1081+
self.assertTypedEqual(bytes(BytesWithBytes(BytesSubclass(b'abc'))), b'abc')
10831082
self.assertTypedEqual(BytesSubclass(BytesWithBytes(BytesSubclass(b'abc'))),
10841083
BytesSubclass(b'abc'))
10851084
self.assertTypedEqual(BytesSubclass(BytesWithBytes(OtherBytesSubclass(b'abc'))),

Lib/test/test_capi/test_abstract.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -80,9 +80,9 @@ def test_object_str(self):
8080
self.assertTypedEqual(object_str('\U0001f40d'), '\U0001f40d')
8181
self.assertTypedEqual(object_str(StrSubclass('abc')), 'abc')
8282
self.assertTypedEqual(object_str(WithStr('abc')), 'abc')
83-
self.assertTypedEqual(object_str(WithStr(StrSubclass('abc'))), StrSubclass('abc'))
83+
self.assertTypedEqual(object_str(WithStr(StrSubclass('abc'))), 'abc')
8484
self.assertTypedEqual(object_str(WithRepr('<abc>')), '<abc>')
85-
self.assertTypedEqual(object_str(WithRepr(StrSubclass('<abc>'))), StrSubclass('<abc>'))
85+
self.assertTypedEqual(object_str(WithRepr(StrSubclass('<abc>'))), '<abc>')
8686
self.assertTypedEqual(object_str(NULL), '<NULL>')
8787

8888
def test_object_repr(self):
@@ -93,9 +93,9 @@ def test_object_repr(self):
9393
self.assertTypedEqual(object_repr('\U0001f40d'), "'\U0001f40d'")
9494
self.assertTypedEqual(object_repr(StrSubclass('abc')), "'abc'")
9595
self.assertTypedEqual(object_repr(WithRepr('<abc>')), '<abc>')
96-
self.assertTypedEqual(object_repr(WithRepr(StrSubclass('<abc>'))), StrSubclass('<abc>'))
96+
self.assertTypedEqual(object_repr(WithRepr(StrSubclass('<abc>'))), '<abc>')
9797
self.assertTypedEqual(object_repr(WithRepr('<\U0001f40d>')), '<\U0001f40d>')
98-
self.assertTypedEqual(object_repr(WithRepr(StrSubclass('<\U0001f40d>'))), StrSubclass('<\U0001f40d>'))
98+
self.assertTypedEqual(object_repr(WithRepr(StrSubclass('<\U0001f40d>'))), '<\U0001f40d>')
9999
self.assertTypedEqual(object_repr(NULL), '<NULL>')
100100

101101
def test_object_ascii(self):
@@ -106,7 +106,7 @@ def test_object_ascii(self):
106106
self.assertTypedEqual(object_ascii('\U0001f40d'), r"'\U0001f40d'")
107107
self.assertTypedEqual(object_ascii(StrSubclass('abc')), "'abc'")
108108
self.assertTypedEqual(object_ascii(WithRepr('<abc>')), '<abc>')
109-
self.assertTypedEqual(object_ascii(WithRepr(StrSubclass('<abc>'))), StrSubclass('<abc>'))
109+
self.assertTypedEqual(object_ascii(WithRepr(StrSubclass('<abc>'))), '<abc>')
110110
self.assertTypedEqual(object_ascii(WithRepr('<\U0001f40d>')), r'<\U0001f40d>')
111111
self.assertTypedEqual(object_ascii(WithRepr(StrSubclass('<\U0001f40d>'))), r'<\U0001f40d>')
112112
self.assertTypedEqual(object_ascii(NULL), '<NULL>')
@@ -118,7 +118,7 @@ def test_object_bytes(self):
118118
self.assertTypedEqual(object_bytes(b'abc'), b'abc')
119119
self.assertTypedEqual(object_bytes(BytesSubclass(b'abc')), b'abc')
120120
self.assertTypedEqual(object_bytes(WithBytes(b'abc')), b'abc')
121-
self.assertTypedEqual(object_bytes(WithBytes(BytesSubclass(b'abc'))), BytesSubclass(b'abc'))
121+
self.assertTypedEqual(object_bytes(WithBytes(BytesSubclass(b'abc'))), b'abc')
122122
self.assertTypedEqual(object_bytes(bytearray(b'abc')), b'abc')
123123
self.assertTypedEqual(object_bytes(memoryview(b'abc')), b'abc')
124124
self.assertTypedEqual(object_bytes([97, 98, 99]), b'abc')

Lib/test/test_str.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ def test_ascii(self):
149149
self.assertTypedEqual(ascii('\U0001f40d'), r"'\U0001f40d'")
150150
self.assertTypedEqual(ascii(StrSubclass('abc')), "'abc'")
151151
self.assertTypedEqual(ascii(WithRepr('<abc>')), '<abc>')
152-
self.assertTypedEqual(ascii(WithRepr(StrSubclass('<abc>'))), StrSubclass('<abc>'))
152+
self.assertTypedEqual(ascii(WithRepr(StrSubclass('<abc>'))), '<abc>')
153153
self.assertTypedEqual(ascii(WithRepr('<\U0001f40d>')), r'<\U0001f40d>')
154154
self.assertTypedEqual(ascii(WithRepr(StrSubclass('<\U0001f40d>'))), r'<\U0001f40d>')
155155
self.assertRaises(TypeError, ascii, WithRepr(b'byte-repr'))
@@ -193,9 +193,9 @@ def test_repr(self):
193193
self.assertTypedEqual(repr('\U0001f40d'), "'\U0001f40d'")
194194
self.assertTypedEqual(repr(StrSubclass('abc')), "'abc'")
195195
self.assertTypedEqual(repr(WithRepr('<abc>')), '<abc>')
196-
self.assertTypedEqual(repr(WithRepr(StrSubclass('<abc>'))), StrSubclass('<abc>'))
196+
self.assertTypedEqual(repr(WithRepr(StrSubclass('<abc>'))), '<abc>')
197197
self.assertTypedEqual(repr(WithRepr('<\U0001f40d>')), '<\U0001f40d>')
198-
self.assertTypedEqual(repr(WithRepr(StrSubclass('<\U0001f40d>'))), StrSubclass('<\U0001f40d>'))
198+
self.assertTypedEqual(repr(WithRepr(StrSubclass('<\U0001f40d>'))), '<\U0001f40d>')
199199
self.assertRaises(TypeError, repr, WithRepr(b'byte-repr'))
200200

201201
def test_iterators(self):
@@ -2401,23 +2401,23 @@ def __str__(self):
24012401
return self.value
24022402

24032403
self.assertTypedEqual(str(WithStr('abc')), 'abc')
2404-
self.assertTypedEqual(str(WithStr(StrSubclass('abc'))), StrSubclass('abc'))
2404+
self.assertTypedEqual(str(WithStr(StrSubclass('abc'))), 'abc')
24052405
self.assertTypedEqual(StrSubclass(WithStr('abc')), StrSubclass('abc'))
24062406
self.assertTypedEqual(StrSubclass(WithStr(StrSubclass('abc'))),
24072407
StrSubclass('abc'))
24082408
self.assertTypedEqual(StrSubclass(WithStr(OtherStrSubclass('abc'))),
24092409
StrSubclass('abc'))
24102410

24112411
self.assertTypedEqual(str(StrWithStr('abc')), 'abc')
2412-
self.assertTypedEqual(str(StrWithStr(StrSubclass('abc'))), StrSubclass('abc'))
2412+
self.assertTypedEqual(str(StrWithStr(StrSubclass('abc'))), 'abc')
24132413
self.assertTypedEqual(StrSubclass(StrWithStr('abc')), StrSubclass('abc'))
24142414
self.assertTypedEqual(StrSubclass(StrWithStr(StrSubclass('abc'))),
24152415
StrSubclass('abc'))
24162416
self.assertTypedEqual(StrSubclass(StrWithStr(OtherStrSubclass('abc'))),
24172417
StrSubclass('abc'))
24182418

24192419
self.assertTypedEqual(str(WithRepr('<abc>')), '<abc>')
2420-
self.assertTypedEqual(str(WithRepr(StrSubclass('<abc>'))), StrSubclass('<abc>'))
2420+
self.assertTypedEqual(str(WithRepr(StrSubclass('<abc>'))), '<abc>')
24212421
self.assertTypedEqual(StrSubclass(WithRepr('<abc>')), StrSubclass('<abc>'))
24222422
self.assertTypedEqual(StrSubclass(WithRepr(StrSubclass('<abc>'))),
24232423
StrSubclass('<abc>'))
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
:c:func:`PyObject_Str`, :c:func:`PyObject_Repr` and :func:`PyObject_ASCII`
2+
now always return an instance of :class:`str`, even if ``__str__()`` or
3+
``__repr__()`` return an instance of :class:`!str` subclass.
4+
:func:`PyObject_Bytes` now always returns an instance of :class:`bytes`,
5+
even if ``__bytes__()`` returns an instance of :class:`!bytes` subclass.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
:func:`str`, :func:`repr` and :func:`ascii` now always return an instance of
2+
:class:`str`, even if ``__str__()`` or ``__repr__()`` return an instance of
3+
:class:`!str` subclass. :func:`bytes` now always returns an instance of
4+
:class:`bytes`, even if ``__bytes__()`` returns an instance of
5+
:class:`!bytes` subclass.

Objects/bytesobject.c

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2617,12 +2617,19 @@ bytes_new_impl(PyTypeObject *type, PyObject *x, const char *encoding,
26172617
Py_DECREF(func);
26182618
if (bytes == NULL)
26192619
return NULL;
2620-
if (!PyBytes_Check(bytes)) {
2621-
PyErr_Format(PyExc_TypeError,
2622-
"__bytes__ returned non-bytes (type %.200s)",
2623-
Py_TYPE(bytes)->tp_name);
2624-
Py_DECREF(bytes);
2625-
return NULL;
2620+
if (!PyBytes_CheckExact(bytes)) {
2621+
if (!PyBytes_Check(bytes)) {
2622+
PyErr_Format(PyExc_TypeError,
2623+
"__bytes__ returned non-bytes (type %.200s)",
2624+
Py_TYPE(bytes)->tp_name);
2625+
Py_DECREF(bytes);
2626+
return NULL;
2627+
}
2628+
Py_SETREF(bytes, PyBytes_FromStringAndSize(PyBytes_AS_STRING(bytes),
2629+
PyBytes_GET_SIZE(bytes)));
2630+
if (bytes == NULL) {
2631+
return NULL;
2632+
}
26262633
}
26272634
}
26282635
else if (PyErr_Occurred())

Objects/object.c

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -674,8 +674,8 @@ PyObject_Repr(PyObject *v)
674674
res = (*Py_TYPE(v)->tp_repr)(v);
675675
_Py_LeaveRecursiveCallTstate(tstate);
676676

677-
if (res == NULL) {
678-
return NULL;
677+
if (res == NULL || PyUnicode_CheckExact(res)) {
678+
return res;
679679
}
680680
if (!PyUnicode_Check(res)) {
681681
_PyErr_Format(tstate, PyExc_TypeError,
@@ -684,6 +684,7 @@ PyObject_Repr(PyObject *v)
684684
Py_DECREF(res);
685685
return NULL;
686686
}
687+
Py_SETREF(res, _PyUnicode_Copy(res));
687688
return res;
688689
}
689690

@@ -726,6 +727,10 @@ PyObject_Str(PyObject *v)
726727
if (res == NULL) {
727728
return NULL;
728729
}
730+
if (PyUnicode_CheckExact(res)) {
731+
assert(_PyUnicode_CheckConsistency(res, 1));
732+
return res;
733+
}
729734
if (!PyUnicode_Check(res)) {
730735
_PyErr_Format(tstate, PyExc_TypeError,
731736
"__str__ returned non-string (type %.200s)",
@@ -734,6 +739,7 @@ PyObject_Str(PyObject *v)
734739
return NULL;
735740
}
736741
assert(_PyUnicode_CheckConsistency(res, 1));
742+
Py_SETREF(res, _PyUnicode_Copy(res));
737743
return res;
738744
}
739745

@@ -782,13 +788,18 @@ PyObject_Bytes(PyObject *v)
782788
Py_DECREF(func);
783789
if (result == NULL)
784790
return NULL;
791+
if (PyBytes_CheckExact(result)) {
792+
return result;
793+
}
785794
if (!PyBytes_Check(result)) {
786795
PyErr_Format(PyExc_TypeError,
787796
"__bytes__ returned non-bytes (type %.200s)",
788797
Py_TYPE(result)->tp_name);
789798
Py_DECREF(result);
790799
return NULL;
791800
}
801+
Py_SETREF(result, PyBytes_FromStringAndSize(PyBytes_AS_STRING(result),
802+
PyBytes_GET_SIZE(result)));
792803
return result;
793804
}
794805
else if (PyErr_Occurred())

0 commit comments

Comments
 (0)