mirror of
https://github.com/RustPython/RustPython.git
synced 2026-10-28 10:44:36 +08:00
Support Python 3.14 complex values in struct formats (#8829)
Accept F/D through the existing buffer packing implementation and restore its complex round-trip test. Fix exact __complex__ results warning as subclasses so valid conversions also work with warnings treated as errors. Codex assisted with implementation, regression coverage, and review. Tested: CPython 3.14.7 comparison; struct/memoryview/complex/cmath; workspace and C-API tests; Clippy; pytest; prek Assisted-by: Codex:gpt-6-astra
This commit is contained in:
1 parent
00dfed8ff9
commit
5f18b9cd56
3 files changed
+68
-19
No files matched your search
+35
-18
@@ -1,6 +1,6 @@
|
||||
use crate::{
|
||||
AsObject, Py, PyObject, PyObjectRef, PyResult, TryFromObject, VirtualMachine,
|
||||
builtins::{PyBaseExceptionRef, PyBytesRef, PyTuple, PyTupleRef, PyType, PyTypeRef},
|
||||
builtins::{PyBaseExceptionRef, PyBytesRef, PyComplex, PyTuple, PyTupleRef, PyType, PyTypeRef},
|
||||
common::{static_cell, str::wchar_t},
|
||||
convert::ToPyObject,
|
||||
exceptions,
|
||||
@@ -780,7 +780,7 @@ struct PackFloatComplex(f32, f32);
|
||||
struct PackDoubleComplex(f64, f64);
|
||||
|
||||
macro_rules! make_pack_complex {
|
||||
($T:ty, $Elem:ty, $Bits:ty) => {
|
||||
($T:ty, $Elem:ty, $Bits:ty, $fmt:literal) => {
|
||||
impl Packable for $T {
|
||||
fn pack<E: ByteOrder>(
|
||||
vm: &VirtualMachine,
|
||||
@@ -788,20 +788,37 @@ macro_rules! make_pack_complex {
|
||||
arg: PyObjectRef,
|
||||
data: &mut [u8],
|
||||
) -> Result<(), PackError> {
|
||||
let c = ArgIntoComplex::try_from_object(vm, arg)
|
||||
.map_err(|_| {
|
||||
PackError::new(
|
||||
PackErrorKind::Type,
|
||||
vm,
|
||||
"required argument is not a complex",
|
||||
)
|
||||
})?
|
||||
.into_complex();
|
||||
let half = size_of::<$Elem>();
|
||||
(c.re as $Elem).to_bits().pack_int::<E>(&mut data[..half]);
|
||||
(c.im as $Elem)
|
||||
.to_bits()
|
||||
.pack_int::<E>(&mut data[half..half * 2]);
|
||||
let c = if let Some(value) = arg.downcast_ref::<PyComplex>() {
|
||||
value.to_complex()
|
||||
} else {
|
||||
ArgIntoComplex::try_from_object(vm, arg)
|
||||
.map_err(|_| {
|
||||
PackError::new(
|
||||
PackErrorKind::Type,
|
||||
vm,
|
||||
"required argument is not a complex",
|
||||
)
|
||||
})?
|
||||
.into_complex()
|
||||
};
|
||||
for (component, bytes) in [c.re, c.im]
|
||||
.into_iter()
|
||||
.zip(data.chunks_exact_mut(size_of::<$Elem>()))
|
||||
{
|
||||
let narrowed = component as $Elem;
|
||||
// CPython uses native casts for matching-endian complex formats.
|
||||
if E::convert(1u16) != 1 && narrowed.is_infinite() != component.is_infinite() {
|
||||
return Err(PackError {
|
||||
kind: PackErrorKind::Value,
|
||||
exception: vm.new_overflow_error(concat!(
|
||||
"float too large to pack with ",
|
||||
$fmt,
|
||||
" format"
|
||||
)),
|
||||
});
|
||||
}
|
||||
narrowed.to_bits().pack_int::<E>(bytes);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -815,8 +832,8 @@ macro_rules! make_pack_complex {
|
||||
};
|
||||
}
|
||||
|
||||
make_pack_complex!(PackFloatComplex, f32, u32);
|
||||
make_pack_complex!(PackDoubleComplex, f64, u64);
|
||||
make_pack_complex!(PackFloatComplex, f32, u32, "f");
|
||||
make_pack_complex!(PackDoubleComplex, f64, u64, "d");
|
||||
|
||||
impl Packable for f16 {
|
||||
fn pack<E: ByteOrder>(
|
||||
|
||||
@@ -94,7 +94,9 @@ impl PyObjectRef {
|
||||
let result = method?.call((), vm)?;
|
||||
|
||||
let ret_class = result.class().to_owned();
|
||||
if let Some(ret) = result.downcast_ref::<PyComplex>() {
|
||||
if let Some(ret) = result.downcast_ref::<PyComplex>()
|
||||
&& !result.class().is(vm.ctx.types.complex_type)
|
||||
{
|
||||
_warnings::warn(
|
||||
vm.ctx.exceptions.deprecation_warning,
|
||||
format!(
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
import struct
|
||||
import sys
|
||||
import warnings
|
||||
|
||||
from testutils import assert_raises
|
||||
|
||||
|
||||
class ComplexLike:
|
||||
def __complex__(self):
|
||||
return 1 + 2j
|
||||
|
||||
|
||||
class ComplexSubclass(complex):
|
||||
def __complex__(self):
|
||||
raise AssertionError("complex subclasses use their stored value")
|
||||
|
||||
|
||||
with warnings.catch_warnings():
|
||||
warnings.simplefilter("error")
|
||||
for code in ("F", "D"):
|
||||
expected = struct.pack(code, 1 + 2j)
|
||||
assert struct.pack(code, ComplexLike()) == expected
|
||||
assert struct.pack(code, ComplexSubclass(1, 2)) == expected
|
||||
|
||||
native = "<" if sys.byteorder == "little" else ">"
|
||||
swapped = ">" if sys.byteorder == "little" else "<"
|
||||
for value in (complex(1e300, 0), complex(0, 1e300)):
|
||||
assert struct.pack(native + "F", value) == struct.pack("@F", value)
|
||||
with assert_raises(OverflowError):
|
||||
struct.pack(swapped + "F", value)
|
||||
Reference in new issue
Block a user