mirror of
https://github.com/RustPython/RustPython.git
synced 2026-06-02 19:39:49 +09:00
Merge pull request #2335 from carbotaniuman/number-reform
Return same object if exact type in int, float, complex and fix pow
This commit is contained in:
@@ -82,8 +82,6 @@ class ComplexTest(unittest.TestCase):
|
||||
q = z.__truediv__(y)
|
||||
self.assertClose(q, x)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_truediv(self):
|
||||
simple_real = [float(i) for i in range(-5, 6)]
|
||||
simple_complex = [complex(x, y) for x in simple_real for y in simple_real]
|
||||
@@ -171,8 +169,6 @@ class ComplexTest(unittest.TestCase):
|
||||
self.assertRaises(TypeError, divmod, 1+1j, 1+0j)
|
||||
self.assertRaises(TypeError, divmod, 1+1j, 0+0j)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_pow(self):
|
||||
self.assertAlmostEqual(pow(1+1j, 0+0j), 1.0)
|
||||
self.assertAlmostEqual(pow(0+0j, 2+0j), 0.0)
|
||||
@@ -416,8 +412,6 @@ class ComplexTest(unittest.TestCase):
|
||||
self.assertEqual(complex(complex1(1j)), 2j)
|
||||
self.assertRaises(TypeError, complex, complex2(1j))
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
@support.requires_IEEE_754
|
||||
def test_constructor_special_numbers(self):
|
||||
class complex2(complex):
|
||||
|
||||
@@ -4,8 +4,9 @@ use num_traits::Zero;
|
||||
use super::float;
|
||||
use super::pystr::PyStr;
|
||||
use super::pytype::PyTypeRef;
|
||||
use crate::function::{OptionalArg, OptionalOption};
|
||||
use crate::pyobject::{
|
||||
BorrowValue, IntoPyObject, Never,
|
||||
BorrowValue, IdProtocol, IntoPyObject, Never,
|
||||
PyArithmaticValue::{self, *},
|
||||
PyClassImpl, PyComparisonValue, PyContext, PyObjectRef, PyRef, PyResult, PyValue, TypeProtocol,
|
||||
};
|
||||
@@ -44,27 +45,6 @@ pub fn init(context: &PyContext) {
|
||||
PyComplex::extend_class(context, &context.types.complex_type);
|
||||
}
|
||||
|
||||
fn try_complex(obj: &PyObjectRef, vm: &VirtualMachine) -> PyResult<Option<Complex64>> {
|
||||
if let Some(complex) = obj.payload_if_exact::<PyComplex>(vm) {
|
||||
return Ok(Some(complex.value));
|
||||
}
|
||||
if let Some(method) = vm.get_method(obj.clone(), "__complex__") {
|
||||
let result = vm.invoke(&method?, ())?;
|
||||
// TODO: returning strict subclasses of complex in __complex__ is deprecated
|
||||
return match result.payload::<PyComplex>() {
|
||||
Some(complex_obj) => Ok(Some(complex_obj.value)),
|
||||
None => Err(vm.new_type_error(format!(
|
||||
"__complex__ returned non-complex (type '{}')",
|
||||
result.class().name
|
||||
))),
|
||||
};
|
||||
}
|
||||
if let Some(float) = float::try_float(obj, vm)? {
|
||||
return Ok(Some(Complex64::new(float, 0.0)));
|
||||
}
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
fn to_op_complex(value: &PyObjectRef, vm: &VirtualMachine) -> PyResult<Option<Complex64>> {
|
||||
let r = if let Some(complex) = value.payload_if_subclass::<PyComplex>(vm) {
|
||||
Some(complex.value)
|
||||
@@ -84,6 +64,26 @@ fn inner_div(v1: Complex64, v2: Complex64, vm: &VirtualMachine) -> PyResult<Comp
|
||||
Ok(v1.fdiv(v2))
|
||||
}
|
||||
|
||||
fn inner_pow(v1: Complex64, v2: Complex64, vm: &VirtualMachine) -> PyResult<Complex64> {
|
||||
if v1.is_zero() {
|
||||
return if v2.im != 0.0 {
|
||||
let msg = format!("{} cannot be raised to a negative or complex power", v1);
|
||||
Err(vm.new_zero_division_error(msg))
|
||||
} else if v2.is_zero() {
|
||||
Ok(Complex64::new(1.0, 0.0))
|
||||
} else {
|
||||
Ok(Complex64::new(0.0, 0.0))
|
||||
};
|
||||
}
|
||||
|
||||
let ans = v1.powc(v2);
|
||||
if ans.is_infinite() && !(v1.is_infinite() || v2.is_infinite()) {
|
||||
Err(vm.new_overflow_error("complex exponentiation overflow".to_owned()))
|
||||
} else {
|
||||
Ok(ans)
|
||||
}
|
||||
}
|
||||
|
||||
#[pyimpl(flags(BASETYPE), with(Comparable, Hashable))]
|
||||
impl PyComplex {
|
||||
pub fn to_complex(&self) -> Complex64 {
|
||||
@@ -235,9 +235,14 @@ impl PyComplex {
|
||||
fn pow(
|
||||
&self,
|
||||
other: PyObjectRef,
|
||||
mod_val: OptionalOption<PyObjectRef>,
|
||||
vm: &VirtualMachine,
|
||||
) -> PyResult<PyArithmaticValue<Complex64>> {
|
||||
self.op(other, |a, b| Ok(a.powc(b)), vm)
|
||||
if mod_val.flatten().is_some() {
|
||||
Err(vm.new_value_error("complex modulo not allowed".to_owned()))
|
||||
} else {
|
||||
self.op(other, |a, b| Ok(inner_pow(a, b, vm)?), vm)
|
||||
}
|
||||
}
|
||||
|
||||
#[pymethod(name = "__rpow__")]
|
||||
@@ -246,7 +251,7 @@ impl PyComplex {
|
||||
other: PyObjectRef,
|
||||
vm: &VirtualMachine,
|
||||
) -> PyResult<PyArithmaticValue<Complex64>> {
|
||||
self.op(other, |a, b| Ok(b.powc(a)), vm)
|
||||
self.op(other, |a, b| Ok(inner_pow(b, a, vm)?), vm)
|
||||
}
|
||||
|
||||
#[pymethod(name = "__bool__")]
|
||||
@@ -256,13 +261,25 @@ impl PyComplex {
|
||||
|
||||
#[pyslot]
|
||||
fn tp_new(cls: PyTypeRef, args: ComplexArgs, vm: &VirtualMachine) -> PyResult<PyRef<Self>> {
|
||||
let real = match args.real {
|
||||
None => Complex64::new(0.0, 0.0),
|
||||
Some(obj) => {
|
||||
if let Some(c) = try_complex(&obj, vm)? {
|
||||
let imag_missing = args.imag.is_missing();
|
||||
let (real, real_was_complex) = match args.real {
|
||||
OptionalArg::Missing => (Complex64::new(0.0, 0.0), false),
|
||||
OptionalArg::Present(val) => {
|
||||
let val = if cls.is(&vm.ctx.types.complex_type) && imag_missing {
|
||||
match val.downcast_exact::<PyComplex>(vm) {
|
||||
Ok(c) => {
|
||||
return Ok(c);
|
||||
}
|
||||
Err(val) => val,
|
||||
}
|
||||
} else {
|
||||
val
|
||||
};
|
||||
|
||||
if let Some(c) = try_complex(&val, vm)? {
|
||||
c
|
||||
} else if let Some(s) = obj.payload_if_subclass::<PyStr>(vm) {
|
||||
if args.imag.is_some() {
|
||||
} else if let Some(s) = val.payload_if_subclass::<PyStr>(vm) {
|
||||
if args.imag.is_present() {
|
||||
return Err(vm.new_type_error(
|
||||
"complex() can't take second arg if first is a string".to_owned(),
|
||||
));
|
||||
@@ -274,15 +291,17 @@ impl PyComplex {
|
||||
} else {
|
||||
return Err(vm.new_type_error(format!(
|
||||
"complex() first argument must be a string or a number, not '{}'",
|
||||
obj.class().name
|
||||
val.class().name
|
||||
)));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let imag = match args.imag {
|
||||
None => Complex64::new(0.0, 0.0),
|
||||
Some(obj) => {
|
||||
let (imag, imag_was_complex) = match args.imag {
|
||||
// Copy the imaginary from the real to the real of the imaginary
|
||||
// if an imaginary argument is not passed in
|
||||
OptionalArg::Missing => (Complex64::new(real.im, 0.0), false),
|
||||
OptionalArg::Present(obj) => {
|
||||
if let Some(c) = try_complex(&obj, vm)? {
|
||||
c
|
||||
} else if obj.class().issubclass(&vm.ctx.types.str_type) {
|
||||
@@ -298,7 +317,18 @@ impl PyComplex {
|
||||
}
|
||||
};
|
||||
|
||||
let value = Complex64::new(real.re - imag.im, real.im + imag.re);
|
||||
let final_real = if imag_was_complex {
|
||||
real.re - imag.im
|
||||
} else {
|
||||
real.re
|
||||
};
|
||||
|
||||
let final_imag = if real_was_complex && !imag_missing {
|
||||
imag.re + real.im
|
||||
} else {
|
||||
imag.re
|
||||
};
|
||||
let value = Complex64::new(final_real, final_imag);
|
||||
Self::from(value).into_ref_with_type(vm, cls)
|
||||
}
|
||||
|
||||
@@ -340,10 +370,10 @@ impl Hashable for PyComplex {
|
||||
|
||||
#[derive(FromArgs)]
|
||||
struct ComplexArgs {
|
||||
#[pyarg(any, default)]
|
||||
real: Option<PyObjectRef>,
|
||||
#[pyarg(any, default)]
|
||||
imag: Option<PyObjectRef>,
|
||||
#[pyarg(any, optional)]
|
||||
real: OptionalArg<PyObjectRef>,
|
||||
#[pyarg(any, optional)]
|
||||
imag: OptionalArg<PyObjectRef>,
|
||||
}
|
||||
|
||||
fn parse_str(s: &str) -> Option<Complex64> {
|
||||
@@ -382,3 +412,31 @@ fn parse_str(s: &str) -> Option<Complex64> {
|
||||
};
|
||||
Some(value)
|
||||
}
|
||||
|
||||
/// Tries converting a python object into a complex, returns an option of whether the complex
|
||||
/// and whether the object was a complex originally or coereced into one
|
||||
fn try_complex(obj: &PyObjectRef, vm: &VirtualMachine) -> PyResult<Option<(Complex64, bool)>> {
|
||||
if let Some(complex) = obj.payload_if_exact::<PyComplex>(vm) {
|
||||
return Ok(Some((complex.value, true)));
|
||||
}
|
||||
if let Some(method) = vm.get_method(obj.clone(), "__complex__") {
|
||||
let result = vm.invoke(&method?, ())?;
|
||||
// TODO: returning strict subclasses of complex in __complex__ is deprecated
|
||||
return match result.payload::<PyComplex>() {
|
||||
Some(complex_obj) => Ok(Some((complex_obj.value, true))),
|
||||
None => Err(vm.new_type_error(format!(
|
||||
"__complex__ returned non-complex (type '{}')",
|
||||
result.class().name
|
||||
))),
|
||||
};
|
||||
}
|
||||
// `complex` does not have a `__complex__` by default, so subclasses might not either,
|
||||
// use the actual stored value in this case
|
||||
if let Some(complex) = obj.payload_if_subclass::<PyComplex>(vm) {
|
||||
return Ok(Some((complex.value, true)));
|
||||
}
|
||||
if let Some(float) = float::try_float(obj, vm)? {
|
||||
return Ok(Some((Complex64::new(float, 0.0), false)));
|
||||
}
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ use super::pytype::PyTypeRef;
|
||||
use crate::format::FormatSpec;
|
||||
use crate::function::{OptionalArg, OptionalOption};
|
||||
use crate::pyobject::{
|
||||
BorrowValue, IntoPyObject,
|
||||
BorrowValue, IdProtocol, IntoPyObject,
|
||||
PyArithmaticValue::{self, *},
|
||||
PyClassImpl, PyComparisonValue, PyContext, PyObjectRef, PyRef, PyResult, PyValue,
|
||||
TryFromObject, TypeProtocol,
|
||||
@@ -163,7 +163,19 @@ impl PyFloat {
|
||||
vm: &VirtualMachine,
|
||||
) -> PyResult<PyRef<Self>> {
|
||||
let float_val = match arg {
|
||||
OptionalArg::Missing => 0.0,
|
||||
OptionalArg::Present(val) => {
|
||||
let val = if cls.is(&vm.ctx.types.float_type) {
|
||||
match val.downcast_exact::<PyFloat>(vm) {
|
||||
Ok(f) => {
|
||||
return Ok(f);
|
||||
}
|
||||
Err(val) => val,
|
||||
}
|
||||
} else {
|
||||
val
|
||||
};
|
||||
|
||||
if let Some(f) = try_float(&val, vm)? {
|
||||
f
|
||||
} else if let Some(s) = val.payload_if_subclass::<PyStr>(vm) {
|
||||
@@ -184,7 +196,6 @@ impl PyFloat {
|
||||
)));
|
||||
}
|
||||
}
|
||||
OptionalArg::Missing => 0.0,
|
||||
};
|
||||
PyFloat::from(float_val).into_ref_with_type(vm, cls)
|
||||
}
|
||||
@@ -315,8 +326,17 @@ impl PyFloat {
|
||||
}
|
||||
|
||||
#[pymethod(name = "__pow__")]
|
||||
fn pow(&self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult {
|
||||
self.complex_op(other, |a, b| float_pow(a, b, vm), vm)
|
||||
fn pow(
|
||||
&self,
|
||||
other: PyObjectRef,
|
||||
mod_val: OptionalOption<PyObjectRef>,
|
||||
vm: &VirtualMachine,
|
||||
) -> PyResult {
|
||||
if mod_val.flatten().is_some() {
|
||||
Err(vm.new_type_error("floating point pow() does not accept a 3rd argument".to_owned()))
|
||||
} else {
|
||||
self.complex_op(other, |a, b| float_pow(a, b, vm), vm)
|
||||
}
|
||||
}
|
||||
|
||||
#[pymethod(name = "__rpow__")]
|
||||
|
||||
@@ -14,7 +14,7 @@ use super::pybool::IntoPyBool;
|
||||
use super::pystr::{PyStr, PyStrRef};
|
||||
use super::pytype::PyTypeRef;
|
||||
use crate::format::FormatSpec;
|
||||
use crate::function::OptionalArg;
|
||||
use crate::function::{OptionalArg, OptionalOption};
|
||||
use crate::pyobject::{
|
||||
BorrowValue, IdProtocol, IntoPyObject, IntoPyResult, PyArithmaticValue, PyClassImpl,
|
||||
PyComparisonValue, PyContext, PyObjectRef, PyRef, PyResult, PyValue, TryFromObject,
|
||||
@@ -262,6 +262,17 @@ impl PyInt {
|
||||
})?;
|
||||
try_int_radix(&val, base, vm)
|
||||
} else {
|
||||
let val = if cls.is(&vm.ctx.types.int_type) {
|
||||
match val.downcast_exact::<PyInt>(vm) {
|
||||
Ok(i) => {
|
||||
return Ok(i);
|
||||
}
|
||||
Err(val) => val,
|
||||
}
|
||||
} else {
|
||||
val
|
||||
};
|
||||
|
||||
try_int(&val, vm)
|
||||
}
|
||||
} else if let OptionalArg::Present(_) = options.base {
|
||||
@@ -397,8 +408,37 @@ impl PyInt {
|
||||
}
|
||||
|
||||
#[pymethod(name = "__pow__")]
|
||||
fn pow(&self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult {
|
||||
self.general_op(other, |a, b| inner_pow(a, b, vm), vm)
|
||||
fn pow(
|
||||
&self,
|
||||
other: PyObjectRef,
|
||||
mod_val: OptionalOption<PyObjectRef>,
|
||||
vm: &VirtualMachine,
|
||||
) -> PyResult {
|
||||
match mod_val.flatten() {
|
||||
Some(int_ref) => {
|
||||
let int = match int_ref.payload_if_subclass::<PyInt>(vm) {
|
||||
Some(val) => val,
|
||||
None => return Ok(vm.ctx.not_implemented()),
|
||||
};
|
||||
|
||||
let modulus = int.borrow_value();
|
||||
if modulus.is_zero() {
|
||||
return Err(vm.new_value_error("pow() 3rd argument cannot be 0".to_owned()));
|
||||
}
|
||||
self.general_op(
|
||||
other,
|
||||
|a, b| {
|
||||
if b.is_negative() {
|
||||
Err(vm.new_value_error("modular inverses not supported".to_owned()))
|
||||
} else {
|
||||
Ok(vm.ctx.new_int(a.modpow(b, modulus)))
|
||||
}
|
||||
},
|
||||
vm,
|
||||
)
|
||||
}
|
||||
None => self.general_op(other, |a, b| inner_pow(a, b, vm), vm),
|
||||
}
|
||||
}
|
||||
|
||||
#[pymethod(name = "__rpow__")]
|
||||
@@ -709,66 +749,6 @@ struct IntToByteArgs {
|
||||
signed: OptionalArg<IntoPyBool>,
|
||||
}
|
||||
|
||||
// Casting function:
|
||||
pub(crate) fn try_int(obj: &PyObjectRef, vm: &VirtualMachine) -> PyResult<BigInt> {
|
||||
fn try_convert(obj: &PyObjectRef, lit: &[u8], vm: &VirtualMachine) -> PyResult<BigInt> {
|
||||
let base = 10;
|
||||
match bytes_to_int(lit, base) {
|
||||
Some(i) => Ok(i),
|
||||
None => Err(vm.new_value_error(format!(
|
||||
"invalid literal for int() with base {}: {}",
|
||||
base,
|
||||
vm.to_repr(obj)?,
|
||||
))),
|
||||
}
|
||||
};
|
||||
|
||||
// test for strings and bytes
|
||||
if let Some(s) = obj.downcast_ref::<PyStr>() {
|
||||
return try_convert(obj, s.borrow_value().as_bytes(), vm);
|
||||
}
|
||||
if let Ok(r) = try_bytes_like(vm, &obj, |x| try_convert(obj, x, vm)) {
|
||||
return r;
|
||||
}
|
||||
// strict `int` check
|
||||
if let Some(int) = obj.payload_if_exact::<PyInt>(vm) {
|
||||
return Ok(int.borrow_value().clone());
|
||||
}
|
||||
// call __int__, then __index__, then __trunc__ (converting the __trunc__ result via __index__ if needed)
|
||||
// TODO: using __int__ is deprecated and removed in Python 3.10
|
||||
if let Some(method) = vm.get_method(obj.clone(), "__int__") {
|
||||
let result = vm.invoke(&method?, ())?;
|
||||
return match result.payload::<PyInt>() {
|
||||
Some(int_obj) => Ok(int_obj.borrow_value().clone()),
|
||||
None => Err(vm.new_type_error(format!(
|
||||
"__int__ returned non-int (type '{}')",
|
||||
result.class().name
|
||||
))),
|
||||
};
|
||||
}
|
||||
// TODO: returning strict subclasses of int in __index__ is deprecated
|
||||
if let Some(r) = vm.to_index_opt(obj.clone()).transpose()? {
|
||||
return Ok(r.borrow_value().clone());
|
||||
}
|
||||
if let Some(method) = vm.get_method(obj.clone(), "__trunc__") {
|
||||
let result = vm.invoke(&method?, ())?;
|
||||
return vm
|
||||
.to_index_opt(result.clone())
|
||||
.unwrap_or_else(|| {
|
||||
Err(vm.new_type_error(format!(
|
||||
"__trunc__ returned non-Integral (type '{}')",
|
||||
result.class().name
|
||||
)))
|
||||
})
|
||||
.map(|int_obj| int_obj.borrow_value().clone());
|
||||
}
|
||||
|
||||
Err(vm.new_type_error(format!(
|
||||
"int() argument must be a string, a bytes-like object or a number, not '{}'",
|
||||
obj.class().name
|
||||
)))
|
||||
}
|
||||
|
||||
fn try_int_radix(obj: &PyObjectRef, base: u32, vm: &VirtualMachine) -> PyResult<BigInt> {
|
||||
debug_assert!(base == 0 || (2..=36).contains(&base));
|
||||
|
||||
@@ -917,6 +897,65 @@ pub fn to_float(int: &BigInt, vm: &VirtualMachine) -> PyResult<f64> {
|
||||
.ok_or_else(|| vm.new_overflow_error("int too large to convert to float".to_owned()))
|
||||
}
|
||||
|
||||
pub(crate) fn try_int(obj: &PyObjectRef, vm: &VirtualMachine) -> PyResult<BigInt> {
|
||||
fn try_convert(obj: &PyObjectRef, lit: &[u8], vm: &VirtualMachine) -> PyResult<BigInt> {
|
||||
let base = 10;
|
||||
match bytes_to_int(lit, base) {
|
||||
Some(i) => Ok(i),
|
||||
None => Err(vm.new_value_error(format!(
|
||||
"invalid literal for int() with base {}: {}",
|
||||
base,
|
||||
vm.to_repr(obj)?,
|
||||
))),
|
||||
}
|
||||
};
|
||||
|
||||
// test for strings and bytes
|
||||
if let Some(s) = obj.downcast_ref::<PyStr>() {
|
||||
return try_convert(obj, s.borrow_value().as_bytes(), vm);
|
||||
}
|
||||
if let Ok(r) = try_bytes_like(vm, &obj, |x| try_convert(obj, x, vm)) {
|
||||
return r;
|
||||
}
|
||||
// strict `int` check
|
||||
if let Some(int) = obj.payload_if_exact::<PyInt>(vm) {
|
||||
return Ok(int.borrow_value().clone());
|
||||
}
|
||||
// call __int__, then __index__, then __trunc__ (converting the __trunc__ result via __index__ if needed)
|
||||
// TODO: using __int__ is deprecated and removed in Python 3.10
|
||||
if let Some(method) = vm.get_method(obj.clone(), "__int__") {
|
||||
let result = vm.invoke(&method?, ())?;
|
||||
return match result.payload::<PyInt>() {
|
||||
Some(int_obj) => Ok(int_obj.borrow_value().clone()),
|
||||
None => Err(vm.new_type_error(format!(
|
||||
"__int__ returned non-int (type '{}')",
|
||||
result.class().name
|
||||
))),
|
||||
};
|
||||
}
|
||||
// TODO: returning strict subclasses of int in __index__ is deprecated
|
||||
if let Some(r) = vm.to_index_opt(obj.clone()).transpose()? {
|
||||
return Ok(r.borrow_value().clone());
|
||||
}
|
||||
if let Some(method) = vm.get_method(obj.clone(), "__trunc__") {
|
||||
let result = vm.invoke(&method?, ())?;
|
||||
return vm
|
||||
.to_index_opt(result.clone())
|
||||
.unwrap_or_else(|| {
|
||||
Err(vm.new_type_error(format!(
|
||||
"__trunc__ returned non-Integral (type '{}')",
|
||||
result.class().name
|
||||
)))
|
||||
})
|
||||
.map(|int_obj| int_obj.borrow_value().clone());
|
||||
}
|
||||
|
||||
Err(vm.new_type_error(format!(
|
||||
"int() argument must be a string, a bytes-like object or a number, not '{}'",
|
||||
obj.class().name
|
||||
)))
|
||||
}
|
||||
|
||||
pub(crate) fn init(context: &PyContext) {
|
||||
PyInt::extend_class(context, &context.types.int_type);
|
||||
}
|
||||
|
||||
@@ -19,16 +19,19 @@ mod decl {
|
||||
use crate::builtins::pybool::{self, IntoPyBool};
|
||||
use crate::builtins::pystr::{PyStr, PyStrRef};
|
||||
use crate::builtins::pytype::PyTypeRef;
|
||||
use crate::builtins::PyInt;
|
||||
use crate::byteslike::PyBytesLike;
|
||||
use crate::common::{hash::PyHash, str::to_ascii};
|
||||
#[cfg(feature = "rustpython-compiler")]
|
||||
use crate::compile;
|
||||
use crate::exceptions::PyBaseExceptionRef;
|
||||
use crate::function::{single_or_tuple_any, Args, FuncArgs, KwArgs, OptionalArg};
|
||||
use crate::function::{
|
||||
single_or_tuple_any, Args, FuncArgs, KwArgs, OptionalArg, OptionalOption,
|
||||
};
|
||||
use crate::iterator;
|
||||
use crate::pyobject::{
|
||||
BorrowValue, Either, IdProtocol, ItemProtocol, PyCallable, PyIterable, PyObjectRef,
|
||||
PyResult, PyValue, TryFromObject, TypeProtocol,
|
||||
BorrowValue, Either, IdProtocol, ItemProtocol, PyArithmaticValue, PyCallable, PyIterable,
|
||||
PyObjectRef, PyResult, PyValue, TryFromObject, TypeProtocol,
|
||||
};
|
||||
use crate::readline::{Readline, ReadlineResult};
|
||||
use crate::scope::Scope;
|
||||
@@ -177,7 +180,7 @@ mod decl {
|
||||
#[pyfunction]
|
||||
fn divmod(a: PyObjectRef, b: PyObjectRef, vm: &VirtualMachine) -> PyResult {
|
||||
vm.call_or_reflection(&a, &b, "__divmod__", "__rdivmod__", |vm, a, b| {
|
||||
Err(vm.new_unsupported_operand_error(a, b, "divmod"))
|
||||
Err(vm.new_unsupported_binop_error(a, b, "divmod"))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -582,40 +585,53 @@ mod decl {
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::suspicious_else_formatting)]
|
||||
#[pyfunction]
|
||||
fn pow(
|
||||
x: PyObjectRef,
|
||||
y: PyObjectRef,
|
||||
mod_value: OptionalArg<PyIntRef>,
|
||||
mod_value: OptionalOption<PyObjectRef>,
|
||||
vm: &VirtualMachine,
|
||||
) -> PyResult {
|
||||
match mod_value {
|
||||
OptionalArg::Missing => {
|
||||
vm.call_or_reflection(&x, &y, "__pow__", "__rpow__", |vm, x, y| {
|
||||
Err(vm.new_unsupported_operand_error(x, y, "pow"))
|
||||
})
|
||||
}
|
||||
OptionalArg::Present(m) => {
|
||||
// Check if the 3rd argument is defined and perform modulus on the result
|
||||
if !(x.isinstance(&vm.ctx.types.int_type) && y.isinstance(&vm.ctx.types.int_type)) {
|
||||
return Err(vm.new_type_error(
|
||||
"pow() 3rd argument not allowed unless all arguments are integers"
|
||||
.to_owned(),
|
||||
));
|
||||
match mod_value.flatten() {
|
||||
None => vm.call_or_reflection(&x, &y, "__pow__", "__rpow__", |vm, x, y| {
|
||||
Err(vm.new_unsupported_binop_error(x, y, "pow"))
|
||||
}),
|
||||
Some(z) => {
|
||||
let try_pow_value = |obj: &PyObjectRef,
|
||||
args: (PyObjectRef, PyObjectRef, PyObjectRef)|
|
||||
-> Option<PyResult> {
|
||||
if let Some(method) = obj.get_class_attr("__pow__") {
|
||||
let result = match vm.invoke(&method, args) {
|
||||
Ok(x) => x,
|
||||
Err(e) => return Some(Err(e)),
|
||||
};
|
||||
if let PyArithmaticValue::Implemented(x) =
|
||||
PyArithmaticValue::from_object(vm, result)
|
||||
{
|
||||
return Some(Ok(x));
|
||||
}
|
||||
}
|
||||
None
|
||||
};
|
||||
|
||||
if let Some(val) = try_pow_value(&x, (x.clone(), y.clone(), z.clone())) {
|
||||
return val;
|
||||
}
|
||||
let y = int::get_value(&y);
|
||||
if y.sign() == Sign::Minus {
|
||||
return Err(vm.new_value_error(
|
||||
"pow() 2nd argument cannot be negative when 3rd argument specified"
|
||||
.to_owned(),
|
||||
));
|
||||
|
||||
if !x.class().is(&y.class()) {
|
||||
if let Some(val) = try_pow_value(&y, (x.clone(), y.clone(), z.clone())) {
|
||||
return val;
|
||||
}
|
||||
}
|
||||
let m = m.borrow_value();
|
||||
if m.is_zero() {
|
||||
return Err(vm.new_value_error("pow() 3rd argument cannot be 0".to_owned()));
|
||||
|
||||
if !x.class().is(&z.class()) && !y.class().is(&z.class()) {
|
||||
if let Some(val) = try_pow_value(&z, (x.clone(), y.clone(), z.clone())) {
|
||||
return val;
|
||||
}
|
||||
}
|
||||
let x = int::get_value(&x);
|
||||
Ok(vm.ctx.new_int(x.modpow(&y, &m)))
|
||||
|
||||
Err(vm.new_unsupported_ternop_error(&x, &y, &z, "pow"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
83
vm/src/vm.rs
83
vm/src/vm.rs
@@ -599,7 +599,7 @@ impl VirtualMachine {
|
||||
self.new_exception_msg(name_error, msg)
|
||||
}
|
||||
|
||||
pub fn new_unsupported_operand_error(
|
||||
pub fn new_unsupported_binop_error(
|
||||
&self,
|
||||
a: &PyObjectRef,
|
||||
b: &PyObjectRef,
|
||||
@@ -613,6 +613,22 @@ impl VirtualMachine {
|
||||
))
|
||||
}
|
||||
|
||||
pub fn new_unsupported_ternop_error(
|
||||
&self,
|
||||
a: &PyObjectRef,
|
||||
b: &PyObjectRef,
|
||||
c: &PyObjectRef,
|
||||
op: &str,
|
||||
) -> PyBaseExceptionRef {
|
||||
self.new_type_error(format!(
|
||||
"Unsupported operand types for '{}': '{}', '{}', and '{}'",
|
||||
op,
|
||||
a.class().name,
|
||||
b.class().name,
|
||||
c.class().name
|
||||
))
|
||||
}
|
||||
|
||||
pub fn new_os_error(&self, msg: String) -> PyBaseExceptionRef {
|
||||
let os_error = self.ctx.exceptions.os_error.clone();
|
||||
self.new_exception_msg(os_error, msg)
|
||||
@@ -1153,10 +1169,15 @@ impl VirtualMachine {
|
||||
// Try to call the default method
|
||||
self.call_or_unsupported(lhs, rhs, default, move |vm, lhs, rhs| {
|
||||
// Try to call the reflection method
|
||||
vm.call_or_unsupported(rhs, lhs, reflection, |_, rhs, lhs| {
|
||||
// switch them around again
|
||||
// don't call reflection method if operands are of the same type
|
||||
if !lhs.class().is(&rhs.class()) {
|
||||
vm.call_or_unsupported(rhs, lhs, reflection, |_, rhs, lhs| {
|
||||
// switch them around again
|
||||
unsupported(vm, lhs, rhs)
|
||||
})
|
||||
} else {
|
||||
unsupported(vm, lhs, rhs)
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1289,182 +1310,182 @@ impl VirtualMachine {
|
||||
|
||||
pub fn _sub(&self, a: &PyObjectRef, b: &PyObjectRef) -> PyResult {
|
||||
self.call_or_reflection(a, b, "__sub__", "__rsub__", |vm, a, b| {
|
||||
Err(vm.new_unsupported_operand_error(a, b, "-"))
|
||||
Err(vm.new_unsupported_binop_error(a, b, "-"))
|
||||
})
|
||||
}
|
||||
|
||||
pub fn _isub(&self, a: &PyObjectRef, b: &PyObjectRef) -> PyResult {
|
||||
self.call_or_unsupported(a, b, "__isub__", |vm, a, b| {
|
||||
vm.call_or_reflection(a, b, "__sub__", "__rsub__", |vm, a, b| {
|
||||
Err(vm.new_unsupported_operand_error(a, b, "-="))
|
||||
Err(vm.new_unsupported_binop_error(a, b, "-="))
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
pub fn _add(&self, a: &PyObjectRef, b: &PyObjectRef) -> PyResult {
|
||||
self.call_or_reflection(a, b, "__add__", "__radd__", |vm, a, b| {
|
||||
Err(vm.new_unsupported_operand_error(a, b, "+"))
|
||||
Err(vm.new_unsupported_binop_error(a, b, "+"))
|
||||
})
|
||||
}
|
||||
|
||||
pub fn _iadd(&self, a: &PyObjectRef, b: &PyObjectRef) -> PyResult {
|
||||
self.call_or_unsupported(a, b, "__iadd__", |vm, a, b| {
|
||||
vm.call_or_reflection(a, b, "__add__", "__radd__", |vm, a, b| {
|
||||
Err(vm.new_unsupported_operand_error(a, b, "+="))
|
||||
Err(vm.new_unsupported_binop_error(a, b, "+="))
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
pub fn _mul(&self, a: &PyObjectRef, b: &PyObjectRef) -> PyResult {
|
||||
self.call_or_reflection(a, b, "__mul__", "__rmul__", |vm, a, b| {
|
||||
Err(vm.new_unsupported_operand_error(a, b, "*"))
|
||||
Err(vm.new_unsupported_binop_error(a, b, "*"))
|
||||
})
|
||||
}
|
||||
|
||||
pub fn _imul(&self, a: &PyObjectRef, b: &PyObjectRef) -> PyResult {
|
||||
self.call_or_unsupported(a, b, "__imul__", |vm, a, b| {
|
||||
vm.call_or_reflection(a, b, "__mul__", "__rmul__", |vm, a, b| {
|
||||
Err(vm.new_unsupported_operand_error(a, b, "*="))
|
||||
Err(vm.new_unsupported_binop_error(a, b, "*="))
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
pub fn _matmul(&self, a: &PyObjectRef, b: &PyObjectRef) -> PyResult {
|
||||
self.call_or_reflection(a, b, "__matmul__", "__rmatmul__", |vm, a, b| {
|
||||
Err(vm.new_unsupported_operand_error(a, b, "@"))
|
||||
Err(vm.new_unsupported_binop_error(a, b, "@"))
|
||||
})
|
||||
}
|
||||
|
||||
pub fn _imatmul(&self, a: &PyObjectRef, b: &PyObjectRef) -> PyResult {
|
||||
self.call_or_unsupported(a, b, "__imatmul__", |vm, a, b| {
|
||||
vm.call_or_reflection(a, b, "__matmul__", "__rmatmul__", |vm, a, b| {
|
||||
Err(vm.new_unsupported_operand_error(a, b, "@="))
|
||||
Err(vm.new_unsupported_binop_error(a, b, "@="))
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
pub fn _truediv(&self, a: &PyObjectRef, b: &PyObjectRef) -> PyResult {
|
||||
self.call_or_reflection(a, b, "__truediv__", "__rtruediv__", |vm, a, b| {
|
||||
Err(vm.new_unsupported_operand_error(a, b, "/"))
|
||||
Err(vm.new_unsupported_binop_error(a, b, "/"))
|
||||
})
|
||||
}
|
||||
|
||||
pub fn _itruediv(&self, a: &PyObjectRef, b: &PyObjectRef) -> PyResult {
|
||||
self.call_or_unsupported(a, b, "__itruediv__", |vm, a, b| {
|
||||
vm.call_or_reflection(a, b, "__truediv__", "__rtruediv__", |vm, a, b| {
|
||||
Err(vm.new_unsupported_operand_error(a, b, "/="))
|
||||
Err(vm.new_unsupported_binop_error(a, b, "/="))
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
pub fn _floordiv(&self, a: &PyObjectRef, b: &PyObjectRef) -> PyResult {
|
||||
self.call_or_reflection(a, b, "__floordiv__", "__rfloordiv__", |vm, a, b| {
|
||||
Err(vm.new_unsupported_operand_error(a, b, "//"))
|
||||
Err(vm.new_unsupported_binop_error(a, b, "//"))
|
||||
})
|
||||
}
|
||||
|
||||
pub fn _ifloordiv(&self, a: &PyObjectRef, b: &PyObjectRef) -> PyResult {
|
||||
self.call_or_unsupported(a, b, "__ifloordiv__", |vm, a, b| {
|
||||
vm.call_or_reflection(a, b, "__floordiv__", "__rfloordiv__", |vm, a, b| {
|
||||
Err(vm.new_unsupported_operand_error(a, b, "//="))
|
||||
Err(vm.new_unsupported_binop_error(a, b, "//="))
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
pub fn _pow(&self, a: &PyObjectRef, b: &PyObjectRef) -> PyResult {
|
||||
self.call_or_reflection(a, b, "__pow__", "__rpow__", |vm, a, b| {
|
||||
Err(vm.new_unsupported_operand_error(a, b, "**"))
|
||||
Err(vm.new_unsupported_binop_error(a, b, "**"))
|
||||
})
|
||||
}
|
||||
|
||||
pub fn _ipow(&self, a: &PyObjectRef, b: &PyObjectRef) -> PyResult {
|
||||
self.call_or_unsupported(a, b, "__ipow__", |vm, a, b| {
|
||||
vm.call_or_reflection(a, b, "__pow__", "__rpow__", |vm, a, b| {
|
||||
Err(vm.new_unsupported_operand_error(a, b, "**="))
|
||||
Err(vm.new_unsupported_binop_error(a, b, "**="))
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
pub fn _mod(&self, a: &PyObjectRef, b: &PyObjectRef) -> PyResult {
|
||||
self.call_or_reflection(a, b, "__mod__", "__rmod__", |vm, a, b| {
|
||||
Err(vm.new_unsupported_operand_error(a, b, "%"))
|
||||
Err(vm.new_unsupported_binop_error(a, b, "%"))
|
||||
})
|
||||
}
|
||||
|
||||
pub fn _imod(&self, a: &PyObjectRef, b: &PyObjectRef) -> PyResult {
|
||||
self.call_or_unsupported(a, b, "__imod__", |vm, a, b| {
|
||||
vm.call_or_reflection(a, b, "__mod__", "__rmod__", |vm, a, b| {
|
||||
Err(vm.new_unsupported_operand_error(a, b, "%="))
|
||||
Err(vm.new_unsupported_binop_error(a, b, "%="))
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
pub fn _lshift(&self, a: &PyObjectRef, b: &PyObjectRef) -> PyResult {
|
||||
self.call_or_reflection(a, b, "__lshift__", "__rlshift__", |vm, a, b| {
|
||||
Err(vm.new_unsupported_operand_error(a, b, "<<"))
|
||||
Err(vm.new_unsupported_binop_error(a, b, "<<"))
|
||||
})
|
||||
}
|
||||
|
||||
pub fn _ilshift(&self, a: &PyObjectRef, b: &PyObjectRef) -> PyResult {
|
||||
self.call_or_unsupported(a, b, "__ilshift__", |vm, a, b| {
|
||||
vm.call_or_reflection(a, b, "__lshift__", "__rlshift__", |vm, a, b| {
|
||||
Err(vm.new_unsupported_operand_error(a, b, "<<="))
|
||||
Err(vm.new_unsupported_binop_error(a, b, "<<="))
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
pub fn _rshift(&self, a: &PyObjectRef, b: &PyObjectRef) -> PyResult {
|
||||
self.call_or_reflection(a, b, "__rshift__", "__rrshift__", |vm, a, b| {
|
||||
Err(vm.new_unsupported_operand_error(a, b, ">>"))
|
||||
Err(vm.new_unsupported_binop_error(a, b, ">>"))
|
||||
})
|
||||
}
|
||||
|
||||
pub fn _irshift(&self, a: &PyObjectRef, b: &PyObjectRef) -> PyResult {
|
||||
self.call_or_unsupported(a, b, "__irshift__", |vm, a, b| {
|
||||
vm.call_or_reflection(a, b, "__rshift__", "__rrshift__", |vm, a, b| {
|
||||
Err(vm.new_unsupported_operand_error(a, b, ">>="))
|
||||
Err(vm.new_unsupported_binop_error(a, b, ">>="))
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
pub fn _xor(&self, a: &PyObjectRef, b: &PyObjectRef) -> PyResult {
|
||||
self.call_or_reflection(a, b, "__xor__", "__rxor__", |vm, a, b| {
|
||||
Err(vm.new_unsupported_operand_error(a, b, "^"))
|
||||
Err(vm.new_unsupported_binop_error(a, b, "^"))
|
||||
})
|
||||
}
|
||||
|
||||
pub fn _ixor(&self, a: &PyObjectRef, b: &PyObjectRef) -> PyResult {
|
||||
self.call_or_unsupported(a, b, "__ixor__", |vm, a, b| {
|
||||
vm.call_or_reflection(a, b, "__xor__", "__rxor__", |vm, a, b| {
|
||||
Err(vm.new_unsupported_operand_error(a, b, "^="))
|
||||
Err(vm.new_unsupported_binop_error(a, b, "^="))
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
pub fn _or(&self, a: &PyObjectRef, b: &PyObjectRef) -> PyResult {
|
||||
self.call_or_reflection(a, b, "__or__", "__ror__", |vm, a, b| {
|
||||
Err(vm.new_unsupported_operand_error(a, b, "|"))
|
||||
Err(vm.new_unsupported_binop_error(a, b, "|"))
|
||||
})
|
||||
}
|
||||
|
||||
pub fn _ior(&self, a: &PyObjectRef, b: &PyObjectRef) -> PyResult {
|
||||
self.call_or_unsupported(a, b, "__ior__", |vm, a, b| {
|
||||
vm.call_or_reflection(a, b, "__or__", "__ror__", |vm, a, b| {
|
||||
Err(vm.new_unsupported_operand_error(a, b, "|="))
|
||||
Err(vm.new_unsupported_binop_error(a, b, "|="))
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
pub fn _and(&self, a: &PyObjectRef, b: &PyObjectRef) -> PyResult {
|
||||
self.call_or_reflection(a, b, "__and__", "__rand__", |vm, a, b| {
|
||||
Err(vm.new_unsupported_operand_error(a, b, "&"))
|
||||
Err(vm.new_unsupported_binop_error(a, b, "&"))
|
||||
})
|
||||
}
|
||||
|
||||
pub fn _iand(&self, a: &PyObjectRef, b: &PyObjectRef) -> PyResult {
|
||||
self.call_or_unsupported(a, b, "__iand__", |vm, a, b| {
|
||||
vm.call_or_reflection(a, b, "__and__", "__rand__", |vm, a, b| {
|
||||
Err(vm.new_unsupported_operand_error(a, b, "&="))
|
||||
Err(vm.new_unsupported_binop_error(a, b, "&="))
|
||||
})
|
||||
})
|
||||
}
|
||||
@@ -1517,7 +1538,7 @@ impl VirtualMachine {
|
||||
match op {
|
||||
PyComparisonOp::Eq => Ok(Either::B(v.is(&w))),
|
||||
PyComparisonOp::Ne => Ok(Either::B(!v.is(&w))),
|
||||
_ => Err(self.new_unsupported_operand_error(v, w, op.operator_token())),
|
||||
_ => Err(self.new_unsupported_binop_error(v, w, op.operator_token())),
|
||||
}
|
||||
// TODO: _Py_LeaveRecursiveCall(tstate);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user