Skip to content

Commit 222d193

Browse files
committed
Call __instancecheck__ and __subclasscheck__ as part of isinstance and issubclass
1 parent 587f871 commit 222d193

28 files changed

+338
-159
lines changed

tests/snippets/isinstance.py

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
2+
class Regular:
3+
pass
4+
5+
6+
assert isinstance(Regular(), Regular)
7+
8+
9+
class MCNotInstanceOf(type):
10+
def __instancecheck__(self, instance):
11+
return False
12+
13+
14+
class NotInstanceOf(metaclass=MCNotInstanceOf):
15+
pass
16+
17+
18+
class InheritedNotInstanceOf(NotInstanceOf):
19+
pass
20+
21+
22+
assert not isinstance(Regular(), NotInstanceOf)
23+
assert not isinstance(1, NotInstanceOf)
24+
25+
# weird cpython behaviour if exact match then isinstance return true
26+
assert isinstance(NotInstanceOf(), NotInstanceOf)
27+
assert not NotInstanceOf.__instancecheck__(NotInstanceOf())
28+
assert not isinstance(InheritedNotInstanceOf(), NotInstanceOf)
29+
30+
31+
class MCAlwaysInstanceOf(type):
32+
def __instancecheck__(self, instance):
33+
return True
34+
35+
36+
class AlwaysInstanceOf(metaclass=MCAlwaysInstanceOf):
37+
pass
38+
39+
40+
assert isinstance(AlwaysInstanceOf(), AlwaysInstanceOf)
41+
assert isinstance(Regular(), AlwaysInstanceOf)
42+
assert isinstance(1, AlwaysInstanceOf)
43+
44+
45+
class MCReturnInt(type):
46+
def __instancecheck__(self, instance):
47+
return 3
48+
49+
50+
class ReturnInt(metaclass=MCReturnInt):
51+
pass
52+
53+
54+
assert isinstance("a", ReturnInt) is True

tests/snippets/issubclass.py

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
2+
class A:
3+
pass
4+
5+
6+
class B(A):
7+
pass
8+
9+
10+
assert issubclass(A, A)
11+
assert issubclass(B, A)
12+
assert not issubclass(A, B)
13+
14+
15+
class MCNotSubClass(type):
16+
def __subclasscheck__(self, subclass):
17+
return False
18+
19+
20+
class NotSubClass(metaclass=MCNotSubClass):
21+
pass
22+
23+
24+
class InheritedNotSubClass(NotSubClass):
25+
pass
26+
27+
28+
assert not issubclass(A, NotSubClass)
29+
assert not issubclass(NotSubClass, NotSubClass)
30+
assert not issubclass(InheritedNotSubClass, NotSubClass)
31+
assert not issubclass(NotSubClass, InheritedNotSubClass)
32+
33+
34+
class MCAlwaysSubClass(type):
35+
def __subclasscheck__(self, subclass):
36+
return True
37+
38+
39+
class AlwaysSubClass(metaclass=MCAlwaysSubClass):
40+
pass
41+
42+
43+
class InheritedAlwaysSubClass(AlwaysSubClass):
44+
pass
45+
46+
47+
assert issubclass(A, AlwaysSubClass)
48+
assert issubclass(AlwaysSubClass, AlwaysSubClass)
49+
assert issubclass(InheritedAlwaysSubClass, AlwaysSubClass)
50+
assert issubclass(AlwaysSubClass, InheritedAlwaysSubClass)
51+
52+
53+
class MCAVirtualSubClass(type):
54+
def __subclasscheck__(self, subclass):
55+
return subclass is A
56+
57+
58+
class AVirtualSubClass(metaclass=MCAVirtualSubClass):
59+
pass
60+
61+
62+
assert issubclass(A, AVirtualSubClass)
63+
assert not isinstance(B, AVirtualSubClass)

vm/src/builtins.rs

Lines changed: 23 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -197,9 +197,9 @@ fn builtin_eval(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
197197
);
198198

199199
// Determine code object:
200-
let code_obj = if objtype::isinstance(source, &vm.ctx.code_type()) {
200+
let code_obj = if objtype::real_isinstance(source, &vm.ctx.code_type()) {
201201
source.clone()
202-
} else if objtype::isinstance(source, &vm.ctx.str_type()) {
202+
} else if objtype::real_isinstance(source, &vm.ctx.str_type()) {
203203
let mode = compile::Mode::Eval;
204204
let source = objstr::get_value(source);
205205
// TODO: fix this newline bug:
@@ -234,7 +234,7 @@ fn builtin_exec(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
234234
);
235235

236236
// Determine code object:
237-
let code_obj = if objtype::isinstance(source, &vm.ctx.str_type()) {
237+
let code_obj = if objtype::real_isinstance(source, &vm.ctx.str_type()) {
238238
let mode = compile::Mode::Exec;
239239
let source = objstr::get_value(source);
240240
// TODO: fix this newline bug:
@@ -245,7 +245,7 @@ fn builtin_exec(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
245245
vm.new_exception(syntax_error, err.to_string())
246246
},
247247
)?
248-
} else if objtype::isinstance(source, &vm.ctx.code_type()) {
248+
} else if objtype::real_isinstance(source, &vm.ctx.code_type()) {
249249
source.clone()
250250
} else {
251251
return Err(vm.new_type_error("source argument must be str or code object".to_string()));
@@ -346,21 +346,25 @@ fn builtin_id(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
346346
// builtin_input
347347

348348
fn builtin_isinstance(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
349-
arg_check!(vm, args, required = [(obj, None), (typ, None)]);
349+
arg_check!(
350+
vm,
351+
args,
352+
required = [(obj, None), (typ, Some(vm.get_type()))]
353+
);
350354

351-
let isinstance = objtype::isinstance(obj, typ);
352-
Ok(vm.context().new_bool(isinstance))
355+
let isinstance = objtype::isinstance(vm, obj, typ)?;
356+
Ok(vm.new_bool(isinstance))
353357
}
354358

355359
fn builtin_issubclass(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
356-
if args.args.len() != 2 {
357-
panic!("issubclass expects exactly two parameters");
358-
}
359-
360-
let cls1 = &args.args[0];
361-
let cls2 = &args.args[1];
360+
arg_check!(
361+
vm,
362+
args,
363+
required = [(subclass, Some(vm.get_type())), (cls, Some(vm.get_type()))]
364+
);
362365

363-
Ok(vm.context().new_bool(objtype::issubclass(cls1, cls2)))
366+
let issubclass = objtype::issubclass(vm, subclass, cls)?;
367+
Ok(vm.context().new_bool(issubclass))
364368
}
365369

366370
fn builtin_iter(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
@@ -498,7 +502,7 @@ fn builtin_next(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
498502
match vm.call_method(iterator, "__next__", vec![]) {
499503
Ok(value) => Ok(value),
500504
Err(value) => {
501-
if objtype::isinstance(&value, &vm.ctx.exceptions.stop_iteration) {
505+
if objtype::real_isinstance(&value, &vm.ctx.exceptions.stop_iteration) {
502506
match default_value {
503507
None => Err(value),
504508
Some(value) => Ok(value.clone()),
@@ -578,7 +582,7 @@ pub fn builtin_print(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
578582
.get_optional_kwarg("sep")
579583
.filter(|obj| !obj.is(&vm.get_none()));
580584
if let Some(ref obj) = sep_arg {
581-
if !objtype::isinstance(obj, &vm.ctx.str_type()) {
585+
if !objtype::real_isinstance(obj, &vm.ctx.str_type()) {
582586
return Err(vm.new_type_error(format!(
583587
"sep must be None or a string, not {}",
584588
objtype::get_type_name(&obj.typ())
@@ -592,7 +596,7 @@ pub fn builtin_print(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
592596
.get_optional_kwarg("end")
593597
.filter(|obj| !obj.is(&vm.get_none()));
594598
if let Some(ref obj) = end_arg {
595-
if !objtype::isinstance(obj, &vm.ctx.str_type()) {
599+
if !objtype::real_isinstance(obj, &vm.ctx.str_type()) {
596600
return Err(vm.new_type_error(format!(
597601
"end must be None or a string, not {}",
598602
objtype::get_type_name(&obj.typ())
@@ -805,9 +809,9 @@ pub fn builtin_build_class_(vm: &mut VirtualMachine, mut args: PyFuncArgs) -> Py
805809
let mut metaclass = args.get_kwarg("metaclass", vm.get_type());
806810

807811
for base in bases.clone() {
808-
if objtype::issubclass(&base.typ(), &metaclass) {
812+
if objtype::issubclass(vm, &base.typ(), &metaclass)? {
809813
metaclass = base.typ();
810-
} else if !objtype::issubclass(&metaclass, &base.typ()) {
814+
} else if !objtype::real_issubclass(&metaclass, &base.typ()) {
811815
return Err(vm.new_type_error("metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases".to_string()));
812816
}
813817
}

vm/src/exceptions.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,11 @@ fn exception_init(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
2323
pub fn print_exception(vm: &mut VirtualMachine, exc: &PyObjectRef) {
2424
if let Some(tb) = exc.get_attr("__traceback__") {
2525
println!("Traceback (most recent call last):");
26-
if objtype::isinstance(&tb, &vm.ctx.list_type()) {
26+
if objtype::real_isinstance(&tb, &vm.ctx.list_type()) {
2727
let mut elements = objsequence::get_elements(&tb).to_vec();
2828
elements.reverse();
2929
for element in elements.iter() {
30-
if objtype::isinstance(&element, &vm.ctx.tuple_type()) {
30+
if objtype::real_isinstance(&element, &vm.ctx.tuple_type()) {
3131
let element = objsequence::get_elements(&element);
3232
let filename = if let Ok(x) = vm.to_str(&element[0]) {
3333
objstr::get_value(&x)

vm/src/frame.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ impl Frame {
115115
Err(exception) => {
116116
// unwind block stack on exception and find any handlers.
117117
// Add an entry in the traceback:
118-
assert!(objtype::isinstance(
118+
assert!(objtype::real_isinstance(
119119
&exception,
120120
&vm.ctx.exceptions.base_exception_type
121121
));
@@ -525,7 +525,7 @@ impl Frame {
525525
0 | 2 | 3 => panic!("Not implemented!"),
526526
_ => panic!("Invalid parameter for RAISE_VARARGS, must be between 0 to 3"),
527527
};
528-
if objtype::isinstance(&exception, &vm.ctx.exceptions.base_exception_type) {
528+
if objtype::real_isinstance(&exception, &vm.ctx.exceptions.base_exception_type) {
529529
info!("Exception raised: {:?}", exception);
530530
Err(exception)
531531
} else {

vm/src/macros.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@ macro_rules! type_check {
1818
// None indicates that we have no type requirement (i.e. we accept any type)
1919
if let Some(expected_type) = $arg_type {
2020
let arg = &$args.args[$arg_count];
21-
if !$crate::obj::objtype::isinstance(arg, &expected_type) {
21+
22+
if !$crate::obj::objtype::isinstance($vm, arg, &expected_type)? {
2223
let arg_typ = arg.typ();
2324
let expected_type_name = $vm.to_pystr(&expected_type)?;
2425
let actual_type = $vm.to_pystr(&arg_typ)?;

vm/src/obj/objbool.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ The class bool is a subclass of the class int, and cannot be subclassed.";
5050
}
5151

5252
pub fn not(vm: &mut VirtualMachine, obj: &PyObjectRef) -> PyResult {
53-
if objtype::isinstance(obj, &vm.ctx.bool_type()) {
53+
if objtype::real_isinstance(obj, &vm.ctx.bool_type()) {
5454
let value = get_value(obj);
5555
Ok(vm.ctx.new_bool(!value))
5656
} else {

vm/src/obj/objbytearray.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ fn bytearray_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
119119
required = [(cls, None)],
120120
optional = [(val_option, None)]
121121
);
122-
if !objtype::issubclass(cls, &vm.ctx.bytearray_type()) {
122+
if !objtype::real_issubclass(cls, &vm.ctx.bytearray_type()) {
123123
return Err(vm.new_type_error(format!("{:?} is not a subtype of bytearray", cls)));
124124
}
125125

@@ -157,7 +157,7 @@ fn bytearray_eq(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
157157
required = [(a, Some(vm.ctx.bytearray_type())), (b, None)]
158158
);
159159

160-
let result = if objtype::isinstance(b, &vm.ctx.bytearray_type()) {
160+
let result = if objtype::real_isinstance(b, &vm.ctx.bytearray_type()) {
161161
get_value(a).to_vec() == get_value(b).to_vec()
162162
} else {
163163
false

vm/src/obj/objbytes.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ fn bytes_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
5252
required = [(cls, None)],
5353
optional = [(val_option, None)]
5454
);
55-
if !objtype::issubclass(cls, &vm.ctx.bytes_type()) {
55+
if !objtype::real_issubclass(cls, &vm.ctx.bytes_type()) {
5656
return Err(vm.new_type_error(format!("{:?} is not a subtype of bytes", cls)));
5757
}
5858

@@ -80,7 +80,7 @@ fn bytes_eq(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
8080
required = [(a, Some(vm.ctx.bytes_type())), (b, None)]
8181
);
8282

83-
let result = if objtype::isinstance(b, &vm.ctx.bytes_type()) {
83+
let result = if objtype::real_isinstance(b, &vm.ctx.bytes_type()) {
8484
get_value(a).to_vec() == get_value(b).to_vec()
8585
} else {
8686
false
@@ -95,7 +95,7 @@ fn bytes_ge(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
9595
required = [(a, Some(vm.ctx.bytes_type())), (b, None)]
9696
);
9797

98-
let result = if objtype::isinstance(b, &vm.ctx.bytes_type()) {
98+
let result = if objtype::real_isinstance(b, &vm.ctx.bytes_type()) {
9999
get_value(a).to_vec() >= get_value(b).to_vec()
100100
} else {
101101
return Err(vm.new_type_error(format!(
@@ -114,7 +114,7 @@ fn bytes_gt(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
114114
required = [(a, Some(vm.ctx.bytes_type())), (b, None)]
115115
);
116116

117-
let result = if objtype::isinstance(b, &vm.ctx.bytes_type()) {
117+
let result = if objtype::real_isinstance(b, &vm.ctx.bytes_type()) {
118118
get_value(a).to_vec() > get_value(b).to_vec()
119119
} else {
120120
return Err(vm.new_type_error(format!(
@@ -133,7 +133,7 @@ fn bytes_le(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
133133
required = [(a, Some(vm.ctx.bytes_type())), (b, None)]
134134
);
135135

136-
let result = if objtype::isinstance(b, &vm.ctx.bytes_type()) {
136+
let result = if objtype::real_isinstance(b, &vm.ctx.bytes_type()) {
137137
get_value(a).to_vec() <= get_value(b).to_vec()
138138
} else {
139139
return Err(vm.new_type_error(format!(
@@ -152,7 +152,7 @@ fn bytes_lt(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
152152
required = [(a, Some(vm.ctx.bytes_type())), (b, None)]
153153
);
154154

155-
let result = if objtype::isinstance(b, &vm.ctx.bytes_type()) {
155+
let result = if objtype::real_isinstance(b, &vm.ctx.bytes_type()) {
156156
get_value(a).to_vec() < get_value(b).to_vec()
157157
} else {
158158
return Err(vm.new_type_error(format!(

vm/src/obj/objcomplex.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ fn complex_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
6060
optional = [(real, None), (imag, None)]
6161
);
6262

63-
if !objtype::issubclass(cls, &vm.ctx.complex_type()) {
63+
if !objtype::real_issubclass(cls, &vm.ctx.complex_type()) {
6464
return Err(vm.new_type_error(format!("{:?} is not a subtype of complex", cls)));
6565
}
6666

@@ -109,9 +109,9 @@ fn complex_add(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
109109
);
110110

111111
let v1 = get_value(i);
112-
if objtype::isinstance(i2, &vm.ctx.complex_type()) {
112+
if objtype::real_isinstance(i2, &vm.ctx.complex_type()) {
113113
Ok(vm.ctx.new_complex(v1 + get_value(i2)))
114-
} else if objtype::isinstance(i2, &vm.ctx.int_type()) {
114+
} else if objtype::real_isinstance(i2, &vm.ctx.int_type()) {
115115
Ok(vm.ctx.new_complex(Complex64::new(
116116
v1.re + objint::get_value(i2).to_f64().unwrap(),
117117
v1.im,
@@ -130,7 +130,7 @@ fn complex_radd(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
130130

131131
let v1 = get_value(i);
132132

133-
if objtype::isinstance(i2, &vm.ctx.int_type()) {
133+
if objtype::real_isinstance(i2, &vm.ctx.int_type()) {
134134
Ok(vm.ctx.new_complex(Complex64::new(
135135
v1.re + objint::get_value(i2).to_f64().unwrap(),
136136
v1.im,
@@ -156,14 +156,14 @@ fn complex_eq(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
156156

157157
let z = get_value(zelf);
158158

159-
let result = if objtype::isinstance(other, &vm.ctx.complex_type()) {
159+
let result = if objtype::real_isinstance(other, &vm.ctx.complex_type()) {
160160
z == get_value(other)
161-
} else if objtype::isinstance(other, &vm.ctx.int_type()) {
161+
} else if objtype::real_isinstance(other, &vm.ctx.int_type()) {
162162
match objint::get_value(other).to_f64() {
163163
Some(f) => z.im == 0.0f64 && z.re == f,
164164
None => false,
165165
}
166-
} else if objtype::isinstance(other, &vm.ctx.float_type()) {
166+
} else if objtype::real_isinstance(other, &vm.ctx.float_type()) {
167167
z.im == 0.0 && z.re == objfloat::get_value(other)
168168
} else {
169169
return Ok(vm.ctx.not_implemented());

0 commit comments

Comments
 (0)