Skip to content

Commit 3051702

Browse files
authored
Merge branch 'master' into pyrange-extend
2 parents 22a35c9 + fc729fd commit 3051702

21 files changed

+466
-273
lines changed

tests/snippets/builtin_complex.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,11 @@
2424
assert complex(1, 2) != 'foo'
2525
assert complex(1, 2).__eq__('foo') == NotImplemented
2626

27+
# __mul__
28+
29+
assert complex(2, -3) * complex(-5, 7) == complex(11, 29)
30+
assert complex(2, -3) * 5 == complex(10, -15)
31+
2732
# __neg__
2833

2934
assert -complex(1, -1) == complex(-1, 1)

tests/snippets/bytearray.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,3 +65,9 @@
6565
pass
6666
else:
6767
assert False
68+
69+
a = bytearray(b'appen')
70+
assert len(a) == 5
71+
a.append(100)
72+
assert len(a) == 6
73+
assert a.pop() == 100

tests/snippets/floats.py

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
b = 1.3
99
c = 1.2
1010
z = 2
11+
ov = 10 ** 1000
1112

1213
assert -a == -1.2
1314

@@ -37,6 +38,20 @@
3738
assert 6 / a == 5.0
3839
assert 2.0 % z == 0.0
3940
assert z % 2.0 == 0.0
41+
assert_raises(OverflowError, lambda: a + ov)
42+
assert_raises(OverflowError, lambda: a - ov)
43+
assert_raises(OverflowError, lambda: a * ov)
44+
assert_raises(OverflowError, lambda: a / ov)
45+
assert_raises(OverflowError, lambda: a // ov)
46+
assert_raises(OverflowError, lambda: a % ov)
47+
assert_raises(OverflowError, lambda: a ** ov)
48+
assert_raises(OverflowError, lambda: ov + a)
49+
assert_raises(OverflowError, lambda: ov - a)
50+
assert_raises(OverflowError, lambda: ov * a)
51+
assert_raises(OverflowError, lambda: ov / a)
52+
assert_raises(OverflowError, lambda: ov // a)
53+
assert_raises(OverflowError, lambda: ov % a)
54+
# assert_raises(OverflowError, lambda: ov ** a)
4055

4156
assert a < 5
4257
assert a <= 5
@@ -91,6 +106,8 @@
91106
assert 2.0.__rmul__(1.0) == 2.0
92107
assert 1.0.__truediv__(2.0) == 0.5
93108
assert 1.0.__rtruediv__(2.0) == 2.0
109+
assert 2.5.__divmod__(2.0) == (1.0, 0.5)
110+
assert 2.0.__rdivmod__(2.5) == (1.0, 0.5)
94111

95112
assert 1.0.__add__(1) == 2.0
96113
assert 1.0.__radd__(1) == 2.0
@@ -105,13 +122,32 @@
105122
assert_raises(ZeroDivisionError, lambda: 2.0 / 0)
106123
assert_raises(ZeroDivisionError, lambda: 2.0 // 0)
107124
assert_raises(ZeroDivisionError, lambda: 2.0 % 0)
125+
assert_raises(ZeroDivisionError, lambda: divmod(2.0, 0))
126+
assert_raises(ZeroDivisionError, lambda: 2 / 0.0)
127+
assert_raises(ZeroDivisionError, lambda: 2 // 0.0)
128+
assert_raises(ZeroDivisionError, lambda: 2 % 0.0)
129+
# assert_raises(ZeroDivisionError, lambda: divmod(2, 0.0))
108130

109131
assert 1.2.__int__() == 1
110132
assert 1.2.__float__() == 1.2
111133
assert 1.2.__trunc__() == 1
112134
assert int(1.2) == 1
113135
assert float(1.2) == 1.2
114-
# assert math.trunc(1.2) == 1
136+
assert math.trunc(1.2) == 1
137+
assert_raises(OverflowError, float('inf').__trunc__)
138+
assert_raises(ValueError, float('nan').__trunc__)
139+
assert 0.5.__round__() == 0.0
140+
assert 1.5.__round__() == 2.0
141+
assert 0.5.__round__(0) == 0.0
142+
assert 1.5.__round__(0) == 2.0
143+
assert 0.5.__round__(None) == 0.0
144+
assert 1.5.__round__(None) == 2.0
145+
assert_raises(OverflowError, float('inf').__round__)
146+
assert_raises(ValueError, float('nan').__round__)
147+
148+
assert 1.2 ** 2 == 1.44
149+
assert_raises(OverflowError, lambda: 1.2 ** (10 ** 1000))
150+
assert 3 ** 2.0 == 9.0
115151

116152
assert (1.7).real == 1.7
117153
assert (1.3).is_integer() == False

tests/snippets/ints.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,3 +74,37 @@
7474
with assertRaises(TypeError):
7575
# check that first parameter is truly positional only
7676
int(val_options=1)
77+
78+
class A(object):
79+
def __int__(self):
80+
return 10
81+
82+
assert int(A()) == 10
83+
84+
class B(object):
85+
pass
86+
87+
b = B()
88+
b.__int__ = lambda: 20
89+
90+
with assertRaises(TypeError):
91+
assert int(b) == 20
92+
93+
class C(object):
94+
def __int__(self):
95+
return 'str'
96+
97+
with assertRaises(TypeError):
98+
int(C())
99+
100+
class I(int):
101+
def __int__(self):
102+
return 3
103+
104+
assert int(I(1)) == 3
105+
106+
class F(float):
107+
def __int__(self):
108+
return 3
109+
110+
assert int(F(1.2)) == 3

tests/snippets/math_basics.py

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from testutils import assertRaises
1+
from testutils import assert_raises
22

33
a = 4
44

@@ -18,3 +18,23 @@
1818
assert a - 3 == 1
1919
assert -a == -4
2020
assert +a == 4
21+
22+
assert round(1.2) == 1
23+
assert round(1.8) == 2
24+
assert round(0.5) == 0
25+
assert round(1.5) == 2
26+
assert round(-0.5) == 0
27+
assert round(-1.5) == -2
28+
29+
assert_raises(
30+
ValueError,
31+
lambda: round(float('nan')),
32+
'ValueError: cannot convert float NaN to integer')
33+
assert_raises(
34+
OverflowError,
35+
lambda: round(float('inf')),
36+
'OverflowError: cannot convert float NaN to integer')
37+
assert_raises(
38+
OverflowError,
39+
lambda: round(-float('inf')),
40+
'OverflowError: cannot convert float NaN to integer')

tests/snippets/testutils.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,9 @@ def assert_raises(exc_type, expr, msg=None):
1414
except exc_type:
1515
pass
1616
else:
17-
failmsg = '{!s} was not raised'.format(exc_type.__name__)
17+
failmsg = '{} was not raised'.format(exc_type.__name__)
1818
if msg is not None:
19-
failmsg += ': {!s}'.format(msg)
19+
failmsg += ': {}'.format(msg)
2020
assert False, failmsg
2121

2222

@@ -29,8 +29,8 @@ def __enter__(self):
2929

3030
def __exit__(self, exc_type, exc_val, exc_tb):
3131
if exc_type is None:
32-
failmsg = '{!s} was not raised'.format(self.expected.__name__)
33-
assert False, failmsg
32+
failmsg = '{} was not raised'.format(self.expected.__name__)
33+
assert False, failmsg
3434
if not issubclass(exc_type, self.expected):
3535
return False
3636
return True

vm/src/builtins.rs

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -123,11 +123,14 @@ fn builtin_dir(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult {
123123
}
124124

125125
fn builtin_divmod(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult {
126-
arg_check!(vm, args, required = [(x, None), (y, None)]);
127-
match vm.get_method(x.clone(), "__divmod__") {
128-
Ok(attrib) => vm.invoke(attrib, vec![y.clone()]),
129-
Err(..) => Err(vm.new_type_error("unsupported operand type(s) for divmod".to_string())),
130-
}
126+
arg_check!(vm, args, required = [(a, None), (b, None)]);
127+
vm.call_or_reflection(
128+
a.clone(),
129+
b.clone(),
130+
"__divmod__",
131+
"__rdivmod__",
132+
|vm, a, b| Err(vm.new_unsupported_operand_error(a, b, "divmod")),
133+
)
131134
}
132135

133136
/// Implements `eval`.

vm/src/obj/objbytearray.rs

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use std::ops::{Deref, DerefMut};
77
use num_traits::ToPrimitive;
88

99
use crate::function::OptionalArg;
10-
use crate::pyobject::{PyContext, PyObjectRef, PyRef, PyResult, PyValue};
10+
use crate::pyobject::{PyClassImpl, PyContext, PyObjectRef, PyRef, PyResult, PyValue};
1111
use crate::vm::VirtualMachine;
1212

1313
use super::objint;
@@ -79,15 +79,12 @@ pub fn init(context: &PyContext) {
7979
"istitle" =>context.new_rustfunc(PyByteArrayRef::istitle),
8080
"isupper" => context.new_rustfunc(PyByteArrayRef::isupper),
8181
"lower" => context.new_rustfunc(PyByteArrayRef::lower),
82+
"append" => context.new_rustfunc(PyByteArrayRef::append),
8283
"pop" => context.new_rustfunc(PyByteArrayRef::pop),
8384
"upper" => context.new_rustfunc(PyByteArrayRef::upper)
8485
});
8586

86-
let bytearrayiterator_type = &context.bytearrayiterator_type;
87-
extend_class!(context, bytearrayiterator_type, {
88-
"__next__" => context.new_rustfunc(PyByteArrayIteratorRef::next),
89-
"__iter__" => context.new_rustfunc(PyByteArrayIteratorRef::iter),
90-
});
87+
PyByteArrayIterator::extend_class(context, &context.bytearrayiterator_type);
9188
}
9289

9390
fn bytearray_new(
@@ -213,6 +210,10 @@ impl PyByteArrayRef {
213210
self.value.borrow_mut().clear();
214211
}
215212

213+
fn append(self, x: u8, _vm: &VirtualMachine) {
214+
self.value.borrow_mut().push(x);
215+
}
216+
216217
fn pop(self, vm: &VirtualMachine) -> PyResult<u8> {
217218
let mut bytes = self.value.borrow_mut();
218219
bytes
@@ -282,6 +283,7 @@ mod tests {
282283
}
283284
}
284285

286+
#[pyclass]
285287
#[derive(Debug)]
286288
pub struct PyByteArrayIterator {
287289
position: Cell<usize>,
@@ -294,10 +296,10 @@ impl PyValue for PyByteArrayIterator {
294296
}
295297
}
296298

297-
type PyByteArrayIteratorRef = PyRef<PyByteArrayIterator>;
298-
299-
impl PyByteArrayIteratorRef {
300-
fn next(self, vm: &VirtualMachine) -> PyResult<u8> {
299+
#[pyimpl]
300+
impl PyByteArrayIterator {
301+
#[pymethod(name = "__next__")]
302+
fn next(&self, vm: &VirtualMachine) -> PyResult<u8> {
301303
if self.position.get() < self.bytearray.value.borrow().len() {
302304
let ret = self.bytearray.value.borrow()[self.position.get()];
303305
self.position.set(self.position.get() + 1);
@@ -307,7 +309,8 @@ impl PyByteArrayIteratorRef {
307309
}
308310
}
309311

310-
fn iter(self, _vm: &VirtualMachine) -> Self {
311-
self
312+
#[pymethod(name = "__iter__")]
313+
fn iter(zelf: PyRef<Self>, _vm: &VirtualMachine) -> PyRef<Self> {
314+
zelf
312315
}
313316
}

vm/src/obj/objbytes.rs

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -64,11 +64,7 @@ pub fn init(context: &PyContext) {
6464
extend_class!(context, bytes_type, {
6565
"fromhex" => context.new_rustfunc(PyBytesRef::fromhex),
6666
});
67-
let bytesiterator_type = &context.bytesiterator_type;
68-
extend_class!(context, bytesiterator_type, {
69-
"__next__" => context.new_rustfunc(PyBytesIteratorRef::next),
70-
"__iter__" => context.new_rustfunc(PyBytesIteratorRef::iter),
71-
});
67+
PyBytesIterator::extend_class(context, &context.bytesiterator_type);
7268
}
7369

7470
#[pyimpl]
@@ -271,6 +267,7 @@ impl PyBytesRef {
271267
}
272268
}
273269

270+
#[pyclass]
274271
#[derive(Debug)]
275272
pub struct PyBytesIterator {
276273
position: Cell<usize>,
@@ -283,10 +280,10 @@ impl PyValue for PyBytesIterator {
283280
}
284281
}
285282

286-
type PyBytesIteratorRef = PyRef<PyBytesIterator>;
287-
288-
impl PyBytesIteratorRef {
289-
fn next(self, vm: &VirtualMachine) -> PyResult<u8> {
283+
#[pyimpl]
284+
impl PyBytesIterator {
285+
#[pymethod(name = "__next__")]
286+
fn next(&self, vm: &VirtualMachine) -> PyResult<u8> {
290287
if self.position.get() < self.bytes.inner.len() {
291288
let ret = self.bytes[self.position.get()];
292289
self.position.set(self.position.get() + 1);
@@ -296,7 +293,8 @@ impl PyBytesIteratorRef {
296293
}
297294
}
298295

299-
fn iter(self, _vm: &VirtualMachine) -> Self {
300-
self
296+
#[pymethod(name = "__iter__")]
297+
fn iter(zelf: PyRef<Self>, _vm: &VirtualMachine) -> PyRef<Self> {
298+
zelf
301299
}
302300
}

vm/src/obj/objcomplex.rs

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,9 @@ impl PyComplexRef {
6767
}
6868

6969
fn to_complex(value: PyObjectRef, vm: &VirtualMachine) -> PyResult<Option<Complex64>> {
70-
if objtype::isinstance(&value, &vm.ctx.int_type()) {
70+
if objtype::isinstance(&value, &vm.ctx.complex_type()) {
71+
Ok(Some(get_value(&value)))
72+
} else if objtype::isinstance(&value, &vm.ctx.int_type()) {
7173
match objint::get_value(&value).to_f64() {
7274
Some(v) => Ok(Some(Complex64::new(v, 0.0))),
7375
None => Err(vm.new_overflow_error("int too large to convert to float".to_string())),
@@ -161,6 +163,28 @@ impl PyComplex {
161163
vm.ctx.new_bool(result)
162164
}
163165

166+
#[pymethod(name = "__float__")]
167+
fn float(&self, vm: &VirtualMachine) -> PyResult {
168+
return Err(vm.new_type_error(String::from("Can't convert complex to float")));
169+
}
170+
171+
#[pymethod(name = "__int__")]
172+
fn int(&self, vm: &VirtualMachine) -> PyResult {
173+
return Err(vm.new_type_error(String::from("Can't convert complex to int")));
174+
}
175+
176+
#[pymethod(name = "__mul__")]
177+
fn mul(&self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult {
178+
match to_complex(other, vm) {
179+
Ok(Some(other)) => Ok(vm.ctx.new_complex(Complex64::new(
180+
self.value.re * other.re - self.value.im * other.im,
181+
self.value.re * other.im + self.value.im * other.re,
182+
))),
183+
Ok(None) => Ok(vm.ctx.not_implemented()),
184+
Err(err) => Err(err),
185+
}
186+
}
187+
164188
#[pymethod(name = "__neg__")]
165189
fn neg(&self, _vm: &VirtualMachine) -> PyComplex {
166190
PyComplex::from(-self.value)

0 commit comments

Comments
 (0)