@@ -435,11 +435,8 @@ struct error_fetch_and_normalize {
435
435
}
436
436
}
437
437
438
- template <typename GilScopedAcquire>
439
- error_fetch_and_normalize (const GilScopedAcquire &, const error_fetch_and_normalize &other)
440
- : m_type{other.m_type }, m_value{other.m_value }, m_trace{other.m_trace },
441
- m_lazy_error_string{other.m_lazy_error_string },
442
- m_lazy_error_string_completed{other.m_lazy_error_string_completed } {}
438
+ error_fetch_and_normalize (const error_fetch_and_normalize &) = delete ;
439
+ error_fetch_and_normalize (error_fetch_and_normalize &&) = delete ;
443
440
444
441
std::string complete_lazy_error_string () const {
445
442
std::string result;
@@ -521,10 +518,13 @@ struct error_fetch_and_normalize {
521
518
}
522
519
523
520
void restore () {
524
- // As long as this type is copyable, there is no point in releasing m_type, m_value,
525
- // m_trace, but simply holding on the the references makes it possible to produce
526
- // what() even after restore().
521
+ if (m_restore_called) {
522
+ pybind11_fail (" Internal error: pybind11::detail::error_fetch_and_normalize::restore() "
523
+ " called a second time. ORIGINAL ERROR: "
524
+ + error_string ());
525
+ }
527
526
PyErr_Restore (m_type.inc_ref ().ptr (), m_value.inc_ref ().ptr (), m_trace.inc_ref ().ptr ());
527
+ m_restore_called = true ;
528
528
}
529
529
530
530
bool matches (handle exc) const {
@@ -542,6 +542,7 @@ struct error_fetch_and_normalize {
542
542
object m_type, m_value, m_trace;
543
543
mutable std::string m_lazy_error_string;
544
544
mutable bool m_lazy_error_string_completed = false ;
545
+ mutable bool m_restore_called = false ;
545
546
};
546
547
547
548
inline std::string error_string () {
@@ -564,18 +565,21 @@ class PYBIND11_EXPORT_EXCEPTION error_already_set : public std::exception {
564
565
public:
565
566
// / Fetches the current Python exception (using PyErr_Fetch()), which will clear the
566
567
// / current Python error indicator.
567
- error_already_set () : m_fetched_error{" pybind11::error_already_set" } {}
568
+ error_already_set ()
569
+ : m_fetched_error{
570
+ std::make_shared<detail::error_fetch_and_normalize>(" pybind11::error_already_set" )} {}
568
571
569
572
// / WARNING: This destructor needs to acquire the Python GIL. This can lead to
570
573
// / crashes (undefined behavior) if the Python interpreter is finalizing.
571
- ~error_already_set ();
574
+ ~error_already_set () override ;
572
575
573
- // / The C++ standard explicitly prohibits deleting this copy ctor: C++17 18.1.5.
574
- // / WARNING: This copy constructor needs to acquire the Python GIL. This can lead to
575
- // / crashes (undefined behavior) if the Python interpreter is finalizing.
576
- error_already_set (const error_already_set &);
576
+ // This copy ctor does not need the GIL because it simply increments a shared_ptr use_count.
577
+ error_already_set (const error_already_set &) noexcept = default ;
577
578
578
- error_already_set (error_already_set &&) = default ;
579
+ // This move ctor cannot easily be deleted (some compilers need it).
580
+ // It is the responsibility of the caller to not use the moved-from object.
581
+ // For simplicity, guarding ifs are omitted.
582
+ error_already_set (error_already_set &&) noexcept = default ;
579
583
580
584
// / The what() result is built lazily on demand.
581
585
// / WARNING: This member function needs to acquire the Python GIL. This can lead to
@@ -587,7 +591,7 @@ class PYBIND11_EXPORT_EXCEPTION error_already_set : public std::exception {
587
591
// / NOTE: This member function will always restore the normalized exception, which may or may
588
592
// / not be the original Python exception.
589
593
// / WARNING: The GIL must be held when this member function is called!
590
- void restore () { m_fetched_error. restore (); }
594
+ void restore () { m_fetched_error-> restore (); }
591
595
592
596
// / If it is impossible to raise the currently-held error, such as in a destructor, we can
593
597
// / write it out using Python's unraisable hook (`sys.unraisablehook`). The error context
@@ -611,14 +615,14 @@ class PYBIND11_EXPORT_EXCEPTION error_already_set : public std::exception {
611
615
// / Check if the currently trapped error type matches the given Python exception class (or a
612
616
// / subclass thereof). May also be passed a tuple to search for any exception class matches in
613
617
// / the given tuple.
614
- bool matches (handle exc) const { return m_fetched_error. matches (exc); }
618
+ bool matches (handle exc) const { return m_fetched_error-> matches (exc); }
615
619
616
- const object &type () const { return m_fetched_error. m_type ; }
617
- const object &value () const { return m_fetched_error. m_value ; }
618
- const object &trace () const { return m_fetched_error. m_trace ; }
620
+ const object &type () const { return m_fetched_error-> m_type ; }
621
+ const object &value () const { return m_fetched_error-> m_value ; }
622
+ const object &trace () const { return m_fetched_error-> m_trace ; }
619
623
620
624
private:
621
- detail::error_fetch_and_normalize m_fetched_error;
625
+ std::shared_ptr< detail::error_fetch_and_normalize> m_fetched_error;
622
626
};
623
627
#if defined(_MSC_VER)
624
628
# pragma warning(pop)
0 commit comments