From 8a56d78bda41dbc7894df978ea3d122a324eaa16 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 17 Mar 2026 11:14:22 +0000 Subject: [PATCH 1/5] Initial plan From ed2fd1e16fcd2bc6e61fc2289c46d1eb0ad773b4 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 17 Mar 2026 11:27:11 +0000 Subject: [PATCH 2/5] Preserve str subclass returned by __repr__ Co-authored-by: youknowone <69878+youknowone@users.noreply.github.com> --- Lib/test/test_str.py | 1 - crates/vm/src/builtins/str.rs | 5 ++--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/Lib/test/test_str.py b/Lib/test/test_str.py index 11e2abb82c5..0dd92c93396 100644 --- a/Lib/test/test_str.py +++ b/Lib/test/test_str.py @@ -2414,7 +2414,6 @@ def test_ucs4(self): else: self.fail("Should have raised UnicodeDecodeError") - @unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: is not def test_conversion(self): # Make sure __str__() works properly class StrWithStr(str): diff --git a/crates/vm/src/builtins/str.rs b/crates/vm/src/builtins/str.rs index 8707c5cf769..90cb988c184 100644 --- a/crates/vm/src/builtins/str.rs +++ b/crates/vm/src/builtins/str.rs @@ -394,13 +394,12 @@ impl Constructor for PyStr { type Args = StrArgs; fn slot_new(cls: PyTypeRef, func_args: FuncArgs, vm: &VirtualMachine) -> PyResult { - // Optimization: return exact str as-is (only when no encoding/errors provided) + // Optimization: for exact str, return PyObject_Str result as-is if cls.is(vm.ctx.types.str_type) && func_args.args.len() == 1 && func_args.kwargs.is_empty() - && func_args.args[0].class().is(vm.ctx.types.str_type) { - return Ok(func_args.args[0].clone()); + return func_args.args[0].str(vm).map(Into::into); } let args: Self::Args = func_args.bind(vm)?; From 2ff66e7ed74145e8f3fd85de2f4bab2d0a29abb8 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 17 Mar 2026 11:29:34 +0000 Subject: [PATCH 3/5] Format str constructor fast path Co-authored-by: youknowone <69878+youknowone@users.noreply.github.com> --- crates/vm/src/builtins/str.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/crates/vm/src/builtins/str.rs b/crates/vm/src/builtins/str.rs index 90cb988c184..59b4145c75a 100644 --- a/crates/vm/src/builtins/str.rs +++ b/crates/vm/src/builtins/str.rs @@ -395,9 +395,7 @@ impl Constructor for PyStr { fn slot_new(cls: PyTypeRef, func_args: FuncArgs, vm: &VirtualMachine) -> PyResult { // Optimization: for exact str, return PyObject_Str result as-is - if cls.is(vm.ctx.types.str_type) - && func_args.args.len() == 1 - && func_args.kwargs.is_empty() + if cls.is(vm.ctx.types.str_type) && func_args.args.len() == 1 && func_args.kwargs.is_empty() { return func_args.args[0].str(vm).map(Into::into); } From 514053415cbe168fb776db83efac2d61e690a1f7 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 17 Mar 2026 14:35:43 +0000 Subject: [PATCH 4/5] Tighten str subclass conversion regression Co-authored-by: youknowone <69878+youknowone@users.noreply.github.com> --- Lib/test/test_str.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Lib/test/test_str.py b/Lib/test/test_str.py index 0dd92c93396..968b29d3067 100644 --- a/Lib/test/test_str.py +++ b/Lib/test/test_str.py @@ -2440,6 +2440,10 @@ def __str__(self): self.assertTypedEqual(StrSubclass(StrWithStr(OtherStrSubclass('abc'))), StrSubclass('abc')) + str_value = StrSubclass('abc') + self.assertIs(str(WithStr(str_value)), str_value) + self.assertIs(str(StrWithStr(str_value)), str_value) + self.assertTypedEqual(str(WithRepr('')), '') self.assertTypedEqual(str(WithRepr(StrSubclass(''))), StrSubclass('')) self.assertTypedEqual(StrSubclass(WithRepr('')), StrSubclass('')) @@ -2448,6 +2452,9 @@ def __str__(self): self.assertTypedEqual(StrSubclass(WithRepr(OtherStrSubclass(''))), StrSubclass('')) + repr_value = StrSubclass('') + self.assertIs(str(WithRepr(repr_value)), repr_value) + def test_unicode_repr(self): class s1: def __repr__(self): From 9a11d44a5c63fa30291651a5d51223d1aa32c19e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 19 Mar 2026 07:46:32 +0000 Subject: [PATCH 5/5] Align test_str with CPython and move local coverage Co-authored-by: youknowone <69878+youknowone@users.noreply.github.com> --- Lib/test/test_str.py | 7 ----- extra_tests/snippets/builtin_str_subclass.py | 27 ++++++++++++++++++++ 2 files changed, 27 insertions(+), 7 deletions(-) diff --git a/Lib/test/test_str.py b/Lib/test/test_str.py index 968b29d3067..0dd92c93396 100644 --- a/Lib/test/test_str.py +++ b/Lib/test/test_str.py @@ -2440,10 +2440,6 @@ def __str__(self): self.assertTypedEqual(StrSubclass(StrWithStr(OtherStrSubclass('abc'))), StrSubclass('abc')) - str_value = StrSubclass('abc') - self.assertIs(str(WithStr(str_value)), str_value) - self.assertIs(str(StrWithStr(str_value)), str_value) - self.assertTypedEqual(str(WithRepr('')), '') self.assertTypedEqual(str(WithRepr(StrSubclass(''))), StrSubclass('')) self.assertTypedEqual(StrSubclass(WithRepr('')), StrSubclass('')) @@ -2452,9 +2448,6 @@ def __str__(self): self.assertTypedEqual(StrSubclass(WithRepr(OtherStrSubclass(''))), StrSubclass('')) - repr_value = StrSubclass('') - self.assertIs(str(WithRepr(repr_value)), repr_value) - def test_unicode_repr(self): class s1: def __repr__(self): diff --git a/extra_tests/snippets/builtin_str_subclass.py b/extra_tests/snippets/builtin_str_subclass.py index 73e23615c4d..4e1f6080715 100644 --- a/extra_tests/snippets/builtin_str_subclass.py +++ b/extra_tests/snippets/builtin_str_subclass.py @@ -19,6 +19,33 @@ def __init__(self, value): assert y + " other" == "1 other" assert y.x == "substr" + +class ReprStrSubclass(str): + pass + + +class WithStr: + def __init__(self, value): + self.value = value + + def __str__(self): + return self.value + + +class WithRepr: + def __init__(self, value): + self.value = value + + def __repr__(self): + return self.value + + +str_value = ReprStrSubclass("abc") +assert str(WithStr(str_value)) is str_value + +repr_value = ReprStrSubclass("") +assert str(WithRepr(repr_value)) is repr_value + ## Base strings currently get an attribute dict, but shouldn't. # with assert_raises(AttributeError): # "hello".x = 5