From 37f66dd998281f47d7490d2ba4202d20177b4dca Mon Sep 17 00:00:00 2001 From: sobolevn Date: Wed, 11 Aug 2021 21:53:52 +0300 Subject: [PATCH] `id()` for `bytes` stays the same after `* 1`, refs #2840 --- extra_tests/snippets/bytes.py | 24 +++++++++++++++++++++++- vm/src/builtins/bytes.rs | 18 +++++++++++++----- 2 files changed, 36 insertions(+), 6 deletions(-) diff --git a/extra_tests/snippets/bytes.py b/extra_tests/snippets/bytes.py index 3a9ca55d4..00723ad94 100644 --- a/extra_tests/snippets/bytes.py +++ b/extra_tests/snippets/bytes.py @@ -623,4 +623,26 @@ assert bytes(A()) == b"bytess" # Issue #2125 b = b'abc' -assert bytes(b) is b \ No newline at end of file +assert bytes(b) is b + + +# Regression to +# https://github.com/RustPython/RustPython/issues/2840 + +a = b'123abc!?' +assert id(a) == id(a) +assert id(a) != id(a * -1) +assert id(a) != id(a * 0) +assert id(a) == id(a * 1) # only case when `id` stays the same +assert id(a) != id(a * 2) + + +class SubBytes(bytes): + pass + +b = SubBytes(b'0123abc*&') +assert id(b) == id(b) +assert id(b) != id(b * -1) +assert id(b) != id(b * 0) +assert id(b) != id(b * 1) +assert id(b) != id(b * 2) diff --git a/vm/src/builtins/bytes.rs b/vm/src/builtins/bytes.rs index cc829539e..e39254a10 100644 --- a/vm/src/builtins/bytes.rs +++ b/vm/src/builtins/bytes.rs @@ -21,8 +21,8 @@ use crate::slots::{BufferProtocol, Comparable, Hashable, Iterable, PyComparisonO use crate::utils::Either; use crate::vm::VirtualMachine; use crate::{ - IntoPyObject, PyClassImpl, PyComparisonValue, PyContext, PyIterable, PyObjectRef, PyRef, - PyResult, PyValue, TryFromObject, TypeProtocol, + IdProtocol, IntoPyObject, PyClassImpl, PyComparisonValue, PyContext, PyIterable, PyObjectRef, + PyRef, PyResult, PyValue, TryFromObject, TypeProtocol, }; use crate::builtins::memory::{BufferOptions, PyBuffer}; @@ -430,11 +430,19 @@ impl PyBytes { #[pymethod(name = "__mul__")] #[pymethod(name = "__rmul__")] - fn mul(&self, value: isize, vm: &VirtualMachine) -> PyResult { - if value > 0 && self.inner.len() as isize > std::isize::MAX / value { + fn mul(zelf: PyRef, value: isize, vm: &VirtualMachine) -> PyResult> { + if value > 0 && zelf.inner.len() as isize > std::isize::MAX / value { return Err(vm.new_overflow_error("repeated bytes are too long".to_owned())); } - Ok(self.inner.repeat(value).into()) + if value == 1 && zelf.class().is(&vm.ctx.types.bytes_type) { + // Special case: when some `bytes` is multiplied by `1`, + // nothing really happens, we need to return an object itself + // with the same `id()` to be compatible with CPython. + // This only works for `bytes` itself, not its subclasses. + return Ok(zelf); + } + let bytes: PyBytes = zelf.inner.repeat(value).into(); + Ok(bytes.into_ref(vm)) } #[pymethod(name = "__mod__")]