diff --git a/include/nanobind/nb_error.h b/include/nanobind/nb_error.h index 5d38c685..0b92c736 100644 --- a/include/nanobind/nb_error.h +++ b/include/nanobind/nb_error.h @@ -90,9 +90,8 @@ class NB_EXPORT python_error : public std::exception { using cast_error = std::bad_cast; enum class exception_type { - stop_iteration, index_error, key_error, value_error, - type_error, buffer_error, import_error, attribute_error, - next_overload + runtime_error, stop_iteration, index_error, key_error, value_error, + type_error, buffer_error, import_error, attribute_error, next_overload }; // Base interface used to expose common Python exceptions in C++ diff --git a/include/nanobind/nb_lib.h b/include/nanobind/nb_lib.h index b71093fa..e051059e 100644 --- a/include/nanobind/nb_lib.h +++ b/include/nanobind/nb_lib.h @@ -66,7 +66,7 @@ struct NB_CORE cleanup_list { // ======================================================================== -/// Raise a std::runtime_error with the given message +/// Raise a runtime error with the given message #if defined(__GNUC__) __attribute__((noreturn, __format__ (__printf__, 1, 2))) #else @@ -74,6 +74,14 @@ struct NB_CORE cleanup_list { #endif NB_CORE void raise(const char *fmt, ...); +/// Raise a type error with the given message +#if defined(__GNUC__) + __attribute__((noreturn, __format__ (__printf__, 1, 2))) +#else + [[noreturn]] +#endif +NB_CORE void raise_type_error(const char *fmt, ...); + /// Abort the process with a fatal error #if defined(__GNUC__) __attribute__((noreturn, __format__ (__printf__, 1, 2))) diff --git a/src/common.cpp b/src/common.cpp index bb5b28e5..88e84f6d 100644 --- a/src/common.cpp +++ b/src/common.cpp @@ -13,6 +13,27 @@ NAMESPACE_BEGIN(NB_NAMESPACE) NAMESPACE_BEGIN(detail) +NB_NOINLINE static builtin_exception +create_exception(exception_type type, const char *fmt, va_list args_) { + char buf[512]; + va_list args; + + va_copy(args, args_); + int size = vsnprintf(buf, sizeof(buf), fmt, args); + va_end(args); + + if (size < (int) sizeof(buf)) { + return builtin_exception(type, buf); + } else { + scoped_pymalloc temp(size + 1); + + va_copy(args, args_); + vsnprintf(temp.get(), size + 1, fmt, args); + va_end(args); + + return builtin_exception(type, temp.get()); + } +} #if defined(__GNUC__) __attribute__((noreturn, __format__ (__printf__, 1, 2))) @@ -20,23 +41,26 @@ NAMESPACE_BEGIN(detail) [[noreturn]] #endif void raise(const char *fmt, ...) { - char buf[512]; va_list args; - va_start(args, fmt); - int size = vsnprintf(buf, sizeof(buf), fmt, args); + builtin_exception err = + create_exception(exception_type::runtime_error, fmt, args); va_end(args); + throw err; +} - if (size < (int) sizeof(buf)) - throw std::runtime_error(buf); - - scoped_pymalloc temp(size + 1); - +#if defined(__GNUC__) + __attribute__((noreturn, __format__ (__printf__, 1, 2))) +#else + [[noreturn]] +#endif +void raise_type_error(const char *fmt, ...) { + va_list args; va_start(args, fmt); - vsnprintf(temp.get(), size + 1, fmt, args); + builtin_exception err = + create_exception(exception_type::type_error, fmt, args); va_end(args); - - throw std::runtime_error(temp.get()); + throw err; } /// Abort the process with a fatal error diff --git a/src/nb_func.cpp b/src/nb_func.cpp index 8a9a2e8f..55c82a97 100644 --- a/src/nb_func.cpp +++ b/src/nb_func.cpp @@ -141,6 +141,7 @@ static bool set_builtin_exception_status(builtin_exception &e) { PyObject *o; switch (e.type()) { + case exception_type::runtime_error: o = PyExc_RuntimeError; break; case exception_type::stop_iteration: o = PyExc_StopIteration; break; case exception_type::index_error: o = PyExc_IndexError; break; case exception_type::key_error: o = PyExc_KeyError; break;