1use self::types::{PyBaseException, PyBaseExceptionRef, PyException, PyMemoryError};
2pub use super::exception_group::exception_group;
3use crate::common::lock::PyRwLock;
4use crate::object::{Traverse, TraverseFn};
5use crate::{
6 AsObject, Context, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, TryFromObject,
7 VirtualMachine,
8 builtins::{
9 PyList, PyNone, PyStr, PyStrRef, PyTuple, PyTupleRef, PyType, PyTypeRef,
10 traceback::{PyTraceback, PyTracebackRef},
11 },
12 class::{PyClassImpl, StaticType},
13 convert::{IntoPyException, ToPyException, ToPyObject},
14 function::{ArgIterable, FuncArgs, IntoFuncArgs, PySetterValue},
15 py_io::{self, Write},
16 stdlib::sys,
17 suggestion::offer_suggestions,
18 types::{Callable, Constructor, Initializer, Representable},
19};
20use core::fmt::{self, Display, Formatter};
21use core::sync::atomic::{AtomicBool, Ordering};
22use itertools::Itertools;
23#[cfg(feature = "host_env")]
24use std::io::{BufRead, BufReader};
25use std::sync::Mutex;
26use std::{collections::HashSet, io};
27
28unsafe impl Traverse for PyBaseException {
29 fn traverse(&self, tracer_fn: &mut TraverseFn<'_>) {
30 self.traceback.traverse(tracer_fn);
31 self.cause.traverse(tracer_fn);
32 self.context.traverse(tracer_fn);
33 self.args.traverse(tracer_fn);
34 }
35}
36
37impl core::fmt::Debug for PyBaseException {
38 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
39 f.write_str("PyBaseException")
41 }
42}
43
44const MEMORY_ERROR_FREELIST_SIZE: usize = 16;
45
46struct MemoryErrorHusk(*mut PyObject);
47unsafe impl Send for MemoryErrorHusk {}
48
49static MEMORY_ERROR_FREELIST: Mutex<[Option<MemoryErrorHusk>; MEMORY_ERROR_FREELIST_SIZE]> =
50 Mutex::new([const { None }; MEMORY_ERROR_FREELIST_SIZE]);
51
52impl PyPayload for PyBaseException {
53 #[inline]
54 fn class(ctx: &Context) -> &'static Py<PyType> {
55 ctx.exceptions.base_exception_type
56 }
57}
58
59impl PyPayload for PyMemoryError {
60 const PAYLOAD_TYPE_ID: core::any::TypeId = <PyException as PyPayload>::PAYLOAD_TYPE_ID;
61 const HAS_FREELIST: bool = true;
62 const MAX_FREELIST: usize = MEMORY_ERROR_FREELIST_SIZE;
63
64 #[inline]
65 unsafe fn validate_downcastable_from(obj: &PyObject) -> bool {
66 obj.class()
67 .fast_issubclass(<Self as StaticType>::static_type())
68 }
69
70 fn class(_ctx: &Context) -> &'static Py<PyType> {
71 <Self as StaticType>::static_type()
72 }
73
74 unsafe fn freelist_push(obj: *mut PyObject) -> bool {
75 let Ok(mut list) = MEMORY_ERROR_FREELIST.lock() else {
76 return false;
77 };
78 match list.iter_mut().find(|element| element.is_none()) {
79 Some(element) => {
80 *element = Some(MemoryErrorHusk(obj));
81 true
82 }
83 None => false,
84 }
85 }
86
87 unsafe fn freelist_pop(_payload: &Self) -> Option<core::ptr::NonNull<PyObject>> {
88 let husk = MEMORY_ERROR_FREELIST
89 .lock()
90 .ok()?
91 .iter_mut()
92 .find_map(|element| element.take())?;
93 core::ptr::NonNull::new(husk.0)
94 }
95}
96
97impl VirtualMachine {
98 pub fn print_exception(&self, exc: &Py<PyBaseException>) {
103 let vm = self;
104 let write_fallback = |exc, errstr| {
105 if let Ok(stderr) = sys::get_stderr(vm) {
106 let mut stderr = py_io::PyWriter(stderr, vm);
107 let _ = writeln!(stderr, "{errstr}");
109 let _ = self.write_exception(&mut stderr, exc);
110 } else {
111 eprintln!("{errstr}\nlost sys.stderr");
112 let _ = self.write_exception(&mut py_io::IoWriter(io::stderr()), exc);
113 }
114 };
115 if let Ok(excepthook) = vm.sys_module.get_attr("excepthook", vm) {
116 let (exc_type, exc_val, exc_tb) = vm.split_exception(exc.to_owned());
117 if let Err(eh_exc) = excepthook.call((exc_type, exc_val, exc_tb), vm) {
118 write_fallback(&eh_exc, "Error in sys.excepthook:");
119 write_fallback(exc, "Original exception was:");
120 }
121 } else {
122 write_fallback(exc, "missing sys.excepthook");
123 }
124 }
125
126 pub fn write_exception<W: Write>(
127 &self,
128 output: &mut W,
129 exc: &Py<PyBaseException>,
130 ) -> Result<(), W::Error> {
131 let seen = &mut HashSet::<usize>::new();
132 self.write_exception_recursive(output, exc, seen)
133 }
134
135 fn write_exception_recursive<W: Write>(
136 &self,
137 output: &mut W,
138 exc: &Py<PyBaseException>,
139 seen: &mut HashSet<usize>,
140 ) -> Result<(), W::Error> {
141 seen.insert(exc.get_id());
145
146 #[allow(clippy::manual_map)]
147 if let Some((cause_or_context, msg)) = if let Some(cause) = exc.__cause__() {
148 Some((
151 cause,
152 "\nThe above exception was the direct cause of the following exception:\n",
153 ))
154 } else if let Some(context) = exc.__context__() {
155 Some((
161 context,
162 "\nDuring handling of the above exception, another exception occurred:\n",
163 ))
164 } else {
165 None
166 } {
167 if !seen.contains(&cause_or_context.get_id()) {
168 self.write_exception_recursive(output, &cause_or_context, seen)?;
169 writeln!(output, "{msg}")?;
170 } else {
171 seen.insert(cause_or_context.get_id());
172 }
173 }
174
175 self.write_exception_inner(output, exc)
176 }
177
178 pub fn write_exception_inner<W: Write>(
180 &self,
181 output: &mut W,
182 exc: &Py<PyBaseException>,
183 ) -> Result<(), W::Error> {
184 let vm = self;
185
186 let traceback = exc.traceback.read().clone();
187 if let Some(tb) = traceback {
188 writeln!(output, "Traceback (most recent call last):")?;
189 for tb in tb.iter() {
190 write_traceback_entry(output, &tb)?;
191 }
192 }
193
194 let varargs = exc.args();
195 let args_repr = vm.exception_args_as_string(&varargs, true);
196
197 let exc_class = exc.class();
198
199 if exc_class.fast_issubclass(vm.ctx.exceptions.syntax_error) {
200 return self.write_syntaxerror(output, exc, exc_class, &args_repr);
201 }
202
203 let exc_name = exc_class.name();
204 match args_repr.len() {
205 0 => write!(output, "{exc_name}"),
206 1 => write!(output, "{}: {}", exc_name, args_repr[0]),
207 _ => write!(
208 output,
209 "{}: ({})",
210 exc_name,
211 args_repr.into_iter().format(", "),
212 ),
213 }?;
214
215 if let Some(suggestions) = offer_suggestions(exc, vm) {
216 writeln!(output, ". Did you mean: '{suggestions}'?")
217 } else {
218 writeln!(output)
219 }
220 }
221
222 fn write_syntaxerror<W: Write>(
228 &self,
229 output: &mut W,
230 exc: &Py<PyBaseException>,
231 exc_type: &Py<PyType>,
232 args_repr: &[PyRef<PyStr>],
233 ) -> Result<(), W::Error> {
234 let vm = self;
235 debug_assert!(exc_type.fast_issubclass(vm.ctx.exceptions.syntax_error));
236
237 let getattr = |attr: &'static str| exc.as_object().get_attr(attr, vm).ok();
238
239 let maybe_lineno = getattr("lineno").map(|obj| {
240 obj.str(vm)
241 .unwrap_or_else(|_| vm.ctx.new_str("<lineno str() failed>"))
242 });
243 let maybe_filename = getattr("filename").and_then(|obj| obj.str(vm).ok());
244
245 let maybe_text = getattr("text").map(|obj| {
246 obj.str(vm)
247 .unwrap_or_else(|_| vm.ctx.new_str("<text str() failed>"))
248 });
249
250 let mut filename_suffix = String::new();
251
252 if let Some(lineno) = maybe_lineno {
253 let filename = match maybe_filename {
254 Some(filename) => filename,
255 None => vm.ctx.new_str("<string>"),
256 };
257 writeln!(output, r##" File "{filename}", line {lineno}"##,)?;
258 } else if let Some(filename) = maybe_filename {
259 filename_suffix = format!(" ({filename})");
260 }
261
262 if let Some(text) = maybe_text {
263 use rustpython_common::wtf8::CodePoint;
265 let text_wtf8 = text.as_wtf8();
266 let r_text = text_wtf8.trim_end_matches(|cp: CodePoint| {
267 cp == CodePoint::from_char('\n') || cp == CodePoint::from_char('\r')
268 });
269 let l_text = r_text.trim_start_matches(|cp: CodePoint| {
270 cp == CodePoint::from_char(' ')
271 || cp == CodePoint::from_char('\n')
272 || cp == CodePoint::from_char('\x0c') });
274 let spaces = (r_text.len() - l_text.len()) as isize;
275
276 writeln!(output, " {l_text}")?;
277
278 let maybe_offset: Option<isize> =
279 getattr("offset").and_then(|obj| obj.try_to_value::<isize>(vm).ok());
280
281 if let Some(offset) = maybe_offset {
282 let maybe_end_offset: Option<isize> =
283 getattr("end_offset").and_then(|obj| obj.try_to_value::<isize>(vm).ok());
284 let maybe_end_lineno: Option<isize> =
285 getattr("end_lineno").and_then(|obj| obj.try_to_value::<isize>(vm).ok());
286 let maybe_lineno_int: Option<isize> =
287 getattr("lineno").and_then(|obj| obj.try_to_value::<isize>(vm).ok());
288
289 let same_line = match (maybe_lineno_int, maybe_end_lineno) {
291 (Some(lineno), Some(end_lineno)) => lineno == end_lineno,
292 _ => true,
293 };
294
295 let lone_line_continuation =
297 maybe_end_offset == Some(-1) && l_text.to_string_lossy() == "\\";
298
299 if same_line && !lone_line_continuation {
300 let mut end_offset = match maybe_end_offset {
301 Some(0) | None => offset,
302 Some(end_offset) => end_offset,
303 };
304
305 if offset == end_offset || end_offset == -1 {
306 end_offset = offset + 1;
307 }
308
309 let colno = offset - 1 - spaces;
311 let end_colno = end_offset - 1 - spaces;
312 if colno >= 0 {
313 let caret_space = l_text
314 .code_points()
315 .take(colno as usize)
316 .map(|cp| cp.to_char().filter(|c| c.is_whitespace()).unwrap_or(' '))
317 .collect::<String>();
318
319 let mut error_width = end_colno - colno;
320 if error_width < 1 {
321 error_width = 1;
322 }
323
324 writeln!(
325 output,
326 " {}{}",
327 caret_space,
328 "^".repeat(error_width as usize)
329 )?;
330 }
331 }
332 }
333 }
334
335 let exc_name = exc_type.name();
336
337 match args_repr.len() {
338 0 => write!(output, "{exc_name}{filename_suffix}"),
339 1 => write!(output, "{}: {}{}", exc_name, args_repr[0], filename_suffix),
340 _ => write!(
341 output,
342 "{}: ({}){}",
343 exc_name,
344 args_repr.iter().format(", "),
345 filename_suffix
346 ),
347 }?;
348
349 match offer_suggestions(exc, vm) {
350 Some(suggestions) => writeln!(output, ". Did you mean: '{suggestions}'?"),
351 None => writeln!(output),
352 }
353 }
354
355 fn exception_args_as_string(&self, varargs: &Py<PyTuple>, str_single: bool) -> Vec<PyStrRef> {
356 let vm = self;
357 match varargs.as_slice().len() {
358 0 => vec![],
359 1 => {
360 let args0_repr = if str_single {
361 varargs.as_slice()[0]
362 .str(vm)
363 .unwrap_or_else(|_| PyStr::from("<element str() failed>").into_ref(&vm.ctx))
364 } else {
365 varargs.as_slice()[0].repr(vm).unwrap_or_else(|_| {
366 PyStr::from("<element repr() failed>").into_ref(&vm.ctx)
367 })
368 };
369 vec![args0_repr]
370 }
371 _ => varargs
372 .as_slice()
373 .iter()
374 .map(|vararg| {
375 vararg.repr(vm).unwrap_or_else(|_| {
376 PyStr::from("<element repr() failed>").into_ref(&vm.ctx)
377 })
378 })
379 .collect(),
380 }
381 }
382
383 pub fn split_exception(
384 &self,
385 exc: PyBaseExceptionRef,
386 ) -> (PyObjectRef, PyObjectRef, PyObjectRef) {
387 let tb = exc.traceback().to_pyobject(self);
388 let class = exc.class().to_owned();
389 (class.into(), exc.into(), tb)
390 }
391
392 pub fn normalize_exception(
394 &self,
395 exc_type: PyObjectRef,
396 exc_val: PyObjectRef,
397 exc_tb: PyObjectRef,
398 ) -> PyResult<PyBaseExceptionRef> {
399 let ctor = ExceptionCtor::try_from_object(self, exc_type)?;
400 let exc = ctor.instantiate_value(exc_val, self)?;
401 if let Some(tb) = Option::<PyTracebackRef>::try_from_object(self, exc_tb)? {
402 exc.set_traceback(Some(tb));
403 }
404 Ok(exc)
405 }
406
407 pub fn invoke_exception(
408 &self,
409 cls: &Py<PyType>,
410 args: Vec<PyObjectRef>,
411 ) -> PyResult<PyBaseExceptionRef> {
412 let res = PyType::call(cls, args.into_args(self), self)?;
414 res.downcast::<PyBaseException>().map_err(|obj| {
415 self.new_type_error(format!(
416 "calling {} should have returned an instance of BaseException, not {}",
417 cls,
418 obj.class()
419 ))
420 })
421 }
422}
423
424#[cfg(feature = "host_env")]
425fn print_source_line<W: Write>(
426 output: &mut W,
427 filename: &str,
428 lineno: usize,
429) -> Result<(), W::Error> {
430 let file = match crate::host_env::fs::open(filename) {
433 Ok(file) => file,
434 Err(_) => return Ok(()),
435 };
436 let file = BufReader::new(file);
437
438 for (i, line) in file.lines().enumerate() {
439 if i + 1 == lineno {
440 if let Ok(line) = line {
441 writeln!(output, " {}", line.trim_start())?;
443 }
444 return Ok(());
445 }
446 }
447
448 Ok(())
449}
450
451fn write_traceback_entry<W: Write>(
453 output: &mut W,
454 tb_entry: &Py<PyTraceback>,
455) -> Result<(), W::Error> {
456 let filename = tb_entry.frame.iframe().code().source_path().as_str();
457 writeln!(
458 output,
459 r##" File "{}", line {}, in {}"##,
460 filename.trim_start_matches(r"\\?\"),
461 tb_entry.lineno,
462 tb_entry.frame.iframe().code().obj_name
463 )?;
464
465 #[cfg(feature = "host_env")]
466 print_source_line(output, filename, tb_entry.lineno.get())?;
467
468 Ok(())
469}
470
471#[derive(Clone)]
472pub enum ExceptionCtor {
473 Class(PyTypeRef),
474 Instance(PyBaseExceptionRef),
475}
476
477impl TryFromObject for ExceptionCtor {
478 fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult<Self> {
479 obj.downcast::<PyType>()
480 .and_then(|cls| {
481 if cls.fast_issubclass(vm.ctx.exceptions.base_exception_type) {
482 Ok(Self::Class(cls))
483 } else {
484 Err(cls.into())
485 }
486 })
487 .or_else(|obj| obj.downcast::<PyBaseException>().map(Self::Instance))
488 .map_err(|obj| {
489 vm.new_type_error(format!(
490 "exceptions must be classes or instances deriving from BaseException, not {}",
491 obj.class().name()
492 ))
493 })
494 }
495}
496
497impl ExceptionCtor {
498 pub fn instantiate(self, vm: &VirtualMachine) -> PyResult<PyBaseExceptionRef> {
499 match self {
500 Self::Class(cls) => vm.invoke_exception(&cls, vec![]),
501 Self::Instance(exc) => Ok(exc),
502 }
503 }
504
505 pub fn instantiate_value(
506 self,
507 value: PyObjectRef,
508 vm: &VirtualMachine,
509 ) -> PyResult<PyBaseExceptionRef> {
510 let exc_inst = value.clone().downcast::<PyBaseException>().ok();
511 match (self, exc_inst) {
512 (Self::Instance(_exc_a), Some(_exc_b)) => {
514 Err(vm.new_type_error("instance exception may not have a separate value"))
515 }
516 (Self::Instance(exc), None) => Ok(exc),
518 (Self::Class(cls), Some(exc)) if exc.fast_isinstance(&cls) => Ok(exc),
520 (Self::Class(cls), _) => {
522 let args = match_class!(match value {
523 PyNone => vec![],
524 tup @ PyTuple => tup.as_slice().to_vec(),
525 exc @ PyBaseException => exc.args().as_slice().to_vec(),
526 obj => vec![obj],
527 });
528 vm.invoke_exception(&cls, args)
529 }
530 }
531 }
532}
533
534#[derive(Debug)]
535pub struct ExceptionZoo {
536 pub base_exception_type: &'static Py<PyType>,
537 pub base_exception_group: &'static Py<PyType>,
538 pub system_exit: &'static Py<PyType>,
539 pub keyboard_interrupt: &'static Py<PyType>,
540 pub generator_exit: &'static Py<PyType>,
541 pub exception_type: &'static Py<PyType>,
542 pub stop_iteration: &'static Py<PyType>,
543 pub stop_async_iteration: &'static Py<PyType>,
544 pub arithmetic_error: &'static Py<PyType>,
545 pub floating_point_error: &'static Py<PyType>,
546 pub overflow_error: &'static Py<PyType>,
547 pub zero_division_error: &'static Py<PyType>,
548 pub assertion_error: &'static Py<PyType>,
549 pub attribute_error: &'static Py<PyType>,
550 pub buffer_error: &'static Py<PyType>,
551 pub eof_error: &'static Py<PyType>,
552 pub import_error: &'static Py<PyType>,
553 pub module_not_found_error: &'static Py<PyType>,
554 pub lookup_error: &'static Py<PyType>,
555 pub index_error: &'static Py<PyType>,
556 pub key_error: &'static Py<PyType>,
557 pub memory_error: &'static Py<PyType>,
558 pub name_error: &'static Py<PyType>,
559 pub unbound_local_error: &'static Py<PyType>,
560 pub os_error: &'static Py<PyType>,
561 pub blocking_io_error: &'static Py<PyType>,
562 pub child_process_error: &'static Py<PyType>,
563 pub connection_error: &'static Py<PyType>,
564 pub broken_pipe_error: &'static Py<PyType>,
565 pub connection_aborted_error: &'static Py<PyType>,
566 pub connection_refused_error: &'static Py<PyType>,
567 pub connection_reset_error: &'static Py<PyType>,
568 pub file_exists_error: &'static Py<PyType>,
569 pub file_not_found_error: &'static Py<PyType>,
570 pub interrupted_error: &'static Py<PyType>,
571 pub is_a_directory_error: &'static Py<PyType>,
572 pub not_a_directory_error: &'static Py<PyType>,
573 pub permission_error: &'static Py<PyType>,
574 pub process_lookup_error: &'static Py<PyType>,
575 pub timeout_error: &'static Py<PyType>,
576 pub reference_error: &'static Py<PyType>,
577 pub runtime_error: &'static Py<PyType>,
578 pub not_implemented_error: &'static Py<PyType>,
579 pub recursion_error: &'static Py<PyType>,
580 pub python_finalization_error: &'static Py<PyType>,
581 pub syntax_error: &'static Py<PyType>,
582 pub incomplete_input_error: &'static Py<PyType>,
583 pub indentation_error: &'static Py<PyType>,
584 pub tab_error: &'static Py<PyType>,
585 pub system_error: &'static Py<PyType>,
586 pub type_error: &'static Py<PyType>,
587 pub value_error: &'static Py<PyType>,
588 pub unicode_error: &'static Py<PyType>,
589 pub unicode_decode_error: &'static Py<PyType>,
590 pub unicode_encode_error: &'static Py<PyType>,
591 pub unicode_translate_error: &'static Py<PyType>,
592
593 #[cfg(feature = "jit")]
594 pub jit_error: &'static Py<PyType>,
595
596 pub warning: &'static Py<PyType>,
597 pub deprecation_warning: &'static Py<PyType>,
598 pub pending_deprecation_warning: &'static Py<PyType>,
599 pub runtime_warning: &'static Py<PyType>,
600 pub syntax_warning: &'static Py<PyType>,
601 pub user_warning: &'static Py<PyType>,
602 pub future_warning: &'static Py<PyType>,
603 pub import_warning: &'static Py<PyType>,
604 pub unicode_warning: &'static Py<PyType>,
605 pub bytes_warning: &'static Py<PyType>,
606 pub resource_warning: &'static Py<PyType>,
607 pub encoding_warning: &'static Py<PyType>,
608}
609
610macro_rules! extend_exception {
611 (
612 $exc_struct:ident,
613 $ctx:expr,
614 $class:expr
615 ) => {
616 extend_exception!($exc_struct, $ctx, $class, {});
617 };
618 (
619 $exc_struct:ident,
620 $ctx:expr,
621 $class:expr,
622 { $($name:expr => $value:expr),* $(,)* }
623 ) => {
624 $exc_struct::extend_class($ctx, $class);
625 extend_class!($ctx, $class, {
626 $($name => $value,)*
627 });
628 };
629}
630
631impl PyBaseException {
632 pub(crate) fn new(args: Vec<PyObjectRef>, vm: &VirtualMachine) -> Self {
633 Self {
634 traceback: PyRwLock::new(None),
635 cause: PyRwLock::new(None),
636 context: PyRwLock::new(None),
637 suppress_context: AtomicBool::new(false),
638 args: PyRwLock::new(PyTuple::new_ref(args, &vm.ctx)),
639 }
640 }
641
642 pub fn get_arg(&self, idx: usize) -> Option<PyObjectRef> {
643 self.args.read().as_slice().get(idx).cloned()
644 }
645}
646
647impl PyBaseException {
648 pub fn set_traceback(&self, traceback: Option<PyTracebackRef>) {
649 *self.traceback.write() = traceback;
650 }
651
652 pub fn set_cause(&self, cause: Option<PyRef<Self>>) {
653 let mut c = self.cause.write();
654 self.set_suppress_context(true);
655 *c = cause;
656 }
657
658 pub fn set_context(&self, context: Option<PyRef<Self>>) {
659 *self.context.write() = context;
660 }
661
662 pub(super) fn __suppress_context__(&self) -> bool {
663 self.suppress_context.load(Ordering::Relaxed)
664 }
665
666 pub(super) fn set_suppress_context(&self, suppress_context: bool) {
667 self.suppress_context
668 .store(suppress_context, Ordering::Relaxed);
669 }
670
671 pub fn args(&self) -> PyTupleRef {
672 self.args.read().clone()
673 }
674 pub fn __traceback__(&self) -> Option<PyTracebackRef> {
675 self.traceback.read().clone()
676 }
677 pub fn __cause__(&self) -> Option<PyRef<Self>> {
678 self.cause.read().clone()
679 }
680 pub fn __context__(&self) -> Option<PyRef<Self>> {
681 self.context.read().clone()
682 }
683}
684
685#[pyclass(
686 with(Py, PyRef, Constructor, Initializer, Representable),
687 flags(BASETYPE, HAS_DICT)
688)]
689impl PyBaseException {}
690
691#[pyclass]
692impl Py<PyBaseException> {
693 #[inline]
694 pub fn traceback(&self) -> Option<PyTracebackRef> {
695 self.__traceback__()
696 }
697
698 #[pymethod]
699 pub(super) fn __str__(&self, vm: &VirtualMachine) -> PyStrRef {
700 let str_args = vm.exception_args_as_string(&self.args(), true);
701 match str_args.into_iter().exactly_one() {
702 Err(i) if i.len() == 0 => vm.ctx.empty_str.to_owned(),
703 Ok(s) => s,
704 Err(i) => PyStr::from(format!("({})", i.format(", "))).into_ref(&vm.ctx),
705 }
706 }
707
708 #[pymethod]
709 pub fn add_note(&self, note: PyStrRef, vm: &VirtualMachine) -> PyResult<()> {
710 let dict = crate::builtins::object::object_get_dict(self.as_object().to_owned(), vm)
711 .map_err(|_| vm.new_attribute_error("Exception object has no __dict__"))?;
712
713 let notes = if let Ok(notes) = dict.get_item("__notes__", vm) {
714 notes
715 } else {
716 let new_notes = vm.ctx.new_list(vec![]);
717 dict.set_item("__notes__", new_notes.clone().into(), vm)?;
718 new_notes.into()
719 };
720
721 let notes = notes
722 .downcast::<PyList>()
723 .map_err(|_| vm.new_type_error("Cannot add note: __notes__ is not a list"))?;
724
725 notes.borrow_vec_mut().push(note.into());
726 Ok(())
727 }
728
729 #[pymethod]
730 fn __reduce__(&self, vm: &VirtualMachine) -> PyTupleRef {
731 if let Some(dict) = self.as_object().dict().filter(|x| !x.is_empty()) {
732 vm.new_tuple((self.class().to_owned(), self.args(), dict))
733 } else {
734 vm.new_tuple((self.class().to_owned(), self.args()))
735 }
736 }
737
738 #[pymethod]
739 fn __setstate__(&self, state: PyObjectRef, vm: &VirtualMachine) -> PyResult<PyObjectRef> {
740 if !vm.is_none(&state) {
741 let dict = state
742 .downcast::<crate::builtins::PyDict>()
743 .map_err(|_| vm.new_type_error("state is not a dictionary"))?;
744
745 for (key, value) in &dict {
746 let key_str = key.str(vm)?;
747 if key_str.as_bytes().starts_with(b"__") {
748 continue;
749 }
750 self.as_object().set_attr(&key_str, value, vm)?;
751 }
752 }
753 Ok(vm.ctx.none())
754 }
755
756 #[pygetset]
757 fn __dict__(zelf: PyObjectRef, vm: &VirtualMachine) -> PyResult<crate::builtins::PyDictRef> {
758 crate::builtins::object::object_get_dict(zelf, vm)
759 }
760
761 #[pygetset(setter)]
762 fn set___dict__(zelf: PyObjectRef, value: PySetterValue, vm: &VirtualMachine) -> PyResult<()> {
763 crate::builtins::object::object_generic_set_dict(zelf, value, vm)
764 }
765
766 #[pygetset]
767 pub fn args(&self) -> PyTupleRef {
768 self.payload.args()
769 }
770
771 #[pygetset(setter)]
772 fn set_args(&self, value: PySetterValue, vm: &VirtualMachine) -> PyResult<()> {
773 let PySetterValue::Assign(value) = value else {
774 return Err(vm.new_type_error("args may not be deleted"));
775 };
776 let args: ArgIterable = value.try_into_value(vm)?;
777 let args = args.iter(vm)?.collect::<PyResult<Vec<_>>>()?;
778 *self.args.write() = PyTuple::new_ref(args, &vm.ctx);
779 Ok(())
780 }
781
782 #[pygetset]
783 pub fn __traceback__(&self) -> Option<PyTracebackRef> {
784 self.payload.__traceback__()
785 }
786
787 #[pygetset(setter)]
788 fn set___traceback__(&self, value: PySetterValue, vm: &VirtualMachine) -> PyResult<()> {
789 let PySetterValue::Assign(value) = value else {
790 return Err(vm.new_type_error("__traceback__ may not be deleted"));
791 };
792 let traceback = if vm.is_none(&value) {
793 None
794 } else {
795 match value.downcast::<PyTraceback>() {
796 Ok(tb) => Some(tb),
797 Err(_) => {
798 return Err(vm.new_type_error("__traceback__ must be a traceback or None"));
799 }
800 }
801 };
802 self.set_traceback(traceback);
803 Ok(())
804 }
805
806 #[pygetset]
807 pub fn __cause__(&self) -> Option<PyRef<PyBaseException>> {
808 self.payload.__cause__()
809 }
810
811 #[pygetset(setter)]
812 fn set___cause__(&self, value: PySetterValue, vm: &VirtualMachine) -> PyResult<()> {
813 let PySetterValue::Assign(value) = value else {
814 return Err(vm.new_type_error("__cause__ may not be deleted"));
815 };
816 let cause = if vm.is_none(&value) {
817 None
818 } else {
819 match value.downcast::<PyBaseException>() {
820 Ok(exc) => Some(exc),
821 Err(_) => {
822 return Err(vm.new_type_error(
823 "exception cause must be None or derive from BaseException",
824 ));
825 }
826 }
827 };
828 self.set_cause(cause);
829 Ok(())
830 }
831
832 #[pygetset]
833 pub fn __context__(&self) -> Option<PyRef<PyBaseException>> {
834 self.payload.__context__()
835 }
836
837 #[pygetset(setter)]
838 fn set___context__(&self, value: PySetterValue, vm: &VirtualMachine) -> PyResult<()> {
839 let PySetterValue::Assign(value) = value else {
840 return Err(vm.new_type_error("__context__ may not be deleted"));
841 };
842 let context = if vm.is_none(&value) {
843 None
844 } else {
845 match value.downcast::<PyBaseException>() {
846 Ok(exc) => Some(exc),
847 Err(_) => {
848 return Err(vm.new_type_error(
849 "exception context must be None or derive from BaseException",
850 ));
851 }
852 }
853 };
854 self.set_context(context);
855 Ok(())
856 }
857}
858
859#[pyclass]
860impl PyRef<PyBaseException> {
861 #[pymethod]
862 fn with_traceback(self, tb: Option<PyTracebackRef>) -> Self {
863 *self.traceback.write() = tb;
864 self
865 }
866}
867
868impl Constructor for PyBaseException {
869 type Args = FuncArgs;
870
871 fn slot_new(cls: PyTypeRef, args: FuncArgs, vm: &VirtualMachine) -> PyResult {
872 if cls.is(Self::class(&vm.ctx)) && !args.kwargs.is_empty() {
873 return Err(vm.new_type_error("BaseException() takes no keyword arguments"));
874 }
875 Self::new(args.args, vm)
876 .into_ref_with_type_lazy_dict(vm, cls)
877 .map(Into::into)
878 }
879
880 fn py_new(_cls: &Py<PyType>, _args: FuncArgs, _vm: &VirtualMachine) -> PyResult<Self> {
881 unimplemented!("use slot_new")
882 }
883}
884
885impl Initializer for PyBaseException {
886 type Args = FuncArgs;
887
888 fn init(zelf: &Py<Self>, args: Self::Args, vm: &VirtualMachine) -> PyResult<()> {
889 *zelf.args.write() = PyTuple::new_ref(args.args, &vm.ctx);
890 Ok(())
891 }
892}
893
894impl Representable for PyBaseException {
895 #[inline]
896 fn repr_str(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<String> {
897 let repr_args = vm.exception_args_as_string(&zelf.args(), false);
898 let cls = zelf.class();
899 Ok(format!("{}({})", cls.name(), repr_args.iter().format(", ")))
900 }
901}
902
903impl ExceptionZoo {
904 pub(crate) fn init() -> Self {
905 use self::types::*;
906
907 let base_exception_type = PyBaseException::init_builtin_type();
908
909 let base_exception_group = PyBaseExceptionGroup::init_builtin_type();
911 let system_exit = PySystemExit::init_builtin_type();
912 let keyboard_interrupt = PyKeyboardInterrupt::init_builtin_type();
913 let generator_exit = PyGeneratorExit::init_builtin_type();
914
915 let exception_type = PyException::init_builtin_type();
916 let stop_iteration = PyStopIteration::init_builtin_type();
917 let stop_async_iteration = PyStopAsyncIteration::init_builtin_type();
918 let arithmetic_error = PyArithmeticError::init_builtin_type();
919 let floating_point_error = PyFloatingPointError::init_builtin_type();
920 let overflow_error = PyOverflowError::init_builtin_type();
921 let zero_division_error = PyZeroDivisionError::init_builtin_type();
922
923 let assertion_error = PyAssertionError::init_builtin_type();
924 let attribute_error = PyAttributeError::init_builtin_type();
925 let buffer_error = PyBufferError::init_builtin_type();
926 let eof_error = PyEOFError::init_builtin_type();
927
928 let import_error = PyImportError::init_builtin_type();
929 let module_not_found_error = PyModuleNotFoundError::init_builtin_type();
930
931 let lookup_error = PyLookupError::init_builtin_type();
932 let index_error = PyIndexError::init_builtin_type();
933 let key_error = PyKeyError::init_builtin_type();
934
935 let memory_error = PyMemoryError::init_builtin_type();
936
937 let name_error = PyNameError::init_builtin_type();
938 let unbound_local_error = PyUnboundLocalError::init_builtin_type();
939
940 let os_error = PyOSError::init_builtin_type();
942 let blocking_io_error = PyBlockingIOError::init_builtin_type();
943 let child_process_error = PyChildProcessError::init_builtin_type();
944
945 let connection_error = PyConnectionError::init_builtin_type();
946 let broken_pipe_error = PyBrokenPipeError::init_builtin_type();
947 let connection_aborted_error = PyConnectionAbortedError::init_builtin_type();
948 let connection_refused_error = PyConnectionRefusedError::init_builtin_type();
949 let connection_reset_error = PyConnectionResetError::init_builtin_type();
950
951 let file_exists_error = PyFileExistsError::init_builtin_type();
952 let file_not_found_error = PyFileNotFoundError::init_builtin_type();
953 let interrupted_error = PyInterruptedError::init_builtin_type();
954 let is_a_directory_error = PyIsADirectoryError::init_builtin_type();
955 let not_a_directory_error = PyNotADirectoryError::init_builtin_type();
956 let permission_error = PyPermissionError::init_builtin_type();
957 let process_lookup_error = PyProcessLookupError::init_builtin_type();
958 let timeout_error = PyTimeoutError::init_builtin_type();
959
960 let reference_error = PyReferenceError::init_builtin_type();
961
962 let runtime_error = PyRuntimeError::init_builtin_type();
963 let not_implemented_error = PyNotImplementedError::init_builtin_type();
964 let recursion_error = PyRecursionError::init_builtin_type();
965 let python_finalization_error = PyPythonFinalizationError::init_builtin_type();
966
967 let syntax_error = PySyntaxError::init_builtin_type();
968 let incomplete_input_error = PyIncompleteInputError::init_builtin_type();
969 let indentation_error = PyIndentationError::init_builtin_type();
970 let tab_error = PyTabError::init_builtin_type();
971
972 let system_error = PySystemError::init_builtin_type();
973 let type_error = PyTypeError::init_builtin_type();
974 let value_error = PyValueError::init_builtin_type();
975 let unicode_error = PyUnicodeError::init_builtin_type();
976 let unicode_decode_error = PyUnicodeDecodeError::init_builtin_type();
977 let unicode_encode_error = PyUnicodeEncodeError::init_builtin_type();
978 let unicode_translate_error = PyUnicodeTranslateError::init_builtin_type();
979
980 #[cfg(feature = "jit")]
981 let jit_error = PyJitError::init_builtin_type();
982
983 let warning = PyWarning::init_builtin_type();
984 let deprecation_warning = PyDeprecationWarning::init_builtin_type();
985 let pending_deprecation_warning = PyPendingDeprecationWarning::init_builtin_type();
986 let runtime_warning = PyRuntimeWarning::init_builtin_type();
987 let syntax_warning = PySyntaxWarning::init_builtin_type();
988 let user_warning = PyUserWarning::init_builtin_type();
989 let future_warning = PyFutureWarning::init_builtin_type();
990 let import_warning = PyImportWarning::init_builtin_type();
991 let unicode_warning = PyUnicodeWarning::init_builtin_type();
992 let bytes_warning = PyBytesWarning::init_builtin_type();
993 let resource_warning = PyResourceWarning::init_builtin_type();
994 let encoding_warning = PyEncodingWarning::init_builtin_type();
995
996 Self {
997 base_exception_type,
998 base_exception_group,
999 system_exit,
1000 keyboard_interrupt,
1001 generator_exit,
1002 exception_type,
1003 stop_iteration,
1004 stop_async_iteration,
1005 arithmetic_error,
1006 floating_point_error,
1007 overflow_error,
1008 zero_division_error,
1009 assertion_error,
1010 attribute_error,
1011 buffer_error,
1012 eof_error,
1013 import_error,
1014 module_not_found_error,
1015 lookup_error,
1016 index_error,
1017 key_error,
1018 memory_error,
1019 name_error,
1020 unbound_local_error,
1021 os_error,
1022 blocking_io_error,
1023 child_process_error,
1024 connection_error,
1025 broken_pipe_error,
1026 connection_aborted_error,
1027 connection_refused_error,
1028 connection_reset_error,
1029 file_exists_error,
1030 file_not_found_error,
1031 interrupted_error,
1032 is_a_directory_error,
1033 not_a_directory_error,
1034 permission_error,
1035 process_lookup_error,
1036 timeout_error,
1037 reference_error,
1038 runtime_error,
1039 not_implemented_error,
1040 recursion_error,
1041 python_finalization_error,
1042 syntax_error,
1043 incomplete_input_error,
1044 indentation_error,
1045 tab_error,
1046 system_error,
1047 type_error,
1048 value_error,
1049 unicode_error,
1050 unicode_decode_error,
1051 unicode_encode_error,
1052 unicode_translate_error,
1053
1054 #[cfg(feature = "jit")]
1055 jit_error,
1056
1057 warning,
1058 deprecation_warning,
1059 pending_deprecation_warning,
1060 runtime_warning,
1061 syntax_warning,
1062 user_warning,
1063 future_warning,
1064 import_warning,
1065 unicode_warning,
1066 bytes_warning,
1067 resource_warning,
1068 encoding_warning,
1069 }
1070 }
1071
1072 #[allow(
1074 clippy::redundant_clone,
1075 reason = "temporary workaround until errno/winerror handling is fixed"
1076 )]
1077 pub fn extend(ctx: &'static Context) {
1078 use self::types::*;
1079
1080 let excs = &ctx.exceptions;
1081
1082 PyBaseException::extend_class(ctx, excs.base_exception_type);
1083
1084 extend_exception!(PyBaseExceptionGroup, ctx, excs.base_exception_group);
1086
1087 extend_exception!(PySystemExit, ctx, excs.system_exit);
1088 extend_exception!(PyKeyboardInterrupt, ctx, excs.keyboard_interrupt);
1089 extend_exception!(PyGeneratorExit, ctx, excs.generator_exit);
1090
1091 extend_exception!(PyException, ctx, excs.exception_type);
1092
1093 extend_exception!(PyStopIteration, ctx, excs.stop_iteration);
1094 extend_exception!(PyStopAsyncIteration, ctx, excs.stop_async_iteration);
1095
1096 extend_exception!(PyArithmeticError, ctx, excs.arithmetic_error);
1097 extend_exception!(PyFloatingPointError, ctx, excs.floating_point_error);
1098 extend_exception!(PyOverflowError, ctx, excs.overflow_error);
1099 extend_exception!(PyZeroDivisionError, ctx, excs.zero_division_error);
1100
1101 extend_exception!(PyAssertionError, ctx, excs.assertion_error);
1102 extend_exception!(PyAttributeError, ctx, excs.attribute_error);
1103 extend_exception!(PyBufferError, ctx, excs.buffer_error);
1104 extend_exception!(PyEOFError, ctx, excs.eof_error);
1105
1106 extend_exception!(PyImportError, ctx, excs.import_error);
1107 extend_exception!(PyModuleNotFoundError, ctx, excs.module_not_found_error);
1108
1109 extend_exception!(PyLookupError, ctx, excs.lookup_error);
1110 extend_exception!(PyIndexError, ctx, excs.index_error);
1111
1112 extend_exception!(PyKeyError, ctx, excs.key_error);
1113
1114 extend_exception!(PyMemoryError, ctx, excs.memory_error);
1115 extend_exception!(PyNameError, ctx, excs.name_error);
1116 extend_exception!(PyUnboundLocalError, ctx, excs.unbound_local_error);
1117
1118 extend_exception!(PyOSError, ctx, excs.os_error);
1121
1122 extend_exception!(PyBlockingIOError, ctx, excs.blocking_io_error);
1123 extend_exception!(PyChildProcessError, ctx, excs.child_process_error);
1124
1125 extend_exception!(PyConnectionError, ctx, excs.connection_error);
1126 extend_exception!(PyBrokenPipeError, ctx, excs.broken_pipe_error);
1127 extend_exception!(PyConnectionAbortedError, ctx, excs.connection_aborted_error);
1128 extend_exception!(PyConnectionRefusedError, ctx, excs.connection_refused_error);
1129 extend_exception!(PyConnectionResetError, ctx, excs.connection_reset_error);
1130
1131 extend_exception!(PyFileExistsError, ctx, excs.file_exists_error);
1132 extend_exception!(PyFileNotFoundError, ctx, excs.file_not_found_error);
1133 extend_exception!(PyInterruptedError, ctx, excs.interrupted_error);
1134 extend_exception!(PyIsADirectoryError, ctx, excs.is_a_directory_error);
1135 extend_exception!(PyNotADirectoryError, ctx, excs.not_a_directory_error);
1136 extend_exception!(PyPermissionError, ctx, excs.permission_error);
1137 extend_exception!(PyProcessLookupError, ctx, excs.process_lookup_error);
1138 extend_exception!(PyTimeoutError, ctx, excs.timeout_error);
1139
1140 extend_exception!(PyReferenceError, ctx, excs.reference_error);
1141 extend_exception!(PyRuntimeError, ctx, excs.runtime_error);
1142 extend_exception!(PyNotImplementedError, ctx, excs.not_implemented_error);
1143 extend_exception!(PyRecursionError, ctx, excs.recursion_error);
1144 extend_exception!(
1145 PyPythonFinalizationError,
1146 ctx,
1147 excs.python_finalization_error
1148 );
1149
1150 extend_exception!(PySyntaxError, ctx, excs.syntax_error);
1151 extend_exception!(PyIncompleteInputError, ctx, excs.incomplete_input_error);
1152 extend_exception!(PyIndentationError, ctx, excs.indentation_error);
1153 extend_exception!(PyTabError, ctx, excs.tab_error);
1154
1155 extend_exception!(PySystemError, ctx, excs.system_error);
1156 extend_exception!(PyTypeError, ctx, excs.type_error);
1157 extend_exception!(PyValueError, ctx, excs.value_error);
1158 extend_exception!(PyUnicodeError, ctx, excs.unicode_error);
1159 extend_exception!(PyUnicodeDecodeError, ctx, excs.unicode_decode_error);
1160 extend_exception!(PyUnicodeEncodeError, ctx, excs.unicode_encode_error);
1161 extend_exception!(PyUnicodeTranslateError, ctx, excs.unicode_translate_error);
1162
1163 #[cfg(feature = "jit")]
1164 extend_exception!(PyJitError, ctx, excs.jit_error);
1165
1166 extend_exception!(PyWarning, ctx, excs.warning);
1167 extend_exception!(PyDeprecationWarning, ctx, excs.deprecation_warning);
1168 extend_exception!(
1169 PyPendingDeprecationWarning,
1170 ctx,
1171 excs.pending_deprecation_warning
1172 );
1173 extend_exception!(PyRuntimeWarning, ctx, excs.runtime_warning);
1174 extend_exception!(PySyntaxWarning, ctx, excs.syntax_warning);
1175 extend_exception!(PyUserWarning, ctx, excs.user_warning);
1176 extend_exception!(PyFutureWarning, ctx, excs.future_warning);
1177 extend_exception!(PyImportWarning, ctx, excs.import_warning);
1178 extend_exception!(PyUnicodeWarning, ctx, excs.unicode_warning);
1179 extend_exception!(PyBytesWarning, ctx, excs.bytes_warning);
1180 extend_exception!(PyResourceWarning, ctx, excs.resource_warning);
1181 extend_exception!(PyEncodingWarning, ctx, excs.encoding_warning);
1182 }
1183}
1184
1185#[cfg(feature = "serde")]
1186pub struct SerializeException<'vm, 's> {
1187 vm: &'vm VirtualMachine,
1188 exc: &'s Py<PyBaseException>,
1189}
1190
1191#[cfg(feature = "serde")]
1192impl<'vm, 's> SerializeException<'vm, 's> {
1193 pub fn new(vm: &'vm VirtualMachine, exc: &'s Py<PyBaseException>) -> Self {
1194 SerializeException { vm, exc }
1195 }
1196}
1197
1198#[cfg(feature = "serde")]
1199pub struct SerializeExceptionOwned<'vm> {
1200 vm: &'vm VirtualMachine,
1201 exc: PyBaseExceptionRef,
1202}
1203
1204#[cfg(feature = "serde")]
1205impl serde::Serialize for SerializeExceptionOwned<'_> {
1206 fn serialize<S: serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
1207 let Self { vm, exc } = self;
1208 SerializeException::new(vm, exc).serialize(s)
1209 }
1210}
1211
1212#[cfg(feature = "serde")]
1213impl serde::Serialize for SerializeException<'_, '_> {
1214 fn serialize<S: serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
1215 use serde::ser::*;
1216
1217 let mut struc = s.serialize_struct("PyBaseException", 7)?;
1218 struc.serialize_field("exc_type", &*self.exc.class().name())?;
1219 let tbs = {
1220 struct Tracebacks(PyTracebackRef);
1221 impl serde::Serialize for Tracebacks {
1222 fn serialize<S: serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
1223 let mut s = s.serialize_seq(None)?;
1224 for tb in self.0.iter() {
1225 s.serialize_element(&**tb)?;
1226 }
1227 s.end()
1228 }
1229 }
1230 self.exc.traceback().map(Tracebacks)
1231 };
1232 struc.serialize_field("traceback", &tbs)?;
1233 struc.serialize_field(
1234 "cause",
1235 &self
1236 .exc
1237 .__cause__()
1238 .map(|exc| SerializeExceptionOwned { vm: self.vm, exc }),
1239 )?;
1240 struc.serialize_field(
1241 "context",
1242 &self
1243 .exc
1244 .__context__()
1245 .map(|exc| SerializeExceptionOwned { vm: self.vm, exc }),
1246 )?;
1247 struc.serialize_field("suppress_context", &self.exc.__suppress_context__())?;
1248
1249 let args = {
1250 struct Args<'vm>(&'vm VirtualMachine, PyTupleRef);
1251 impl serde::Serialize for Args<'_> {
1252 fn serialize<S: serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
1253 s.collect_seq(
1254 self.1
1255 .as_slice()
1256 .iter()
1257 .map(|arg| crate::py_serde::PyObjectSerializer::new(self.0, arg)),
1258 )
1259 }
1260 }
1261 Args(self.vm, self.exc.args())
1262 };
1263 struc.serialize_field("args", &args)?;
1264
1265 let rendered = {
1266 let mut rendered = String::new();
1267 self.vm
1268 .write_exception(&mut rendered, self.exc)
1269 .map_err(S::Error::custom)?;
1270 rendered
1271 };
1272 struc.serialize_field("rendered", &rendered)?;
1273
1274 struc.end()
1275 }
1276}
1277
1278#[derive(Debug)]
1279pub struct NulError;
1280
1281impl Display for NulError {
1282 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1283 write!(f, "embedded null character")
1284 }
1285}
1286
1287pub fn nul_char_error(vm: &VirtualMachine) -> PyBaseExceptionRef {
1288 vm.new_value_error("embedded null character")
1289}
1290
1291pub fn nul_char_type_error(vm: &VirtualMachine) -> PyBaseExceptionRef {
1292 vm.new_type_error("embedded null character")
1293}
1294
1295pub fn nul_byte_error(vm: &VirtualMachine) -> PyBaseExceptionRef {
1296 vm.new_value_error("embedded null byte")
1297}
1298
1299impl ToPyException for alloc::ffi::NulError {
1300 fn to_pyexception(&self, vm: &VirtualMachine) -> PyBaseExceptionRef {
1301 nul_char_error(vm)
1302 }
1303}
1304
1305impl ToPyException for alloc::ffi::FromVecWithNulError {
1306 fn to_pyexception(&self, vm: &VirtualMachine) -> PyBaseExceptionRef {
1307 nul_char_error(vm)
1308 }
1309}
1310
1311impl ToPyException for core::ffi::FromBytesWithNulError {
1312 fn to_pyexception(&self, vm: &VirtualMachine) -> PyBaseExceptionRef {
1313 nul_char_error(vm)
1314 }
1315}
1316
1317impl ToPyException for NulError {
1318 fn to_pyexception(&self, vm: &VirtualMachine) -> PyBaseExceptionRef {
1319 nul_char_error(vm)
1320 }
1321}
1322
1323#[cfg(windows)]
1324impl<C> ToPyException for widestring::error::ContainsNul<C> {
1325 fn to_pyexception(&self, vm: &VirtualMachine) -> PyBaseExceptionRef {
1326 nul_char_error(vm)
1327 }
1328}
1329
1330#[cfg(windows)]
1331impl ToPyException for widestring::error::MissingNulTerminator {
1332 fn to_pyexception(&self, vm: &VirtualMachine) -> PyBaseExceptionRef {
1333 nul_char_error(vm)
1334 }
1335}
1336
1337#[cfg(windows)]
1338impl<C> ToPyException for widestring::error::NulError<C> {
1339 fn to_pyexception(&self, vm: &VirtualMachine) -> PyBaseExceptionRef {
1340 nul_char_error(vm)
1341 }
1342}
1343
1344#[cfg(any(unix, windows, target_os = "wasi"))]
1345pub(crate) fn errno_to_exc_type(errno: i32, vm: &VirtualMachine) -> Option<&'static Py<PyType>> {
1346 use crate::stdlib::errno::errors;
1347 let excs = &vm.ctx.exceptions;
1348 match errno {
1349 #[allow(unreachable_patterns)] errors::EWOULDBLOCK | errors::EAGAIN => Some(excs.blocking_io_error),
1351 errors::EALREADY => Some(excs.blocking_io_error),
1352 errors::EINPROGRESS => Some(excs.blocking_io_error),
1353 errors::EPIPE => Some(excs.broken_pipe_error),
1354 #[cfg(not(target_os = "wasi"))]
1355 errors::ESHUTDOWN => Some(excs.broken_pipe_error),
1356 errors::ECHILD => Some(excs.child_process_error),
1357 errors::ECONNABORTED => Some(excs.connection_aborted_error),
1358 errors::ECONNREFUSED => Some(excs.connection_refused_error),
1359 errors::ECONNRESET => Some(excs.connection_reset_error),
1360 errors::EEXIST => Some(excs.file_exists_error),
1361 errors::ENOENT => Some(excs.file_not_found_error),
1362 errors::EISDIR => Some(excs.is_a_directory_error),
1363 errors::ENOTDIR => Some(excs.not_a_directory_error),
1364 errors::EINTR => Some(excs.interrupted_error),
1365 errors::EACCES => Some(excs.permission_error),
1366 errors::EPERM => Some(excs.permission_error),
1367 #[cfg(target_vendor = "apple")]
1369 errors::ENOTCAPABLE => Some(excs.permission_error),
1370 errors::ESRCH => Some(excs.process_lookup_error),
1371 errors::ETIMEDOUT => Some(excs.timeout_error),
1372 _ => None,
1373 }
1374}
1375
1376#[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))]
1377pub(crate) fn errno_to_exc_type(_errno: i32, _vm: &VirtualMachine) -> Option<&'static Py<PyType>> {
1378 None
1379}
1380
1381pub(crate) trait ToOSErrorBuilder {
1382 fn to_os_error_builder(&self, vm: &VirtualMachine) -> OSErrorBuilder;
1383}
1384
1385pub(crate) struct OSErrorBuilder {
1386 exc_type: PyTypeRef,
1387 errno: Option<i32>,
1388 strerror: Option<PyObjectRef>,
1389 filename: Option<PyObjectRef>,
1390 #[cfg(windows)]
1391 winerror: Option<PyObjectRef>,
1392 filename2: Option<PyObjectRef>,
1393}
1394
1395impl OSErrorBuilder {
1396 #[must_use]
1397 pub(crate) fn with_subtype(
1398 exc_type: PyTypeRef,
1399 errno: Option<i32>,
1400 strerror: impl ToPyObject,
1401 vm: &VirtualMachine,
1402 ) -> Self {
1403 let strerror = strerror.to_pyobject(vm);
1404 Self {
1405 exc_type,
1406 errno,
1407 strerror: Some(strerror),
1408 filename: None,
1409 #[cfg(windows)]
1410 winerror: None,
1411 filename2: None,
1412 }
1413 }
1414
1415 #[must_use]
1416 pub(crate) fn with_errno(errno: i32, strerror: impl ToPyObject, vm: &VirtualMachine) -> Self {
1417 let exc_type = errno_to_exc_type(errno, vm)
1418 .unwrap_or(vm.ctx.exceptions.os_error)
1419 .to_owned();
1420 Self::with_subtype(exc_type, Some(errno), strerror, vm)
1421 }
1422
1423 #[must_use]
1424 #[allow(dead_code)]
1425 pub(crate) fn filename(mut self, filename: PyObjectRef) -> Self {
1426 self.filename.replace(filename);
1427 self
1428 }
1429
1430 #[must_use]
1431 #[allow(dead_code)]
1432 pub(crate) fn filename2(mut self, filename: PyObjectRef) -> Self {
1433 self.filename2.replace(filename);
1434 self
1435 }
1436
1437 #[must_use]
1438 #[cfg(windows)]
1439 pub(crate) fn winerror(mut self, winerror: PyObjectRef) -> Self {
1440 self.winerror.replace(winerror);
1441 self
1442 }
1443
1444 #[must_use]
1448 #[cfg(windows)]
1449 pub(crate) fn without_winerror(mut self) -> Self {
1450 self.winerror = None;
1451 self
1452 }
1453
1454 pub(crate) fn build(self, vm: &VirtualMachine) -> PyRef<types::PyOSError> {
1455 use types::PyOSError;
1456
1457 let Self {
1458 exc_type,
1459 errno,
1460 strerror,
1461 filename,
1462 #[cfg(windows)]
1463 winerror,
1464 filename2,
1465 } = self;
1466
1467 let args = if let Some(errno) = errno {
1468 let winerror = cfg_select! {
1469 windows => winerror.to_pyobject(vm),
1470 _ => vm.ctx.none(),
1471 };
1472
1473 vec![
1474 errno.to_pyobject(vm),
1475 strerror.to_pyobject(vm),
1476 filename.to_pyobject(vm),
1477 winerror,
1478 filename2.to_pyobject(vm),
1479 ]
1480 } else {
1481 vec![strerror.to_pyobject(vm)]
1482 };
1483
1484 vm.new_payload_exception::<PyOSError>(exc_type, args.into())
1485 .expect("new_os_error usage error")
1486 }
1487}
1488
1489impl IntoPyException for OSErrorBuilder {
1490 fn into_pyexception(self, vm: &VirtualMachine) -> PyBaseExceptionRef {
1491 self.build(vm).upcast()
1492 }
1493}
1494
1495impl ToOSErrorBuilder for std::io::Error {
1496 fn to_os_error_builder(&self, vm: &VirtualMachine) -> OSErrorBuilder {
1497 use crate::host_env::os::ErrorExt;
1498
1499 let errno = self.posix_errno();
1500 #[cfg(windows)]
1501 let msg = 'msg: {
1502 const MAX_POSIX_ERRNO: i32 = 127;
1505 if errno > 0
1506 && errno <= MAX_POSIX_ERRNO
1507 && let Some(s) = crate::host_env::errno::strerror_string(errno)
1508 && !s.starts_with("Unknown error")
1509 {
1510 break 'msg s;
1511 }
1512 self.to_string()
1513 };
1514 #[cfg(unix)]
1515 let msg =
1516 crate::host_env::errno::strerror_string(errno).unwrap_or_else(|| self.to_string());
1517 #[cfg(not(any(windows, unix)))]
1518 let msg = self.to_string();
1519
1520 #[allow(unused_mut)]
1521 let mut builder = OSErrorBuilder::with_errno(errno, msg, vm);
1522 #[cfg(windows)]
1523 if let Some(winerror) = self.raw_os_error() {
1524 builder = builder.winerror(winerror.to_pyobject(vm));
1525 }
1526 builder
1527 }
1528}
1529
1530impl ToPyException for std::io::Error {
1531 fn to_pyexception(&self, vm: &VirtualMachine) -> PyBaseExceptionRef {
1532 let builder = self.to_os_error_builder(vm);
1533 builder.into_pyexception(vm)
1534 }
1535}
1536
1537impl IntoPyException for std::io::Error {
1538 fn into_pyexception(self, vm: &VirtualMachine) -> PyBaseExceptionRef {
1539 self.to_pyexception(vm)
1540 }
1541}
1542
1543#[cfg(all(unix, not(target_os = "redox")))]
1544impl ToPyException for rustpython_host_env::fcntl::LockfError {
1545 fn to_pyexception(&self, vm: &VirtualMachine) -> PyBaseExceptionRef {
1546 match self {
1547 Self::InvalidCmd => vm.new_value_error("unrecognized lockf argument"),
1548 Self::Overflow(e) => vm.new_overflow_error(e.clone()),
1549 Self::Io(err) => err.to_pyexception(vm),
1550 }
1551 }
1552}
1553
1554#[cfg(unix)]
1555impl ToPyException for rustpython_host_env::posix::AccessError {
1556 fn to_pyexception(&self, vm: &VirtualMachine) -> PyBaseExceptionRef {
1557 match self {
1558 Self::InvalidMode => vm.new_value_error(
1559 "One of the flags is wrong, there are only 4 possibilities F_OK, R_OK, W_OK and X_OK",
1560 ),
1561 Self::Os(errno) => std::io::Error::from_raw_os_error(*errno).to_pyexception(vm),
1562 }
1563 }
1564}
1565
1566#[cfg(all(unix, not(target_os = "redox")))]
1567impl ToPyException for rustpython_host_env::socket::AncillaryPackError {
1568 fn to_pyexception(&self, vm: &VirtualMachine) -> PyBaseExceptionRef {
1569 match self {
1570 Self::ItemTooLarge => vm.new_os_error("ancillary data item too large"),
1571 Self::TooMuchData => vm.new_os_error("too much ancillary data"),
1572 Self::UnexpectedNullHeader => {
1573 vm.new_runtime_error("unexpected NULL result from CMSG_FIRSTHDR/CMSG_NXTHDR")
1574 }
1575 }
1576 }
1577}
1578
1579#[cfg(any(unix, windows))]
1580impl ToPyException for rustpython_host_env::time::CheckedTmError {
1581 fn to_pyexception(&self, vm: &VirtualMachine) -> PyBaseExceptionRef {
1582 match self {
1583 Self::YearOutOfRange => vm.new_overflow_error("year out of range"),
1584 Self::MonthOutOfRange => vm.new_value_error("month out of range"),
1585 Self::DayOfMonthOutOfRange => vm.new_value_error("day of month out of range"),
1586 Self::HourOutOfRange => vm.new_value_error("hour out of range"),
1587 Self::MinuteOutOfRange => vm.new_value_error("minute out of range"),
1588 Self::SecondsOutOfRange => vm.new_value_error("seconds out of range"),
1589 Self::DayOfWeekOutOfRange => vm.new_value_error("day of week out of range"),
1590 Self::DayOfYearOutOfRange => vm.new_value_error("day of year out of range"),
1591 Self::EmbeddedNul => vm.new_value_error("embedded null character"),
1592 }
1593 }
1594}
1595
1596#[cfg(windows)]
1597impl ToPyException for rustpython_host_env::winapi::BuildEnvironmentBlockError {
1598 fn to_pyexception(&self, vm: &VirtualMachine) -> PyBaseExceptionRef {
1599 match self {
1600 Self::ContainsNul => vm.new_value_error("embedded null character"),
1601 Self::IllegalName => vm.new_value_error("illegal environment variable name"),
1602 }
1603 }
1604}
1605
1606#[cfg(windows)]
1607impl ToPyException for rustpython_host_env::winapi::BatchedWaitError {
1608 fn to_pyexception(&self, vm: &VirtualMachine) -> PyBaseExceptionRef {
1609 match self {
1610 Self::Timeout => vm
1611 .new_os_subtype_error(
1612 vm.ctx.exceptions.timeout_error.to_owned(),
1613 None,
1614 "timed out",
1615 )
1616 .upcast(),
1617 Self::Interrupted => vm
1618 .new_errno_error(libc::EINTR, "Interrupted system call")
1619 .upcast(),
1620 Self::Os(err) => vm.new_os_error(*err as i32),
1621 }
1622 }
1623}
1624
1625#[cfg(windows)]
1626impl ToPyException for rustpython_host_env::nt::ReadlinkError {
1627 fn to_pyexception(&self, vm: &VirtualMachine) -> PyBaseExceptionRef {
1628 match self {
1629 Self::Io(err) => err.to_pyexception(vm),
1630 Self::NotSymbolicLink => {
1631 vm.new_os_error("The file or directory is not a reparse point")
1632 }
1633 Self::InvalidReparseData => vm.new_os_error("Invalid reparse data"),
1634 }
1635 }
1636}
1637
1638#[cfg(windows)]
1639impl ToPyException for rustpython_host_env::nt::ReadConsoleError {
1640 fn to_pyexception(&self, vm: &VirtualMachine) -> PyBaseExceptionRef {
1641 match self {
1642 Self::Io(err) => err.to_pyexception(vm),
1643 Self::BufferTooSmall {
1644 available,
1645 required,
1646 } => vm.new_system_error(format!(
1647 "Buffer had room for {available} bytes but {required} bytes required",
1648 )),
1649 }
1650 }
1651}
1652
1653#[cfg(windows)]
1654impl ToPyException for rustpython_host_env::winreg::ExpandEnvironmentStringsError {
1655 fn to_pyexception(&self, vm: &VirtualMachine) -> PyBaseExceptionRef {
1656 match self {
1657 Self::Os => vm.new_os_error("ExpandEnvironmentStringsW failed"),
1658 Self::Utf16(e) => vm.new_value_error(format!("UTF16 error: {e}")),
1659 }
1660 }
1661}
1662
1663#[cfg(windows)]
1664impl ToPyException for rustpython_host_env::winreg::QueryStringError {
1665 fn to_pyexception(&self, vm: &VirtualMachine) -> PyBaseExceptionRef {
1666 match self {
1667 Self::Code(err) => std::io::Error::from_raw_os_error(*err as i32).to_pyexception(vm),
1668 Self::Utf16(e) => vm.new_value_error(format!("UTF16 error: {e}")),
1669 }
1670 }
1671}
1672
1673#[cfg(windows)]
1674impl ToPyException for rustpython_host_env::wmi::ExecQueryError {
1675 fn to_pyexception(&self, vm: &VirtualMachine) -> PyBaseExceptionRef {
1676 match self {
1677 Self::MoreData => vm.new_os_error(format!(
1678 "Query returns more than {} characters",
1679 rustpython_host_env::wmi::BUFFER_SIZE
1680 )),
1681 Self::Code(err) => std::io::Error::from_raw_os_error(*err as i32).to_pyexception(vm),
1682 }
1683 }
1684}
1685
1686#[cfg(unix)]
1687impl ToPyException for rustpython_host_env::multiprocessing::SemError {
1688 fn to_pyexception(&self, vm: &VirtualMachine) -> PyBaseExceptionRef {
1689 let excs = &vm.ctx.exceptions;
1690 let exc_type = match self {
1691 Self::AlreadyExists => excs.file_exists_error.to_owned(),
1692 Self::NotFound => excs.file_not_found_error.to_owned(),
1693 _ => excs.os_error.to_owned(),
1694 };
1695 vm.new_os_subtype_error(exc_type, Some(self.raw_os_error()), self.description())
1696 .upcast()
1697 }
1698}
1699
1700pub(super) mod types {
1701 use crate::class::PyClassDef;
1702 use crate::common::lock::PyRwLock;
1703 use crate::object::{Traverse, TraverseFn};
1704 #[cfg_attr(target_arch = "wasm32", allow(unused_imports))]
1705 use crate::{
1706 AsObject, Py, PyAtomicRef, PyObject, PyObjectRef, PyPayload, PyRef, PyResult,
1707 VirtualMachine,
1708 builtins::{
1709 PyInt, PyStrRef, PyTupleRef, PyType, PyTypeRef, traceback::PyTracebackRef,
1710 tuple::IntoPyTuple,
1711 },
1712 convert::ToPyResult,
1713 function::{ArgBytesLike, FuncArgs, KwArgs, PySetterValue},
1714 types::{Constructor, Initializer},
1715 };
1716 use core::sync::atomic::{AtomicBool, AtomicIsize, Ordering};
1717 use crossbeam_utils::atomic::AtomicCell;
1718 use itertools::Itertools;
1719 use rustpython_common::{
1720 str::UnicodeEscapeCodepoint,
1721 wtf8::{Wtf8, Wtf8Buf, wtf8_concat},
1722 };
1723
1724 pub use crate::exception_group::types::PyBaseExceptionGroup;
1726
1727 pub type PyBaseExceptionRef = PyRef<PyBaseException>;
1732
1733 #[pyclass(module = false, name = "BaseException", traverse = "manual")]
1736 pub struct PyBaseException {
1737 pub(super) traceback: PyRwLock<Option<PyTracebackRef>>,
1738 pub(super) cause: PyRwLock<Option<PyRef<Self>>>,
1739 pub(super) context: PyRwLock<Option<PyRef<Self>>>,
1740 #[pymember(name = "__suppress_context__", writable)]
1741 pub(super) suppress_context: AtomicBool,
1742 pub(super) args: PyRwLock<PyTupleRef>,
1743 }
1744
1745 #[pyexception(name, base = PyBaseException, ctx = "system_exit", traverse = "manual")]
1746 #[repr(C)]
1747 pub struct PySystemExit {
1748 base: PyBaseException,
1749 #[pymember(writable)]
1750 code: PyAtomicRef<Option<PyObject>>,
1751 }
1752
1753 impl crate::class::PySubclass for PySystemExit {
1754 type Base = PyBaseException;
1755 fn as_base(&self) -> &Self::Base {
1756 &self.base
1757 }
1758 }
1759
1760 unsafe impl Traverse for PySystemExit {
1761 fn traverse(&self, tracer_fn: &mut TraverseFn<'_>) {
1762 self.base.traverse(tracer_fn);
1763 if let Some(obj) = self.code.deref() {
1764 tracer_fn(obj);
1765 }
1766 }
1767 }
1768
1769 impl core::fmt::Debug for PySystemExit {
1770 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1771 f.debug_struct("PySystemExit").finish_non_exhaustive()
1772 }
1773 }
1774
1775 #[pyexception(with(Constructor, Initializer))]
1776 impl PySystemExit {}
1777
1778 impl Initializer for PySystemExit {
1779 type Args = FuncArgs;
1780 fn slot_init(zelf: &PyObject, args: FuncArgs, vm: &VirtualMachine) -> PyResult<()> {
1781 let code = match args.args.len() {
1783 0 => vm.ctx.none(),
1784 1 => args.args[0].clone(),
1785 _ => vm.ctx.new_tuple(args.args.clone()).into(),
1786 };
1787 PyBaseException::slot_init(zelf, args, vm)?;
1788 let exc: &Py<Self> = zelf.downcast_ref::<Self>().unwrap();
1789 exc.code.swap_to_temporary_refs(Some(code), vm);
1790 Ok(())
1791 }
1792
1793 fn init(_zelf: &Py<Self>, _args: Self::Args, _vm: &VirtualMachine) -> PyResult<()> {
1794 unreachable!("slot_init is defined")
1795 }
1796 }
1797
1798 impl Constructor for PySystemExit {
1799 type Args = FuncArgs;
1800
1801 fn py_new(_cls: &Py<PyType>, args: FuncArgs, vm: &VirtualMachine) -> PyResult<Self> {
1802 let base_exception = PyBaseException::new(args.args, vm);
1803 Ok(Self {
1804 base: base_exception,
1805 code: None.into(),
1806 })
1807 }
1808 }
1809
1810 #[pyexception(name, base = PyBaseException, ctx = "generator_exit", impl)]
1811 #[derive(Debug)]
1812 #[repr(transparent)]
1813 pub struct PyGeneratorExit(PyBaseException);
1814
1815 #[pyexception(name, base = PyBaseException, ctx = "keyboard_interrupt", impl)]
1816 #[derive(Debug)]
1817 #[repr(transparent)]
1818 pub struct PyKeyboardInterrupt(PyBaseException);
1819
1820 #[pyexception(name, base = PyBaseException, ctx = "exception_type", impl)]
1821 #[derive(Debug)]
1822 #[repr(transparent)]
1823 pub struct PyException(PyBaseException);
1824
1825 #[pyexception(name, base = PyException, ctx = "stop_iteration", traverse = "manual")]
1826 #[repr(C)]
1827 pub struct PyStopIteration {
1828 base: PyException,
1829 #[pymember(writable)]
1830 value: PyAtomicRef<Option<PyObject>>,
1831 }
1832
1833 impl crate::class::PySubclass for PyStopIteration {
1834 type Base = PyException;
1835 fn as_base(&self) -> &Self::Base {
1836 &self.base
1837 }
1838 }
1839
1840 impl core::fmt::Debug for PyStopIteration {
1841 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1842 f.debug_struct("PyStopIteration").finish_non_exhaustive()
1843 }
1844 }
1845
1846 unsafe impl Traverse for PyStopIteration {
1847 fn traverse(&self, tracer_fn: &mut TraverseFn<'_>) {
1848 self.base.0.traverse(tracer_fn);
1849 if let Some(obj) = self.value.deref() {
1850 tracer_fn(obj);
1851 }
1852 }
1853 }
1854
1855 impl Constructor for PyStopIteration {
1856 type Args = FuncArgs;
1857
1858 fn py_new(_cls: &Py<PyType>, args: FuncArgs, vm: &VirtualMachine) -> PyResult<Self> {
1859 let base_exception = PyBaseException::new(args.args, vm);
1860 Ok(Self {
1861 base: PyException(base_exception),
1862 value: None.into(),
1863 })
1864 }
1865 }
1866
1867 impl Initializer for PyStopIteration {
1868 type Args = FuncArgs;
1869 fn slot_init(zelf: &PyObject, args: FuncArgs, vm: &VirtualMachine) -> PyResult<()> {
1870 let value = match args.args.len() {
1871 0 => vm.ctx.none(),
1872 _ => args.args[0].clone(),
1873 };
1874 PyBaseException::slot_init(zelf, args, vm)?;
1875 let exc: &Py<Self> = zelf.downcast_ref::<Self>().unwrap();
1876 exc.value.swap_to_temporary_refs(Some(value), vm);
1877 Ok(())
1878 }
1879
1880 fn init(_zelf: &Py<Self>, _args: Self::Args, _vm: &VirtualMachine) -> PyResult<()> {
1881 unreachable!("slot_init is defined")
1882 }
1883 }
1884
1885 #[pyexception(with(Constructor, Initializer))]
1886 impl PyStopIteration {}
1887
1888 #[pyexception(name, base = PyException, ctx = "stop_async_iteration", impl)]
1889 #[derive(Debug)]
1890 #[repr(transparent)]
1891 pub struct PyStopAsyncIteration(PyException);
1892
1893 #[pyexception(name, base = PyException, ctx = "arithmetic_error", impl)]
1894 #[derive(Debug)]
1895 #[repr(transparent)]
1896 pub struct PyArithmeticError(PyException);
1897
1898 #[pyexception(name, base = PyArithmeticError, ctx = "floating_point_error", impl)]
1899 #[derive(Debug)]
1900 #[repr(transparent)]
1901 pub struct PyFloatingPointError(PyArithmeticError);
1902
1903 #[pyexception(name, base = PyArithmeticError, ctx = "overflow_error", impl)]
1904 #[derive(Debug)]
1905 #[repr(transparent)]
1906 pub struct PyOverflowError(PyArithmeticError);
1907
1908 #[pyexception(name, base = PyArithmeticError, ctx = "zero_division_error", impl)]
1909 #[derive(Debug)]
1910 #[repr(transparent)]
1911 pub struct PyZeroDivisionError(PyArithmeticError);
1912
1913 #[pyexception(name, base = PyException, ctx = "assertion_error", impl)]
1914 #[derive(Debug)]
1915 #[repr(transparent)]
1916 pub struct PyAssertionError(PyException);
1917
1918 #[pyexception(name, base = PyException, ctx = "attribute_error", traverse = "manual")]
1919 #[repr(C)]
1920 pub struct PyAttributeError {
1921 base: PyException,
1922 #[pymember(writable)]
1923 name: PyAtomicRef<Option<PyObject>>,
1924 #[pymember(writable)]
1925 obj: PyAtomicRef<Option<PyObject>>,
1926 }
1927
1928 impl crate::class::PySubclass for PyAttributeError {
1929 type Base = PyException;
1930 fn as_base(&self) -> &Self::Base {
1931 &self.base
1932 }
1933 }
1934
1935 impl core::fmt::Debug for PyAttributeError {
1936 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1937 f.debug_struct("PyAttributeError").finish_non_exhaustive()
1938 }
1939 }
1940
1941 unsafe impl Traverse for PyAttributeError {
1942 fn traverse(&self, tracer_fn: &mut TraverseFn<'_>) {
1943 self.base.0.traverse(tracer_fn);
1944 if let Some(obj) = self.name.deref() {
1945 tracer_fn(obj);
1946 }
1947 if let Some(obj) = self.obj.deref() {
1948 tracer_fn(obj);
1949 }
1950 }
1951 }
1952
1953 impl Constructor for PyAttributeError {
1954 type Args = FuncArgs;
1955
1956 fn py_new(_cls: &Py<PyType>, args: FuncArgs, vm: &VirtualMachine) -> PyResult<Self> {
1957 Ok(Self {
1958 base: PyException(PyBaseException::new(args.args, vm)),
1959 name: None.into(),
1960 obj: None.into(),
1961 })
1962 }
1963 }
1964
1965 #[pyexception(with(Constructor, Initializer))]
1966 impl PyAttributeError {
1967 #[pymethod]
1968 fn __getstate__(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult {
1969 let obj = zelf.as_object();
1970 let state = match obj.dict() {
1971 Some(dict) => dict.copy().into_ref(&vm.ctx),
1972 None => vm.ctx.new_dict(),
1973 };
1974 let _ = state.del_item("obj", vm);
1975 if let Some(name) = zelf.name.load_owned() {
1976 state.set_item("name", name, vm)?;
1977 }
1978 Ok(state.into())
1979 }
1980
1981 #[pymethod]
1982 fn __reduce__(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<PyTupleRef> {
1983 let state = Self::__getstate__(zelf, vm)?;
1984 let base: &Py<PyBaseException> = zelf
1985 .as_object()
1986 .downcast_ref()
1987 .expect("AttributeError is a BaseException");
1988 Ok(vm.ctx.new_tuple(vec![
1989 zelf.class().to_owned().into(),
1990 base.args().into(),
1991 state,
1992 ]))
1993 }
1994 }
1995
1996 impl Initializer for PyAttributeError {
1997 type Args = FuncArgs;
1998
1999 fn slot_init(zelf: &PyObject, args: FuncArgs, vm: &VirtualMachine) -> PyResult<()> {
2000 let mut kwargs = args.kwargs.clone();
2002 let name = kwargs.swap_remove("name");
2003 let obj = kwargs.swap_remove("obj");
2004
2005 if let Some(invalid_key) = kwargs.keys().next() {
2007 return Err(vm.new_unexpected_keyword_type_error(
2008 Some(Self::NAME),
2009 &invalid_key.to_string(),
2010 ));
2011 }
2012
2013 let base_args = FuncArgs::new(args.args, KwArgs::default());
2015 PyBaseException::slot_init(zelf, base_args, vm)?;
2016
2017 let exc: &Py<Self> = zelf
2018 .downcast_ref()
2019 .expect("AttributeError instance has AttributeError payload");
2020 exc.name.swap_to_temporary_refs(name, vm);
2021 exc.obj.swap_to_temporary_refs(obj, vm);
2022 Ok(())
2023 }
2024
2025 fn init(_zelf: &Py<Self>, _args: Self::Args, _vm: &VirtualMachine) -> PyResult<()> {
2026 unreachable!("slot_init is defined")
2027 }
2028 }
2029
2030 #[pyexception(name, base = PyException, ctx = "buffer_error", impl)]
2031 #[derive(Debug)]
2032 #[repr(transparent)]
2033 pub struct PyBufferError(PyException);
2034
2035 #[pyexception(name, base = PyException, ctx = "eof_error", impl)]
2036 #[derive(Debug)]
2037 #[repr(transparent)]
2038 pub struct PyEOFError(PyException);
2039
2040 #[pyexception(name, base = PyException, ctx = "import_error", traverse = "manual")]
2041 #[repr(C)]
2042 pub struct PyImportError {
2043 base: PyException,
2044 #[pymember(writable)]
2045 msg: PyAtomicRef<Option<PyObject>>,
2046 #[pymember(writable)]
2047 name: PyAtomicRef<Option<PyObject>>,
2048 #[pymember(writable)]
2049 path: PyAtomicRef<Option<PyObject>>,
2050 #[pymember(writable)]
2051 name_from: PyAtomicRef<Option<PyObject>>,
2052 }
2053
2054 impl crate::class::PySubclass for PyImportError {
2055 type Base = PyException;
2056 fn as_base(&self) -> &Self::Base {
2057 &self.base
2058 }
2059 }
2060
2061 impl core::fmt::Debug for PyImportError {
2062 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
2063 f.debug_struct("PyImportError").finish_non_exhaustive()
2064 }
2065 }
2066
2067 unsafe impl Traverse for PyImportError {
2068 fn traverse(&self, tracer_fn: &mut TraverseFn<'_>) {
2069 self.base.0.traverse(tracer_fn);
2070 if let Some(obj) = self.msg.deref() {
2071 tracer_fn(obj);
2072 }
2073 if let Some(obj) = self.name.deref() {
2074 tracer_fn(obj);
2075 }
2076 if let Some(obj) = self.path.deref() {
2077 tracer_fn(obj);
2078 }
2079 if let Some(obj) = self.name_from.deref() {
2080 tracer_fn(obj);
2081 }
2082 }
2083 }
2084
2085 impl Constructor for PyImportError {
2086 type Args = FuncArgs;
2087
2088 fn py_new(_cls: &Py<PyType>, args: FuncArgs, vm: &VirtualMachine) -> PyResult<Self> {
2089 Ok(Self {
2090 base: PyException(PyBaseException::new(args.args, vm)),
2091 msg: None.into(),
2092 name: None.into(),
2093 path: None.into(),
2094 name_from: None.into(),
2095 })
2096 }
2097 }
2098
2099 #[pyexception(with(Constructor, Initializer))]
2100 impl PyImportError {
2101 #[pyslot]
2102 fn slot_str(zelf: &PyObject, vm: &VirtualMachine) -> PyResult<PyStrRef> {
2103 let exc: &Py<Self> = zelf
2104 .downcast_ref()
2105 .expect("slot wrapper checked ImportError");
2106 if let Some(msg) = exc.msg.load_owned()
2107 && msg.class().is(vm.ctx.types.str_type)
2108 {
2109 return msg.str(vm);
2110 }
2111 let base: &Py<PyBaseException> =
2112 zelf.downcast_ref().expect("ImportError is a BaseException");
2113 Ok(base.__str__(vm))
2114 }
2115
2116 #[pymethod]
2117 fn __reduce__(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<PyTupleRef> {
2118 let obj = zelf.as_object();
2119 let exc = zelf;
2120 let base: &Py<PyBaseException> =
2121 obj.downcast_ref().expect("ImportError is a BaseException");
2122 let args: PyObjectRef = match base.get_arg(0) {
2123 Some(arg) => vm.new_tuple((arg,)).into(),
2124 None => base.args().into(),
2125 };
2126 let mut result: Vec<PyObjectRef> = vec![obj.class().to_owned().into(), args];
2127
2128 let mut state = obj
2129 .dict()
2130 .filter(|dict| !dict.is_empty())
2131 .map(|dict| dict.copy().into_ref(&vm.ctx));
2132 let mut insert = |key: &str, value: Option<PyObjectRef>| -> PyResult<()> {
2133 let Some(value) = value else {
2134 return Ok(());
2135 };
2136 let dict = state.get_or_insert_with(|| vm.ctx.new_dict());
2137 dict.set_item(key, value, vm)?;
2138 Ok(())
2139 };
2140 insert("name", exc.name.load_owned())?;
2141 insert("path", exc.path.load_owned())?;
2142 insert("name_from", exc.name_from.load_owned())?;
2143 if let Some(state) = state {
2144 result.push(state.into());
2145 }
2146 Ok(result.into_pytuple(vm))
2147 }
2148 }
2149
2150 impl Initializer for PyImportError {
2151 type Args = FuncArgs;
2152
2153 fn slot_init(zelf: &PyObject, args: FuncArgs, vm: &VirtualMachine) -> PyResult<()> {
2154 let mut kwargs = args.kwargs.clone();
2156 let name = kwargs.swap_remove("name");
2157 let path = kwargs.swap_remove("path");
2158 let name_from = kwargs.swap_remove("name_from");
2159
2160 if let Some(invalid_key) = kwargs.keys().next() {
2162 return Err(vm.new_unexpected_keyword_type_error(
2163 Some("ImportError"),
2164 &invalid_key.to_string(),
2165 ));
2166 }
2167
2168 let msg = if args.args.len() == 1 {
2169 Some(args.args[0].clone())
2170 } else {
2171 None
2172 };
2173 let base_args = FuncArgs::new(args.args, KwArgs::default());
2174 PyBaseException::slot_init(zelf, base_args, vm)?;
2175 let exc: &Py<Self> = zelf
2176 .downcast_ref()
2177 .expect("ImportError instance has ImportError payload");
2178 exc.name.swap_to_temporary_refs(name, vm);
2179 exc.path.swap_to_temporary_refs(path, vm);
2180 exc.name_from.swap_to_temporary_refs(name_from, vm);
2181 exc.msg.swap_to_temporary_refs(msg, vm);
2182 Ok(())
2183 }
2184
2185 fn init(_zelf: &Py<Self>, _args: Self::Args, _vm: &VirtualMachine) -> PyResult<()> {
2186 unreachable!("slot_init is defined")
2187 }
2188 }
2189
2190 #[pyexception(name, base = PyImportError, ctx = "module_not_found_error", impl)]
2191 #[derive(Debug)]
2192 #[repr(transparent)]
2193 pub struct PyModuleNotFoundError(PyImportError);
2194
2195 #[pyexception(name, base = PyException, ctx = "lookup_error", impl)]
2196 #[derive(Debug)]
2197 #[repr(transparent)]
2198 pub struct PyLookupError(PyException);
2199
2200 #[pyexception(name, base = PyLookupError, ctx = "index_error", impl)]
2201 #[derive(Debug)]
2202 #[repr(transparent)]
2203 pub struct PyIndexError(PyLookupError);
2204
2205 #[pyexception(name, base = PyLookupError, ctx = "key_error")]
2206 #[derive(Debug)]
2207 #[repr(transparent)]
2208 pub struct PyKeyError(PyLookupError);
2209
2210 #[pyexception]
2211 impl PyKeyError {
2212 #[pymethod]
2213 fn __str__(zelf: &Py<PyBaseException>, vm: &VirtualMachine) -> PyStrRef {
2214 let args = zelf.args();
2215 if args.as_slice().len() == 1 {
2216 vm.exception_args_as_string(&args, false)
2217 .into_iter()
2218 .exactly_one()
2219 .unwrap()
2220 } else {
2221 zelf.__str__(vm)
2222 }
2223 }
2224 }
2225
2226 #[pyexception(name, base = PyException, ctx = "memory_error", impl, payload = "manual", traverse = "manual")]
2227 #[derive(Debug)]
2228 #[repr(transparent)]
2229 pub struct PyMemoryError(PyException);
2230
2231 impl PyMemoryError {
2232 pub(crate) fn empty(vm: &VirtualMachine) -> Self {
2233 Self(PyException(PyBaseException::new(vec![], vm)))
2234 }
2235 }
2236
2237 unsafe impl Traverse for PyMemoryError {
2238 fn traverse(&self, tracer_fn: &mut TraverseFn<'_>) {
2239 self.0.0.traverse(tracer_fn);
2240 }
2241
2242 fn clear(&mut self, out: &mut Vec<PyObjectRef>) {
2243 let base = &mut self.0.0;
2244 if let Some(traceback) = base.traceback.get_mut().take() {
2245 out.push(traceback.into());
2246 }
2247
2248 if let Some(cause) = base.cause.get_mut().take() {
2249 out.push(cause.into());
2250 }
2251
2252 if let Some(context) = base.context.get_mut().take() {
2253 out.push(context.into());
2254 }
2255 }
2256 }
2257
2258 #[pyexception(name, base = PyException, ctx = "name_error", traverse = "manual")]
2259 #[repr(C)]
2260 pub struct PyNameError {
2261 base: PyException,
2262 #[pymember(writable)]
2263 name: PyAtomicRef<Option<PyObject>>,
2264 }
2265
2266 impl crate::class::PySubclass for PyNameError {
2267 type Base = PyException;
2268 fn as_base(&self) -> &Self::Base {
2269 &self.base
2270 }
2271 }
2272
2273 impl core::fmt::Debug for PyNameError {
2274 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
2275 f.debug_struct("PyNameError").finish_non_exhaustive()
2276 }
2277 }
2278
2279 unsafe impl Traverse for PyNameError {
2280 fn traverse(&self, tracer_fn: &mut TraverseFn<'_>) {
2281 self.base.0.traverse(tracer_fn);
2282 if let Some(obj) = self.name.deref() {
2283 tracer_fn(obj);
2284 }
2285 }
2286 }
2287
2288 impl Constructor for PyNameError {
2289 type Args = FuncArgs;
2290
2291 fn py_new(_cls: &Py<PyType>, args: FuncArgs, vm: &VirtualMachine) -> PyResult<Self> {
2292 Ok(Self {
2293 base: PyException(PyBaseException::new(args.args, vm)),
2294 name: None.into(),
2295 })
2296 }
2297 }
2298
2299 #[pyexception(with(Constructor, Initializer))]
2301 impl PyNameError {}
2302
2303 impl Initializer for PyNameError {
2304 type Args = FuncArgs;
2305 fn slot_init(zelf: &PyObject, args: FuncArgs, vm: &VirtualMachine) -> PyResult<()> {
2306 let mut kwargs = args.kwargs.clone();
2308 let name = kwargs.swap_remove("name");
2309
2310 if let Some(invalid_key) = kwargs.keys().next() {
2312 return Err(vm.new_unexpected_keyword_type_error(
2313 Some(Self::NAME),
2314 &invalid_key.to_string(),
2315 ));
2316 }
2317
2318 let base_args = FuncArgs::new(args.args, KwArgs::default());
2320 PyBaseException::slot_init(zelf, base_args, vm)?;
2321
2322 let exc: &Py<Self> = zelf
2323 .downcast_ref()
2324 .expect("NameError instance has NameError payload");
2325 exc.name.swap_to_temporary_refs(name, vm);
2327 Ok(())
2328 }
2329
2330 fn init(_zelf: &Py<Self>, _args: Self::Args, _vm: &VirtualMachine) -> PyResult<()> {
2331 unreachable!("slot_init is defined")
2332 }
2333 }
2334
2335 #[pyexception(name, base = PyNameError, ctx = "unbound_local_error", impl)]
2336 #[derive(Debug)]
2337 #[repr(transparent)]
2338 pub struct PyUnboundLocalError(PyNameError);
2339
2340 #[pyexception(name, base = PyException, ctx = "os_error", traverse = "manual")]
2341 #[repr(C)]
2342 pub struct PyOSError {
2343 base: PyException,
2344 #[pymember(writable)]
2345 errno: PyAtomicRef<Option<PyObject>>,
2346 #[pymember(writable)]
2347 strerror: PyAtomicRef<Option<PyObject>>,
2348 #[pymember(writable)]
2349 filename: PyAtomicRef<Option<PyObject>>,
2350 #[pymember(writable)]
2351 filename2: PyAtomicRef<Option<PyObject>>,
2352 #[cfg(windows)]
2353 #[pymember(writable)]
2354 winerror: PyAtomicRef<Option<PyObject>>,
2355 written: AtomicCell<isize>,
2358 }
2359
2360 impl crate::class::PySubclass for PyOSError {
2361 type Base = PyException;
2362 fn as_base(&self) -> &Self::Base {
2363 &self.base
2364 }
2365 }
2366
2367 impl core::fmt::Debug for PyOSError {
2368 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
2369 f.debug_struct("PyOSError").finish_non_exhaustive()
2370 }
2371 }
2372
2373 unsafe impl Traverse for PyOSError {
2374 fn traverse(&self, tracer_fn: &mut TraverseFn<'_>) {
2375 self.base.0.traverse(tracer_fn);
2379 if let Some(obj) = self.errno.deref() {
2380 tracer_fn(obj);
2381 }
2382 if let Some(obj) = self.strerror.deref() {
2383 tracer_fn(obj);
2384 }
2385 if let Some(obj) = self.filename.deref() {
2386 tracer_fn(obj);
2387 }
2388 if let Some(obj) = self.filename2.deref() {
2389 tracer_fn(obj);
2390 }
2391 #[cfg(windows)]
2392 if let Some(obj) = self.winerror.deref() {
2393 tracer_fn(obj);
2394 }
2395 }
2396 }
2397
2398 impl Constructor for PyOSError {
2400 type Args = FuncArgs;
2401
2402 fn py_new(_cls: &Py<PyType>, args: FuncArgs, vm: &VirtualMachine) -> PyResult<Self> {
2403 let len = args.args.len();
2404 let (errno, strerror) = if (2..=5).contains(&len) {
2406 (Some(args.args[0].clone()), Some(args.args[1].clone()))
2407 } else {
2408 (None, None)
2409 };
2410 let filename = if (3..=5).contains(&len) {
2411 Some(args.args[2].clone())
2412 } else {
2413 None
2414 };
2415 let filename2 = if len == 5 {
2416 args.args.get(4).cloned()
2417 } else {
2418 None
2419 };
2420 let base_args = if (3..=5).contains(&len) {
2422 args.args[..2].to_vec()
2423 } else {
2424 args.args.to_vec()
2425 };
2426 let base_exception = PyBaseException::new(base_args, vm);
2427 Ok(Self {
2428 base: PyException(base_exception),
2429 errno: errno.into(),
2430 strerror: strerror.into(),
2431 filename: filename.into(),
2432 filename2: filename2.into(),
2433 #[cfg(windows)]
2434 winerror: None.into(),
2435 written: AtomicCell::new(-1),
2436 })
2437 }
2438
2439 fn slot_new(cls: PyTypeRef, args: FuncArgs, vm: &VirtualMachine) -> PyResult {
2440 if oserror_use_init(&cls) {
2441 let payload = Self {
2442 base: PyException(PyBaseException::new(vec![], vm)),
2443 errno: None.into(),
2444 strerror: None.into(),
2445 filename: None.into(),
2446 filename2: None.into(),
2447 #[cfg(windows)]
2448 winerror: None.into(),
2449 written: AtomicCell::new(-1),
2450 };
2451 return payload
2452 .into_ref_with_type_lazy_dict(vm, cls)
2453 .map(Into::into);
2454 }
2455
2456 if !args.kwargs.is_empty() {
2457 return Err(
2458 vm.new_type_error(format!("{}() takes no keyword arguments", cls.slot_name()))
2459 );
2460 }
2461
2462 if *cls.name() == *vm.ctx.exceptions.os_error.name() {
2466 let args_vec = args.args.to_vec();
2467 let len = args_vec.len();
2468 if (2..=5).contains(&len) {
2469 let errno = &args_vec[0];
2470 if let Some(error) = errno
2471 .downcast_ref::<PyInt>()
2472 .and_then(|errno| errno.try_to_primitive::<i32>(vm).ok())
2473 .and_then(|errno| super::errno_to_exc_type(errno, vm))
2474 .and_then(|typ| {
2475 vm.new_payload_exception::<Self>(typ.to_owned(), args_vec.into())
2476 .ok()
2477 })
2478 {
2479 return error.to_pyresult(vm);
2480 }
2481 }
2482 }
2483 let payload = Self::py_new(&cls, args.clone(), vm)?;
2484 let obj = payload.into_ref_with_type_lazy_dict(vm, cls)?;
2485 oserror_init(obj.as_object(), args, vm)?;
2486 Ok(obj.into())
2487 }
2488 }
2489
2490 fn oserror_use_init(cls: &Py<PyType>) -> bool {
2491 let init = cls.slots.init.load();
2492 let new = cls.slots.new.load();
2493 let slot_init: fn(&PyObject, FuncArgs, &VirtualMachine) -> PyResult<()> =
2494 PyOSError::slot_init;
2495 let slot_new: fn(PyTypeRef, FuncArgs, &VirtualMachine) -> PyResult = PyOSError::slot_new;
2496 !matches!(init, Some(f) if core::ptr::fn_addr_eq(f, slot_init))
2497 && matches!(new, Some(f) if core::ptr::fn_addr_eq(f, slot_new))
2498 }
2499
2500 fn oserror_init(zelf: &PyObject, args: FuncArgs, vm: &VirtualMachine) -> PyResult<()> {
2501 let len = args.args.len();
2502 let mut new_args = args;
2503
2504 #[allow(deprecated)]
2509 let exc: &Py<PyOSError> = zelf.downcast_ref::<PyOSError>().unwrap();
2510
2511 let is_blocking_io_error = zelf
2513 .class()
2514 .is(vm.ctx.exceptions.blocking_io_error.as_ref());
2515
2516 let mut set_filename = true;
2519 if len <= 5 {
2520 if 2 <= len {
2522 let _ = unsafe { exc.errno.swap(Some(new_args.args[0].clone())) };
2523 let _ = unsafe { exc.strerror.swap(Some(new_args.args[1].clone())) };
2524 }
2525 if 3 <= len {
2526 let third_arg = &new_args.args[2];
2527 if is_blocking_io_error
2529 && !vm.is_none(third_arg)
2530 && crate::protocol::PyNumber::check(third_arg)
2531 && let Ok(written) = third_arg.try_index(vm)
2532 && let Ok(n) = written.try_to_primitive::<isize>(vm)
2533 {
2534 exc.written.store(n);
2535 set_filename = false;
2536 let _ = unsafe { exc.filename.swap(None) };
2539 let _ = unsafe { exc.filename2.swap(None) };
2540 }
2541 if set_filename {
2542 let _ = unsafe { exc.filename.swap(Some(third_arg.clone())) };
2543 }
2544 }
2545 #[cfg(windows)]
2546 if 4 <= len {
2547 let winerror = new_args.args.get(3).cloned();
2548 let _ = unsafe { exc.winerror.swap(winerror.clone()) };
2550
2551 if let Some(errno) = winerror
2553 .as_ref()
2554 .and_then(|w| w.downcast_ref::<crate::builtins::PyInt>())
2555 .and_then(|w| w.try_to_primitive::<i32>(vm).ok())
2556 .map(crate::host_env::os::winerror_to_errno)
2557 {
2558 let errno_obj = vm.new_pyobj(errno);
2559 let _ = unsafe { exc.errno.swap(Some(errno_obj.clone())) };
2560 new_args.args[0] = errno_obj;
2561 }
2562 }
2563 }
2564
2565 let has_filename = exc
2568 .filename
2569 .load_owned()
2570 .as_ref()
2571 .is_some_and(|f| !vm.is_none(f));
2572 if (3..=5).contains(&len) && has_filename {
2573 if let Some(filename2) = new_args.args.get(4)
2574 && !vm.is_none(filename2)
2575 {
2576 let _ = unsafe { exc.filename2.swap(Some(filename2.clone())) };
2577 }
2578 new_args.args.truncate(2);
2579 }
2580 PyBaseException::slot_init(zelf, new_args, vm)
2581 }
2582
2583 impl Initializer for PyOSError {
2584 type Args = FuncArgs;
2585
2586 fn slot_init(zelf: &PyObject, args: FuncArgs, vm: &VirtualMachine) -> PyResult<()> {
2587 if !oserror_use_init(zelf.class()) {
2588 return Ok(());
2589 }
2590 if !args.kwargs.is_empty() {
2591 return Err(vm.new_type_error(format!(
2592 "{}() takes no keyword arguments",
2593 zelf.class().slot_name()
2594 )));
2595 }
2596 oserror_init(zelf, args, vm)
2597 }
2598
2599 fn init(_zelf: &Py<Self>, _args: Self::Args, _vm: &VirtualMachine) -> PyResult<()> {
2600 unreachable!("slot_init is defined")
2601 }
2602 }
2603
2604 #[pyexception(with(Constructor, Initializer))]
2605 impl PyOSError {
2606 #[pyslot]
2607 fn slot_str(zelf: &PyObject, vm: &VirtualMachine) -> PyResult<PyStrRef> {
2608 let obj = zelf;
2609
2610 let errno_field = obj.get_attr("errno", vm).ok().filter(|v| !vm.is_none(v));
2612 let strerror = obj.get_attr("strerror", vm).ok().filter(|v| !vm.is_none(v));
2613 let filename = obj.get_attr("filename", vm).ok().filter(|v| !vm.is_none(v));
2614 let filename2 = obj
2615 .get_attr("filename2", vm)
2616 .ok()
2617 .filter(|v| !vm.is_none(v));
2618 #[cfg(windows)]
2619 let winerror = obj.get_attr("winerror", vm).ok().filter(|v| !vm.is_none(v));
2620
2621 #[cfg(windows)]
2623 if let Some(ref win_err) = winerror {
2624 let code = win_err.str(vm)?;
2625 if let Some(ref f) = filename {
2626 let msg = strerror
2627 .as_ref()
2628 .map(|s| s.str(vm))
2629 .transpose()?
2630 .map_or_else(|| "None".to_owned(), |s| s.to_string());
2631
2632 if let Some(ref f2) = filename2 {
2633 return Ok(vm.ctx.new_str(format!(
2634 "[WinError {}] {}: {} -> {}",
2635 code,
2636 msg,
2637 f.repr(vm)?,
2638 f2.repr(vm)?
2639 )));
2640 }
2641 return Ok(vm.ctx.new_str(format!(
2642 "[WinError {}] {}: {}",
2643 code,
2644 msg,
2645 f.repr(vm)?
2646 )));
2647 }
2648 if let Some(ref s) = strerror {
2650 return Ok(vm
2651 .ctx
2652 .new_str(format!("[WinError {}] {}", code, s.str(vm)?)));
2653 }
2654 }
2655
2656 if let Some(ref f) = filename {
2658 let errno_str = errno_field
2659 .as_ref()
2660 .map(|e| e.str(vm))
2661 .transpose()?
2662 .map_or_else(|| "None".to_owned(), |s| s.to_string());
2663 let msg = strerror
2664 .as_ref()
2665 .map(|s| s.str(vm))
2666 .transpose()?
2667 .map_or_else(|| "None".to_owned(), |s| s.to_string());
2668 if let Some(ref f2) = filename2 {
2669 return Ok(vm.ctx.new_str(format!(
2670 "[Errno {}] {}: {} -> {}",
2671 errno_str,
2672 msg,
2673 f.repr(vm)?,
2674 f2.repr(vm)?
2675 )));
2676 }
2677 return Ok(vm.ctx.new_str(format!(
2678 "[Errno {}] {}: {}",
2679 errno_str,
2680 msg,
2681 f.repr(vm)?
2682 )));
2683 }
2684
2685 if let (Some(e), Some(s)) = (&errno_field, &strerror) {
2687 return Ok(vm
2688 .ctx
2689 .new_str(format!("[Errno {}] {}", e.str(vm)?, s.str(vm)?)));
2690 }
2691
2692 let base: &Py<PyBaseException> =
2694 zelf.downcast_ref().expect("OSError is a BaseException");
2695 Ok(base.__str__(vm))
2696 }
2697
2698 #[pymethod]
2699 fn __reduce__(zelf: &Py<Self>, vm: &VirtualMachine) -> PyTupleRef {
2700 let base: &Py<PyBaseException> = zelf
2701 .as_object()
2702 .downcast_ref()
2703 .expect("OSError is a BaseException");
2704 let args = base.args();
2705 let obj = zelf.as_object().to_owned();
2706 let mut result: Vec<PyObjectRef> = vec![obj.class().to_owned().into()];
2707
2708 if args.as_slice().len() >= 2 && args.as_slice().len() <= 5 {
2709 let errno = base.get_arg(0).expect("args len checked");
2711 let msg = base.get_arg(1).expect("args len checked");
2712
2713 if let Ok(filename) = obj.get_attr("filename", vm) {
2714 if !vm.is_none(&filename) {
2715 let mut args_reduced: Vec<PyObjectRef> = vec![errno, msg, filename];
2716
2717 let filename2 = obj
2718 .get_attr("filename2", vm)
2719 .ok()
2720 .filter(|f| !vm.is_none(f));
2721
2722 let winerror: Option<PyObjectRef> = cfg_select! {
2723 windows => obj.get_attr("winerror", vm).ok().filter(|w| !vm.is_none(w)),
2724 _ => None,
2725 };
2726
2727 if let Some(filename2) = filename2 {
2728 #[allow(clippy::unnecessary_literal_unwrap)]
2729 let winerror = winerror.unwrap_or_else(|| vm.ctx.none());
2730 args_reduced.extend([winerror, filename2]);
2731 } else {
2732 if let Some(winerror) = winerror {
2735 args_reduced.push(winerror);
2736 }
2737 }
2738 result.push(args_reduced.into_pytuple(vm).into());
2739 } else {
2740 result.push(args.into());
2743 }
2744 } else {
2745 result.push(args.into());
2746 }
2747 } else {
2748 result.push(args.into());
2749 }
2750
2751 if let Some(dict) = obj.dict().filter(|x| !x.is_empty()) {
2752 result.push(dict.into());
2753 }
2754 result.into_pytuple(vm)
2755 }
2756
2757 #[pygetset]
2758 fn characters_written(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<isize> {
2759 let written = zelf.written.load();
2760 if written == -1 {
2761 Err(vm.new_attribute_error("characters_written"))
2762 } else {
2763 Ok(written)
2764 }
2765 }
2766
2767 #[pygetset(setter)]
2768 fn set_characters_written(
2769 zelf: &Py<Self>,
2770 value: PySetterValue,
2771 vm: &VirtualMachine,
2772 ) -> PyResult<()> {
2773 match value {
2774 PySetterValue::Delete => {
2775 if zelf.written.load() == -1 {
2776 Err(vm.new_attribute_error("characters_written"))
2777 } else {
2778 zelf.written.store(-1);
2779 Ok(())
2780 }
2781 }
2782 PySetterValue::Assign(v) => {
2783 let n = v
2784 .try_index(vm)?
2785 .try_to_primitive::<isize>(vm)
2786 .map_err(|_| {
2787 vm.new_value_error("cannot convert characters_written value to isize")
2788 })?;
2789 zelf.written.store(n);
2790 Ok(())
2791 }
2792 }
2793 }
2794 }
2795
2796 #[pyexception(name, base = PyOSError, ctx = "blocking_io_error", impl)]
2797 #[repr(transparent)]
2798 #[derive(Debug)]
2799 pub struct PyBlockingIOError(PyOSError);
2800
2801 #[pyexception(name, base = PyOSError, ctx = "child_process_error", impl)]
2802 #[repr(transparent)]
2803 #[derive(Debug)]
2804 pub struct PyChildProcessError(PyOSError);
2805
2806 #[pyexception(name, base = PyOSError, ctx = "connection_error", impl)]
2807 #[repr(transparent)]
2808 #[derive(Debug)]
2809 pub struct PyConnectionError(PyOSError);
2810
2811 #[pyexception(name, base = PyConnectionError, ctx = "broken_pipe_error", impl)]
2812 #[repr(transparent)]
2813 #[derive(Debug)]
2814 pub struct PyBrokenPipeError(PyConnectionError);
2815
2816 #[pyexception(
2817 name,
2818 base = PyConnectionError,
2819 ctx = "connection_aborted_error",
2820 impl
2821 )]
2822 #[repr(transparent)]
2823 #[derive(Debug)]
2824 pub struct PyConnectionAbortedError(PyConnectionError);
2825
2826 #[pyexception(
2827 name,
2828 base = PyConnectionError,
2829 ctx = "connection_refused_error",
2830 impl
2831 )]
2832 #[repr(transparent)]
2833 #[derive(Debug)]
2834 pub struct PyConnectionRefusedError(PyConnectionError);
2835
2836 #[pyexception(name, base = PyConnectionError, ctx = "connection_reset_error", impl)]
2837 #[repr(transparent)]
2838 #[derive(Debug)]
2839 pub struct PyConnectionResetError(PyConnectionError);
2840
2841 #[pyexception(name, base = PyOSError, ctx = "file_exists_error", impl)]
2842 #[repr(transparent)]
2843 #[derive(Debug)]
2844 pub struct PyFileExistsError(PyOSError);
2845
2846 #[pyexception(name, base = PyOSError, ctx = "file_not_found_error", impl)]
2847 #[repr(transparent)]
2848 #[derive(Debug)]
2849 pub struct PyFileNotFoundError(PyOSError);
2850
2851 #[pyexception(name, base = PyOSError, ctx = "interrupted_error", impl)]
2852 #[repr(transparent)]
2853 #[derive(Debug)]
2854 pub struct PyInterruptedError(PyOSError);
2855
2856 #[pyexception(name, base = PyOSError, ctx = "is_a_directory_error", impl)]
2857 #[repr(transparent)]
2858 #[derive(Debug)]
2859 pub struct PyIsADirectoryError(PyOSError);
2860
2861 #[pyexception(name, base = PyOSError, ctx = "not_a_directory_error", impl)]
2862 #[repr(transparent)]
2863 #[derive(Debug)]
2864 pub struct PyNotADirectoryError(PyOSError);
2865
2866 #[pyexception(name, base = PyOSError, ctx = "permission_error", impl)]
2867 #[repr(transparent)]
2868 #[derive(Debug)]
2869 pub struct PyPermissionError(PyOSError);
2870
2871 #[pyexception(name, base = PyOSError, ctx = "process_lookup_error", impl)]
2872 #[repr(transparent)]
2873 #[derive(Debug)]
2874 pub struct PyProcessLookupError(PyOSError);
2875
2876 #[pyexception(name, base = PyOSError, ctx = "timeout_error", impl)]
2877 #[derive(Debug)]
2878 #[repr(transparent)]
2879 pub struct PyTimeoutError(PyOSError);
2880
2881 #[pyexception(name, base = PyException, ctx = "reference_error", impl)]
2882 #[derive(Debug)]
2883 #[repr(transparent)]
2884 pub struct PyReferenceError(PyException);
2885
2886 #[pyexception(name, base = PyException, ctx = "runtime_error", impl)]
2887 #[derive(Debug)]
2888 #[repr(transparent)]
2889 pub struct PyRuntimeError(PyException);
2890
2891 #[pyexception(name, base = PyRuntimeError, ctx = "not_implemented_error", impl)]
2892 #[derive(Debug)]
2893 #[repr(transparent)]
2894 pub struct PyNotImplementedError(PyRuntimeError);
2895
2896 #[pyexception(name, base = PyRuntimeError, ctx = "recursion_error", impl)]
2897 #[derive(Debug)]
2898 #[repr(transparent)]
2899 pub struct PyRecursionError(PyRuntimeError);
2900
2901 #[pyexception(name, base = PyRuntimeError, ctx = "python_finalization_error", impl)]
2902 #[derive(Debug)]
2903 #[repr(transparent)]
2904 pub struct PyPythonFinalizationError(PyRuntimeError);
2905
2906 #[pyexception(name, base = PyException, ctx = "syntax_error", traverse = "manual")]
2907 #[repr(C)]
2908 pub struct PySyntaxError {
2909 base: PyException,
2910 #[pymember(writable)]
2911 msg: PyAtomicRef<Option<PyObject>>,
2912 #[pymember(writable)]
2913 filename: PyAtomicRef<Option<PyObject>>,
2914 #[pymember(writable)]
2915 lineno: PyAtomicRef<Option<PyObject>>,
2916 #[pymember(writable)]
2917 offset: PyAtomicRef<Option<PyObject>>,
2918 #[pymember(writable)]
2919 text: PyAtomicRef<Option<PyObject>>,
2920 #[pymember(writable)]
2921 end_lineno: PyAtomicRef<Option<PyObject>>,
2922 #[pymember(writable)]
2923 end_offset: PyAtomicRef<Option<PyObject>>,
2924 #[pymember(writable)]
2925 print_file_and_line: PyAtomicRef<Option<PyObject>>,
2926 #[pymember(name = "_metadata", writable)]
2927 metadata: PyAtomicRef<Option<PyObject>>,
2928 }
2929
2930 impl crate::class::PySubclass for PySyntaxError {
2931 type Base = PyException;
2932 fn as_base(&self) -> &Self::Base {
2933 &self.base
2934 }
2935 }
2936
2937 impl core::fmt::Debug for PySyntaxError {
2938 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
2939 f.debug_struct("PySyntaxError").finish_non_exhaustive()
2940 }
2941 }
2942
2943 unsafe impl Traverse for PySyntaxError {
2944 fn traverse(&self, tracer_fn: &mut TraverseFn<'_>) {
2945 self.base.0.traverse(tracer_fn);
2946 for cell in [
2947 &self.msg,
2948 &self.filename,
2949 &self.lineno,
2950 &self.offset,
2951 &self.text,
2952 &self.end_lineno,
2953 &self.end_offset,
2954 &self.print_file_and_line,
2955 &self.metadata,
2956 ] {
2957 if let Some(obj) = cell.deref() {
2958 tracer_fn(obj);
2959 }
2960 }
2961 }
2962 }
2963
2964 impl Constructor for PySyntaxError {
2965 type Args = FuncArgs;
2966
2967 fn py_new(_cls: &Py<PyType>, args: FuncArgs, vm: &VirtualMachine) -> PyResult<Self> {
2968 Ok(Self {
2969 base: PyException(PyBaseException::new(args.args, vm)),
2970 msg: None.into(),
2971 filename: None.into(),
2972 lineno: None.into(),
2973 offset: None.into(),
2974 text: None.into(),
2975 end_lineno: None.into(),
2976 end_offset: None.into(),
2977 print_file_and_line: None.into(),
2978 metadata: None.into(),
2979 })
2980 }
2981 }
2982
2983 #[pyexception(with(Constructor, Initializer))]
2984 impl PySyntaxError {
2985 #[pyslot]
2986 fn slot_str(zelf: &PyObject, vm: &VirtualMachine) -> PyResult<PyStrRef> {
2987 let exc: &Py<Self> = zelf
2988 .downcast_ref()
2989 .expect("slot wrapper checked SyntaxError");
2990 fn basename(filename: &Wtf8) -> &Wtf8 {
2991 let bytes = filename.as_bytes();
2992 let pos = if cfg!(windows) {
2993 bytes.iter().rposition(|&b| b == b'/' || b == b'\\')
2994 } else {
2995 bytes.iter().rposition(|&b| b == b'/')
2996 };
2997 match pos {
2998 Some(pos) => unsafe { Wtf8::from_bytes_unchecked(&bytes[pos + 1..]) },
3000 None => filename,
3001 }
3002 }
3003
3004 let maybe_lineno = exc
3005 .lineno
3006 .load_owned()
3007 .filter(|obj| obj.class().is(vm.ctx.types.int_type))
3008 .and_then(|obj| {
3009 obj.downcast_ref::<PyInt>()
3010 .map(|int| int.as_bigint().to_string())
3011 });
3012 let maybe_filename = exc
3013 .filename
3014 .load_owned()
3015 .filter(|obj| obj.fast_isinstance(vm.ctx.types.str_type))
3016 .map(|obj| {
3017 obj.str(vm)
3018 .unwrap_or_else(|_| vm.ctx.new_str("<filename str() failed>"))
3019 });
3020
3021 let msg = match exc.msg.load_owned() {
3022 Some(obj) => obj
3023 .str(vm)
3024 .unwrap_or_else(|_| vm.ctx.new_str("<msg str() failed>")),
3025 None => vm.ctx.none().str(vm)?,
3026 };
3027
3028 let msg_with_location_info: Wtf8Buf = match (maybe_lineno, maybe_filename) {
3029 (Some(lineno), Some(filename)) => wtf8_concat!(
3030 msg.as_wtf8(),
3031 " (",
3032 basename(filename.as_wtf8()),
3033 ", line ",
3034 lineno.as_str(),
3035 ")"
3036 ),
3037 (Some(lineno), None) => {
3038 wtf8_concat!(msg.as_wtf8(), " (line ", lineno.as_str(), ")")
3039 }
3040 (None, Some(filename)) => {
3041 wtf8_concat!(msg.as_wtf8(), " (", basename(filename.as_wtf8()), ")")
3042 }
3043 (None, None) => msg.as_wtf8().to_owned(),
3044 };
3045
3046 Ok(vm.ctx.new_str(msg_with_location_info))
3047 }
3048 }
3049
3050 impl Initializer for PySyntaxError {
3051 type Args = FuncArgs;
3052
3053 fn slot_init(zelf: &PyObject, args: FuncArgs, vm: &VirtualMachine) -> PyResult<()> {
3054 let len = args.args.len();
3055 let new_args = args;
3056
3057 let mut filename = None;
3058 let mut lineno = None;
3059 let mut offset = None;
3060 let mut text = None;
3061 let mut end_lineno = None;
3062 let mut end_offset = None;
3063 let mut metadata = None;
3064
3065 let have_location = if len == 2
3066 && let Ok(location_tuple) = new_args.args[1]
3067 .clone()
3068 .downcast::<crate::builtins::PyTuple>()
3069 {
3070 let location_tup_len = location_tuple.as_slice().len();
3071
3072 match location_tup_len {
3073 4 | 6 | 7 => {}
3074 5 => {
3075 return Err(vm.new_type_error(
3076 "end_offset must be provided when end_lineno is provided",
3077 ));
3078 }
3079 given if given < 4 => {
3080 return Err(vm.new_type_error(format!(
3081 "function takes at least 4 arguments ({given} given)"
3082 )));
3083 }
3084 given => {
3085 return Err(vm.new_type_error(format!(
3086 "function takes at most 7 arguments ({given} given)"
3087 )));
3088 }
3089 }
3090
3091 let at = |index: usize| -> Option<PyObjectRef> {
3092 (location_tup_len > index).then(|| location_tuple.as_slice()[index].to_owned())
3093 };
3094 filename = at(0);
3095 lineno = at(1);
3096 offset = at(2);
3097 text = at(3);
3098 end_lineno = at(4);
3099 end_offset = at(5);
3100 metadata = at(6);
3101 true
3102 } else {
3103 false
3104 };
3105
3106 PyBaseException::slot_init(zelf, new_args, vm)?;
3107 let exc: &Py<Self> = zelf
3108 .downcast_ref()
3109 .expect("SyntaxError instance has SyntaxError payload");
3110 if len >= 1 {
3111 exc.msg.swap_to_temporary_refs(
3112 Some(exc.base.0.get_arg(0).expect("args len >= 1")),
3113 vm,
3114 );
3115 }
3116 exc.print_file_and_line
3117 .swap_to_temporary_refs(Some(vm.ctx.none()), vm);
3118 if have_location {
3119 exc.filename.swap_to_temporary_refs(filename, vm);
3120 exc.lineno.swap_to_temporary_refs(lineno, vm);
3121 exc.offset.swap_to_temporary_refs(offset, vm);
3122 exc.text.swap_to_temporary_refs(text, vm);
3123 exc.end_lineno.swap_to_temporary_refs(end_lineno, vm);
3124 exc.end_offset.swap_to_temporary_refs(end_offset, vm);
3125 exc.metadata.swap_to_temporary_refs(metadata, vm);
3126 }
3127 Ok(())
3128 }
3129
3130 fn init(_zelf: &Py<Self>, _args: Self::Args, _vm: &VirtualMachine) -> PyResult<()> {
3131 unreachable!("slot_init is defined")
3132 }
3133 }
3134
3135 #[pyexception(
3137 name = "_IncompleteInputError",
3138 base = PySyntaxError,
3139 ctx = "incomplete_input_error",
3140 impl
3141 )]
3142 #[derive(Debug)]
3143 #[repr(transparent)]
3144 pub struct PyIncompleteInputError(PySyntaxError);
3145
3146 #[pyexception(name, base = PySyntaxError, ctx = "indentation_error", impl)]
3147 #[derive(Debug)]
3148 #[repr(transparent)]
3149 pub struct PyIndentationError(PySyntaxError);
3150
3151 #[pyexception(name, base = PyIndentationError, ctx = "tab_error", impl)]
3152 #[derive(Debug)]
3153 #[repr(transparent)]
3154 pub struct PyTabError(PyIndentationError);
3155
3156 #[pyexception(name, base = PyException, ctx = "system_error", impl)]
3157 #[derive(Debug)]
3158 #[repr(transparent)]
3159 pub struct PySystemError(PyException);
3160
3161 #[pyexception(name, base = PyException, ctx = "type_error", impl)]
3162 #[derive(Debug)]
3163 #[repr(transparent)]
3164 pub struct PyTypeError(PyException);
3165
3166 #[pyexception(name, base = PyException, ctx = "value_error", impl)]
3167 #[derive(Debug)]
3168 #[repr(transparent)]
3169 pub struct PyValueError(PyException);
3170
3171 fn parse_tuple_arity(args: &FuncArgs, count: usize, vm: &VirtualMachine) -> PyResult<()> {
3174 let given = args.args.len();
3175 if given == count {
3176 return Ok(());
3177 }
3178 Err(vm.new_type_error(format!(
3179 "function takes exactly {count} arguments ({given} given)"
3180 )))
3181 }
3182
3183 fn unicode_error_payload(exc: &PyObject) -> &Py<PyUnicodeError> {
3184 exc.downcast_ref()
3185 .expect("slot wrapper checked the unicode error type")
3186 }
3187
3188 fn unicode_cell_str(
3191 cell: &PyAtomicRef<Option<PyObject>>,
3192 vm: &VirtualMachine,
3193 ) -> PyResult<PyStrRef> {
3194 match cell.load_owned() {
3195 Some(obj) => obj.str(vm),
3196 None => Ok(vm.ctx.new_str("<NULL>")),
3197 }
3198 }
3199
3200 fn unicode_error_object(
3201 exc: &Py<PyUnicodeError>,
3202 as_bytes: bool,
3203 vm: &VirtualMachine,
3204 ) -> PyResult<PyObjectRef> {
3205 let Some(obj) = exc.object.load_owned() else {
3206 return Err(vm.new_type_error("UnicodeError 'object' attribute is not set"));
3207 };
3208 let ok = if as_bytes {
3209 obj.fast_isinstance(vm.ctx.types.bytes_type)
3210 } else {
3211 obj.fast_isinstance(vm.ctx.types.str_type)
3212 };
3213 if !ok {
3214 let kind = if as_bytes { "bytes" } else { "string" };
3215 return Err(
3216 vm.new_type_error(format!("UnicodeError 'object' attribute must be a {kind}"))
3217 );
3218 }
3219 Ok(obj)
3220 }
3221
3222 #[pyexception(name, base = PyValueError, ctx = "unicode_error", traverse = "manual")]
3223 #[repr(C)]
3224 pub struct PyUnicodeError {
3225 base: PyValueError,
3226 encoding: PyAtomicRef<Option<PyObject>>,
3227 object: PyAtomicRef<Option<PyObject>>,
3228 start: AtomicIsize,
3229 end: AtomicIsize,
3230 reason: PyAtomicRef<Option<PyObject>>,
3231 }
3232
3233 impl crate::class::PySubclass for PyUnicodeError {
3234 type Base = PyValueError;
3235 fn as_base(&self) -> &Self::Base {
3236 &self.base
3237 }
3238 }
3239
3240 impl core::fmt::Debug for PyUnicodeError {
3241 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
3242 f.debug_struct("PyUnicodeError").finish_non_exhaustive()
3243 }
3244 }
3245
3246 unsafe impl Traverse for PyUnicodeError {
3247 fn traverse(&self, tracer_fn: &mut TraverseFn<'_>) {
3248 self.base.0.0.traverse(tracer_fn);
3249 if let Some(obj) = self.encoding.deref() {
3250 tracer_fn(obj);
3251 }
3252 if let Some(obj) = self.object.deref() {
3253 tracer_fn(obj);
3254 }
3255 if let Some(obj) = self.reason.deref() {
3256 tracer_fn(obj);
3257 }
3258 }
3259 }
3260
3261 fn store_unicode_error(
3262 zelf: &PyObject,
3263 encoding: Option<PyObjectRef>,
3264 object: Option<PyObjectRef>,
3265 start: isize,
3266 end: isize,
3267 reason: Option<PyObjectRef>,
3268 vm: &VirtualMachine,
3269 ) {
3270 let exc: &Py<PyUnicodeError> = zelf
3271 .downcast_ref()
3272 .expect("unicode error subclass has PyUnicodeError payload");
3273 exc.encoding.swap_to_temporary_refs(encoding, vm);
3274 exc.object.swap_to_temporary_refs(object, vm);
3275 exc.start.store(start, Ordering::Relaxed);
3276 exc.end.store(end, Ordering::Relaxed);
3277 exc.reason.swap_to_temporary_refs(reason, vm);
3278 }
3279
3280 #[pyexception(with(Constructor))]
3281 impl PyUnicodeError {}
3282
3283 impl Constructor for PyUnicodeError {
3284 type Args = FuncArgs;
3285
3286 fn py_new(_cls: &Py<PyType>, args: FuncArgs, vm: &VirtualMachine) -> PyResult<Self> {
3287 Ok(Self {
3288 base: PyValueError(PyException(PyBaseException::new(args.args, vm))),
3289 encoding: None.into(),
3290 object: None.into(),
3291 start: AtomicIsize::new(0),
3292 end: AtomicIsize::new(0),
3293 reason: None.into(),
3294 })
3295 }
3296 }
3297
3298 #[pyexception(name, base = PyUnicodeError, ctx = "unicode_decode_error")]
3299 #[derive(Debug)]
3300 #[repr(transparent)]
3301 pub struct PyUnicodeDecodeError(
3302 #[pymember(name = "encoding", path = "encoding", writable)]
3303 #[pymember(name = "object", path = "object", writable)]
3304 #[pymember(name = "start", path = "start", writable)]
3305 #[pymember(name = "end", path = "end", writable)]
3306 #[pymember(name = "reason", path = "reason", writable)]
3307 PyUnicodeError,
3308 );
3309
3310 #[pyexception(with(Initializer))]
3311 impl PyUnicodeDecodeError {
3312 #[pyslot]
3313 fn slot_str(zelf: &PyObject, vm: &VirtualMachine) -> PyResult<PyStrRef> {
3314 let payload = unicode_error_payload(zelf);
3315 if payload.object.deref().is_none() {
3316 return Ok(vm.ctx.empty_str.to_owned());
3317 }
3318 let reason = unicode_cell_str(&payload.reason, vm)?;
3319 let encoding = unicode_cell_str(&payload.encoding, vm)?;
3320 let object: ArgBytesLike =
3321 unicode_error_object(payload, true, vm)?.try_into_value(vm)?;
3322 let start = payload.start.load(Ordering::Relaxed);
3323 let end = payload.end.load(Ordering::Relaxed);
3324 let start_u = usize::try_from(start).ok();
3325 let end_u = usize::try_from(end).ok();
3326 Ok(vm.ctx.new_str(
3327 if let (Some(start_u), Some(end_u)) = (start_u, end_u)
3328 && start_u < object.len()
3329 && end_u <= object.len()
3330 && end_u == start_u + 1
3331 {
3332 let b = object.borrow_buf()[start_u];
3333 format!(
3334 "'{encoding}' codec can't decode byte {b:#04x} in position {start}: {reason}"
3335 )
3336 } else {
3337 format!(
3338 "'{encoding}' codec can't decode bytes in position {start}-{}: {reason}",
3339 end - 1,
3340 )
3341 },
3342 ))
3343 }
3344 }
3345
3346 impl Initializer for PyUnicodeDecodeError {
3347 type Args = FuncArgs;
3348
3349 fn slot_init(zelf: &PyObject, args: FuncArgs, vm: &VirtualMachine) -> PyResult<()> {
3350 parse_tuple_arity(&args, 5, vm)?;
3351 type Args = (PyStrRef, ArgBytesLike, isize, isize, PyStrRef);
3352 let (encoding, object, start, end, reason): Args = args.bind(vm)?;
3353 store_unicode_error(
3354 zelf,
3355 Some(encoding.into()),
3356 Some(vm.ctx.new_bytes(object.borrow_buf().to_vec()).into()),
3357 start,
3358 end,
3359 Some(reason.into()),
3360 vm,
3361 );
3362 Ok(())
3363 }
3364
3365 fn init(_zelf: &Py<Self>, _args: Self::Args, _vm: &VirtualMachine) -> PyResult<()> {
3366 unreachable!("slot_init is defined")
3367 }
3368 }
3369
3370 #[pyexception(name, base = PyUnicodeError, ctx = "unicode_encode_error")]
3371 #[derive(Debug)]
3372 #[repr(transparent)]
3373 pub struct PyUnicodeEncodeError(
3374 #[pymember(name = "encoding", path = "encoding", writable)]
3375 #[pymember(name = "object", path = "object", writable)]
3376 #[pymember(name = "start", path = "start", writable)]
3377 #[pymember(name = "end", path = "end", writable)]
3378 #[pymember(name = "reason", path = "reason", writable)]
3379 PyUnicodeError,
3380 );
3381
3382 #[pyexception(with(Initializer))]
3383 impl PyUnicodeEncodeError {
3384 #[pyslot]
3385 fn slot_str(zelf: &PyObject, vm: &VirtualMachine) -> PyResult<PyStrRef> {
3386 let payload = unicode_error_payload(zelf);
3387 if payload.object.deref().is_none() {
3388 return Ok(vm.ctx.empty_str.to_owned());
3389 }
3390 let reason = unicode_cell_str(&payload.reason, vm)?;
3391 let encoding = unicode_cell_str(&payload.encoding, vm)?;
3392 let object: PyStrRef = unicode_error_object(payload, false, vm)?.try_into_value(vm)?;
3393 let start = payload.start.load(Ordering::Relaxed);
3394 let end = payload.end.load(Ordering::Relaxed);
3395 let start_u = usize::try_from(start).ok();
3396 let end_u = usize::try_from(end).ok();
3397 Ok(vm.ctx.new_str(
3398 if let (Some(start_u), Some(end_u)) = (start_u, end_u)
3399 && start_u < object.char_len()
3400 && end_u <= object.char_len()
3401 && end_u == start_u + 1
3402 {
3403 let ch = object.as_wtf8().code_points().nth(start_u).unwrap();
3404 format!(
3405 "'{encoding}' codec can't encode character '{}' in position {start}: {reason}",
3406 UnicodeEscapeCodepoint(ch)
3407 )
3408 } else {
3409 format!(
3410 "'{encoding}' codec can't encode characters in position {start}-{}: {reason}",
3411 end - 1,
3412 )
3413 },
3414 ))
3415 }
3416 }
3417
3418 impl Initializer for PyUnicodeEncodeError {
3419 type Args = FuncArgs;
3420
3421 fn slot_init(zelf: &PyObject, args: FuncArgs, vm: &VirtualMachine) -> PyResult<()> {
3422 parse_tuple_arity(&args, 5, vm)?;
3423 type Args = (PyStrRef, PyStrRef, isize, isize, PyStrRef);
3424 let (encoding, object, start, end, reason): Args = args.bind(vm)?;
3425 store_unicode_error(
3426 zelf,
3427 Some(encoding.into()),
3428 Some(object.into()),
3429 start,
3430 end,
3431 Some(reason.into()),
3432 vm,
3433 );
3434 Ok(())
3435 }
3436
3437 fn init(_zelf: &Py<Self>, _args: Self::Args, _vm: &VirtualMachine) -> PyResult<()> {
3438 unreachable!("slot_init is defined")
3439 }
3440 }
3441
3442 #[pyexception(name, base = PyUnicodeError, ctx = "unicode_translate_error")]
3443 #[derive(Debug)]
3444 #[repr(transparent)]
3445 pub struct PyUnicodeTranslateError(
3446 #[pymember(name = "encoding", path = "encoding", writable)]
3447 #[pymember(name = "object", path = "object", writable)]
3448 #[pymember(name = "start", path = "start", writable)]
3449 #[pymember(name = "end", path = "end", writable)]
3450 #[pymember(name = "reason", path = "reason", writable)]
3451 PyUnicodeError,
3452 );
3453
3454 #[pyexception(with(Initializer))]
3455 impl PyUnicodeTranslateError {
3456 #[pyslot]
3457 fn slot_str(zelf: &PyObject, vm: &VirtualMachine) -> PyResult<PyStrRef> {
3458 let payload = unicode_error_payload(zelf);
3459 if payload.object.deref().is_none() {
3460 return Ok(vm.ctx.empty_str.to_owned());
3461 }
3462 let reason = unicode_cell_str(&payload.reason, vm)?;
3463 let object: PyStrRef = unicode_error_object(payload, false, vm)?.try_into_value(vm)?;
3464 let start = payload.start.load(Ordering::Relaxed);
3465 let end = payload.end.load(Ordering::Relaxed);
3466 let start_u = usize::try_from(start).ok();
3467 let end_u = usize::try_from(end).ok();
3468 Ok(vm.ctx.new_str(
3469 if let (Some(start_u), Some(end_u)) = (start_u, end_u)
3470 && start_u < object.char_len()
3471 && end_u <= object.char_len()
3472 && end_u == start_u + 1
3473 {
3474 let ch = object.as_wtf8().code_points().nth(start_u).unwrap();
3475 format!(
3476 "can't translate character '{}' in position {start}: {reason}",
3477 UnicodeEscapeCodepoint(ch)
3478 )
3479 } else {
3480 format!(
3481 "can't translate characters in position {start}-{}: {reason}",
3482 end - 1,
3483 )
3484 },
3485 ))
3486 }
3487 }
3488
3489 impl Initializer for PyUnicodeTranslateError {
3490 type Args = FuncArgs;
3491
3492 fn slot_init(zelf: &PyObject, args: FuncArgs, vm: &VirtualMachine) -> PyResult<()> {
3493 parse_tuple_arity(&args, 4, vm)?;
3494 type Args = (PyStrRef, isize, isize, PyStrRef);
3495 let (object, start, end, reason): Args = args.bind(vm)?;
3496 store_unicode_error(
3497 zelf,
3498 None,
3499 Some(object.into()),
3500 start,
3501 end,
3502 Some(reason.into()),
3503 vm,
3504 );
3505 Ok(())
3506 }
3507
3508 fn init(_zelf: &Py<Self>, _args: Self::Args, _vm: &VirtualMachine) -> PyResult<()> {
3509 unreachable!("slot_init is defined")
3510 }
3511 }
3512
3513 #[cfg(feature = "jit")]
3515 #[pyexception(name, base = PyException, ctx = "jit_error", impl)]
3516 #[derive(Debug)]
3517 #[repr(transparent)]
3518 pub struct PyJitError(PyException);
3519
3520 #[pyexception(name, base = PyException, ctx = "warning", impl)]
3522 #[derive(Debug)]
3523 #[repr(transparent)]
3524 pub struct PyWarning(PyException);
3525
3526 #[pyexception(name, base = PyWarning, ctx = "deprecation_warning", impl)]
3527 #[derive(Debug)]
3528 #[repr(transparent)]
3529 pub struct PyDeprecationWarning(PyWarning);
3530
3531 #[pyexception(name, base = PyWarning, ctx = "pending_deprecation_warning", impl)]
3532 #[derive(Debug)]
3533 #[repr(transparent)]
3534 pub struct PyPendingDeprecationWarning(PyWarning);
3535
3536 #[pyexception(name, base = PyWarning, ctx = "runtime_warning", impl)]
3537 #[derive(Debug)]
3538 #[repr(transparent)]
3539 pub struct PyRuntimeWarning(PyWarning);
3540
3541 #[pyexception(name, base = PyWarning, ctx = "syntax_warning", impl)]
3542 #[derive(Debug)]
3543 #[repr(transparent)]
3544 pub struct PySyntaxWarning(PyWarning);
3545
3546 #[pyexception(name, base = PyWarning, ctx = "user_warning", impl)]
3547 #[derive(Debug)]
3548 #[repr(transparent)]
3549 pub struct PyUserWarning(PyWarning);
3550
3551 #[pyexception(name, base = PyWarning, ctx = "future_warning", impl)]
3552 #[derive(Debug)]
3553 #[repr(transparent)]
3554 pub struct PyFutureWarning(PyWarning);
3555
3556 #[pyexception(name, base = PyWarning, ctx = "import_warning", impl)]
3557 #[derive(Debug)]
3558 #[repr(transparent)]
3559 pub struct PyImportWarning(PyWarning);
3560
3561 #[pyexception(name, base = PyWarning, ctx = "unicode_warning", impl)]
3562 #[derive(Debug)]
3563 #[repr(transparent)]
3564 pub struct PyUnicodeWarning(PyWarning);
3565
3566 #[pyexception(name, base = PyWarning, ctx = "bytes_warning", impl)]
3567 #[derive(Debug)]
3568 #[repr(transparent)]
3569 pub struct PyBytesWarning(PyWarning);
3570
3571 #[pyexception(name, base = PyWarning, ctx = "resource_warning", impl)]
3572 #[derive(Debug)]
3573 #[repr(transparent)]
3574 pub struct PyResourceWarning(PyWarning);
3575
3576 #[pyexception(name, base = PyWarning, ctx = "encoding_warning", impl)]
3577 #[derive(Debug)]
3578 #[repr(transparent)]
3579 pub struct PyEncodingWarning(PyWarning);
3580}
3581
3582fn check_except_star_type_valid(match_type: &PyObject, vm: &VirtualMachine) -> PyResult<()> {
3584 let base_exc: PyObjectRef = vm.ctx.exceptions.base_exception_type.to_owned().into();
3585 let base_eg: PyObjectRef = vm.ctx.exceptions.base_exception_group.to_owned().into();
3586
3587 let check_one = |exc_type: &PyObject| -> PyResult<()> {
3589 if !exc_type.is_subclass(&base_exc, vm)? {
3591 return Err(vm.new_type_error(
3592 "catching classes that do not inherit from BaseException is not allowed",
3593 ));
3594 }
3595 if exc_type.is_subclass(&base_eg, vm)? {
3597 return Err(vm.new_type_error(
3598 "catching ExceptionGroup with except* is not allowed. Use except instead.",
3599 ));
3600 }
3601 Ok(())
3602 };
3603
3604 if let Ok(tuple) = match_type.to_owned().downcast::<PyTuple>() {
3606 for item in tuple.as_slice() {
3607 check_one(item)?;
3608 }
3609 } else {
3610 check_one(match_type)?;
3611 }
3612 Ok(())
3613}
3614
3615pub fn exception_group_match(
3618 exc_value: &PyObject,
3619 match_type: &PyObject,
3620 vm: &VirtualMachine,
3621) -> PyResult<(PyObjectRef, PyObjectRef)> {
3622 if vm.is_none(exc_value) {
3626 return Ok((vm.ctx.none(), vm.ctx.none()));
3627 }
3628
3629 check_except_star_type_valid(match_type, vm)?;
3631
3632 if exc_value.is_instance(match_type, vm)? {
3634 let is_eg = exc_value.fast_isinstance(vm.ctx.exceptions.base_exception_group);
3636 let matched = if is_eg {
3637 exc_value.to_owned()
3638 } else {
3639 let excs = vm.ctx.new_tuple(vec![exc_value.to_owned()]);
3641 let eg_type: PyObjectRef = crate::exception_group::exception_group().to_owned().into();
3642 let wrapped = eg_type.call((vm.ctx.new_str(""), excs), vm)?;
3643 if let Ok(exc) = exc_value.to_owned().downcast::<types::PyBaseException>()
3645 && let Some(tb) = exc.traceback()
3646 && let Ok(wrapped_exc) = wrapped.clone().downcast::<types::PyBaseException>()
3647 {
3648 wrapped_exc.set_traceback(Some(tb));
3649 }
3650 wrapped
3651 };
3652 return Ok((vm.ctx.none(), matched));
3653 }
3654
3655 if exc_value.fast_isinstance(vm.ctx.exceptions.base_exception_group) {
3657 let pair = vm.call_method(exc_value, "split", (match_type.to_owned(),))?;
3658 if !pair.class().is(vm.ctx.types.tuple_type) {
3659 return Err(vm.new_type_error(format!(
3660 "{}.split must return a tuple, not {}",
3661 exc_value.class().name(),
3662 pair.class().name()
3663 )));
3664 }
3665 let pair_tuple: PyTupleRef = pair.try_into_value(vm)?;
3666 if pair_tuple.as_slice().len() < 2 {
3667 return Err(vm.new_type_error(format!(
3668 "{}.split must return a 2-tuple, got tuple of size {}",
3669 exc_value.class().name(),
3670 pair_tuple.as_slice().len()
3671 )));
3672 }
3673 let matched = pair_tuple.as_slice()[0].clone();
3674 let rest = pair_tuple.as_slice()[1].clone();
3675 return Ok((rest, matched));
3676 }
3677
3678 Ok((exc_value.to_owned(), vm.ctx.none()))
3680}
3681
3682pub fn prep_reraise_star(orig: &PyObject, excs: &PyObject, vm: &VirtualMachine) -> PyResult {
3685 use crate::builtins::PyList;
3686
3687 let excs_list = excs
3688 .downcast_ref::<PyList>()
3689 .ok_or_else(|| vm.new_type_error("expected list for prep_reraise_star"))?;
3690
3691 let excs_vec: Vec<PyObjectRef> = excs_list.borrow_vec().to_vec();
3692
3693 if excs_vec.is_empty() {
3695 return Ok(vm.ctx.none());
3696 }
3697
3698 if !orig.fast_isinstance(vm.ctx.exceptions.base_exception_group) {
3701 let first = excs_vec.into_iter().find(|e| !vm.is_none(e));
3703 return Ok(first.unwrap_or_else(|| vm.ctx.none()));
3704 }
3705
3706 let mut raised: Vec<PyObjectRef> = Vec::new();
3708 let mut reraised: Vec<PyObjectRef> = Vec::new();
3709
3710 for exc in excs_vec {
3711 if vm.is_none(&exc) {
3712 continue;
3713 }
3714 if is_exception_from_orig(&exc, orig, vm) {
3716 reraised.push(exc);
3717 } else {
3718 raised.push(exc);
3719 }
3720 }
3721
3722 if raised.is_empty() && reraised.is_empty() {
3724 return Ok(vm.ctx.none());
3725 }
3726
3727 let reraised_eg = exception_group_projection(orig, &reraised, vm)?;
3729
3730 if raised.is_empty() {
3732 return Ok(reraised_eg);
3733 }
3734
3735 if !vm.is_none(&reraised_eg) {
3737 raised.push(reraised_eg);
3738 }
3739
3740 if raised.len() == 1 {
3742 return Ok(raised.into_iter().next().unwrap());
3743 }
3744
3745 let excs_tuple = vm.ctx.new_tuple(raised);
3747 let eg_type: PyObjectRef = crate::exception_group::exception_group().to_owned().into();
3748 eg_type.call((vm.ctx.new_str(""), excs_tuple), vm)
3749}
3750
3751fn is_exception_from_orig(exc: &PyObject, orig: &PyObject, vm: &VirtualMachine) -> bool {
3755 let mut exc_leaf_ids = HashSet::new();
3757 collect_exception_group_leaf_ids(exc, &mut exc_leaf_ids, vm);
3758
3759 if exc_leaf_ids.is_empty() {
3760 return false;
3761 }
3762
3763 let mut orig_leaf_ids = HashSet::new();
3765 collect_exception_group_leaf_ids(orig, &mut orig_leaf_ids, vm);
3766
3767 exc_leaf_ids.iter().all(|id| orig_leaf_ids.contains(id))
3769}
3770
3771fn collect_exception_group_leaf_ids(
3773 exc: &PyObject,
3774 leaf_ids: &mut HashSet<usize>,
3775 vm: &VirtualMachine,
3776) {
3777 if vm.is_none(exc) {
3778 return;
3779 }
3780
3781 if !exc.fast_isinstance(vm.ctx.exceptions.base_exception_group) {
3783 leaf_ids.insert(exc.get_id());
3784 return;
3785 }
3786
3787 if let Ok(excs_attr) = exc.get_attr("exceptions", vm)
3789 && let Ok(tuple) = excs_attr.downcast::<PyTuple>()
3790 {
3791 for e in tuple.as_slice() {
3792 collect_exception_group_leaf_ids(e, leaf_ids, vm);
3793 }
3794 }
3795}
3796
3797fn exception_group_projection(
3801 orig: &PyObject,
3802 keep: &[PyObjectRef],
3803 vm: &VirtualMachine,
3804) -> PyResult {
3805 if keep.is_empty() {
3806 return Ok(vm.ctx.none());
3807 }
3808
3809 let mut leaf_ids = HashSet::new();
3811 for e in keep {
3812 collect_exception_group_leaf_ids(e, &mut leaf_ids, vm);
3813 }
3814
3815 split_by_leaf_ids(orig, &leaf_ids, vm)
3817}
3818
3819fn split_by_leaf_ids(exc: &PyObject, leaf_ids: &HashSet<usize>, vm: &VirtualMachine) -> PyResult {
3822 if vm.is_none(exc) {
3823 return Ok(vm.ctx.none());
3824 }
3825
3826 if !exc.fast_isinstance(vm.ctx.exceptions.base_exception_group) {
3828 if leaf_ids.contains(&exc.get_id()) {
3829 return Ok(exc.to_owned());
3830 }
3831 return Ok(vm.ctx.none());
3832 }
3833
3834 let excs_attr = exc.get_attr("exceptions", vm)?;
3836 let tuple: PyTupleRef = excs_attr.try_into_value(vm)?;
3837
3838 let mut matched = Vec::new();
3839 for e in tuple.as_slice() {
3840 let m = split_by_leaf_ids(e, leaf_ids, vm)?;
3841 if !vm.is_none(&m) {
3842 matched.push(m);
3843 }
3844 }
3845
3846 if matched.is_empty() {
3847 return Ok(vm.ctx.none());
3848 }
3849
3850 let group = exc
3851 .downcast_ref::<crate::exception_group::types::PyBaseExceptionGroup>()
3852 .ok_or_else(|| vm.new_type_error("expected a BaseExceptionGroup"))?;
3853 crate::exception_group::types::derive_and_copy_attributes(group, matched, vm)
3854}