@@ -411,175 +411,7 @@ T reinterpret_steal(handle h) {
411411}
412412
413413PYBIND11_NAMESPACE_BEGIN (detail)
414-
415- // Equivalent to obj.__class__.__name__ (or obj.__name__ if obj is a class).
416- inline const char *obj_class_name(PyObject *obj) {
417- if (Py_TYPE (obj) == &PyType_Type) {
418- return reinterpret_cast <PyTypeObject *>(obj)->tp_name ;
419- }
420- return Py_TYPE (obj)->tp_name ;
421- }
422-
423- std::string error_string ();
424-
425- struct error_fetch_and_normalize {
426- // Immediate normalization is long-established behavior (starting with
427- // https://github.com/pybind/pybind11/commit/135ba8deafb8bf64a15b24d1513899eb600e2011
428- // from Sep 2016) and safest. Normalization could be deferred, but this could mask
429- // errors elsewhere, the performance gain is very minor in typical situations
430- // (usually the dominant bottleneck is EH unwinding), and the implementation here
431- // would be more complex.
432- explicit error_fetch_and_normalize (const char *called) {
433- PyErr_Fetch (&m_type.ptr (), &m_value.ptr (), &m_trace.ptr ());
434- if (!m_type) {
435- pybind11_fail (" Internal error: " + std::string (called)
436- + " called while "
437- " Python error indicator not set." );
438- }
439- const char *exc_type_name_orig = detail::obj_class_name (m_type.ptr ());
440- if (exc_type_name_orig == nullptr ) {
441- pybind11_fail (" Internal error: " + std::string (called)
442- + " failed to obtain the name "
443- " of the original active exception type." );
444- }
445- m_lazy_error_string = exc_type_name_orig;
446- // PyErr_NormalizeException() may change the exception type if there are cascading
447- // failures. This can potentially be extremely confusing.
448- PyErr_NormalizeException (&m_type.ptr (), &m_value.ptr (), &m_trace.ptr ());
449- if (m_type.ptr () == nullptr ) {
450- pybind11_fail (" Internal error: " + std::string (called)
451- + " failed to normalize the "
452- " active exception." );
453- }
454- const char *exc_type_name_norm = detail::obj_class_name (m_type.ptr ());
455- if (exc_type_name_orig == nullptr ) {
456- pybind11_fail (" Internal error: " + std::string (called)
457- + " failed to obtain the name "
458- " of the normalized active exception type." );
459- }
460- if (exc_type_name_norm != m_lazy_error_string) {
461- std::string msg = std::string (called)
462- + " : MISMATCH of original and normalized "
463- " active exception types: " ;
464- msg += " ORIGINAL " ;
465- msg += m_lazy_error_string;
466- msg += " REPLACED BY " ;
467- msg += exc_type_name_norm;
468- msg += " : " + format_value_and_trace ();
469- pybind11_fail (msg);
470- }
471- }
472-
473- error_fetch_and_normalize (const error_fetch_and_normalize &) = delete ;
474- error_fetch_and_normalize (error_fetch_and_normalize &&) = delete ;
475-
476- std::string format_value_and_trace () const {
477- std::string result;
478- std::string message_error_string;
479- if (m_value) {
480- auto value_str = reinterpret_steal<object>(PyObject_Str (m_value.ptr ()));
481- if (!value_str) {
482- message_error_string = detail::error_string ();
483- result = " <MESSAGE UNAVAILABLE DUE TO ANOTHER EXCEPTION>" ;
484- } else {
485- result = value_str.cast <std::string>();
486- }
487- } else {
488- result = " <MESSAGE UNAVAILABLE>" ;
489- }
490- if (result.empty ()) {
491- result = " <EMPTY MESSAGE>" ;
492- }
493-
494- bool have_trace = false ;
495- if (m_trace) {
496- #if !defined(PYPY_VERSION)
497- auto *tb = reinterpret_cast <PyTracebackObject *>(m_trace.ptr ());
498-
499- // Get the deepest trace possible.
500- while (tb->tb_next ) {
501- tb = tb->tb_next ;
502- }
503-
504- PyFrameObject *frame = tb->tb_frame ;
505- Py_XINCREF (frame);
506- result += " \n\n At:\n " ;
507- while (frame) {
508- # if PY_VERSION_HEX >= 0x030900B1
509- PyCodeObject *f_code = PyFrame_GetCode (frame);
510- # else
511- PyCodeObject *f_code = frame->f_code ;
512- Py_INCREF (f_code);
513- # endif
514- int lineno = PyFrame_GetLineNumber (frame);
515- result += " " ;
516- result += handle (f_code->co_filename ).cast <std::string>();
517- result += ' (' ;
518- result += std::to_string (lineno);
519- result += " ): " ;
520- result += handle (f_code->co_name ).cast <std::string>();
521- result += ' \n ' ;
522- Py_DECREF (f_code);
523- # if PY_VERSION_HEX >= 0x030900B1
524- auto *b_frame = PyFrame_GetBack (frame);
525- # else
526- auto *b_frame = frame->f_back ;
527- Py_XINCREF (b_frame);
528- # endif
529- Py_DECREF (frame);
530- frame = b_frame;
531- }
532-
533- have_trace = true ;
534- #endif // ! defined(PYPY_VERSION)
535- }
536-
537- if (!message_error_string.empty ()) {
538- if (!have_trace) {
539- result += ' \n ' ;
540- }
541- result += " \n MESSAGE UNAVAILABLE DUE TO EXCEPTION: " + message_error_string;
542- }
543-
544- return result;
545- }
546-
547- std::string const &error_string () const {
548- if (!m_lazy_error_string_completed) {
549- m_lazy_error_string += " : " + format_value_and_trace ();
550- m_lazy_error_string_completed = true ;
551- }
552- return m_lazy_error_string;
553- }
554-
555- void restore () {
556- if (m_restore_called) {
557- pybind11_fail (" Internal error: pybind11::detail::error_fetch_and_normalize::restore() "
558- " called a second time. ORIGINAL ERROR: "
559- + error_string ());
560- }
561- PyErr_Restore (m_type.inc_ref ().ptr (), m_value.inc_ref ().ptr (), m_trace.inc_ref ().ptr ());
562- m_restore_called = true ;
563- }
564-
565- bool matches (handle exc) const {
566- return (PyErr_GivenExceptionMatches (m_type.ptr (), exc.ptr ()) != 0 );
567- }
568-
569- // Not protecting these for simplicity.
570- object m_type, m_value, m_trace;
571-
572- private:
573- // Only protecting invariants.
574- mutable std::string m_lazy_error_string;
575- mutable bool m_lazy_error_string_completed = false ;
576- mutable bool m_restore_called = false ;
577- };
578-
579- inline std::string error_string () {
580- return error_fetch_and_normalize (" pybind11::detail::error_string" ).error_string ();
581- }
582-
414+ std::string error_string(const char *called = nullptr );
583415PYBIND11_NAMESPACE_END (detail)
584416
585417#if defined(_MSC_VER)
@@ -592,30 +424,39 @@ PYBIND11_NAMESPACE_END(detail)
592424// / thrown to propagate python-side errors back through C++ which can either be caught manually or
593425// / else falls back to the function dispatcher (which then raises the captured error back to
594426// / python).
595- class PYBIND11_EXPORT_EXCEPTION error_already_set : public std::exception {
427+ class PYBIND11_EXPORT_EXCEPTION error_already_set : public std::runtime_error {
596428public:
597- // / Fetches the current Python exception (using PyErr_Fetch()), which will clear the
598- // / current Python error indicator.
599- error_already_set ()
600- : m_fetched_error{new detail::error_fetch_and_normalize (" pybind11::error_already_set" ),
601- m_fetched_error_deleter} {}
602-
603- // / The what() result is built lazily on demand.
604- // / WARNING: This member function needs to acquire the Python GIL. This can lead to
429+ // / Constructs a new exception from the current Python error indicator. The current
430+ // / Python error indicator will be cleared.
431+ error_already_set () : std::runtime_error(detail::error_string(" pybind11::error_already_set" )) {
432+ PyErr_Fetch (&m_type.ptr (), &m_value.ptr (), &m_trace.ptr ());
433+ }
434+
435+ // / WARNING: The GIL must be held when this copy constructor is invoked!
436+ error_already_set (const error_already_set &) = default ;
437+ error_already_set (error_already_set &&) = default ;
438+
439+ // / WARNING: This destructor needs to acquire the Python GIL. This can lead to
605440 // / crashes (undefined behavior) if the Python interpreter is finalizing.
606- const char * what () const noexcept override ;
441+ inline ~error_already_set () override ;
607442
608443 // / Restores the currently-held Python error (which will clear the Python error indicator first
609- // / if already set).
444+ // / if already set). After this call, the current object no longer stores the error variables.
445+ // / NOTE: Any copies of this object may still store the error variables. Currently there is no
446+ // protection against calling restore() from multiple copies.
610447 // / NOTE: This member function will always restore the normalized exception, which may or may
611448 // / not be the original Python exception.
612449 // / WARNING: The GIL must be held when this member function is called!
613- void restore () { m_fetched_error->restore (); }
450+ void restore () {
451+ PyErr_Restore (m_type.release ().ptr (), m_value.release ().ptr (), m_trace.release ().ptr ());
452+ }
614453
615454 // / If it is impossible to raise the currently-held error, such as in a destructor, we can
616455 // / write it out using Python's unraisable hook (`sys.unraisablehook`). The error context
617456 // / should be some object whose `repr()` helps identify the location of the error. Python
618- // / already knows the type and value of the error, so there is no need to repeat that.
457+ // / already knows the type and value of the error, so there is no need to repeat that. After
458+ // / this call, the current object no longer stores the error variables, and neither does
459+ // / Python.
619460 void discard_as_unraisable (object err_context) {
620461 restore ();
621462 PyErr_WriteUnraisable (err_context.ptr ());
@@ -634,18 +475,16 @@ class PYBIND11_EXPORT_EXCEPTION error_already_set : public std::exception {
634475 // / Check if the currently trapped error type matches the given Python exception class (or a
635476 // / subclass thereof). May also be passed a tuple to search for any exception class matches in
636477 // / the given tuple.
637- bool matches (handle exc) const { return m_fetched_error->matches (exc); }
478+ bool matches (handle exc) const {
479+ return (PyErr_GivenExceptionMatches (m_type.ptr (), exc.ptr ()) != 0 );
480+ }
638481
639- const object &type () const { return m_fetched_error-> m_type ; }
640- const object &value () const { return m_fetched_error-> m_value ; }
641- const object &trace () const { return m_fetched_error-> m_trace ; }
482+ const object &type () const { return m_type; }
483+ const object &value () const { return m_value; }
484+ const object &trace () const { return m_trace; }
642485
643486private:
644- std::shared_ptr<detail::error_fetch_and_normalize> m_fetched_error;
645-
646- // / WARNING: This custom deleter needs to acquire the Python GIL. This can lead to
647- // / crashes (undefined behavior) if the Python interpreter is finalizing.
648- static void m_fetched_error_deleter (detail::error_fetch_and_normalize *raw_ptr);
487+ object m_type, m_value, m_trace;
649488};
650489#if defined(_MSC_VER)
651490# pragma warning(pop)
@@ -681,7 +520,8 @@ inline void raise_from(PyObject *type, const char *message) {
681520
682521// / Sets the current Python error indicator with the chosen error, performing a 'raise from'
683522// / from the error contained in error_already_set to indicate that the chosen error was
684- // / caused by the original error.
523+ // / caused by the original error. After this function is called error_already_set will
524+ // / no longer contain an error.
685525inline void raise_from (error_already_set &err, PyObject *type, const char *message) {
686526 err.restore ();
687527 raise_from (type, message);
0 commit comments