Fix Exception.__init__ · RustPython/RustPython@f5b44f5 · GitHub
Skip to content

Commit f5b44f5

Browse files
committed
Fix Exception.__init__
1 parent 24bff8d commit f5b44f5

6 files changed

Lines changed: 135 additions & 85 deletions

File tree

crates/derive-impl/src/pyclass.rs

Lines changed: 4 additions & 30 deletions

crates/stdlib/src/hashlib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ pub mod _hashlib {
1111
class::StaticType,
1212
convert::ToPyObject,
1313
function::{ArgBytesLike, ArgStrOrBytesLike, FuncArgs, OptionalArg},
14-
types::{Constructor, Initializer, Representable},
14+
types::{Constructor, Representable},
1515
};
1616
use blake2::{Blake2b512, Blake2s256};
1717
use digest::{DynDigest, core_api::BlockSizeUser};

crates/stdlib/src/ssl/error.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ pub(crate) mod ssl_error {
77
use crate::vm::{
88
Py, PyPayload, PyRef, PyResult, VirtualMachine,
99
builtins::{PyBaseException, PyOSError, PyStrRef},
10-
types::{Constructor, Initializer},
10+
types::Constructor,
1111
};
1212

1313
// Error type constants - exposed as pyattr and available for internal use

crates/vm/src/exception_group.rs

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -348,8 +348,18 @@ pub(super) mod types {
348348
impl Initializer for PyBaseExceptionGroup {
349349
type Args = FuncArgs;
350350

351-
fn slot_init(_zelf: PyObjectRef, _args: FuncArgs, _vm: &VirtualMachine) -> PyResult<()> {
352-
// No-op: __new__ already set up the correct args (message, exceptions_tuple)
351+
fn slot_init(zelf: PyObjectRef, args: FuncArgs, vm: &VirtualMachine) -> PyResult<()> {
352+
// BaseExceptionGroup_init: no kwargs allowed
353+
if !args.kwargs.is_empty() {
354+
return Err(vm.new_type_error(format!(
355+
"{} does not take keyword arguments",
356+
zelf.class().name()
357+
)));
358+
}
359+
// Do NOT call PyBaseException::slot_init here.
360+
// slot_new already set args to (message, exceptions_tuple).
361+
// Calling base init would overwrite with original args (message, exceptions_list).
362+
let _ = (zelf, args, vm);
353363
Ok(())
354364
}
355365

crates/vm/src/exceptions.rs

Lines changed: 90 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1086,15 +1086,16 @@ fn syntax_error_set_msg(
10861086
}
10871087

10881088
fn system_exit_code(exc: PyBaseExceptionRef) -> Option<PyObjectRef> {
1089-
exc.args.read().first().map(|code| {
1090-
match_class!(match code {
1091-
ref tup @ PyTuple => match tup.as_slice() {
1092-
[x] => x.clone(),
1093-
_ => code.clone(),
1094-
},
1095-
other => other.clone(),
1096-
})
1097-
})
1089+
// SystemExit.code based on args length:
1090+
// - size == 0: code is None
1091+
// - size == 1: code is args[0]
1092+
// - size > 1: code is args (the whole tuple)
1093+
let args = exc.args.read();
1094+
match args.len() {
1095+
0 => None,
1096+
1 => Some(args.first().unwrap().clone()),
1097+
_ => Some(args.as_object().to_owned()),
1098+
}
10981099
}
10991100

11001101
#[cfg(feature = "serde")]
@@ -1255,7 +1256,7 @@ pub(super) mod types {
12551256
},
12561257
convert::ToPyObject,
12571258
convert::ToPyResult,
1258-
function::{ArgBytesLike, FuncArgs},
1259+
function::{ArgBytesLike, FuncArgs, KwArgs},
12591260
types::{Constructor, Initializer},
12601261
};
12611262
use crossbeam_utils::atomic::AtomicCell;
@@ -1393,11 +1394,29 @@ pub(super) mod types {
13931394
pub(super) args: PyRwLock<PyTupleRef>,
13941395
}
13951396

1396-
#[pyexception(name, base = PyBaseException, ctx = "system_exit", impl)]
1397+
#[pyexception(name, base = PyBaseException, ctx = "system_exit")]
13971398
#[derive(Debug)]
13981399
#[repr(transparent)]
13991400
pub struct PySystemExit(PyBaseException);
14001401

1402+
// SystemExit_init: has its own __init__ that sets the code attribute
1403+
#[pyexception(with(Initializer))]
1404+
impl PySystemExit {}
1405+
1406+
impl Initializer for PySystemExit {
1407+
type Args = FuncArgs;
1408+
fn slot_init(zelf: PyObjectRef, args: FuncArgs, vm: &VirtualMachine) -> PyResult<()> {
1409+
// Call BaseException_init first (handles args)
1410+
PyBaseException::slot_init(zelf, args, vm)
1411+
// Note: code is computed dynamically via system_exit_code getter
1412+
// so we don't need to set it here explicitly
1413+
}
1414+
1415+
fn init(_zelf: PyRef<Self>, _args: Self::Args, _vm: &VirtualMachine) -> PyResult<()> {
1416+
unreachable!("slot_init is defined")
1417+
}
1418+
}
1419+
14011420
#[pyexception(name, base = PyBaseException, ctx = "generator_exit", impl)]
14021421
#[derive(Debug)]
14031422
#[repr(transparent)]
@@ -1474,16 +1493,25 @@ pub(super) mod types {
14741493
type Args = FuncArgs;
14751494

14761495
fn slot_init(zelf: PyObjectRef, args: FuncArgs, vm: &VirtualMachine) -> PyResult<()> {
1477-
zelf.set_attr(
1478-
"name",
1479-
vm.unwrap_or_none(args.kwargs.get("name").cloned()),
1480-
vm,
1481-
)?;
1482-
zelf.set_attr(
1483-
"obj",
1484-
vm.unwrap_or_none(args.kwargs.get("obj").cloned()),
1485-
vm,
1486-
)?;
1496+
// Only 'name' and 'obj' kwargs are allowed
1497+
let mut kwargs = args.kwargs.clone();
1498+
let name = kwargs.swap_remove("name");
1499+
let obj = kwargs.swap_remove("obj");
1500+
1501+
// Reject unknown kwargs
1502+
if let Some(invalid_key) = kwargs.keys().next() {
1503+
return Err(vm.new_type_error(format!(
1504+
"AttributeError() got an unexpected keyword argument '{invalid_key}'"
1505+
)));
1506+
}
1507+
1508+
// Pass args without kwargs to BaseException_init
1509+
let base_args = FuncArgs::new(args.args.clone(), KwArgs::default());
1510+
PyBaseException::slot_init(zelf.clone(), base_args, vm)?;
1511+
1512+
// Set attributes
1513+
zelf.set_attr("name", vm.unwrap_or_none(name), vm)?;
1514+
zelf.set_attr("obj", vm.unwrap_or_none(obj), vm)?;
14871515
Ok(())
14881516
}
14891517

@@ -1529,9 +1557,11 @@ pub(super) mod types {
15291557
type Args = FuncArgs;
15301558

15311559
fn slot_init(zelf: PyObjectRef, args: FuncArgs, vm: &VirtualMachine) -> PyResult<()> {
1560+
// Only 'name', 'path', 'name_from' kwargs are allowed
15321561
let mut kwargs = args.kwargs.clone();
15331562
let name = kwargs.swap_remove("name");
15341563
let path = kwargs.swap_remove("path");
1564+
let name_from = kwargs.swap_remove("name_from");
15351565

15361566
// Check for any remaining invalid keyword arguments
15371567
if let Some(invalid_key) = kwargs.keys().next() {
@@ -1543,6 +1573,7 @@ pub(super) mod types {
15431573
let dict = zelf.dict().unwrap();
15441574
dict.set_item("name", vm.unwrap_or_none(name), vm)?;
15451575
dict.set_item("path", vm.unwrap_or_none(path), vm)?;
1576+
dict.set_item("name_from", vm.unwrap_or_none(name_from), vm)?;
15461577
PyBaseException::slot_init(zelf, args, vm)
15471578
}
15481579

@@ -1592,11 +1623,45 @@ pub(super) mod types {
15921623
#[repr(transparent)]
15931624
pub struct PyMemoryError(PyException);
15941625

1595-
#[pyexception(name, base = PyException, ctx = "name_error", impl)]
1626+
#[pyexception(name, base = PyException, ctx = "name_error")]
15961627
#[derive(Debug)]
15971628
#[repr(transparent)]
15981629
pub struct PyNameError(PyException);
15991630

1631+
// NameError_init: handles the .name. kwarg
1632+
#[pyexception(with(Initializer))]
1633+
impl PyNameError {}
1634+
1635+
impl Initializer for PyNameError {
1636+
type Args = FuncArgs;
1637+
fn slot_init(zelf: PyObjectRef, args: FuncArgs, vm: &VirtualMachine) -> PyResult<()> {
1638+
// Only 'name' kwarg is allowed
1639+
let mut kwargs = args.kwargs.clone();
1640+
let name = kwargs.swap_remove("name");
1641+
1642+
// Reject unknown kwargs
1643+
if let Some(invalid_key) = kwargs.keys().next() {
1644+
return Err(vm.new_type_error(format!(
1645+
"NameError() got an unexpected keyword argument '{invalid_key}'"
1646+
)));
1647+
}
1648+
1649+
// Pass args without kwargs to BaseException_init
1650+
let base_args = FuncArgs::new(args.args.clone(), KwArgs::default());
1651+
PyBaseException::slot_init(zelf.clone(), base_args, vm)?;
1652+
1653+
// Set name attribute if provided
1654+
if let Some(name) = name {
1655+
zelf.set_attr("name", name, vm)?;
1656+
}
1657+
Ok(())
1658+
}
1659+
1660+
fn init(_zelf: PyRef<Self>, _args: Self::Args, _vm: &VirtualMachine) -> PyResult<()> {
1661+
unreachable!("slot_init is defined")
1662+
}
1663+
}
1664+
16001665
#[pyexception(name, base = PyNameError, ctx = "unbound_local_error", impl)]
16011666
#[derive(Debug)]
16021667
#[repr(transparent)]
@@ -2232,31 +2297,17 @@ pub(super) mod types {
22322297
}
22332298
}
22342299

2300+
// MiddlingExtendsException: inherits __init__ from SyntaxError via MRO
22352301
#[pyexception(
22362302
name = "_IncompleteInputError",
22372303
base = PySyntaxError,
2238-
ctx = "incomplete_input_error"
2304+
ctx = "incomplete_input_error",
2305+
impl
22392306
)]
22402307
#[derive(Debug)]
22412308
#[repr(transparent)]
22422309
pub struct PyIncompleteInputError(PySyntaxError);
22432310

2244-
#[pyexception(with(Initializer))]
2245-
impl PyIncompleteInputError {}
2246-
2247-
impl Initializer for PyIncompleteInputError {
2248-
type Args = FuncArgs;
2249-
2250-
fn slot_init(zelf: PyObjectRef, args: FuncArgs, vm: &VirtualMachine) -> PyResult<()> {
2251-
zelf.set_attr("name", vm.ctx.new_str("SyntaxError"), vm)?;
2252-
PySyntaxError::slot_init(zelf, args, vm)
2253-
}
2254-
2255-
fn init(_zelf: PyRef<Self>, _args: Self::Args, _vm: &VirtualMachine) -> PyResult<()> {
2256-
unreachable!("slot_init is defined")
2257-
}
2258-
}
2259-
22602311
#[pyexception(name, base = PySyntaxError, ctx = "indentation_error", impl)]
22612312
#[derive(Debug)]
22622313
#[repr(transparent)]

extra_tests/snippets/builtin_exceptions.py

Lines changed: 27 additions & 12 deletions

0 commit comments

Comments
 (0)