disallow __new__, __init__ (#6446)

* disallow __new__, __init__

* migrate to Initializer

* apply review
This commit is contained in:
Jeong, YunWon
2025-12-22 21:10:43 +09:00
committed by GitHub
parent 569bee103f
commit fdd2ac3b30
7 changed files with 459 additions and 370 deletions

View File

@@ -63,6 +63,7 @@ impl FromStr for AttrName {
#[derive(Default)]
struct ImplContext {
is_trait: bool,
attribute_items: ItemNursery,
method_items: MethodNursery,
getset_items: GetSetNursery,
@@ -232,7 +233,10 @@ pub(crate) fn impl_pyclass_impl(attr: PunctuatedNestedMeta, item: Item) -> Resul
}
}
Item::Trait(mut trai) => {
let mut context = ImplContext::default();
let mut context = ImplContext {
is_trait: true,
..Default::default()
};
let mut has_extend_slots = false;
for item in &trai.items {
let has = match item {
@@ -710,21 +714,16 @@ pub(crate) fn impl_pyexception_impl(attr: PunctuatedNestedMeta, item: Item) -> R
};
// Check if with(Constructor) is specified. If Constructor trait is used, don't generate slot_new
let mut has_slot_new = false;
let mut extra_attrs = Vec::new();
let mut with_items = vec![];
for nested in &attr {
if let NestedMeta::Meta(Meta::List(MetaList { path, nested, .. })) = nested {
// If we already found the constructor trait, no need to keep looking for it
if !has_slot_new && path.is_ident("with") {
// Check if Constructor is in the list
if path.is_ident("with") {
for meta in nested {
if let NestedMeta::Meta(Meta::Path(p)) = meta
&& p.is_ident("Constructor")
{
has_slot_new = true;
}
with_items.push(meta.get_ident().expect("with() has non-ident item").clone());
}
continue;
}
extra_attrs.push(NestedMeta::Meta(Meta::List(MetaList {
path: path.clone(),
@@ -734,43 +733,40 @@ pub(crate) fn impl_pyexception_impl(attr: PunctuatedNestedMeta, item: Item) -> R
}
}
let mut has_slot_init = false;
let with_contains = |with_items: &[Ident], s: &str| {
// Check if Constructor is in the list
with_items.iter().any(|ident| ident == s)
};
let syn::ItemImpl {
generics,
self_ty,
items,
..
} = &imp;
for item in items {
// FIXME: better detection or correct wrapper implementation
let Some(ident) = item.get_ident() else {
continue;
};
let item_name = ident.to_string();
match item_name.as_str() {
"slot_new" => {
has_slot_new = true;
}
"slot_init" => {
has_slot_init = true;
}
_ => continue,
}
}
// TODO: slot_new, slot_init must be Constructor or Initializer later
let slot_new = if has_slot_new {
let slot_new = if with_contains(&with_items, "Constructor") {
quote!()
} else {
with_items.push(Ident::new("Constructor", Span::call_site()));
quote! {
#[pyslot]
pub fn slot_new(
cls: ::rustpython_vm::builtins::PyTypeRef,
args: ::rustpython_vm::function::FuncArgs,
vm: &::rustpython_vm::VirtualMachine,
) -> ::rustpython_vm::PyResult {
<Self as ::rustpython_vm::class::PyClassDef>::Base::slot_new(cls, args, vm)
impl ::rustpython_vm::types::Constructor for #self_ty {
type Args = ::rustpython_vm::function::FuncArgs;
fn slot_new(
cls: ::rustpython_vm::builtins::PyTypeRef,
args: ::rustpython_vm::function::FuncArgs,
vm: &::rustpython_vm::VirtualMachine,
) -> ::rustpython_vm::PyResult {
<Self as ::rustpython_vm::class::PyClassDef>::Base::slot_new(cls, args, vm)
}
fn py_new(
_cls: &::rustpython_vm::Py<::rustpython_vm::builtins::PyType>,
_args: Self::Args,
_vm: &::rustpython_vm::VirtualMachine
) -> ::rustpython_vm::PyResult<Self> {
unreachable!("slot_new is defined")
}
}
}
};
@@ -779,19 +775,29 @@ pub(crate) fn impl_pyexception_impl(attr: PunctuatedNestedMeta, item: Item) -> R
// from `BaseException` in `SimpleExtendsException` macro.
// See: `(initproc)BaseException_init`
// spell-checker:ignore initproc
let slot_init = if has_slot_init {
let slot_init = if with_contains(&with_items, "Initializer") {
quote!()
} else {
// FIXME: this is a generic logic for types not only for exceptions
with_items.push(Ident::new("Initializer", Span::call_site()));
quote! {
#[pyslot]
#[pymethod(name="__init__")]
pub fn slot_init(
zelf: ::rustpython_vm::PyObjectRef,
args: ::rustpython_vm::function::FuncArgs,
vm: &::rustpython_vm::VirtualMachine,
) -> ::rustpython_vm::PyResult<()> {
<Self as ::rustpython_vm::class::PyClassDef>::Base::slot_init(zelf, args, vm)
impl ::rustpython_vm::types::Initializer for #self_ty {
type Args = ::rustpython_vm::function::FuncArgs;
fn slot_init(
zelf: ::rustpython_vm::PyObjectRef,
args: ::rustpython_vm::function::FuncArgs,
vm: &::rustpython_vm::VirtualMachine,
) -> ::rustpython_vm::PyResult<()> {
<Self as ::rustpython_vm::class::PyClassDef>::Base::slot_init(zelf, args, vm)
}
fn init(
_zelf: ::rustpython_vm::PyRef<Self>,
_args: Self::Args,
_vm: &::rustpython_vm::VirtualMachine
) -> ::rustpython_vm::PyResult<()> {
unreachable!("slot_init is defined")
}
}
}
};
@@ -803,13 +809,13 @@ pub(crate) fn impl_pyexception_impl(attr: PunctuatedNestedMeta, item: Item) -> R
};
Ok(quote! {
#[pyclass(flags(BASETYPE, HAS_DICT) #extra_attrs_tokens)]
#[pyclass(flags(BASETYPE, HAS_DICT), with(#(#with_items),*) #extra_attrs_tokens)]
impl #generics #self_ty {
#(#items)*
#slot_new
#slot_init
}
#slot_new
#slot_init
})
}
@@ -892,6 +898,23 @@ where
let item_meta = MethodItemMeta::from_attr(ident.clone(), &item_attr)?;
let py_name = item_meta.method_name()?;
// Disallow __new__ and __init__ as pymethod in impl blocks (not in traits)
if !args.context.is_trait {
if py_name == "__new__" {
return Err(syn::Error::new(
ident.span(),
"#[pymethod] cannot define '__new__'. Use #[pyclass(with(Constructor))] instead.",
));
}
if py_name == "__init__" {
return Err(syn::Error::new(
ident.span(),
"#[pymethod] cannot define '__init__'. Use #[pyclass(with(Initializer))] instead.",
));
}
}
let raw = item_meta.raw()?;
let sig_doc = text_signature(func.sig(), &py_name);

View File

@@ -1540,7 +1540,7 @@ mod _sqlite {
size: Option<c_int>,
}
#[pyclass(with(Constructor, IterNext, Iterable), flags(BASETYPE))]
#[pyclass(with(Constructor, Initializer, IterNext, Iterable), flags(BASETYPE))]
impl Cursor {
fn new(
connection: PyRef<Connection>,
@@ -1571,24 +1571,6 @@ mod _sqlite {
}
}
#[pymethod]
fn __init__(&self, _connection: PyRef<Connection>, _vm: &VirtualMachine) -> PyResult<()> {
let mut guard = self.inner.lock();
if guard.is_some() {
// Already initialized (e.g., from a call to super().__init__)
return Ok(());
}
*guard = Some(CursorInner {
description: None,
row_cast_map: vec![],
lastrowid: -1,
rowcount: -1,
statement: None,
closed: false,
});
Ok(())
}
fn check_cursor_state(inner: Option<&CursorInner>, vm: &VirtualMachine) -> PyResult<()> {
match inner {
Some(inner) if inner.closed => Err(new_programming_error(
@@ -1949,6 +1931,27 @@ mod _sqlite {
}
}
impl Initializer for Cursor {
type Args = PyRef<Connection>;
fn init(zelf: PyRef<Self>, _connection: Self::Args, _vm: &VirtualMachine) -> PyResult<()> {
let mut guard = zelf.inner.lock();
if guard.is_some() {
// Already initialized (e.g., from a call to super().__init__)
return Ok(());
}
*guard = Some(CursorInner {
description: None,
row_cast_map: vec![],
lastrowid: -1,
rowcount: -1,
statement: None,
closed: false,
});
Ok(())
}
}
impl SelfIter for Cursor {}
impl IterNext for Cursor {
fn next(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<PyIterReturn> {

View File

@@ -7,7 +7,7 @@ pub(crate) mod ssl_error {
use crate::vm::{
PyPayload, PyRef, PyResult, VirtualMachine,
builtins::{PyBaseExceptionRef, PyOSError, PyStrRef},
types::Constructor,
types::{Constructor, Initializer},
};
// Error type constants - exposed as pyattr and available for internal use

View File

@@ -2,11 +2,11 @@ use super::{PyDictRef, PyList, PyStr, PyStrRef, PyType, PyTypeRef};
use crate::common::hash::PyHash;
use crate::types::PyTypeFlags;
use crate::{
AsObject, Context, Py, PyObject, PyObjectRef, PyPayload, PyResult, VirtualMachine,
AsObject, Context, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine,
class::PyClassImpl,
convert::ToPyResult,
function::{Either, FuncArgs, PyArithmeticValue, PyComparisonValue, PySetterValue},
types::{Constructor, PyComparisonOp},
types::{Constructor, Initializer, PyComparisonOp},
};
use itertools::Itertools;
@@ -115,6 +115,18 @@ impl Constructor for PyBaseObject {
}
}
impl Initializer for PyBaseObject {
type Args = FuncArgs;
fn slot_init(_zelf: PyObjectRef, _args: FuncArgs, _vm: &VirtualMachine) -> PyResult<()> {
Ok(())
}
fn init(_zelf: PyRef<Self>, _args: Self::Args, _vm: &VirtualMachine) -> PyResult<()> {
unreachable!("slot_init is defined")
}
}
// TODO: implement _PyType_GetSlotNames properly
fn type_slot_names(typ: &Py<PyType>, vm: &VirtualMachine) -> PyResult<Option<super::PyListRef>> {
// let attributes = typ.attributes.read();
@@ -235,7 +247,7 @@ fn object_getstate_default(obj: &PyObject, required: bool, vm: &VirtualMachine)
// getstate.call((), vm)
// }
#[pyclass(with(Constructor), flags(BASETYPE))]
#[pyclass(with(Constructor, Initializer), flags(BASETYPE))]
impl PyBaseObject {
#[pymethod(raw)]
fn __getstate__(vm: &VirtualMachine, args: FuncArgs) -> PyResult {
@@ -444,19 +456,17 @@ impl PyBaseObject {
obj.str(vm)
}
#[pyslot]
#[pymethod]
fn __init__(_zelf: PyObjectRef, _args: FuncArgs, _vm: &VirtualMachine) -> PyResult<()> {
Ok(())
}
#[pygetset]
fn __class__(obj: PyObjectRef) -> PyTypeRef {
obj.class().to_owned()
}
#[pygetset(name = "__class__", setter)]
fn set_class(instance: PyObjectRef, value: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> {
#[pygetset(setter)]
fn set___class__(
instance: PyObjectRef,
value: PyObjectRef,
vm: &VirtualMachine,
) -> PyResult<()> {
match value.downcast::<PyType>() {
Ok(cls) => {
let both_module = instance.class().fast_issubclass(vm.ctx.types.module_type)

View File

@@ -43,13 +43,14 @@ pub(super) mod types {
use super::*;
use crate::PyPayload;
use crate::builtins::PyGenericAlias;
use crate::types::{Constructor, Initializer};
#[pyexception(name, base = PyBaseException, ctx = "base_exception_group")]
#[derive(Debug)]
#[repr(transparent)]
pub struct PyBaseExceptionGroup(PyBaseException);
#[pyexception]
#[pyexception(with(Constructor, Initializer))]
impl PyBaseExceptionGroup {
#[pyclassmethod]
fn __class_getitem__(
@@ -60,117 +61,6 @@ pub(super) mod types {
PyGenericAlias::from_args(cls, args, vm)
}
#[pyslot]
fn slot_new(cls: PyTypeRef, args: FuncArgs, vm: &VirtualMachine) -> PyResult {
// Validate exactly 2 positional arguments
if args.args.len() != 2 {
return Err(vm.new_type_error(format!(
"BaseExceptionGroup.__new__() takes exactly 2 positional arguments ({} given)",
args.args.len()
)));
}
// Validate message is str
let message = args.args[0].clone();
if !message.fast_isinstance(vm.ctx.types.str_type) {
return Err(vm.new_type_error(format!(
"argument 1 must be str, not {}",
message.class().name()
)));
}
// Validate exceptions is a sequence (not set or None)
let exceptions_arg = &args.args[1];
// Check for set/frozenset (not a sequence - unordered)
if exceptions_arg.fast_isinstance(vm.ctx.types.set_type)
|| exceptions_arg.fast_isinstance(vm.ctx.types.frozenset_type)
{
return Err(vm.new_type_error("second argument (exceptions) must be a sequence"));
}
// Check for None
if exceptions_arg.is(&vm.ctx.none) {
return Err(vm.new_type_error("second argument (exceptions) must be a sequence"));
}
let exceptions: Vec<PyObjectRef> = exceptions_arg.try_to_value(vm).map_err(|_| {
vm.new_type_error("second argument (exceptions) must be a sequence")
})?;
// Validate non-empty
if exceptions.is_empty() {
return Err(vm.new_value_error(
"second argument (exceptions) must be a non-empty sequence".to_owned(),
));
}
// Validate all items are BaseException instances
let mut has_non_exception = false;
for (i, exc) in exceptions.iter().enumerate() {
if !exc.fast_isinstance(vm.ctx.exceptions.base_exception_type) {
return Err(vm.new_value_error(format!(
"Item {} of second argument (exceptions) is not an exception",
i
)));
}
// Check if any exception is not an Exception subclass
// With dynamic ExceptionGroup (inherits from both BaseExceptionGroup and Exception),
// ExceptionGroup instances are automatically instances of Exception
if !exc.fast_isinstance(vm.ctx.exceptions.exception_type) {
has_non_exception = true;
}
}
// Get the dynamic ExceptionGroup type
let exception_group_type = crate::exception_group::exception_group();
// Determine the actual class to use
let actual_cls = if cls.is(exception_group_type) {
// ExceptionGroup cannot contain BaseExceptions that are not Exception
if has_non_exception {
return Err(
vm.new_type_error("Cannot nest BaseExceptions in an ExceptionGroup")
);
}
cls
} else if cls.is(vm.ctx.exceptions.base_exception_group) {
// Auto-convert to ExceptionGroup if all are Exception subclasses
if !has_non_exception {
exception_group_type.to_owned()
} else {
cls
}
} else {
// User-defined subclass
if has_non_exception && cls.fast_issubclass(vm.ctx.exceptions.exception_type) {
return Err(vm.new_type_error(format!(
"Cannot nest BaseExceptions in '{}'",
cls.name()
)));
}
cls
};
// Create the exception with (message, exceptions_tuple) as args
let exceptions_tuple = vm.ctx.new_tuple(exceptions);
let init_args = vec![message, exceptions_tuple.into()];
PyBaseException::new(init_args, vm)
.into_ref_with_type(vm, actual_cls)
.map(Into::into)
}
#[pyslot]
#[pymethod(name = "__init__")]
fn slot_init(_zelf: PyObjectRef, _args: FuncArgs, _vm: &VirtualMachine) -> PyResult<()> {
// CPython's BaseExceptionGroup.__init__ just calls BaseException.__init__
// which stores args as-is. Since __new__ already set up the correct args
// (message, exceptions_tuple), we don't need to do anything here.
// This also allows subclasses to pass extra arguments to __new__ without
// __init__ complaining about argument count.
Ok(())
}
#[pymethod]
fn derive(
zelf: PyRef<PyBaseException>,
@@ -351,6 +241,136 @@ pub(super) mod types {
}
}
impl Constructor for PyBaseExceptionGroup {
type Args = crate::function::PosArgs;
fn slot_new(cls: PyTypeRef, args: FuncArgs, vm: &VirtualMachine) -> PyResult {
let args: Self::Args = args.bind(vm)?;
let args = args.into_vec();
// Validate exactly 2 positional arguments
if args.len() != 2 {
return Err(vm.new_type_error(format!(
"BaseExceptionGroup.__new__() takes exactly 2 positional arguments ({} given)",
args.len()
)));
}
// Validate message is str
let message = args[0].clone();
if !message.fast_isinstance(vm.ctx.types.str_type) {
return Err(vm.new_type_error(format!(
"argument 1 must be str, not {}",
message.class().name()
)));
}
// Validate exceptions is a sequence (not set or None)
let exceptions_arg = &args[1];
// Check for set/frozenset (not a sequence - unordered)
if exceptions_arg.fast_isinstance(vm.ctx.types.set_type)
|| exceptions_arg.fast_isinstance(vm.ctx.types.frozenset_type)
{
return Err(vm.new_type_error("second argument (exceptions) must be a sequence"));
}
// Check for None
if exceptions_arg.is(&vm.ctx.none) {
return Err(vm.new_type_error("second argument (exceptions) must be a sequence"));
}
let exceptions: Vec<PyObjectRef> = exceptions_arg.try_to_value(vm).map_err(|_| {
vm.new_type_error("second argument (exceptions) must be a sequence")
})?;
// Validate non-empty
if exceptions.is_empty() {
return Err(vm.new_value_error(
"second argument (exceptions) must be a non-empty sequence".to_owned(),
));
}
// Validate all items are BaseException instances
let mut has_non_exception = false;
for (i, exc) in exceptions.iter().enumerate() {
if !exc.fast_isinstance(vm.ctx.exceptions.base_exception_type) {
return Err(vm.new_value_error(format!(
"Item {} of second argument (exceptions) is not an exception",
i
)));
}
// Check if any exception is not an Exception subclass
// With dynamic ExceptionGroup (inherits from both BaseExceptionGroup and Exception),
// ExceptionGroup instances are automatically instances of Exception
if !exc.fast_isinstance(vm.ctx.exceptions.exception_type) {
has_non_exception = true;
}
}
// Get the dynamic ExceptionGroup type
let exception_group_type = crate::exception_group::exception_group();
// Determine the actual class to use
let actual_cls = if cls.is(exception_group_type) {
// ExceptionGroup cannot contain BaseExceptions that are not Exception
if has_non_exception {
return Err(
vm.new_type_error("Cannot nest BaseExceptions in an ExceptionGroup")
);
}
cls
} else if cls.is(vm.ctx.exceptions.base_exception_group) {
// Auto-convert to ExceptionGroup if all are Exception subclasses
if !has_non_exception {
exception_group_type.to_owned()
} else {
cls
}
} else {
// User-defined subclass
if has_non_exception && cls.fast_issubclass(vm.ctx.exceptions.exception_type) {
return Err(vm.new_type_error(format!(
"Cannot nest BaseExceptions in '{}'",
cls.name()
)));
}
cls
};
// Create the exception with (message, exceptions_tuple) as args
let exceptions_tuple = vm.ctx.new_tuple(exceptions);
let init_args = vec![message, exceptions_tuple.into()];
PyBaseException::new(init_args, vm)
.into_ref_with_type(vm, actual_cls)
.map(Into::into)
}
fn py_new(_cls: &Py<PyType>, _args: Self::Args, _vm: &VirtualMachine) -> PyResult<Self> {
unimplemented!("use slot_new")
}
}
impl Initializer for PyBaseExceptionGroup {
type Args = FuncArgs;
fn slot_init(
_zelf: PyObjectRef,
_args: ::rustpython_vm::function::FuncArgs,
_vm: &::rustpython_vm::VirtualMachine,
) -> ::rustpython_vm::PyResult<()> {
// CPython's BaseExceptionGroup.__init__ just calls BaseException.__init__
// which stores args as-is. Since __new__ already set up the correct args
// (message, exceptions_tuple), we don't need to do anything here.
// This also allows subclasses to pass extra arguments to __new__ without
// __init__ complaining about argument count.
Ok(())
}
fn init(_zelf: PyRef<Self>, _args: Self::Args, _vm: &VirtualMachine) -> PyResult<()> {
unreachable!("slot_init is defined")
}
}
// Helper functions for ExceptionGroup
fn is_base_exception_group(obj: &PyObject, vm: &VirtualMachine) -> bool {
obj.fast_isinstance(vm.ctx.exceptions.base_exception_group)

View File

@@ -1371,18 +1371,19 @@ pub(super) mod types {
#[repr(transparent)]
pub struct PyStopIteration(PyException);
#[pyexception]
impl PyStopIteration {
#[pyslot]
#[pymethod(name = "__init__")]
pub(crate) fn slot_init(
zelf: PyObjectRef,
args: ::rustpython_vm::function::FuncArgs,
vm: &::rustpython_vm::VirtualMachine,
) -> ::rustpython_vm::PyResult<()> {
#[pyexception(with(Initializer))]
impl PyStopIteration {}
impl Initializer for PyStopIteration {
type Args = FuncArgs;
fn slot_init(zelf: PyObjectRef, args: FuncArgs, vm: &VirtualMachine) -> PyResult<()> {
zelf.set_attr("value", vm.unwrap_or_none(args.args.first().cloned()), vm)?;
Ok(())
}
fn init(_zelf: PyRef<Self>, _args: Self::Args, _vm: &VirtualMachine) -> PyResult<()> {
unreachable!("slot_init is defined")
}
}
#[pyexception(name, base = PyException, ctx = "stop_async_iteration", impl)]
@@ -1419,15 +1420,13 @@ pub(super) mod types {
#[repr(transparent)]
pub struct PyAttributeError(PyException);
#[pyexception]
impl PyAttributeError {
#[pyslot]
#[pymethod(name = "__init__")]
pub(crate) fn slot_init(
zelf: PyObjectRef,
args: ::rustpython_vm::function::FuncArgs,
vm: &::rustpython_vm::VirtualMachine,
) -> ::rustpython_vm::PyResult<()> {
#[pyexception(with(Initializer))]
impl PyAttributeError {}
impl Initializer for PyAttributeError {
type Args = FuncArgs;
fn slot_init(zelf: PyObjectRef, args: FuncArgs, vm: &VirtualMachine) -> PyResult<()> {
zelf.set_attr(
"name",
vm.unwrap_or_none(args.kwargs.get("name").cloned()),
@@ -1440,6 +1439,10 @@ pub(super) mod types {
)?;
Ok(())
}
fn init(_zelf: PyRef<Self>, _args: Self::Args, _vm: &VirtualMachine) -> PyResult<()> {
unreachable!("slot_init is defined")
}
}
#[pyexception(name, base = PyException, ctx = "buffer_error", impl)]
@@ -1457,15 +1460,28 @@ pub(super) mod types {
#[repr(transparent)]
pub struct PyImportError(PyException);
#[pyexception]
#[pyexception(with(Initializer))]
impl PyImportError {
#[pyslot]
#[pymethod(name = "__init__")]
pub(crate) fn slot_init(
zelf: PyObjectRef,
args: ::rustpython_vm::function::FuncArgs,
vm: &::rustpython_vm::VirtualMachine,
) -> ::rustpython_vm::PyResult<()> {
#[pymethod]
fn __reduce__(exc: PyBaseExceptionRef, vm: &VirtualMachine) -> PyTupleRef {
let obj = exc.as_object().to_owned();
let mut result: Vec<PyObjectRef> = vec![
obj.class().to_owned().into(),
vm.new_tuple((exc.get_arg(0).unwrap(),)).into(),
];
if let Some(dict) = obj.dict().filter(|x| !x.is_empty()) {
result.push(dict.into());
}
result.into_pytuple(vm)
}
}
impl Initializer for PyImportError {
type Args = FuncArgs;
fn slot_init(zelf: PyObjectRef, args: FuncArgs, vm: &VirtualMachine) -> PyResult<()> {
let mut kwargs = args.kwargs.clone();
let name = kwargs.swap_remove("name");
let path = kwargs.swap_remove("path");
@@ -1482,19 +1498,9 @@ pub(super) mod types {
dict.set_item("path", vm.unwrap_or_none(path), vm)?;
PyBaseException::slot_init(zelf, args, vm)
}
#[pymethod]
fn __reduce__(exc: PyBaseExceptionRef, vm: &VirtualMachine) -> PyTupleRef {
let obj = exc.as_object().to_owned();
let mut result: Vec<PyObjectRef> = vec![
obj.class().to_owned().into(),
vm.new_tuple((exc.get_arg(0).unwrap(),)).into(),
];
if let Some(dict) = obj.dict().filter(|x| !x.is_empty()) {
result.push(dict.into());
}
result.into_pytuple(vm)
fn init(_zelf: PyRef<Self>, _args: Self::Args, _vm: &VirtualMachine) -> PyResult<()> {
unreachable!("slot_init is defined")
}
}
@@ -1660,11 +1666,10 @@ pub(super) mod types {
}
}
#[pyexception(with(Constructor))]
impl PyOSError {
#[pyslot]
#[pymethod(name = "__init__")]
pub fn slot_init(zelf: PyObjectRef, args: FuncArgs, vm: &VirtualMachine) -> PyResult<()> {
impl Initializer for PyOSError {
type Args = FuncArgs;
fn slot_init(zelf: PyObjectRef, args: FuncArgs, vm: &VirtualMachine) -> PyResult<()> {
let len = args.args.len();
let mut new_args = args;
@@ -1718,6 +1723,13 @@ pub(super) mod types {
PyBaseException::slot_init(zelf, new_args, vm)
}
fn init(_zelf: PyRef<Self>, _args: Self::Args, _vm: &VirtualMachine) -> PyResult<()> {
unreachable!("slot_init is defined")
}
}
#[pyexception(with(Constructor, Initializer))]
impl PyOSError {
#[pymethod]
fn __str__(exc: PyBaseExceptionRef, vm: &VirtualMachine) -> PyResult<PyStrRef> {
let obj = exc.as_object().to_owned();
@@ -2011,44 +2023,8 @@ pub(super) mod types {
#[repr(transparent)]
pub struct PySyntaxError(PyException);
#[pyexception]
#[pyexception(with(Initializer))]
impl PySyntaxError {
#[pyslot]
#[pymethod(name = "__init__")]
fn slot_init(zelf: PyObjectRef, args: FuncArgs, vm: &VirtualMachine) -> PyResult<()> {
let len = args.args.len();
let new_args = args;
zelf.set_attr("print_file_and_line", vm.ctx.none(), vm)?;
if len == 2
&& let Ok(location_tuple) = new_args.args[1]
.clone()
.downcast::<crate::builtins::PyTuple>()
{
let location_tup_len = location_tuple.len();
for (i, &attr) in [
"filename",
"lineno",
"offset",
"text",
"end_lineno",
"end_offset",
]
.iter()
.enumerate()
{
if location_tup_len > i {
zelf.set_attr(attr, location_tuple[i].to_owned(), vm)?;
} else {
break;
}
}
}
PyBaseException::slot_init(zelf, new_args, vm)
}
#[pymethod]
fn __str__(exc: PyBaseExceptionRef, vm: &VirtualMachine) -> PyStrRef {
fn basename(filename: &str) -> &str {
@@ -2097,6 +2073,48 @@ pub(super) mod types {
}
}
impl Initializer for PySyntaxError {
type Args = FuncArgs;
fn slot_init(zelf: PyObjectRef, args: FuncArgs, vm: &VirtualMachine) -> PyResult<()> {
let len = args.args.len();
let new_args = args;
zelf.set_attr("print_file_and_line", vm.ctx.none(), vm)?;
if len == 2
&& let Ok(location_tuple) = new_args.args[1]
.clone()
.downcast::<crate::builtins::PyTuple>()
{
let location_tup_len = location_tuple.len();
for (i, &attr) in [
"filename",
"lineno",
"offset",
"text",
"end_lineno",
"end_offset",
]
.iter()
.enumerate()
{
if location_tup_len > i {
zelf.set_attr(attr, location_tuple[i].to_owned(), vm)?;
} else {
break;
}
}
}
PyBaseException::slot_init(zelf, new_args, vm)
}
fn init(_zelf: PyRef<Self>, _args: Self::Args, _vm: &VirtualMachine) -> PyResult<()> {
unreachable!("slot_init is defined")
}
}
#[pyexception(
name = "_IncompleteInputError",
base = PySyntaxError,
@@ -2106,17 +2124,19 @@ pub(super) mod types {
#[repr(transparent)]
pub struct PyIncompleteInputError(PySyntaxError);
#[pyexception]
impl PyIncompleteInputError {
#[pyslot]
#[pymethod(name = "__init__")]
pub(crate) fn slot_init(
zelf: PyObjectRef,
_args: FuncArgs,
vm: &VirtualMachine,
) -> PyResult<()> {
#[pyexception(with(Initializer))]
impl PyIncompleteInputError {}
impl Initializer for PyIncompleteInputError {
type Args = FuncArgs;
fn slot_init(zelf: PyObjectRef, args: FuncArgs, vm: &VirtualMachine) -> PyResult<()> {
zelf.set_attr("name", vm.ctx.new_str("SyntaxError"), vm)?;
Ok(())
PySyntaxError::slot_init(zelf, args, vm)
}
fn init(_zelf: PyRef<Self>, _args: Self::Args, _vm: &VirtualMachine) -> PyResult<()> {
unreachable!("slot_init is defined")
}
}
@@ -2155,26 +2175,8 @@ pub(super) mod types {
#[repr(transparent)]
pub struct PyUnicodeDecodeError(PyUnicodeError);
#[pyexception]
#[pyexception(with(Initializer))]
impl PyUnicodeDecodeError {
#[pyslot]
#[pymethod(name = "__init__")]
pub(crate) fn slot_init(
zelf: PyObjectRef,
args: FuncArgs,
vm: &VirtualMachine,
) -> PyResult<()> {
type Args = (PyStrRef, ArgBytesLike, isize, isize, PyStrRef);
let (encoding, object, start, end, reason): Args = args.bind(vm)?;
zelf.set_attr("encoding", encoding, vm)?;
let object_as_bytes = vm.ctx.new_bytes(object.borrow_buf().to_vec());
zelf.set_attr("object", object_as_bytes, vm)?;
zelf.set_attr("start", vm.ctx.new_int(start), vm)?;
zelf.set_attr("end", vm.ctx.new_int(end), vm)?;
zelf.set_attr("reason", reason, vm)?;
Ok(())
}
#[pymethod]
fn __str__(exc: PyBaseExceptionRef, vm: &VirtualMachine) -> PyResult<String> {
let Ok(object) = exc.as_object().get_attr("object", vm) else {
@@ -2202,30 +2204,33 @@ pub(super) mod types {
}
}
#[pyexception(name, base = PyUnicodeError, ctx = "unicode_encode_error")]
#[derive(Debug)]
#[repr(transparent)]
pub struct PyUnicodeEncodeError(PyUnicodeError);
impl Initializer for PyUnicodeDecodeError {
type Args = FuncArgs;
#[pyexception]
impl PyUnicodeEncodeError {
#[pyslot]
#[pymethod(name = "__init__")]
pub(crate) fn slot_init(
zelf: PyObjectRef,
args: FuncArgs,
vm: &VirtualMachine,
) -> PyResult<()> {
type Args = (PyStrRef, PyStrRef, isize, isize, PyStrRef);
fn slot_init(zelf: PyObjectRef, args: FuncArgs, vm: &VirtualMachine) -> PyResult<()> {
type Args = (PyStrRef, ArgBytesLike, isize, isize, PyStrRef);
let (encoding, object, start, end, reason): Args = args.bind(vm)?;
zelf.set_attr("encoding", encoding, vm)?;
zelf.set_attr("object", object, vm)?;
let object_as_bytes = vm.ctx.new_bytes(object.borrow_buf().to_vec());
zelf.set_attr("object", object_as_bytes, vm)?;
zelf.set_attr("start", vm.ctx.new_int(start), vm)?;
zelf.set_attr("end", vm.ctx.new_int(end), vm)?;
zelf.set_attr("reason", reason, vm)?;
Ok(())
}
fn init(_zelf: PyRef<Self>, _args: Self::Args, _vm: &VirtualMachine) -> PyResult<()> {
unreachable!("slot_init is defined")
}
}
#[pyexception(name, base = PyUnicodeError, ctx = "unicode_encode_error")]
#[derive(Debug)]
#[repr(transparent)]
pub struct PyUnicodeEncodeError(PyUnicodeError);
#[pyexception(with(Initializer))]
impl PyUnicodeEncodeError {
#[pymethod]
fn __str__(exc: PyBaseExceptionRef, vm: &VirtualMachine) -> PyResult<String> {
let Ok(object) = exc.as_object().get_attr("object", vm) else {
@@ -2254,22 +2259,13 @@ pub(super) mod types {
}
}
#[pyexception(name, base = PyUnicodeError, ctx = "unicode_translate_error")]
#[derive(Debug)]
#[repr(transparent)]
pub struct PyUnicodeTranslateError(PyUnicodeError);
impl Initializer for PyUnicodeEncodeError {
type Args = FuncArgs;
#[pyexception]
impl PyUnicodeTranslateError {
#[pyslot]
#[pymethod(name = "__init__")]
pub(crate) fn slot_init(
zelf: PyObjectRef,
args: FuncArgs,
vm: &VirtualMachine,
) -> PyResult<()> {
type Args = (PyStrRef, isize, isize, PyStrRef);
let (object, start, end, reason): Args = args.bind(vm)?;
fn slot_init(zelf: PyObjectRef, args: FuncArgs, vm: &VirtualMachine) -> PyResult<()> {
type Args = (PyStrRef, PyStrRef, isize, isize, PyStrRef);
let (encoding, object, start, end, reason): Args = args.bind(vm)?;
zelf.set_attr("encoding", encoding, vm)?;
zelf.set_attr("object", object, vm)?;
zelf.set_attr("start", vm.ctx.new_int(start), vm)?;
zelf.set_attr("end", vm.ctx.new_int(end), vm)?;
@@ -2277,6 +2273,18 @@ pub(super) mod types {
Ok(())
}
fn init(_zelf: PyRef<Self>, _args: Self::Args, _vm: &VirtualMachine) -> PyResult<()> {
unreachable!("slot_init is defined")
}
}
#[pyexception(name, base = PyUnicodeError, ctx = "unicode_translate_error")]
#[derive(Debug)]
#[repr(transparent)]
pub struct PyUnicodeTranslateError(PyUnicodeError);
#[pyexception(with(Initializer))]
impl PyUnicodeTranslateError {
#[pymethod]
fn __str__(exc: PyBaseExceptionRef, vm: &VirtualMachine) -> PyResult<String> {
let Ok(object) = exc.as_object().get_attr("object", vm) else {
@@ -2301,6 +2309,24 @@ pub(super) mod types {
}
}
impl Initializer for PyUnicodeTranslateError {
type Args = FuncArgs;
fn slot_init(zelf: PyObjectRef, args: FuncArgs, vm: &VirtualMachine) -> PyResult<()> {
type Args = (PyStrRef, isize, isize, PyStrRef);
let (object, start, end, reason): Args = args.bind(vm)?;
zelf.set_attr("object", object, vm)?;
zelf.set_attr("start", vm.ctx.new_int(start), vm)?;
zelf.set_attr("end", vm.ctx.new_int(end), vm)?;
zelf.set_attr("reason", reason, vm)?;
Ok(())
}
fn init(_zelf: PyRef<Self>, _args: Self::Args, _vm: &VirtualMachine) -> PyResult<()> {
unreachable!("slot_init is defined")
}
}
/// JIT error.
#[cfg(feature = "jit")]
#[pyexception(name, base = PyException, ctx = "jit_error", impl)]

View File

@@ -3,21 +3,57 @@ use super::{PY_CF_OPTIMIZED_AST, PY_CF_TYPE_COMMENTS, PY_COMPILE_FLAG_AST_ONLY};
#[pymodule]
pub(crate) mod _ast {
use crate::{
AsObject, Context, Py, PyObjectRef, PyPayload, PyResult, VirtualMachine,
AsObject, Context, Py, PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine,
builtins::{PyStrRef, PyTupleRef, PyType, PyTypeRef},
function::FuncArgs,
types::Constructor,
types::{Constructor, Initializer},
};
#[pyattr]
#[pyclass(module = "_ast", name = "AST")]
#[derive(Debug, PyPayload)]
pub(crate) struct NodeAst;
#[pyclass(with(Constructor), flags(BASETYPE, HAS_DICT))]
#[pyclass(with(Constructor, Initializer), flags(BASETYPE, HAS_DICT))]
impl NodeAst {
#[pyslot]
#[pymethod]
fn __init__(zelf: PyObjectRef, args: FuncArgs, vm: &VirtualMachine) -> PyResult<()> {
#[pyattr]
fn _fields(ctx: &Context) -> PyTupleRef {
ctx.empty_tuple.clone()
}
}
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
// FIXME: This is probably incorrect. Please check if init should be called outside of __new__
Self::slot_init(zelf.clone(), args, vm)?;
Ok(zelf)
}
fn py_new(_cls: &Py<PyType>, _args: Self::Args, _vm: &VirtualMachine) -> PyResult<Self> {
unimplemented!("use slot_new")
}
}
impl Initializer for NodeAst {
type Args = FuncArgs;
fn slot_init(zelf: PyObjectRef, args: FuncArgs, vm: &VirtualMachine) -> PyResult<()> {
let fields = zelf.get_attr("_fields", vm)?;
let fields: Vec<PyStrRef> = fields.try_to_value(vm)?;
let n_args = args.args.len();
@@ -47,37 +83,8 @@ pub(crate) mod _ast {
Ok(())
}
#[pyattr]
fn _fields(ctx: &Context) -> PyTupleRef {
ctx.empty_tuple.clone()
}
}
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
Self::__init__(zelf.clone(), args, vm)?;
Ok(zelf)
}
fn py_new(_cls: &Py<PyType>, _args: Self::Args, _vm: &VirtualMachine) -> PyResult<Self> {
unimplemented!("use slot_new")
fn init(_zelf: PyRef<Self>, _args: Self::Args, _vm: &VirtualMachine) -> PyResult<()> {
unreachable!("slot_init is defined")
}
}