1use self::types::{PyBaseException, PyBaseExceptionRef};
2use crate::common::lock::PyRwLock;
3use crate::object::{Traverse, TraverseFn};
4use crate::{
5 AsObject, Context, Py, PyObjectRef, PyPayload, PyRef, PyResult, TryFromObject, VirtualMachine,
6 builtins::{
7 PyList, PyNone, PyStr, PyStrRef, PyTuple, PyTupleRef, PyType, PyTypeRef,
8 traceback::{PyTraceback, PyTracebackRef},
9 },
10 class::{PyClassImpl, StaticType},
11 convert::{IntoPyException, ToPyException, ToPyObject},
12 function::{ArgIterable, FuncArgs, IntoFuncArgs, PySetterValue},
13 py_io::{self, Write},
14 stdlib::sys,
15 suggestion::offer_suggestions,
16 types::{Callable, Constructor, Initializer, Representable},
17};
18use crossbeam_utils::atomic::AtomicCell;
19use itertools::Itertools;
20use std::{
21 collections::HashSet,
22 io::{self, BufRead, BufReader},
23};
24
25pub use super::exception_group::exception_group;
26
27unsafe impl Traverse for PyBaseException {
28 fn traverse(&self, tracer_fn: &mut TraverseFn<'_>) {
29 self.traceback.traverse(tracer_fn);
30 self.cause.traverse(tracer_fn);
31 self.context.traverse(tracer_fn);
32 self.args.traverse(tracer_fn);
33 }
34}
35
36impl core::fmt::Debug for PyBaseException {
37 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
38 f.write_str("PyBaseException")
40 }
41}
42
43impl PyPayload for PyBaseException {
44 #[inline]
45 fn class(ctx: &Context) -> &'static Py<PyType> {
46 ctx.exceptions.base_exception_type
47 }
48}
49
50impl VirtualMachine {
51 pub fn print_exception(&self, exc: PyBaseExceptionRef) {
56 let vm = self;
57 let write_fallback = |exc, errstr| {
58 if let Ok(stderr) = sys::get_stderr(vm) {
59 let mut stderr = py_io::PyWriter(stderr, vm);
60 let _ = writeln!(stderr, "{errstr}");
62 let _ = self.write_exception(&mut stderr, exc);
63 } else {
64 eprintln!("{errstr}\nlost sys.stderr");
65 let _ = self.write_exception(&mut py_io::IoWriter(io::stderr()), exc);
66 }
67 };
68 if let Ok(excepthook) = vm.sys_module.get_attr("excepthook", vm) {
69 let (exc_type, exc_val, exc_tb) = vm.split_exception(exc.clone());
70 if let Err(eh_exc) = excepthook.call((exc_type, exc_val, exc_tb), vm) {
71 write_fallback(&eh_exc, "Error in sys.excepthook:");
72 write_fallback(&exc, "Original exception was:");
73 }
74 } else {
75 write_fallback(&exc, "missing sys.excepthook");
76 }
77 }
78
79 pub fn write_exception<W: Write>(
80 &self,
81 output: &mut W,
82 exc: &Py<PyBaseException>,
83 ) -> Result<(), W::Error> {
84 let seen = &mut HashSet::<usize>::new();
85 self.write_exception_recursive(output, exc, seen)
86 }
87
88 fn write_exception_recursive<W: Write>(
89 &self,
90 output: &mut W,
91 exc: &Py<PyBaseException>,
92 seen: &mut HashSet<usize>,
93 ) -> Result<(), W::Error> {
94 seen.insert(exc.get_id());
98
99 #[allow(clippy::manual_map)]
100 if let Some((cause_or_context, msg)) = if let Some(cause) = exc.__cause__() {
101 Some((
104 cause,
105 "\nThe above exception was the direct cause of the following exception:\n",
106 ))
107 } else if let Some(context) = exc.__context__() {
108 Some((
114 context,
115 "\nDuring handling of the above exception, another exception occurred:\n",
116 ))
117 } else {
118 None
119 } {
120 if !seen.contains(&cause_or_context.get_id()) {
121 self.write_exception_recursive(output, &cause_or_context, seen)?;
122 writeln!(output, "{msg}")?;
123 } else {
124 seen.insert(cause_or_context.get_id());
125 }
126 }
127
128 self.write_exception_inner(output, exc)
129 }
130
131 pub fn write_exception_inner<W: Write>(
133 &self,
134 output: &mut W,
135 exc: &Py<PyBaseException>,
136 ) -> Result<(), W::Error> {
137 let vm = self;
138 if let Some(tb) = exc.traceback.read().clone() {
139 writeln!(output, "Traceback (most recent call last):")?;
140 for tb in tb.iter() {
141 write_traceback_entry(output, &tb)?;
142 }
143 }
144
145 let varargs = exc.args();
146 let args_repr = vm.exception_args_as_string(varargs, true);
147
148 let exc_class = exc.class();
149
150 if exc_class.fast_issubclass(vm.ctx.exceptions.syntax_error) {
151 return self.write_syntaxerror(output, exc, exc_class, &args_repr);
152 }
153
154 let exc_name = exc_class.name();
155 match args_repr.len() {
156 0 => write!(output, "{exc_name}"),
157 1 => write!(output, "{}: {}", exc_name, args_repr[0]),
158 _ => write!(
159 output,
160 "{}: ({})",
161 exc_name,
162 args_repr.into_iter().format(", "),
163 ),
164 }?;
165
166 match offer_suggestions(exc, vm) {
167 Some(suggestions) => writeln!(output, ". Did you mean: '{suggestions}'?"),
168 None => writeln!(output),
169 }
170 }
171
172 fn write_syntaxerror<W: Write>(
178 &self,
179 output: &mut W,
180 exc: &Py<PyBaseException>,
181 exc_type: &Py<PyType>,
182 args_repr: &[PyRef<PyStr>],
183 ) -> Result<(), W::Error> {
184 let vm = self;
185 debug_assert!(exc_type.fast_issubclass(vm.ctx.exceptions.syntax_error));
186
187 let getattr = |attr: &'static str| exc.as_object().get_attr(attr, vm).ok();
188
189 let maybe_lineno = getattr("lineno").map(|obj| {
190 obj.str(vm)
191 .unwrap_or_else(|_| vm.ctx.new_str("<lineno str() failed>"))
192 });
193 let maybe_filename = getattr("filename").and_then(|obj| obj.str(vm).ok());
194
195 let maybe_text = getattr("text").map(|obj| {
196 obj.str(vm)
197 .unwrap_or_else(|_| vm.ctx.new_str("<text str() failed>"))
198 });
199
200 let mut filename_suffix = String::new();
201
202 if let Some(lineno) = maybe_lineno {
203 let filename = match maybe_filename {
204 Some(filename) => filename,
205 None => vm.ctx.new_str("<string>"),
206 };
207 writeln!(output, r##" File "{filename}", line {lineno}"##,)?;
208 } else if let Some(filename) = maybe_filename {
209 filename_suffix = format!(" ({filename})");
210 }
211
212 if let Some(text) = maybe_text {
213 use rustpython_common::wtf8::CodePoint;
215 let text_wtf8 = text.as_wtf8();
216 let r_text = text_wtf8.trim_end_matches(|cp: CodePoint| {
217 cp == CodePoint::from_char('\n') || cp == CodePoint::from_char('\r')
218 });
219 let l_text = r_text.trim_start_matches(|cp: CodePoint| {
220 cp == CodePoint::from_char(' ')
221 || cp == CodePoint::from_char('\n')
222 || cp == CodePoint::from_char('\x0c') });
224 let spaces = (r_text.len() - l_text.len()) as isize;
225
226 writeln!(output, " {l_text}")?;
227
228 let maybe_offset: Option<isize> =
229 getattr("offset").and_then(|obj| obj.try_to_value::<isize>(vm).ok());
230
231 if let Some(offset) = maybe_offset {
232 let maybe_end_offset: Option<isize> =
233 getattr("end_offset").and_then(|obj| obj.try_to_value::<isize>(vm).ok());
234 let maybe_end_lineno: Option<isize> =
235 getattr("end_lineno").and_then(|obj| obj.try_to_value::<isize>(vm).ok());
236 let maybe_lineno_int: Option<isize> =
237 getattr("lineno").and_then(|obj| obj.try_to_value::<isize>(vm).ok());
238
239 let same_line = match (maybe_lineno_int, maybe_end_lineno) {
241 (Some(lineno), Some(end_lineno)) => lineno == end_lineno,
242 _ => true,
243 };
244
245 if same_line {
246 let mut end_offset = match maybe_end_offset {
247 Some(0) | None => offset,
248 Some(end_offset) => end_offset,
249 };
250
251 if offset == end_offset || end_offset == -1 {
252 end_offset = offset + 1;
253 }
254
255 let colno = offset - 1 - spaces;
257 let end_colno = end_offset - 1 - spaces;
258 if colno >= 0 {
259 let caret_space = l_text
260 .code_points()
261 .take(colno as usize)
262 .map(|cp| cp.to_char().filter(|c| c.is_whitespace()).unwrap_or(' '))
263 .collect::<String>();
264
265 let mut error_width = end_colno - colno;
266 if error_width < 1 {
267 error_width = 1;
268 }
269
270 writeln!(
271 output,
272 " {}{}",
273 caret_space,
274 "^".repeat(error_width as usize)
275 )?;
276 }
277 }
278 }
279 }
280
281 let exc_name = exc_type.name();
282
283 match args_repr.len() {
284 0 => write!(output, "{exc_name}{filename_suffix}"),
285 1 => write!(output, "{}: {}{}", exc_name, args_repr[0], filename_suffix),
286 _ => write!(
287 output,
288 "{}: ({}){}",
289 exc_name,
290 args_repr.iter().format(", "),
291 filename_suffix
292 ),
293 }?;
294
295 match offer_suggestions(exc, vm) {
296 Some(suggestions) => writeln!(output, ". Did you mean: '{suggestions}'?"),
297 None => writeln!(output),
298 }
299 }
300
301 fn exception_args_as_string(&self, varargs: PyTupleRef, str_single: bool) -> Vec<PyStrRef> {
302 let vm = self;
303 match varargs.len() {
304 0 => vec![],
305 1 => {
306 let args0_repr = if str_single {
307 varargs[0]
308 .str(vm)
309 .unwrap_or_else(|_| PyStr::from("<element str() failed>").into_ref(&vm.ctx))
310 } else {
311 varargs[0].repr(vm).unwrap_or_else(|_| {
312 PyStr::from("<element repr() failed>").into_ref(&vm.ctx)
313 })
314 };
315 vec![args0_repr]
316 }
317 _ => varargs
318 .iter()
319 .map(|vararg| {
320 vararg.repr(vm).unwrap_or_else(|_| {
321 PyStr::from("<element repr() failed>").into_ref(&vm.ctx)
322 })
323 })
324 .collect(),
325 }
326 }
327
328 pub fn split_exception(
329 &self,
330 exc: PyBaseExceptionRef,
331 ) -> (PyObjectRef, PyObjectRef, PyObjectRef) {
332 let tb = exc.__traceback__().to_pyobject(self);
333 let class = exc.class().to_owned();
334 (class.into(), exc.into(), tb)
335 }
336
337 pub fn normalize_exception(
339 &self,
340 exc_type: PyObjectRef,
341 exc_val: PyObjectRef,
342 exc_tb: PyObjectRef,
343 ) -> PyResult<PyBaseExceptionRef> {
344 let ctor = ExceptionCtor::try_from_object(self, exc_type)?;
345 let exc = ctor.instantiate_value(exc_val, self)?;
346 if let Some(tb) = Option::<PyTracebackRef>::try_from_object(self, exc_tb)? {
347 exc.set_traceback_typed(Some(tb));
348 }
349 Ok(exc)
350 }
351
352 pub fn invoke_exception(
353 &self,
354 cls: PyTypeRef,
355 args: Vec<PyObjectRef>,
356 ) -> PyResult<PyBaseExceptionRef> {
357 let res = PyType::call(&cls, args.into_args(self), self)?;
359 res.downcast::<PyBaseException>().map_err(|obj| {
360 self.new_type_error(format!(
361 "calling {} should have returned an instance of BaseException, not {}",
362 cls,
363 obj.class()
364 ))
365 })
366 }
367}
368
369fn print_source_line<W: Write>(
370 output: &mut W,
371 filename: &str,
372 lineno: usize,
373) -> Result<(), W::Error> {
374 let file = match std::fs::File::open(filename) {
377 Ok(file) => file,
378 Err(_) => return Ok(()),
379 };
380 let file = BufReader::new(file);
381
382 for (i, line) in file.lines().enumerate() {
383 if i + 1 == lineno {
384 if let Ok(line) = line {
385 writeln!(output, " {}", line.trim_start())?;
387 }
388 return Ok(());
389 }
390 }
391
392 Ok(())
393}
394
395fn write_traceback_entry<W: Write>(
397 output: &mut W,
398 tb_entry: &Py<PyTraceback>,
399) -> Result<(), W::Error> {
400 let filename = tb_entry.frame.code.source_path().as_str();
401 writeln!(
402 output,
403 r##" File "{}", line {}, in {}"##,
404 filename.trim_start_matches(r"\\?\"),
405 tb_entry.lineno,
406 tb_entry.frame.code.obj_name
407 )?;
408 print_source_line(output, filename, tb_entry.lineno.get())?;
409
410 Ok(())
411}
412
413#[derive(Clone)]
414pub enum ExceptionCtor {
415 Class(PyTypeRef),
416 Instance(PyBaseExceptionRef),
417}
418
419impl TryFromObject for ExceptionCtor {
420 fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult<Self> {
421 obj.downcast::<PyType>()
422 .and_then(|cls| {
423 if cls.fast_issubclass(vm.ctx.exceptions.base_exception_type) {
424 Ok(Self::Class(cls))
425 } else {
426 Err(cls.into())
427 }
428 })
429 .or_else(|obj| obj.downcast::<PyBaseException>().map(Self::Instance))
430 .map_err(|obj| {
431 vm.new_type_error(format!(
432 "exceptions must be classes or instances deriving from BaseException, not {}",
433 obj.class().name()
434 ))
435 })
436 }
437}
438
439impl ExceptionCtor {
440 pub fn instantiate(self, vm: &VirtualMachine) -> PyResult<PyBaseExceptionRef> {
441 match self {
442 Self::Class(cls) => vm.invoke_exception(cls, vec![]),
443 Self::Instance(exc) => Ok(exc),
444 }
445 }
446
447 pub fn instantiate_value(
448 self,
449 value: PyObjectRef,
450 vm: &VirtualMachine,
451 ) -> PyResult<PyBaseExceptionRef> {
452 let exc_inst = value.clone().downcast::<PyBaseException>().ok();
453 match (self, exc_inst) {
454 (Self::Instance(_exc_a), Some(_exc_b)) => {
456 Err(vm.new_type_error("instance exception may not have a separate value"))
457 }
458 (Self::Instance(exc), None) => Ok(exc),
460 (Self::Class(cls), Some(exc)) if exc.fast_isinstance(&cls) => Ok(exc),
462 (Self::Class(cls), _) => {
464 let args = match_class!(match value {
465 PyNone => vec![],
466 tup @ PyTuple => tup.to_vec(),
467 exc @ PyBaseException => exc.args().to_vec(),
468 obj => vec![obj],
469 });
470 vm.invoke_exception(cls, args)
471 }
472 }
473 }
474}
475
476#[derive(Debug)]
477pub struct ExceptionZoo {
478 pub base_exception_type: &'static Py<PyType>,
479 pub base_exception_group: &'static Py<PyType>,
480 pub system_exit: &'static Py<PyType>,
481 pub keyboard_interrupt: &'static Py<PyType>,
482 pub generator_exit: &'static Py<PyType>,
483 pub exception_type: &'static Py<PyType>,
484 pub stop_iteration: &'static Py<PyType>,
485 pub stop_async_iteration: &'static Py<PyType>,
486 pub arithmetic_error: &'static Py<PyType>,
487 pub floating_point_error: &'static Py<PyType>,
488 pub overflow_error: &'static Py<PyType>,
489 pub zero_division_error: &'static Py<PyType>,
490 pub assertion_error: &'static Py<PyType>,
491 pub attribute_error: &'static Py<PyType>,
492 pub buffer_error: &'static Py<PyType>,
493 pub eof_error: &'static Py<PyType>,
494 pub import_error: &'static Py<PyType>,
495 pub module_not_found_error: &'static Py<PyType>,
496 pub lookup_error: &'static Py<PyType>,
497 pub index_error: &'static Py<PyType>,
498 pub key_error: &'static Py<PyType>,
499 pub memory_error: &'static Py<PyType>,
500 pub name_error: &'static Py<PyType>,
501 pub unbound_local_error: &'static Py<PyType>,
502 pub os_error: &'static Py<PyType>,
503 pub blocking_io_error: &'static Py<PyType>,
504 pub child_process_error: &'static Py<PyType>,
505 pub connection_error: &'static Py<PyType>,
506 pub broken_pipe_error: &'static Py<PyType>,
507 pub connection_aborted_error: &'static Py<PyType>,
508 pub connection_refused_error: &'static Py<PyType>,
509 pub connection_reset_error: &'static Py<PyType>,
510 pub file_exists_error: &'static Py<PyType>,
511 pub file_not_found_error: &'static Py<PyType>,
512 pub interrupted_error: &'static Py<PyType>,
513 pub is_a_directory_error: &'static Py<PyType>,
514 pub not_a_directory_error: &'static Py<PyType>,
515 pub permission_error: &'static Py<PyType>,
516 pub process_lookup_error: &'static Py<PyType>,
517 pub timeout_error: &'static Py<PyType>,
518 pub reference_error: &'static Py<PyType>,
519 pub runtime_error: &'static Py<PyType>,
520 pub not_implemented_error: &'static Py<PyType>,
521 pub recursion_error: &'static Py<PyType>,
522 pub python_finalization_error: &'static Py<PyType>,
523 pub syntax_error: &'static Py<PyType>,
524 pub incomplete_input_error: &'static Py<PyType>,
525 pub indentation_error: &'static Py<PyType>,
526 pub tab_error: &'static Py<PyType>,
527 pub system_error: &'static Py<PyType>,
528 pub type_error: &'static Py<PyType>,
529 pub value_error: &'static Py<PyType>,
530 pub unicode_error: &'static Py<PyType>,
531 pub unicode_decode_error: &'static Py<PyType>,
532 pub unicode_encode_error: &'static Py<PyType>,
533 pub unicode_translate_error: &'static Py<PyType>,
534
535 #[cfg(feature = "jit")]
536 pub jit_error: &'static Py<PyType>,
537
538 pub warning: &'static Py<PyType>,
539 pub deprecation_warning: &'static Py<PyType>,
540 pub pending_deprecation_warning: &'static Py<PyType>,
541 pub runtime_warning: &'static Py<PyType>,
542 pub syntax_warning: &'static Py<PyType>,
543 pub user_warning: &'static Py<PyType>,
544 pub future_warning: &'static Py<PyType>,
545 pub import_warning: &'static Py<PyType>,
546 pub unicode_warning: &'static Py<PyType>,
547 pub bytes_warning: &'static Py<PyType>,
548 pub resource_warning: &'static Py<PyType>,
549 pub encoding_warning: &'static Py<PyType>,
550}
551
552macro_rules! extend_exception {
553 (
554 $exc_struct:ident,
555 $ctx:expr,
556 $class:expr
557 ) => {
558 extend_exception!($exc_struct, $ctx, $class, {});
559 };
560 (
561 $exc_struct:ident,
562 $ctx:expr,
563 $class:expr,
564 { $($name:expr => $value:expr),* $(,)* }
565 ) => {
566 $exc_struct::extend_class($ctx, $class);
567 extend_class!($ctx, $class, {
568 $($name => $value,)*
569 });
570 };
571}
572
573impl PyBaseException {
574 pub(crate) fn new(args: Vec<PyObjectRef>, vm: &VirtualMachine) -> Self {
575 Self {
576 traceback: PyRwLock::new(None),
577 cause: PyRwLock::new(None),
578 context: PyRwLock::new(None),
579 suppress_context: AtomicCell::new(false),
580 args: PyRwLock::new(PyTuple::new_ref(args, &vm.ctx)),
581 }
582 }
583
584 pub fn get_arg(&self, idx: usize) -> Option<PyObjectRef> {
585 self.args.read().get(idx).cloned()
586 }
587}
588
589#[pyclass(
590 with(Py, PyRef, Constructor, Initializer, Representable),
591 flags(BASETYPE, HAS_DICT)
592)]
593impl PyBaseException {
594 #[pygetset]
595 pub fn args(&self) -> PyTupleRef {
596 self.args.read().clone()
597 }
598
599 #[pygetset(setter)]
600 fn set_args(&self, args: ArgIterable, vm: &VirtualMachine) -> PyResult<()> {
601 let args = args.iter(vm)?.collect::<PyResult<Vec<_>>>()?;
602 *self.args.write() = PyTuple::new_ref(args, &vm.ctx);
603 Ok(())
604 }
605
606 #[pygetset]
607 pub fn __traceback__(&self) -> Option<PyTracebackRef> {
608 self.traceback.read().clone()
609 }
610
611 #[pygetset(setter)]
612 pub fn set___traceback__(&self, value: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> {
613 let traceback = if vm.is_none(&value) {
614 None
615 } else {
616 match value.downcast::<PyTraceback>() {
617 Ok(tb) => Some(tb),
618 Err(_) => {
619 return Err(vm.new_type_error("__traceback__ must be a traceback or None"));
620 }
621 }
622 };
623 self.set_traceback_typed(traceback);
624 Ok(())
625 }
626
627 pub(crate) fn set_traceback_typed(&self, traceback: Option<PyTracebackRef>) {
629 *self.traceback.write() = traceback;
630 }
631
632 #[pygetset]
633 pub fn __cause__(&self) -> Option<PyRef<Self>> {
634 self.cause.read().clone()
635 }
636
637 #[pygetset(setter)]
638 pub fn set___cause__(&self, cause: Option<PyRef<Self>>) {
639 let mut c = self.cause.write();
640 self.set_suppress_context(true);
641 *c = cause;
642 }
643
644 #[pygetset]
645 pub fn __context__(&self) -> Option<PyRef<Self>> {
646 self.context.read().clone()
647 }
648
649 #[pygetset(setter)]
650 pub fn set___context__(&self, context: Option<PyRef<Self>>) {
651 *self.context.write() = context;
652 }
653
654 #[pygetset]
655 pub(super) fn __suppress_context__(&self) -> bool {
656 self.suppress_context.load()
657 }
658
659 #[pygetset(name = "__suppress_context__", setter)]
660 fn set_suppress_context(&self, suppress_context: bool) {
661 self.suppress_context.store(suppress_context);
662 }
663}
664
665#[pyclass]
666impl Py<PyBaseException> {
667 #[pymethod]
668 pub(super) fn __str__(&self, vm: &VirtualMachine) -> PyResult<PyStrRef> {
669 let str_args = vm.exception_args_as_string(self.args(), true);
670 Ok(match str_args.into_iter().exactly_one() {
671 Err(i) if i.len() == 0 => vm.ctx.empty_str.to_owned(),
672 Ok(s) => s,
673 Err(i) => PyStr::from(format!("({})", i.format(", "))).into_ref(&vm.ctx),
674 })
675 }
676}
677
678#[pyclass]
679impl PyRef<PyBaseException> {
680 #[pymethod]
681 fn with_traceback(self, tb: Option<PyTracebackRef>) -> PyResult<Self> {
682 *self.traceback.write() = tb;
683 Ok(self)
684 }
685
686 #[pymethod]
687 fn add_note(self, note: PyStrRef, vm: &VirtualMachine) -> PyResult<()> {
688 let dict = self
689 .as_object()
690 .dict()
691 .ok_or_else(|| vm.new_attribute_error("Exception object has no __dict__"))?;
692
693 let notes = if let Ok(notes) = dict.get_item("__notes__", vm) {
694 notes
695 } else {
696 let new_notes = vm.ctx.new_list(vec![]);
697 dict.set_item("__notes__", new_notes.clone().into(), vm)?;
698 new_notes.into()
699 };
700
701 let notes = notes
702 .downcast::<PyList>()
703 .map_err(|_| vm.new_type_error("__notes__ must be a list"))?;
704
705 notes.borrow_vec_mut().push(note.into());
706 Ok(())
707 }
708
709 #[pymethod]
710 fn __reduce__(self, vm: &VirtualMachine) -> PyTupleRef {
711 if let Some(dict) = self.as_object().dict().filter(|x| !x.is_empty()) {
712 vm.new_tuple((self.class().to_owned(), self.args(), dict))
713 } else {
714 vm.new_tuple((self.class().to_owned(), self.args()))
715 }
716 }
717
718 #[pymethod]
719 fn __setstate__(self, state: PyObjectRef, vm: &VirtualMachine) -> PyResult<PyObjectRef> {
720 if !vm.is_none(&state) {
721 let dict = state
722 .downcast::<crate::builtins::PyDict>()
723 .map_err(|_| vm.new_type_error("state is not a dictionary"))?;
724
725 for (key, value) in &dict {
726 let key_str = key.str(vm)?;
727 if key_str.as_bytes().starts_with(b"__") {
728 continue;
729 }
730 self.as_object().set_attr(&key_str, value.clone(), vm)?;
731 }
732 }
733 Ok(vm.ctx.none())
734 }
735}
736
737impl Constructor for PyBaseException {
738 type Args = FuncArgs;
739
740 fn slot_new(cls: PyTypeRef, args: FuncArgs, vm: &VirtualMachine) -> PyResult {
741 if cls.is(Self::class(&vm.ctx)) && !args.kwargs.is_empty() {
742 return Err(vm.new_type_error("BaseException() takes no keyword arguments"));
743 }
744 Self::new(args.args, vm)
745 .into_ref_with_type(vm, cls)
746 .map(Into::into)
747 }
748
749 fn py_new(_cls: &Py<PyType>, _args: FuncArgs, _vm: &VirtualMachine) -> PyResult<Self> {
750 unimplemented!("use slot_new")
751 }
752}
753
754impl Initializer for PyBaseException {
755 type Args = FuncArgs;
756
757 fn init(zelf: PyRef<Self>, args: Self::Args, vm: &VirtualMachine) -> PyResult<()> {
758 *zelf.args.write() = PyTuple::new_ref(args.args, &vm.ctx);
759 Ok(())
760 }
761}
762
763impl Representable for PyBaseException {
764 #[inline]
765 fn repr_str(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<String> {
766 let repr_args = vm.exception_args_as_string(zelf.args(), false);
767 let cls = zelf.class();
768 Ok(format!("{}({})", cls.name(), repr_args.iter().format(", ")))
769 }
770}
771
772impl ExceptionZoo {
773 pub(crate) fn init() -> Self {
774 use self::types::*;
775
776 let base_exception_type = PyBaseException::init_builtin_type();
777
778 let base_exception_group = PyBaseExceptionGroup::init_builtin_type();
780 let system_exit = PySystemExit::init_builtin_type();
781 let keyboard_interrupt = PyKeyboardInterrupt::init_builtin_type();
782 let generator_exit = PyGeneratorExit::init_builtin_type();
783
784 let exception_type = PyException::init_builtin_type();
785 let stop_iteration = PyStopIteration::init_builtin_type();
786 let stop_async_iteration = PyStopAsyncIteration::init_builtin_type();
787 let arithmetic_error = PyArithmeticError::init_builtin_type();
788 let floating_point_error = PyFloatingPointError::init_builtin_type();
789 let overflow_error = PyOverflowError::init_builtin_type();
790 let zero_division_error = PyZeroDivisionError::init_builtin_type();
791
792 let assertion_error = PyAssertionError::init_builtin_type();
793 let attribute_error = PyAttributeError::init_builtin_type();
794 let buffer_error = PyBufferError::init_builtin_type();
795 let eof_error = PyEOFError::init_builtin_type();
796
797 let import_error = PyImportError::init_builtin_type();
798 let module_not_found_error = PyModuleNotFoundError::init_builtin_type();
799
800 let lookup_error = PyLookupError::init_builtin_type();
801 let index_error = PyIndexError::init_builtin_type();
802 let key_error = PyKeyError::init_builtin_type();
803
804 let memory_error = PyMemoryError::init_builtin_type();
805
806 let name_error = PyNameError::init_builtin_type();
807 let unbound_local_error = PyUnboundLocalError::init_builtin_type();
808
809 let os_error = PyOSError::init_builtin_type();
811 let blocking_io_error = PyBlockingIOError::init_builtin_type();
812 let child_process_error = PyChildProcessError::init_builtin_type();
813
814 let connection_error = PyConnectionError::init_builtin_type();
815 let broken_pipe_error = PyBrokenPipeError::init_builtin_type();
816 let connection_aborted_error = PyConnectionAbortedError::init_builtin_type();
817 let connection_refused_error = PyConnectionRefusedError::init_builtin_type();
818 let connection_reset_error = PyConnectionResetError::init_builtin_type();
819
820 let file_exists_error = PyFileExistsError::init_builtin_type();
821 let file_not_found_error = PyFileNotFoundError::init_builtin_type();
822 let interrupted_error = PyInterruptedError::init_builtin_type();
823 let is_a_directory_error = PyIsADirectoryError::init_builtin_type();
824 let not_a_directory_error = PyNotADirectoryError::init_builtin_type();
825 let permission_error = PyPermissionError::init_builtin_type();
826 let process_lookup_error = PyProcessLookupError::init_builtin_type();
827 let timeout_error = PyTimeoutError::init_builtin_type();
828
829 let reference_error = PyReferenceError::init_builtin_type();
830
831 let runtime_error = PyRuntimeError::init_builtin_type();
832 let not_implemented_error = PyNotImplementedError::init_builtin_type();
833 let recursion_error = PyRecursionError::init_builtin_type();
834 let python_finalization_error = PyPythonFinalizationError::init_builtin_type();
835
836 let syntax_error = PySyntaxError::init_builtin_type();
837 let incomplete_input_error = PyIncompleteInputError::init_builtin_type();
838 let indentation_error = PyIndentationError::init_builtin_type();
839 let tab_error = PyTabError::init_builtin_type();
840
841 let system_error = PySystemError::init_builtin_type();
842 let type_error = PyTypeError::init_builtin_type();
843 let value_error = PyValueError::init_builtin_type();
844 let unicode_error = PyUnicodeError::init_builtin_type();
845 let unicode_decode_error = PyUnicodeDecodeError::init_builtin_type();
846 let unicode_encode_error = PyUnicodeEncodeError::init_builtin_type();
847 let unicode_translate_error = PyUnicodeTranslateError::init_builtin_type();
848
849 #[cfg(feature = "jit")]
850 let jit_error = PyJitError::init_builtin_type();
851
852 let warning = PyWarning::init_builtin_type();
853 let deprecation_warning = PyDeprecationWarning::init_builtin_type();
854 let pending_deprecation_warning = PyPendingDeprecationWarning::init_builtin_type();
855 let runtime_warning = PyRuntimeWarning::init_builtin_type();
856 let syntax_warning = PySyntaxWarning::init_builtin_type();
857 let user_warning = PyUserWarning::init_builtin_type();
858 let future_warning = PyFutureWarning::init_builtin_type();
859 let import_warning = PyImportWarning::init_builtin_type();
860 let unicode_warning = PyUnicodeWarning::init_builtin_type();
861 let bytes_warning = PyBytesWarning::init_builtin_type();
862 let resource_warning = PyResourceWarning::init_builtin_type();
863 let encoding_warning = PyEncodingWarning::init_builtin_type();
864
865 Self {
866 base_exception_type,
867 base_exception_group,
868 system_exit,
869 keyboard_interrupt,
870 generator_exit,
871 exception_type,
872 stop_iteration,
873 stop_async_iteration,
874 arithmetic_error,
875 floating_point_error,
876 overflow_error,
877 zero_division_error,
878 assertion_error,
879 attribute_error,
880 buffer_error,
881 eof_error,
882 import_error,
883 module_not_found_error,
884 lookup_error,
885 index_error,
886 key_error,
887 memory_error,
888 name_error,
889 unbound_local_error,
890 os_error,
891 blocking_io_error,
892 child_process_error,
893 connection_error,
894 broken_pipe_error,
895 connection_aborted_error,
896 connection_refused_error,
897 connection_reset_error,
898 file_exists_error,
899 file_not_found_error,
900 interrupted_error,
901 is_a_directory_error,
902 not_a_directory_error,
903 permission_error,
904 process_lookup_error,
905 timeout_error,
906 reference_error,
907 runtime_error,
908 not_implemented_error,
909 recursion_error,
910 python_finalization_error,
911 syntax_error,
912 incomplete_input_error,
913 indentation_error,
914 tab_error,
915 system_error,
916 type_error,
917 value_error,
918 unicode_error,
919 unicode_decode_error,
920 unicode_encode_error,
921 unicode_translate_error,
922
923 #[cfg(feature = "jit")]
924 jit_error,
925
926 warning,
927 deprecation_warning,
928 pending_deprecation_warning,
929 runtime_warning,
930 syntax_warning,
931 user_warning,
932 future_warning,
933 import_warning,
934 unicode_warning,
935 bytes_warning,
936 resource_warning,
937 encoding_warning,
938 }
939 }
940
941 #[allow(
943 clippy::redundant_clone,
944 reason = "temporary workaround until errno/winerror handling is fixed"
945 )]
946 pub fn extend(ctx: &'static Context) {
947 use self::types::*;
948
949 let excs = &ctx.exceptions;
950
951 PyBaseException::extend_class(ctx, excs.base_exception_type);
952
953 extend_exception!(PyBaseExceptionGroup, ctx, excs.base_exception_group, {
955 "message" => ctx.new_readonly_getset("message", excs.base_exception_group, make_arg_getter(0)),
956 "exceptions" => ctx.new_readonly_getset("exceptions", excs.base_exception_group, make_arg_getter(1)),
957 });
958
959 extend_exception!(PySystemExit, ctx, excs.system_exit, {
960 "code" => ctx.new_readonly_getset("code", excs.system_exit, system_exit_code),
961 });
962 extend_exception!(PyKeyboardInterrupt, ctx, excs.keyboard_interrupt);
963 extend_exception!(PyGeneratorExit, ctx, excs.generator_exit);
964
965 extend_exception!(PyException, ctx, excs.exception_type);
966
967 extend_exception!(PyStopIteration, ctx, excs.stop_iteration, {
968 "value" => ctx.none(),
969 });
970 extend_exception!(PyStopAsyncIteration, ctx, excs.stop_async_iteration);
971
972 extend_exception!(PyArithmeticError, ctx, excs.arithmetic_error);
973 extend_exception!(PyFloatingPointError, ctx, excs.floating_point_error);
974 extend_exception!(PyOverflowError, ctx, excs.overflow_error);
975 extend_exception!(PyZeroDivisionError, ctx, excs.zero_division_error);
976
977 extend_exception!(PyAssertionError, ctx, excs.assertion_error);
978 extend_exception!(PyAttributeError, ctx, excs.attribute_error, {
979 "name" => ctx.none(),
980 "obj" => ctx.none(),
981 });
982 extend_exception!(PyBufferError, ctx, excs.buffer_error);
983 extend_exception!(PyEOFError, ctx, excs.eof_error);
984
985 extend_exception!(PyImportError, ctx, excs.import_error, {
986 "msg" => ctx.new_readonly_getset("msg", excs.import_error, make_arg_getter(0)),
987 });
988 extend_exception!(PyModuleNotFoundError, ctx, excs.module_not_found_error);
989
990 extend_exception!(PyLookupError, ctx, excs.lookup_error);
991 extend_exception!(PyIndexError, ctx, excs.index_error);
992
993 extend_exception!(PyKeyError, ctx, excs.key_error);
994
995 extend_exception!(PyMemoryError, ctx, excs.memory_error);
996 extend_exception!(PyNameError, ctx, excs.name_error, {
997 "name" => ctx.none(),
998 });
999 extend_exception!(PyUnboundLocalError, ctx, excs.unbound_local_error);
1000
1001 extend_exception!(PyOSError, ctx, excs.os_error);
1004
1005 extend_exception!(PyBlockingIOError, ctx, excs.blocking_io_error);
1006 extend_exception!(PyChildProcessError, ctx, excs.child_process_error);
1007
1008 extend_exception!(PyConnectionError, ctx, excs.connection_error);
1009 extend_exception!(PyBrokenPipeError, ctx, excs.broken_pipe_error);
1010 extend_exception!(PyConnectionAbortedError, ctx, excs.connection_aborted_error);
1011 extend_exception!(PyConnectionRefusedError, ctx, excs.connection_refused_error);
1012 extend_exception!(PyConnectionResetError, ctx, excs.connection_reset_error);
1013
1014 extend_exception!(PyFileExistsError, ctx, excs.file_exists_error);
1015 extend_exception!(PyFileNotFoundError, ctx, excs.file_not_found_error);
1016 extend_exception!(PyInterruptedError, ctx, excs.interrupted_error);
1017 extend_exception!(PyIsADirectoryError, ctx, excs.is_a_directory_error);
1018 extend_exception!(PyNotADirectoryError, ctx, excs.not_a_directory_error);
1019 extend_exception!(PyPermissionError, ctx, excs.permission_error);
1020 extend_exception!(PyProcessLookupError, ctx, excs.process_lookup_error);
1021 extend_exception!(PyTimeoutError, ctx, excs.timeout_error);
1022
1023 extend_exception!(PyReferenceError, ctx, excs.reference_error);
1024 extend_exception!(PyRuntimeError, ctx, excs.runtime_error);
1025 extend_exception!(PyNotImplementedError, ctx, excs.not_implemented_error);
1026 extend_exception!(PyRecursionError, ctx, excs.recursion_error);
1027 extend_exception!(
1028 PyPythonFinalizationError,
1029 ctx,
1030 excs.python_finalization_error
1031 );
1032
1033 extend_exception!(PySyntaxError, ctx, excs.syntax_error, {
1034 "msg" => ctx.new_static_getset(
1035 "msg",
1036 excs.syntax_error,
1037 make_arg_getter(0),
1038 syntax_error_set_msg,
1039 ),
1040 "filename" => ctx.none(),
1042 "lineno" => ctx.none(),
1043 "end_lineno" => ctx.none(),
1044 "offset" => ctx.none(),
1045 "end_offset" => ctx.none(),
1046 "text" => ctx.none(),
1047 });
1048 extend_exception!(PyIncompleteInputError, ctx, excs.incomplete_input_error);
1049 extend_exception!(PyIndentationError, ctx, excs.indentation_error);
1050 extend_exception!(PyTabError, ctx, excs.tab_error);
1051
1052 extend_exception!(PySystemError, ctx, excs.system_error);
1053 extend_exception!(PyTypeError, ctx, excs.type_error);
1054 extend_exception!(PyValueError, ctx, excs.value_error);
1055 extend_exception!(PyUnicodeError, ctx, excs.unicode_error);
1056 extend_exception!(PyUnicodeDecodeError, ctx, excs.unicode_decode_error);
1057 extend_exception!(PyUnicodeEncodeError, ctx, excs.unicode_encode_error);
1058 extend_exception!(PyUnicodeTranslateError, ctx, excs.unicode_translate_error);
1059
1060 #[cfg(feature = "jit")]
1061 extend_exception!(PyJitError, ctx, excs.jit_error);
1062
1063 extend_exception!(PyWarning, ctx, excs.warning);
1064 extend_exception!(PyDeprecationWarning, ctx, excs.deprecation_warning);
1065 extend_exception!(
1066 PyPendingDeprecationWarning,
1067 ctx,
1068 excs.pending_deprecation_warning
1069 );
1070 extend_exception!(PyRuntimeWarning, ctx, excs.runtime_warning);
1071 extend_exception!(PySyntaxWarning, ctx, excs.syntax_warning);
1072 extend_exception!(PyUserWarning, ctx, excs.user_warning);
1073 extend_exception!(PyFutureWarning, ctx, excs.future_warning);
1074 extend_exception!(PyImportWarning, ctx, excs.import_warning);
1075 extend_exception!(PyUnicodeWarning, ctx, excs.unicode_warning);
1076 extend_exception!(PyBytesWarning, ctx, excs.bytes_warning);
1077 extend_exception!(PyResourceWarning, ctx, excs.resource_warning);
1078 extend_exception!(PyEncodingWarning, ctx, excs.encoding_warning);
1079 }
1080}
1081
1082fn make_arg_getter(idx: usize) -> impl Fn(PyBaseExceptionRef) -> Option<PyObjectRef> {
1083 move |exc| exc.get_arg(idx)
1084}
1085
1086fn syntax_error_set_msg(
1087 exc: PyBaseExceptionRef,
1088 value: PySetterValue,
1089 vm: &VirtualMachine,
1090) -> PyResult<()> {
1091 let mut args = exc.args.write();
1092 let mut new_args = args.as_slice().to_vec();
1093 if new_args.is_empty() {
1095 new_args.push(vm.ctx.none());
1096 }
1097 match value {
1098 PySetterValue::Assign(value) => new_args[0] = value,
1099 PySetterValue::Delete => new_args[0] = vm.ctx.none(),
1100 }
1101 *args = PyTuple::new_ref(new_args, &vm.ctx);
1102 Ok(())
1103}
1104
1105fn system_exit_code(exc: PyBaseExceptionRef) -> Option<PyObjectRef> {
1106 let args = exc.args.read();
1111 match args.len() {
1112 0 => None,
1113 1 => Some(args.first().unwrap().clone()),
1114 _ => Some(args.as_object().to_owned()),
1115 }
1116}
1117
1118#[cfg(feature = "serde")]
1119pub struct SerializeException<'vm, 's> {
1120 vm: &'vm VirtualMachine,
1121 exc: &'s Py<PyBaseException>,
1122}
1123
1124#[cfg(feature = "serde")]
1125impl<'vm, 's> SerializeException<'vm, 's> {
1126 pub fn new(vm: &'vm VirtualMachine, exc: &'s Py<PyBaseException>) -> Self {
1127 SerializeException { vm, exc }
1128 }
1129}
1130
1131#[cfg(feature = "serde")]
1132pub struct SerializeExceptionOwned<'vm> {
1133 vm: &'vm VirtualMachine,
1134 exc: PyBaseExceptionRef,
1135}
1136
1137#[cfg(feature = "serde")]
1138impl serde::Serialize for SerializeExceptionOwned<'_> {
1139 fn serialize<S: serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
1140 let Self { vm, exc } = self;
1141 SerializeException::new(vm, exc).serialize(s)
1142 }
1143}
1144
1145#[cfg(feature = "serde")]
1146impl serde::Serialize for SerializeException<'_, '_> {
1147 fn serialize<S: serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
1148 use serde::ser::*;
1149
1150 let mut struc = s.serialize_struct("PyBaseException", 7)?;
1151 struc.serialize_field("exc_type", &*self.exc.class().name())?;
1152 let tbs = {
1153 struct Tracebacks(PyTracebackRef);
1154 impl serde::Serialize for Tracebacks {
1155 fn serialize<S: serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
1156 let mut s = s.serialize_seq(None)?;
1157 for tb in self.0.iter() {
1158 s.serialize_element(&**tb)?;
1159 }
1160 s.end()
1161 }
1162 }
1163 self.exc.__traceback__().map(Tracebacks)
1164 };
1165 struc.serialize_field("traceback", &tbs)?;
1166 struc.serialize_field(
1167 "cause",
1168 &self
1169 .exc
1170 .__cause__()
1171 .map(|exc| SerializeExceptionOwned { vm: self.vm, exc }),
1172 )?;
1173 struc.serialize_field(
1174 "context",
1175 &self
1176 .exc
1177 .__context__()
1178 .map(|exc| SerializeExceptionOwned { vm: self.vm, exc }),
1179 )?;
1180 struc.serialize_field("suppress_context", &self.exc.__suppress_context__())?;
1181
1182 let args = {
1183 struct Args<'vm>(&'vm VirtualMachine, PyTupleRef);
1184 impl serde::Serialize for Args<'_> {
1185 fn serialize<S: serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
1186 s.collect_seq(
1187 self.1
1188 .iter()
1189 .map(|arg| crate::py_serde::PyObjectSerializer::new(self.0, arg)),
1190 )
1191 }
1192 }
1193 Args(self.vm, self.exc.args())
1194 };
1195 struc.serialize_field("args", &args)?;
1196
1197 let rendered = {
1198 let mut rendered = String::new();
1199 self.vm
1200 .write_exception(&mut rendered, self.exc)
1201 .map_err(S::Error::custom)?;
1202 rendered
1203 };
1204 struc.serialize_field("rendered", &rendered)?;
1205
1206 struc.end()
1207 }
1208}
1209
1210pub fn cstring_error(vm: &VirtualMachine) -> PyBaseExceptionRef {
1211 vm.new_value_error("embedded null character")
1212}
1213
1214impl ToPyException for alloc::ffi::NulError {
1215 fn to_pyexception(&self, vm: &VirtualMachine) -> PyBaseExceptionRef {
1216 cstring_error(vm)
1217 }
1218}
1219
1220#[cfg(windows)]
1221impl<C> ToPyException for widestring::error::ContainsNul<C> {
1222 fn to_pyexception(&self, vm: &VirtualMachine) -> PyBaseExceptionRef {
1223 cstring_error(vm)
1224 }
1225}
1226
1227#[cfg(any(unix, windows, target_os = "wasi"))]
1228pub(crate) fn errno_to_exc_type(errno: i32, vm: &VirtualMachine) -> Option<&'static Py<PyType>> {
1229 use crate::stdlib::errno::errors;
1230 let excs = &vm.ctx.exceptions;
1231 match errno {
1232 #[allow(unreachable_patterns)] errors::EWOULDBLOCK | errors::EAGAIN => Some(excs.blocking_io_error),
1234 errors::EALREADY => Some(excs.blocking_io_error),
1235 errors::EINPROGRESS => Some(excs.blocking_io_error),
1236 errors::EPIPE => Some(excs.broken_pipe_error),
1237 #[cfg(not(target_os = "wasi"))]
1238 errors::ESHUTDOWN => Some(excs.broken_pipe_error),
1239 errors::ECHILD => Some(excs.child_process_error),
1240 errors::ECONNABORTED => Some(excs.connection_aborted_error),
1241 errors::ECONNREFUSED => Some(excs.connection_refused_error),
1242 errors::ECONNRESET => Some(excs.connection_reset_error),
1243 errors::EEXIST => Some(excs.file_exists_error),
1244 errors::ENOENT => Some(excs.file_not_found_error),
1245 errors::EISDIR => Some(excs.is_a_directory_error),
1246 errors::ENOTDIR => Some(excs.not_a_directory_error),
1247 errors::EINTR => Some(excs.interrupted_error),
1248 errors::EACCES => Some(excs.permission_error),
1249 errors::EPERM => Some(excs.permission_error),
1250 errors::ESRCH => Some(excs.process_lookup_error),
1251 errors::ETIMEDOUT => Some(excs.timeout_error),
1252 _ => None,
1253 }
1254}
1255
1256#[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))]
1257pub(crate) fn errno_to_exc_type(_errno: i32, _vm: &VirtualMachine) -> Option<&'static Py<PyType>> {
1258 None
1259}
1260
1261pub(crate) trait ToOSErrorBuilder {
1262 fn to_os_error_builder(&self, vm: &VirtualMachine) -> OSErrorBuilder;
1263}
1264
1265pub(crate) struct OSErrorBuilder {
1266 exc_type: PyTypeRef,
1267 errno: Option<i32>,
1268 strerror: Option<PyObjectRef>,
1269 filename: Option<PyObjectRef>,
1270 #[cfg(windows)]
1271 winerror: Option<PyObjectRef>,
1272 filename2: Option<PyObjectRef>,
1273}
1274
1275impl OSErrorBuilder {
1276 #[must_use]
1277 pub fn with_subtype(
1278 exc_type: PyTypeRef,
1279 errno: Option<i32>,
1280 strerror: impl ToPyObject,
1281 vm: &VirtualMachine,
1282 ) -> Self {
1283 let strerror = strerror.to_pyobject(vm);
1284 Self {
1285 exc_type,
1286 errno,
1287 strerror: Some(strerror),
1288 filename: None,
1289 #[cfg(windows)]
1290 winerror: None,
1291 filename2: None,
1292 }
1293 }
1294
1295 #[must_use]
1296 pub fn with_errno(errno: i32, strerror: impl ToPyObject, vm: &VirtualMachine) -> Self {
1297 let exc_type = errno_to_exc_type(errno, vm)
1298 .unwrap_or(vm.ctx.exceptions.os_error)
1299 .to_owned();
1300 Self::with_subtype(exc_type, Some(errno), strerror, vm)
1301 }
1302
1303 #[must_use]
1304 #[allow(dead_code)]
1305 pub(crate) fn filename(mut self, filename: PyObjectRef) -> Self {
1306 self.filename.replace(filename);
1307 self
1308 }
1309
1310 #[must_use]
1311 #[allow(dead_code)]
1312 pub(crate) fn filename2(mut self, filename: PyObjectRef) -> Self {
1313 self.filename2.replace(filename);
1314 self
1315 }
1316
1317 #[must_use]
1318 #[cfg(windows)]
1319 pub(crate) fn winerror(mut self, winerror: PyObjectRef) -> Self {
1320 self.winerror.replace(winerror);
1321 self
1322 }
1323
1324 #[must_use]
1328 #[cfg(windows)]
1329 pub(crate) fn without_winerror(mut self) -> Self {
1330 self.winerror = None;
1331 self
1332 }
1333
1334 pub fn build(self, vm: &VirtualMachine) -> PyRef<types::PyOSError> {
1335 use types::PyOSError;
1336
1337 let OSErrorBuilder {
1338 exc_type,
1339 errno,
1340 strerror,
1341 filename,
1342 #[cfg(windows)]
1343 winerror,
1344 filename2,
1345 } = self;
1346
1347 let args = if let Some(errno) = errno {
1348 #[cfg(windows)]
1349 let winerror = winerror.to_pyobject(vm);
1350 #[cfg(not(windows))]
1351 let winerror = vm.ctx.none();
1352
1353 vec![
1354 errno.to_pyobject(vm),
1355 strerror.to_pyobject(vm),
1356 filename.to_pyobject(vm),
1357 winerror,
1358 filename2.to_pyobject(vm),
1359 ]
1360 } else {
1361 vec![strerror.to_pyobject(vm)]
1362 };
1363
1364 let payload = PyOSError::py_new(&exc_type, args.clone().into(), vm)
1365 .expect("new_os_error usage error");
1366 let os_error = payload
1367 .into_ref_with_type(vm, exc_type)
1368 .expect("new_os_error usage error");
1369 PyOSError::slot_init(os_error.as_object().to_owned(), args.into(), vm)
1370 .expect("new_os_error usage error");
1371 os_error
1372 }
1373}
1374
1375impl IntoPyException for OSErrorBuilder {
1376 fn into_pyexception(self, vm: &VirtualMachine) -> PyBaseExceptionRef {
1377 self.build(vm).upcast()
1378 }
1379}
1380
1381impl ToOSErrorBuilder for std::io::Error {
1382 fn to_os_error_builder(&self, vm: &VirtualMachine) -> OSErrorBuilder {
1383 use crate::common::os::ErrorExt;
1384
1385 let errno = self.posix_errno();
1386 #[cfg(windows)]
1387 let msg = 'msg: {
1388 const MAX_POSIX_ERRNO: i32 = 127;
1391 if errno > 0 && errno <= MAX_POSIX_ERRNO {
1392 let ptr = unsafe { libc::strerror(errno) };
1393 if !ptr.is_null() {
1394 let s = unsafe { core::ffi::CStr::from_ptr(ptr) }.to_string_lossy();
1395 if !s.starts_with("Unknown error") {
1396 break 'msg s.into_owned();
1397 }
1398 }
1399 }
1400 self.to_string()
1401 };
1402 #[cfg(unix)]
1403 let msg = {
1404 let ptr = unsafe { libc::strerror(errno) };
1405 if !ptr.is_null() {
1406 unsafe { core::ffi::CStr::from_ptr(ptr) }
1407 .to_string_lossy()
1408 .into_owned()
1409 } else {
1410 self.to_string()
1411 }
1412 };
1413 #[cfg(not(any(windows, unix)))]
1414 let msg = self.to_string();
1415
1416 #[allow(unused_mut)]
1417 let mut builder = OSErrorBuilder::with_errno(errno, msg, vm);
1418 #[cfg(windows)]
1419 if let Some(winerror) = self.raw_os_error() {
1420 builder = builder.winerror(winerror.to_pyobject(vm));
1421 }
1422 builder
1423 }
1424}
1425
1426impl ToPyException for std::io::Error {
1427 fn to_pyexception(&self, vm: &VirtualMachine) -> PyBaseExceptionRef {
1428 let builder = self.to_os_error_builder(vm);
1429 builder.into_pyexception(vm)
1430 }
1431}
1432
1433impl IntoPyException for std::io::Error {
1434 fn into_pyexception(self, vm: &VirtualMachine) -> PyBaseExceptionRef {
1435 self.to_pyexception(vm)
1436 }
1437}
1438
1439#[cfg(unix)]
1440impl IntoPyException for nix::Error {
1441 fn into_pyexception(self, vm: &VirtualMachine) -> PyBaseExceptionRef {
1442 std::io::Error::from(self).into_pyexception(vm)
1443 }
1444}
1445
1446#[cfg(unix)]
1447impl IntoPyException for rustix::io::Errno {
1448 fn into_pyexception(self, vm: &VirtualMachine) -> PyBaseExceptionRef {
1449 std::io::Error::from(self).into_pyexception(vm)
1450 }
1451}
1452
1453pub(super) mod types {
1454 use crate::common::lock::PyRwLock;
1455 use crate::object::{MaybeTraverse, Traverse, TraverseFn};
1456 #[cfg_attr(target_arch = "wasm32", allow(unused_imports))]
1457 use crate::{
1458 AsObject, Py, PyAtomicRef, PyObject, PyObjectRef, PyPayload, PyRef, PyResult,
1459 VirtualMachine,
1460 builtins::{
1461 PyInt, PyStrRef, PyTupleRef, PyType, PyTypeRef, traceback::PyTracebackRef,
1462 tuple::IntoPyTuple,
1463 },
1464 convert::ToPyResult,
1465 function::{ArgBytesLike, FuncArgs, KwArgs},
1466 types::{Constructor, Initializer},
1467 };
1468 use crossbeam_utils::atomic::AtomicCell;
1469 use itertools::Itertools;
1470 use rustpython_common::{
1471 str::UnicodeEscapeCodepoint,
1472 wtf8::{Wtf8, Wtf8Buf, wtf8_concat},
1473 };
1474
1475 pub use crate::exception_group::types::PyBaseExceptionGroup;
1477
1478 pub type PyBaseExceptionRef = PyRef<PyBaseException>;
1483
1484 #[pyclass(module = false, name = "BaseException", traverse = "manual")]
1487 pub struct PyBaseException {
1488 pub(super) traceback: PyRwLock<Option<PyTracebackRef>>,
1489 pub(super) cause: PyRwLock<Option<PyRef<Self>>>,
1490 pub(super) context: PyRwLock<Option<PyRef<Self>>>,
1491 pub(super) suppress_context: AtomicCell<bool>,
1492 pub(super) args: PyRwLock<PyTupleRef>,
1493 }
1494
1495 #[pyexception(name, base = PyBaseException, ctx = "system_exit")]
1496 #[derive(Debug)]
1497 #[repr(transparent)]
1498 pub struct PySystemExit(PyBaseException);
1499
1500 #[pyexception(with(Initializer))]
1502 impl PySystemExit {}
1503
1504 impl Initializer for PySystemExit {
1505 type Args = FuncArgs;
1506 fn slot_init(zelf: PyObjectRef, args: FuncArgs, vm: &VirtualMachine) -> PyResult<()> {
1507 PyBaseException::slot_init(zelf, args, vm)
1509 }
1512
1513 fn init(_zelf: PyRef<Self>, _args: Self::Args, _vm: &VirtualMachine) -> PyResult<()> {
1514 unreachable!("slot_init is defined")
1515 }
1516 }
1517
1518 #[pyexception(name, base = PyBaseException, ctx = "generator_exit", impl)]
1519 #[derive(Debug)]
1520 #[repr(transparent)]
1521 pub struct PyGeneratorExit(PyBaseException);
1522
1523 #[pyexception(name, base = PyBaseException, ctx = "keyboard_interrupt", impl)]
1524 #[derive(Debug)]
1525 #[repr(transparent)]
1526 pub struct PyKeyboardInterrupt(PyBaseException);
1527
1528 #[pyexception(name, base = PyBaseException, ctx = "exception_type", impl)]
1529 #[derive(Debug)]
1530 #[repr(transparent)]
1531 pub struct PyException(PyBaseException);
1532
1533 #[pyexception(name, base = PyException, ctx = "stop_iteration")]
1534 #[derive(Debug)]
1535 #[repr(transparent)]
1536 pub struct PyStopIteration(PyException);
1537
1538 #[pyexception(with(Initializer))]
1539 impl PyStopIteration {}
1540
1541 impl Initializer for PyStopIteration {
1542 type Args = FuncArgs;
1543 fn slot_init(zelf: PyObjectRef, args: FuncArgs, vm: &VirtualMachine) -> PyResult<()> {
1544 zelf.set_attr("value", vm.unwrap_or_none(args.args.first().cloned()), vm)?;
1545 Ok(())
1546 }
1547
1548 fn init(_zelf: PyRef<Self>, _args: Self::Args, _vm: &VirtualMachine) -> PyResult<()> {
1549 unreachable!("slot_init is defined")
1550 }
1551 }
1552
1553 #[pyexception(name, base = PyException, ctx = "stop_async_iteration", impl)]
1554 #[derive(Debug)]
1555 #[repr(transparent)]
1556 pub struct PyStopAsyncIteration(PyException);
1557
1558 #[pyexception(name, base = PyException, ctx = "arithmetic_error", impl)]
1559 #[derive(Debug)]
1560 #[repr(transparent)]
1561 pub struct PyArithmeticError(PyException);
1562
1563 #[pyexception(name, base = PyArithmeticError, ctx = "floating_point_error", impl)]
1564 #[derive(Debug)]
1565 #[repr(transparent)]
1566 pub struct PyFloatingPointError(PyArithmeticError);
1567 #[pyexception(name, base = PyArithmeticError, ctx = "overflow_error", impl)]
1568 #[derive(Debug)]
1569 #[repr(transparent)]
1570 pub struct PyOverflowError(PyArithmeticError);
1571
1572 #[pyexception(name, base = PyArithmeticError, ctx = "zero_division_error", impl)]
1573 #[derive(Debug)]
1574 #[repr(transparent)]
1575 pub struct PyZeroDivisionError(PyArithmeticError);
1576
1577 #[pyexception(name, base = PyException, ctx = "assertion_error", impl)]
1578 #[derive(Debug)]
1579 #[repr(transparent)]
1580 pub struct PyAssertionError(PyException);
1581
1582 #[pyexception(name, base = PyException, ctx = "attribute_error")]
1583 #[derive(Debug)]
1584 #[repr(transparent)]
1585 pub struct PyAttributeError(PyException);
1586
1587 #[pyexception(with(Initializer))]
1588 impl PyAttributeError {}
1589
1590 impl Initializer for PyAttributeError {
1591 type Args = FuncArgs;
1592
1593 fn slot_init(zelf: PyObjectRef, args: FuncArgs, vm: &VirtualMachine) -> PyResult<()> {
1594 let mut kwargs = args.kwargs.clone();
1596 let name = kwargs.swap_remove("name");
1597 let obj = kwargs.swap_remove("obj");
1598
1599 if let Some(invalid_key) = kwargs.keys().next() {
1601 return Err(vm.new_type_error(format!(
1602 "AttributeError() got an unexpected keyword argument '{invalid_key}'"
1603 )));
1604 }
1605
1606 let base_args = FuncArgs::new(args.args.clone(), KwArgs::default());
1608 PyBaseException::slot_init(zelf.clone(), base_args, vm)?;
1609
1610 zelf.set_attr("name", vm.unwrap_or_none(name), vm)?;
1612 zelf.set_attr("obj", vm.unwrap_or_none(obj), vm)?;
1613 Ok(())
1614 }
1615
1616 fn init(_zelf: PyRef<Self>, _args: Self::Args, _vm: &VirtualMachine) -> PyResult<()> {
1617 unreachable!("slot_init is defined")
1618 }
1619 }
1620
1621 #[pyexception(name, base = PyException, ctx = "buffer_error", impl)]
1622 #[derive(Debug)]
1623 #[repr(transparent)]
1624 pub struct PyBufferError(PyException);
1625
1626 #[pyexception(name, base = PyException, ctx = "eof_error", impl)]
1627 #[derive(Debug)]
1628 #[repr(transparent)]
1629 pub struct PyEOFError(PyException);
1630
1631 #[pyexception(name, base = PyException, ctx = "import_error")]
1632 #[derive(Debug)]
1633 #[repr(transparent)]
1634 pub struct PyImportError(PyException);
1635
1636 #[pyexception(with(Initializer))]
1637 impl PyImportError {
1638 #[pymethod]
1639 fn __reduce__(exc: PyBaseExceptionRef, vm: &VirtualMachine) -> PyTupleRef {
1640 let obj = exc.as_object().to_owned();
1641 let mut result: Vec<PyObjectRef> = vec![
1642 obj.class().to_owned().into(),
1643 vm.new_tuple((exc.get_arg(0).unwrap(),)).into(),
1644 ];
1645
1646 if let Some(dict) = obj.dict().filter(|x| !x.is_empty()) {
1647 result.push(dict.into());
1648 }
1649
1650 result.into_pytuple(vm)
1651 }
1652 }
1653
1654 impl Initializer for PyImportError {
1655 type Args = FuncArgs;
1656
1657 fn slot_init(zelf: PyObjectRef, args: FuncArgs, vm: &VirtualMachine) -> PyResult<()> {
1658 let mut kwargs = args.kwargs.clone();
1660 let name = kwargs.swap_remove("name");
1661 let path = kwargs.swap_remove("path");
1662 let name_from = kwargs.swap_remove("name_from");
1663
1664 if let Some(invalid_key) = kwargs.keys().next() {
1666 return Err(vm.new_type_error(format!(
1667 "'{invalid_key}' is an invalid keyword argument for ImportError"
1668 )));
1669 }
1670
1671 let dict = zelf.dict().unwrap();
1672 dict.set_item("name", vm.unwrap_or_none(name), vm)?;
1673 dict.set_item("path", vm.unwrap_or_none(path), vm)?;
1674 dict.set_item("name_from", vm.unwrap_or_none(name_from), vm)?;
1675 PyBaseException::slot_init(zelf, args, vm)
1676 }
1677
1678 fn init(_zelf: PyRef<Self>, _args: Self::Args, _vm: &VirtualMachine) -> PyResult<()> {
1679 unreachable!("slot_init is defined")
1680 }
1681 }
1682
1683 #[pyexception(name, base = PyImportError, ctx = "module_not_found_error", impl)]
1684 #[derive(Debug)]
1685 #[repr(transparent)]
1686 pub struct PyModuleNotFoundError(PyImportError);
1687
1688 #[pyexception(name, base = PyException, ctx = "lookup_error", impl)]
1689 #[derive(Debug)]
1690 #[repr(transparent)]
1691 pub struct PyLookupError(PyException);
1692
1693 #[pyexception(name, base = PyLookupError, ctx = "index_error", impl)]
1694 #[derive(Debug)]
1695 #[repr(transparent)]
1696 pub struct PyIndexError(PyLookupError);
1697
1698 #[pyexception(name, base = PyLookupError, ctx = "key_error")]
1699 #[derive(Debug)]
1700 #[repr(transparent)]
1701 pub struct PyKeyError(PyLookupError);
1702
1703 #[pyexception]
1704 impl PyKeyError {
1705 #[pymethod]
1706 fn __str__(zelf: &Py<PyBaseException>, vm: &VirtualMachine) -> PyResult<PyStrRef> {
1707 let args = zelf.args();
1708 Ok(if args.len() == 1 {
1709 vm.exception_args_as_string(args, false)
1710 .into_iter()
1711 .exactly_one()
1712 .unwrap()
1713 } else {
1714 zelf.__str__(vm)?
1715 })
1716 }
1717 }
1718
1719 #[pyexception(name, base = PyException, ctx = "memory_error", impl)]
1720 #[derive(Debug)]
1721 #[repr(transparent)]
1722 pub struct PyMemoryError(PyException);
1723
1724 #[pyexception(name, base = PyException, ctx = "name_error")]
1725 #[derive(Debug)]
1726 #[repr(transparent)]
1727 pub struct PyNameError(PyException);
1728
1729 #[pyexception(with(Initializer))]
1731 impl PyNameError {}
1732
1733 impl Initializer for PyNameError {
1734 type Args = FuncArgs;
1735 fn slot_init(zelf: PyObjectRef, args: FuncArgs, vm: &VirtualMachine) -> PyResult<()> {
1736 let mut kwargs = args.kwargs.clone();
1738 let name = kwargs.swap_remove("name");
1739
1740 if let Some(invalid_key) = kwargs.keys().next() {
1742 return Err(vm.new_type_error(format!(
1743 "NameError() got an unexpected keyword argument '{invalid_key}'"
1744 )));
1745 }
1746
1747 let base_args = FuncArgs::new(args.args.clone(), KwArgs::default());
1749 PyBaseException::slot_init(zelf.clone(), base_args, vm)?;
1750
1751 if let Some(name) = name {
1753 zelf.set_attr("name", name, vm)?;
1754 }
1755 Ok(())
1756 }
1757
1758 fn init(_zelf: PyRef<Self>, _args: Self::Args, _vm: &VirtualMachine) -> PyResult<()> {
1759 unreachable!("slot_init is defined")
1760 }
1761 }
1762
1763 #[pyexception(name, base = PyNameError, ctx = "unbound_local_error", impl)]
1764 #[derive(Debug)]
1765 #[repr(transparent)]
1766 pub struct PyUnboundLocalError(PyNameError);
1767
1768 #[pyexception(name, base = PyException, ctx = "os_error")]
1769 #[repr(C)]
1770 pub struct PyOSError {
1771 base: PyException,
1772 errno: PyAtomicRef<Option<PyObject>>,
1773 strerror: PyAtomicRef<Option<PyObject>>,
1774 filename: PyAtomicRef<Option<PyObject>>,
1775 filename2: PyAtomicRef<Option<PyObject>>,
1776 #[cfg(windows)]
1777 winerror: PyAtomicRef<Option<PyObject>>,
1778 written: AtomicCell<isize>,
1781 }
1782
1783 impl crate::class::PySubclass for PyOSError {
1784 type Base = PyException;
1785 fn as_base(&self) -> &Self::Base {
1786 &self.base
1787 }
1788 }
1789
1790 impl core::fmt::Debug for PyOSError {
1791 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1792 f.debug_struct("PyOSError").finish_non_exhaustive()
1793 }
1794 }
1795
1796 unsafe impl Traverse for PyOSError {
1797 fn traverse(&self, tracer_fn: &mut TraverseFn<'_>) {
1798 self.base.try_traverse(tracer_fn);
1799 if let Some(obj) = self.errno.deref() {
1800 tracer_fn(obj);
1801 }
1802 if let Some(obj) = self.strerror.deref() {
1803 tracer_fn(obj);
1804 }
1805 if let Some(obj) = self.filename.deref() {
1806 tracer_fn(obj);
1807 }
1808 if let Some(obj) = self.filename2.deref() {
1809 tracer_fn(obj);
1810 }
1811 #[cfg(windows)]
1812 if let Some(obj) = self.winerror.deref() {
1813 tracer_fn(obj);
1814 }
1815 }
1816 }
1817
1818 impl Constructor for PyOSError {
1820 type Args = FuncArgs;
1821
1822 fn py_new(_cls: &Py<PyType>, args: FuncArgs, vm: &VirtualMachine) -> PyResult<Self> {
1823 let len = args.args.len();
1824 let (errno, strerror) = if (2..=5).contains(&len) {
1826 (Some(args.args[0].clone()), Some(args.args[1].clone()))
1827 } else {
1828 (None, None)
1829 };
1830 let filename = if (3..=5).contains(&len) {
1831 Some(args.args[2].clone())
1832 } else {
1833 None
1834 };
1835 let filename2 = if len == 5 {
1836 args.args.get(4).cloned()
1837 } else {
1838 None
1839 };
1840 let base_args = if (3..=5).contains(&len) {
1842 args.args[..2].to_vec()
1843 } else {
1844 args.args.to_vec()
1845 };
1846 let base_exception = PyBaseException::new(base_args, vm);
1847 Ok(Self {
1848 base: PyException(base_exception),
1849 errno: errno.into(),
1850 strerror: strerror.into(),
1851 filename: filename.into(),
1852 filename2: filename2.into(),
1853 #[cfg(windows)]
1854 winerror: None.into(),
1855 written: AtomicCell::new(-1),
1856 })
1857 }
1858
1859 fn slot_new(cls: PyTypeRef, args: FuncArgs, vm: &VirtualMachine) -> PyResult {
1860 if *cls.name() == *vm.ctx.exceptions.os_error.name() {
1864 let args_vec = args.args.to_vec();
1865 let len = args_vec.len();
1866 if (2..=5).contains(&len) {
1867 let errno = &args_vec[0];
1868 if let Some(error) = errno
1869 .downcast_ref::<PyInt>()
1870 .and_then(|errno| errno.try_to_primitive::<i32>(vm).ok())
1871 .and_then(|errno| super::errno_to_exc_type(errno, vm))
1872 .and_then(|typ| vm.invoke_exception(typ.to_owned(), args_vec).ok())
1873 {
1874 return error.to_pyresult(vm);
1875 }
1876 }
1877 }
1878 let payload = Self::py_new(&cls, args, vm)?;
1879 payload.into_ref_with_type(vm, cls).map(Into::into)
1880 }
1881 }
1882
1883 impl Initializer for PyOSError {
1884 type Args = FuncArgs;
1885
1886 fn slot_init(zelf: PyObjectRef, args: FuncArgs, vm: &VirtualMachine) -> PyResult<()> {
1887 let len = args.args.len();
1888 let mut new_args = args;
1889
1890 #[allow(deprecated)]
1895 let exc: &Py<PyOSError> = zelf.downcast_ref::<PyOSError>().unwrap();
1896
1897 let is_blocking_io_error =
1899 zelf.class()
1900 .is(vm.ctx.exceptions.blocking_io_error.as_ref());
1901
1902 let mut set_filename = true;
1905 if len <= 5 {
1906 if 2 <= len {
1908 let _ = unsafe { exc.errno.swap(Some(new_args.args[0].clone())) };
1909 let _ = unsafe { exc.strerror.swap(Some(new_args.args[1].clone())) };
1910 }
1911 if 3 <= len {
1912 let third_arg = &new_args.args[2];
1913 if is_blocking_io_error
1915 && !vm.is_none(third_arg)
1916 && crate::protocol::PyNumber::check(third_arg)
1917 && let Ok(written) = third_arg.try_index(vm)
1918 && let Ok(n) = written.try_to_primitive::<isize>(vm)
1919 {
1920 exc.written.store(n);
1921 set_filename = false;
1922 let _ = unsafe { exc.filename.swap(None) };
1924 }
1925 if set_filename {
1926 let _ = unsafe { exc.filename.swap(Some(third_arg.clone())) };
1927 }
1928 }
1929 #[cfg(windows)]
1930 if 4 <= len {
1931 let winerror = new_args.args.get(3).cloned();
1932 let _ = unsafe { exc.winerror.swap(winerror.clone()) };
1934
1935 if let Some(errno) = winerror
1937 .as_ref()
1938 .and_then(|w| w.downcast_ref::<crate::builtins::PyInt>())
1939 .and_then(|w| w.try_to_primitive::<i32>(vm).ok())
1940 .map(crate::common::os::winerror_to_errno)
1941 {
1942 let errno_obj = vm.new_pyobj(errno);
1943 let _ = unsafe { exc.errno.swap(Some(errno_obj.clone())) };
1944 new_args.args[0] = errno_obj;
1945 }
1946 }
1947 if len == 5 {
1948 let _ = unsafe { exc.filename2.swap(new_args.args.get(4).cloned()) };
1949 }
1950 }
1951
1952 let has_filename = exc.filename.to_owned().filter(|f| !vm.is_none(f)).is_some();
1955 if (3..=5).contains(&len) && has_filename {
1956 new_args.args.truncate(2);
1957 }
1958 PyBaseException::slot_init(zelf, new_args, vm)
1959 }
1960
1961 fn init(_zelf: PyRef<Self>, _args: Self::Args, _vm: &VirtualMachine) -> PyResult<()> {
1962 unreachable!("slot_init is defined")
1963 }
1964 }
1965
1966 #[pyexception(with(Constructor, Initializer))]
1967 impl PyOSError {
1968 #[pymethod]
1969 fn __str__(zelf: &Py<PyBaseException>, vm: &VirtualMachine) -> PyResult<PyStrRef> {
1970 let obj = zelf.as_object();
1971
1972 let errno_field = obj.get_attr("errno", vm).ok().filter(|v| !vm.is_none(v));
1974 let strerror = obj.get_attr("strerror", vm).ok().filter(|v| !vm.is_none(v));
1975 let filename = obj.get_attr("filename", vm).ok().filter(|v| !vm.is_none(v));
1976 let filename2 = obj
1977 .get_attr("filename2", vm)
1978 .ok()
1979 .filter(|v| !vm.is_none(v));
1980 #[cfg(windows)]
1981 let winerror = obj.get_attr("winerror", vm).ok().filter(|v| !vm.is_none(v));
1982
1983 #[cfg(windows)]
1985 if let Some(ref win_err) = winerror {
1986 let code = win_err.str(vm)?;
1987 if let Some(ref f) = filename {
1988 let msg = strerror
1989 .as_ref()
1990 .map(|s| s.str(vm))
1991 .transpose()?
1992 .map(|s| s.to_string())
1993 .unwrap_or_else(|| "None".to_owned());
1994 if let Some(ref f2) = filename2 {
1995 return Ok(vm.ctx.new_str(format!(
1996 "[WinError {}] {}: {} -> {}",
1997 code,
1998 msg,
1999 f.repr(vm)?,
2000 f2.repr(vm)?
2001 )));
2002 }
2003 return Ok(vm.ctx.new_str(format!(
2004 "[WinError {}] {}: {}",
2005 code,
2006 msg,
2007 f.repr(vm)?
2008 )));
2009 }
2010 if let Some(ref s) = strerror {
2012 return Ok(vm
2013 .ctx
2014 .new_str(format!("[WinError {}] {}", code, s.str(vm)?)));
2015 }
2016 }
2017
2018 if let Some(ref f) = filename {
2020 let errno_str = errno_field
2021 .as_ref()
2022 .map(|e| e.str(vm))
2023 .transpose()?
2024 .map(|s| s.to_string())
2025 .unwrap_or_else(|| "None".to_owned());
2026 let msg = strerror
2027 .as_ref()
2028 .map(|s| s.str(vm))
2029 .transpose()?
2030 .map(|s| s.to_string())
2031 .unwrap_or_else(|| "None".to_owned());
2032 if let Some(ref f2) = filename2 {
2033 return Ok(vm.ctx.new_str(format!(
2034 "[Errno {}] {}: {} -> {}",
2035 errno_str,
2036 msg,
2037 f.repr(vm)?,
2038 f2.repr(vm)?
2039 )));
2040 }
2041 return Ok(vm.ctx.new_str(format!(
2042 "[Errno {}] {}: {}",
2043 errno_str,
2044 msg,
2045 f.repr(vm)?
2046 )));
2047 }
2048
2049 if let (Some(e), Some(s)) = (&errno_field, &strerror) {
2051 return Ok(vm
2052 .ctx
2053 .new_str(format!("[Errno {}] {}", e.str(vm)?, s.str(vm)?)));
2054 }
2055
2056 zelf.__str__(vm)
2058 }
2059
2060 #[pymethod]
2061 fn __reduce__(exc: PyBaseExceptionRef, vm: &VirtualMachine) -> PyTupleRef {
2062 let args = exc.args();
2063 let obj = exc.as_object().to_owned();
2064 let mut result: Vec<PyObjectRef> = vec![obj.class().to_owned().into()];
2065
2066 if args.len() >= 2 && args.len() <= 5 {
2067 let errno = exc.get_arg(0).unwrap();
2069 let msg = exc.get_arg(1).unwrap();
2070
2071 if let Ok(filename) = obj.get_attr("filename", vm) {
2072 if !vm.is_none(&filename) {
2073 let mut args_reduced: Vec<PyObjectRef> = vec![errno, msg, filename];
2074
2075 let filename2 = obj
2076 .get_attr("filename2", vm)
2077 .ok()
2078 .filter(|f| !vm.is_none(f));
2079 #[cfg(windows)]
2080 let winerror = obj.get_attr("winerror", vm).ok().filter(|w| !vm.is_none(w));
2081
2082 if let Some(filename2) = filename2 {
2083 #[cfg(windows)]
2084 {
2085 args_reduced.push(winerror.unwrap_or_else(|| vm.ctx.none()));
2086 }
2087 #[cfg(not(windows))]
2088 args_reduced.push(vm.ctx.none());
2089 args_reduced.push(filename2);
2090 } else {
2091 #[cfg(windows)]
2094 if let Some(winerror) = winerror {
2095 args_reduced.push(winerror);
2096 }
2097 }
2098 result.push(args_reduced.into_pytuple(vm).into());
2099 } else {
2100 result.push(args.into());
2103 }
2104 } else {
2105 result.push(args.into());
2106 }
2107 } else {
2108 result.push(args.into());
2109 }
2110
2111 if let Some(dict) = obj.dict().filter(|x| !x.is_empty()) {
2112 result.push(dict.into());
2113 }
2114 result.into_pytuple(vm)
2115 }
2116
2117 #[pygetset]
2119 fn errno(&self) -> Option<PyObjectRef> {
2120 self.errno.to_owned()
2121 }
2122
2123 #[pygetset(setter)]
2124 fn set_errno(&self, value: Option<PyObjectRef>, vm: &VirtualMachine) {
2125 self.errno.swap_to_temporary_refs(value, vm);
2126 }
2127
2128 #[pygetset]
2129 fn strerror(&self) -> Option<PyObjectRef> {
2130 self.strerror.to_owned()
2131 }
2132
2133 #[pygetset(setter, name = "strerror")]
2134 fn set_strerror(&self, value: Option<PyObjectRef>, vm: &VirtualMachine) {
2135 self.strerror.swap_to_temporary_refs(value, vm);
2136 }
2137
2138 #[pygetset]
2139 fn filename(&self) -> Option<PyObjectRef> {
2140 self.filename.to_owned()
2141 }
2142
2143 #[pygetset(setter)]
2144 fn set_filename(&self, value: Option<PyObjectRef>, vm: &VirtualMachine) {
2145 self.filename.swap_to_temporary_refs(value, vm);
2146 }
2147
2148 #[pygetset]
2149 fn filename2(&self) -> Option<PyObjectRef> {
2150 self.filename2.to_owned()
2151 }
2152
2153 #[pygetset(setter)]
2154 fn set_filename2(&self, value: Option<PyObjectRef>, vm: &VirtualMachine) {
2155 self.filename2.swap_to_temporary_refs(value, vm);
2156 }
2157
2158 #[cfg(windows)]
2159 #[pygetset]
2160 fn winerror(&self) -> Option<PyObjectRef> {
2161 self.winerror.to_owned()
2162 }
2163
2164 #[cfg(windows)]
2165 #[pygetset(setter)]
2166 fn set_winerror(&self, value: Option<PyObjectRef>, vm: &VirtualMachine) {
2167 self.winerror.swap_to_temporary_refs(value, vm);
2168 }
2169
2170 #[pygetset]
2171 fn characters_written(&self, vm: &VirtualMachine) -> PyResult<isize> {
2172 let written = self.written.load();
2173 if written == -1 {
2174 Err(vm.new_attribute_error("characters_written"))
2175 } else {
2176 Ok(written)
2177 }
2178 }
2179
2180 #[pygetset(setter)]
2181 fn set_characters_written(
2182 &self,
2183 value: Option<PyObjectRef>,
2184 vm: &VirtualMachine,
2185 ) -> PyResult<()> {
2186 match value {
2187 None => {
2188 if self.written.load() == -1 {
2190 Err(vm.new_attribute_error("characters_written"))
2191 } else {
2192 self.written.store(-1);
2193 Ok(())
2194 }
2195 }
2196 Some(v) => {
2197 let n = v
2198 .try_index(vm)?
2199 .try_to_primitive::<isize>(vm)
2200 .map_err(|_| {
2201 vm.new_value_error("cannot convert characters_written value to isize")
2202 })?;
2203 self.written.store(n);
2204 Ok(())
2205 }
2206 }
2207 }
2208 }
2209
2210 #[pyexception(name, base = PyOSError, ctx = "blocking_io_error", impl)]
2211 #[repr(transparent)]
2212 #[derive(Debug)]
2213 pub struct PyBlockingIOError(PyOSError);
2214
2215 #[pyexception(name, base = PyOSError, ctx = "child_process_error", impl)]
2216 #[repr(transparent)]
2217 #[derive(Debug)]
2218 pub struct PyChildProcessError(PyOSError);
2219
2220 #[pyexception(name, base = PyOSError, ctx = "connection_error", impl)]
2221 #[repr(transparent)]
2222 #[derive(Debug)]
2223 pub struct PyConnectionError(PyOSError);
2224
2225 #[pyexception(name, base = PyConnectionError, ctx = "broken_pipe_error", impl)]
2226 #[repr(transparent)]
2227 #[derive(Debug)]
2228 pub struct PyBrokenPipeError(PyConnectionError);
2229
2230 #[pyexception(
2231 name,
2232 base = PyConnectionError,
2233 ctx = "connection_aborted_error",
2234 impl
2235 )]
2236 #[repr(transparent)]
2237 #[derive(Debug)]
2238 pub struct PyConnectionAbortedError(PyConnectionError);
2239
2240 #[pyexception(
2241 name,
2242 base = PyConnectionError,
2243 ctx = "connection_refused_error",
2244 impl
2245 )]
2246 #[repr(transparent)]
2247 #[derive(Debug)]
2248 pub struct PyConnectionRefusedError(PyConnectionError);
2249
2250 #[pyexception(name, base = PyConnectionError, ctx = "connection_reset_error", impl)]
2251 #[repr(transparent)]
2252 #[derive(Debug)]
2253 pub struct PyConnectionResetError(PyConnectionError);
2254
2255 #[pyexception(name, base = PyOSError, ctx = "file_exists_error", impl)]
2256 #[repr(transparent)]
2257 #[derive(Debug)]
2258 pub struct PyFileExistsError(PyOSError);
2259
2260 #[pyexception(name, base = PyOSError, ctx = "file_not_found_error", impl)]
2261 #[repr(transparent)]
2262 #[derive(Debug)]
2263 pub struct PyFileNotFoundError(PyOSError);
2264
2265 #[pyexception(name, base = PyOSError, ctx = "interrupted_error", impl)]
2266 #[repr(transparent)]
2267 #[derive(Debug)]
2268 pub struct PyInterruptedError(PyOSError);
2269
2270 #[pyexception(name, base = PyOSError, ctx = "is_a_directory_error", impl)]
2271 #[repr(transparent)]
2272 #[derive(Debug)]
2273 pub struct PyIsADirectoryError(PyOSError);
2274
2275 #[pyexception(name, base = PyOSError, ctx = "not_a_directory_error", impl)]
2276 #[repr(transparent)]
2277 #[derive(Debug)]
2278 pub struct PyNotADirectoryError(PyOSError);
2279
2280 #[pyexception(name, base = PyOSError, ctx = "permission_error", impl)]
2281 #[repr(transparent)]
2282 #[derive(Debug)]
2283 pub struct PyPermissionError(PyOSError);
2284
2285 #[pyexception(name, base = PyOSError, ctx = "process_lookup_error", impl)]
2286 #[repr(transparent)]
2287 #[derive(Debug)]
2288 pub struct PyProcessLookupError(PyOSError);
2289
2290 #[pyexception(name, base = PyOSError, ctx = "timeout_error", impl)]
2291 #[derive(Debug)]
2292 #[repr(transparent)]
2293 pub struct PyTimeoutError(PyOSError);
2294
2295 #[pyexception(name, base = PyException, ctx = "reference_error", impl)]
2296 #[derive(Debug)]
2297 #[repr(transparent)]
2298 pub struct PyReferenceError(PyException);
2299
2300 #[pyexception(name, base = PyException, ctx = "runtime_error", impl)]
2301 #[derive(Debug)]
2302 #[repr(transparent)]
2303 pub struct PyRuntimeError(PyException);
2304
2305 #[pyexception(name, base = PyRuntimeError, ctx = "not_implemented_error", impl)]
2306 #[derive(Debug)]
2307 #[repr(transparent)]
2308 pub struct PyNotImplementedError(PyRuntimeError);
2309
2310 #[pyexception(name, base = PyRuntimeError, ctx = "recursion_error", impl)]
2311 #[derive(Debug)]
2312 #[repr(transparent)]
2313 pub struct PyRecursionError(PyRuntimeError);
2314
2315 #[pyexception(name, base = PyRuntimeError, ctx = "python_finalization_error", impl)]
2316 #[derive(Debug)]
2317 #[repr(transparent)]
2318 pub struct PyPythonFinalizationError(PyRuntimeError);
2319
2320 #[pyexception(name, base = PyException, ctx = "syntax_error")]
2321 #[derive(Debug)]
2322 #[repr(transparent)]
2323 pub struct PySyntaxError(PyException);
2324
2325 #[pyexception(with(Initializer))]
2326 impl PySyntaxError {
2327 #[pymethod]
2328 fn __str__(zelf: &Py<PyBaseException>, vm: &VirtualMachine) -> PyResult<PyStrRef> {
2329 fn basename(filename: &Wtf8) -> &Wtf8 {
2330 let bytes = filename.as_bytes();
2331 let pos = if cfg!(windows) {
2332 bytes.iter().rposition(|&b| b == b'/' || b == b'\\')
2333 } else {
2334 bytes.iter().rposition(|&b| b == b'/')
2335 };
2336 match pos {
2337 Some(pos) => unsafe { Wtf8::from_bytes_unchecked(&bytes[pos + 1..]) },
2339 None => filename,
2340 }
2341 }
2342
2343 let maybe_lineno = zelf
2344 .as_object()
2345 .get_attr("lineno", vm)
2346 .and_then(|obj| obj.str_utf8(vm))
2347 .ok();
2348 let maybe_filename = zelf.as_object().get_attr("filename", vm).ok().map(|obj| {
2349 obj.str(vm)
2350 .unwrap_or_else(|_| vm.ctx.new_str("<filename str() failed>"))
2351 });
2352
2353 let msg = match zelf.as_object().get_attr("msg", vm) {
2354 Ok(obj) => obj
2355 .str(vm)
2356 .unwrap_or_else(|_| vm.ctx.new_str("<msg str() failed>")),
2357 Err(_) => {
2358 return Py::<PyBaseException>::__str__(zelf, vm);
2360 }
2361 };
2362
2363 let msg_with_location_info: Wtf8Buf = match (maybe_lineno, maybe_filename) {
2364 (Some(lineno), Some(filename)) => wtf8_concat!(
2365 msg.as_wtf8(),
2366 " (",
2367 basename(filename.as_wtf8()),
2368 ", line ",
2369 lineno.as_str(),
2370 ")"
2371 ),
2372 (Some(lineno), None) => {
2373 wtf8_concat!(msg.as_wtf8(), " (line ", lineno.as_str(), ")")
2374 }
2375 (None, Some(filename)) => {
2376 wtf8_concat!(msg.as_wtf8(), " (", basename(filename.as_wtf8()), ")")
2377 }
2378 (None, None) => msg.as_wtf8().to_owned(),
2379 };
2380
2381 Ok(vm.ctx.new_str(msg_with_location_info))
2382 }
2383 }
2384
2385 impl Initializer for PySyntaxError {
2386 type Args = FuncArgs;
2387
2388 fn slot_init(zelf: PyObjectRef, args: FuncArgs, vm: &VirtualMachine) -> PyResult<()> {
2389 let len = args.args.len();
2390 let new_args = args;
2391
2392 zelf.set_attr("print_file_and_line", vm.ctx.none(), vm)?;
2393
2394 if len == 2
2395 && let Ok(location_tuple) = new_args.args[1]
2396 .clone()
2397 .downcast::<crate::builtins::PyTuple>()
2398 {
2399 let location_tup_len = location_tuple.len();
2400
2401 match location_tup_len {
2402 4 | 6 => {}
2403 5 => {
2404 return Err(vm.new_type_error(
2405 "end_offset must be provided when end_lineno is provided".to_owned(),
2406 ));
2407 }
2408 _ => {
2409 return Err(vm.new_type_error(format!(
2410 "function takes exactly 4 or 6 arguments ({} given)",
2411 location_tup_len
2412 )));
2413 }
2414 }
2415
2416 for (i, &attr) in [
2417 "filename",
2418 "lineno",
2419 "offset",
2420 "text",
2421 "end_lineno",
2422 "end_offset",
2423 ]
2424 .iter()
2425 .enumerate()
2426 {
2427 if location_tup_len > i {
2428 zelf.set_attr(attr, location_tuple[i].to_owned(), vm)?;
2429 } else {
2430 break;
2431 }
2432 }
2433 }
2434
2435 PyBaseException::slot_init(zelf, new_args, vm)
2436 }
2437
2438 fn init(_zelf: PyRef<Self>, _args: Self::Args, _vm: &VirtualMachine) -> PyResult<()> {
2439 unreachable!("slot_init is defined")
2440 }
2441 }
2442
2443 #[pyexception(
2445 name = "_IncompleteInputError",
2446 base = PySyntaxError,
2447 ctx = "incomplete_input_error",
2448 impl
2449 )]
2450 #[derive(Debug)]
2451 #[repr(transparent)]
2452 pub struct PyIncompleteInputError(PySyntaxError);
2453
2454 #[pyexception(name, base = PySyntaxError, ctx = "indentation_error", impl)]
2455 #[derive(Debug)]
2456 #[repr(transparent)]
2457 pub struct PyIndentationError(PySyntaxError);
2458
2459 #[pyexception(name, base = PyIndentationError, ctx = "tab_error", impl)]
2460 #[derive(Debug)]
2461 #[repr(transparent)]
2462 pub struct PyTabError(PyIndentationError);
2463
2464 #[pyexception(name, base = PyException, ctx = "system_error", impl)]
2465 #[derive(Debug)]
2466 #[repr(transparent)]
2467 pub struct PySystemError(PyException);
2468
2469 #[pyexception(name, base = PyException, ctx = "type_error", impl)]
2470 #[derive(Debug)]
2471 #[repr(transparent)]
2472 pub struct PyTypeError(PyException);
2473
2474 #[pyexception(name, base = PyException, ctx = "value_error", impl)]
2475 #[derive(Debug)]
2476 #[repr(transparent)]
2477 pub struct PyValueError(PyException);
2478
2479 #[pyexception(name, base = PyValueError, ctx = "unicode_error", impl)]
2480 #[derive(Debug)]
2481 #[repr(transparent)]
2482 pub struct PyUnicodeError(PyValueError);
2483
2484 #[pyexception(name, base = PyUnicodeError, ctx = "unicode_decode_error")]
2485 #[derive(Debug)]
2486 #[repr(transparent)]
2487 pub struct PyUnicodeDecodeError(PyUnicodeError);
2488
2489 #[pyexception(with(Initializer))]
2490 impl PyUnicodeDecodeError {
2491 #[pymethod]
2492 fn __str__(zelf: &Py<PyBaseException>, vm: &VirtualMachine) -> PyResult<PyStrRef> {
2493 let Ok(object) = zelf.as_object().get_attr("object", vm) else {
2494 return Ok(vm.ctx.empty_str.to_owned());
2495 };
2496 let object: ArgBytesLike = object.try_into_value(vm)?;
2497 let encoding: PyStrRef = zelf
2498 .as_object()
2499 .get_attr("encoding", vm)?
2500 .try_into_value(vm)?;
2501 let start: usize = zelf.as_object().get_attr("start", vm)?.try_into_value(vm)?;
2502 let end: usize = zelf.as_object().get_attr("end", vm)?.try_into_value(vm)?;
2503 let reason: PyStrRef = zelf
2504 .as_object()
2505 .get_attr("reason", vm)?
2506 .try_into_value(vm)?;
2507 Ok(vm.ctx.new_str(if start < object.len() && end <= object.len() && end == start + 1 {
2508 let b = object.borrow_buf()[start];
2509 format!(
2510 "'{encoding}' codec can't decode byte {b:#02x} in position {start}: {reason}"
2511 )
2512 } else {
2513 format!(
2514 "'{encoding}' codec can't decode bytes in position {start}-{}: {reason}",
2515 end - 1,
2516 )
2517 }))
2518 }
2519 }
2520
2521 impl Initializer for PyUnicodeDecodeError {
2522 type Args = FuncArgs;
2523
2524 fn slot_init(zelf: PyObjectRef, args: FuncArgs, vm: &VirtualMachine) -> PyResult<()> {
2525 type Args = (PyStrRef, ArgBytesLike, isize, isize, PyStrRef);
2526 let (encoding, object, start, end, reason): Args = args.bind(vm)?;
2527 zelf.set_attr("encoding", encoding, vm)?;
2528 let object_as_bytes = vm.ctx.new_bytes(object.borrow_buf().to_vec());
2529 zelf.set_attr("object", object_as_bytes, vm)?;
2530 zelf.set_attr("start", vm.ctx.new_int(start), vm)?;
2531 zelf.set_attr("end", vm.ctx.new_int(end), vm)?;
2532 zelf.set_attr("reason", reason, vm)?;
2533 Ok(())
2534 }
2535
2536 fn init(_zelf: PyRef<Self>, _args: Self::Args, _vm: &VirtualMachine) -> PyResult<()> {
2537 unreachable!("slot_init is defined")
2538 }
2539 }
2540
2541 #[pyexception(name, base = PyUnicodeError, ctx = "unicode_encode_error")]
2542 #[derive(Debug)]
2543 #[repr(transparent)]
2544 pub struct PyUnicodeEncodeError(PyUnicodeError);
2545
2546 #[pyexception(with(Initializer))]
2547 impl PyUnicodeEncodeError {
2548 #[pymethod]
2549 fn __str__(zelf: &Py<PyBaseException>, vm: &VirtualMachine) -> PyResult<PyStrRef> {
2550 let Ok(object) = zelf.as_object().get_attr("object", vm) else {
2551 return Ok(vm.ctx.empty_str.to_owned());
2552 };
2553 let object: PyStrRef = object.try_into_value(vm)?;
2554 let encoding: PyStrRef = zelf
2555 .as_object()
2556 .get_attr("encoding", vm)?
2557 .try_into_value(vm)?;
2558 let start: usize = zelf.as_object().get_attr("start", vm)?.try_into_value(vm)?;
2559 let end: usize = zelf.as_object().get_attr("end", vm)?.try_into_value(vm)?;
2560 let reason: PyStrRef = zelf
2561 .as_object()
2562 .get_attr("reason", vm)?
2563 .try_into_value(vm)?;
2564 Ok(vm.ctx.new_str(if start < object.char_len() && end <= object.char_len() && end == start + 1 {
2565 let ch = object.as_wtf8().code_points().nth(start).unwrap();
2566 format!(
2567 "'{encoding}' codec can't encode character '{}' in position {start}: {reason}",
2568 UnicodeEscapeCodepoint(ch)
2569 )
2570 } else {
2571 format!(
2572 "'{encoding}' codec can't encode characters in position {start}-{}: {reason}",
2573 end - 1,
2574 )
2575 }))
2576 }
2577 }
2578
2579 impl Initializer for PyUnicodeEncodeError {
2580 type Args = FuncArgs;
2581
2582 fn slot_init(zelf: PyObjectRef, args: FuncArgs, vm: &VirtualMachine) -> PyResult<()> {
2583 type Args = (PyStrRef, PyStrRef, isize, isize, PyStrRef);
2584 let (encoding, object, start, end, reason): Args = args.bind(vm)?;
2585 zelf.set_attr("encoding", encoding, vm)?;
2586 zelf.set_attr("object", object, vm)?;
2587 zelf.set_attr("start", vm.ctx.new_int(start), vm)?;
2588 zelf.set_attr("end", vm.ctx.new_int(end), vm)?;
2589 zelf.set_attr("reason", reason, vm)?;
2590 Ok(())
2591 }
2592
2593 fn init(_zelf: PyRef<Self>, _args: Self::Args, _vm: &VirtualMachine) -> PyResult<()> {
2594 unreachable!("slot_init is defined")
2595 }
2596 }
2597
2598 #[pyexception(name, base = PyUnicodeError, ctx = "unicode_translate_error")]
2599 #[derive(Debug)]
2600 #[repr(transparent)]
2601 pub struct PyUnicodeTranslateError(PyUnicodeError);
2602
2603 #[pyexception(with(Initializer))]
2604 impl PyUnicodeTranslateError {
2605 #[pymethod]
2606 fn __str__(zelf: &Py<PyBaseException>, vm: &VirtualMachine) -> PyResult<PyStrRef> {
2607 let Ok(object) = zelf.as_object().get_attr("object", vm) else {
2608 return Ok(vm.ctx.empty_str.to_owned());
2609 };
2610 let object: PyStrRef = object.try_into_value(vm)?;
2611 let start: usize = zelf.as_object().get_attr("start", vm)?.try_into_value(vm)?;
2612 let end: usize = zelf.as_object().get_attr("end", vm)?.try_into_value(vm)?;
2613 let reason: PyStrRef = zelf
2614 .as_object()
2615 .get_attr("reason", vm)?
2616 .try_into_value(vm)?;
2617 Ok(vm.ctx.new_str(
2618 if start < object.char_len() && end <= object.char_len() && end == start + 1 {
2619 let ch = object.as_wtf8().code_points().nth(start).unwrap();
2620 format!(
2621 "can't translate character '{}' in position {start}: {reason}",
2622 UnicodeEscapeCodepoint(ch)
2623 )
2624 } else {
2625 format!(
2626 "can't translate characters in position {start}-{}: {reason}",
2627 end - 1,
2628 )
2629 },
2630 ))
2631 }
2632 }
2633
2634 impl Initializer for PyUnicodeTranslateError {
2635 type Args = FuncArgs;
2636
2637 fn slot_init(zelf: PyObjectRef, args: FuncArgs, vm: &VirtualMachine) -> PyResult<()> {
2638 type Args = (PyStrRef, isize, isize, PyStrRef);
2639 let (object, start, end, reason): Args = args.bind(vm)?;
2640 zelf.set_attr("object", object, vm)?;
2641 zelf.set_attr("start", vm.ctx.new_int(start), vm)?;
2642 zelf.set_attr("end", vm.ctx.new_int(end), vm)?;
2643 zelf.set_attr("reason", reason, vm)?;
2644 Ok(())
2645 }
2646
2647 fn init(_zelf: PyRef<Self>, _args: Self::Args, _vm: &VirtualMachine) -> PyResult<()> {
2648 unreachable!("slot_init is defined")
2649 }
2650 }
2651
2652 #[cfg(feature = "jit")]
2654 #[pyexception(name, base = PyException, ctx = "jit_error", impl)]
2655 #[derive(Debug)]
2656 #[repr(transparent)]
2657 pub struct PyJitError(PyException);
2658
2659 #[pyexception(name, base = PyException, ctx = "warning", impl)]
2661 #[derive(Debug)]
2662 #[repr(transparent)]
2663 pub struct PyWarning(PyException);
2664
2665 #[pyexception(name, base = PyWarning, ctx = "deprecation_warning", impl)]
2666 #[derive(Debug)]
2667 #[repr(transparent)]
2668 pub struct PyDeprecationWarning(PyWarning);
2669
2670 #[pyexception(name, base = PyWarning, ctx = "pending_deprecation_warning", impl)]
2671 #[derive(Debug)]
2672 #[repr(transparent)]
2673 pub struct PyPendingDeprecationWarning(PyWarning);
2674
2675 #[pyexception(name, base = PyWarning, ctx = "runtime_warning", impl)]
2676 #[derive(Debug)]
2677 #[repr(transparent)]
2678 pub struct PyRuntimeWarning(PyWarning);
2679
2680 #[pyexception(name, base = PyWarning, ctx = "syntax_warning", impl)]
2681 #[derive(Debug)]
2682 #[repr(transparent)]
2683 pub struct PySyntaxWarning(PyWarning);
2684
2685 #[pyexception(name, base = PyWarning, ctx = "user_warning", impl)]
2686 #[derive(Debug)]
2687 #[repr(transparent)]
2688 pub struct PyUserWarning(PyWarning);
2689
2690 #[pyexception(name, base = PyWarning, ctx = "future_warning", impl)]
2691 #[derive(Debug)]
2692 #[repr(transparent)]
2693 pub struct PyFutureWarning(PyWarning);
2694
2695 #[pyexception(name, base = PyWarning, ctx = "import_warning", impl)]
2696 #[derive(Debug)]
2697 #[repr(transparent)]
2698 pub struct PyImportWarning(PyWarning);
2699
2700 #[pyexception(name, base = PyWarning, ctx = "unicode_warning", impl)]
2701 #[derive(Debug)]
2702 #[repr(transparent)]
2703 pub struct PyUnicodeWarning(PyWarning);
2704
2705 #[pyexception(name, base = PyWarning, ctx = "bytes_warning", impl)]
2706 #[derive(Debug)]
2707 #[repr(transparent)]
2708 pub struct PyBytesWarning(PyWarning);
2709
2710 #[pyexception(name, base = PyWarning, ctx = "resource_warning", impl)]
2711 #[derive(Debug)]
2712 #[repr(transparent)]
2713 pub struct PyResourceWarning(PyWarning);
2714
2715 #[pyexception(name, base = PyWarning, ctx = "encoding_warning", impl)]
2716 #[derive(Debug)]
2717 #[repr(transparent)]
2718 pub struct PyEncodingWarning(PyWarning);
2719}
2720
2721fn check_except_star_type_valid(match_type: &PyObjectRef, vm: &VirtualMachine) -> PyResult<()> {
2723 let base_exc: PyObjectRef = vm.ctx.exceptions.base_exception_type.to_owned().into();
2724 let base_eg: PyObjectRef = vm.ctx.exceptions.base_exception_group.to_owned().into();
2725
2726 let check_one = |exc_type: &PyObjectRef| -> PyResult<()> {
2728 if !exc_type.is_subclass(&base_exc, vm)? {
2730 return Err(vm.new_type_error(
2731 "catching classes that do not inherit from BaseException is not allowed",
2732 ));
2733 }
2734 if exc_type.is_subclass(&base_eg, vm)? {
2736 return Err(vm.new_type_error(
2737 "catching ExceptionGroup with except* is not allowed. Use except instead.",
2738 ));
2739 }
2740 Ok(())
2741 };
2742
2743 if let Ok(tuple) = match_type.clone().downcast::<PyTuple>() {
2745 for item in tuple.iter() {
2746 check_one(item)?;
2747 }
2748 } else {
2749 check_one(match_type)?;
2750 }
2751 Ok(())
2752}
2753
2754pub fn exception_group_match(
2757 exc_value: &PyObjectRef,
2758 match_type: &PyObjectRef,
2759 vm: &VirtualMachine,
2760) -> PyResult<(PyObjectRef, PyObjectRef)> {
2761 if vm.is_none(exc_value) {
2765 return Ok((vm.ctx.none(), vm.ctx.none()));
2766 }
2767
2768 check_except_star_type_valid(match_type, vm)?;
2770
2771 if exc_value.is_instance(match_type, vm)? {
2773 let is_eg = exc_value.fast_isinstance(vm.ctx.exceptions.base_exception_group);
2775 let matched = if is_eg {
2776 exc_value.clone()
2777 } else {
2778 let excs = vm.ctx.new_tuple(vec![exc_value.clone()]);
2780 let eg_type: PyObjectRef = crate::exception_group::exception_group().to_owned().into();
2781 let wrapped = eg_type.call((vm.ctx.new_str(""), excs), vm)?;
2782 if let Ok(exc) = exc_value.clone().downcast::<types::PyBaseException>()
2784 && let Some(tb) = exc.__traceback__()
2785 && let Ok(wrapped_exc) = wrapped.clone().downcast::<types::PyBaseException>()
2786 {
2787 let _ = wrapped_exc.set___traceback__(tb.into(), vm);
2788 }
2789 wrapped
2790 };
2791 return Ok((vm.ctx.none(), matched));
2792 }
2793
2794 if exc_value.fast_isinstance(vm.ctx.exceptions.base_exception_group) {
2796 let pair = vm.call_method(exc_value, "split", (match_type.clone(),))?;
2797 if !pair.class().is(vm.ctx.types.tuple_type) {
2798 return Err(vm.new_type_error(format!(
2799 "{}.split must return a tuple, not {}",
2800 exc_value.class().name(),
2801 pair.class().name()
2802 )));
2803 }
2804 let pair_tuple: PyTupleRef = pair.try_into_value(vm)?;
2805 if pair_tuple.len() < 2 {
2806 return Err(vm.new_type_error(format!(
2807 "{}.split must return a 2-tuple, got tuple of size {}",
2808 exc_value.class().name(),
2809 pair_tuple.len()
2810 )));
2811 }
2812 let matched = pair_tuple[0].clone();
2813 let rest = pair_tuple[1].clone();
2814 return Ok((rest, matched));
2815 }
2816
2817 Ok((exc_value.clone(), vm.ctx.none()))
2819}
2820
2821pub fn prep_reraise_star(orig: PyObjectRef, excs: PyObjectRef, vm: &VirtualMachine) -> PyResult {
2824 use crate::builtins::PyList;
2825
2826 let excs_list = excs
2827 .downcast::<PyList>()
2828 .map_err(|_| vm.new_type_error("expected list for prep_reraise_star"))?;
2829
2830 let excs_vec: Vec<PyObjectRef> = excs_list.borrow_vec().to_vec();
2831
2832 if excs_vec.is_empty() {
2834 return Ok(vm.ctx.none());
2835 }
2836
2837 if !orig.fast_isinstance(vm.ctx.exceptions.base_exception_group) {
2840 let first = excs_vec.into_iter().find(|e| !vm.is_none(e));
2842 return Ok(first.unwrap_or_else(|| vm.ctx.none()));
2843 }
2844
2845 let mut raised: Vec<PyObjectRef> = Vec::new();
2847 let mut reraised: Vec<PyObjectRef> = Vec::new();
2848
2849 for exc in excs_vec {
2850 if vm.is_none(&exc) {
2851 continue;
2852 }
2853 if is_exception_from_orig(&exc, &orig, vm) {
2855 reraised.push(exc);
2856 } else {
2857 raised.push(exc);
2858 }
2859 }
2860
2861 if raised.is_empty() && reraised.is_empty() {
2863 return Ok(vm.ctx.none());
2864 }
2865
2866 let reraised_eg = exception_group_projection(&orig, &reraised, vm)?;
2868
2869 if raised.is_empty() {
2871 return Ok(reraised_eg);
2872 }
2873
2874 if !vm.is_none(&reraised_eg) {
2876 raised.push(reraised_eg);
2877 }
2878
2879 if raised.len() == 1 {
2881 return Ok(raised.into_iter().next().unwrap());
2882 }
2883
2884 let excs_tuple = vm.ctx.new_tuple(raised);
2886 let eg_type: PyObjectRef = crate::exception_group::exception_group().to_owned().into();
2887 eg_type.call((vm.ctx.new_str(""), excs_tuple), vm)
2888}
2889
2890fn is_exception_from_orig(exc: &PyObjectRef, orig: &PyObjectRef, vm: &VirtualMachine) -> bool {
2894 let mut exc_leaf_ids = HashSet::new();
2896 collect_exception_group_leaf_ids(exc, &mut exc_leaf_ids, vm);
2897
2898 if exc_leaf_ids.is_empty() {
2899 return false;
2900 }
2901
2902 let mut orig_leaf_ids = HashSet::new();
2904 collect_exception_group_leaf_ids(orig, &mut orig_leaf_ids, vm);
2905
2906 exc_leaf_ids.iter().all(|id| orig_leaf_ids.contains(id))
2908}
2909
2910fn collect_exception_group_leaf_ids(
2912 exc: &PyObjectRef,
2913 leaf_ids: &mut HashSet<usize>,
2914 vm: &VirtualMachine,
2915) {
2916 if vm.is_none(exc) {
2917 return;
2918 }
2919
2920 if !exc.fast_isinstance(vm.ctx.exceptions.base_exception_group) {
2922 leaf_ids.insert(exc.get_id());
2923 return;
2924 }
2925
2926 if let Ok(excs_attr) = exc.get_attr("exceptions", vm)
2928 && let Ok(tuple) = excs_attr.downcast::<PyTuple>()
2929 {
2930 for e in tuple.iter() {
2931 collect_exception_group_leaf_ids(e, leaf_ids, vm);
2932 }
2933 }
2934}
2935
2936fn exception_group_projection(
2940 orig: &PyObjectRef,
2941 keep: &[PyObjectRef],
2942 vm: &VirtualMachine,
2943) -> PyResult {
2944 if keep.is_empty() {
2945 return Ok(vm.ctx.none());
2946 }
2947
2948 let mut leaf_ids = HashSet::new();
2950 for e in keep {
2951 collect_exception_group_leaf_ids(e, &mut leaf_ids, vm);
2952 }
2953
2954 split_by_leaf_ids(orig, &leaf_ids, vm)
2956}
2957
2958fn split_by_leaf_ids(
2961 exc: &PyObjectRef,
2962 leaf_ids: &HashSet<usize>,
2963 vm: &VirtualMachine,
2964) -> PyResult {
2965 if vm.is_none(exc) {
2966 return Ok(vm.ctx.none());
2967 }
2968
2969 if !exc.fast_isinstance(vm.ctx.exceptions.base_exception_group) {
2971 if leaf_ids.contains(&exc.get_id()) {
2972 return Ok(exc.clone());
2973 }
2974 return Ok(vm.ctx.none());
2975 }
2976
2977 let excs_attr = exc.get_attr("exceptions", vm)?;
2979 let tuple: PyTupleRef = excs_attr.try_into_value(vm)?;
2980
2981 let mut matched = Vec::new();
2982 for e in tuple.iter() {
2983 let m = split_by_leaf_ids(e, leaf_ids, vm)?;
2984 if !vm.is_none(&m) {
2985 matched.push(m);
2986 }
2987 }
2988
2989 if matched.is_empty() {
2990 return Ok(vm.ctx.none());
2991 }
2992
2993 let matched_tuple = vm.ctx.new_tuple(matched);
2995 vm.call_method(exc, "derive", (matched_tuple,))
2996}