mirror of
https://github.com/RustPython/RustPython.git
synced 2026-10-28 18:59:04 +08:00
Merge pull request #1766 from RustPython/coolreader18/stdlib-typing
Add the typing standard library module, rework tp_new
This commit is contained in:
15 files changed
+2702
-194
No files matched your search
@@ -827,8 +827,6 @@ class ClassCreationTests(unittest.TestCase):
|
||||
C = types.new_class("C", (int,))
|
||||
self.assertTrue(issubclass(C, int))
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_new_class_meta(self):
|
||||
Meta = self.Meta
|
||||
settings = {"metaclass": Meta, "z": 2}
|
||||
@@ -839,8 +837,6 @@ class ClassCreationTests(unittest.TestCase):
|
||||
self.assertEqual(C.y, 1)
|
||||
self.assertEqual(C.z, 2)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_new_class_exec_body(self):
|
||||
Meta = self.Meta
|
||||
def func(ns):
|
||||
@@ -866,8 +862,6 @@ class ClassCreationTests(unittest.TestCase):
|
||||
self.assertEqual(C.__name__, "C")
|
||||
self.assertEqual(C.__bases__, (object,))
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_new_class_meta_with_base(self):
|
||||
Meta = self.Meta
|
||||
def func(ns):
|
||||
@@ -1010,8 +1004,6 @@ class ClassCreationTests(unittest.TestCase):
|
||||
for bases in [x, y, z, t]:
|
||||
self.assertIs(types.resolve_bases(bases), bases)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_metaclass_derivation(self):
|
||||
# issue1294232: correct metaclass calculation
|
||||
new_calls = [] # to check the order of __new__ calls
|
||||
@@ -1066,8 +1058,6 @@ class ClassCreationTests(unittest.TestCase):
|
||||
new_calls.clear()
|
||||
self.assertIn('BMeta_was_here', E.__dict__)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_metaclass_override_function(self):
|
||||
# Special case: the given metaclass isn't a class,
|
||||
# so there is no metaclass calculation.
|
||||
@@ -1469,8 +1459,6 @@ class CoroutineTests(unittest.TestCase):
|
||||
self.assertIs(foo(), coro)
|
||||
self.assertIs(foo().__await__(), coro)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_duck_gen(self):
|
||||
class GenLike:
|
||||
def send(self): pass
|
||||
|
||||
Vendored
+2414
File diff suppressed because it is too large.
Load diff
@@ -169,7 +169,9 @@ class T5(int):
|
||||
|
||||
assert str(super(int, T5(5))) == "<super: <class 'int'>, <T5 object>>"
|
||||
|
||||
#assert str(super(type, None)) == "<super: <class 'type'>, NULL>"
|
||||
assert str(super(type, None)) == "<super: <class 'type'>, NULL>"
|
||||
|
||||
assert str(super(int).__get__(T5(5))) == "<super: <class 'int'>, <T5 object>>"
|
||||
|
||||
a = 1
|
||||
class A:
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
try:
|
||||
import gc
|
||||
except ImportError:
|
||||
gc = None
|
||||
|
||||
assert type(type) is type
|
||||
assert type(object) is type
|
||||
assert type(object()) is object
|
||||
@@ -50,13 +55,17 @@ assert B.__bases__ == (A,)
|
||||
|
||||
del D
|
||||
|
||||
try: # gc sweep is needed here for CPython...
|
||||
import gc; gc.collect()
|
||||
except: # ...while RustPython doesn't have `gc` yet.
|
||||
pass
|
||||
if gc:
|
||||
# gc sweep is needed here for CPython...
|
||||
gc.collect()
|
||||
# ...while RustPython doesn't have `gc` yet.
|
||||
|
||||
assert B.__subclasses__() == []
|
||||
assert C.__subclasses__() == []
|
||||
if gc:
|
||||
# D.__new__ is a method bound to the D type, so just deleting D
|
||||
# from globals won't actually invalidate the weak reference that
|
||||
# subclasses holds. TODO: implement a proper tracing gc
|
||||
assert B.__subclasses__() == []
|
||||
assert C.__subclasses__() == []
|
||||
|
||||
assert type in object.__subclasses__()
|
||||
|
||||
|
||||
+4
-1
@@ -967,7 +967,10 @@ pub fn builtin_build_class_(
|
||||
|
||||
let class = vm.invoke(
|
||||
metaclass.as_object(),
|
||||
vec![name_obj, bases, namespace.into_object()],
|
||||
(
|
||||
Args::from(vec![name_obj, bases, namespace.into_object()]),
|
||||
kwargs,
|
||||
),
|
||||
)?;
|
||||
cells.set_item("__class__", class.clone(), vm)?;
|
||||
Ok(class)
|
||||
|
||||
@@ -44,6 +44,16 @@ impl From<PyObjectRef> for PyFuncArgs {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<(Args, KwArgs)> for PyFuncArgs {
|
||||
fn from(arg: (Args, KwArgs)) -> Self {
|
||||
let Args(args) = arg.0;
|
||||
let KwArgs(kwargs) = arg.1;
|
||||
PyFuncArgs {
|
||||
args,
|
||||
kwargs: kwargs.into_iter().collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl From<(&Args, &KwArgs)> for PyFuncArgs {
|
||||
fn from(arg: (&Args, &KwArgs)) -> Self {
|
||||
let Args(args) = arg.0;
|
||||
@@ -297,6 +307,11 @@ impl<T> Args<T> {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
impl<T> From<Vec<T>> for Args<T> {
|
||||
fn from(v: Vec<T>) -> Self {
|
||||
Args(v)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> AsRef<[T]> for Args<T> {
|
||||
fn as_ref(&self) -> &[T] {
|
||||
|
||||
@@ -153,6 +153,7 @@ macro_rules! extend_class {
|
||||
$(
|
||||
$crate::extend_class!(@set_attr($ctx, $class, $name, $value));
|
||||
)*
|
||||
$ctx.add_tp_new_wrapper(&$class);
|
||||
};
|
||||
|
||||
(@set_attr($ctx:expr, $class:expr, (slot $slot_name:ident), $value:expr)) => {
|
||||
|
||||
@@ -64,7 +64,7 @@ impl PyModuleRef {
|
||||
}
|
||||
|
||||
fn name(self, vm: &VirtualMachine) -> Option<String> {
|
||||
vm.generic_getattribute(
|
||||
vm.generic_getattribute_opt(
|
||||
self.as_object().clone(),
|
||||
PyString::from("__name__").into_ref(vm),
|
||||
)
|
||||
@@ -74,7 +74,7 @@ impl PyModuleRef {
|
||||
|
||||
#[pymethod(magic)]
|
||||
fn getattribute(self, name: PyStringRef, vm: &VirtualMachine) -> PyResult {
|
||||
vm.generic_getattribute(self.as_object().clone(), name.clone())?
|
||||
vm.generic_getattribute_opt(self.as_object().clone(), name.clone())?
|
||||
.ok_or_else(|| {
|
||||
let module_name = if let Some(name) = self.name(vm) {
|
||||
format!(" '{}'", name)
|
||||
|
||||
@@ -2,7 +2,7 @@ use super::objbool;
|
||||
use super::objdict::PyDictRef;
|
||||
use super::objlist::PyList;
|
||||
use super::objstr::PyStringRef;
|
||||
use super::objtype::{self, PyClassRef};
|
||||
use super::objtype::PyClassRef;
|
||||
use crate::function::{OptionalArg, PyFuncArgs};
|
||||
use crate::pyhash;
|
||||
use crate::pyobject::{
|
||||
@@ -210,8 +210,7 @@ impl PyBaseObject {
|
||||
#[pymethod(magic)]
|
||||
fn getattribute(obj: PyObjectRef, name: PyStringRef, vm: &VirtualMachine) -> PyResult {
|
||||
vm_trace!("object.__getattribute__({:?}, {:?})", obj, name);
|
||||
vm.generic_getattribute(obj.clone(), name.clone())?
|
||||
.ok_or_else(|| vm.new_attribute_error(format!("{} has no attribute '{}'", obj, name)))
|
||||
vm.generic_getattribute(obj, name)
|
||||
}
|
||||
|
||||
#[pymethod(magic)]
|
||||
@@ -263,10 +262,6 @@ pub(crate) fn setattr(
|
||||
|
||||
pub fn init(context: &PyContext) {
|
||||
PyBaseObject::extend_class(context, &context.types.object_type);
|
||||
extend_class!(context, &context.types.object_type, {
|
||||
// yeah, it's `type_new`, but we're putting here so it's available on every object
|
||||
"__new__" => context.new_classmethod(objtype::type_new),
|
||||
});
|
||||
}
|
||||
|
||||
fn common_reduce(obj: PyObjectRef, proto: usize, vm: &VirtualMachine) -> PyResult {
|
||||
|
||||
@@ -31,6 +31,10 @@ impl SlotDescriptor for PyStaticMethod {
|
||||
|
||||
#[pyimpl(with(SlotDescriptor), flags(BASETYPE))]
|
||||
impl PyStaticMethod {
|
||||
pub fn new(callable: PyObjectRef) -> Self {
|
||||
Self { callable }
|
||||
}
|
||||
|
||||
#[pyslot]
|
||||
fn tp_new(
|
||||
cls: PyClassRef,
|
||||
|
||||
+96
-72
@@ -6,24 +6,26 @@ https://github.com/python/cpython/blob/50b48572d9a90c5bb36e2bef6179548ea927a35a/
|
||||
|
||||
*/
|
||||
|
||||
use super::objfunction::PyBoundMethod;
|
||||
use super::objstr::PyStringRef;
|
||||
use super::objtype::{self, PyClass, PyClassRef};
|
||||
use crate::function::OptionalArg;
|
||||
use crate::pyobject::{
|
||||
PyClassImpl, PyContext, PyObjectRef, PyRef, PyResult, PyValue, TryFromObject, TypeProtocol,
|
||||
IdProtocol, PyClassImpl, PyContext, PyObjectRef, PyRef, PyResult, PyValue, TryFromObject,
|
||||
TypeProtocol,
|
||||
};
|
||||
use crate::scope::NameProtocol;
|
||||
use crate::slots::SlotDescriptor;
|
||||
use crate::vm::VirtualMachine;
|
||||
|
||||
use itertools::Itertools;
|
||||
|
||||
pub type PySuperRef = PyRef<PySuper>;
|
||||
|
||||
#[pyclass]
|
||||
#[derive(Debug)]
|
||||
pub struct PySuper {
|
||||
obj: PyObjectRef,
|
||||
typ: PyObjectRef,
|
||||
obj_type: PyObjectRef,
|
||||
typ: PyClassRef,
|
||||
obj: Option<(PyObjectRef, PyClassRef)>,
|
||||
}
|
||||
|
||||
impl PyValue for PySuper {
|
||||
@@ -32,48 +34,48 @@ impl PyValue for PySuper {
|
||||
}
|
||||
}
|
||||
|
||||
#[pyimpl]
|
||||
#[pyimpl(with(SlotDescriptor))]
|
||||
impl PySuper {
|
||||
fn new(typ: PyClassRef, obj: PyObjectRef, vm: &VirtualMachine) -> PyResult<Self> {
|
||||
let obj = if vm.is_none(&obj) {
|
||||
None
|
||||
} else {
|
||||
let obj_type = supercheck(typ.clone(), obj.clone(), vm)?;
|
||||
Some((obj, obj_type))
|
||||
};
|
||||
Ok(Self { typ, obj })
|
||||
}
|
||||
|
||||
#[pymethod(name = "__repr__")]
|
||||
fn repr(&self) -> String {
|
||||
let class_type_str = if let Ok(type_class) = self.typ.clone().downcast::<PyClass>() {
|
||||
type_class.name.clone()
|
||||
} else {
|
||||
"NONE".to_owned()
|
||||
};
|
||||
match self.obj_type.clone().downcast::<PyClass>() {
|
||||
Ok(obj_class_typ) => format!(
|
||||
"<super: <class '{}'>, <{} object>>",
|
||||
class_type_str, obj_class_typ.name
|
||||
),
|
||||
_ => format!("<super: <class '{}'> NULL>", class_type_str),
|
||||
let typname = &self.typ.name;
|
||||
match self.obj {
|
||||
Some((_, ref ty)) => format!("<super: <class '{}'>, <{} object>>", typname, ty.name),
|
||||
None => format!("<super: <class '{}'>, NULL>", typname),
|
||||
}
|
||||
}
|
||||
|
||||
#[pymethod(name = "__getattribute__")]
|
||||
fn getattribute(&self, name: PyStringRef, vm: &VirtualMachine) -> PyResult {
|
||||
let inst = self.obj.clone();
|
||||
let typ = self.typ.clone();
|
||||
|
||||
match typ.payload::<PyClass>() {
|
||||
Some(PyClass { ref mro, .. }) => {
|
||||
for class in mro {
|
||||
if let Ok(item) = vm.get_attribute(class.as_object().clone(), name.clone()) {
|
||||
if item.payload_is::<PyBoundMethod>() {
|
||||
// This is a classmethod
|
||||
return Ok(item);
|
||||
}
|
||||
return vm.call_if_get_descriptor(item, inst.clone());
|
||||
}
|
||||
}
|
||||
Err(vm.new_attribute_error(format!(
|
||||
"{} has no attribute '{}'",
|
||||
inst,
|
||||
name.as_str()
|
||||
)))
|
||||
fn getattribute(zelf: PyRef<Self>, name: PyStringRef, vm: &VirtualMachine) -> PyResult {
|
||||
let (inst, obj_type) = match zelf.obj.clone() {
|
||||
Some(o) => o,
|
||||
None => return vm.generic_getattribute(zelf.into_object(), name),
|
||||
};
|
||||
// skip the classes in obj_type.mro up to and including zelf.typ
|
||||
let mut it = obj_type.iter_mro().peekable();
|
||||
for _ in it.peeking_take_while(|cls| !cls.is(&zelf.typ)) {}
|
||||
for cls in it.skip(1) {
|
||||
if let Some(descr) = cls.get_attr(name.as_str()) {
|
||||
return vm
|
||||
.call_get_descriptor_specific(
|
||||
descr.clone(),
|
||||
if inst.is(&obj_type) { None } else { Some(inst) },
|
||||
Some(obj_type.clone().into_object()),
|
||||
)
|
||||
.unwrap_or(Ok(descr));
|
||||
}
|
||||
_ => panic!("not Class"),
|
||||
}
|
||||
vm.generic_getattribute(zelf.into_object(), name)
|
||||
}
|
||||
|
||||
#[pyslot]
|
||||
@@ -84,7 +86,7 @@ impl PySuper {
|
||||
vm: &VirtualMachine,
|
||||
) -> PyResult<PySuperRef> {
|
||||
// Get the type:
|
||||
let py_type = if let OptionalArg::Present(ty) = py_type {
|
||||
let typ = if let OptionalArg::Present(ty) = py_type {
|
||||
ty
|
||||
} else {
|
||||
match vm.current_scope().load_cell(vm, "__class__") {
|
||||
@@ -99,60 +101,82 @@ impl PySuper {
|
||||
};
|
||||
|
||||
// Check type argument:
|
||||
if !objtype::isinstance(py_type.as_object(), &vm.get_type()) {
|
||||
if !objtype::isinstance(typ.as_object(), &vm.get_type()) {
|
||||
return Err(vm.new_type_error(format!(
|
||||
"super() argument 1 must be type, not {}",
|
||||
py_type.class().name
|
||||
typ.class().name
|
||||
)));
|
||||
}
|
||||
|
||||
// Get the bound object:
|
||||
let py_obj = if let OptionalArg::Present(obj) = py_obj {
|
||||
obj.clone()
|
||||
let obj = if let OptionalArg::Present(obj) = py_obj {
|
||||
obj
|
||||
} else {
|
||||
let frame = vm.current_frame().expect("no current frame for super()");
|
||||
if let Some(first_arg) = frame.code.arg_names.get(0) {
|
||||
match vm.get_locals().get_item_option(first_arg, vm)? {
|
||||
Some(obj) => obj.clone(),
|
||||
match frame.scope.get_locals().get_item_option(first_arg, vm)? {
|
||||
Some(obj) => obj,
|
||||
_ => {
|
||||
return Err(vm.new_type_error(format!(
|
||||
"super arguement {} was not supplied",
|
||||
"super argument {} was not supplied",
|
||||
first_arg
|
||||
)));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return Err(vm.new_type_error(
|
||||
"super must be called with 1 argument or from inside class method".to_owned(),
|
||||
));
|
||||
vm.get_none()
|
||||
}
|
||||
};
|
||||
|
||||
// Check obj type:
|
||||
let obj_type = if !objtype::isinstance(&py_obj, &py_type) {
|
||||
let is_subclass = if let Ok(py_obj) = PyClassRef::try_from_object(vm, py_obj.clone()) {
|
||||
objtype::issubclass(&py_obj, &py_type)
|
||||
} else {
|
||||
false
|
||||
};
|
||||
if !is_subclass {
|
||||
return Err(vm.new_type_error(
|
||||
"super(type, obj): obj must be an instance or subtype of type".to_owned(),
|
||||
));
|
||||
}
|
||||
PyClassRef::try_from_object(vm, py_obj.clone())?
|
||||
} else {
|
||||
py_obj.class()
|
||||
};
|
||||
|
||||
PySuper {
|
||||
obj: py_obj,
|
||||
typ: py_type.into_object(),
|
||||
obj_type: obj_type.into_object(),
|
||||
}
|
||||
.into_ref_with_type(vm, cls)
|
||||
PySuper::new(typ, obj, vm)?.into_ref_with_type(vm, cls)
|
||||
}
|
||||
}
|
||||
|
||||
impl SlotDescriptor for PySuper {
|
||||
fn descr_get(
|
||||
vm: &VirtualMachine,
|
||||
zelf: PyObjectRef,
|
||||
obj: Option<PyObjectRef>,
|
||||
_cls: OptionalArg<PyObjectRef>,
|
||||
) -> PyResult {
|
||||
let (zelf, obj) = Self::_unwrap(zelf, obj, vm)?;
|
||||
if vm.is_none(&obj) || zelf.obj.is_some() {
|
||||
return Ok(zelf.into_object());
|
||||
}
|
||||
let zelf_class = zelf.as_object().class();
|
||||
if zelf_class.is(&vm.ctx.types.super_type) {
|
||||
Ok(PySuper::new(zelf.typ.clone(), obj, vm)?
|
||||
.into_ref(vm)
|
||||
.into_object())
|
||||
} else {
|
||||
let obj = zelf.obj.clone().map_or(vm.get_none(), |(o, _)| o);
|
||||
vm.invoke(
|
||||
zelf_class.as_object(),
|
||||
vec![zelf.typ.clone().into_object(), obj],
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn supercheck(ty: PyClassRef, obj: PyObjectRef, vm: &VirtualMachine) -> PyResult<PyClassRef> {
|
||||
if let Ok(cls) = obj.clone().downcast::<PyClass>() {
|
||||
if objtype::issubclass(&cls, &ty) {
|
||||
return Ok(cls);
|
||||
}
|
||||
}
|
||||
if objtype::isinstance(&obj, &ty) {
|
||||
return Ok(obj.class());
|
||||
}
|
||||
let class_attr = vm.get_attribute(obj.clone(), "__class__")?;
|
||||
if let Ok(cls) = class_attr.downcast::<PyClass>() {
|
||||
if !cls.is(&ty) && objtype::issubclass(&cls, &ty) {
|
||||
return Ok(cls);
|
||||
}
|
||||
}
|
||||
Err(vm
|
||||
.new_type_error("super(type, obj): obj must be an instance or subtype of type".to_owned()))
|
||||
}
|
||||
|
||||
pub fn init(context: &PyContext) {
|
||||
let super_type = &context.types.super_type;
|
||||
PySuper::extend_class(context, super_type);
|
||||
|
||||
+57
-73
@@ -5,6 +5,7 @@ use std::fmt;
|
||||
use super::objdict::PyDictRef;
|
||||
use super::objlist::PyList;
|
||||
use super::objmappingproxy::PyMappingProxy;
|
||||
use super::objstaticmethod::PyStaticMethod;
|
||||
use super::objstr::PyStringRef;
|
||||
use super::objtuple::PyTuple;
|
||||
use super::objweakref::PyWeak;
|
||||
@@ -44,45 +45,15 @@ impl PyValue for PyClass {
|
||||
}
|
||||
}
|
||||
|
||||
struct IterMro<'a> {
|
||||
cls: &'a PyClassRef,
|
||||
offset: Option<usize>,
|
||||
}
|
||||
|
||||
impl<'a> Iterator for IterMro<'a> {
|
||||
type Item = &'a PyClassRef;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
match self.offset {
|
||||
None => {
|
||||
self.offset = Some(0);
|
||||
Some(&self.cls)
|
||||
}
|
||||
Some(offset) => {
|
||||
if offset < self.cls.mro.len() {
|
||||
self.offset = Some(offset + 1);
|
||||
Some(&self.cls.mro[offset])
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[pyimpl(flags(BASETYPE))]
|
||||
impl PyClassRef {
|
||||
fn iter_mro(&self) -> IterMro {
|
||||
IterMro {
|
||||
cls: self,
|
||||
offset: None,
|
||||
}
|
||||
pub fn iter_mro(&self) -> impl Iterator<Item = &PyClassRef> + DoubleEndedIterator {
|
||||
std::iter::once(self).chain(self.mro.iter())
|
||||
}
|
||||
|
||||
#[pyproperty(name = "__mro__")]
|
||||
fn get_mro(self) -> PyTuple {
|
||||
let elements: Vec<PyObjectRef> =
|
||||
_mro(&self).iter().map(|x| x.as_object().clone()).collect();
|
||||
let elements: Vec<PyObjectRef> = self.iter_mro().map(|x| x.as_object().clone()).collect();
|
||||
PyTuple::from(elements)
|
||||
}
|
||||
|
||||
@@ -252,11 +223,12 @@ impl PyClassRef {
|
||||
|
||||
#[pymethod]
|
||||
fn mro(self, vm: &VirtualMachine) -> PyObjectRef {
|
||||
let mut mro = vec![self.clone().into_object()];
|
||||
mro.extend(self.mro.iter().map(|x| x.clone().into_object()));
|
||||
vm.ctx.new_list(mro)
|
||||
vm.ctx.new_list(
|
||||
self.iter_mro()
|
||||
.map(|cls| cls.clone().into_object())
|
||||
.collect(),
|
||||
)
|
||||
}
|
||||
|
||||
#[pyslot]
|
||||
fn tp_new(metatype: PyClassRef, args: PyFuncArgs, vm: &VirtualMachine) -> PyResult {
|
||||
vm_trace!("type.__new__ {:?}", args);
|
||||
@@ -311,9 +283,16 @@ impl PyClassRef {
|
||||
(metatype, base, bases)
|
||||
};
|
||||
|
||||
let attributes = dict.to_attributes();
|
||||
let mut attributes = dict.to_attributes();
|
||||
if let Some(f) = attributes.get_mut("__new__") {
|
||||
if f.class().is(&vm.ctx.function_type()) {
|
||||
*f = PyStaticMethod::new(f.clone()).into_ref(vm).into_object();
|
||||
}
|
||||
}
|
||||
|
||||
let typ = new(metatype, name.as_str(), base.clone(), bases, attributes)?;
|
||||
typ.slots.borrow_mut().flags = base.slots.borrow().flags;
|
||||
vm.ctx.add_tp_new_wrapper(&typ);
|
||||
Ok(typ.into())
|
||||
}
|
||||
|
||||
@@ -321,9 +300,12 @@ impl PyClassRef {
|
||||
#[pymethod(magic)]
|
||||
fn call(self, args: PyFuncArgs, vm: &VirtualMachine) -> PyResult {
|
||||
vm_trace!("type_call: {:?}", self);
|
||||
let new = vm.get_attribute(self.as_object().clone(), "__new__")?;
|
||||
let new_args = args.insert(self.into_object());
|
||||
let obj = vm.invoke(&new, new_args)?;
|
||||
let obj = call_tp_new(self.clone(), self.clone(), args.clone(), vm)?;
|
||||
|
||||
if (self.is(&vm.ctx.types.type_type) && args.kwargs.is_empty()) || !isinstance(&obj, &self)
|
||||
{
|
||||
return Ok(obj);
|
||||
}
|
||||
|
||||
if let Some(init_method_or_err) = vm.get_method(obj.clone(), "__init__") {
|
||||
let init_method = init_method_or_err?;
|
||||
@@ -356,10 +338,6 @@ pub(crate) fn init(ctx: &PyContext) {
|
||||
PyClassRef::extend_class(ctx, &ctx.types.type_type);
|
||||
}
|
||||
|
||||
fn _mro(cls: &PyClassRef) -> Vec<PyClassRef> {
|
||||
cls.iter_mro().cloned().collect()
|
||||
}
|
||||
|
||||
/// Determines if `obj` actually an instance of `cls`, this doesn't call __instancecheck__, so only
|
||||
/// use this if `cls` is known to have not overridden the base __instancecheck__ magic method.
|
||||
#[inline]
|
||||
@@ -371,11 +349,34 @@ pub fn isinstance<T: TypeProtocol>(obj: &T, cls: &PyClassRef) -> bool {
|
||||
/// so only use this if `cls` is known to have not overridden the base __subclasscheck__ magic
|
||||
/// method.
|
||||
pub fn issubclass(subclass: &PyClassRef, cls: &PyClassRef) -> bool {
|
||||
let mro = &subclass.mro;
|
||||
subclass.is(cls) || mro.iter().any(|c| c.is(cls.as_object()))
|
||||
subclass.iter_mro().any(|c| c.is(cls))
|
||||
}
|
||||
|
||||
pub fn type_new(
|
||||
fn call_tp_new(
|
||||
typ: PyClassRef,
|
||||
subtype: PyClassRef,
|
||||
args: PyFuncArgs,
|
||||
vm: &VirtualMachine,
|
||||
) -> PyResult {
|
||||
for cls in typ.iter_mro() {
|
||||
if let Some(new_meth) = cls.get_attr("__new__") {
|
||||
if !vm.ctx.is_tp_new_wrapper(&new_meth) {
|
||||
let new_meth = vm.call_if_get_descriptor(new_meth, typ.clone().into_object())?;
|
||||
return vm.invoke(&new_meth, args.insert(typ.clone().into_object()));
|
||||
}
|
||||
}
|
||||
}
|
||||
let class_with_new_slot = typ
|
||||
.iter_mro()
|
||||
.cloned()
|
||||
.find(|cls| cls.slots.borrow().new.is_some())
|
||||
.expect("Should be able to find a new slot somewhere in the mro");
|
||||
let slots = class_with_new_slot.slots.borrow();
|
||||
let new_slot = slots.new.as_ref().unwrap();
|
||||
new_slot(vm, args.insert(subtype.into_object()))
|
||||
}
|
||||
|
||||
pub fn tp_new_wrapper(
|
||||
zelf: PyClassRef,
|
||||
cls: PyClassRef,
|
||||
args: PyFuncArgs,
|
||||
@@ -388,21 +389,7 @@ pub fn type_new(
|
||||
cls = cls.name,
|
||||
)));
|
||||
}
|
||||
|
||||
let class_with_new_slot = if cls.slots.borrow().new.is_some() {
|
||||
cls.clone()
|
||||
} else {
|
||||
cls.mro
|
||||
.iter()
|
||||
.cloned()
|
||||
.find(|cls| cls.slots.borrow().new.is_some())
|
||||
.expect("Should be able to find a new slot somewhere in the mro")
|
||||
};
|
||||
|
||||
let slots = class_with_new_slot.slots.borrow();
|
||||
let new = slots.new.as_ref().unwrap();
|
||||
|
||||
new(vm, args.insert(cls.into_object()))
|
||||
call_tp_new(zelf, cls, args, vm)
|
||||
}
|
||||
|
||||
impl PyClassRef {
|
||||
@@ -425,21 +412,15 @@ impl PyClassRef {
|
||||
|
||||
// This is the internal has_attr implementation for fast lookup on a class.
|
||||
pub fn has_attr(&self, attr_name: &str) -> bool {
|
||||
self.attributes.borrow().contains_key(attr_name)
|
||||
|| self
|
||||
.mro
|
||||
.iter()
|
||||
.any(|c| c.attributes.borrow().contains_key(attr_name))
|
||||
self.iter_mro()
|
||||
.any(|c| c.attributes.borrow().contains_key(attr_name))
|
||||
}
|
||||
|
||||
pub fn get_attributes(self) -> PyAttributes {
|
||||
// Gather all members here:
|
||||
let mut attributes = PyAttributes::new();
|
||||
|
||||
let mut base_classes: Vec<&PyClassRef> = self.iter_mro().collect();
|
||||
base_classes.reverse();
|
||||
|
||||
for bc in base_classes {
|
||||
for bc in self.iter_mro().rev() {
|
||||
for (name, value) in bc.attributes.borrow().iter() {
|
||||
attributes.insert(name.to_owned(), value.clone());
|
||||
}
|
||||
@@ -495,7 +476,10 @@ pub fn new(
|
||||
bases: Vec<PyClassRef>,
|
||||
dict: HashMap<String, PyObjectRef>,
|
||||
) -> PyResult<PyClassRef> {
|
||||
let mros = bases.iter().map(|x| _mro(&x)).collect();
|
||||
let mros = bases
|
||||
.iter()
|
||||
.map(|x| x.iter_mro().cloned().collect())
|
||||
.collect();
|
||||
let mro = linearise_mro(mros).unwrap();
|
||||
let new_type = PyObject {
|
||||
payload: PyClass {
|
||||
|
||||
@@ -33,6 +33,7 @@ use crate::obj::objnamespace::PyNamespace;
|
||||
use crate::obj::objnone::{PyNone, PyNoneRef};
|
||||
use crate::obj::objobject;
|
||||
use crate::obj::objset::PySet;
|
||||
use crate::obj::objstaticmethod::PyStaticMethod;
|
||||
use crate::obj::objstr;
|
||||
use crate::obj::objtuple::{PyTuple, PyTupleRef};
|
||||
use crate::obj::objtype::{self, PyClass, PyClassRef};
|
||||
@@ -105,6 +106,7 @@ pub struct PyContext {
|
||||
pub types: TypeZoo,
|
||||
pub exceptions: exceptions::ExceptionZoo,
|
||||
pub int_cache_pool: Vec<PyObjectRef>,
|
||||
tp_new_wrapper: PyObjectRef,
|
||||
}
|
||||
|
||||
pub type PyNotImplementedRef = PyRef<PyNotImplemented>;
|
||||
@@ -159,6 +161,12 @@ impl PyContext {
|
||||
|
||||
let empty_tuple = create_object(PyTuple::from(vec![]), &types.tuple_type);
|
||||
|
||||
let tp_new_wrapper = create_object(
|
||||
PyBuiltinFunction::new(objtype::tp_new_wrapper.into_func()),
|
||||
&types.builtin_function_or_method_type,
|
||||
)
|
||||
.into_object();
|
||||
|
||||
let context = PyContext {
|
||||
true_value,
|
||||
false_value,
|
||||
@@ -171,6 +179,7 @@ impl PyContext {
|
||||
types,
|
||||
exceptions,
|
||||
int_cache_pool,
|
||||
tp_new_wrapper,
|
||||
};
|
||||
initialize_types(&context);
|
||||
|
||||
@@ -502,6 +511,16 @@ impl PyContext {
|
||||
None,
|
||||
)
|
||||
}
|
||||
pub fn new_staticmethod<F, T, R, VM>(&self, f: F) -> PyObjectRef
|
||||
where
|
||||
F: IntoPyNativeFunc<T, R, VM>,
|
||||
{
|
||||
PyObject::new(
|
||||
PyStaticMethod::new(self.new_method(f)),
|
||||
self.staticmethod_type(),
|
||||
None,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn new_readonly_getset<F, T>(&self, name: impl Into<String>, f: F) -> PyObjectRef
|
||||
where
|
||||
@@ -581,6 +600,19 @@ impl PyContext {
|
||||
bytecode::Constant::Ellipsis => self.ellipsis(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_tp_new_wrapper(&self, ty: &PyClassRef) {
|
||||
if !ty.attributes.borrow().contains_key("__new__") {
|
||||
let new_wrapper =
|
||||
self.new_bound_method(self.tp_new_wrapper.clone(), ty.clone().into_object());
|
||||
ty.set_str_attr("__new__", new_wrapper);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_tp_new_wrapper(&self, obj: &PyObjectRef) -> bool {
|
||||
obj.payload::<PyBoundMethod>()
|
||||
.map_or(false, |bound| bound.function.is(&self.tp_new_wrapper))
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for PyContext {
|
||||
@@ -1220,6 +1252,7 @@ pub trait PyClassImpl: PyClassDef {
|
||||
fn extend_class(ctx: &PyContext, class: &PyClassRef) {
|
||||
Self::impl_extend_class(ctx, class);
|
||||
class.slots.borrow_mut().flags = Self::TP_FLAGS;
|
||||
ctx.add_tp_new_wrapper(&class);
|
||||
if let Some(doc) = Self::DOC {
|
||||
class.set_str_attr("__doc__", ctx.new_str(doc.into()));
|
||||
}
|
||||
|
||||
@@ -767,9 +767,9 @@ impl PyItertoolsTee {
|
||||
.into_object())
|
||||
}
|
||||
|
||||
#[pymethod(name = "__new__")]
|
||||
#[pyslot]
|
||||
#[allow(clippy::new_ret_no_self)]
|
||||
fn new(
|
||||
fn tp_new(
|
||||
_cls: PyClassRef,
|
||||
iterable: PyObjectRef,
|
||||
n: OptionalArg<usize>,
|
||||
|
||||
+54
-18
@@ -8,9 +8,9 @@ use std::borrow::Borrow;
|
||||
use std::cell::{Cell, Ref, RefCell};
|
||||
use std::collections::hash_map::HashMap;
|
||||
use std::collections::hash_set::HashSet;
|
||||
use std::fmt;
|
||||
use std::rc::Rc;
|
||||
use std::sync::{Mutex, MutexGuard};
|
||||
use std::{env, fmt};
|
||||
|
||||
use arr_macro::arr;
|
||||
use num_bigint::BigInt;
|
||||
@@ -21,7 +21,7 @@ use rustpython_compiler::{compile, error::CompileError};
|
||||
|
||||
use crate::builtins::{self, to_ascii};
|
||||
use crate::bytecode;
|
||||
use crate::exceptions::{PyBaseException, PyBaseExceptionRef};
|
||||
use crate::exceptions::{self, PyBaseException, PyBaseExceptionRef};
|
||||
use crate::frame::{ExecutionResult, Frame, FrameRef};
|
||||
use crate::frozen;
|
||||
use crate::function::{OptionalArg, PyFuncArgs};
|
||||
@@ -234,8 +234,10 @@ impl VirtualMachine {
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
import::import_builtin(self, "signal").expect("Couldn't initialize signal module");
|
||||
|
||||
import::init_importlib(self, initialize_parameter)
|
||||
.expect("Initialize importlib fail");
|
||||
self.expect_pyresult(
|
||||
import::init_importlib(self, initialize_parameter),
|
||||
"Initialize importlib fail",
|
||||
);
|
||||
|
||||
self.initialized = true;
|
||||
}
|
||||
@@ -490,6 +492,26 @@ impl VirtualMachine {
|
||||
self.new_exception_msg(import_error, msg)
|
||||
}
|
||||
|
||||
// TODO: #[track_caller] when stabilized
|
||||
fn _py_panic_failed(&self, exc: &PyBaseExceptionRef, msg: &str) -> ! {
|
||||
let show_backtrace = env::var_os("RUST_BACKTRACE").map_or(false, |v| &v != "0");
|
||||
let after = if show_backtrace {
|
||||
exceptions::print_exception(self, exc);
|
||||
"exception backtrace above"
|
||||
} else {
|
||||
"run with RUST_BACKTRACE=1 to see Python backtrace"
|
||||
};
|
||||
panic!("{}; {}", msg, after)
|
||||
}
|
||||
pub fn unwrap_pyresult<T>(&self, result: PyResult<T>) -> T {
|
||||
result.unwrap_or_else(|exc| {
|
||||
self._py_panic_failed(&exc, "called `vm.unwrap_pyresult()` on an `Err` value")
|
||||
})
|
||||
}
|
||||
pub fn expect_pyresult<T>(&self, result: PyResult<T>, msg: &str) -> T {
|
||||
result.unwrap_or_else(|exc| self._py_panic_failed(&exc, msg))
|
||||
}
|
||||
|
||||
pub fn new_scope_with_builtins(&self) -> Scope {
|
||||
Scope::with_builtins(None, self.ctx.new_dict(), self)
|
||||
}
|
||||
@@ -618,23 +640,32 @@ impl VirtualMachine {
|
||||
objbool::boolval(self, ret)
|
||||
}
|
||||
|
||||
pub fn call_get_descriptor(&self, descr: PyObjectRef, obj: PyObjectRef) -> Option<PyResult> {
|
||||
pub fn call_get_descriptor_specific(
|
||||
&self,
|
||||
descr: PyObjectRef,
|
||||
obj: Option<PyObjectRef>,
|
||||
cls: Option<PyObjectRef>,
|
||||
) -> Option<PyResult> {
|
||||
let descr_class = descr.class();
|
||||
let slots = descr_class.slots.borrow();
|
||||
Some(if let Some(descr_get) = slots.borrow().descr_get.as_ref() {
|
||||
let cls = obj.class();
|
||||
descr_get(
|
||||
self,
|
||||
descr,
|
||||
Some(obj.clone()),
|
||||
OptionalArg::Present(cls.into_object()),
|
||||
)
|
||||
if let Some(descr_get) = slots.descr_get.as_ref() {
|
||||
Some(descr_get(self, descr, obj, OptionalArg::from_option(cls)))
|
||||
} else if let Some(ref descriptor) = descr_class.get_attr("__get__") {
|
||||
let cls = obj.class();
|
||||
self.invoke(descriptor, vec![descr, obj.clone(), cls.into_object()])
|
||||
Some(self.invoke(
|
||||
descriptor,
|
||||
vec![
|
||||
descr,
|
||||
obj.unwrap_or_else(|| self.get_none()),
|
||||
cls.unwrap_or_else(|| self.get_none()),
|
||||
],
|
||||
))
|
||||
} else {
|
||||
return None;
|
||||
})
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn call_get_descriptor(&self, descr: PyObjectRef, obj: PyObjectRef) -> Option<PyResult> {
|
||||
self.call_get_descriptor_specific(descr, Some(obj.clone()), Some(obj.class().into_object()))
|
||||
}
|
||||
|
||||
pub fn call_if_get_descriptor(&self, attr: PyObjectRef, obj: PyObjectRef) -> PyResult {
|
||||
@@ -853,8 +884,13 @@ impl VirtualMachine {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn generic_getattribute(&self, obj: PyObjectRef, name: PyStringRef) -> PyResult {
|
||||
self.generic_getattribute_opt(obj.clone(), name.clone())?
|
||||
.ok_or_else(|| self.new_attribute_error(format!("{} has no attribute '{}'", obj, name)))
|
||||
}
|
||||
|
||||
/// CPython _PyObject_GenericGetAttrWithDict
|
||||
pub fn generic_getattribute(
|
||||
pub fn generic_getattribute_opt(
|
||||
&self,
|
||||
obj: PyObjectRef,
|
||||
name_str: PyStringRef,
|
||||
|
||||
Reference in new issue
Block a user