All exceptions are now modified with extend_exception! macro

This commit is contained in:
Nikita Sobolev authored and GitHub committed 2021-09-16 03:17:19 +09:00
1 parent 00a86ba65a
commit b723bbfbb5
7 files changed
+724 -206

No files matched your search

-1
View File
@@ -42,7 +42,6 @@ BaseException
| +-- NotImplementedError
| +-- RecursionError
+-- SyntaxError
| +-- TargetScopeError
| +-- IndentationError
| +-- TabError
+-- SystemError
+18
View File
@@ -41,6 +41,24 @@ pub fn pyclass(attr: TokenStream, item: TokenStream) -> TokenStream {
result_to_tokens(pyclass::impl_pyclass(attr, item))
}
/// This macro serves a goal of generating multiple
/// `BaseException` / `Exception`
/// subtypes in a uniform and convenient manner.
/// It looks like `SimpleExtendsException` in `CPython`.
/// https://github.com/python/cpython/blob/main/Objects/exceptions.c
///
/// We need `ctx` to be ready to add
/// `properties` / `custom` constructors / slots / methods etc.
/// So, we use `extend_class!` macro as the second
/// step in exception type definition.
#[proc_macro]
pub fn define_exception(input: TokenStream) -> TokenStream {
let exc_def = parse_macro_input!(input as pyclass::PyExceptionDef);
result_to_tokens(pyclass::impl_define_exception(exc_def))
}
/// Helper macro to define `Exception` types.
/// More-or-less is an alias to `pyclass` macro.
#[proc_macro_attribute]
pub fn pyexception(attr: TokenStream, item: TokenStream) -> TokenStream {
let attr = parse_macro_input!(attr as AttributeArgs);
+112 -1
View File
@@ -6,8 +6,10 @@ use crate::util::{
use proc_macro2::TokenStream;
use quote::{quote, quote_spanned, ToTokens};
use std::collections::HashMap;
use syn::parse::{Parse, ParseStream, Result as ParsingResult};
use syn::{
parse_quote, spanned::Spanned, Attribute, AttributeArgs, Ident, Item, Meta, NestedMeta, Result,
parse_quote, spanned::Spanned, Attribute, AttributeArgs, Ident, Item, LitStr, Meta, NestedMeta,
Result, Token,
};
use syn_ext::ext::*;
@@ -288,6 +290,71 @@ pub(crate) fn impl_pyexception(
Ok(ret)
}
pub(crate) fn impl_define_exception(
exc_def: PyExceptionDef,
) -> std::result::Result<TokenStream, Diagnostic> {
let PyExceptionDef {
class_name,
base_class,
ctx_name,
docs,
tp_new,
init,
} = exc_def;
// We need this method, because of how `CPython` copies `__new__`
// from `BaseException` in `SimpleExtendsException` macro.
// See: `BaseException_new`
let tp_new_slot = match tp_new {
Some(tp_call) => quote! { #tp_call(cls, args, vm) },
None => quote! { #base_class::tp_new(cls, args, vm) },
};
// We need this method, because of how `CPython` copies `__init__`
// from `BaseException` in `SimpleExtendsException` macro.
// See: `(initproc)BaseException_init`
let init_method = match init {
Some(init_def) => quote! { #init_def(zelf, args, vm) },
None => quote! { #base_class::init(zelf, args, vm) },
};
let ret = quote! {
#[pyexception(#class_name, #base_class)]
#[derive(Debug)]
#[doc = #docs]
struct #class_name {}
// We need this to make extend mechanism work:
impl PyValue for #class_name {
fn class(vm: &VirtualMachine) -> &PyTypeRef {
&vm.ctx.exceptions.#ctx_name
}
}
#[pyimpl(flags(BASETYPE, HAS_DICT))]
impl #class_name {
#[pyslot]
pub(crate) fn tp_new(
cls: PyTypeRef,
args: FuncArgs,
vm: &VirtualMachine,
) -> PyResult {
#tp_new_slot
}
#[pymethod(magic)]
pub(crate) fn init(
zelf: PyRef<PyBaseException>,
args: FuncArgs,
vm: &VirtualMachine,
) -> PyResult<()> {
#init_method
}
}
};
Ok(ret)
}
/// #[pymethod] and #[pyclassmethod]
struct MethodItem {
inner: ContentItemInner,
@@ -971,6 +1038,50 @@ where
Ok((result, cfgs))
}
#[derive(Debug)]
pub(crate) struct PyExceptionDef {
pub class_name: Ident,
pub base_class: Ident,
pub ctx_name: Ident,
pub docs: LitStr,
/// Holds optional `tp_new` slot to be used instead of a default one:
pub tp_new: Option<Ident>,
/// We also store `__init__` magic method, that can
pub init: Option<Ident>,
}
impl Parse for PyExceptionDef {
fn parse(input: ParseStream) -> ParsingResult<Self> {
let class_name: Ident = input.parse()?;
input.parse::<Token![,]>()?;
let base_class: Ident = input.parse()?;
input.parse::<Token![,]>()?;
let ctx_name: Ident = input.parse()?;
input.parse::<Token![,]>()?;
let docs: LitStr = input.parse()?;
input.parse::<Option<Token![,]>>()?;
let tp_new: Option<Ident> = input.parse()?;
input.parse::<Option<Token![,]>>()?;
let init: Option<Ident> = input.parse()?;
input.parse::<Option<Token![,]>>()?; // leading `,`
Ok(PyExceptionDef {
class_name,
base_class,
ctx_name,
docs,
tp_new,
init,
})
}
}
fn parse_vec_ident(
attr: &[NestedMeta],
item: &Item,
-1
View File
@@ -109,7 +109,6 @@ fn new_module_item(
inner: ContentItemInner { index, attr_name },
pyattrs: pyattrs.unwrap_or_else(Vec::new),
}),
"pyexception" => unreachable!("#[pyexception] {:?}", pyattrs.unwrap_or_else(Vec::new)),
other => unreachable!("#[pymodule] doesn't accept #[{}]", other),
}
}
+14 -5
View File
@@ -1,3 +1,4 @@
import builtins
import platform
import sys
@@ -161,37 +162,45 @@ except MyError as exc:
assert BaseException.__new__.__qualname__ == 'BaseException.__new__'
assert BaseException.__init__.__qualname__ == 'BaseException.__init__'
assert BaseException().__dict__ == {}
assert BaseException.__doc__
assert Exception.__new__.__qualname__ == 'Exception.__new__'
assert Exception.__init__.__qualname__ == 'Exception.__init__'
assert Exception().__dict__ == {}
assert Exception.__doc__
# Extends `BaseException`, simple:
assert KeyboardInterrupt.__new__.__qualname__ == 'KeyboardInterrupt.__new__'
assert KeyboardInterrupt.__init__.__qualname__ == 'KeyboardInterrupt.__init__'
assert KeyboardInterrupt().__dict__ == {}
assert KeyboardInterrupt.__doc__
# Extends `Exception`, simple:
assert TypeError.__new__.__qualname__ == 'TypeError.__new__'
assert TypeError.__init__.__qualname__ == 'TypeError.__init__'
assert TypeError().__dict__ == {}
assert TypeError.__doc__
# Extends `Exception`, complex:
assert OSError.__new__.__qualname__ == 'OSError.__new__'
assert OSError.__init__.__qualname__ == 'OSError.__init__'
assert OSError().__dict__ == {}
assert OSError.__doc__
assert OSError.errno
assert OSError.strerror
assert OSError(1, 2).errno
assert OSError(1, 2).strerror
# Custom `__new__` and `__init__`:
assert ImportError.__init__.__qualname__ == 'ImportError.__init__'
assert ImportError(name='a').name == 'a'
assert (
ModuleNotFoundError.__init__.__qualname__ == 'ModuleNotFoundError.__init__'
)
assert ModuleNotFoundError(name='a').name == 'a'
# Check that all exceptions have string `__doc__`:
for exc in filter(
lambda obj: isinstance(obj, BaseException),
vars(builtins).values(),
):
assert isinstance(exc.__doc__, str)
-1
View File
@@ -988,7 +988,6 @@ pub fn make_module(vm: &VirtualMachine, module: PyObjectRef) {
"NotImplementedError" => ctx.exceptions.not_implemented_error.clone(),
"RecursionError" => ctx.exceptions.recursion_error.clone(),
"SyntaxError" => ctx.exceptions.syntax_error.clone(),
"TargetScopeError" => ctx.exceptions.target_scope_error.clone(),
"IndentationError" => ctx.exceptions.indentation_error.clone(),
"TabError" => ctx.exceptions.tab_error.clone(),
"SystemError" => ctx.exceptions.system_error.clone(),
+580 -197
View File
@@ -155,6 +155,10 @@ impl PyBaseException {
}
}
fn base_exception_new(cls: PyTypeRef, args: FuncArgs, vm: &VirtualMachine) -> PyResult {
PyBaseException::tp_new(cls, args, vm)
}
pub fn chain<T>(e1: PyResult<()>, e2: PyResult<T>) -> PyResult<T> {
match (e1, e2) {
(Err(e1), Err(e)) => {
@@ -484,7 +488,6 @@ pub struct ExceptionZoo {
pub not_implemented_error: PyTypeRef,
pub recursion_error: PyTypeRef,
pub syntax_error: PyTypeRef,
pub target_scope_error: PyTypeRef,
pub indentation_error: PyTypeRef,
pub tab_error: PyTypeRef,
pub system_error: PyTypeRef,
@@ -522,214 +525,523 @@ pub fn create_exception_type(name: &str, base: &PyTypeRef) -> PyTypeRef {
create_type_with_slots(name, PyType::static_type(), base, exception_slots())
}
/// This macro serves a goal of generating multiple BaseException / Exception
/// subtypes in a uniform and convenient manner.
/// It looks like `SimpleExtendsException` in `CPython`.
/// https://github.com/python/cpython/blob/main/Objects/exceptions.c
///
/// We need `ctx` to be ready to add `properties` / `custom` constructors / slots / etc.
/// So, we use `extend_class!` macro as the second step in exception type definition.
macro_rules! extends_exception {
macro_rules! extend_exception {
(
$class_name: ident,
$base_class: ident,
$ctx_name: ident,
$docs: tt
$exc_struct:ident,
$ctx:expr,
$class:expr
) => {
#[pyexception($class_name, $base_class)]
#[derive(Debug)]
#[doc = $docs]
struct $class_name {}
// We need this to make extend mechanism work:
impl PyValue for $class_name {
fn class(vm: &VirtualMachine) -> &PyTypeRef {
&vm.ctx.exceptions.$ctx_name
}
}
#[pyimpl(flags(BASETYPE, HAS_DICT))]
impl $class_name {
#[pyslot]
fn tp_new(cls: PyTypeRef, args: FuncArgs, vm: &VirtualMachine) -> PyResult {
// We need this method, because of how `CPython` copies `init`
// from `BaseException` in `SimpleExtendsException` macro.
// See: `BaseException_new`
PyBaseException::tp_new(cls, args, vm)
}
#[pymethod(magic)]
fn init(
zelf: PyRef<PyBaseException>,
args: FuncArgs,
vm: &VirtualMachine,
) -> PyResult<()> {
// We need this method, because of how `CPython` copies `init`
// from `BaseException` in `SimpleExtendsException` macro.
// See: `(initproc)BaseException_init`
$base_class::init(zelf, args, vm)
}
}
extend_exception!($exc_struct, $ctx, $class, {});
};
(
$exc_struct:ident,
$ctx:expr,
$class:expr,
{ $($name:expr => $value:expr),* $(,)* }
) => {
$exc_struct::extend_class($ctx, $class);
extend_class!($ctx, $class, {
$($name => $value,)*
});
};
}
// Exception types that extend `BaseException`,
// sorted the same way CPython does.
// Sorted By Hierarchy then alphabetized.
extends_exception! {
PyException,
PyBaseException,
exception_type,
"Common base class for all non-exit exceptions."
}
extends_exception! {
PyGeneratorExit,
PyBaseException,
generator_exit,
"Request that a generator exit."
}
extends_exception! {
define_exception! {
PySystemExit,
PyBaseException,
system_exit,
"Request to exit from the interpreter."
}
extends_exception! {
define_exception! {
PyGeneratorExit,
PyBaseException,
generator_exit,
"Request that a generator exit."
}
define_exception! {
PyKeyboardInterrupt,
PyBaseException,
keyboard_interrupt,
"Program interrupted by user."
}
// Exception types that extend `Exception`,
// sorted the same way CPython does.
extends_exception! {
PyTypeError,
// Base `Exception` type
define_exception! {
PyException,
type_error,
"Inappropriate argument type."
PyBaseException,
exception_type,
"Common base class for all non-exit exceptions."
}
extends_exception! {
define_exception! {
PyStopIteration,
PyException,
stop_iteration,
"Signal the end from iterator.__next__()."
}
define_exception! {
PyStopAsyncIteration,
PyException,
stop_async_iteration,
"Signal the end from iterator.__anext__()."
}
extends_exception! {
PyStopIteration,
define_exception! {
PyArithmeticError,
PyException,
stop_iteration,
"Signal the end from iterator.__next__()."
arithmetic_error,
"Base class for arithmetic errors."
}
define_exception! {
PyFloatingPointError,
PyArithmeticError,
floating_point_error,
"Floating point operation failed."
}
define_exception! {
PyOverflowError,
PyArithmeticError,
overflow_error,
"Result too large to be represented."
}
define_exception! {
PyZeroDivisionError,
PyArithmeticError,
zero_division_error,
"Second argument to a division or modulo operation was zero."
}
extends_exception! {
define_exception! {
PyAssertionError,
PyException,
assertion_error,
"Assertion failed."
}
define_exception! {
PyAttributeError,
PyException,
attribute_error,
"Attribute not found."
}
define_exception! {
PyBufferError,
PyException,
buffer_error,
"Buffer error."
}
define_exception! {
PyEOFError,
PyException,
eof_error,
"Read beyond end of file."
}
define_exception! {
PyImportError,
PyException,
import_error,
"Import can't find module, or can't find name in module.",
base_exception_new,
import_error_init,
}
define_exception! {
PyModuleNotFoundError,
PyImportError,
module_not_found_error,
"Module not found."
}
define_exception! {
PyLookupError,
PyException,
lookup_error,
"Base class for lookup errors."
}
define_exception! {
PyIndexError,
PyLookupError,
index_error,
"Sequence index out of range."
}
define_exception! {
PyKeyError,
PyLookupError,
key_error,
"Mapping key not found."
}
define_exception! {
PyMemoryError,
PyException,
memory_error,
"Out of memory."
}
define_exception! {
PyNameError,
PyException,
name_error,
"Name not found globally."
}
define_exception! {
PyUnboundLocalError,
PyNameError,
unbound_local_error,
"Local name referenced but not bound to a value."
}
// OS Errors:
define_exception! {
PyOSError,
PyException,
os_error,
"Base class for I/O related errors."
}
define_exception! {
PyBlockingIOError,
PyOSError,
blocking_io_error,
"I/O operation would block."
}
define_exception! {
PyChildProcessError,
PyOSError,
child_process_error,
"Child process error."
}
define_exception! {
PyConnectionError,
PyOSError,
connection_error,
"Connection error."
}
define_exception! {
PyBrokenPipeError,
PyConnectionError,
broken_pipe_error,
"Broken pipe."
}
define_exception! {
PyConnectionAbortedError,
PyConnectionError,
connection_aborted_error,
"Connection aborted."
}
define_exception! {
PyConnectionRefusedError,
PyConnectionError,
connection_refused_error,
"Connection refused."
}
define_exception! {
PyConnectionResetError,
PyConnectionError,
connection_reset_error,
"Connection reset."
}
define_exception! {
PyFileExistsError,
PyOSError,
file_exists_error,
"File already exists."
}
define_exception! {
PyFileNotFoundError,
PyOSError,
file_not_found_error,
"File not found."
}
define_exception! {
PyInterruptedError,
PyOSError,
interrupted_error,
"Interrupted by signal."
}
define_exception! {
PyIsADirectoryError,
PyOSError,
is_a_directory_error,
"Operation doesn't work on directories."
}
define_exception! {
PyNotADirectoryError,
PyOSError,
not_a_directory_error,
"Operation only works on directories."
}
define_exception! {
PyPermissionError,
PyOSError,
permission_error,
"Not enough permissions."
}
define_exception! {
PyProcessLookupError,
PyOSError,
process_lookup_error,
"Process not found."
}
define_exception! {
PyTimeoutError,
PyOSError,
timeout_error,
"Timeout expired."
}
macro_rules! extend_exception {
( $ctx:expr, $class:expr, { $($name:expr => $value:expr),* $(,)* }) => {
$class.set_str_attr("__new__", $ctx.new_method("__new__", $class.clone(), PyBaseException::tp_new));
$class.set_str_attr("__init__", $ctx.new_method("__init__", $class.clone(), PyBaseException::init));
define_exception! {
PyReferenceError,
PyException,
reference_error,
"Weak ref proxy used after referent went away."
}
extend_class!($ctx, $class, {
$(
$name => $value,
)*
});
};
define_exception! {
PyRuntimeError,
PyException,
runtime_error,
"Unspecified run-time error."
}
define_exception! {
PyNotImplementedError,
PyRuntimeError,
not_implemented_error,
"Method or function hasn't been implemented yet."
}
define_exception! {
PyRecursionError,
PyRuntimeError,
recursion_error,
"Recursion limit exceeded."
}
define_exception! {
PySyntaxError,
PyException,
syntax_error,
"Invalid syntax."
}
define_exception! {
PyIndentationError,
PySyntaxError,
indentation_error,
"Improper indentation."
}
define_exception! {
PyTabError,
PyIndentationError,
tab_error,
"Improper mixture of spaces and tabs."
}
define_exception! {
PySystemError,
PyException,
system_error,
"Internal error in the Python interpreter.\n\nPlease report this to the Python maintainer, along with the traceback,\nthe Python version, and the hardware/OS platform and version."
}
define_exception! {
PyTypeError,
PyException,
type_error,
"Inappropriate argument type."
}
define_exception! {
PyValueError,
PyException,
value_error,
"Inappropriate argument value (of correct type)."
}
define_exception! {
PyUnicodeError,
PyValueError,
unicode_error,
"Unicode related error."
}
define_exception! {
PyUnicodeDecodeError,
PyUnicodeError,
unicode_decode_error,
"Unicode decoding error."
}
define_exception! {
PyUnicodeEncodeError,
PyUnicodeError,
unicode_encode_error,
"Unicode encoding error."
}
define_exception! {
PyUnicodeTranslateError,
PyUnicodeError,
unicode_translate_error,
"Unicode translation error."
}
#[cfg(feature = "jit")]
define_exception! {
PyJitError,
PyException,
jit_error,
"JIT error."
}
// Warnings
define_exception! {
PyWarning,
PyException,
warning,
"Base class for warning categories."
}
define_exception! {
PyDeprecationWarning,
PyWarning,
deprecation_warning,
"Base class for warnings about deprecated features."
}
define_exception! {
PyPendingDeprecationWarning,
PyWarning,
pending_deprecation_warning,
"Base class for warnings about features which will be deprecated\nin the future."
}
define_exception! {
PyRuntimeWarning,
PyWarning,
runtime_warning,
"Base class for warnings about dubious runtime behavior."
}
define_exception! {
PySyntaxWarning,
PyWarning,
syntax_warning,
"Base class for warnings about dubious syntax."
}
define_exception! {
PyUserWarning,
PyWarning,
user_warning,
"Base class for warnings generated by user code."
}
define_exception! {
PyFutureWarning,
PyWarning,
future_warning,
"Base class for warnings about constructs that will change semantically\nin the future."
}
define_exception! {
PyImportWarning,
PyWarning,
import_warning,
"Base class for warnings about probable mistakes in module imports."
}
define_exception! {
PyUnicodeWarning,
PyWarning,
unicode_warning,
"Base class for warnings about Unicode related problems, mostly\nrelated to conversion problems."
}
define_exception! {
PyBytesWarning,
PyWarning,
bytes_warning,
"Base class for warnings about bytes and buffer related problems, mostly\nrelated to conversion from str or comparing to str."
}
define_exception! {
PyResourceWarning,
PyWarning,
resource_warning,
"Base class for warnings about resource usage."
}
impl ExceptionZoo {
pub(crate) fn init() -> Self {
// The same order as definitions:
let base_exception_type = PyBaseException::init_bare_type().clone();
let exception_type = PyException::init_bare_type().clone();
// Sorted By Hierarchy then alphabetized.
let system_exit = PySystemExit::init_bare_type().clone();
let keyboard_interrupt = PyKeyboardInterrupt::init_bare_type().clone();
let generator_exit = PyGeneratorExit::init_bare_type().clone();
let exception_type = PyException::init_bare_type().clone();
let stop_iteration = PyStopIteration::init_bare_type().clone();
let stop_async_iteration = PyStopAsyncIteration::init_bare_type().clone();
let arithmetic_error = create_exception_type("ArithmeticError", &exception_type);
let floating_point_error = create_exception_type("FloatingPointError", &arithmetic_error);
let overflow_error = create_exception_type("OverflowError", &arithmetic_error);
let zero_division_error = create_exception_type("ZeroDivisionError", &arithmetic_error);
let assertion_error = create_exception_type("AssertionError", &exception_type);
let attribute_error = create_exception_type("AttributeError", &exception_type);
let buffer_error = create_exception_type("BufferError", &exception_type);
let eof_error = create_exception_type("EOFError", &exception_type);
let import_error = create_exception_type("ImportError", &exception_type);
let module_not_found_error = create_exception_type("ModuleNotFoundError", &import_error);
let lookup_error = create_exception_type("LookupError", &exception_type);
let index_error = create_exception_type("IndexError", &lookup_error);
let key_error = create_exception_type("KeyError", &lookup_error);
let memory_error = create_exception_type("MemoryError", &exception_type);
let name_error = create_exception_type("NameError", &exception_type);
let unbound_local_error = create_exception_type("UnboundLocalError", &name_error);
let arithmetic_error = PyArithmeticError::init_bare_type().clone();
let floating_point_error = PyFloatingPointError::init_bare_type().clone();
let overflow_error = PyOverflowError::init_bare_type().clone();
let zero_division_error = PyZeroDivisionError::init_bare_type().clone();
let assertion_error = PyAssertionError::init_bare_type().clone();
let attribute_error = PyAttributeError::init_bare_type().clone();
let buffer_error = PyBufferError::init_bare_type().clone();
let eof_error = PyEOFError::init_bare_type().clone();
let import_error = PyImportError::init_bare_type().clone();
let module_not_found_error = PyModuleNotFoundError::init_bare_type().clone();
let lookup_error = PyLookupError::init_bare_type().clone();
let index_error = PyIndexError::init_bare_type().clone();
let key_error = PyKeyError::init_bare_type().clone();
let memory_error = PyMemoryError::init_bare_type().clone();
let name_error = PyNameError::init_bare_type().clone();
let unbound_local_error = PyUnboundLocalError::init_bare_type().clone();
// os errors
let os_error = PyOSError::init_bare_type().clone();
let blocking_io_error = create_exception_type("BlockingIOError", &os_error);
let child_process_error = create_exception_type("ChildProcessError", &os_error);
let connection_error = create_exception_type("ConnectionError", &os_error);
let connection_aborted_error =
create_exception_type("ConnectionAbortedError", &connection_error);
let connection_refused_error =
create_exception_type("ConnectionRefusedError", &connection_error);
let connection_reset_error =
create_exception_type("ConnectionResetError", &connection_error);
let file_exists_error = create_exception_type("FileExistsError", &os_error);
let file_not_found_error = create_exception_type("FileNotFoundError", &os_error);
let interrupted_error = create_exception_type("InterruptedError", &os_error);
let is_a_directory_error = create_exception_type("IsADirectoryError", &os_error);
let not_a_directory_error = create_exception_type("NotADirectoryError", &os_error);
let broken_pipe_error = create_exception_type("BrokenPipeError", &connection_error);
let permission_error = create_exception_type("PermissionError", &os_error);
let process_lookup_error = create_exception_type("ProcessLookupError", &os_error);
let timeout_error = create_exception_type("TimeoutError", &os_error);
let blocking_io_error = PyBlockingIOError::init_bare_type().clone();
let child_process_error = PyChildProcessError::init_bare_type().clone();
let reference_error = create_exception_type("ReferenceError", &exception_type);
let runtime_error = create_exception_type("RuntimeError", &exception_type);
let not_implemented_error = create_exception_type("NotImplementedError", &runtime_error);
let recursion_error = create_exception_type("RecursionError", &runtime_error);
let syntax_error = create_exception_type("SyntaxError", &exception_type);
let indentation_error = create_exception_type("IndentationError", &syntax_error);
let tab_error = create_exception_type("TabError", &indentation_error);
let target_scope_error = create_exception_type("TargetScopeError", &syntax_error);
let system_error = create_exception_type("SystemError", &exception_type);
let connection_error = PyConnectionError::init_bare_type().clone();
let broken_pipe_error = PyBrokenPipeError::init_bare_type().clone();
let connection_aborted_error = PyConnectionAbortedError::init_bare_type().clone();
let connection_refused_error = PyConnectionRefusedError::init_bare_type().clone();
let connection_reset_error = PyConnectionResetError::init_bare_type().clone();
let file_exists_error = PyFileExistsError::init_bare_type().clone();
let file_not_found_error = PyFileNotFoundError::init_bare_type().clone();
let interrupted_error = PyInterruptedError::init_bare_type().clone();
let is_a_directory_error = PyIsADirectoryError::init_bare_type().clone();
let not_a_directory_error = PyNotADirectoryError::init_bare_type().clone();
let permission_error = PyPermissionError::init_bare_type().clone();
let process_lookup_error = PyProcessLookupError::init_bare_type().clone();
let timeout_error = PyTimeoutError::init_bare_type().clone();
let reference_error = PyReferenceError::init_bare_type().clone();
let runtime_error = PyRuntimeError::init_bare_type().clone();
let not_implemented_error = PyNotImplementedError::init_bare_type().clone();
let recursion_error = PyRecursionError::init_bare_type().clone();
let syntax_error = PySyntaxError::init_bare_type().clone();
let indentation_error = PyIndentationError::init_bare_type().clone();
let tab_error = PyTabError::init_bare_type().clone();
let system_error = PySystemError::init_bare_type().clone();
let type_error = PyTypeError::init_bare_type().clone();
let value_error = create_exception_type("ValueError", &exception_type);
let unicode_error = create_exception_type("UnicodeError", &value_error);
let unicode_decode_error = create_exception_type("UnicodeDecodeError", &unicode_error);
let unicode_encode_error = create_exception_type("UnicodeEncodeError", &unicode_error);
let unicode_translate_error =
create_exception_type("UnicodeTranslateError", &unicode_error);
let value_error = PyValueError::init_bare_type().clone();
let unicode_error = PyUnicodeError::init_bare_type().clone();
let unicode_decode_error = PyUnicodeDecodeError::init_bare_type().clone();
let unicode_encode_error = PyUnicodeEncodeError::init_bare_type().clone();
let unicode_translate_error = PyUnicodeTranslateError::init_bare_type().clone();
#[cfg(feature = "jit")]
let jit_error = create_exception_type("JitError", &exception_type);
let jit_error = PyJitError::init_bare_type().clone();
let warning = create_exception_type("Warning", &exception_type);
let deprecation_warning = create_exception_type("DeprecationWarning", &warning);
let pending_deprecation_warning =
create_exception_type("PendingDeprecationWarning", &warning);
let runtime_warning = create_exception_type("RuntimeWarning", &warning);
let syntax_warning = create_exception_type("SyntaxWarning", &warning);
let user_warning = create_exception_type("UserWarning", &warning);
let future_warning = create_exception_type("FutureWarning", &warning);
let import_warning = create_exception_type("ImportWarning", &warning);
let unicode_warning = create_exception_type("UnicodeWarning", &warning);
let bytes_warning = create_exception_type("BytesWarning", &warning);
let resource_warning = create_exception_type("ResourceWarning", &warning);
let warning = PyWarning::init_bare_type().clone();
let deprecation_warning = PyDeprecationWarning::init_bare_type().clone();
let pending_deprecation_warning = PyPendingDeprecationWarning::init_bare_type().clone();
let runtime_warning = PyRuntimeWarning::init_bare_type().clone();
let syntax_warning = PySyntaxWarning::init_bare_type().clone();
let user_warning = PyUserWarning::init_bare_type().clone();
let future_warning = PyFutureWarning::init_bare_type().clone();
let import_warning = PyImportWarning::init_bare_type().clone();
let unicode_warning = PyUnicodeWarning::init_bare_type().clone();
let bytes_warning = PyBytesWarning::init_bare_type().clone();
let resource_warning = PyResourceWarning::init_bare_type().clone();
Self {
base_exception_type,
@@ -776,7 +1088,6 @@ impl ExceptionZoo {
not_implemented_error,
recursion_error,
syntax_error,
target_scope_error,
indentation_error,
tab_error,
system_error,
@@ -804,29 +1115,99 @@ impl ExceptionZoo {
}
}
// TODO: remove it after fixing `errno` / `winerror` problem
#[allow(clippy::redundant_clone)]
pub fn extend(ctx: &PyContext) {
let excs = &ctx.exceptions;
PyBaseException::extend_class(ctx, &excs.base_exception_type);
PyException::extend_class(ctx, &excs.exception_type);
PyGeneratorExit::extend_class(ctx, &excs.generator_exit);
PySystemExit::extend_class(ctx, &excs.system_exit);
extend_class!(ctx, &excs.system_exit, {
// Sorted By Hierarchy then alphabetized.
extend_exception!(PySystemExit, ctx, &excs.system_exit, {
"code" => ctx.new_readonly_getset("code", excs.system_exit.clone(), system_exit_code),
});
PyKeyboardInterrupt::extend_class(ctx, &excs.keyboard_interrupt);
extend_exception!(PyKeyboardInterrupt, ctx, &excs.keyboard_interrupt);
extend_exception!(PyGeneratorExit, ctx, &excs.generator_exit);
PyTypeError::extend_class(ctx, &excs.type_error);
PyStopAsyncIteration::extend_class(ctx, &excs.stop_async_iteration);
PyStopIteration::extend_class(ctx, &excs.stop_iteration);
extend_class!(ctx, &excs.stop_iteration, {
extend_exception!(PyException, ctx, &excs.exception_type);
extend_exception!(PyStopIteration, ctx, &excs.stop_iteration, {
"value" => ctx.new_readonly_getset("value", excs.stop_iteration.clone(), make_arg_getter(0)),
});
extend_exception!(PyStopAsyncIteration, ctx, &excs.stop_async_iteration);
extend_exception!(ctx, &excs.arithmetic_error, {});
extend_exception!(PyArithmeticError, ctx, &excs.arithmetic_error);
extend_exception!(PyFloatingPointError, ctx, &excs.floating_point_error);
extend_exception!(PyOverflowError, ctx, &excs.overflow_error);
extend_exception!(PyZeroDivisionError, ctx, &excs.zero_division_error);
extend_class!(ctx, &excs.syntax_error, {
extend_exception!(PyAssertionError, ctx, &excs.assertion_error);
extend_exception!(PyAttributeError, ctx, &excs.attribute_error);
extend_exception!(PyBufferError, ctx, &excs.buffer_error);
extend_exception!(PyEOFError, ctx, &excs.eof_error);
extend_exception!(PyImportError, ctx, &excs.import_error, {
"msg" => ctx.new_readonly_getset("msg", excs.import_error.clone(), make_arg_getter(0)),
});
extend_exception!(PyModuleNotFoundError, ctx, &excs.module_not_found_error);
extend_exception!(PyLookupError, ctx, &excs.lookup_error);
extend_exception!(PyIndexError, ctx, &excs.index_error);
extend_exception!(PyKeyError, ctx, &excs.key_error, {
"__str__" => ctx.new_method("__str__", excs.key_error.clone(), key_error_str),
});
extend_exception!(PyMemoryError, ctx, &excs.memory_error);
extend_exception!(PyNameError, ctx, &excs.name_error);
extend_exception!(PyUnboundLocalError, ctx, &excs.unbound_local_error);
// os errors:
let errno_getter =
ctx.new_readonly_getset("errno", excs.os_error.clone(), |exc: PyBaseExceptionRef| {
let args = exc.args();
let args = args.as_slice();
args.get(0).filter(|_| args.len() > 1).cloned()
});
extend_exception!(PyOSError, ctx, &excs.os_error, {
"errno" => errno_getter.clone(),
"strerror" => ctx.new_readonly_getset("strerror", excs.os_error.clone(), make_arg_getter(1)),
});
// TODO: this isn't really accurate
#[cfg(windows)]
excs.os_error.set_str_attr("winerror", errno_getter.clone());
extend_exception!(PyBlockingIOError, ctx, &excs.blocking_io_error);
extend_exception!(PyChildProcessError, ctx, &excs.child_process_error);
extend_exception!(PyConnectionError, ctx, &excs.connection_error);
extend_exception!(PyBrokenPipeError, ctx, &excs.broken_pipe_error);
extend_exception!(
PyConnectionAbortedError,
ctx,
&excs.connection_aborted_error
);
extend_exception!(
PyConnectionRefusedError,
ctx,
&excs.connection_refused_error
);
extend_exception!(PyConnectionResetError, ctx, &excs.connection_reset_error);
extend_exception!(PyFileExistsError, ctx, &excs.file_exists_error);
extend_exception!(PyFileNotFoundError, ctx, &excs.file_not_found_error);
extend_exception!(PyInterruptedError, ctx, &excs.interrupted_error);
extend_exception!(PyIsADirectoryError, ctx, &excs.is_a_directory_error);
extend_exception!(PyNotADirectoryError, ctx, &excs.not_a_directory_error);
extend_exception!(PyPermissionError, ctx, &excs.permission_error);
extend_exception!(PyProcessLookupError, ctx, &excs.process_lookup_error);
extend_exception!(PyTimeoutError, ctx, &excs.timeout_error);
extend_exception!(PyReferenceError, ctx, &excs.reference_error);
extend_exception!(PyRuntimeError, ctx, &excs.runtime_error);
extend_exception!(PyNotImplementedError, ctx, &excs.not_implemented_error);
extend_exception!(PyRecursionError, ctx, &excs.recursion_error);
extend_exception!(PySyntaxError, ctx, &excs.syntax_error, {
"msg" => ctx.new_readonly_getset("msg", excs.syntax_error.clone(), make_arg_getter(0)),
// TODO: members
"filename" => ctx.none(),
@@ -834,60 +1215,62 @@ impl ExceptionZoo {
"offset" => ctx.none(),
"text" => ctx.none(),
});
extend_exception!(PyIndentationError, ctx, &excs.indentation_error);
extend_exception!(PyTabError, ctx, &excs.tab_error);
extend_exception!(ctx, &excs.import_error, {
"__init__" => ctx.new_method("__init__", excs.import_error.clone(), import_error_init),
"msg" => ctx.new_readonly_getset("msg", excs.import_error.clone(), make_arg_getter(0)),
});
extend_class!(ctx, &excs.key_error, {
"__str__" => ctx.new_method("__str__", excs.key_error.clone(), key_error_str),
});
PyOSError::extend_class(ctx, &excs.os_error);
let errno_getter =
ctx.new_readonly_getset("errno", excs.os_error.clone(), |exc: PyBaseExceptionRef| {
let args = exc.args();
let args = args.as_slice();
args.get(0).filter(|_| args.len() > 1).cloned()
});
#[cfg(windows)]
extend_class!(ctx, &excs.os_error, {
// TODO: this isn't really accurate
"winerror" => errno_getter.clone(),
});
extend_class!(ctx, &excs.os_error, {
"errno" => errno_getter,
"strerror" => ctx.new_readonly_getset("strerror", excs.os_error.clone(), make_arg_getter(1)),
});
extend_class!(ctx, &excs.unicode_decode_error, {
extend_exception!(PySystemError, ctx, &excs.system_error);
extend_exception!(PyTypeError, ctx, &excs.type_error);
extend_exception!(PyValueError, ctx, &excs.value_error);
extend_exception!(PyUnicodeError, ctx, &excs.unicode_error);
extend_exception!(PyUnicodeDecodeError, ctx, &excs.unicode_decode_error, {
"encoding" => ctx.new_readonly_getset("encoding", excs.unicode_decode_error.clone(), make_arg_getter(0)),
"object" => ctx.new_readonly_getset("object", excs.unicode_decode_error.clone(), make_arg_getter(1)),
"start" => ctx.new_readonly_getset("start", excs.unicode_decode_error.clone(), make_arg_getter(2)),
"end" => ctx.new_readonly_getset("end", excs.unicode_decode_error.clone(), make_arg_getter(3)),
"reason" => ctx.new_readonly_getset("reason", excs.unicode_decode_error.clone(), make_arg_getter(4)),
});
extend_class!(ctx, &excs.unicode_encode_error, {
extend_exception!(PyUnicodeEncodeError, ctx, &excs.unicode_encode_error, {
"encoding" => ctx.new_readonly_getset("encoding", excs.unicode_encode_error.clone(), make_arg_getter(0)),
"object" => ctx.new_readonly_getset("object", excs.unicode_encode_error.clone(), make_arg_getter(1)),
"start" => ctx.new_readonly_getset("start", excs.unicode_encode_error.clone(), make_arg_getter(2), ),
"end" => ctx.new_readonly_getset("end", excs.unicode_encode_error.clone(), make_arg_getter(3)),
"reason" => ctx.new_readonly_getset("reason", excs.unicode_encode_error.clone(), make_arg_getter(4)),
});
extend_class!(ctx, &excs.unicode_translate_error, {
extend_exception!(PyUnicodeTranslateError, ctx, &excs.unicode_translate_error, {
"encoding" => ctx.new_readonly_getset("encoding", excs.unicode_translate_error.clone(), none_getter),
"object" => ctx.new_readonly_getset("object", excs.unicode_translate_error.clone(), make_arg_getter(0)),
"start" => ctx.new_readonly_getset("start", excs.unicode_translate_error.clone(), make_arg_getter(1)),
"end" => ctx.new_readonly_getset("end", excs.unicode_translate_error.clone(), make_arg_getter(2)),
"reason" => ctx.new_readonly_getset("reason", excs.unicode_translate_error.clone(), make_arg_getter(3)),
});
#[cfg(feature = "jit")]
extend_exception!(PyJitError, ctx, &excs.jit_error);
extend_exception!(PyWarning, ctx, &excs.warning);
extend_exception!(PyDeprecationWarning, ctx, &excs.deprecation_warning);
extend_exception!(
PyPendingDeprecationWarning,
ctx,
&excs.pending_deprecation_warning
);
extend_exception!(PyRuntimeWarning, ctx, &excs.runtime_warning);
extend_exception!(PySyntaxWarning, ctx, &excs.syntax_warning);
extend_exception!(PyUserWarning, ctx, &excs.user_warning);
extend_exception!(PyFutureWarning, ctx, &excs.future_warning);
extend_exception!(PyImportWarning, ctx, &excs.import_warning);
extend_exception!(PyUnicodeWarning, ctx, &excs.unicode_warning);
extend_exception!(PyBytesWarning, ctx, &excs.bytes_warning);
extend_exception!(PyResourceWarning, ctx, &excs.resource_warning);
}
}
fn import_error_init(exc_self: PyObjectRef, args: FuncArgs, vm: &VirtualMachine) -> PyResult<()> {
fn import_error_init(
zelf: PyRef<PyBaseException>,
args: FuncArgs,
vm: &VirtualMachine,
) -> PyResult<()> {
let exc_self = zelf.into_object();
vm.set_attr(
&exc_self,
"name",