Merge pull request #1766 from RustPython/coolreader18/stdlib-typing

Add the typing standard library module, rework tp_new
This commit is contained in:
Noah authored and GitHub committed 2020-02-28 22:50:07 -06:00
commit 7be93a96da
15 files changed
+2702 -194

No files matched your search

-12
View File
@@ -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
+2414
View File
File diff suppressed because it is too large. Load diff
+3 -1
View File
@@ -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:
+15 -6
View File
@@ -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
View File
@@ -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)
+15
View File
@@ -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] {
+1
View File
@@ -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)) => {
+2 -2
View File
@@ -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
View File
@@ -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 {
+4
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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()));
}
+2 -2
View File
@@ -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
View File
@@ -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,