Skip to content

Implement bytearray.__str__ && bytes.__str__#7477

Open
moreal wants to merge 3 commits intoRustPython:mainfrom
moreal:missing-str
Open

Implement bytearray.__str__ && bytes.__str__#7477
moreal wants to merge 3 commits intoRustPython:mainfrom
moreal:missing-str

Conversation

@moreal
Copy link
Contributor

@moreal moreal commented Mar 20, 2026

This pull request let bytearray.__str__ and bytes.__str__ make warning. With -b or -bb option, it will show or raise warning.

Behavior changes

CPython (3.14.3)

$ python3
Python 3.14.3 (main, Feb  3 2026, 15:32:20) [Clang 17.0.0 (clang-1700.6.3.2)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> str(bytes.fromhex(b'aa'))
"b'\\xaa'"
>>> str(bytearray.fromhex(b'aa'))
"bytearray(b'\\xaa')"

$ python3 -b
Python 3.14.3 (main, Feb  3 2026, 15:32:20) [Clang 17.0.0 (clang-1700.6.3.2)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> str(bytes.fromhex(b'aa'))
<python-input-0>:1: BytesWarning: str() on a bytes instance
"b'\\xaa'"
>>> str(bytearray.fromhex(b'aa'))
<python-input-1>:1: BytesWarning: str() on a bytearray instance
"bytearray(b'\\xaa')"

RustPython (before)

$ cargo run
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.26s
     Running `target/debug/rustpython`
Welcome to the magnificent Rust Python 0.5.0 interpreter 😱 🖖
RustPython 3.14.0
Type "help", "copyright", "credits" or "license" for more information.
>>>>> str(bytes.fromhex(b'aa'))
"b'\\xaa'"
>>>>> str(bytearray.fromhex(b'aa'))
"bytearray(b'\\xaa')"

$ cargo run -- -b
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.26s
     Running `target/debug/rustpython -b`
Welcome to the magnificent Rust Python 0.5.0 interpreter 😱 🖖
RustPython 3.14.0
Type "help", "copyright", "credits" or "license" for more information.
>>>>> str(bytes.fromhex(b'aa'))
"b'\\xaa'"
>>>>> str(bytearray.fromhex(b'aa'))
"bytearray(b'\\xaa')"

RustPython (after)

$ cargo run
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.26s
     Running `target/debug/rustpython`
Welcome to the magnificent Rust Python 0.5.0 interpreter 😱 🖖
RustPython 3.14.0
Type "help", "copyright", "credits" or "license" for more information.
>>>>> str(bytes.fromhex(b'aa'))
"b'\\xaa'"
>>>>> str(bytearray.fromhex(b'aa'))
"bytearray(b'\\xaa')"

$ cargo run -- -b
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.26s
     Running `target/debug/rustpython -b`
Welcome to the magnificent Rust Python 0.5.0 interpreter 😱 🖖
RustPython 3.14.0
Type "help", "copyright", "credits" or "license" for more information.
>>>>> str(bytes.fromhex(b'aa'))
<stdin>:1: BytesWarning: str() on a bytes instance
"b'\\xaa'"
>>>>> str(bytearray.fromhex(b'aa'))
<stdin>:1: BytesWarning: str() on a bytearray instance
"bytearray(b'\\xaa')"

CPython reference

bytes.__str__ https://github.com/python/cpython/blob/v3.14.3/Objects/bytesobject.c#L1423-L1433

static PyObject *
bytes_str(PyObject *op)
{
    if (_Py_GetConfig()->bytes_warning) {
        if (PyErr_WarnEx(PyExc_BytesWarning,
                         "str() on a bytes instance", 1)) {
            return NULL;
        }
    }
    return bytes_repr(op);
}

bytearray.__str__ https://github.com/python/cpython/blob/v3.14.3/Objects/bytearrayobject.c#L1171-L1181

static PyObject *
bytearray_str(PyObject *op)
{
    if (_Py_GetConfig()->bytes_warning) {
        if (PyErr_WarnEx(PyExc_BytesWarning,
                         "str() on a bytearray instance", 1)) {
                return NULL;
        }
    }
    return bytearray_repr(op);
}

Summary by CodeRabbit

  • New Features
    • Added string conversion for bytes and bytearray objects so str() returns a user-readable representation.
    • Added a configurable warning that notifies when converting bytes/bytearray to strings (can be enabled or disabled in settings).

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 20, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

Run ID: dfe6059b-71e8-47c0-a20c-0665a7ec429d

📥 Commits

Reviewing files that changed from the base of the PR and between 08fbe7a and 590e514.

📒 Files selected for processing (2)
  • crates/vm/src/builtins/bytearray.rs
  • crates/vm/src/builtins/bytes.rs
🚧 Files skipped from review as they are similar to previous changes (2)
  • crates/vm/src/builtins/bytes.rs
  • crates/vm/src/builtins/bytearray.rs

📝 Walkthrough

Walkthrough

Added slot_str implementations for PyBytes and PyByteArray that emit a conditional bytes_warning and return a string representation; introduced PyBytesInner::warn_on_str to centralize the warning logic.

Changes

Cohort / File(s) Summary
Byte __str__ slots
crates/vm/src/builtins/bytes.rs, crates/vm/src/builtins/bytearray.rs
Added #[pyslot] fn slot_str(...) for PyBytes and PyByteArray to call the warning helper and produce the str()/PyStr representation.
Bytes warning helper
crates/vm/src/bytes_inner.rs
Added pub(crate) fn warn_on_str(message: &'static str, vm: &VirtualMachine) -> PyResult<()> which conditionally emits bytes_warning based on VM config.

Sequence Diagram

sequenceDiagram
    participant Caller
    participant PyObj as PyBytes / PyByteArray
    participant Inner as PyBytesInner
    participant VM as VirtualMachine
    participant Config as VM Config

    Caller->>PyObj: str(obj)
    PyObj->>Inner: warn_on_str(message, vm)
    Inner->>Config: read bytes_warning
    Config-->>Inner: bytes_warning level
    alt bytes_warning > 0
        Inner->>VM: emit bytes_warning(exception, message)
    end
    Inner-->>PyObj: Ok(())
    PyObj->>PyObj: compute repr / repr_with_name
    PyObj-->>Caller: PyStr (string)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • Add tp_str #6495 — Introduces the Str slot variant and wiring for slot-based __str__, closely related to adding slot_str implementations for bytes types.

Suggested reviewers

  • youknowone

Poem

🐰 I nibble bytes and spin a tale so bright,
A gentle warning springs before they speak at night.
From inner burrows to the VM's light,
Strings now hop out tidy and polite. 🥕✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 44.44% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title directly and accurately summarizes the main changes: implementing __str__ for both bytearray and bytes classes by adding slot implementations.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@moreal moreal marked this pull request as ready for review March 20, 2026 18:01
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
crates/vm/src/builtins/bytes.rs (1)

227-231: Please add regression coverage for warning modes.

Recommend adding tests that assert:

  1. default mode: no warning,
  2. -b: BytesWarning emitted,
  3. -bb: warning escalates appropriately,
    while str(bytes_obj) output remains unchanged in all cases.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/vm/src/builtins/bytes.rs` around lines 227 - 231, Add regression tests
for the bytes __str__ path that exercise PyBytesInner::warn_on_str via the
bytes.__str__ implementation: create a bytes object, call str(bytes_obj) and
assert its output is unchanged, then run three scenarios asserting
warnings/behavior: (1) default mode — no warning emitted; (2) warning mode "-b"
— a BytesWarning is emitted when calling str(bytes_obj); (3) warning mode "-bb"
— the warning is escalated (treated as an error) when calling str(bytes_obj).
Use the same call into the code path that triggers PyBytesInner::warn_on_str and
make explicit assertions about the presence/absence/type of warning while
keeping the returned string identical in all cases.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@crates/vm/src/builtins/bytes.rs`:
- Around line 227-231: Add regression tests for the bytes __str__ path that
exercise PyBytesInner::warn_on_str via the bytes.__str__ implementation: create
a bytes object, call str(bytes_obj) and assert its output is unchanged, then run
three scenarios asserting warnings/behavior: (1) default mode — no warning
emitted; (2) warning mode "-b" — a BytesWarning is emitted when calling
str(bytes_obj); (3) warning mode "-bb" — the warning is escalated (treated as an
error) when calling str(bytes_obj). Use the same call into the code path that
triggers PyBytesInner::warn_on_str and make explicit assertions about the
presence/absence/type of warning while keeping the returned string identical in
all cases.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

Run ID: 97d67d10-55d4-45a2-9406-15e1acf0abdc

📥 Commits

Reviewing files that changed from the base of the PR and between a1203ae and 08fbe7a.

📒 Files selected for processing (3)
  • crates/vm/src/builtins/bytearray.rs
  • crates/vm/src/builtins/bytes.rs
  • crates/vm/src/bytes_inner.rs

Comment on lines +218 to +219
#[pymethod]
fn __str__(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<PyStrRef> {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
#[pymethod]
fn __str__(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<PyStrRef> {
#[pyslot]
fn slot_str(zelf: &PyObject, vm: &VirtualMachine) -> PyResult<PyStrRef> {

does this work?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. I applied it in 590e514

I think it would be nice to have a trait that implements slot_str (like Representable), but I can't think of a good name for it. 😅

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you are interested in, could you also check this? #6485
That was a trial that removing single function traits. But this is a big change of interface design. I'd like to listen how other people think about it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I left my thought on the pull request #6485 (comment)

@moreal moreal requested a review from youknowone March 21, 2026 13:09
@youknowone youknowone enabled auto-merge (squash) March 21, 2026 13:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants