mirror of
https://github.com/RustPython/RustPython.git
synced 2026-06-09 22:49:57 +09:00
Merge pull request #347 from silmeth/master
Fix Range’s len() + division and mod by 0
This commit is contained in:
@@ -18,6 +18,11 @@ def assert_raises(expr, exc_type):
|
||||
|
||||
assert range(2**63+1)[2**63] == 9223372036854775808
|
||||
|
||||
# len tests
|
||||
assert len(range(10, 5)) == 0, 'Range with no elements should have length = 0'
|
||||
assert len(range(10, 5, -2)) == 3, 'Expected length 3, for elements: 10, 8, 6'
|
||||
assert len(range(5, 10, 2)) == 3, 'Expected length 3, for elements: 5, 7, 9'
|
||||
|
||||
# index tests
|
||||
assert range(10).index(6) == 6
|
||||
assert range(4, 10).index(6) == 2
|
||||
|
||||
34
tests/snippets/division_by_zero.py
Normal file
34
tests/snippets/division_by_zero.py
Normal file
@@ -0,0 +1,34 @@
|
||||
try:
|
||||
5 / 0
|
||||
except ZeroDivisionError:
|
||||
pass
|
||||
else:
|
||||
assert False, 'Expected ZeroDivisionError'
|
||||
|
||||
try:
|
||||
5 / -0.0
|
||||
except ZeroDivisionError:
|
||||
pass
|
||||
else:
|
||||
assert False, 'Expected ZeroDivisionError'
|
||||
|
||||
try:
|
||||
5 / (2-2)
|
||||
except ZeroDivisionError:
|
||||
pass
|
||||
else:
|
||||
assert False, 'Expected ZeroDivisionError'
|
||||
|
||||
try:
|
||||
5 % 0
|
||||
except ZeroDivisionError:
|
||||
pass
|
||||
else:
|
||||
assert False, 'Expected ZeroDivisionError'
|
||||
|
||||
try:
|
||||
raise ZeroDivisionError('Is an ArithmeticError subclass?')
|
||||
except ArithmeticError:
|
||||
pass
|
||||
else:
|
||||
assert False, 'Expected ZeroDivisionError'
|
||||
@@ -692,6 +692,11 @@ pub fn make_module(ctx: &PyContext) -> PyObjectRef {
|
||||
ctx.exceptions.base_exception_type.clone(),
|
||||
);
|
||||
ctx.set_attr(&py_mod, "Exception", ctx.exceptions.exception_type.clone());
|
||||
ctx.set_attr(
|
||||
&py_mod,
|
||||
"ArithmeticError",
|
||||
ctx.exceptions.arithmetic_error.clone(),
|
||||
);
|
||||
ctx.set_attr(
|
||||
&py_mod,
|
||||
"AssertionError",
|
||||
@@ -703,6 +708,11 @@ pub fn make_module(ctx: &PyContext) -> PyObjectRef {
|
||||
ctx.exceptions.attribute_error.clone(),
|
||||
);
|
||||
ctx.set_attr(&py_mod, "NameError", ctx.exceptions.name_error.clone());
|
||||
ctx.set_attr(
|
||||
&py_mod,
|
||||
"OverflowError",
|
||||
ctx.exceptions.overflow_error.clone(),
|
||||
);
|
||||
ctx.set_attr(
|
||||
&py_mod,
|
||||
"RuntimeError",
|
||||
@@ -722,6 +732,11 @@ pub fn make_module(ctx: &PyContext) -> PyObjectRef {
|
||||
"StopIteration",
|
||||
ctx.exceptions.stop_iteration.clone(),
|
||||
);
|
||||
ctx.set_attr(
|
||||
&py_mod,
|
||||
"ZeroDivisionError",
|
||||
ctx.exceptions.zero_division_error.clone(),
|
||||
);
|
||||
|
||||
py_mod
|
||||
}
|
||||
|
||||
@@ -81,6 +81,7 @@ fn exception_str(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ExceptionZoo {
|
||||
pub arithmetic_error: PyObjectRef,
|
||||
pub assertion_error: PyObjectRef,
|
||||
pub attribute_error: PyObjectRef,
|
||||
pub base_exception_type: PyObjectRef,
|
||||
@@ -93,12 +94,14 @@ pub struct ExceptionZoo {
|
||||
pub name_error: PyObjectRef,
|
||||
pub not_implemented_error: PyObjectRef,
|
||||
pub os_error: PyObjectRef,
|
||||
pub overflow_error: PyObjectRef,
|
||||
pub permission_error: PyObjectRef,
|
||||
pub runtime_error: PyObjectRef,
|
||||
pub stop_iteration: PyObjectRef,
|
||||
pub syntax_error: PyObjectRef,
|
||||
pub type_error: PyObjectRef,
|
||||
pub value_error: PyObjectRef,
|
||||
pub zero_division_error: PyObjectRef,
|
||||
}
|
||||
|
||||
impl ExceptionZoo {
|
||||
@@ -113,6 +116,8 @@ impl ExceptionZoo {
|
||||
|
||||
let exception_type = create_type("Exception", &type_type, &base_exception_type, &dict_type);
|
||||
|
||||
let arithmetic_error =
|
||||
create_type("ArithmeticError", &type_type, &exception_type, &dict_type);
|
||||
let assertion_error =
|
||||
create_type("AssertionError", &type_type, &exception_type, &dict_type);
|
||||
let attribute_error =
|
||||
@@ -128,8 +133,18 @@ impl ExceptionZoo {
|
||||
let type_error = create_type("TypeError", &type_type, &exception_type, &dict_type);
|
||||
let value_error = create_type("ValueError", &type_type, &exception_type, &dict_type);
|
||||
|
||||
let overflow_error =
|
||||
create_type("OverflowError", &type_type, &arithmetic_error, &dict_type);
|
||||
let zero_division_error = create_type(
|
||||
"ZeroDivisionError",
|
||||
&type_type,
|
||||
&arithmetic_error,
|
||||
&dict_type,
|
||||
);
|
||||
|
||||
let module_not_found_error =
|
||||
create_type("ModuleNotFoundError", &type_type, &import_error, &dict_type);
|
||||
|
||||
let not_implemented_error = create_type(
|
||||
"NotImplementedError",
|
||||
&type_type,
|
||||
@@ -142,6 +157,7 @@ impl ExceptionZoo {
|
||||
let permission_error = create_type("PermissionError", &type_type, &os_error, &dict_type);
|
||||
|
||||
ExceptionZoo {
|
||||
arithmetic_error,
|
||||
assertion_error,
|
||||
attribute_error,
|
||||
base_exception_type,
|
||||
@@ -154,12 +170,14 @@ impl ExceptionZoo {
|
||||
name_error,
|
||||
not_implemented_error,
|
||||
os_error,
|
||||
overflow_error,
|
||||
permission_error,
|
||||
runtime_error,
|
||||
stop_iteration,
|
||||
syntax_error,
|
||||
type_error,
|
||||
value_error,
|
||||
zero_division_error,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -366,17 +366,25 @@ fn int_truediv(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
args,
|
||||
required = [(i, Some(vm.ctx.int_type())), (i2, None)]
|
||||
);
|
||||
let v1 = get_value(i);
|
||||
if objtype::isinstance(i2, &vm.ctx.int_type()) {
|
||||
Ok(vm
|
||||
.ctx
|
||||
.new_float(v1.to_f64().unwrap() / get_value(i2).to_f64().unwrap()))
|
||||
|
||||
let v1 = get_value(i)
|
||||
.to_f64()
|
||||
.ok_or_else(|| vm.new_overflow_error("int too large to convert to float".to_string()))?;
|
||||
|
||||
let v2 = if objtype::isinstance(i2, &vm.ctx.int_type()) {
|
||||
get_value(i2)
|
||||
.to_f64()
|
||||
.ok_or_else(|| vm.new_overflow_error("int too large to convert to float".to_string()))?
|
||||
} else if objtype::isinstance(i2, &vm.ctx.float_type()) {
|
||||
Ok(vm
|
||||
.ctx
|
||||
.new_float(v1.to_f64().unwrap() / objfloat::get_value(i2)))
|
||||
objfloat::get_value(i2)
|
||||
} else {
|
||||
Err(vm.new_type_error(format!("Cannot divide {} and {}", i.borrow(), i2.borrow())))
|
||||
return Err(vm.new_type_error(format!("Cannot divide {} and {}", i.borrow(), i2.borrow())));
|
||||
};
|
||||
|
||||
if v2 == 0.0 {
|
||||
Err(vm.new_zero_division_error("integer division by zero".to_string()))
|
||||
} else {
|
||||
Ok(vm.ctx.new_float(v1 / v2))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -388,7 +396,13 @@ fn int_mod(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
);
|
||||
let v1 = get_value(i);
|
||||
if objtype::isinstance(i2, &vm.ctx.int_type()) {
|
||||
Ok(vm.ctx.new_int(v1 % get_value(i2)))
|
||||
let v2 = get_value(i2);
|
||||
|
||||
if v2 != BigInt::zero() {
|
||||
Ok(vm.ctx.new_int(v1 % get_value(i2)))
|
||||
} else {
|
||||
Err(vm.new_zero_division_error("integer modulo by zero".to_string()))
|
||||
}
|
||||
} else {
|
||||
Err(vm.new_type_error(format!("Cannot modulo {} and {}", i.borrow(), i2.borrow())))
|
||||
}
|
||||
|
||||
@@ -18,12 +18,24 @@ pub struct RangeType {
|
||||
}
|
||||
|
||||
impl RangeType {
|
||||
#[inline]
|
||||
pub fn try_len(&self) -> Option<usize> {
|
||||
match self.step.sign() {
|
||||
Sign::Plus if self.start < self.end => ((&self.end - &self.start - 1usize)
|
||||
/ &self.step)
|
||||
.to_usize()
|
||||
.map(|sz| sz + 1),
|
||||
Sign::Minus if self.start > self.end => ((&self.start - &self.end - 1usize)
|
||||
/ (-&self.step))
|
||||
.to_usize()
|
||||
.map(|sz| sz + 1),
|
||||
_ => Some(0),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn len(&self) -> usize {
|
||||
((self.end.clone() - self.start.clone()) / self.step.clone())
|
||||
.abs()
|
||||
.to_usize()
|
||||
.unwrap()
|
||||
self.try_len().unwrap()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -76,6 +88,15 @@ impl RangeType {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn repr(&self) -> String {
|
||||
if self.step == BigInt::one() {
|
||||
format!("range({}, {})", self.start, self.end)
|
||||
} else {
|
||||
format!("range({}, {}, {})", self.start, self.end, self.step)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn init(context: &PyContext) {
|
||||
@@ -88,6 +109,7 @@ pub fn init(context: &PyContext) {
|
||||
"__getitem__",
|
||||
context.new_rustfunc(range_getitem),
|
||||
);
|
||||
context.set_attr(&range_type, "__repr__", context.new_rustfunc(range_repr));
|
||||
context.set_attr(&range_type, "__bool__", context.new_rustfunc(range_bool));
|
||||
context.set_attr(
|
||||
&range_type,
|
||||
@@ -153,12 +175,14 @@ fn range_iter(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
fn range_len(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
arg_check!(vm, args, required = [(zelf, Some(vm.ctx.range_type()))]);
|
||||
|
||||
let len = match zelf.borrow().payload {
|
||||
PyObjectPayload::Range { ref range } => range.len(),
|
||||
if let Some(len) = match zelf.borrow().payload {
|
||||
PyObjectPayload::Range { ref range } => range.try_len(),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
Ok(vm.ctx.new_int(len))
|
||||
} {
|
||||
Ok(vm.ctx.new_int(len))
|
||||
} else {
|
||||
Err(vm.new_overflow_error("Python int too large to convert to Rust usize".to_string()))
|
||||
}
|
||||
}
|
||||
|
||||
fn range_getitem(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
@@ -224,6 +248,17 @@ fn range_getitem(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
}
|
||||
}
|
||||
|
||||
fn range_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
arg_check!(vm, args, required = [(zelf, Some(vm.ctx.range_type()))]);
|
||||
|
||||
let s = match zelf.borrow().payload {
|
||||
PyObjectPayload::Range { ref range } => range.repr(),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
Ok(vm.ctx.new_str(s))
|
||||
}
|
||||
|
||||
fn range_bool(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
arg_check!(vm, args, required = [(zelf, Some(vm.ctx.range_type()))]);
|
||||
|
||||
|
||||
14
vm/src/vm.rs
14
vm/src/vm.rs
@@ -104,6 +104,11 @@ impl VirtualMachine {
|
||||
self.new_exception(os_error, msg)
|
||||
}
|
||||
|
||||
pub fn new_overflow_error(&mut self, msg: String) -> PyObjectRef {
|
||||
let overflow_error = self.ctx.exceptions.overflow_error.clone();
|
||||
self.new_exception(overflow_error, msg)
|
||||
}
|
||||
|
||||
/// Create a new python ValueError object. Useful for raising errors from
|
||||
/// python functions implemented in rust.
|
||||
pub fn new_value_error(&mut self, msg: String) -> PyObjectRef {
|
||||
@@ -122,8 +127,13 @@ impl VirtualMachine {
|
||||
}
|
||||
|
||||
pub fn new_not_implemented_error(&mut self, msg: String) -> PyObjectRef {
|
||||
let value_error = self.ctx.exceptions.not_implemented_error.clone();
|
||||
self.new_exception(value_error, msg)
|
||||
let not_implemented_error = self.ctx.exceptions.not_implemented_error.clone();
|
||||
self.new_exception(not_implemented_error, msg)
|
||||
}
|
||||
|
||||
pub fn new_zero_division_error(&mut self, msg: String) -> PyObjectRef {
|
||||
let zero_division_error = self.ctx.exceptions.zero_division_error.clone();
|
||||
self.new_exception(zero_division_error, msg)
|
||||
}
|
||||
|
||||
pub fn new_scope(&mut self, parent_scope: Option<PyObjectRef>) -> PyObjectRef {
|
||||
|
||||
Reference in New Issue
Block a user