@@ -664,29 +664,10 @@ class cpp_function : public function {
664664 e.restore ();
665665 return nullptr ;
666666 } catch (...) {
667- /* When an exception is caught, give each registered exception
668- translator a chance to translate it to a Python exception
669- in reverse order of registration.
670-
671- A translator may choose to do one of the following:
672-
673- - catch the exception and call PyErr_SetString or PyErr_SetObject
674- to set a standard (or custom) Python exception, or
675- - do nothing and let the exception fall through to the next translator, or
676- - delegate translation to the next translator by throwing a new type of exception. */
677-
678- auto last_exception = std::current_exception ();
679- auto ®istered_exception_translators = get_internals ().registered_exception_translators ;
680- for (auto & translator : registered_exception_translators) {
681- try {
682- translator (last_exception);
683- } catch (...) {
684- last_exception = std::current_exception ();
685- continue ;
686- }
687- return nullptr ;
688- }
689- PyErr_SetString (PyExc_SystemError, " Exception escaped from default exception translator!" );
667+ // When an exception is caught, try to translate it into Python exception.
668+ auto untranslated = translate_exception (std::current_exception ());
669+ if (untranslated)
670+ PyErr_SetString (PyExc_SystemError, " Exception escaped from default exception translator!" );
690671 return nullptr ;
691672 }
692673
@@ -1841,6 +1822,112 @@ void print(Args &&...args) {
18411822 detail::print (c.args (), c.kwargs ());
18421823}
18431824
1825+ NAMESPACE_BEGIN (detail)
1826+
1827+ template <typename T>
1828+ struct dependent_false { static constexpr bool value = false ; };
1829+
1830+ template <typename Block, typename Signature = remove_cv_t <function_signature_t <Block>>, typename SFINAE = void >
1831+ struct with_block_call_traits {
1832+ static void call (Block &&block, object &&) {
1833+ static_assert (dependent_false<Block>::value,
1834+ " The inner block function passed to pybind11::with should either take no arguments, "
1835+ " or a single argument convertible from a pybind11::object& or pybind11::object&&, "
1836+ " and should return void." );
1837+ }
1838+ };
1839+
1840+ template <typename Block>
1841+ struct with_block_call_traits <Block, void ()> {
1842+ public:
1843+ static void call (Block &&block, object &&) {
1844+ std::forward<Block>(block)();
1845+ }
1846+ };
1847+
1848+ template <typename Block, typename Arg>
1849+ struct with_block_call_traits <Block, void (Arg),
1850+ enable_if_t <std::is_convertible<object&, Arg>::value ||
1851+ std::is_convertible<object&&, Arg>::value>> {
1852+ private:
1853+ static void call_impl (Block &&block, object &&obj, std::true_type) {
1854+ std::forward<Block>(block)(std::move (obj));
1855+ }
1856+
1857+ static void call_impl (Block &&block, object &&obj, std::false_type) {
1858+ std::forward<Block>(block)(obj);
1859+ }
1860+
1861+ public:
1862+ static void call (Block &&block, object &&obj) {
1863+ call_impl (std::forward<Block>(block), std::move (obj), std::is_convertible<object&&, Arg>());
1864+ }
1865+ };
1866+
1867+ template <typename Block>
1868+ void call_with_block (Block &&block, object &&obj) {
1869+ with_block_call_traits<Block>::call (std::forward<Block>(block), std::move (obj));
1870+ }
1871+
1872+ NAMESPACE_END (detail)
1873+
1874+ enum class with_exception_policy {
1875+ cascade,
1876+ translate
1877+ };
1878+
1879+ // PEP 343 specification: https://www.python.org/dev/peps/pep-0343/#specification-the-with-statement
1880+ template <typename Block>
1881+ void with (const object &mgr, Block &&block, with_exception_policy policy = with_exception_policy::translate) {
1882+ object exit = mgr.attr (" __exit__" );
1883+ object value = mgr.attr (" __enter__" )();
1884+ bool exc = true ;
1885+
1886+ std::exception_ptr original_exception = nullptr ;
1887+ try {
1888+ try {
1889+ detail::call_with_block (std::forward<Block>(block), std::move (value));
1890+ }
1891+ catch (const error_already_set &) {
1892+ exc = false ;
1893+ // If already a Python error, catch in the outer try-catch
1894+ original_exception = std::current_exception ();
1895+ throw ;
1896+ }
1897+ catch (...) {
1898+ exc = false ;
1899+ // Else, try our best to translate the error into a Python error before calling mrg.__exit__
1900+ original_exception = std::current_exception ();
1901+ if (policy == with_exception_policy::translate) {
1902+ auto untranslated = translate_exception (std::current_exception ());
1903+ if (untranslated)
1904+ std::rethrow_exception (untranslated);
1905+ else
1906+ throw error_already_set ();
1907+ }
1908+ else {
1909+ throw ;
1910+ }
1911+ }
1912+ }
1913+ catch (const error_already_set &e) {
1914+ // A Python error
1915+ auto exit_result = exit (e.get_type () ? e.get_type () : none (),
1916+ e.get_value () ? e.get_value () : none (),
1917+ e.get_trace () ? e.get_trace () : none ());
1918+ if (!bool_ (std::move (exit_result)))
1919+ std::rethrow_exception (original_exception);
1920+ }
1921+ catch (...) {
1922+ // Not a Python error
1923+ exit (none (), none (), none ());
1924+ std::rethrow_exception (original_exception);
1925+ }
1926+
1927+ if (exc)
1928+ exit (none (), none (), none ());
1929+ }
1930+
18441931#if defined(WITH_THREAD) && !defined(PYPY_VERSION)
18451932
18461933/* The functions below essentially reproduce the PyGILState_* API using a RAII
0 commit comments