Skip to content

Commit ec8baa7

Browse files
Preserve str subclass type returned by __str__ / __repr__ (#7701)
Closes #7450. CPython's unicode_new_impl returns the PyObject_Str result as-is when type == &PyUnicode_Type, only invoking unicode_subtype_new for actual str subclasses. RustPython's PyStr::Constructor stripped the result via Self::from(s.as_wtf8().to_owned()) and re-materialized through into_ref_with_type, dropping the subclass type even when cls is exactly str. Add a slot_new branch that returns input.str(vm)? directly when cls is str_type with no encoding. Subtype construction and the bytes-decoding path are unchanged. Unmasks test_str.StrTest.test_conversion (11 assertTypedEqual cases).
1 parent a95ad5c commit ec8baa7

2 files changed

Lines changed: 12 additions & 1 deletion

File tree

‎Lib/test/test_str.py‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2414,7 +2414,6 @@ def test_ucs4(self):
24142414
else:
24152415
self.fail("Should have raised UnicodeDecodeError")
24162416

2417-
@unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: <class 'str'> is not <class 'test.test_str.StrSubclass'>
24182417
def test_conversion(self):
24192418
# Make sure __str__() works properly
24202419
class StrWithStr(str):

‎crates/vm/src/builtins/str.rs‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -406,6 +406,18 @@ impl Constructor for PyStr {
406406
}
407407

408408
let args: Self::Args = func_args.bind(vm)?;
409+
410+
// CPython parity: when cls is exactly str, return the __str__ / __repr__
411+
// result as-is so any str subclass type the user returned is preserved
412+
// (matches unicode_new_impl which only invokes unicode_subtype_new when
413+
// type != &PyUnicode_Type).
414+
if cls.is(vm.ctx.types.str_type)
415+
&& args.encoding.is_missing()
416+
&& let OptionalArg::Present(input) = &args.object
417+
{
418+
return Ok(input.str(vm)?.into());
419+
}
420+
409421
let payload = Self::py_new(&cls, args, vm)?;
410422
payload.into_ref_with_type(vm, cls).map(Into::into)
411423
}

0 commit comments

Comments
 (0)