rustpython_vm/
exceptions.rs

1use self::types::{PyBaseException, PyBaseExceptionRef};
2use crate::common::lock::PyRwLock;
3use crate::object::{Traverse, TraverseFn};
4use crate::{
5    builtins::{
6        traceback::PyTracebackRef, PyNone, PyStr, PyStrRef, PyTuple, PyTupleRef, PyType, PyTypeRef,
7    },
8    class::{PyClassImpl, StaticType},
9    convert::{ToPyException, ToPyObject},
10    function::{ArgIterable, FuncArgs, IntoFuncArgs},
11    py_io::{self, Write},
12    stdlib::sys,
13    suggestion::offer_suggestions,
14    types::{Callable, Constructor, Initializer, Representable},
15    AsObject, Context, Py, PyObjectRef, PyPayload, PyRef, PyResult, TryFromObject, VirtualMachine,
16};
17use crossbeam_utils::atomic::AtomicCell;
18use itertools::Itertools;
19use std::{
20    collections::HashSet,
21    io::{self, BufRead, BufReader},
22};
23
24unsafe impl Traverse for PyBaseException {
25    fn traverse(&self, tracer_fn: &mut TraverseFn) {
26        self.traceback.traverse(tracer_fn);
27        self.cause.traverse(tracer_fn);
28        self.context.traverse(tracer_fn);
29        self.args.traverse(tracer_fn);
30    }
31}
32
33impl std::fmt::Debug for PyBaseException {
34    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
35        // TODO: implement more detailed, non-recursive Debug formatter
36        f.write_str("PyBaseException")
37    }
38}
39
40impl PyPayload for PyBaseException {
41    fn class(ctx: &Context) -> &'static Py<PyType> {
42        ctx.exceptions.base_exception_type
43    }
44}
45
46impl VirtualMachine {
47    // Why `impl VirtualMachine?
48    // These functions are natively free function in CPython - not methods of PyException
49
50    /// Print exception chain by calling sys.excepthook
51    pub fn print_exception(&self, exc: PyBaseExceptionRef) {
52        let vm = self;
53        let write_fallback = |exc, errstr| {
54            if let Ok(stderr) = sys::get_stderr(vm) {
55                let mut stderr = py_io::PyWriter(stderr, vm);
56                // if this fails stderr might be closed -- ignore it
57                let _ = writeln!(stderr, "{errstr}");
58                let _ = self.write_exception(&mut stderr, exc);
59            } else {
60                eprintln!("{errstr}\nlost sys.stderr");
61                let _ = self.write_exception(&mut py_io::IoWriter(io::stderr()), exc);
62            }
63        };
64        if let Ok(excepthook) = vm.sys_module.get_attr("excepthook", vm) {
65            let (exc_type, exc_val, exc_tb) = vm.split_exception(exc.clone());
66            if let Err(eh_exc) = excepthook.call((exc_type, exc_val, exc_tb), vm) {
67                write_fallback(&eh_exc, "Error in sys.excepthook:");
68                write_fallback(&exc, "Original exception was:");
69            }
70        } else {
71            write_fallback(&exc, "missing sys.excepthook");
72        }
73    }
74
75    pub fn write_exception<W: Write>(
76        &self,
77        output: &mut W,
78        exc: &PyBaseExceptionRef,
79    ) -> Result<(), W::Error> {
80        let seen = &mut HashSet::<usize>::new();
81        self.write_exception_recursive(output, exc, seen)
82    }
83
84    fn write_exception_recursive<W: Write>(
85        &self,
86        output: &mut W,
87        exc: &PyBaseExceptionRef,
88        seen: &mut HashSet<usize>,
89    ) -> Result<(), W::Error> {
90        // This function should not be called directly,
91        // use `wite_exception` as a public interface.
92        // It is similar to `print_exception_recursive` from `CPython`.
93        seen.insert(exc.get_id());
94
95        #[allow(clippy::manual_map)]
96        if let Some((cause_or_context, msg)) = if let Some(cause) = exc.cause() {
97            // This can be a special case: `raise e from e`,
98            // we just ignore it and treat like `raise e` without any extra steps.
99            Some((
100                cause,
101                "\nThe above exception was the direct cause of the following exception:\n",
102            ))
103        } else if let Some(context) = exc.context() {
104            // This can be a special case:
105            //   e = ValueError('e')
106            //   e.__context__ = e
107            // In this case, we just ignore
108            // `__context__` part from going into recursion.
109            Some((
110                context,
111                "\nDuring handling of the above exception, another exception occurred:\n",
112            ))
113        } else {
114            None
115        } {
116            if !seen.contains(&cause_or_context.get_id()) {
117                self.write_exception_recursive(output, &cause_or_context, seen)?;
118                writeln!(output, "{msg}")?;
119            } else {
120                seen.insert(cause_or_context.get_id());
121            }
122        }
123
124        self.write_exception_inner(output, exc)
125    }
126
127    /// Print exception with traceback
128    pub fn write_exception_inner<W: Write>(
129        &self,
130        output: &mut W,
131        exc: &PyBaseExceptionRef,
132    ) -> Result<(), W::Error> {
133        let vm = self;
134        if let Some(tb) = exc.traceback.read().clone() {
135            writeln!(output, "Traceback (most recent call last):")?;
136            for tb in tb.iter() {
137                write_traceback_entry(output, &tb)?;
138            }
139        }
140
141        let varargs = exc.args();
142        let args_repr = vm.exception_args_as_string(varargs, true);
143
144        let exc_class = exc.class();
145
146        if exc_class.fast_issubclass(vm.ctx.exceptions.syntax_error) {
147            return self.write_syntaxerror(output, exc, exc_class, &args_repr);
148        }
149
150        let exc_name = exc_class.name();
151        match args_repr.len() {
152            0 => write!(output, "{exc_name}"),
153            1 => write!(output, "{}: {}", exc_name, args_repr[0]),
154            _ => write!(
155                output,
156                "{}: ({})",
157                exc_name,
158                args_repr.into_iter().format(", "),
159            ),
160        }?;
161
162        match offer_suggestions(exc, vm) {
163            Some(suggestions) => writeln!(output, ". Did you mean: '{suggestions}'?"),
164            None => writeln!(output),
165        }
166    }
167
168    /// Format and write a SyntaxError
169    /// This logic is derived from TracebackException._format_syntax_error
170    ///
171    /// The logic has support for `end_offset` to highlight a range in the source code,
172    /// but it looks like `end_offset` is not used yet when SyntaxErrors are created.
173    fn write_syntaxerror<W: Write>(
174        &self,
175        output: &mut W,
176        exc: &PyBaseExceptionRef,
177        exc_type: &Py<PyType>,
178        args_repr: &[PyRef<PyStr>],
179    ) -> Result<(), W::Error> {
180        let vm = self;
181        debug_assert!(exc_type.fast_issubclass(vm.ctx.exceptions.syntax_error));
182
183        let getattr = |attr: &'static str| exc.as_object().get_attr(attr, vm).ok();
184
185        let maybe_lineno = getattr("lineno").map(|obj| {
186            obj.str(vm)
187                .unwrap_or_else(|_| vm.ctx.new_str("<lineno str() failed>"))
188        });
189        let maybe_filename = getattr("filename").and_then(|obj| obj.str(vm).ok());
190
191        let maybe_text = getattr("text").map(|obj| {
192            obj.str(vm)
193                .unwrap_or_else(|_| vm.ctx.new_str("<text str() failed>"))
194        });
195
196        let mut filename_suffix = String::new();
197
198        if let Some(lineno) = maybe_lineno {
199            writeln!(
200                output,
201                r##"  File "{}", line {}"##,
202                maybe_filename
203                    .as_ref()
204                    .map(|s| s.as_str())
205                    .unwrap_or("<string>"),
206                lineno
207            )?;
208        } else if let Some(filename) = maybe_filename {
209            filename_suffix = format!(" ({})", filename);
210        }
211
212        if let Some(text) = maybe_text {
213            // if text ends with \n, remove it
214            let rtext = text.as_str().trim_end_matches('\n');
215            let ltext = rtext.trim_start_matches([' ', '\n', '\x0c']); // \x0c is \f
216            let spaces = (rtext.len() - ltext.len()) as isize;
217
218            writeln!(output, "    {}", ltext)?;
219
220            let maybe_offset: Option<isize> =
221                getattr("offset").and_then(|obj| obj.try_to_value::<isize>(vm).ok());
222
223            if let Some(offset) = maybe_offset {
224                let maybe_end_offset: Option<isize> =
225                    getattr("end_offset").and_then(|obj| obj.try_to_value::<isize>(vm).ok());
226
227                let mut end_offset = match maybe_end_offset {
228                    Some(0) | None => offset,
229                    Some(end_offset) => end_offset,
230                };
231
232                if offset == end_offset || end_offset == -1 {
233                    end_offset = offset + 1;
234                }
235
236                // Convert 1-based column offset to 0-based index into stripped text
237                let colno = offset - 1 - spaces;
238                let end_colno = end_offset - 1 - spaces;
239                if colno >= 0 {
240                    let caretspace = ltext.chars().collect::<Vec<_>>()[..colno as usize]
241                        .iter()
242                        .map(|c| if c.is_whitespace() { *c } else { ' ' })
243                        .collect::<String>();
244
245                    let mut error_width = end_colno - colno;
246                    if error_width < 1 {
247                        error_width = 1;
248                    }
249
250                    writeln!(
251                        output,
252                        "    {}{}",
253                        caretspace,
254                        "^".repeat(error_width as usize)
255                    )?;
256                }
257            }
258        }
259
260        let exc_name = exc_type.name();
261
262        match args_repr.len() {
263            0 => write!(output, "{exc_name}{filename_suffix}"),
264            1 => write!(output, "{}: {}{}", exc_name, args_repr[0], filename_suffix),
265            _ => write!(
266                output,
267                "{}: ({}){}",
268                exc_name,
269                args_repr.iter().format(", "),
270                filename_suffix
271            ),
272        }?;
273
274        match offer_suggestions(exc, vm) {
275            Some(suggestions) => writeln!(output, ". Did you mean: '{suggestions}'?"),
276            None => writeln!(output),
277        }
278    }
279
280    fn exception_args_as_string(&self, varargs: PyTupleRef, str_single: bool) -> Vec<PyStrRef> {
281        let vm = self;
282        match varargs.len() {
283            0 => vec![],
284            1 => {
285                let args0_repr = if str_single {
286                    varargs[0]
287                        .str(vm)
288                        .unwrap_or_else(|_| PyStr::from("<element str() failed>").into_ref(&vm.ctx))
289                } else {
290                    varargs[0].repr(vm).unwrap_or_else(|_| {
291                        PyStr::from("<element repr() failed>").into_ref(&vm.ctx)
292                    })
293                };
294                vec![args0_repr]
295            }
296            _ => varargs
297                .iter()
298                .map(|vararg| {
299                    vararg.repr(vm).unwrap_or_else(|_| {
300                        PyStr::from("<element repr() failed>").into_ref(&vm.ctx)
301                    })
302                })
303                .collect(),
304        }
305    }
306
307    pub fn split_exception(
308        &self,
309        exc: PyBaseExceptionRef,
310    ) -> (PyObjectRef, PyObjectRef, PyObjectRef) {
311        let tb = exc.traceback().to_pyobject(self);
312        let class = exc.class().to_owned();
313        (class.into(), exc.into(), tb)
314    }
315
316    /// Similar to PyErr_NormalizeException in CPython
317    pub fn normalize_exception(
318        &self,
319        exc_type: PyObjectRef,
320        exc_val: PyObjectRef,
321        exc_tb: PyObjectRef,
322    ) -> PyResult<PyBaseExceptionRef> {
323        let ctor = ExceptionCtor::try_from_object(self, exc_type)?;
324        let exc = ctor.instantiate_value(exc_val, self)?;
325        if let Some(tb) = Option::<PyTracebackRef>::try_from_object(self, exc_tb)? {
326            exc.set_traceback(Some(tb));
327        }
328        Ok(exc)
329    }
330
331    pub fn invoke_exception(
332        &self,
333        cls: PyTypeRef,
334        args: Vec<PyObjectRef>,
335    ) -> PyResult<PyBaseExceptionRef> {
336        // TODO: fast-path built-in exceptions by directly instantiating them? Is that really worth it?
337        let res = PyType::call(&cls, args.into_args(self), self)?;
338        PyBaseExceptionRef::try_from_object(self, res)
339    }
340}
341
342fn print_source_line<W: Write>(
343    output: &mut W,
344    filename: &str,
345    lineno: usize,
346) -> Result<(), W::Error> {
347    // TODO: use io.open() method instead, when available, according to https://github.com/python/cpython/blob/main/Python/traceback.c#L393
348    // TODO: support different encodings
349    let file = match std::fs::File::open(filename) {
350        Ok(file) => file,
351        Err(_) => return Ok(()),
352    };
353    let file = BufReader::new(file);
354
355    for (i, line) in file.lines().enumerate() {
356        if i + 1 == lineno {
357            if let Ok(line) = line {
358                // Indented with 4 spaces
359                writeln!(output, "    {}", line.trim_start())?;
360            }
361            return Ok(());
362        }
363    }
364
365    Ok(())
366}
367
368/// Print exception occurrence location from traceback element
369fn write_traceback_entry<W: Write>(
370    output: &mut W,
371    tb_entry: &PyTracebackRef,
372) -> Result<(), W::Error> {
373    let filename = tb_entry.frame.code.source_path.as_str();
374    writeln!(
375        output,
376        r##"  File "{}", line {}, in {}"##,
377        filename, tb_entry.lineno, tb_entry.frame.code.obj_name
378    )?;
379    print_source_line(output, filename, tb_entry.lineno.to_usize())?;
380
381    Ok(())
382}
383
384#[derive(Clone)]
385pub enum ExceptionCtor {
386    Class(PyTypeRef),
387    Instance(PyBaseExceptionRef),
388}
389
390impl TryFromObject for ExceptionCtor {
391    fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult<Self> {
392        obj.downcast::<PyType>()
393            .and_then(|cls| {
394                if cls.fast_issubclass(vm.ctx.exceptions.base_exception_type) {
395                    Ok(Self::Class(cls))
396                } else {
397                    Err(cls.into())
398                }
399            })
400            .or_else(|obj| obj.downcast::<PyBaseException>().map(Self::Instance))
401            .map_err(|obj| {
402                vm.new_type_error(format!(
403                    "exceptions must be classes or instances deriving from BaseException, not {}",
404                    obj.class().name()
405                ))
406            })
407    }
408}
409
410impl ExceptionCtor {
411    pub fn instantiate(self, vm: &VirtualMachine) -> PyResult<PyBaseExceptionRef> {
412        match self {
413            Self::Class(cls) => vm.invoke_exception(cls, vec![]),
414            Self::Instance(exc) => Ok(exc),
415        }
416    }
417
418    pub fn instantiate_value(
419        self,
420        value: PyObjectRef,
421        vm: &VirtualMachine,
422    ) -> PyResult<PyBaseExceptionRef> {
423        let exc_inst = value.clone().downcast::<PyBaseException>().ok();
424        match (self, exc_inst) {
425            // both are instances; which would we choose?
426            (Self::Instance(_exc_a), Some(_exc_b)) => {
427                Err(vm
428                    .new_type_error("instance exception may not have a separate value".to_owned()))
429            }
430            // if the "type" is an instance and the value isn't, use the "type"
431            (Self::Instance(exc), None) => Ok(exc),
432            // if the value is an instance of the type, use the instance value
433            (Self::Class(cls), Some(exc)) if exc.fast_isinstance(&cls) => Ok(exc),
434            // otherwise; construct an exception of the type using the value as args
435            (Self::Class(cls), _) => {
436                let args = match_class!(match value {
437                    PyNone => vec![],
438                    tup @ PyTuple => tup.to_vec(),
439                    exc @ PyBaseException => exc.args().to_vec(),
440                    obj => vec![obj],
441                });
442                vm.invoke_exception(cls, args)
443            }
444        }
445    }
446}
447
448#[derive(Debug, Clone)]
449pub struct ExceptionZoo {
450    pub base_exception_type: &'static Py<PyType>,
451    pub base_exception_group: &'static Py<PyType>,
452    pub exception_group: &'static Py<PyType>,
453    pub system_exit: &'static Py<PyType>,
454    pub keyboard_interrupt: &'static Py<PyType>,
455    pub generator_exit: &'static Py<PyType>,
456    pub exception_type: &'static Py<PyType>,
457    pub stop_iteration: &'static Py<PyType>,
458    pub stop_async_iteration: &'static Py<PyType>,
459    pub arithmetic_error: &'static Py<PyType>,
460    pub floating_point_error: &'static Py<PyType>,
461    pub overflow_error: &'static Py<PyType>,
462    pub zero_division_error: &'static Py<PyType>,
463    pub assertion_error: &'static Py<PyType>,
464    pub attribute_error: &'static Py<PyType>,
465    pub buffer_error: &'static Py<PyType>,
466    pub eof_error: &'static Py<PyType>,
467    pub import_error: &'static Py<PyType>,
468    pub module_not_found_error: &'static Py<PyType>,
469    pub lookup_error: &'static Py<PyType>,
470    pub index_error: &'static Py<PyType>,
471    pub key_error: &'static Py<PyType>,
472    pub memory_error: &'static Py<PyType>,
473    pub name_error: &'static Py<PyType>,
474    pub unbound_local_error: &'static Py<PyType>,
475    pub os_error: &'static Py<PyType>,
476    pub blocking_io_error: &'static Py<PyType>,
477    pub child_process_error: &'static Py<PyType>,
478    pub connection_error: &'static Py<PyType>,
479    pub broken_pipe_error: &'static Py<PyType>,
480    pub connection_aborted_error: &'static Py<PyType>,
481    pub connection_refused_error: &'static Py<PyType>,
482    pub connection_reset_error: &'static Py<PyType>,
483    pub file_exists_error: &'static Py<PyType>,
484    pub file_not_found_error: &'static Py<PyType>,
485    pub interrupted_error: &'static Py<PyType>,
486    pub is_a_directory_error: &'static Py<PyType>,
487    pub not_a_directory_error: &'static Py<PyType>,
488    pub permission_error: &'static Py<PyType>,
489    pub process_lookup_error: &'static Py<PyType>,
490    pub timeout_error: &'static Py<PyType>,
491    pub reference_error: &'static Py<PyType>,
492    pub runtime_error: &'static Py<PyType>,
493    pub not_implemented_error: &'static Py<PyType>,
494    pub recursion_error: &'static Py<PyType>,
495    pub syntax_error: &'static Py<PyType>,
496    pub indentation_error: &'static Py<PyType>,
497    pub tab_error: &'static Py<PyType>,
498    pub system_error: &'static Py<PyType>,
499    pub type_error: &'static Py<PyType>,
500    pub value_error: &'static Py<PyType>,
501    pub unicode_error: &'static Py<PyType>,
502    pub unicode_decode_error: &'static Py<PyType>,
503    pub unicode_encode_error: &'static Py<PyType>,
504    pub unicode_translate_error: &'static Py<PyType>,
505
506    #[cfg(feature = "jit")]
507    pub jit_error: &'static Py<PyType>,
508
509    pub warning: &'static Py<PyType>,
510    pub deprecation_warning: &'static Py<PyType>,
511    pub pending_deprecation_warning: &'static Py<PyType>,
512    pub runtime_warning: &'static Py<PyType>,
513    pub syntax_warning: &'static Py<PyType>,
514    pub user_warning: &'static Py<PyType>,
515    pub future_warning: &'static Py<PyType>,
516    pub import_warning: &'static Py<PyType>,
517    pub unicode_warning: &'static Py<PyType>,
518    pub bytes_warning: &'static Py<PyType>,
519    pub resource_warning: &'static Py<PyType>,
520    pub encoding_warning: &'static Py<PyType>,
521}
522
523macro_rules! extend_exception {
524    (
525        $exc_struct:ident,
526        $ctx:expr,
527        $class:expr
528    ) => {
529        extend_exception!($exc_struct, $ctx, $class, {});
530    };
531    (
532        $exc_struct:ident,
533        $ctx:expr,
534        $class:expr,
535        { $($name:expr => $value:expr),* $(,)* }
536    ) => {
537        $exc_struct::extend_class($ctx, $class);
538        extend_class!($ctx, $class, {
539            $($name => $value,)*
540        });
541    };
542}
543
544impl PyBaseException {
545    pub(crate) fn new(args: Vec<PyObjectRef>, vm: &VirtualMachine) -> PyBaseException {
546        PyBaseException {
547            traceback: PyRwLock::new(None),
548            cause: PyRwLock::new(None),
549            context: PyRwLock::new(None),
550            suppress_context: AtomicCell::new(false),
551            args: PyRwLock::new(PyTuple::new_ref(args, &vm.ctx)),
552        }
553    }
554
555    pub fn get_arg(&self, idx: usize) -> Option<PyObjectRef> {
556        self.args.read().get(idx).cloned()
557    }
558}
559
560#[pyclass(
561    with(PyRef, Constructor, Initializer, Representable),
562    flags(BASETYPE, HAS_DICT)
563)]
564impl PyBaseException {
565    #[pygetset]
566    pub fn args(&self) -> PyTupleRef {
567        self.args.read().clone()
568    }
569
570    #[pygetset(setter)]
571    fn set_args(&self, args: ArgIterable, vm: &VirtualMachine) -> PyResult<()> {
572        let args = args.iter(vm)?.collect::<PyResult<Vec<_>>>()?;
573        *self.args.write() = PyTuple::new_ref(args, &vm.ctx);
574        Ok(())
575    }
576
577    #[pygetset(magic)]
578    pub fn traceback(&self) -> Option<PyTracebackRef> {
579        self.traceback.read().clone()
580    }
581
582    #[pygetset(magic, setter)]
583    pub fn set_traceback(&self, traceback: Option<PyTracebackRef>) {
584        *self.traceback.write() = traceback;
585    }
586
587    #[pygetset(magic)]
588    pub fn cause(&self) -> Option<PyRef<Self>> {
589        self.cause.read().clone()
590    }
591
592    #[pygetset(magic, setter)]
593    pub fn set_cause(&self, cause: Option<PyRef<Self>>) {
594        let mut c = self.cause.write();
595        self.set_suppress_context(true);
596        *c = cause;
597    }
598
599    #[pygetset(magic)]
600    pub fn context(&self) -> Option<PyRef<Self>> {
601        self.context.read().clone()
602    }
603
604    #[pygetset(magic, setter)]
605    pub fn set_context(&self, context: Option<PyRef<Self>>) {
606        *self.context.write() = context;
607    }
608
609    #[pygetset(name = "__suppress_context__")]
610    pub(super) fn get_suppress_context(&self) -> bool {
611        self.suppress_context.load()
612    }
613
614    #[pygetset(name = "__suppress_context__", setter)]
615    fn set_suppress_context(&self, suppress_context: bool) {
616        self.suppress_context.store(suppress_context);
617    }
618
619    #[pymethod(magic)]
620    pub(super) fn str(&self, vm: &VirtualMachine) -> PyStrRef {
621        let str_args = vm.exception_args_as_string(self.args(), true);
622        match str_args.into_iter().exactly_one() {
623            Err(i) if i.len() == 0 => vm.ctx.empty_str.to_owned(),
624            Ok(s) => s,
625            Err(i) => PyStr::from(format!("({})", i.format(", "))).into_ref(&vm.ctx),
626        }
627    }
628}
629
630#[pyclass]
631impl PyRef<PyBaseException> {
632    #[pymethod]
633    fn with_traceback(self, tb: Option<PyTracebackRef>) -> PyResult<Self> {
634        *self.traceback.write() = tb;
635        Ok(self)
636    }
637
638    #[pymethod(magic)]
639    fn reduce(self, vm: &VirtualMachine) -> PyTupleRef {
640        if let Some(dict) = self.as_object().dict().filter(|x| !x.is_empty()) {
641            vm.new_tuple((self.class().to_owned(), self.args(), dict))
642        } else {
643            vm.new_tuple((self.class().to_owned(), self.args()))
644        }
645    }
646}
647
648impl Constructor for PyBaseException {
649    type Args = FuncArgs;
650
651    fn py_new(cls: PyTypeRef, args: FuncArgs, vm: &VirtualMachine) -> PyResult {
652        PyBaseException::new(args.args, vm)
653            .into_ref_with_type(vm, cls)
654            .map(Into::into)
655    }
656}
657
658impl Initializer for PyBaseException {
659    type Args = FuncArgs;
660
661    fn init(zelf: PyRef<Self>, args: Self::Args, vm: &VirtualMachine) -> PyResult<()> {
662        *zelf.args.write() = PyTuple::new_ref(args.args, &vm.ctx);
663        Ok(())
664    }
665}
666
667impl Representable for PyBaseException {
668    #[inline]
669    fn repr_str(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<String> {
670        let repr_args = vm.exception_args_as_string(zelf.args(), false);
671        let cls = zelf.class();
672        Ok(format!("{}({})", cls.name(), repr_args.iter().format(", ")))
673    }
674}
675
676impl ExceptionZoo {
677    pub(crate) fn init() -> Self {
678        use self::types::*;
679
680        let base_exception_type = PyBaseException::init_builtin_type();
681
682        // Sorted By Hierarchy then alphabetized.
683        let base_exception_group = PyBaseExceptionGroup::init_builtin_type();
684        let exception_group = PyExceptionGroup::init_builtin_type();
685        let system_exit = PySystemExit::init_builtin_type();
686        let keyboard_interrupt = PyKeyboardInterrupt::init_builtin_type();
687        let generator_exit = PyGeneratorExit::init_builtin_type();
688
689        let exception_type = PyException::init_builtin_type();
690        let stop_iteration = PyStopIteration::init_builtin_type();
691        let stop_async_iteration = PyStopAsyncIteration::init_builtin_type();
692        let arithmetic_error = PyArithmeticError::init_builtin_type();
693        let floating_point_error = PyFloatingPointError::init_builtin_type();
694        let overflow_error = PyOverflowError::init_builtin_type();
695        let zero_division_error = PyZeroDivisionError::init_builtin_type();
696
697        let assertion_error = PyAssertionError::init_builtin_type();
698        let attribute_error = PyAttributeError::init_builtin_type();
699        let buffer_error = PyBufferError::init_builtin_type();
700        let eof_error = PyEOFError::init_builtin_type();
701
702        let import_error = PyImportError::init_builtin_type();
703        let module_not_found_error = PyModuleNotFoundError::init_builtin_type();
704
705        let lookup_error = PyLookupError::init_builtin_type();
706        let index_error = PyIndexError::init_builtin_type();
707        let key_error = PyKeyError::init_builtin_type();
708
709        let memory_error = PyMemoryError::init_builtin_type();
710
711        let name_error = PyNameError::init_builtin_type();
712        let unbound_local_error = PyUnboundLocalError::init_builtin_type();
713
714        // os errors
715        let os_error = PyOSError::init_builtin_type();
716        let blocking_io_error = PyBlockingIOError::init_builtin_type();
717        let child_process_error = PyChildProcessError::init_builtin_type();
718
719        let connection_error = PyConnectionError::init_builtin_type();
720        let broken_pipe_error = PyBrokenPipeError::init_builtin_type();
721        let connection_aborted_error = PyConnectionAbortedError::init_builtin_type();
722        let connection_refused_error = PyConnectionRefusedError::init_builtin_type();
723        let connection_reset_error = PyConnectionResetError::init_builtin_type();
724
725        let file_exists_error = PyFileExistsError::init_builtin_type();
726        let file_not_found_error = PyFileNotFoundError::init_builtin_type();
727        let interrupted_error = PyInterruptedError::init_builtin_type();
728        let is_a_directory_error = PyIsADirectoryError::init_builtin_type();
729        let not_a_directory_error = PyNotADirectoryError::init_builtin_type();
730        let permission_error = PyPermissionError::init_builtin_type();
731        let process_lookup_error = PyProcessLookupError::init_builtin_type();
732        let timeout_error = PyTimeoutError::init_builtin_type();
733
734        let reference_error = PyReferenceError::init_builtin_type();
735
736        let runtime_error = PyRuntimeError::init_builtin_type();
737        let not_implemented_error = PyNotImplementedError::init_builtin_type();
738        let recursion_error = PyRecursionError::init_builtin_type();
739
740        let syntax_error = PySyntaxError::init_builtin_type();
741        let indentation_error = PyIndentationError::init_builtin_type();
742        let tab_error = PyTabError::init_builtin_type();
743
744        let system_error = PySystemError::init_builtin_type();
745        let type_error = PyTypeError::init_builtin_type();
746        let value_error = PyValueError::init_builtin_type();
747        let unicode_error = PyUnicodeError::init_builtin_type();
748        let unicode_decode_error = PyUnicodeDecodeError::init_builtin_type();
749        let unicode_encode_error = PyUnicodeEncodeError::init_builtin_type();
750        let unicode_translate_error = PyUnicodeTranslateError::init_builtin_type();
751
752        #[cfg(feature = "jit")]
753        let jit_error = PyJitError::init_builtin_type();
754
755        let warning = PyWarning::init_builtin_type();
756        let deprecation_warning = PyDeprecationWarning::init_builtin_type();
757        let pending_deprecation_warning = PyPendingDeprecationWarning::init_builtin_type();
758        let runtime_warning = PyRuntimeWarning::init_builtin_type();
759        let syntax_warning = PySyntaxWarning::init_builtin_type();
760        let user_warning = PyUserWarning::init_builtin_type();
761        let future_warning = PyFutureWarning::init_builtin_type();
762        let import_warning = PyImportWarning::init_builtin_type();
763        let unicode_warning = PyUnicodeWarning::init_builtin_type();
764        let bytes_warning = PyBytesWarning::init_builtin_type();
765        let resource_warning = PyResourceWarning::init_builtin_type();
766        let encoding_warning = PyEncodingWarning::init_builtin_type();
767
768        Self {
769            base_exception_type,
770            base_exception_group,
771            exception_group,
772            system_exit,
773            keyboard_interrupt,
774            generator_exit,
775            exception_type,
776            stop_iteration,
777            stop_async_iteration,
778            arithmetic_error,
779            floating_point_error,
780            overflow_error,
781            zero_division_error,
782            assertion_error,
783            attribute_error,
784            buffer_error,
785            eof_error,
786            import_error,
787            module_not_found_error,
788            lookup_error,
789            index_error,
790            key_error,
791            memory_error,
792            name_error,
793            unbound_local_error,
794            os_error,
795            blocking_io_error,
796            child_process_error,
797            connection_error,
798            broken_pipe_error,
799            connection_aborted_error,
800            connection_refused_error,
801            connection_reset_error,
802            file_exists_error,
803            file_not_found_error,
804            interrupted_error,
805            is_a_directory_error,
806            not_a_directory_error,
807            permission_error,
808            process_lookup_error,
809            timeout_error,
810            reference_error,
811            runtime_error,
812            not_implemented_error,
813            recursion_error,
814            syntax_error,
815            indentation_error,
816            tab_error,
817            system_error,
818            type_error,
819            value_error,
820            unicode_error,
821            unicode_decode_error,
822            unicode_encode_error,
823            unicode_translate_error,
824
825            #[cfg(feature = "jit")]
826            jit_error,
827
828            warning,
829            deprecation_warning,
830            pending_deprecation_warning,
831            runtime_warning,
832            syntax_warning,
833            user_warning,
834            future_warning,
835            import_warning,
836            unicode_warning,
837            bytes_warning,
838            resource_warning,
839            encoding_warning,
840        }
841    }
842
843    // TODO: remove it after fixing `errno` / `winerror` problem
844    #[allow(clippy::redundant_clone)]
845    pub fn extend(ctx: &Context) {
846        use self::types::*;
847
848        let excs = &ctx.exceptions;
849
850        PyBaseException::extend_class(ctx, excs.base_exception_type);
851
852        // Sorted By Hierarchy then alphabetized.
853        extend_exception!(PyBaseExceptionGroup, ctx, excs.base_exception_group, {
854            "message" => ctx.new_readonly_getset("message", excs.base_exception_group, make_arg_getter(0)),
855            "exceptions" => ctx.new_readonly_getset("exceptions", excs.base_exception_group, make_arg_getter(1)),
856        });
857        extend_exception!(PyExceptionGroup, ctx, excs.exception_group);
858        extend_exception!(PySystemExit, ctx, excs.system_exit, {
859            "code" => ctx.new_readonly_getset("code", excs.system_exit, system_exit_code),
860        });
861        extend_exception!(PyKeyboardInterrupt, ctx, excs.keyboard_interrupt);
862        extend_exception!(PyGeneratorExit, ctx, excs.generator_exit);
863
864        extend_exception!(PyException, ctx, excs.exception_type);
865
866        extend_exception!(PyStopIteration, ctx, excs.stop_iteration, {
867            "value" => ctx.none(),
868        });
869        extend_exception!(PyStopAsyncIteration, ctx, excs.stop_async_iteration);
870
871        extend_exception!(PyArithmeticError, ctx, excs.arithmetic_error);
872        extend_exception!(PyFloatingPointError, ctx, excs.floating_point_error);
873        extend_exception!(PyOverflowError, ctx, excs.overflow_error);
874        extend_exception!(PyZeroDivisionError, ctx, excs.zero_division_error);
875
876        extend_exception!(PyAssertionError, ctx, excs.assertion_error);
877        extend_exception!(PyAttributeError, ctx, excs.attribute_error, {
878            "name" => ctx.none(),
879            "obj" => ctx.none(),
880        });
881        extend_exception!(PyBufferError, ctx, excs.buffer_error);
882        extend_exception!(PyEOFError, ctx, excs.eof_error);
883
884        extend_exception!(PyImportError, ctx, excs.import_error, {
885            "msg" => ctx.new_readonly_getset("msg", excs.import_error, make_arg_getter(0)),
886            "name" => ctx.none(),
887            "path" => ctx.none(),
888        });
889        extend_exception!(PyModuleNotFoundError, ctx, excs.module_not_found_error);
890
891        extend_exception!(PyLookupError, ctx, excs.lookup_error);
892        extend_exception!(PyIndexError, ctx, excs.index_error);
893
894        extend_exception!(PyKeyError, ctx, excs.key_error);
895
896        extend_exception!(PyMemoryError, ctx, excs.memory_error);
897        extend_exception!(PyNameError, ctx, excs.name_error, {
898            "name" => ctx.none(),
899        });
900        extend_exception!(PyUnboundLocalError, ctx, excs.unbound_local_error);
901
902        // os errors:
903        let errno_getter =
904            ctx.new_readonly_getset("errno", excs.os_error, |exc: PyBaseExceptionRef| {
905                let args = exc.args();
906                args.first()
907                    .filter(|_| args.len() > 1 && args.len() <= 5)
908                    .cloned()
909            });
910        let strerror_getter =
911            ctx.new_readonly_getset("strerror", excs.os_error, |exc: PyBaseExceptionRef| {
912                let args = exc.args();
913                args.get(1)
914                    .filter(|_| args.len() >= 2 && args.len() <= 5)
915                    .cloned()
916            });
917        extend_exception!(PyOSError, ctx, excs.os_error, {
918            // POSIX exception code
919            "errno" => errno_getter.clone(),
920            // exception strerror
921            "strerror" => strerror_getter.clone(),
922            // exception filename
923            "filename" => ctx.none(),
924            // second exception filename
925            "filename2" => ctx.none(),
926        });
927        #[cfg(windows)]
928        excs.os_error.set_str_attr("winerror", ctx.none(), ctx);
929
930        extend_exception!(PyBlockingIOError, ctx, excs.blocking_io_error);
931        extend_exception!(PyChildProcessError, ctx, excs.child_process_error);
932
933        extend_exception!(PyConnectionError, ctx, excs.connection_error);
934        extend_exception!(PyBrokenPipeError, ctx, excs.broken_pipe_error);
935        extend_exception!(PyConnectionAbortedError, ctx, excs.connection_aborted_error);
936        extend_exception!(PyConnectionRefusedError, ctx, excs.connection_refused_error);
937        extend_exception!(PyConnectionResetError, ctx, excs.connection_reset_error);
938
939        extend_exception!(PyFileExistsError, ctx, excs.file_exists_error);
940        extend_exception!(PyFileNotFoundError, ctx, excs.file_not_found_error);
941        extend_exception!(PyInterruptedError, ctx, excs.interrupted_error);
942        extend_exception!(PyIsADirectoryError, ctx, excs.is_a_directory_error);
943        extend_exception!(PyNotADirectoryError, ctx, excs.not_a_directory_error);
944        extend_exception!(PyPermissionError, ctx, excs.permission_error);
945        extend_exception!(PyProcessLookupError, ctx, excs.process_lookup_error);
946        extend_exception!(PyTimeoutError, ctx, excs.timeout_error);
947
948        extend_exception!(PyReferenceError, ctx, excs.reference_error);
949        extend_exception!(PyRuntimeError, ctx, excs.runtime_error);
950        extend_exception!(PyNotImplementedError, ctx, excs.not_implemented_error);
951        extend_exception!(PyRecursionError, ctx, excs.recursion_error);
952
953        extend_exception!(PySyntaxError, ctx, excs.syntax_error, {
954            "msg" => ctx.new_readonly_getset("msg", excs.syntax_error, make_arg_getter(0)),
955            // TODO: members
956            "filename" => ctx.none(),
957            "lineno" => ctx.none(),
958            "end_lineno" => ctx.none(),
959            "offset" => ctx.none(),
960            "end_offset" => ctx.none(),
961            "text" => ctx.none(),
962        });
963        extend_exception!(PyIndentationError, ctx, excs.indentation_error);
964        extend_exception!(PyTabError, ctx, excs.tab_error);
965
966        extend_exception!(PySystemError, ctx, excs.system_error);
967        extend_exception!(PyTypeError, ctx, excs.type_error);
968        extend_exception!(PyValueError, ctx, excs.value_error);
969        extend_exception!(PyUnicodeError, ctx, excs.unicode_error, {
970            "encoding" => ctx.new_readonly_getset("encoding", excs.unicode_error, make_arg_getter(0)),
971            "object" => ctx.new_readonly_getset("object", excs.unicode_error, make_arg_getter(1)),
972            "start" => ctx.new_readonly_getset("start", excs.unicode_error, make_arg_getter(2)),
973            "end" => ctx.new_readonly_getset("end", excs.unicode_error, make_arg_getter(3)),
974            "reason" => ctx.new_readonly_getset("reason", excs.unicode_error, make_arg_getter(4)),
975        });
976        extend_exception!(PyUnicodeDecodeError, ctx, excs.unicode_decode_error);
977        extend_exception!(PyUnicodeEncodeError, ctx, excs.unicode_encode_error);
978        extend_exception!(PyUnicodeTranslateError, ctx, excs.unicode_translate_error, {
979            "encoding" => ctx.new_readonly_getset("encoding", excs.unicode_translate_error, none_getter),
980            "object" => ctx.new_readonly_getset("object", excs.unicode_translate_error, make_arg_getter(0)),
981            "start" => ctx.new_readonly_getset("start", excs.unicode_translate_error, make_arg_getter(1)),
982            "end" => ctx.new_readonly_getset("end", excs.unicode_translate_error, make_arg_getter(2)),
983            "reason" => ctx.new_readonly_getset("reason", excs.unicode_translate_error, make_arg_getter(3)),
984        });
985
986        #[cfg(feature = "jit")]
987        extend_exception!(PyJitError, ctx, excs.jit_error);
988
989        extend_exception!(PyWarning, ctx, excs.warning);
990        extend_exception!(PyDeprecationWarning, ctx, excs.deprecation_warning);
991        extend_exception!(
992            PyPendingDeprecationWarning,
993            ctx,
994            excs.pending_deprecation_warning
995        );
996        extend_exception!(PyRuntimeWarning, ctx, excs.runtime_warning);
997        extend_exception!(PySyntaxWarning, ctx, excs.syntax_warning);
998        extend_exception!(PyUserWarning, ctx, excs.user_warning);
999        extend_exception!(PyFutureWarning, ctx, excs.future_warning);
1000        extend_exception!(PyImportWarning, ctx, excs.import_warning);
1001        extend_exception!(PyUnicodeWarning, ctx, excs.unicode_warning);
1002        extend_exception!(PyBytesWarning, ctx, excs.bytes_warning);
1003        extend_exception!(PyResourceWarning, ctx, excs.resource_warning);
1004        extend_exception!(PyEncodingWarning, ctx, excs.encoding_warning);
1005    }
1006}
1007
1008fn none_getter(_obj: PyObjectRef, vm: &VirtualMachine) -> PyRef<PyNone> {
1009    vm.ctx.none.clone()
1010}
1011
1012fn make_arg_getter(idx: usize) -> impl Fn(PyBaseExceptionRef) -> Option<PyObjectRef> {
1013    move |exc| exc.get_arg(idx)
1014}
1015
1016fn system_exit_code(exc: PyBaseExceptionRef) -> Option<PyObjectRef> {
1017    exc.args.read().first().map(|code| {
1018        match_class!(match code {
1019            ref tup @ PyTuple => match tup.as_slice() {
1020                [x] => x.clone(),
1021                _ => code.clone(),
1022            },
1023            other => other.clone(),
1024        })
1025    })
1026}
1027
1028#[cfg(feature = "serde")]
1029pub struct SerializeException<'vm, 's> {
1030    vm: &'vm VirtualMachine,
1031    exc: &'s PyBaseExceptionRef,
1032}
1033
1034#[cfg(feature = "serde")]
1035impl<'vm, 's> SerializeException<'vm, 's> {
1036    pub fn new(vm: &'vm VirtualMachine, exc: &'s PyBaseExceptionRef) -> Self {
1037        SerializeException { vm, exc }
1038    }
1039}
1040
1041#[cfg(feature = "serde")]
1042pub struct SerializeExceptionOwned<'vm> {
1043    vm: &'vm VirtualMachine,
1044    exc: PyBaseExceptionRef,
1045}
1046
1047#[cfg(feature = "serde")]
1048impl serde::Serialize for SerializeExceptionOwned<'_> {
1049    fn serialize<S: serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
1050        let Self { vm, exc } = self;
1051        SerializeException::new(vm, exc).serialize(s)
1052    }
1053}
1054
1055#[cfg(feature = "serde")]
1056impl serde::Serialize for SerializeException<'_, '_> {
1057    fn serialize<S: serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
1058        use serde::ser::*;
1059
1060        let mut struc = s.serialize_struct("PyBaseException", 7)?;
1061        struc.serialize_field("exc_type", &*self.exc.class().name())?;
1062        let tbs = {
1063            struct Tracebacks(PyTracebackRef);
1064            impl serde::Serialize for Tracebacks {
1065                fn serialize<S: serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
1066                    let mut s = s.serialize_seq(None)?;
1067                    for tb in self.0.iter() {
1068                        s.serialize_element(&**tb)?;
1069                    }
1070                    s.end()
1071                }
1072            }
1073            self.exc.traceback().map(Tracebacks)
1074        };
1075        struc.serialize_field("traceback", &tbs)?;
1076        struc.serialize_field(
1077            "cause",
1078            &self
1079                .exc
1080                .cause()
1081                .map(|exc| SerializeExceptionOwned { vm: self.vm, exc }),
1082        )?;
1083        struc.serialize_field(
1084            "context",
1085            &self
1086                .exc
1087                .context()
1088                .map(|exc| SerializeExceptionOwned { vm: self.vm, exc }),
1089        )?;
1090        struc.serialize_field("suppress_context", &self.exc.get_suppress_context())?;
1091
1092        let args = {
1093            struct Args<'vm>(&'vm VirtualMachine, PyTupleRef);
1094            impl serde::Serialize for Args<'_> {
1095                fn serialize<S: serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
1096                    s.collect_seq(
1097                        self.1
1098                            .iter()
1099                            .map(|arg| crate::py_serde::PyObjectSerializer::new(self.0, arg)),
1100                    )
1101                }
1102            }
1103            Args(self.vm, self.exc.args())
1104        };
1105        struc.serialize_field("args", &args)?;
1106
1107        let rendered = {
1108            let mut rendered = String::new();
1109            self.vm
1110                .write_exception(&mut rendered, self.exc)
1111                .map_err(S::Error::custom)?;
1112            rendered
1113        };
1114        struc.serialize_field("rendered", &rendered)?;
1115
1116        struc.end()
1117    }
1118}
1119
1120pub fn cstring_error(vm: &VirtualMachine) -> PyBaseExceptionRef {
1121    vm.new_value_error("embedded null character".to_owned())
1122}
1123
1124impl ToPyException for std::ffi::NulError {
1125    fn to_pyexception(&self, vm: &VirtualMachine) -> PyBaseExceptionRef {
1126        cstring_error(vm)
1127    }
1128}
1129
1130#[cfg(windows)]
1131impl<C> ToPyException for widestring::error::ContainsNul<C> {
1132    fn to_pyexception(&self, vm: &VirtualMachine) -> PyBaseExceptionRef {
1133        cstring_error(vm)
1134    }
1135}
1136
1137#[cfg(any(unix, windows, target_os = "wasi"))]
1138pub(crate) fn errno_to_exc_type(errno: i32, vm: &VirtualMachine) -> Option<&'static Py<PyType>> {
1139    use crate::stdlib::errno::errors;
1140    let excs = &vm.ctx.exceptions;
1141    match errno {
1142        errors::EWOULDBLOCK => Some(excs.blocking_io_error),
1143        errors::EALREADY => Some(excs.blocking_io_error),
1144        errors::EINPROGRESS => Some(excs.blocking_io_error),
1145        errors::EPIPE => Some(excs.broken_pipe_error),
1146        #[cfg(not(target_os = "wasi"))]
1147        errors::ESHUTDOWN => Some(excs.broken_pipe_error),
1148        errors::ECHILD => Some(excs.child_process_error),
1149        errors::ECONNABORTED => Some(excs.connection_aborted_error),
1150        errors::ECONNREFUSED => Some(excs.connection_refused_error),
1151        errors::ECONNRESET => Some(excs.connection_reset_error),
1152        errors::EEXIST => Some(excs.file_exists_error),
1153        errors::ENOENT => Some(excs.file_not_found_error),
1154        errors::EISDIR => Some(excs.is_a_directory_error),
1155        errors::ENOTDIR => Some(excs.not_a_directory_error),
1156        errors::EINTR => Some(excs.interrupted_error),
1157        errors::EACCES => Some(excs.permission_error),
1158        errors::EPERM => Some(excs.permission_error),
1159        errors::ESRCH => Some(excs.process_lookup_error),
1160        errors::ETIMEDOUT => Some(excs.timeout_error),
1161        _ => None,
1162    }
1163}
1164
1165#[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))]
1166pub(crate) fn errno_to_exc_type(_errno: i32, _vm: &VirtualMachine) -> Option<&'static Py<PyType>> {
1167    None
1168}
1169
1170pub(super) mod types {
1171    use crate::common::lock::PyRwLock;
1172    #[cfg_attr(target_arch = "wasm32", allow(unused_imports))]
1173    use crate::{
1174        builtins::{
1175            traceback::PyTracebackRef, tuple::IntoPyTuple, PyInt, PyStrRef, PyTupleRef, PyTypeRef,
1176        },
1177        convert::ToPyResult,
1178        function::FuncArgs,
1179        types::{Constructor, Initializer},
1180        AsObject, PyObjectRef, PyRef, PyResult, VirtualMachine,
1181    };
1182    use crossbeam_utils::atomic::AtomicCell;
1183    use itertools::Itertools;
1184
1185    // This module is designed to be used as `use builtins::*;`.
1186    // Do not add any pub symbols not included in builtins module.
1187    // `PyBaseExceptionRef` is the only exception.
1188
1189    pub type PyBaseExceptionRef = PyRef<PyBaseException>;
1190
1191    // Sorted By Hierarchy then alphabetized.
1192
1193    #[pyclass(module = false, name = "BaseException", traverse = "manual")]
1194    pub struct PyBaseException {
1195        pub(super) traceback: PyRwLock<Option<PyTracebackRef>>,
1196        pub(super) cause: PyRwLock<Option<PyRef<Self>>>,
1197        pub(super) context: PyRwLock<Option<PyRef<Self>>>,
1198        pub(super) suppress_context: AtomicCell<bool>,
1199        pub(super) args: PyRwLock<PyTupleRef>,
1200    }
1201
1202    #[pyexception(name, base = "PyBaseException", ctx = "system_exit", impl)]
1203    #[derive(Debug)]
1204    pub struct PySystemExit {}
1205
1206    #[pyexception(name, base = "PyBaseException", ctx = "base_exception_group", impl)]
1207    #[derive(Debug)]
1208    pub struct PyBaseExceptionGroup {}
1209
1210    #[pyexception(name, base = "PyBaseExceptionGroup", ctx = "exception_group", impl)]
1211    #[derive(Debug)]
1212    pub struct PyExceptionGroup {}
1213
1214    #[pyexception(name, base = "PyBaseException", ctx = "generator_exit", impl)]
1215    #[derive(Debug)]
1216    pub struct PyGeneratorExit {}
1217
1218    #[pyexception(name, base = "PyBaseException", ctx = "keyboard_interrupt", impl)]
1219    #[derive(Debug)]
1220    pub struct PyKeyboardInterrupt {}
1221
1222    #[pyexception(name, base = "PyBaseException", ctx = "exception_type", impl)]
1223    #[derive(Debug)]
1224    pub struct PyException {}
1225
1226    #[pyexception(name, base = "PyException", ctx = "stop_iteration")]
1227    #[derive(Debug)]
1228    pub struct PyStopIteration {}
1229
1230    #[pyexception]
1231    impl PyStopIteration {
1232        #[pyslot]
1233        #[pymethod(name = "__init__")]
1234        pub(crate) fn slot_init(
1235            zelf: PyObjectRef,
1236            args: ::rustpython_vm::function::FuncArgs,
1237            vm: &::rustpython_vm::VirtualMachine,
1238        ) -> ::rustpython_vm::PyResult<()> {
1239            zelf.set_attr("value", vm.unwrap_or_none(args.args.first().cloned()), vm)?;
1240            Ok(())
1241        }
1242    }
1243
1244    #[pyexception(name, base = "PyException", ctx = "stop_async_iteration", impl)]
1245    #[derive(Debug)]
1246    pub struct PyStopAsyncIteration {}
1247
1248    #[pyexception(name, base = "PyException", ctx = "arithmetic_error", impl)]
1249    #[derive(Debug)]
1250    pub struct PyArithmeticError {}
1251
1252    #[pyexception(name, base = "PyArithmeticError", ctx = "floating_point_error", impl)]
1253    #[derive(Debug)]
1254    pub struct PyFloatingPointError {}
1255
1256    #[pyexception(name, base = "PyArithmeticError", ctx = "overflow_error", impl)]
1257    #[derive(Debug)]
1258    pub struct PyOverflowError {}
1259
1260    #[pyexception(name, base = "PyArithmeticError", ctx = "zero_division_error", impl)]
1261    #[derive(Debug)]
1262    pub struct PyZeroDivisionError {}
1263
1264    #[pyexception(name, base = "PyException", ctx = "assertion_error", impl)]
1265    #[derive(Debug)]
1266    pub struct PyAssertionError {}
1267
1268    #[pyexception(name, base = "PyException", ctx = "attribute_error", impl)]
1269    #[derive(Debug)]
1270    pub struct PyAttributeError {}
1271
1272    #[pyexception(name, base = "PyException", ctx = "buffer_error", impl)]
1273    #[derive(Debug)]
1274    pub struct PyBufferError {}
1275
1276    #[pyexception(name, base = "PyException", ctx = "eof_error", impl)]
1277    #[derive(Debug)]
1278    pub struct PyEOFError {}
1279
1280    #[pyexception(name, base = "PyException", ctx = "import_error")]
1281    #[derive(Debug)]
1282    pub struct PyImportError {}
1283
1284    #[pyexception]
1285    impl PyImportError {
1286        #[pyslot]
1287        #[pymethod(name = "__init__")]
1288        pub(crate) fn slot_init(
1289            zelf: PyObjectRef,
1290            args: ::rustpython_vm::function::FuncArgs,
1291            vm: &::rustpython_vm::VirtualMachine,
1292        ) -> ::rustpython_vm::PyResult<()> {
1293            zelf.set_attr(
1294                "name",
1295                vm.unwrap_or_none(args.kwargs.get("name").cloned()),
1296                vm,
1297            )?;
1298            zelf.set_attr(
1299                "path",
1300                vm.unwrap_or_none(args.kwargs.get("path").cloned()),
1301                vm,
1302            )?;
1303            Ok(())
1304        }
1305        #[pymethod(magic)]
1306        fn reduce(exc: PyBaseExceptionRef, vm: &VirtualMachine) -> PyTupleRef {
1307            let obj = exc.as_object().to_owned();
1308            let mut result: Vec<PyObjectRef> = vec![
1309                obj.class().to_owned().into(),
1310                vm.new_tuple((exc.get_arg(0).unwrap(),)).into(),
1311            ];
1312
1313            if let Some(dict) = obj.dict().filter(|x| !x.is_empty()) {
1314                result.push(dict.into());
1315            }
1316
1317            result.into_pytuple(vm)
1318        }
1319    }
1320
1321    #[pyexception(name, base = "PyImportError", ctx = "module_not_found_error", impl)]
1322    #[derive(Debug)]
1323    pub struct PyModuleNotFoundError {}
1324
1325    #[pyexception(name, base = "PyException", ctx = "lookup_error", impl)]
1326    #[derive(Debug)]
1327    pub struct PyLookupError {}
1328
1329    #[pyexception(name, base = "PyLookupError", ctx = "index_error", impl)]
1330    #[derive(Debug)]
1331    pub struct PyIndexError {}
1332
1333    #[pyexception(name, base = "PyLookupError", ctx = "key_error")]
1334    #[derive(Debug)]
1335    pub struct PyKeyError {}
1336
1337    #[pyexception]
1338    impl PyKeyError {
1339        #[pymethod(magic)]
1340        fn str(exc: PyBaseExceptionRef, vm: &VirtualMachine) -> PyStrRef {
1341            let args = exc.args();
1342            if args.len() == 1 {
1343                vm.exception_args_as_string(args, false)
1344                    .into_iter()
1345                    .exactly_one()
1346                    .unwrap()
1347            } else {
1348                exc.str(vm)
1349            }
1350        }
1351    }
1352
1353    #[pyexception(name, base = "PyException", ctx = "memory_error", impl)]
1354    #[derive(Debug)]
1355    pub struct PyMemoryError {}
1356
1357    #[pyexception(name, base = "PyException", ctx = "name_error", impl)]
1358    #[derive(Debug)]
1359    pub struct PyNameError {}
1360
1361    #[pyexception(name, base = "PyNameError", ctx = "unbound_local_error", impl)]
1362    #[derive(Debug)]
1363    pub struct PyUnboundLocalError {}
1364
1365    #[pyexception(name, base = "PyException", ctx = "os_error")]
1366    #[derive(Debug)]
1367    pub struct PyOSError {}
1368
1369    // OS Errors:
1370    #[pyexception]
1371    impl PyOSError {
1372        #[cfg(not(target_arch = "wasm32"))]
1373        fn optional_new(args: Vec<PyObjectRef>, vm: &VirtualMachine) -> Option<PyBaseExceptionRef> {
1374            let len = args.len();
1375            if (2..=5).contains(&len) {
1376                let errno = &args[0];
1377                errno
1378                    .payload_if_subclass::<PyInt>(vm)
1379                    .and_then(|errno| errno.try_to_primitive::<i32>(vm).ok())
1380                    .and_then(|errno| super::errno_to_exc_type(errno, vm))
1381                    .and_then(|typ| vm.invoke_exception(typ.to_owned(), args.to_vec()).ok())
1382            } else {
1383                None
1384            }
1385        }
1386        #[cfg(not(target_arch = "wasm32"))]
1387        #[pyslot]
1388        fn slot_new(cls: PyTypeRef, args: FuncArgs, vm: &VirtualMachine) -> PyResult {
1389            // We need this method, because of how `CPython` copies `init`
1390            // from `BaseException` in `SimpleExtendsException` macro.
1391            // See: `BaseException_new`
1392            if *cls.name() == *vm.ctx.exceptions.os_error.name() {
1393                match Self::optional_new(args.args.to_vec(), vm) {
1394                    Some(error) => error.to_pyresult(vm),
1395                    None => PyBaseException::slot_new(cls, args, vm),
1396                }
1397            } else {
1398                PyBaseException::slot_new(cls, args, vm)
1399            }
1400        }
1401        #[cfg(target_arch = "wasm32")]
1402        #[pyslot]
1403        fn slot_new(cls: PyTypeRef, args: FuncArgs, vm: &VirtualMachine) -> PyResult {
1404            PyBaseException::slot_new(cls, args, vm)
1405        }
1406        #[pyslot]
1407        #[pymethod(name = "__init__")]
1408        fn slot_init(zelf: PyObjectRef, args: FuncArgs, vm: &VirtualMachine) -> PyResult<()> {
1409            let len = args.args.len();
1410            let mut new_args = args;
1411            if (3..=5).contains(&len) {
1412                zelf.set_attr("filename", new_args.args[2].clone(), vm)?;
1413                #[cfg(windows)]
1414                if let Some(winerror) = new_args.args.get(3) {
1415                    zelf.set_attr("winerror", winerror.clone(), vm)?;
1416                }
1417                if let Some(filename2) = new_args.args.get(4) {
1418                    zelf.set_attr("filename2", filename2.clone(), vm)?;
1419                }
1420
1421                new_args.args.truncate(2);
1422            }
1423            PyBaseException::slot_init(zelf, new_args, vm)
1424        }
1425
1426        #[pymethod(magic)]
1427        fn str(exc: PyBaseExceptionRef, vm: &VirtualMachine) -> PyResult<PyStrRef> {
1428            let args = exc.args();
1429            let obj = exc.as_object().to_owned();
1430
1431            if args.len() == 2 {
1432                // SAFETY: len() == 2 is checked so get_arg 1 or 2 won't panic
1433                let errno = exc.get_arg(0).unwrap().str(vm)?;
1434                let msg = exc.get_arg(1).unwrap().str(vm)?;
1435
1436                let s = match obj.get_attr("filename", vm) {
1437                    Ok(filename) => match obj.get_attr("filename2", vm) {
1438                        Ok(filename2) if !vm.is_none(&filename2) => format!(
1439                            "[Errno {}] {}: '{}' -> '{}'",
1440                            errno,
1441                            msg,
1442                            filename.str(vm)?,
1443                            filename2.str(vm)?
1444                        ),
1445                        _ => format!("[Errno {}] {}: '{}'", errno, msg, filename.str(vm)?),
1446                    },
1447                    Err(_) => {
1448                        format!("[Errno {errno}] {msg}")
1449                    }
1450                };
1451                Ok(vm.ctx.new_str(s))
1452            } else {
1453                Ok(exc.str(vm))
1454            }
1455        }
1456
1457        #[pymethod(magic)]
1458        fn reduce(exc: PyBaseExceptionRef, vm: &VirtualMachine) -> PyTupleRef {
1459            let args = exc.args();
1460            let obj = exc.as_object().to_owned();
1461            let mut result: Vec<PyObjectRef> = vec![obj.class().to_owned().into()];
1462
1463            if args.len() >= 2 && args.len() <= 5 {
1464                // SAFETY: len() == 2 is checked so get_arg 1 or 2 won't panic
1465                let errno = exc.get_arg(0).unwrap();
1466                let msg = exc.get_arg(1).unwrap();
1467
1468                if let Ok(filename) = obj.get_attr("filename", vm) {
1469                    if !vm.is_none(&filename) {
1470                        let mut args_reduced: Vec<PyObjectRef> = vec![errno, msg, filename];
1471
1472                        if let Ok(filename2) = obj.get_attr("filename2", vm) {
1473                            if !vm.is_none(&filename2) {
1474                                args_reduced.push(filename2);
1475                            }
1476                        }
1477                        result.push(args_reduced.into_pytuple(vm).into());
1478                    } else {
1479                        result.push(vm.new_tuple((errno, msg)).into());
1480                    }
1481                } else {
1482                    result.push(vm.new_tuple((errno, msg)).into());
1483                }
1484            } else {
1485                result.push(args.into());
1486            }
1487
1488            if let Some(dict) = obj.dict().filter(|x| !x.is_empty()) {
1489                result.push(dict.into());
1490            }
1491            result.into_pytuple(vm)
1492        }
1493    }
1494
1495    #[pyexception(name, base = "PyOSError", ctx = "blocking_io_error", impl)]
1496    #[derive(Debug)]
1497    pub struct PyBlockingIOError {}
1498
1499    #[pyexception(name, base = "PyOSError", ctx = "child_process_error", impl)]
1500    #[derive(Debug)]
1501    pub struct PyChildProcessError {}
1502
1503    #[pyexception(name, base = "PyOSError", ctx = "connection_error", impl)]
1504    #[derive(Debug)]
1505    pub struct PyConnectionError {}
1506
1507    #[pyexception(name, base = "PyConnectionError", ctx = "broken_pipe_error", impl)]
1508    #[derive(Debug)]
1509    pub struct PyBrokenPipeError {}
1510
1511    #[pyexception(
1512        name,
1513        base = "PyConnectionError",
1514        ctx = "connection_aborted_error",
1515        impl
1516    )]
1517    #[derive(Debug)]
1518    pub struct PyConnectionAbortedError {}
1519
1520    #[pyexception(
1521        name,
1522        base = "PyConnectionError",
1523        ctx = "connection_refused_error",
1524        impl
1525    )]
1526    #[derive(Debug)]
1527    pub struct PyConnectionRefusedError {}
1528
1529    #[pyexception(name, base = "PyConnectionError", ctx = "connection_reset_error", impl)]
1530    #[derive(Debug)]
1531    pub struct PyConnectionResetError {}
1532
1533    #[pyexception(name, base = "PyOSError", ctx = "file_exists_error", impl)]
1534    #[derive(Debug)]
1535    pub struct PyFileExistsError {}
1536
1537    #[pyexception(name, base = "PyOSError", ctx = "file_not_found_error", impl)]
1538    #[derive(Debug)]
1539    pub struct PyFileNotFoundError {}
1540
1541    #[pyexception(name, base = "PyOSError", ctx = "interrupted_error", impl)]
1542    #[derive(Debug)]
1543    pub struct PyInterruptedError {}
1544
1545    #[pyexception(name, base = "PyOSError", ctx = "is_a_directory_error", impl)]
1546    #[derive(Debug)]
1547    pub struct PyIsADirectoryError {}
1548
1549    #[pyexception(name, base = "PyOSError", ctx = "not_a_directory_error", impl)]
1550    #[derive(Debug)]
1551    pub struct PyNotADirectoryError {}
1552
1553    #[pyexception(name, base = "PyOSError", ctx = "permission_error", impl)]
1554    #[derive(Debug)]
1555    pub struct PyPermissionError {}
1556
1557    #[pyexception(name, base = "PyOSError", ctx = "process_lookup_error", impl)]
1558    #[derive(Debug)]
1559    pub struct PyProcessLookupError {}
1560
1561    #[pyexception(name, base = "PyOSError", ctx = "timeout_error", impl)]
1562    #[derive(Debug)]
1563    pub struct PyTimeoutError {}
1564
1565    #[pyexception(name, base = "PyException", ctx = "reference_error", impl)]
1566    #[derive(Debug)]
1567    pub struct PyReferenceError {}
1568
1569    #[pyexception(name, base = "PyException", ctx = "runtime_error", impl)]
1570    #[derive(Debug)]
1571    pub struct PyRuntimeError {}
1572
1573    #[pyexception(name, base = "PyRuntimeError", ctx = "not_implemented_error", impl)]
1574    #[derive(Debug)]
1575    pub struct PyNotImplementedError {}
1576
1577    #[pyexception(name, base = "PyRuntimeError", ctx = "recursion_error", impl)]
1578    #[derive(Debug)]
1579    pub struct PyRecursionError {}
1580
1581    #[pyexception(name, base = "PyException", ctx = "syntax_error")]
1582    #[derive(Debug)]
1583    pub struct PySyntaxError {}
1584
1585    #[pyexception]
1586    impl PySyntaxError {
1587        #[pymethod(magic)]
1588        fn str(exc: PyBaseExceptionRef, vm: &VirtualMachine) -> PyStrRef {
1589            fn basename(filename: &str) -> &str {
1590                let splitted = if cfg!(windows) {
1591                    filename.rsplit(&['/', '\\']).next()
1592                } else {
1593                    filename.rsplit('/').next()
1594                };
1595                splitted.unwrap_or(filename)
1596            }
1597
1598            let maybe_lineno = exc.as_object().get_attr("lineno", vm).ok().map(|obj| {
1599                obj.str(vm)
1600                    .unwrap_or_else(|_| vm.ctx.new_str("<lineno str() failed>"))
1601            });
1602            let maybe_filename = exc.as_object().get_attr("filename", vm).ok().map(|obj| {
1603                obj.str(vm)
1604                    .unwrap_or_else(|_| vm.ctx.new_str("<filename str() failed>"))
1605            });
1606
1607            let args = exc.args();
1608
1609            let msg = if args.len() == 1 {
1610                vm.exception_args_as_string(args, false)
1611                    .into_iter()
1612                    .exactly_one()
1613                    .unwrap()
1614            } else {
1615                return exc.str(vm);
1616            };
1617
1618            let msg_with_location_info: String = match (maybe_lineno, maybe_filename) {
1619                (Some(lineno), Some(filename)) => {
1620                    format!("{} ({}, line {})", msg, basename(filename.as_str()), lineno)
1621                }
1622                (Some(lineno), None) => {
1623                    format!("{} (line {})", msg, lineno)
1624                }
1625                (None, Some(filename)) => {
1626                    format!("{} ({})", msg, basename(filename.as_str()))
1627                }
1628                (None, None) => msg.to_string(),
1629            };
1630
1631            vm.ctx.new_str(msg_with_location_info)
1632        }
1633    }
1634
1635    #[pyexception(name, base = "PySyntaxError", ctx = "indentation_error", impl)]
1636    #[derive(Debug)]
1637    pub struct PyIndentationError {}
1638
1639    #[pyexception(name, base = "PyIndentationError", ctx = "tab_error", impl)]
1640    #[derive(Debug)]
1641    pub struct PyTabError {}
1642
1643    #[pyexception(name, base = "PyException", ctx = "system_error", impl)]
1644    #[derive(Debug)]
1645    pub struct PySystemError {}
1646
1647    #[pyexception(name, base = "PyException", ctx = "type_error", impl)]
1648    #[derive(Debug)]
1649    pub struct PyTypeError {}
1650
1651    #[pyexception(name, base = "PyException", ctx = "value_error", impl)]
1652    #[derive(Debug)]
1653    pub struct PyValueError {}
1654
1655    #[pyexception(name, base = "PyValueError", ctx = "unicode_error", impl)]
1656    #[derive(Debug)]
1657    pub struct PyUnicodeError {}
1658
1659    #[pyexception(name, base = "PyUnicodeError", ctx = "unicode_decode_error", impl)]
1660    #[derive(Debug)]
1661    pub struct PyUnicodeDecodeError {}
1662
1663    #[pyexception(name, base = "PyUnicodeError", ctx = "unicode_encode_error", impl)]
1664    #[derive(Debug)]
1665    pub struct PyUnicodeEncodeError {}
1666
1667    #[pyexception(name, base = "PyUnicodeError", ctx = "unicode_translate_error", impl)]
1668    #[derive(Debug)]
1669    pub struct PyUnicodeTranslateError {}
1670
1671    /// JIT error.
1672    #[cfg(feature = "jit")]
1673    #[pyexception(name, base = "PyException", ctx = "jit_error", impl)]
1674    #[derive(Debug)]
1675    pub struct PyJitError {}
1676
1677    // Warnings
1678    #[pyexception(name, base = "PyException", ctx = "warning", impl)]
1679    #[derive(Debug)]
1680    pub struct PyWarning {}
1681
1682    #[pyexception(name, base = "PyWarning", ctx = "deprecation_warning", impl)]
1683    #[derive(Debug)]
1684    pub struct PyDeprecationWarning {}
1685
1686    #[pyexception(name, base = "PyWarning", ctx = "pending_deprecation_warning", impl)]
1687    #[derive(Debug)]
1688    pub struct PyPendingDeprecationWarning {}
1689
1690    #[pyexception(name, base = "PyWarning", ctx = "runtime_warning", impl)]
1691    #[derive(Debug)]
1692    pub struct PyRuntimeWarning {}
1693
1694    #[pyexception(name, base = "PyWarning", ctx = "syntax_warning", impl)]
1695    #[derive(Debug)]
1696    pub struct PySyntaxWarning {}
1697
1698    #[pyexception(name, base = "PyWarning", ctx = "user_warning", impl)]
1699    #[derive(Debug)]
1700    pub struct PyUserWarning {}
1701
1702    #[pyexception(name, base = "PyWarning", ctx = "future_warning", impl)]
1703    #[derive(Debug)]
1704    pub struct PyFutureWarning {}
1705
1706    #[pyexception(name, base = "PyWarning", ctx = "import_warning", impl)]
1707    #[derive(Debug)]
1708    pub struct PyImportWarning {}
1709
1710    #[pyexception(name, base = "PyWarning", ctx = "unicode_warning", impl)]
1711    #[derive(Debug)]
1712    pub struct PyUnicodeWarning {}
1713
1714    #[pyexception(name, base = "PyWarning", ctx = "bytes_warning", impl)]
1715    #[derive(Debug)]
1716    pub struct PyBytesWarning {}
1717
1718    #[pyexception(name, base = "PyWarning", ctx = "resource_warning", impl)]
1719    #[derive(Debug)]
1720    pub struct PyResourceWarning {}
1721
1722    #[pyexception(name, base = "PyWarning", ctx = "encoding_warning", impl)]
1723    #[derive(Debug)]
1724    pub struct PyEncodingWarning {}
1725}
Morty Proxy This is a proxified and sanitized view of the page, visit original site.