Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Lib/test/test_ast/test_ast.py
Original file line number Diff line number Diff line change
Expand Up @@ -706,6 +706,8 @@ class S(str):
with assertNumDeprecated():
self.assertNotIsInstance(Constant(S("42")), Num)

# TODO: RUSTPYTHON; will be removed in Python 3.14
@unittest.expectedFailure
def test_constant_subclasses_deprecated(self):
with warnings.catch_warnings():
warnings.filterwarnings("ignore", "", DeprecationWarning)
Expand Down
12 changes: 0 additions & 12 deletions Lib/test/test_collections.py
Original file line number Diff line number Diff line change
Expand Up @@ -952,8 +952,6 @@ def __aiter__(self):
self.validate_abstract_methods(AsyncIterable, '__aiter__')
self.validate_isinstance(AsyncIterable, '__aiter__')

# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_AsyncIterator(self):
class AI:
def __aiter__(self):
Expand Down Expand Up @@ -1152,8 +1150,6 @@ class NonCol(ColImpl):
self.assertFalse(issubclass(NonCol, Collection))
self.assertFalse(isinstance(NonCol(), Collection))

# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_Iterator(self):
non_samples = [None, 42, 3.14, 1j, b"", "", (), [], {}, set()]
for x in non_samples:
Expand Down Expand Up @@ -1850,8 +1846,6 @@ def test_Set_hash_matches_frozenset(self):
fs = frozenset(s)
self.assertEqual(hash(fs), Set._hash(fs), msg=s)

# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_Mapping(self):
for sample in [dict]:
self.assertIsInstance(sample(), Mapping)
Expand All @@ -1868,8 +1862,6 @@ def __iter__(self):
self.validate_comparison(MyMapping())
self.assertRaises(TypeError, reversed, MyMapping())

# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_MutableMapping(self):
for sample in [dict]:
self.assertIsInstance(sample(), MutableMapping)
Expand Down Expand Up @@ -1904,8 +1896,6 @@ def test_MutableMapping_subclass(self):
mymap['blue'] = 7 # Shouldn't affect 'z'
self.assertEqual(z, {('orange', 3), ('red', 5)})

# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_Sequence(self):
for sample in [tuple, list, bytes, str]:
self.assertIsInstance(sample(), Sequence)
Expand Down Expand Up @@ -1988,8 +1978,6 @@ def test_Buffer(self):
self.assertFalse(issubclass(sample, Buffer))
self.validate_abstract_methods(Buffer, '__buffer__')

# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_MutableSequence(self):
for sample in [tuple, str, bytes]:
self.assertNotIsInstance(sample(), MutableSequence)
Expand Down
2 changes: 0 additions & 2 deletions Lib/test/test_exception_group.py
Original file line number Diff line number Diff line change
Expand Up @@ -315,8 +315,6 @@ def setUp(self):
self.eg = create_simple_eg()
self.eg_template = [ValueError(1), TypeError(int), ValueError(2)]

# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_basics_subgroup_split__bad_arg_type(self):
class C:
pass
Expand Down
4 changes: 0 additions & 4 deletions Lib/test/test_typing.py
Original file line number Diff line number Diff line change
Expand Up @@ -2948,8 +2948,6 @@ class E(C, BP): pass
self.assertNotIsInstance(D(), E)
self.assertNotIsInstance(E(), D)

# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_no_instantiation(self):
class P(Protocol): pass

Expand Down Expand Up @@ -4500,8 +4498,6 @@ def __init__(self, arg):
self.assertEqual(c.from_a, 'foo')
self.assertEqual(c.from_c, 'foo')

# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_new_no_args(self):

class A(Generic[T]):
Expand Down
37 changes: 36 additions & 1 deletion vm/src/builtins/object.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,42 @@ impl PyPayload for PyBaseObject {
impl Constructor for PyBaseObject {
type Args = FuncArgs;

fn py_new(cls: PyTypeRef, _args: Self::Args, vm: &VirtualMachine) -> PyResult {
// = object_new
fn py_new(cls: PyTypeRef, args: Self::Args, vm: &VirtualMachine) -> PyResult {
if !args.args.is_empty() || !args.kwargs.is_empty() {
// Check if type's __new__ != object.__new__
let tp_new = cls.get_attr(identifier!(vm, __new__));
let object_new = vm.ctx.types.object_type.get_attr(identifier!(vm, __new__));

if let (Some(tp_new), Some(object_new)) = (tp_new, object_new) {
if !tp_new.is(&object_new) {
// Type has its own __new__, so object.__new__ is being called
// with excess args. This is the first error case in CPython
return Err(vm.new_type_error(
"object.__new__() takes exactly one argument (the type to instantiate)"
.to_owned(),
));
}

// If we reach here, tp_new == object_new
// Now check if type's __init__ == object.__init__
let tp_init = cls.get_attr(identifier!(vm, __init__));
let object_init = vm.ctx.types.object_type.get_attr(identifier!(vm, __init__));

if let (Some(tp_init), Some(object_init)) = (tp_init, object_init) {
if tp_init.is(&object_init) {
// Both __new__ and __init__ are object's versions,
// so the type accepts no arguments
return Err(
vm.new_type_error(format!("{}() takes no arguments", cls.name()))
);
}
}
// If tp_init != object_init, then the type has custom __init__
// which might accept arguments, so we allow it
}
}

// more or less __new__ operator
let dict = if cls.is(vm.ctx.types.object_type) {
None
Expand Down
33 changes: 31 additions & 2 deletions vm/src/stdlib/ast/python.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,16 @@ use super::{PY_CF_OPTIMIZED_AST, PY_CF_TYPE_COMMENTS, PY_COMPILE_FLAG_AST_ONLY};
pub(crate) mod _ast {
use crate::{
AsObject, Context, PyObjectRef, PyPayload, PyResult, VirtualMachine,
builtins::{PyStrRef, PyTupleRef},
builtins::{PyStrRef, PyTupleRef, PyTypeRef},
function::FuncArgs,
types::Constructor,
};
#[pyattr]
#[pyclass(module = "_ast", name = "AST")]
#[derive(Debug, PyPayload)]
pub(crate) struct NodeAst;

#[pyclass(flags(BASETYPE, HAS_DICT))]
#[pyclass(with(Constructor), flags(BASETYPE, HAS_DICT))]
impl NodeAst {
#[pyslot]
#[pymethod]
Expand Down Expand Up @@ -52,6 +53,34 @@ pub(crate) mod _ast {
}
}

impl Constructor for NodeAst {
type Args = FuncArgs;

fn slot_new(cls: PyTypeRef, args: FuncArgs, vm: &VirtualMachine) -> PyResult {
// AST nodes accept extra arguments (unlike object.__new__)
// This matches CPython's behavior where AST has its own tp_new
let dict = if cls
.slots
.flags
.contains(crate::types::PyTypeFlags::HAS_DICT)
{
Some(vm.ctx.new_dict())
} else {
None
};
let zelf = vm.ctx.new_base_object(cls, dict);

// Initialize the instance with the provided arguments
NodeAst::__init__(zelf.clone(), args, vm)?;

Ok(zelf)
}

fn py_new(_cls: PyTypeRef, _args: Self::Args, _vm: &VirtualMachine) -> PyResult {
unreachable!("slow_new is implemented");
}
}

#[pyattr(name = "PyCF_ONLY_AST")]
use super::PY_COMPILE_FLAG_AST_ONLY;

Expand Down
53 changes: 47 additions & 6 deletions vm/src/stdlib/typing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ pub(crate) fn make_module(vm: &VirtualMachine) -> PyRef<PyModule> {
#[pymodule(name = "_typing")]
pub(crate) mod decl {
use crate::{
PyObjectRef, PyPayload, PyResult, VirtualMachine,
Py, PyObjectRef, PyPayload, PyResult, VirtualMachine,
builtins::{PyTupleRef, PyTypeRef, pystr::AsPyStr},
function::{FuncArgs, IntoFuncArgs},
types::{Constructor, Representable},
Expand Down Expand Up @@ -88,7 +88,7 @@ pub(crate) mod decl {

impl Representable for NoDefault {
#[inline(always)]
fn repr_str(_zelf: &crate::Py<Self>, _vm: &VirtualMachine) -> PyResult<String> {
fn repr_str(_zelf: &Py<Self>, _vm: &VirtualMachine) -> PyResult<String> {
Ok("typing.NoDefault".to_owned())
}
}
Expand All @@ -104,7 +104,7 @@ pub(crate) mod decl {
// compute_value: PyObjectRef,
// module: PyObjectRef,
}
#[pyclass(flags(BASETYPE))]
#[pyclass(with(Constructor, Representable), flags(BASETYPE))]
impl TypeAliasType {
pub const fn new(name: PyObjectRef, type_params: PyTupleRef, value: PyObjectRef) -> Self {
Self {
Expand All @@ -128,10 +128,51 @@ pub(crate) mod decl {
fn __type_params__(&self) -> PyTupleRef {
self.type_params.clone()
}
}

impl Constructor for TypeAliasType {
type Args = FuncArgs;

fn py_new(cls: PyTypeRef, args: Self::Args, vm: &VirtualMachine) -> PyResult {
// TypeAliasType(name, value, *, type_params=None)
if args.args.len() < 2 {
return Err(vm.new_type_error(format!(
"TypeAliasType() missing {} required positional argument{}: {}",
2 - args.args.len(),
if 2 - args.args.len() == 1 { "" } else { "s" },
if args.args.is_empty() {
"'name' and 'value'"
} else {
"'value'"
}
)));
}
if args.args.len() > 2 {
return Err(vm.new_type_error(format!(
"TypeAliasType() takes 2 positional arguments but {} were given",
args.args.len()
)));
}

let name = args.args[0].clone();
let value = args.args[1].clone();

let type_params = if let Some(tp) = args.kwargs.get("type_params") {
tp.clone()
.downcast::<crate::builtins::PyTuple>()
.map_err(|_| vm.new_type_error("type_params must be a tuple".to_owned()))?
} else {
vm.ctx.empty_tuple.clone()
};

let ta = TypeAliasType::new(name, type_params, value);
ta.into_ref_with_type(vm, cls).map(Into::into)
}
}

#[pymethod(name = "__repr__")]
fn repr(&self, vm: &VirtualMachine) -> PyResult<String> {
let name = self.name.str(vm)?;
impl Representable for TypeAliasType {
fn repr_str(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<String> {
let name = zelf.name.str(vm)?;
Ok(name.as_str().to_owned())
}
}
Expand Down