# -*- coding: utf-8 -*- import pytest import env # noqa: F401 from pybind11_tests import class_ as m from pybind11_tests import UserType, ConstructorStats def test_repr(): # In Python 3.3+, repr() accesses __qualname__ assert "pybind11_type" in repr(type(UserType)) assert "UserType" in repr(UserType) def test_instance(msg): with pytest.raises(TypeError) as excinfo: m.NoConstructor() assert msg(excinfo.value) == "m.class_.NoConstructor: No constructor defined!" instance = m.NoConstructor.new_instance() cstats = ConstructorStats.get(m.NoConstructor) assert cstats.alive() == 1 del instance assert cstats.alive() == 0 def test_type(): assert m.check_type(1) == m.DerivedClass1 with pytest.raises(RuntimeError) as execinfo: m.check_type(0) assert "pybind11::detail::get_type_info: unable to find type info" in str( execinfo.value ) assert "Invalid" in str(execinfo.value) # Currently not supported # See https://github.com/pybind/pybind11/issues/2486 # assert m.check_type(2) == int def test_type_of_py(): assert m.get_type_of(1) == int assert m.get_type_of(m.DerivedClass1()) == m.DerivedClass1 assert m.get_type_of(int) == type def test_type_of_classic(): assert m.get_type_classic(1) == int assert m.get_type_classic(m.DerivedClass1()) == m.DerivedClass1 assert m.get_type_classic(int) == type def test_type_of_py_nodelete(): # If the above test deleted the class, this will segfault assert m.get_type_of(m.DerivedClass1()) == m.DerivedClass1 def test_as_type_py(): assert m.as_type(int) == int with pytest.raises(TypeError): assert m.as_type(1) == int with pytest.raises(TypeError): assert m.as_type(m.DerivedClass1()) == m.DerivedClass1 def test_docstrings(doc): assert doc(UserType) == "A `py::class_` type for testing" assert UserType.__name__ == "UserType" assert UserType.__module__ == "pybind11_tests" assert UserType.get_value.__name__ == "get_value" assert UserType.get_value.__module__ == "pybind11_tests" assert ( doc(UserType.get_value) == """ get_value(self: m.UserType) -> int Get value using a method """ ) assert doc(UserType.value) == "Get/set value using a property" assert ( doc(m.NoConstructor.new_instance) == """ new_instance() -> m.class_.NoConstructor Return an instance """ ) def test_qualname(doc): """Tests that a properly qualified name is set in __qualname__ (even in pre-3.3, where we backport the attribute) and that generated docstrings properly use it and the module name""" assert m.NestBase.__qualname__ == "NestBase" assert m.NestBase.Nested.__qualname__ == "NestBase.Nested" assert ( doc(m.NestBase.__init__) == """ __init__(self: m.class_.NestBase) -> None """ ) assert ( doc(m.NestBase.g) == """ g(self: m.class_.NestBase, arg0: m.class_.NestBase.Nested) -> None """ ) assert ( doc(m.NestBase.Nested.__init__) == """ __init__(self: m.class_.NestBase.Nested) -> None """ ) assert ( doc(m.NestBase.Nested.fn) == """ fn(self: m.class_.NestBase.Nested, arg0: int, arg1: m.class_.NestBase, arg2: m.class_.NestBase.Nested) -> None """ # noqa: E501 line too long ) assert ( doc(m.NestBase.Nested.fa) == """ fa(self: m.class_.NestBase.Nested, a: int, b: m.class_.NestBase, c: m.class_.NestBase.Nested) -> None """ # noqa: E501 line too long ) assert m.NestBase.__module__ == "pybind11_tests.class_" assert m.NestBase.Nested.__module__ == "pybind11_tests.class_" def test_inheritance(msg): roger = m.Rabbit("Rabbit") assert roger.name() + " is a " + roger.species() == "Rabbit is a parrot" assert m.pet_name_species(roger) == "Rabbit is a parrot" polly = m.Pet("Polly", "parrot") assert polly.name() + " is a " + polly.species() == "Polly is a parrot" assert m.pet_name_species(polly) == "Polly is a parrot" molly = m.Dog("Molly") assert molly.name() + " is a " + molly.species() == "Molly is a dog" assert m.pet_name_species(molly) == "Molly is a dog" fred = m.Hamster("Fred") assert fred.name() + " is a " + fred.species() == "Fred is a rodent" assert m.dog_bark(molly) == "Woof!" with pytest.raises(TypeError) as excinfo: m.dog_bark(polly) assert ( msg(excinfo.value) == """ dog_bark(): incompatible function arguments. The following argument types are supported: 1. (arg0: m.class_.Dog) -> str Invoked with: """ ) with pytest.raises(TypeError) as excinfo: m.Chimera("lion", "goat") assert "No constructor defined!" in str(excinfo.value) def test_inheritance_init(msg): # Single base class Python(m.Pet): def __init__(self): pass with pytest.raises(TypeError) as exc_info: Python() expected = "m.class_.Pet.__init__() must be called when overriding __init__" assert msg(exc_info.value) == expected # Multiple bases class RabbitHamster(m.Rabbit, m.Hamster): def __init__(self): m.Rabbit.__init__(self, "RabbitHamster") with pytest.raises(TypeError) as exc_info: RabbitHamster() expected = "m.class_.Hamster.__init__() must be called when overriding __init__" assert msg(exc_info.value) == expected def test_automatic_upcasting(): assert type(m.return_class_1()).__name__ == "DerivedClass1" assert type(m.return_class_2()).__name__ == "DerivedClass2" assert type(m.return_none()).__name__ == "NoneType" # Repeat these a few times in a random order to ensure no invalid caching is applied assert type(m.return_class_n(1)).__name__ == "DerivedClass1" assert type(m.return_class_n(2)).__name__ == "DerivedClass2" assert type(m.return_class_n(0)).__name__ == "BaseClass" assert type(m.return_class_n(2)).__name__ == "DerivedClass2" assert type(m.return_class_n(2)).__name__ == "DerivedClass2" assert type(m.return_class_n(0)).__name__ == "BaseClass" assert type(m.return_class_n(1)).__name__ == "DerivedClass1" def test_isinstance(): objects = [tuple(), dict(), m.Pet("Polly", "parrot")] + [m.Dog("Molly")] * 4 expected = (True, True, True, True, True, False, False) assert m.check_instances(objects) == expected def test_mismatched_holder(): import re with pytest.raises(RuntimeError) as excinfo: m.mismatched_holder_1() assert re.match( 'generic_type: type ".*MismatchDerived1" does not have a non-default ' 'holder type while its base ".*MismatchBase1" does', str(excinfo.value), ) with pytest.raises(RuntimeError) as excinfo: m.mismatched_holder_2() assert re.match( 'generic_type: type ".*MismatchDerived2" has a non-default holder type ' 'while its base ".*MismatchBase2" does not', str(excinfo.value), ) def test_override_static(): """#511: problem with inheritance + overwritten def_static""" b = m.MyBase.make() d1 = m.MyDerived.make2() d2 = m.MyDerived.make() assert isinstance(b, m.MyBase) assert isinstance(d1, m.MyDerived) assert isinstance(d2, m.MyDerived) def test_implicit_conversion_life_support(): """Ensure the lifetime of temporary objects created for implicit conversions""" assert m.implicitly_convert_argument(UserType(5)) == 5 assert m.implicitly_convert_variable(UserType(5)) == 5 assert "outside a bound function" in m.implicitly_convert_variable_fail(UserType(5)) def test_operator_new_delete(capture): """Tests that class-specific operator new/delete functions are invoked""" class SubAliased(m.AliasedHasOpNewDelSize): pass with capture: a = m.HasOpNewDel() b = m.HasOpNewDelSize() d = m.HasOpNewDelBoth() assert ( capture == """ A new 8 B new 4 D new 32 """ ) sz_alias = str(m.AliasedHasOpNewDelSize.size_alias) sz_noalias = str(m.AliasedHasOpNewDelSize.size_noalias) with capture: c = m.AliasedHasOpNewDelSize() c2 = SubAliased() assert capture == ("C new " + sz_noalias + "\n" + "C new " + sz_alias + "\n") with capture: del a pytest.gc_collect() del b pytest.gc_collect() del d pytest.gc_collect() assert ( capture == """ A delete B delete 4 D delete """ ) with capture: del c pytest.gc_collect() del c2 pytest.gc_collect() assert capture == ("C delete " + sz_noalias + "\n" + "C delete " + sz_alias + "\n") def test_bind_protected_functions(): """Expose protected member functions to Python using a helper class""" a = m.ProtectedA() assert a.foo() == 42 b = m.ProtectedB() assert b.foo() == 42 class C(m.ProtectedB): def __init__(self): m.ProtectedB.__init__(self) def foo(self): return 0 c = C() assert c.foo() == 0 def test_brace_initialization(): """ Tests that simple POD classes can be constructed using C++11 brace initialization """ a = m.BraceInitialization(123, "test") assert a.field1 == 123 assert a.field2 == "test" # Tests that a non-simple class doesn't get brace initialization (if the # class defines an initializer_list constructor, in particular, it would # win over the expected constructor). b = m.NoBraceInitialization([123, 456]) assert b.vec == [123, 456] @pytest.mark.xfail("env.PYPY") def test_class_refcount(): """Instances must correctly increase/decrease the reference count of their types (#1029)""" from sys import getrefcount class PyDog(m.Dog): pass for cls in m.Dog, PyDog: refcount_1 = getrefcount(cls) molly = [cls("Molly") for _ in range(10)] refcount_2 = getrefcount(cls) del molly pytest.gc_collect() refcount_3 = getrefcount(cls) assert refcount_1 == refcount_3 assert refcount_2 > refcount_1 def test_reentrant_implicit_conversion_failure(msg): # ensure that there is no runaway reentrant implicit conversion (#1035) with pytest.raises(TypeError) as excinfo: m.BogusImplicitConversion(0) assert ( msg(excinfo.value) == """ __init__(): incompatible constructor arguments. The following argument types are supported: 1. m.class_.BogusImplicitConversion(arg0: m.class_.BogusImplicitConversion) Invoked with: 0 """ ) def test_error_after_conversions(): with pytest.raises(TypeError) as exc_info: m.test_error_after_conversions("hello") assert str(exc_info.value).startswith( "Unable to convert function return value to a Python type!" ) def test_aligned(): if hasattr(m, "Aligned"): p = m.Aligned().ptr() assert p % 1024 == 0 # https://foss.heptapod.net/pypy/pypy/-/issues/2742 @pytest.mark.xfail("env.PYPY") def test_final(): with pytest.raises(TypeError) as exc_info: class PyFinalChild(m.IsFinal): pass assert str(exc_info.value).endswith("is not an acceptable base type") # https://foss.heptapod.net/pypy/pypy/-/issues/2742 @pytest.mark.xfail("env.PYPY") def test_non_final_final(): with pytest.raises(TypeError) as exc_info: class PyNonFinalFinalChild(m.IsNonFinalFinal): pass assert str(exc_info.value).endswith("is not an acceptable base type") # https://github.com/pybind/pybind11/issues/1878 def test_exception_rvalue_abort(): with pytest.raises(RuntimeError): m.PyPrintDestructor().throw_something() # https://github.com/pybind/pybind11/issues/1568 def test_multiple_instances_with_same_pointer(capture): n = 100 instances = [m.SamePointer() for _ in range(n)] for i in range(n): # We need to reuse the same allocated memory for with a different type, # to ensure the bug in `deregister_instance_impl` is detected. Otherwise # `Py_TYPE(self) == Py_TYPE(it->second)` will still succeed, even though # the `instance` is already deleted. instances[i] = m.Empty() # No assert: if this does not trigger the error # pybind11_fail("pybind11_object_dealloc(): Tried to deallocate unregistered instance!"); # and just completes without crashing, we're good. # https://github.com/pybind/pybind11/issues/1624 def test_base_and_derived_nested_scope(): assert issubclass(m.DerivedWithNested, m.BaseWithNested) assert m.BaseWithNested.Nested != m.DerivedWithNested.Nested assert m.BaseWithNested.Nested.get_name() == "BaseWithNested::Nested" assert m.DerivedWithNested.Nested.get_name() == "DerivedWithNested::Nested" def test_register_duplicate_class(): import types module_scope = types.ModuleType("module_scope") with pytest.raises(RuntimeError) as exc_info: m.register_duplicate_class_name(module_scope) expected = ( 'generic_type: cannot initialize type "Duplicate": ' "an object with that name is already defined" ) assert str(exc_info.v
Exceptions
##########

Built-in C++ to Python exception translation
============================================

When Python calls C++ code through pybind11, pybind11 provides a C++ exception handler
that will trap C++ exceptions, translate them to the corresponding Python exception,
and raise them so that Python code can handle them.

pybind11 defines translations for ``std::exception`` and its standard
subclasses, and several special exception classes that translate to specific
Python exceptions. Note that these are not actually Python exceptions, so they
cannot be examined using the Python C API. Instead, they are pure C++ objects
that pybind11 will translate the corresponding Python exception when they arrive
at its exception handler.

.. tabularcolumns:: |p{0.5\textwidth}|p{0.45\textwidth}|

+--------------------------------------+--------------------------------------+
|  Exception thrown by C++             |  Translated to Python exception type |
+======================================+======================================+
| :class:`std::exception`              | ``RuntimeError``                     |
+--------------------------------------+--------------------------------------+
| :class:`std::bad_alloc`              | ``MemoryError``                      |
+--------------------------------------+--------------------------------------+
| :class:`std::domain_error`           | ``ValueError``                       |
+--------------------------------------+--------------------------------------+
| :class:`std::invalid_argument`       | ``ValueError``                       |
+--------------------------------------+--------------------------------------+
| :class:`std::length_error`           | ``ValueError``                       |
+--------------------------------------+--------------------------------------+
| :class:`std::out_of_range`           | ``IndexError``                       |
+--------------------------------------+--------------------------------------+
| :class:`std::range_error`            | ``ValueError``                       |
+--------------------------------------+--------------------------------------+
| :class:`std::overflow_error`         | ``OverflowError``                    |
+--------------------------------------+--------------------------------------+
| :class:`pybind11::stop_iteration`    | ``StopIteration`` (used to implement |
|                                      | custom iterators)                    |
+--------------------------------------+--------------------------------------+
| :class:`pybind11::index_error`       | ``IndexError`` (used to indicate out |
|                                      | of bounds access in ``__getitem__``, |
|                                      | ``__setitem__``, etc.)               |
+--------------------------------------+--------------------------------------+
| :class:`pybind11::value_error`       | ``ValueError`` (used to indicate     |
|                                      | wrong value passed in                |
|                                      | ``container.remove(...)``)           |
+--------------------------------------+--------------------------------------+
| :class:`pybind11::key_error`         | ``KeyError`` (used to indicate out   |
|                                      | of bounds access in ``__getitem__``, |
|                                      | ``__setitem__`` in dict-like         |
|                                      | objects, etc.)                       |
+--------------------------------------+--------------------------------------+

Exception translation is not bidirectional. That is, *catching* the C++
exceptions defined above above will not trap exceptions that originate from
Python. For that, catch :class:`pybind11::error_already_set`. See :ref:`below
<handling_python_exceptions_cpp>` for further details.

There is also a special exception :class:`cast_error` that is thrown by
:func:`handle::call` when the input arguments cannot be converted to Python
objects.

Registering custom translators
==============================

If the default exception conversion policy described above is insufficient,
pybind11 also provides support for registering custom exception translators.
To register a simple exception conversion that translates a C++ exception into
a new Python exception using the C++ exception's ``what()`` method, a helper
function is available:

.. code-block:: cpp

    py::register_exception<CppExp>(module, "PyExp");

This call creates a Python exception class with the name ``PyExp`` in the given
module and automatically converts any encountered exceptions of type ``CppExp``
into Python exceptions of type ``PyExp``.

It is possible to specify base class for the exception using the third
parameter, a `handle`:

.. code-block:: cpp

    py::register_exception<CppExp>(module, "PyExp", PyExc_RuntimeError);

Then `PyExp` can be caught both as `PyExp` and `RuntimeError`.

The class objects of the built-in Python exceptions are listed in the Python
documentation on `Standard Exceptions <https://docs.python.org/3/c-api/exceptions.html#standard-exceptions>`_.
The default base class is `PyExc_Exception`.

When more advanced exception translation is needed, the function
``py::register_exception_translator(translator)`` can be used to register
functions that can translate arbitrary exception types (and which may include
additional logic to do so).  The function takes a stateless callable (e.g.  a
function pointer or a lambda function without captured variables) with the call
signature ``void(std::exception_ptr)``.

When a C++ exception is thrown, the registered exception translators are tried
in reverse order of registration (i.e. the last registered translator gets the
first shot at handling the exception).

Inside the translator, ``std::rethrow_exception`` should be used within
a try block to re-throw the exception.  One or more catch clauses to catch
the appropriate exceptions should then be used with each clause using
``PyErr_SetString`` to set a Python exception or ``ex(string)`` to set
the python exception to a custom exception type (see below).

To declare a custom Python exception type, declare a ``py::exception`` variable
and use this in the associated exception translator (note: it is often useful
to make this a static declaration when using it inside a lambda expression
without requiring capturing).

The following example demonstrates this for a hypothetical exception classes
``MyCustomException`` and ``OtherException``: the first is translated to a
custom python exception ``MyCustomError``, while the second is translated to a
standard python RuntimeError:

.. code-block:: cpp

    static py::exception<MyCustomException> exc(m, "MyCustomError");
    py::register_exception_translator([](std::exception_ptr p) {
        try {
            if (p) std::rethrow_exception(p);
        } catch (const MyCustomException &e) {
            exc(e.what());
        } catch (const OtherException &e) {
            PyErr_SetString(PyExc_RuntimeError, e.what());
        }
    });

Multiple exceptions can be handled by a single translator, as shown in the
example above. If the exception is not caught by the current translator, the
previously registered one gets a chance.

If none of the registered exception translators is able to handle the
exception, it is handled by the default converter as described in the previous
section.

.. seealso::

    The file :file:`tests/test_exceptions.cpp` contains examples
    of various custom exception translators and custom exception types.

.. note::

    Call either ``PyErr_SetString`` or a custom exception's call
    operator (``exc(string)``) for every exception caught in a custom exception
    translator.  Failure to do so will cause Python to crash with ``SystemError:
    error return without exception set``.

    Exceptions that you do not plan to handle should simply not be caught, or
    may be explicitly (re-)thrown to delegate it to the other,
    previously-declared existing exception translators.

.. _handling_python_exceptions_cpp:

Handling exceptions from Python in C++
======================================

When C++ calls Python functions, such as in a callback function or when
manipulating Python objects, and Python raises an ``Exception``, pybind11
converts the Python exception into a C++ exception of type
:class:`pybind11::error_already_set` whose payload contains a C++ string textual
summary and the actual Python exception. ``error_already_set`` is used to
propagate Python exception back to Python (or possibly, handle them in C++).

.. tabularcolumns:: |p{0.5\textwidth}|p{0.45\textwidth}|

+--------------------------------------+--------------------------------------+
|  Exception raised in Python          |  Thrown as C++ exception type        |
+======================================+======================================+
| Any Python ``Exception``             | :class:`pybind11::error_already_set` |
+--------------------------------------+--------------------------------------+

For example:

.. code-block:: cpp

    try {
        // open("missing.txt", "r")
        auto file = py::module_::import("io").attr("open")("missing.txt", "r");
        auto text = file.attr("read")();
        file.attr("close")();
    } catch (py::error_already_set &e) {
        if (e.matches(PyExc_FileNotFoundError)) {
            py::print("missing.txt not found");
        } else if (e.match(PyExc_PermissionError)) {
            py::print("missing.txt found but not accessible");
        } else {
            throw;
        }
    }

Note that C++ to Python exception translation does not apply here, since that is
a method for translating C++ exceptions to Python, not vice versa. The error raised
from Python is always ``error_already_set``.

This example illustrates this behavior:

.. code-block:: cpp

    try {
        py::eval("raise ValueError('The Ring')");
    } catch (py::value_error &boromir) {
        // Boromir never gets the ring
        assert(false);
    } catch (py::error_already_set &frodo) {
        // Frodo gets the ring
        py::print("I will take the ring");
    }

    try {
        // py::value_error is a request for pybind11 to raise a Python exception
        throw py::value_error("The ball");
    } catch (py::error_already_set &cat) {
        // cat won't catch the ball since
        // py::value_error is not a Python exception
        assert(false);
    } catch (py::value_error &dog) {
        // dog will catch the ball
        py::print("Run Spot run");
        throw;  // Throw it again (pybind11 will raise ValueError)
    }

Handling errors from the Python C API
=====================================

Where possible, use :ref:`pybind11 wrappers <wrappers>` instead of calling
the Python C API directly. When calling the Python C API directly, in
addition to manually managing reference counts, one must follow the pybind11
error protocol, which is outlined here.

After calling the Python C API, if Python returns an error,
``throw py::error_already_set();``, which allows pybind11 to deal with the
exception and pass it back to the Python interpreter. This includes calls to
the error setting functions such as ``PyErr_SetString``.

.. code-block:: cpp

    PyErr_SetString(PyExc_TypeError, "C API type error demo");
    throw py::error_already_set();

    // But it would be easier to simply...
    throw py::type_error("pybind11 wrapper type error");

Alternately, to ignore the error, call `PyErr_Clear
<https://docs.python.org/3/c-api/exceptions.html#c.PyErr_Clear>`_.

Any Python error must be thrown or cleared, or Python/pybind11 will be left in
an invalid state.

.. _unraisable_exceptions:

Handling unraisable exceptions
==============================

If a Python function invoked from a C++ destructor or any function marked
``noexcept(true)`` (collectively, "noexcept functions") throws an exception, there
is no way to propagate the exception, as such functions may not throw.
Should they throw or fail to catch any exceptions in their call graph,
the C++ runtime calls ``std::terminate()`` to abort immediately.

Similarly, Python exceptions raised in a class's ``__del__`` method do not
propagate, but are logged by Python as an unraisable error. In Python 3.8+, a
`system hook is triggered
<https://docs.python.org/3/library/sys.html#sys.unraisablehook>`_
and an auditing event is logged.

Any noexcept function should have a try-catch block that traps
class:`error_already_set` (or any other exception that can occur). Note that
pybind11 wrappers around Python exceptions such as
:class:`pybind11::value_error` are *not* Python exceptions; they are C++
exceptions that pybind11 catches and converts to Python exceptions. Noexcept
functions cannot propagate these exceptions either. A useful approach is to
convert them to Python exceptions and then ``discard_as_unraisable`` as shown
below.

.. code-block:: cpp

    void nonthrowing_func() noexcept(true) {
        try {
            // ...
        } catch (py::error_already_set &eas) {
            // Discard the Python error using Python APIs, using the C++ magic
            // variable __func__. Python already knows the type and value and of the
            // exception object.
            eas.discard_as_unraisable(__func__);
        } catch (const std::exception &e) {
            // Log and discard C++ exceptions.
            third_party::log(e);
        }
    }

.. versionadded:: 2.6