Skip to content

try to types utility functions to PyObjectRef methods #3107

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Sep 22, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion vm/src/builtins/complex.rs
Original file line number Diff line number Diff line change
Expand Up @@ -446,7 +446,7 @@ fn try_complex(obj: &PyObjectRef, vm: &VirtualMachine) -> PyResult<Option<(Compl
if let Some(complex) = obj.payload_if_subclass::<PyComplex>(vm) {
return Ok(Some((complex.value, true)));
}
if let Some(float) = float::try_float_opt(obj, vm)? {
if let Some(float) = obj.try_to_f64(vm)? {
return Ok(Some((Complex64::new(float, 0.0), false)));
}
Ok(None)
Expand Down
3 changes: 1 addition & 2 deletions vm/src/builtins/filter.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
use super::pybool;
use super::pytype::PyTypeRef;
use crate::iterator;
use crate::slots::{PyIter, SlotConstructor};
Expand Down Expand Up @@ -64,7 +63,7 @@ impl PyIter for PyFilter {
// iteration
vm.invoke(predicate, vec![next_obj.clone()])?
};
if pybool::boolval(vm, predicate_value)? {
if predicate_value.try_to_bool(vm)? {
return Ok(next_obj);
}
}
Expand Down
56 changes: 29 additions & 27 deletions vm/src/builtins/float.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use super::{int, PyBytes, PyInt, PyIntRef, PyStr, PyStrRef, PyTypeRef};
use super::{try_bigint_to_f64, PyBytes, PyInt, PyIntRef, PyStr, PyStrRef, PyTypeRef};
use crate::common::{float_ops, hash};
use crate::format::FormatSpec;
use crate::function::{OptionalArg, OptionalOption};
Expand Down Expand Up @@ -51,29 +51,31 @@ impl From<f64> for PyFloat {
}
}

pub fn try_float_opt(obj: &PyObjectRef, vm: &VirtualMachine) -> PyResult<Option<f64>> {
if let Some(float) = obj.payload_if_exact::<PyFloat>(vm) {
return Ok(Some(float.value));
}
if let Some(method) = vm.get_method(obj.clone(), "__float__") {
let result = vm.invoke(&method?, ())?;
// TODO: returning strict subclasses of float in __float__ is deprecated
return match result.payload::<PyFloat>() {
Some(float_obj) => Ok(Some(float_obj.value)),
None => Err(vm.new_type_error(format!(
"__float__ returned non-float (type '{}')",
result.class().name()
))),
};
}
if let Some(r) = vm.to_index_opt(obj.clone()).transpose()? {
return Ok(Some(int::to_float(r.as_bigint(), vm)?));
impl PyObjectRef {
pub fn try_to_f64(&self, vm: &VirtualMachine) -> PyResult<Option<f64>> {
if let Some(float) = self.payload_if_exact::<PyFloat>(vm) {
return Ok(Some(float.value));
}
if let Some(method) = vm.get_method(self.clone(), "__float__") {
let result = vm.invoke(&method?, ())?;
// TODO: returning strict subclasses of float in __float__ is deprecated
return match result.payload::<PyFloat>() {
Some(float_obj) => Ok(Some(float_obj.value)),
None => Err(vm.new_type_error(format!(
"__float__ returned non-float (type '{}')",
result.class().name()
))),
};
}
if let Some(r) = vm.to_index_opt(self.clone()).transpose()? {
return Ok(Some(try_bigint_to_f64(r.as_bigint(), vm)?));
}
Ok(None)
}
Ok(None)
}

pub fn try_float(obj: &PyObjectRef, vm: &VirtualMachine) -> PyResult<f64> {
try_float_opt(obj, vm)?.ok_or_else(|| {
obj.try_to_f64(vm)?.ok_or_else(|| {
vm.new_type_error(format!("must be real number, not {}", obj.class().name()))
})
}
Expand All @@ -82,7 +84,7 @@ pub(crate) fn to_op_float(obj: &PyObjectRef, vm: &VirtualMachine) -> PyResult<Op
let v = if let Some(float) = obj.payload_if_subclass::<PyFloat>(vm) {
Some(float.value)
} else if let Some(int) = obj.payload_if_subclass::<PyInt>(vm) {
Some(int::to_float(int.as_bigint(), vm)?)
Some(try_bigint_to_f64(int.as_bigint(), vm)?)
} else {
None
};
Expand Down Expand Up @@ -111,7 +113,7 @@ fn inner_mod(v1: f64, v2: f64, vm: &VirtualMachine) -> PyResult<f64> {
.ok_or_else(|| vm.new_zero_division_error("float mod by zero".to_owned()))
}

pub fn try_bigint(value: f64, vm: &VirtualMachine) -> PyResult<BigInt> {
pub fn try_to_bigint(value: f64, vm: &VirtualMachine) -> PyResult<BigInt> {
match value.to_bigint() {
Some(int) => Ok(int),
None => {
Expand Down Expand Up @@ -171,7 +173,7 @@ impl SlotConstructor for PyFloat {
val
};

if let Some(f) = try_float_opt(&val, vm)? {
if let Some(f) = val.try_to_f64(vm)? {
f
} else if let Some(s) = val.payload_if_subclass::<PyStr>(vm) {
float_ops::parse_str(s.as_str().trim()).ok_or_else(|| {
Expand Down Expand Up @@ -379,17 +381,17 @@ impl PyFloat {

#[pymethod(magic)]
fn trunc(&self, vm: &VirtualMachine) -> PyResult<BigInt> {
try_bigint(self.value, vm)
try_to_bigint(self.value, vm)
}

#[pymethod(magic)]
fn floor(&self, vm: &VirtualMachine) -> PyResult<BigInt> {
try_bigint(self.value.floor(), vm)
try_to_bigint(self.value.floor(), vm)
}

#[pymethod(magic)]
fn ceil(&self, vm: &VirtualMachine) -> PyResult<BigInt> {
try_bigint(self.value.ceil(), vm)
try_to_bigint(self.value.ceil(), vm)
}

#[pymethod(magic)]
Expand Down Expand Up @@ -417,7 +419,7 @@ impl PyFloat {
} else {
self.value.round()
};
let int = try_bigint(value, vm)?;
let int = try_to_bigint(value, vm)?;
vm.ctx.new_int(int)
};
Ok(value)
Expand Down
12 changes: 6 additions & 6 deletions vm/src/builtins/int.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
use super::{float, IntoPyBool, PyByteArray, PyBytes, PyStr, PyStrRef, PyTypeRef};
use crate::bytesinner::PyBytesInner;
use crate::common::hash;
use crate::format::FormatSpec;
use crate::function::{OptionalArg, OptionalOption};
use crate::slots::{Comparable, Hashable, PyComparisonOp, SlotConstructor};
use crate::VirtualMachine;
use crate::{bytesinner::PyBytesInner, byteslike::try_bytes_like};
use crate::{
try_value_from_borrowed_object, IdProtocol, IntoPyObject, IntoPyResult, PyArithmaticValue,
PyClassImpl, PyComparisonValue, PyContext, PyObjectRef, PyRef, PyResult, PyValue,
Expand Down Expand Up @@ -142,8 +142,8 @@ pub fn bigint_unsigned_mask(v: &BigInt) -> u32 {

fn inner_pow(int1: &BigInt, int2: &BigInt, vm: &VirtualMachine) -> PyResult {
if int2.is_negative() {
let v1 = to_float(int1, vm)?;
let v2 = to_float(int2, vm)?;
let v1 = try_to_float(int1, vm)?;
let v2 = try_to_float(int2, vm)?;
float::float_pow(v1, v2, vm).into_pyresult(vm)
} else {
Ok(if let Some(v2) = int2.to_u64() {
Expand Down Expand Up @@ -541,7 +541,7 @@ impl PyInt {

#[pymethod(magic)]
fn float(&self, vm: &VirtualMachine) -> PyResult<f64> {
to_float(&self.value, vm)
try_to_float(&self.value, vm)
}

#[pymethod(magic)]
Expand Down Expand Up @@ -914,7 +914,7 @@ pub(crate) fn get_value(obj: &PyObjectRef) -> &BigInt {
&obj.payload::<PyInt>().unwrap().value
}

pub fn to_float(int: &BigInt, vm: &VirtualMachine) -> PyResult<f64> {
pub fn try_to_float(int: &BigInt, vm: &VirtualMachine) -> PyResult<f64> {
i2f(int).ok_or_else(|| vm.new_overflow_error("int too large to convert to float".to_owned()))
}
// num-bigint now returns Some(inf) for to_f64() in some cases, so just keep that the same for now
Expand All @@ -939,7 +939,7 @@ pub(crate) fn try_int(obj: &PyObjectRef, vm: &VirtualMachine) -> PyResult<BigInt
if let Some(s) = obj.downcast_ref::<PyStr>() {
return try_convert(obj, s.as_str().as_bytes(), vm);
}
if let Ok(r) = try_bytes_like(vm, obj, |x| try_convert(obj, x, vm)) {
if let Ok(r) = obj.try_bytes_like(vm, |x| try_convert(obj, x, vm)) {
return r;
}
// strict `int` check
Expand Down
9 changes: 6 additions & 3 deletions vm/src/builtins/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ pub use classmethod::PyClassMethod;
pub(crate) mod code;
pub use code::PyCode;
pub(crate) mod complex;
pub use complex::PyComplex;
pub use complex::{IntoPyComplex, PyComplex};
pub(crate) mod coroutine;
pub use coroutine::PyCoroutine;
pub mod dict;
Expand All @@ -22,7 +22,7 @@ pub use enumerate::PyEnumerate;
pub(crate) mod filter;
pub use filter::PyFilter;
pub(crate) mod float;
pub use float::{PyFloat, PyFloatRef};
pub use float::{IntoPyFloat, PyFloat, PyFloatRef};
pub(crate) mod frame;
pub(crate) mod function;
pub use function::PyFunction;
Expand Down Expand Up @@ -65,7 +65,7 @@ pub use set::PySet;
pub(crate) mod singletons;
pub use singletons::{PyNone, PyNotImplemented};
pub(crate) mod slice;
pub use slice::PySlice;
pub use slice::{PySlice, PySliceRef};
pub(crate) mod staticmethod;
pub use staticmethod::PyStaticMethod;
pub(crate) mod traceback;
Expand All @@ -80,5 +80,8 @@ pub(crate) mod zip;
pub use zip::PyZip;
pub(crate) mod genericalias;

pub use float::try_to_bigint as try_f64_to_bigint;
pub use int::try_to_float as try_bigint_to_f64;

mod make_module;
pub use make_module::{ascii, make_module, print};
3 changes: 1 addition & 2 deletions vm/src/builtins/object.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
use super::dict::{PyDict, PyDictRef};
use super::list::PyList;
use super::pybool;
use super::pystr::{PyStr, PyStrRef};
use super::pytype::PyTypeRef;
use crate::builtins::pytype::PyType;
Expand Down Expand Up @@ -77,7 +76,7 @@ impl PyBaseObject {
.unwrap();
let value = match cmp(zelf, other, PyComparisonOp::Eq, vm)? {
Either::A(obj) => PyArithmaticValue::from_object(vm, obj)
.map(|obj| pybool::boolval(vm, obj))
.map(|obj| obj.try_to_bool(vm))
.transpose()?,
Either::B(value) => value,
};
Expand Down
78 changes: 40 additions & 38 deletions vm/src/builtins/pybool.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,49 +29,51 @@ impl TryFromBorrowedObject for bool {
}
}

/// Convert Python bool into Rust bool.
pub fn boolval(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult<bool> {
if obj.is(&vm.ctx.true_value) {
return Ok(true);
}
if obj.is(&vm.ctx.false_value) {
return Ok(false);
}
let rs_bool = match vm.get_method(obj.clone(), "__bool__") {
Some(method_or_err) => {
// If descriptor returns Error, propagate it further
let method = method_or_err?;
let bool_obj = vm.invoke(&method, ())?;
if !bool_obj.isinstance(&vm.ctx.types.bool_type) {
return Err(vm.new_type_error(format!(
"__bool__ should return bool, returned type {}",
bool_obj.class().name()
)));
}

get_value(&bool_obj)
impl PyObjectRef {
/// Convert Python bool into Rust bool.
pub fn try_to_bool(self, vm: &VirtualMachine) -> PyResult<bool> {
if self.is(&vm.ctx.true_value) {
return Ok(true);
}
if self.is(&vm.ctx.false_value) {
return Ok(false);
}
None => match vm.get_method(obj, "__len__") {
let rs_bool = match vm.get_method(self.clone(), "__bool__") {
Some(method_or_err) => {
// If descriptor returns Error, propagate it further
let method = method_or_err?;
let bool_obj = vm.invoke(&method, ())?;
let int_obj = bool_obj.payload::<PyInt>().ok_or_else(|| {
vm.new_type_error(format!(
"'{}' object cannot be interpreted as an integer",
if !bool_obj.isinstance(&vm.ctx.types.bool_type) {
return Err(vm.new_type_error(format!(
"__bool__ should return bool, returned type {}",
bool_obj.class().name()
))
})?;

let len_val = int_obj.as_bigint();
if len_val.sign() == Sign::Minus {
return Err(vm.new_value_error("__len__() should return >= 0".to_owned()));
)));
}
!len_val.is_zero()

get_value(&bool_obj)
}
None => true,
},
};
Ok(rs_bool)
None => match vm.get_method(self, "__len__") {
Some(method_or_err) => {
let method = method_or_err?;
let bool_obj = vm.invoke(&method, ())?;
let int_obj = bool_obj.payload::<PyInt>().ok_or_else(|| {
vm.new_type_error(format!(
"'{}' object cannot be interpreted as an integer",
bool_obj.class().name()
))
})?;

let len_val = int_obj.as_bigint();
if len_val.sign() == Sign::Minus {
return Err(vm.new_value_error("__len__() should return >= 0".to_owned()));
}
!len_val.is_zero()
}
None => true,
},
};
Ok(rs_bool)
}
}

/// bool(x) -> bool
Expand Down Expand Up @@ -105,7 +107,7 @@ impl SlotConstructor for PyBool {
actual_type
)));
}
let val = x.map_or(Ok(false), |val| boolval(vm, val))?;
let val = x.map_or(Ok(false), |val| val.try_to_bool(vm))?;
Ok(vm.ctx.new_bool(val))
}
}
Expand Down Expand Up @@ -202,7 +204,7 @@ impl IntoPyBool {
impl TryFromObject for IntoPyBool {
fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult<Self> {
Ok(IntoPyBool {
value: boolval(vm, obj)?,
value: obj.try_to_bool(vm)?,
})
}
}
10 changes: 4 additions & 6 deletions vm/src/bytesinner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ use crate::builtins::bytes::{PyBytes, PyBytesRef};
use crate::builtins::int::{PyInt, PyIntRef};
use crate::builtins::pystr::{self, PyStr, PyStrRef};
use crate::builtins::PyTypeRef;
use crate::byteslike::try_bytes_like;
use crate::cformat::CFormatBytes;
use crate::function::{OptionalArg, OptionalOption};
use crate::sliceable::PySliceableSequence;
Expand Down Expand Up @@ -320,10 +319,9 @@ impl PyBytesInner {
// TODO: bytes can compare with any object implemented buffer protocol
// but not memoryview, and not equal if compare with unicode str(PyStr)
PyComparisonValue::from_option(
try_bytes_like(vm, other, |other| {
op.eval_ord(self.elements.as_slice().cmp(other))
})
.ok(),
other
.try_bytes_like(vm, |other| op.eval_ord(self.elements.as_slice().cmp(other)))
.ok(),
)
}

Expand Down Expand Up @@ -1238,7 +1236,7 @@ pub const fn is_py_ascii_whitespace(b: u8) -> bool {
}

pub fn bytes_from_object(vm: &VirtualMachine, obj: &PyObjectRef) -> PyResult<Vec<u8>> {
if let Ok(elements) = try_bytes_like(vm, obj, |bytes| bytes.to_vec()) {
if let Ok(elements) = obj.try_bytes_like(vm, |bytes| bytes.to_vec()) {
return Ok(elements);
}

Expand Down
Loading