Skip to content

Commit 37f66dd

Browse files
committed
id() for bytes stays the same after * 1, refs RustPython#2840
1 parent 03a9d60 commit 37f66dd

File tree

2 files changed

+36
-6
lines changed

2 files changed

+36
-6
lines changed

extra_tests/snippets/bytes.py

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -623,4 +623,26 @@ def __bytes__(self):
623623

624624
# Issue #2125
625625
b = b'abc'
626-
assert bytes(b) is b
626+
assert bytes(b) is b
627+
628+
629+
# Regression to
630+
# https://github.com/RustPython/RustPython/issues/2840
631+
632+
a = b'123abc!?'
633+
assert id(a) == id(a)
634+
assert id(a) != id(a * -1)
635+
assert id(a) != id(a * 0)
636+
assert id(a) == id(a * 1) # only case when `id` stays the same
637+
assert id(a) != id(a * 2)
638+
639+
640+
class SubBytes(bytes):
641+
pass
642+
643+
b = SubBytes(b'0123abc*&')
644+
assert id(b) == id(b)
645+
assert id(b) != id(b * -1)
646+
assert id(b) != id(b * 0)
647+
assert id(b) != id(b * 1)
648+
assert id(b) != id(b * 2)

vm/src/builtins/bytes.rs

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@ use crate::slots::{BufferProtocol, Comparable, Hashable, Iterable, PyComparisonO
2121
use crate::utils::Either;
2222
use crate::vm::VirtualMachine;
2323
use crate::{
24-
IntoPyObject, PyClassImpl, PyComparisonValue, PyContext, PyIterable, PyObjectRef, PyRef,
25-
PyResult, PyValue, TryFromObject, TypeProtocol,
24+
IdProtocol, IntoPyObject, PyClassImpl, PyComparisonValue, PyContext, PyIterable, PyObjectRef,
25+
PyRef, PyResult, PyValue, TryFromObject, TypeProtocol,
2626
};
2727

2828
use crate::builtins::memory::{BufferOptions, PyBuffer};
@@ -430,11 +430,19 @@ impl PyBytes {
430430

431431
#[pymethod(name = "__mul__")]
432432
#[pymethod(name = "__rmul__")]
433-
fn mul(&self, value: isize, vm: &VirtualMachine) -> PyResult<PyBytes> {
434-
if value > 0 && self.inner.len() as isize > std::isize::MAX / value {
433+
fn mul(zelf: PyRef<Self>, value: isize, vm: &VirtualMachine) -> PyResult<PyRef<Self>> {
434+
if value > 0 && zelf.inner.len() as isize > std::isize::MAX / value {
435435
return Err(vm.new_overflow_error("repeated bytes are too long".to_owned()));
436436
}
437-
Ok(self.inner.repeat(value).into())
437+
if value == 1 && zelf.class().is(&vm.ctx.types.bytes_type) {
438+
// Special case: when some `bytes` is multiplied by `1`,
439+
// nothing really happens, we need to return an object itself
440+
// with the same `id()` to be compatible with CPython.
441+
// This only works for `bytes` itself, not its subclasses.
442+
return Ok(zelf);
443+
}
444+
let bytes: PyBytes = zelf.inner.repeat(value).into();
445+
Ok(bytes.into_ref(vm))
438446
}
439447

440448
#[pymethod(name = "__mod__")]

0 commit comments

Comments
 (0)