diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 830c0374ab..51d4bee9c5 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -137,6 +137,7 @@ set(PYBIND11_TEST_FILES test_constants_and_functions test_copy_move test_cpp_conduit + test_cprofile_compatibility.py test_custom_type_casters test_custom_type_setup test_docstring_options @@ -241,6 +242,7 @@ tests_extra_targets("test_exceptions.py" "cross_module_interleaved_error_already tests_extra_targets("test_gil_scoped.py" "cross_module_gil_utils") tests_extra_targets("test_cpp_conduit.py" "exo_planet_pybind11;exo_planet_c_api;home_planet_very_lonely_traveler") +tests_extra_targets("test_cprofile_compatibility.py" "cprofile_compatibility_ext") set(PYBIND11_EIGEN_REPO "https://gitlab.com/libeigen/eigen.git" diff --git a/tests/cprofile_compatibility_ext.cpp b/tests/cprofile_compatibility_ext.cpp new file mode 100644 index 0000000000..842477155c --- /dev/null +++ b/tests/cprofile_compatibility_ext.cpp @@ -0,0 +1,19 @@ +#include + +namespace pybind11_tests { +namespace cprofile_compatibility { +class CppClass {}; +} // namespace cprofile_compatibility +} // namespace pybind11_tests + +namespace py = pybind11; + +PYBIND11_MODULE(cprofile_compatibility_ext, m) { + using namespace pybind11_tests::cprofile_compatibility; + + m.def("free_func_return_secret", []() { return 102; }); + + py::class_(m, "CppClass") + .def(py::init<>()) + .def("member_func_return_secret", [](const CppClass &) { return 203; }); +} diff --git a/tests/test_cprofile_compatibility.py b/tests/test_cprofile_compatibility.py new file mode 100644 index 0000000000..f91a3a1235 --- /dev/null +++ b/tests/test_cprofile_compatibility.py @@ -0,0 +1,43 @@ +from __future__ import annotations + +import cProfile as profile +import io +import pstats + +import cprofile_compatibility_ext + + +def _get_pstats(profiler): + output = io.StringIO() + stats = pstats.Stats(profiler, stream=output) + stats.sort_stats("cumtime") + stats.print_stats() + return output.getvalue() + + +class PyClass: + def py_member_func(self) -> None: + return cprofile_compatibility_ext.CppClass().member_func_return_secret() + + +def test_free_func_return_secret(): + profiler = profile.Profile() + profiler.enable() + assert cprofile_compatibility_ext.free_func_return_secret() == 102 + profiler.disable() + stats_output = _get_pstats(profiler) + raise AssertionError( + f"\npstats.Stats output free_func BEGIN:\n{stats_output}\npstats.Stats output free_func END\n" + ) + + +def test_member_func_return_secret(): + obj = PyClass() + profiler = profile.Profile() + profiler.enable() + assert obj.py_member_func() == 203 + profiler.disable() + stats_output = _get_pstats(profiler) + raise AssertionError( + f"\npstats.Stats output member_func BEGIN:\n{stats_output}\npstats.Stats output member_func END\n" + )