Preserve `str` subclass results from `__repr__` in plain `str()` by Copilot · Pull Request #7454 · RustPython/RustPython
str(obj) was flattening a str subclass returned by __repr__ into an exact str, diverging from CPython. This only affected the plain str(...) constructor path; subtype construction should still normalize to the requested subtype.
-
Constructor behavior
- Change the exact
str(...)fast path inPyStr::slot_newto returnPyObject::str(vm)directly for the single-argument, no-kwargs case. - This preserves subclass identity when
__str__/__repr__returns astrsubclass, instead of rebuilding a new exactstrfrom the underlying data.
- Change the exact
-
Regression coverage
- Re-enable
Lib/test/test_str.py::StrTest.test_conversion, which already coversWithRepr(StrSubclass(...))and related conversion cases. - Keep the
Lib/test/test_str.pychange aligned with CPythonv3.14.3rather than adding RustPython-only assertions there. - Add RustPython-owned identity regression coverage under
extra_tests/snippets/builtin_str_subclass.pyfor the exactstr(...)path preserving astrsubclass returned by__str__and__repr__.
- Re-enable
Example:
class MyStr(str): pass class Foo: def __repr__(self): return MyStr("hello") result = str(Foo()) type(result) # now: MyStr
Original prompt
This section details on the original issue you should resolve
<issue_title>
str()discards str subclass type returned by__repr__</issue_title>
<issue_description>## Description
str()on an object whose__repr__returns astrsubclass instance incorrectly returns a plainstr, discarding the subclass type. CPython preserves the subclass type as-is. This is a CPython compatibility issue.Root Cause
PyStr::py_newcallsinput.str(vm)which correctly resolves to the__repr__return value (a str subclass instance), but then extracts only the raw string data viaSelf::from(s.as_wtf8().to_owned())and creates a new plainPyStr, discarding the subclass type.CPython reference:
unicodeobject.c#L15575-L15596// Key branch: only convert when type is a str subclass if (unicode != NULL && type != &PyUnicode_Type) { Py_SETREF(unicode, unicode_subtype_new(type, unicode)); } return unicode; // when type is exactly str → return PyObject_Str result as-isWhen
type == &PyUnicode_Type(plainstr()call),unicode_new_implreturns thePyObject_Strresult without conversion, preserving the subclass type.Reproduction
class MyStr(str): pass class Foo: def __repr__(self): return MyStr('hello') result = str(Foo()) print(result) print(type(result))Output
RustPython:
CPython:
hello <class '__main__.MyStr'>Environment
- RustPython d248a04 (Python 3.14.0)
- CPython 3.14.3
- OS: Debian 12</issue_description>
Comments on the Issue (you are @copilot in this section)
💬 Send tasks to Copilot coding agent from Slack and Teams to turn conversations into code. Copilot posts an update in your thread when it's finished.
Summary by CodeRabbit
- Bug Fixes
- Improved string conversion behavior to ensure consistent handling of arguments passed to the string constructor.