From a72f898ff4c4237424c468044a6db9d6953b541e Mon Sep 17 00:00:00 2001 From: gatecat Date: Wed, 14 Sep 2022 09:28:47 +0200 Subject: 3rdparty: Bump vendored pybind11 version for py3.11 support Signed-off-by: gatecat --- 3rdparty/pybind11/tests/CMakeLists.txt | 337 ++++++++--- 3rdparty/pybind11/tests/conftest.py | 31 +- 3rdparty/pybind11/tests/constructor_stats.h | 143 +++-- 3rdparty/pybind11/tests/cross_module_gil_utils.cpp | 48 +- .../cross_module_interleaved_error_already_set.cpp | 51 ++ 3rdparty/pybind11/tests/env.py | 20 +- .../tests/extra_python_package/test_files.py | 164 ++--- .../tests/extra_setuptools/test_setuphelper.py | 68 ++- 3rdparty/pybind11/tests/local_bindings.h | 52 +- 3rdparty/pybind11/tests/object.h | 112 ++-- .../pybind11/tests/pybind11_cross_module_tests.cpp | 90 ++- 3rdparty/pybind11/tests/pybind11_tests.cpp | 54 +- 3rdparty/pybind11/tests/pybind11_tests.h | 54 +- 3rdparty/pybind11/tests/pytest.ini | 13 +- 3rdparty/pybind11/tests/requirements.txt | 17 +- 3rdparty/pybind11/tests/test_async.cpp | 5 +- 3rdparty/pybind11/tests/test_async.py | 1 - 3rdparty/pybind11/tests/test_buffers.cpp | 134 ++-- 3rdparty/pybind11/tests/test_buffers.py | 23 +- 3rdparty/pybind11/tests/test_builtin_casters.cpp | 337 ++++++++--- 3rdparty/pybind11/tests/test_builtin_casters.py | 319 ++++++---- 3rdparty/pybind11/tests/test_call_policies.cpp | 44 +- 3rdparty/pybind11/tests/test_call_policies.py | 34 +- 3rdparty/pybind11/tests/test_callbacks.cpp | 181 ++++-- 3rdparty/pybind11/tests/test_callbacks.py | 57 +- 3rdparty/pybind11/tests/test_chrono.cpp | 33 +- 3rdparty/pybind11/tests/test_chrono.py | 41 +- 3rdparty/pybind11/tests/test_class.cpp | 392 +++++++----- 3rdparty/pybind11/tests/test_class.py | 23 +- .../pybind11/tests/test_cmake_build/CMakeLists.txt | 7 +- 3rdparty/pybind11/tests/test_cmake_build/embed.cpp | 8 +- .../installed_embed/CMakeLists.txt | 6 +- .../installed_function/CMakeLists.txt | 3 +- .../installed_target/CMakeLists.txt | 3 +- .../subdirectory_embed/CMakeLists.txt | 8 +- .../subdirectory_function/CMakeLists.txt | 5 +- .../subdirectory_target/CMakeLists.txt | 5 +- 3rdparty/pybind11/tests/test_cmake_build/test.py | 6 +- 3rdparty/pybind11/tests/test_const_name.cpp | 55 ++ 3rdparty/pybind11/tests/test_const_name.py | 29 + .../tests/test_constants_and_functions.cpp | 116 ++-- .../pybind11/tests/test_constants_and_functions.py | 12 +- 3rdparty/pybind11/tests/test_copy_move.cpp | 236 +++++--- 3rdparty/pybind11/tests/test_copy_move.py | 13 +- .../pybind11/tests/test_custom_type_casters.cpp | 174 ++++-- .../pybind11/tests/test_custom_type_casters.py | 12 +- 3rdparty/pybind11/tests/test_custom_type_setup.cpp | 41 ++ 3rdparty/pybind11/tests/test_custom_type_setup.py | 48 ++ 3rdparty/pybind11/tests/test_docstring_options.cpp | 63 +- 3rdparty/pybind11/tests/test_docstring_options.py | 4 +- 3rdparty/pybind11/tests/test_eigen.cpp | 269 ++++++--- 3rdparty/pybind11/tests/test_eigen.py | 91 +-- 3rdparty/pybind11/tests/test_embed/CMakeLists.txt | 8 +- 3rdparty/pybind11/tests/test_embed/catch.cpp | 29 +- .../pybind11/tests/test_embed/external_module.cpp | 11 +- .../pybind11/tests/test_embed/test_interpreter.cpp | 180 +++++- .../pybind11/tests/test_embed/test_interpreter.py | 8 +- .../pybind11/tests/test_embed/test_trampoline.py | 16 + 3rdparty/pybind11/tests/test_enum.cpp | 106 +++- 3rdparty/pybind11/tests/test_enum.py | 69 ++- 3rdparty/pybind11/tests/test_eval.cpp | 53 +- 3rdparty/pybind11/tests/test_eval.py | 21 +- 3rdparty/pybind11/tests/test_eval_call.py | 1 - 3rdparty/pybind11/tests/test_exceptions.cpp | 232 +++++-- 3rdparty/pybind11/tests/test_exceptions.h | 13 + 3rdparty/pybind11/tests/test_exceptions.py | 196 +++++- .../pybind11/tests/test_factory_constructors.cpp | 323 ++++++---- .../pybind11/tests/test_factory_constructors.py | 24 +- 3rdparty/pybind11/tests/test_gil_scoped.cpp | 49 +- 3rdparty/pybind11/tests/test_gil_scoped.py | 1 - 3rdparty/pybind11/tests/test_iostream.cpp | 95 ++- 3rdparty/pybind11/tests/test_iostream.py | 155 +++-- .../pybind11/tests/test_kwargs_and_defaults.cpp | 282 ++++++--- .../pybind11/tests/test_kwargs_and_defaults.py | 123 +++- 3rdparty/pybind11/tests/test_local_bindings.cpp | 47 +- 3rdparty/pybind11/tests/test_local_bindings.py | 6 +- .../pybind11/tests/test_methods_and_attributes.cpp | 322 ++++++---- .../pybind11/tests/test_methods_and_attributes.py | 94 +-- 3rdparty/pybind11/tests/test_modules.cpp | 72 ++- 3rdparty/pybind11/tests/test_modules.py | 52 +- .../pybind11/tests/test_multiple_inheritance.cpp | 277 ++++++--- .../pybind11/tests/test_multiple_inheritance.py | 148 ++++- 3rdparty/pybind11/tests/test_numpy_array.cpp | 470 +++++++++------ 3rdparty/pybind11/tests/test_numpy_array.py | 74 ++- 3rdparty/pybind11/tests/test_numpy_dtypes.cpp | 399 +++++++----- 3rdparty/pybind11/tests/test_numpy_dtypes.py | 71 ++- 3rdparty/pybind11/tests/test_numpy_vectorize.cpp | 94 +-- 3rdparty/pybind11/tests/test_numpy_vectorize.py | 2 +- 3rdparty/pybind11/tests/test_opaque_types.cpp | 20 +- 3rdparty/pybind11/tests/test_opaque_types.py | 8 +- .../pybind11/tests/test_operator_overloading.cpp | 198 +++--- .../pybind11/tests/test_operator_overloading.py | 15 +- 3rdparty/pybind11/tests/test_pickling.cpp | 110 +++- 3rdparty/pybind11/tests/test_pickling.py | 60 +- 3rdparty/pybind11/tests/test_pytypes.cpp | 671 ++++++++++++++++----- 3rdparty/pybind11/tests/test_pytypes.py | 517 +++++++++++++--- .../tests/test_sequences_and_iterators.cpp | 420 +++++++++---- .../pybind11/tests/test_sequences_and_iterators.py | 78 ++- 3rdparty/pybind11/tests/test_smart_ptr.cpp | 512 +++++++++------- 3rdparty/pybind11/tests/test_smart_ptr.py | 29 +- 3rdparty/pybind11/tests/test_stl.cpp | 403 ++++++++++--- 3rdparty/pybind11/tests/test_stl.py | 123 +++- 3rdparty/pybind11/tests/test_stl_binders.cpp | 111 ++-- 3rdparty/pybind11/tests/test_stl_binders.py | 48 +- .../pybind11/tests/test_tagbased_polymorphic.cpp | 117 ++-- .../pybind11/tests/test_tagbased_polymorphic.py | 1 - 3rdparty/pybind11/tests/test_thread.cpp | 66 ++ 3rdparty/pybind11/tests/test_thread.py | 42 ++ 3rdparty/pybind11/tests/test_union.py | 1 - 3rdparty/pybind11/tests/test_virtual_functions.cpp | 341 +++++++---- 3rdparty/pybind11/tests/test_virtual_functions.py | 78 ++- 3rdparty/pybind11/tests/valgrind-numpy-scipy.supp | 140 +++++ 3rdparty/pybind11/tests/valgrind-python.supp | 117 ++++ 113 files changed, 8912 insertions(+), 3529 deletions(-) create mode 100644 3rdparty/pybind11/tests/cross_module_interleaved_error_already_set.cpp create mode 100644 3rdparty/pybind11/tests/test_const_name.cpp create mode 100644 3rdparty/pybind11/tests/test_const_name.py create mode 100644 3rdparty/pybind11/tests/test_custom_type_setup.cpp create mode 100644 3rdparty/pybind11/tests/test_custom_type_setup.py create mode 100644 3rdparty/pybind11/tests/test_embed/test_trampoline.py create mode 100644 3rdparty/pybind11/tests/test_exceptions.h create mode 100644 3rdparty/pybind11/tests/test_thread.cpp create mode 100644 3rdparty/pybind11/tests/test_thread.py create mode 100644 3rdparty/pybind11/tests/valgrind-numpy-scipy.supp create mode 100644 3rdparty/pybind11/tests/valgrind-python.supp (limited to '3rdparty/pybind11/tests') diff --git a/3rdparty/pybind11/tests/CMakeLists.txt b/3rdparty/pybind11/tests/CMakeLists.txt index dae8b5ad..7296cd1b 100644 --- a/3rdparty/pybind11/tests/CMakeLists.txt +++ b/3rdparty/pybind11/tests/CMakeLists.txt @@ -10,27 +10,34 @@ cmake_minimum_required(VERSION 3.4) # The `cmake_minimum_required(VERSION 3.4...3.18)` syntax does not work with # some versions of VS that have a patched CMake 3.11. This forces us to emulate # the behavior using the following workaround: -if(${CMAKE_VERSION} VERSION_LESS 3.18) +if(${CMAKE_VERSION} VERSION_LESS 3.21) cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}) else() - cmake_policy(VERSION 3.18) + cmake_policy(VERSION 3.21) endif() # Only needed for CMake < 3.5 support include(CMakeParseArguments) -# Filter out items; print an optional message if any items filtered +# Filter out items; print an optional message if any items filtered. This ignores extensions. # # Usage: # pybind11_filter_tests(LISTNAME file1.cpp file2.cpp ... MESSAGE "") # -macro(PYBIND11_FILTER_TESTS LISTNAME) +macro(pybind11_filter_tests LISTNAME) cmake_parse_arguments(ARG "" "MESSAGE" "" ${ARGN}) set(PYBIND11_FILTER_TESTS_FOUND OFF) + # Make a list of the test without any extensions, for easier filtering. + set(_TMP_ACTUAL_LIST "${${LISTNAME}};") # enforce ';' at the end to allow matching last item. + string(REGEX REPLACE "\\.[^.;]*;" ";" LIST_WITHOUT_EXTENSIONS "${_TMP_ACTUAL_LIST}") foreach(filename IN LISTS ARG_UNPARSED_ARGUMENTS) - list(FIND ${LISTNAME} ${filename} _FILE_FOUND) + string(REGEX REPLACE "\\.[^.]*$" "" filename_no_ext ${filename}) + # Search in the list without extensions. + list(FIND LIST_WITHOUT_EXTENSIONS ${filename_no_ext} _FILE_FOUND) if(_FILE_FOUND GREATER -1) - list(REMOVE_AT ${LISTNAME} ${_FILE_FOUND}) + list(REMOVE_AT ${LISTNAME} ${_FILE_FOUND}) # And remove from the list with extensions. + list(REMOVE_AT LIST_WITHOUT_EXTENSIONS ${_FILE_FOUND} + )# And our search list, to ensure it is in sync. set(PYBIND11_FILTER_TESTS_FOUND ON) endif() endforeach() @@ -39,6 +46,26 @@ macro(PYBIND11_FILTER_TESTS LISTNAME) endif() endmacro() +macro(possibly_uninitialized) + foreach(VARNAME ${ARGN}) + if(NOT DEFINED "${VARNAME}") + set("${VARNAME}" "") + endif() + endforeach() +endmacro() + +# Function to add additional targets if any of the provided tests are found. +# Needles; Specifies the test names to look for. +# Additions; Specifies the additional test targets to add when any of the needles are found. +macro(tests_extra_targets needles additions) + # Add the index for this relation to the index extra targets map. + list(LENGTH PYBIND11_TEST_EXTRA_TARGETS PYBIND11_TEST_EXTRA_TARGETS_LEN) + list(APPEND PYBIND11_TEST_EXTRA_TARGETS ${PYBIND11_TEST_EXTRA_TARGETS_LEN}) + # Add the test names to look for, and the associated test target additions. + set(PYBIND11_TEST_EXTRA_TARGETS_NEEDLES_${PYBIND11_TEST_EXTRA_TARGETS_LEN} ${needles}) + set(PYBIND11_TEST_EXTRA_TARGETS_ADDITION_${PYBIND11_TEST_EXTRA_TARGETS_LEN} ${additions}) +endmacro() + # New Python support if(DEFINED Python_EXECUTABLE) set(PYTHON_EXECUTABLE "${Python_EXECUTABLE}") @@ -67,7 +94,7 @@ if(CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR) find_package(pybind11 REQUIRED CONFIG) endif() -if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) +if(NOT CMAKE_BUILD_TYPE AND NOT DEFINED CMAKE_CONFIGURATION_TYPES) message(STATUS "Setting tests build type to MinSizeRel as none was specified") set(CMAKE_BUILD_TYPE MinSizeRel @@ -84,52 +111,67 @@ if(PYBIND11_CUDA_TESTS) set(CMAKE_CUDA_STANDARD_REQUIRED ON) endif() -# Full set of test files (you can override these; see below) +# Full set of test files (you can override these; see below, overrides ignore extension) +# Any test that has no extension is both .py and .cpp, so 'foo' will add 'foo.cpp' and 'foo.py'. +# Any test that has an extension is exclusively that and handled as such. set(PYBIND11_TEST_FILES - test_async.cpp - test_buffers.cpp - test_builtin_casters.cpp - test_call_policies.cpp - test_callbacks.cpp - test_chrono.cpp - test_class.cpp - test_constants_and_functions.cpp - test_copy_move.cpp - test_custom_type_casters.cpp - test_docstring_options.cpp - test_eigen.cpp - test_enum.cpp - test_eval.cpp - test_exceptions.cpp - test_factory_constructors.cpp - test_gil_scoped.cpp - test_iostream.cpp - test_kwargs_and_defaults.cpp - test_local_bindings.cpp - test_methods_and_attributes.cpp - test_modules.cpp - test_multiple_inheritance.cpp - test_numpy_array.cpp - test_numpy_dtypes.cpp - test_numpy_vectorize.cpp - test_opaque_types.cpp - test_operator_overloading.cpp - test_pickling.cpp - test_pytypes.cpp - test_sequences_and_iterators.cpp - test_smart_ptr.cpp - test_stl.cpp - test_stl_binders.cpp - test_tagbased_polymorphic.cpp - test_union.cpp - test_virtual_functions.cpp) + test_async + test_buffers + test_builtin_casters + test_call_policies + test_callbacks + test_chrono + test_class + test_const_name + test_constants_and_functions + test_copy_move + test_custom_type_casters + test_custom_type_setup + test_docstring_options + test_eigen + test_enum + test_eval + test_exceptions + test_factory_constructors + test_gil_scoped + test_iostream + test_kwargs_and_defaults + test_local_bindings + test_methods_and_attributes + test_modules + test_multiple_inheritance + test_numpy_array + test_numpy_dtypes + test_numpy_vectorize + test_opaque_types + test_operator_overloading + test_pickling + test_pytypes + test_sequences_and_iterators + test_smart_ptr + test_stl + test_stl_binders + test_tagbased_polymorphic + test_thread + test_union + test_virtual_functions) # Invoking cmake with something like: # cmake -DPYBIND11_TEST_OVERRIDE="test_callbacks.cpp;test_pickling.cpp" .. # lets you override the tests that get compiled and run. You can restore to all tests with: # cmake -DPYBIND11_TEST_OVERRIDE= .. if(PYBIND11_TEST_OVERRIDE) - set(PYBIND11_TEST_FILES ${PYBIND11_TEST_OVERRIDE}) + # Instead of doing a direct override here, we iterate over the overrides without extension and + # match them against entries from the PYBIND11_TEST_FILES, anything that not matches goes into the filter list. + string(REGEX REPLACE "\\.[^.;]*;" ";" TEST_OVERRIDE_NO_EXT "${PYBIND11_TEST_OVERRIDE};") + string(REGEX REPLACE "\\.[^.;]*;" ";" TEST_FILES_NO_EXT "${PYBIND11_TEST_FILES};") + # This allows the override to be done with extensions, preserving backwards compatibility. + foreach(test_name ${TEST_FILES_NO_EXT}) + if(NOT ${test_name} IN_LIST TEST_OVERRIDE_NO_EXT + )# If not in the whitelist, add to be filtered out. + list(APPEND PYBIND11_TEST_FILTER ${test_name}) + endif() + endforeach() endif() # You can also filter tests: @@ -137,11 +179,6 @@ if(PYBIND11_TEST_FILTER) pybind11_filter_tests(PYBIND11_TEST_FILES ${PYBIND11_TEST_FILTER}) endif() -if(PYTHON_VERSION VERSION_LESS 3.5) - pybind11_filter_tests(PYBIND11_TEST_FILES test_async.cpp MESSAGE - "Skipping test_async on Python 2") -endif() - # Skip tests for CUDA check: # /pybind11/tests/test_constants_and_functions.cpp(125): # error: incompatible exception specifications @@ -151,15 +188,47 @@ if(PYBIND11_CUDA_TESTS) "Skipping test_constants_and_functions due to incompatible exception specifications") endif() -string(REPLACE ".cpp" ".py" PYBIND11_PYTEST_FILES "${PYBIND11_TEST_FILES}") +# Now that the test filtering is complete, we need to split the list into the test for PYTEST +# and the list for the cpp targets. +set(PYBIND11_CPPTEST_FILES "") +set(PYBIND11_PYTEST_FILES "") + +foreach(test_name ${PYBIND11_TEST_FILES}) + if(test_name MATCHES "\\.py$") # Ends in .py, purely python test. + list(APPEND PYBIND11_PYTEST_FILES ${test_name}) + elseif(test_name MATCHES "\\.cpp$") # Ends in .cpp, purely cpp test. + list(APPEND PYBIND11_CPPTEST_FILES ${test_name}) + elseif(NOT test_name MATCHES "\\.") # No extension specified, assume both, add extension. + list(APPEND PYBIND11_PYTEST_FILES ${test_name}.py) + list(APPEND PYBIND11_CPPTEST_FILES ${test_name}.cpp) + else() + message(WARNING "Unhanded test extension in test: ${test_name}") + endif() +endforeach() +set(PYBIND11_TEST_FILES ${PYBIND11_CPPTEST_FILES}) +list(SORT PYBIND11_PYTEST_FILES) # Contains the set of test files that require pybind11_cross_module_tests to be # built; if none of these are built (i.e. because TEST_OVERRIDE is used and # doesn't include them) the second module doesn't get built. -set(PYBIND11_CROSS_MODULE_TESTS test_exceptions.py test_local_bindings.py test_stl.py - test_stl_binders.py) +tests_extra_targets("test_exceptions.py;test_local_bindings.py;test_stl.py;test_stl_binders.py" + "pybind11_cross_module_tests") + +# And add additional targets for other tests. +tests_extra_targets("test_exceptions.py" "cross_module_interleaved_error_already_set") +tests_extra_targets("test_gil_scoped.py" "cross_module_gil_utils") -set(PYBIND11_CROSS_MODULE_GIL_TESTS test_gil_scoped.py) +set(PYBIND11_EIGEN_REPO + "https://gitlab.com/libeigen/eigen.git" + CACHE STRING "Eigen repository to use for tests") +# Always use a hash for reconfigure speed and security reasons +# Include the version number for pretty printing (keep in sync) +set(PYBIND11_EIGEN_VERSION_AND_HASH + "3.4.0;929bc0e191d0927b1735b9a1ddc0e8b77e3a25ec" + CACHE STRING "Eigen version to use for tests, format: VERSION;HASH") + +list(GET PYBIND11_EIGEN_VERSION_AND_HASH 0 PYBIND11_EIGEN_VERSION_STRING) +list(GET PYBIND11_EIGEN_VERSION_AND_HASH 1 PYBIND11_EIGEN_VERSION_HASH) # Check if Eigen is available; if not, remove from PYBIND11_TEST_FILES (but # keep it in PYBIND11_PYTEST_FILES, so that we get the "eigen is not installed" @@ -174,22 +243,26 @@ if(PYBIND11_TEST_FILES_EIGEN_I GREATER -1) message(FATAL_ERROR "CMake 3.11+ required when using DOWNLOAD_EIGEN") endif() - set(EIGEN3_VERSION_STRING "3.3.8") - include(FetchContent) FetchContent_Declare( eigen - GIT_REPOSITORY https://gitlab.com/libeigen/eigen.git - GIT_TAG ${EIGEN3_VERSION_STRING}) + GIT_REPOSITORY "${PYBIND11_EIGEN_REPO}" + GIT_TAG "${PYBIND11_EIGEN_VERSION_HASH}") FetchContent_GetProperties(eigen) if(NOT eigen_POPULATED) - message(STATUS "Downloading Eigen") + message( + STATUS + "Downloading Eigen ${PYBIND11_EIGEN_VERSION_STRING} (${PYBIND11_EIGEN_VERSION_HASH}) from ${PYBIND11_EIGEN_REPO}" + ) FetchContent_Populate(eigen) endif() set(EIGEN3_INCLUDE_DIR ${eigen_SOURCE_DIR}) set(EIGEN3_FOUND TRUE) + # When getting locally, the version is not visible from a superprojet, + # so just force it. + set(EIGEN3_VERSION "${PYBIND11_EIGEN_VERSION_STRING}") else() find_package(Eigen3 3.2.7 QUIET CONFIG) @@ -217,7 +290,8 @@ if(PYBIND11_TEST_FILES_EIGEN_I GREATER -1) message(STATUS "Building tests with Eigen v${EIGEN3_VERSION}") else() list(REMOVE_AT PYBIND11_TEST_FILES ${PYBIND11_TEST_FILES_EIGEN_I}) - message(STATUS "Building tests WITHOUT Eigen, use -DDOWNLOAD_EIGEN on CMake 3.11+ to download") + message( + STATUS "Building tests WITHOUT Eigen, use -DDOWNLOAD_EIGEN=ON on CMake 3.11+ to download") endif() endif() @@ -238,10 +312,47 @@ if(Boost_FOUND) endif() endif() +# Check if we need to add -lstdc++fs or -lc++fs or nothing +if(DEFINED CMAKE_CXX_STANDARD AND CMAKE_CXX_STANDARD LESS 17) + set(STD_FS_NO_LIB_NEEDED TRUE) +elseif(MSVC) + set(STD_FS_NO_LIB_NEEDED TRUE) +else() + file( + WRITE ${CMAKE_CURRENT_BINARY_DIR}/main.cpp + "#include \nint main(int argc, char ** argv) {\n std::filesystem::path p(argv[0]);\n return p.string().length();\n}" + ) + try_compile( + STD_FS_NO_LIB_NEEDED ${CMAKE_CURRENT_BINARY_DIR} + SOURCES ${CMAKE_CURRENT_BINARY_DIR}/main.cpp + COMPILE_DEFINITIONS -std=c++17) + try_compile( + STD_FS_NEEDS_STDCXXFS ${CMAKE_CURRENT_BINARY_DIR} + SOURCES ${CMAKE_CURRENT_BINARY_DIR}/main.cpp + COMPILE_DEFINITIONS -std=c++17 + LINK_LIBRARIES stdc++fs) + try_compile( + STD_FS_NEEDS_CXXFS ${CMAKE_CURRENT_BINARY_DIR} + SOURCES ${CMAKE_CURRENT_BINARY_DIR}/main.cpp + COMPILE_DEFINITIONS -std=c++17 + LINK_LIBRARIES c++fs) +endif() + +if(${STD_FS_NEEDS_STDCXXFS}) + set(STD_FS_LIB stdc++fs) +elseif(${STD_FS_NEEDS_CXXFS}) + set(STD_FS_LIB c++fs) +elseif(${STD_FS_NO_LIB_NEEDED}) + set(STD_FS_LIB "") +else() + message(WARNING "Unknown C++17 compiler - not passing -lstdc++fs") + set(STD_FS_LIB "") +endif() + # Compile with compiler warnings turned on function(pybind11_enable_warnings target_name) if(MSVC) - target_compile_options(${target_name} PRIVATE /W4) + target_compile_options(${target_name} PRIVATE /W4 /wd4189) elseif(CMAKE_CXX_COMPILER_ID MATCHES "(GNU|Intel|Clang)" AND NOT PYBIND11_CUDA_TESTS) target_compile_options( ${target_name} @@ -259,40 +370,35 @@ function(pybind11_enable_warnings target_name) target_compile_options(${target_name} PRIVATE /WX) elseif(PYBIND11_CUDA_TESTS) target_compile_options(${target_name} PRIVATE "SHELL:-Werror all-warnings") - elseif(CMAKE_CXX_COMPILER_ID MATCHES "(GNU|Intel|Clang)") + elseif(CMAKE_CXX_COMPILER_ID MATCHES "(GNU|Clang|IntelLLVM)") target_compile_options(${target_name} PRIVATE -Werror) - endif() - endif() - - # Needs to be readded since the ordering requires these to be after the ones above - if(CMAKE_CXX_STANDARD - AND CMAKE_CXX_COMPILER_ID MATCHES "Clang" - AND PYTHON_VERSION VERSION_LESS 3.0) - if(CMAKE_CXX_STANDARD LESS 17) - target_compile_options(${target_name} PUBLIC -Wno-deprecated-register) - else() - target_compile_options(${target_name} PUBLIC -Wno-register) + elseif(CMAKE_CXX_COMPILER_ID STREQUAL "Intel") + if(CMAKE_CXX_STANDARD EQUAL 17) # See PR #3570 + target_compile_options(${target_name} PRIVATE -Wno-conversion) + endif() + target_compile_options( + ${target_name} + PRIVATE + -Werror-all + # "Inlining inhibited by limit max-size", "Inlining inhibited by limit max-total-size" + -diag-disable 11074,11076) endif() endif() endfunction() set(test_targets pybind11_tests) -# Build pybind11_cross_module_tests if any test_whatever.py are being built that require it -foreach(t ${PYBIND11_CROSS_MODULE_TESTS}) - list(FIND PYBIND11_PYTEST_FILES ${t} i) - if(i GREATER -1) - list(APPEND test_targets pybind11_cross_module_tests) - break() - endif() -endforeach() - -foreach(t ${PYBIND11_CROSS_MODULE_GIL_TESTS}) - list(FIND PYBIND11_PYTEST_FILES ${t} i) - if(i GREATER -1) - list(APPEND test_targets cross_module_gil_utils) - break() - endif() +# Check if any tests need extra targets by iterating through the mappings registered. +foreach(i ${PYBIND11_TEST_EXTRA_TARGETS}) + foreach(needle ${PYBIND11_TEST_EXTRA_TARGETS_NEEDLES_${i}}) + if(needle IN_LIST PYBIND11_PYTEST_FILES) + # Add all the additional targets to the test list. List join in newer cmake. + foreach(extra_target ${PYBIND11_TEST_EXTRA_TARGETS_ADDITION_${i}}) + list(APPEND test_targets ${extra_target}) + endforeach() + break() # Breaks out of the needle search, continues with the next mapping. + endif() + endforeach() endforeach() # Support CUDA testing by forcing the target file to compile with NVCC @@ -341,18 +447,31 @@ foreach(target ${test_targets}) target_compile_definitions(${target} PRIVATE -DPYBIND11_TEST_BOOST) endif() + target_link_libraries(${target} PRIVATE ${STD_FS_LIB}) + # Always write the output file directly into the 'tests' directory (even on MSVC) if(NOT CMAKE_LIBRARY_OUTPUT_DIRECTORY) set_target_properties(${target} PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}") - foreach(config ${CMAKE_CONFIGURATION_TYPES}) - string(TOUPPER ${config} config) - set_target_properties(${target} PROPERTIES LIBRARY_OUTPUT_DIRECTORY_${config} - "${CMAKE_CURRENT_BINARY_DIR}") - endforeach() + + if(DEFINED CMAKE_CONFIGURATION_TYPES) + foreach(config ${CMAKE_CONFIGURATION_TYPES}) + string(TOUPPER ${config} config) + set_target_properties(${target} PROPERTIES LIBRARY_OUTPUT_DIRECTORY_${config} + "${CMAKE_CURRENT_BINARY_DIR}") + endforeach() + endif() endif() endforeach() +# Provide nice organisation in IDEs +if(NOT CMAKE_VERSION VERSION_LESS 3.8) + source_group( + TREE "${CMAKE_CURRENT_SOURCE_DIR}/../include" + PREFIX "Header Files" + FILES ${PYBIND11_HEADERS}) +endif() + # Make sure pytest is found or produce a warning pybind11_find_import(pytest VERSION 3.1) @@ -370,12 +489,17 @@ endif() string(REPLACE "test_" "${CMAKE_CURRENT_SOURCE_DIR}/test_" PYBIND11_ABS_PYTEST_FILES "${PYBIND11_PYTEST_FILES}") +set(PYBIND11_TEST_PREFIX_COMMAND + "" + CACHE STRING "Put this before pytest, use for checkers and such") + # A single command to compile and run the tests add_custom_target( pytest - COMMAND ${PYTHON_EXECUTABLE} -m pytest ${PYBIND11_ABS_PYTEST_FILES} + COMMAND ${PYBIND11_TEST_PREFIX_COMMAND} ${PYTHON_EXECUTABLE} -m pytest + ${PYBIND11_ABS_PYTEST_FILES} DEPENDS ${test_targets} - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}" USES_TERMINAL) if(PYBIND11_TEST_OVERRIDE) @@ -386,6 +510,27 @@ if(PYBIND11_TEST_OVERRIDE) "Note: not all tests run: -DPYBIND11_TEST_OVERRIDE is in effect") endif() +# cmake-format: off +add_custom_target( + memcheck + COMMAND + PYTHONMALLOC=malloc + valgrind + --leak-check=full + --show-leak-kinds=definite,indirect + --errors-for-leak-kinds=definite,indirect + --error-exitcode=1 + --read-var-info=yes + --track-origins=yes + --suppressions="${CMAKE_CURRENT_SOURCE_DIR}/valgrind-python.supp" + --suppressions="${CMAKE_CURRENT_SOURCE_DIR}/valgrind-numpy-scipy.supp" + --gen-suppressions=all + ${PYTHON_EXECUTABLE} -m pytest ${PYBIND11_ABS_PYTEST_FILES} + DEPENDS ${test_targets} + WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}" + USES_TERMINAL) +# cmake-format: on + # Add a check target to run all the tests, starting with pytest (we add dependencies to this below) add_custom_target(check DEPENDS pytest) diff --git a/3rdparty/pybind11/tests/conftest.py b/3rdparty/pybind11/tests/conftest.py index 362eb806..02ce263a 100644 --- a/3rdparty/pybind11/tests/conftest.py +++ b/3rdparty/pybind11/tests/conftest.py @@ -1,8 +1,7 @@ -# -*- coding: utf-8 -*- """pytest configuration Extends output capture as needed by pybind11: ignore constructors, optional unordered lines. -Adds docstring and exceptions message sanitizers: ignore Python 2 vs 3 differences. +Adds docstring and exceptions message sanitizers. """ import contextlib @@ -13,19 +12,14 @@ import textwrap import pytest -import env - # Early diagnostic for failed imports -import pybind11_tests # noqa: F401 +import pybind11_tests -_unicode_marker = re.compile(r"u(\'[^\']*\')") _long_marker = re.compile(r"([0-9])L") _hexadecimal = re.compile(r"0x[0-9a-fA-F]+") # Avoid collecting Python3 only files collect_ignore = [] -if env.PY2: - collect_ignore.append("test_async.py") def _strip_and_dedent(s): @@ -45,7 +39,7 @@ def _make_explanation(a, b): ] -class Output(object): +class Output: """Basic output post-processing and comparison""" def __init__(self, string): @@ -83,7 +77,7 @@ class Unordered(Output): return False -class Capture(object): +class Capture: def __init__(self, capfd): self.capfd = capfd self.out = "" @@ -126,7 +120,7 @@ def capture(capsys): return Capture(capsys) -class SanitizedString(object): +class SanitizedString: def __init__(self, sanitizer): self.sanitizer = sanitizer self.string = "" @@ -149,9 +143,7 @@ class SanitizedString(object): def _sanitize_general(s): s = s.strip() s = s.replace("pybind11_tests.", "m.") - s = s.replace("unicode", "str") s = _long_marker.sub(r"\1", s) - s = _unicode_marker.sub(r"\1", s) return s @@ -206,3 +198,16 @@ def gc_collect(): def pytest_configure(): pytest.suppress = suppress pytest.gc_collect = gc_collect + + +def pytest_report_header(config): + del config # Unused. + assert ( + pybind11_tests.compiler_info is not None + ), "Please update pybind11_tests.cpp if this assert fails." + return ( + "C++ Info:" + f" {pybind11_tests.compiler_info}" + f" {pybind11_tests.cpp_std}" + f" {pybind11_tests.PYBIND11_INTERNALS_ID}" + ) diff --git a/3rdparty/pybind11/tests/constructor_stats.h b/3rdparty/pybind11/tests/constructor_stats.h index 805968a0..937f6c23 100644 --- a/3rdparty/pybind11/tests/constructor_stats.h +++ b/3rdparty/pybind11/tests/constructor_stats.h @@ -56,7 +56,8 @@ from the ConstructorStats instance `.values()` method. In some cases, when you need to track instances of a C++ class not registered with pybind11, you need to add a function returning the ConstructorStats for the C++ class; this can be done with: - m.def("get_special_cstats", &ConstructorStats::get, py::return_value_policy::reference) + m.def("get_special_cstats", &ConstructorStats::get, +py::return_value_policy::reference) Finally, you can suppress the output messages, but keep the constructor tracking (for inspection/testing in python) by using the functions with `print_` replaced with `track_` (e.g. @@ -65,15 +66,18 @@ inspection/testing in python) by using the functions with `print_` replaced with */ #include "pybind11_tests.h" -#include + #include -#include #include +#include +#include class ConstructorStats { protected: - std::unordered_map _instances; // Need a map rather than set because members can shared address with parents - std::list _values; // Used to track values (e.g. of value constructors) + std::unordered_map _instances; // Need a map rather than set because members can + // shared address with parents + std::list _values; // Used to track values + // (e.g. of value constructors) public: int default_constructions = 0; int copy_constructions = 0; @@ -96,26 +100,26 @@ public: default_constructions++; } - void created(void *inst) { - ++_instances[inst]; - } + void created(void *inst) { ++_instances[inst]; } void destroyed(void *inst) { - if (--_instances[inst] < 0) + if (--_instances[inst] < 0) { throw std::runtime_error("cstats.destroyed() called with unknown " "instance; potential double-destruction " "or a missing cstats.created()"); + } } static void gc() { // Force garbage collection to ensure any pending destructors are invoked: #if defined(PYPY_VERSION) PyObject *globals = PyEval_GetGlobals(); - PyObject *result = PyRun_String( - "import gc\n" - "for i in range(2):" - " gc.collect()\n", - Py_file_input, globals, globals); + PyObject *result = PyRun_String("import gc\n" + "for i in range(2):\n" + " gc.collect()\n", + Py_file_input, + globals, + globals); if (result == nullptr) throw py::error_already_set(); Py_DECREF(result); @@ -127,15 +131,18 @@ public: int alive() { gc(); int total = 0; - for (const auto &p : _instances) - if (p.second > 0) + for (const auto &p : _instances) { + if (p.second > 0) { total += p.second; + } + } return total; } void value() {} // Recursion terminator // Takes one or more values, converts them to strings, then stores them. - template void value(const T &v, Tmore &&...args) { + template + void value(const T &v, Tmore &&...args) { std::ostringstream oss; oss << v; _values.push_back(oss.str()); @@ -145,19 +152,22 @@ public: // Move out stored values py::list values() { py::list l; - for (const auto &v : _values) l.append(py::cast(v)); + for (const auto &v : _values) { + l.append(py::cast(v)); + } _values.clear(); return l; } // Gets constructor stats from a C++ type index - static ConstructorStats& get(std::type_index type) { + static ConstructorStats &get(std::type_index type) { static std::unordered_map all_cstats; return all_cstats[type]; } // Gets constructor stats from a C++ type - template static ConstructorStats& get() { + template + static ConstructorStats &get() { #if defined(PYPY_VERSION) gc(); #endif @@ -165,11 +175,12 @@ public: } // Gets constructor stats from a Python class - static ConstructorStats& get(py::object class_) { + static ConstructorStats &get(py::object class_) { auto &internals = py::detail::get_internals(); const std::type_index *t1 = nullptr, *t2 = nullptr; try { - auto *type_info = internals.registered_types_py.at((PyTypeObject *) class_.ptr()).at(0); + auto *type_info + = internals.registered_types_py.at((PyTypeObject *) class_.ptr()).at(0); for (auto &p : internals.registered_types_cpp) { if (p.second == type_info) { if (t1) { @@ -179,17 +190,23 @@ public: t1 = &p.first; } } + } catch (const std::out_of_range &) { + } + if (!t1) { + throw std::runtime_error("Unknown class passed to ConstructorStats::get()"); } - catch (const std::out_of_range&) {} - if (!t1) throw std::runtime_error("Unknown class passed to ConstructorStats::get()"); auto &cs1 = get(*t1); - // If we have both a t1 and t2 match, one is probably the trampoline class; return whichever - // has more constructions (typically one or the other will be 0) + // If we have both a t1 and t2 match, one is probably the trampoline class; return + // whichever has more constructions (typically one or the other will be 0) if (t2) { auto &cs2 = get(*t2); - int cs1_total = cs1.default_constructions + cs1.copy_constructions + cs1.move_constructions + (int) cs1._values.size(); - int cs2_total = cs2.default_constructions + cs2.copy_constructions + cs2.move_constructions + (int) cs2._values.size(); - if (cs2_total > cs1_total) return cs2; + int cs1_total = cs1.default_constructions + cs1.copy_constructions + + cs1.move_constructions + (int) cs1._values.size(); + int cs2_total = cs2.default_constructions + cs2.copy_constructions + + cs2.move_constructions + (int) cs2._values.size(); + if (cs2_total > cs1_total) { + return cs2; + } } return cs1; } @@ -198,78 +215,108 @@ public: // To track construction/destruction, you need to call these methods from the various // constructors/operators. The ones that take extra values record the given values in the // constructor stats values for later inspection. -template void track_copy_created(T *inst) { ConstructorStats::get().copy_created(inst); } -template void track_move_created(T *inst) { ConstructorStats::get().move_created(inst); } -template void track_copy_assigned(T *, Values &&...values) { +template +void track_copy_created(T *inst) { + ConstructorStats::get().copy_created(inst); +} +template +void track_move_created(T *inst) { + ConstructorStats::get().move_created(inst); +} +template +void track_copy_assigned(T *, Values &&...values) { auto &cst = ConstructorStats::get(); cst.copy_assignments++; cst.value(std::forward(values)...); } -template void track_move_assigned(T *, Values &&...values) { +template +void track_move_assigned(T *, Values &&...values) { auto &cst = ConstructorStats::get(); cst.move_assignments++; cst.value(std::forward(values)...); } -template void track_default_created(T *inst, Values &&...values) { +template +void track_default_created(T *inst, Values &&...values) { auto &cst = ConstructorStats::get(); cst.default_created(inst); cst.value(std::forward(values)...); } -template void track_created(T *inst, Values &&...values) { +template +void track_created(T *inst, Values &&...values) { auto &cst = ConstructorStats::get(); cst.created(inst); cst.value(std::forward(values)...); } -template void track_destroyed(T *inst) { +template +void track_destroyed(T *inst) { ConstructorStats::get().destroyed(inst); } -template void track_values(T *, Values &&...values) { +template +void track_values(T *, Values &&...values) { ConstructorStats::get().value(std::forward(values)...); } /// Don't cast pointers to Python, print them as strings inline const char *format_ptrs(const char *p) { return p; } template -py::str format_ptrs(T *p) { return "{:#x}"_s.format(reinterpret_cast(p)); } +py::str format_ptrs(T *p) { + return "{:#x}"_s.format(reinterpret_cast(p)); +} template -auto format_ptrs(T &&x) -> decltype(std::forward(x)) { return std::forward(x); } +auto format_ptrs(T &&x) -> decltype(std::forward(x)) { + return std::forward(x); +} template void print_constr_details(T *inst, const std::string &action, Output &&...output) { - py::print("###", py::type_id(), "@", format_ptrs(inst), action, + py::print("###", + py::type_id(), + "@", + format_ptrs(inst), + action, format_ptrs(std::forward(output))...); } // Verbose versions of the above: -template void print_copy_created(T *inst, Values &&...values) { // NB: this prints, but doesn't store, given values +template +void print_copy_created(T *inst, + Values &&...values) { // NB: this prints, but doesn't store, given values print_constr_details(inst, "created via copy constructor", values...); track_copy_created(inst); } -template void print_move_created(T *inst, Values &&...values) { // NB: this prints, but doesn't store, given values +template +void print_move_created(T *inst, + Values &&...values) { // NB: this prints, but doesn't store, given values print_constr_details(inst, "created via move constructor", values...); track_move_created(inst); } -template void print_copy_assigned(T *inst, Values &&...values) { +template +void print_copy_assigned(T *inst, Values &&...values) { print_constr_details(inst, "assigned via copy assignment", values...); track_copy_assigned(inst, values...); } -template void print_move_assigned(T *inst, Values &&...values) { +template +void print_move_assigned(T *inst, Values &&...values) { print_constr_details(inst, "assigned via move assignment", values...); track_move_assigned(inst, values...); } -template void print_default_created(T *inst, Values &&...values) { +template +void print_default_created(T *inst, Values &&...values) { print_constr_details(inst, "created via default constructor", values...); track_default_created(inst, values...); } -template void print_created(T *inst, Values &&...values) { +template +void print_created(T *inst, Values &&...values) { print_constr_details(inst, "created", values...); track_created(inst, values...); } -template void print_destroyed(T *inst, Values &&...values) { // Prints but doesn't store given values +template +void print_destroyed(T *inst, Values &&...values) { // Prints but doesn't store given values print_constr_details(inst, "destroyed", values...); track_destroyed(inst); } -template void print_values(T *inst, Values &&...values) { +template +void print_values(T *inst, Values &&...values) { print_constr_details(inst, ":", values...); track_values(inst, values...); } diff --git a/3rdparty/pybind11/tests/cross_module_gil_utils.cpp b/3rdparty/pybind11/tests/cross_module_gil_utils.cpp index 07db9f6e..1436c35d 100644 --- a/3rdparty/pybind11/tests/cross_module_gil_utils.cpp +++ b/3rdparty/pybind11/tests/cross_module_gil_utils.cpp @@ -7,6 +7,7 @@ BSD-style license that can be found in the LICENSE file. */ #include + #include // This file mimics a DSO that makes pybind11 calls but does not define a @@ -24,50 +25,21 @@ void gil_acquire() { py::gil_scoped_acquire gil; } constexpr char kModuleName[] = "cross_module_gil_utils"; -#if PY_MAJOR_VERSION >= 3 struct PyModuleDef moduledef = { - PyModuleDef_HEAD_INIT, - kModuleName, - NULL, - 0, - NULL, - NULL, - NULL, - NULL, - NULL -}; -#else -PyMethodDef module_methods[] = { - {NULL, NULL, 0, NULL} -}; -#endif + PyModuleDef_HEAD_INIT, kModuleName, nullptr, 0, nullptr, nullptr, nullptr, nullptr, nullptr}; -} // namespace +} // namespace -extern "C" PYBIND11_EXPORT -#if PY_MAJOR_VERSION >= 3 -PyObject* PyInit_cross_module_gil_utils() -#else -void initcross_module_gil_utils() -#endif -{ +extern "C" PYBIND11_EXPORT PyObject *PyInit_cross_module_gil_utils() { - PyObject* m = -#if PY_MAJOR_VERSION >= 3 - PyModule_Create(&moduledef); -#else - Py_InitModule(kModuleName, module_methods); -#endif + PyObject *m = PyModule_Create(&moduledef); - if (m != NULL) { - static_assert( - sizeof(&gil_acquire) == sizeof(void*), - "Function pointer must have the same size as void*"); - PyModule_AddObject(m, "gil_acquire_funcaddr", - PyLong_FromVoidPtr(reinterpret_cast(&gil_acquire))); + if (m != nullptr) { + static_assert(sizeof(&gil_acquire) == sizeof(void *), + "Function pointer must have the same size as void*"); + PyModule_AddObject( + m, "gil_acquire_funcaddr", PyLong_FromVoidPtr(reinterpret_cast(&gil_acquire))); } -#if PY_MAJOR_VERSION >= 3 return m; -#endif } diff --git a/3rdparty/pybind11/tests/cross_module_interleaved_error_already_set.cpp b/3rdparty/pybind11/tests/cross_module_interleaved_error_already_set.cpp new file mode 100644 index 00000000..fdd9939e --- /dev/null +++ b/3rdparty/pybind11/tests/cross_module_interleaved_error_already_set.cpp @@ -0,0 +1,51 @@ +/* + Copyright (c) 2022 Google LLC + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#include + +// This file mimics a DSO that makes pybind11 calls but does not define a PYBIND11_MODULE, +// so that the first call of cross_module_error_already_set() triggers the first call of +// pybind11::detail::get_internals(). + +namespace { + +namespace py = pybind11; + +void interleaved_error_already_set() { + PyErr_SetString(PyExc_RuntimeError, "1st error."); + try { + throw py::error_already_set(); + } catch (const py::error_already_set &) { + // The 2nd error could be conditional in a real application. + PyErr_SetString(PyExc_RuntimeError, "2nd error."); + } // Here the 1st error is destroyed before the 2nd error is fetched. + // The error_already_set dtor triggers a pybind11::detail::get_internals() + // call via pybind11::gil_scoped_acquire. + if (PyErr_Occurred()) { + throw py::error_already_set(); + } +} + +constexpr char kModuleName[] = "cross_module_interleaved_error_already_set"; + +struct PyModuleDef moduledef = { + PyModuleDef_HEAD_INIT, kModuleName, nullptr, 0, nullptr, nullptr, nullptr, nullptr, nullptr}; + +} // namespace + +extern "C" PYBIND11_EXPORT PyObject *PyInit_cross_module_interleaved_error_already_set() { + PyObject *m = PyModule_Create(&moduledef); + if (m != nullptr) { + static_assert(sizeof(&interleaved_error_already_set) == sizeof(void *), + "Function pointer must have the same size as void *"); + PyModule_AddObject( + m, + "funcaddr", + PyLong_FromVoidPtr(reinterpret_cast(&interleaved_error_already_set))); + } + return m; +} diff --git a/3rdparty/pybind11/tests/env.py b/3rdparty/pybind11/tests/env.py index 5cded441..0345df65 100644 --- a/3rdparty/pybind11/tests/env.py +++ b/3rdparty/pybind11/tests/env.py @@ -1,7 +1,8 @@ -# -*- coding: utf-8 -*- import platform import sys +import pytest + LINUX = sys.platform.startswith("linux") MACOS = sys.platform.startswith("darwin") WIN = sys.platform.startswith("win32") or sys.platform.startswith("cygwin") @@ -9,6 +10,19 @@ WIN = sys.platform.startswith("win32") or sys.platform.startswith("cygwin") CPYTHON = platform.python_implementation() == "CPython" PYPY = platform.python_implementation() == "PyPy" -PY2 = sys.version_info.major == 2 -PY = sys.version_info +def deprecated_call(): + """ + pytest.deprecated_call() seems broken in pytest<3.9.x; concretely, it + doesn't work on CPython 3.8.0 with pytest==3.3.2 on Ubuntu 18.04 (#2922). + + This is a narrowed reimplementation of the following PR :( + https://github.com/pytest-dev/pytest/pull/4104 + """ + # TODO: Remove this when testing requires pytest>=3.9. + pieces = pytest.__version__.split(".") + pytest_major_minor = (int(pieces[0]), int(pieces[1])) + if pytest_major_minor < (3, 9): + return pytest.warns((DeprecationWarning, PendingDeprecationWarning)) + else: + return pytest.deprecated_call() diff --git a/3rdparty/pybind11/tests/extra_python_package/test_files.py b/3rdparty/pybind11/tests/extra_python_package/test_files.py index cbd4bff1..8e1ddd85 100644 --- a/3rdparty/pybind11/tests/extra_python_package/test_files.py +++ b/3rdparty/pybind11/tests/extra_python_package/test_files.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- import contextlib import os import string @@ -13,6 +12,16 @@ import zipfile DIR = os.path.abspath(os.path.dirname(__file__)) MAIN_DIR = os.path.dirname(os.path.dirname(DIR)) +PKGCONFIG = """\ +prefix=${{pcfiledir}}/../../ +includedir=${{prefix}}/include + +Name: pybind11 +Description: Seamless operability between C++11 and Python +Version: {VERSION} +Cflags: -I${{includedir}} +""" + main_headers = { "include/pybind11/attr.h", @@ -25,6 +34,7 @@ main_headers = { "include/pybind11/embed.h", "include/pybind11/eval.h", "include/pybind11/functional.h", + "include/pybind11/gil.h", "include/pybind11/iostream.h", "include/pybind11/numpy.h", "include/pybind11/operators.h", @@ -41,9 +51,14 @@ detail_headers = { "include/pybind11/detail/descr.h", "include/pybind11/detail/init.h", "include/pybind11/detail/internals.h", + "include/pybind11/detail/type_caster_base.h", "include/pybind11/detail/typeid.h", } +stl_headers = { + "include/pybind11/stl/filesystem.h", +} + cmake_files = { "share/cmake/pybind11/FindPythonLibsNew.cmake", "share/cmake/pybind11/pybind11Common.cmake", @@ -54,19 +69,21 @@ cmake_files = { "share/cmake/pybind11/pybind11Tools.cmake", } +pkgconfig_files = { + "share/pkgconfig/pybind11.pc", +} + py_files = { "__init__.py", "__main__.py", "_version.py", - "_version.pyi", "commands.py", "py.typed", "setup_helpers.py", - "setup_helpers.pyi", } -headers = main_headers | detail_headers -src_files = headers | cmake_files +headers = main_headers | detail_headers | stl_headers +src_files = headers | cmake_files | pkgconfig_files all_files = src_files | py_files @@ -75,9 +92,11 @@ sdist_files = { "pybind11/include", "pybind11/include/pybind11", "pybind11/include/pybind11/detail", + "pybind11/include/pybind11/stl", "pybind11/share", "pybind11/share/cmake", "pybind11/share/cmake/pybind11", + "pybind11/share/pkgconfig", "pyproject.toml", "setup.cfg", "setup.py", @@ -97,105 +116,106 @@ local_sdist_files = { } +def read_tz_file(tar: tarfile.TarFile, name: str) -> bytes: + start = tar.getnames()[0] + "/" + inner_file = tar.extractfile(tar.getmember(f"{start}{name}")) + assert inner_file + with contextlib.closing(inner_file) as f: + return f.read() + + +def normalize_line_endings(value: bytes) -> bytes: + return value.replace(os.linesep.encode("utf-8"), b"\n") + + def test_build_sdist(monkeypatch, tmpdir): monkeypatch.chdir(MAIN_DIR) - out = subprocess.check_output( - [ - sys.executable, - "setup.py", - "sdist", - "--formats=tar", - "--dist-dir", - str(tmpdir), - ] + subprocess.run( + [sys.executable, "-m", "build", "--sdist", f"--outdir={tmpdir}"], check=True ) - if hasattr(out, "decode"): - out = out.decode() - (sdist,) = tmpdir.visit("*.tar") + (sdist,) = tmpdir.visit("*.tar.gz") - with tarfile.open(str(sdist)) as tar: + with tarfile.open(str(sdist), "r:gz") as tar: start = tar.getnames()[0] + "/" version = start[9:-1] - simpler = set(n.split("/", 1)[-1] for n in tar.getnames()[1:]) + simpler = {n.split("/", 1)[-1] for n in tar.getnames()[1:]} - with contextlib.closing( - tar.extractfile(tar.getmember(start + "setup.py")) - ) as f: - setup_py = f.read() + setup_py = read_tz_file(tar, "setup.py") + pyproject_toml = read_tz_file(tar, "pyproject.toml") + pkgconfig = read_tz_file(tar, "pybind11/share/pkgconfig/pybind11.pc") + cmake_cfg = read_tz_file( + tar, "pybind11/share/cmake/pybind11/pybind11Config.cmake" + ) - with contextlib.closing( - tar.extractfile(tar.getmember(start + "pyproject.toml")) - ) as f: - pyproject_toml = f.read() + assert ( + 'set(pybind11_INCLUDE_DIR "${PACKAGE_PREFIX_DIR}/include")' + in cmake_cfg.decode("utf-8") + ) - files = set("pybind11/{}".format(n) for n in all_files) + files = {f"pybind11/{n}" for n in all_files} files |= sdist_files - files |= set("pybind11{}".format(n) for n in local_sdist_files) + files |= {f"pybind11{n}" for n in local_sdist_files} files.add("pybind11.egg-info/entry_points.txt") files.add("pybind11.egg-info/requires.txt") assert simpler == files with open(os.path.join(MAIN_DIR, "tools", "setup_main.py.in"), "rb") as f: contents = ( - string.Template(f.read().decode()) + string.Template(f.read().decode("utf-8")) .substitute(version=version, extra_cmd="") - .encode() + .encode("utf-8") ) - assert setup_py == contents + assert setup_py == contents with open(os.path.join(MAIN_DIR, "tools", "pyproject.toml"), "rb") as f: contents = f.read() - assert pyproject_toml == contents + assert pyproject_toml == contents + + simple_version = ".".join(version.split(".")[:3]) + pkgconfig_expected = PKGCONFIG.format(VERSION=simple_version).encode("utf-8") + assert normalize_line_endings(pkgconfig) == pkgconfig_expected def test_build_global_dist(monkeypatch, tmpdir): monkeypatch.chdir(MAIN_DIR) monkeypatch.setenv("PYBIND11_GLOBAL_SDIST", "1") - - out = subprocess.check_output( - [ - sys.executable, - "setup.py", - "sdist", - "--formats=tar", - "--dist-dir", - str(tmpdir), - ] + subprocess.run( + [sys.executable, "-m", "build", "--sdist", "--outdir", str(tmpdir)], check=True ) - if hasattr(out, "decode"): - out = out.decode() - (sdist,) = tmpdir.visit("*.tar") + (sdist,) = tmpdir.visit("*.tar.gz") - with tarfile.open(str(sdist)) as tar: + with tarfile.open(str(sdist), "r:gz") as tar: start = tar.getnames()[0] + "/" version = start[16:-1] - simpler = set(n.split("/", 1)[-1] for n in tar.getnames()[1:]) + simpler = {n.split("/", 1)[-1] for n in tar.getnames()[1:]} - with contextlib.closing( - tar.extractfile(tar.getmember(start + "setup.py")) - ) as f: - setup_py = f.read() + setup_py = read_tz_file(tar, "setup.py") + pyproject_toml = read_tz_file(tar, "pyproject.toml") + pkgconfig = read_tz_file(tar, "pybind11/share/pkgconfig/pybind11.pc") + cmake_cfg = read_tz_file( + tar, "pybind11/share/cmake/pybind11/pybind11Config.cmake" + ) - with contextlib.closing( - tar.extractfile(tar.getmember(start + "pyproject.toml")) - ) as f: - pyproject_toml = f.read() + assert ( + 'set(pybind11_INCLUDE_DIR "${PACKAGE_PREFIX_DIR}/include")' + in cmake_cfg.decode("utf-8") + ) - files = set("pybind11/{}".format(n) for n in all_files) + files = {f"pybind11/{n}" for n in all_files} files |= sdist_files - files |= set("pybind11_global{}".format(n) for n in local_sdist_files) + files |= {f"pybind11_global{n}" for n in local_sdist_files} assert simpler == files with open(os.path.join(MAIN_DIR, "tools", "setup_global.py.in"), "rb") as f: contents = ( string.Template(f.read().decode()) .substitute(version=version, extra_cmd="") - .encode() + .encode("utf-8") ) assert setup_py == contents @@ -203,17 +223,21 @@ def test_build_global_dist(monkeypatch, tmpdir): contents = f.read() assert pyproject_toml == contents + simple_version = ".".join(version.split(".")[:3]) + pkgconfig_expected = PKGCONFIG.format(VERSION=simple_version).encode("utf-8") + assert normalize_line_endings(pkgconfig) == pkgconfig_expected + def tests_build_wheel(monkeypatch, tmpdir): monkeypatch.chdir(MAIN_DIR) - subprocess.check_output( - [sys.executable, "-m", "pip", "wheel", ".", "-w", str(tmpdir)] + subprocess.run( + [sys.executable, "-m", "pip", "wheel", ".", "-w", str(tmpdir)], check=True ) (wheel,) = tmpdir.visit("*.whl") - files = set("pybind11/{}".format(n) for n in all_files) + files = {f"pybind11/{n}" for n in all_files} files |= { "dist-info/LICENSE", "dist-info/METADATA", @@ -226,10 +250,8 @@ def tests_build_wheel(monkeypatch, tmpdir): with zipfile.ZipFile(str(wheel)) as z: names = z.namelist() - trimmed = set(n for n in names if "dist-info" not in n) - trimmed |= set( - "dist-info/{}".format(n.split("/", 1)[-1]) for n in names if "dist-info" in n - ) + trimmed = {n for n in names if "dist-info" not in n} + trimmed |= {f"dist-info/{n.split('/', 1)[-1]}" for n in names if "dist-info" in n} assert files == trimmed @@ -237,14 +259,14 @@ def tests_build_global_wheel(monkeypatch, tmpdir): monkeypatch.chdir(MAIN_DIR) monkeypatch.setenv("PYBIND11_GLOBAL_SDIST", "1") - subprocess.check_output( - [sys.executable, "-m", "pip", "wheel", ".", "-w", str(tmpdir)] + subprocess.run( + [sys.executable, "-m", "pip", "wheel", ".", "-w", str(tmpdir)], check=True ) (wheel,) = tmpdir.visit("*.whl") - files = set("data/data/{}".format(n) for n in src_files) - files |= set("data/headers/{}".format(n[8:]) for n in headers) + files = {f"data/data/{n}" for n in src_files} + files |= {f"data/headers/{n[8:]}" for n in headers} files |= { "dist-info/LICENSE", "dist-info/METADATA", @@ -257,6 +279,6 @@ def tests_build_global_wheel(monkeypatch, tmpdir): names = z.namelist() beginning = names[0].split("/", 1)[0].rsplit(".", 1)[0] - trimmed = set(n[len(beginning) + 1 :] for n in names) + trimmed = {n[len(beginning) + 1 :] for n in names} assert files == trimmed diff --git a/3rdparty/pybind11/tests/extra_setuptools/test_setuphelper.py b/3rdparty/pybind11/tests/extra_setuptools/test_setuphelper.py index 0d8bd0e4..d5d3093b 100644 --- a/3rdparty/pybind11/tests/extra_setuptools/test_setuphelper.py +++ b/3rdparty/pybind11/tests/extra_setuptools/test_setuphelper.py @@ -1,13 +1,13 @@ -# -*- coding: utf-8 -*- import os -import sys import subprocess +import sys from textwrap import dedent import pytest DIR = os.path.abspath(os.path.dirname(__file__)) MAIN_DIR = os.path.dirname(os.path.dirname(DIR)) +WIN = sys.platform.startswith("win32") or sys.platform.startswith("cygwin") @pytest.mark.parametrize("parallel", [False, True]) @@ -18,7 +18,7 @@ def test_simple_setup_py(monkeypatch, tmpdir, parallel, std): (tmpdir / "setup.py").write_text( dedent( - u"""\ + f"""\ import sys sys.path.append({MAIN_DIR!r}) @@ -51,13 +51,13 @@ def test_simple_setup_py(monkeypatch, tmpdir, parallel, std): ext_modules=ext_modules, ) """ - ).format(MAIN_DIR=MAIN_DIR, std=std, parallel=parallel), + ), encoding="ascii", ) (tmpdir / "main.cpp").write_text( dedent( - u"""\ + """\ #include int f(int x) { @@ -71,13 +71,20 @@ def test_simple_setup_py(monkeypatch, tmpdir, parallel, std): encoding="ascii", ) - subprocess.check_call( + out = subprocess.check_output( [sys.executable, "setup.py", "build_ext", "--inplace"], - stdout=sys.stdout, - stderr=sys.stderr, ) + if not WIN: + assert b"-g0" in out + out = subprocess.check_output( + [sys.executable, "setup.py", "build_ext", "--inplace", "--force"], + env=dict(os.environ, CFLAGS="-g"), + ) + if not WIN: + assert b"-g0" not in out # Debug helper printout, normally hidden + print(out) for item in tmpdir.listdir(): print(item.basename) @@ -88,7 +95,7 @@ def test_simple_setup_py(monkeypatch, tmpdir, parallel, std): (tmpdir / "test.py").write_text( dedent( - u"""\ + """\ import simple_setup assert simple_setup.f(3) == 9 """ @@ -99,3 +106,46 @@ def test_simple_setup_py(monkeypatch, tmpdir, parallel, std): subprocess.check_call( [sys.executable, "test.py"], stdout=sys.stdout, stderr=sys.stderr ) + + +def test_intree_extensions(monkeypatch, tmpdir): + monkeypatch.syspath_prepend(MAIN_DIR) + + from pybind11.setup_helpers import intree_extensions + + monkeypatch.chdir(tmpdir) + root = tmpdir + root.ensure_dir() + subdir = root / "dir" + subdir.ensure_dir() + src = subdir / "ext.cpp" + src.ensure() + relpath = src.relto(tmpdir) + (ext,) = intree_extensions([relpath]) + assert ext.name == "ext" + subdir.ensure("__init__.py") + (ext,) = intree_extensions([relpath]) + assert ext.name == "dir.ext" + + +def test_intree_extensions_package_dir(monkeypatch, tmpdir): + monkeypatch.syspath_prepend(MAIN_DIR) + + from pybind11.setup_helpers import intree_extensions + + monkeypatch.chdir(tmpdir) + root = tmpdir / "src" + root.ensure_dir() + subdir = root / "dir" + subdir.ensure_dir() + src = subdir / "ext.cpp" + src.ensure() + (ext,) = intree_extensions([src.relto(tmpdir)], package_dir={"": "src"}) + assert ext.name == "dir.ext" + (ext,) = intree_extensions([src.relto(tmpdir)], package_dir={"foo": "src"}) + assert ext.name == "foo.dir.ext" + subdir.ensure("__init__.py") + (ext,) = intree_extensions([src.relto(tmpdir)], package_dir={"": "src"}) + assert ext.name == "dir.ext" + (ext,) = intree_extensions([src.relto(tmpdir)], package_dir={"foo": "src"}) + assert ext.name == "foo.dir.ext" diff --git a/3rdparty/pybind11/tests/local_bindings.h b/3rdparty/pybind11/tests/local_bindings.h index 22537b13..01d27853 100644 --- a/3rdparty/pybind11/tests/local_bindings.h +++ b/3rdparty/pybind11/tests/local_bindings.h @@ -1,10 +1,13 @@ #pragma once #include "pybind11_tests.h" +#include + /// Simple class used to test py::local: -template class LocalBase { +template +class LocalBase { public: - LocalBase(int i) : i(i) { } + explicit LocalBase(int i) : i(i) {} int i = -1; }; @@ -33,32 +36,57 @@ using NonLocalVec2 = std::vector; using NonLocalMap = std::unordered_map; using NonLocalMap2 = std::unordered_map; +// Exception that will be caught via the module local translator. +class LocalException : public std::exception { +public: + explicit LocalException(const char *m) : message{m} {} + const char *what() const noexcept override { return message.c_str(); } + +private: + std::string message = ""; +}; + +// Exception that will be registered with register_local_exception_translator +class LocalSimpleException : public std::exception { +public: + explicit LocalSimpleException(const char *m) : message{m} {} + const char *what() const noexcept override { return message.c_str(); } + +private: + std::string message = ""; +}; + PYBIND11_MAKE_OPAQUE(LocalVec); PYBIND11_MAKE_OPAQUE(LocalVec2); PYBIND11_MAKE_OPAQUE(LocalMap); PYBIND11_MAKE_OPAQUE(NonLocalVec); -//PYBIND11_MAKE_OPAQUE(NonLocalVec2); // same type as LocalVec2 +// PYBIND11_MAKE_OPAQUE(NonLocalVec2); // same type as LocalVec2 PYBIND11_MAKE_OPAQUE(NonLocalMap); PYBIND11_MAKE_OPAQUE(NonLocalMap2); - // Simple bindings (used with the above): template -py::class_ bind_local(Args && ...args) { - return py::class_(std::forward(args)...) - .def(py::init()) - .def("get", [](T &i) { return i.i + Adjust; }); +py::class_ bind_local(Args &&...args) { + return py::class_(std::forward(args)...).def(py::init()).def("get", [](T &i) { + return i.i + Adjust; + }); }; // Simulate a foreign library base class (to match the example in the docs): namespace pets { class Pet { public: - Pet(std::string name) : name_(name) {} + explicit Pet(std::string name) : name_(std::move(name)) {} std::string name_; - const std::string &name() { return name_; } + const std::string &name() const { return name_; } }; } // namespace pets -struct MixGL { int i; MixGL(int i) : i{i} {} }; -struct MixGL2 { int i; MixGL2(int i) : i{i} {} }; +struct MixGL { + int i; + explicit MixGL(int i) : i{i} {} +}; +struct MixGL2 { + int i; + explicit MixGL2(int i) : i{i} {} +}; diff --git a/3rdparty/pybind11/tests/object.h b/3rdparty/pybind11/tests/object.h index 9235f19c..564dd4a7 100644 --- a/3rdparty/pybind11/tests/object.h +++ b/3rdparty/pybind11/tests/object.h @@ -1,8 +1,9 @@ #if !defined(__OBJECT_H) -#define __OBJECT_H +# define __OBJECT_H -#include -#include "constructor_stats.h" +# include "constructor_stats.h" + +# include /// Reference counted object base class class Object { @@ -27,20 +28,23 @@ public: */ void decRef(bool dealloc = true) const { --m_refCount; - if (m_refCount == 0 && dealloc) + if (m_refCount == 0 && dealloc) { delete this; - else if (m_refCount < 0) + } else if (m_refCount < 0) { throw std::runtime_error("Internal error: reference count < 0!"); + } } virtual std::string toString() const = 0; + protected: /** \brief Virtual protected deconstructor. * (Will only be called by \ref ref) */ virtual ~Object() { print_destroyed(this); } + private: - mutable std::atomic m_refCount { 0 }; + mutable std::atomic m_refCount{0}; }; // Tag class used to track constructions of ref objects. When we track constructors, below, we @@ -59,80 +63,105 @@ class ref_tag {}; * * \ingroup libcore */ -template class ref { +template +class ref { public: /// Create a nullptr reference - ref() : m_ptr(nullptr) { print_default_created(this); track_default_created((ref_tag*) this); } + ref() : m_ptr(nullptr) { + print_default_created(this); + track_default_created((ref_tag *) this); + } /// Construct a reference from a pointer - ref(T *ptr) : m_ptr(ptr) { - if (m_ptr) ((Object *) m_ptr)->incRef(); - - print_created(this, "from pointer", m_ptr); track_created((ref_tag*) this, "from pointer"); + explicit ref(T *ptr) : m_ptr(ptr) { + if (m_ptr) { + ((Object *) m_ptr)->incRef(); + } + print_created(this, "from pointer", m_ptr); + track_created((ref_tag *) this, "from pointer"); } /// Copy constructor ref(const ref &r) : m_ptr(r.m_ptr) { - if (m_ptr) + if (m_ptr) { ((Object *) m_ptr)->incRef(); + } - print_copy_created(this, "with pointer", m_ptr); track_copy_created((ref_tag*) this); + print_copy_created(this, "with pointer", m_ptr); + track_copy_created((ref_tag *) this); } /// Move constructor - ref(ref &&r) : m_ptr(r.m_ptr) { + ref(ref &&r) noexcept : m_ptr(r.m_ptr) { r.m_ptr = nullptr; - print_move_created(this, "with pointer", m_ptr); track_move_created((ref_tag*) this); + print_move_created(this, "with pointer", m_ptr); + track_move_created((ref_tag *) this); } /// Destroy this reference ~ref() { - if (m_ptr) + if (m_ptr) { ((Object *) m_ptr)->decRef(); + } - print_destroyed(this); track_destroyed((ref_tag*) this); + print_destroyed(this); + track_destroyed((ref_tag *) this); } /// Move another reference into the current one - ref& operator=(ref&& r) { - print_move_assigned(this, "pointer", r.m_ptr); track_move_assigned((ref_tag*) this); + ref &operator=(ref &&r) noexcept { + print_move_assigned(this, "pointer", r.m_ptr); + track_move_assigned((ref_tag *) this); - if (*this == r) + if (*this == r) { return *this; - if (m_ptr) + } + if (m_ptr) { ((Object *) m_ptr)->decRef(); + } m_ptr = r.m_ptr; r.m_ptr = nullptr; return *this; } /// Overwrite this reference with another reference - ref& operator=(const ref& r) { - print_copy_assigned(this, "pointer", r.m_ptr); track_copy_assigned((ref_tag*) this); + ref &operator=(const ref &r) { + if (this == &r) { + return *this; + } + print_copy_assigned(this, "pointer", r.m_ptr); + track_copy_assigned((ref_tag *) this); - if (m_ptr == r.m_ptr) + if (m_ptr == r.m_ptr) { return *this; - if (m_ptr) + } + if (m_ptr) { ((Object *) m_ptr)->decRef(); + } m_ptr = r.m_ptr; - if (m_ptr) + if (m_ptr) { ((Object *) m_ptr)->incRef(); + } return *this; } /// Overwrite this reference with a pointer to another object - ref& operator=(T *ptr) { - print_values(this, "assigned pointer"); track_values((ref_tag*) this, "assigned pointer"); + ref &operator=(T *ptr) { + print_values(this, "assigned pointer"); + track_values((ref_tag *) this, "assigned pointer"); - if (m_ptr == ptr) + if (m_ptr == ptr) { return *this; - if (m_ptr) + } + if (m_ptr) { ((Object *) m_ptr)->decRef(); + } m_ptr = ptr; - if (m_ptr) + if (m_ptr) { ((Object *) m_ptr)->incRef(); + } return *this; } @@ -143,31 +172,32 @@ public: bool operator!=(const ref &r) const { return m_ptr != r.m_ptr; } /// Compare this reference with a pointer - bool operator==(const T* ptr) const { return m_ptr == ptr; } + bool operator==(const T *ptr) const { return m_ptr == ptr; } /// Compare this reference with a pointer - bool operator!=(const T* ptr) const { return m_ptr != ptr; } + bool operator!=(const T *ptr) const { return m_ptr != ptr; } /// Access the object referenced by this reference - T* operator->() { return m_ptr; } + T *operator->() { return m_ptr; } /// Access the object referenced by this reference - const T* operator->() const { return m_ptr; } + const T *operator->() const { return m_ptr; } /// Return a C++ reference to the referenced object - T& operator*() { return *m_ptr; } + T &operator*() { return *m_ptr; } /// Return a const C++ reference to the referenced object - const T& operator*() const { return *m_ptr; } + const T &operator*() const { return *m_ptr; } /// Return a pointer to the referenced object - operator T* () { return m_ptr; } + explicit operator T *() { return m_ptr; } /// Return a const pointer to the referenced object - T* get_ptr() { return m_ptr; } + T *get_ptr() { return m_ptr; } /// Return a pointer to the referenced object - const T* get_ptr() const { return m_ptr; } + const T *get_ptr() const { return m_ptr; } + private: T *m_ptr; }; diff --git a/3rdparty/pybind11/tests/pybind11_cross_module_tests.cpp b/3rdparty/pybind11/tests/pybind11_cross_module_tests.cpp index f705e310..9379f3f2 100644 --- a/3rdparty/pybind11/tests/pybind11_cross_module_tests.cpp +++ b/3rdparty/pybind11/tests/pybind11_cross_module_tests.cpp @@ -7,10 +7,14 @@ BSD-style license that can be found in the LICENSE file. */ -#include "pybind11_tests.h" -#include "local_bindings.h" #include + +#include "local_bindings.h" +#include "pybind11_tests.h" +#include "test_exceptions.h" + #include +#include PYBIND11_MODULE(pybind11_cross_module_tests, m) { m.doc() = "pybind11 cross-module test module"; @@ -25,17 +29,46 @@ PYBIND11_MODULE(pybind11_cross_module_tests, m) { bind_local(m, "ExternalType2", py::module_local()); // test_exceptions.py - m.def("raise_runtime_error", []() { PyErr_SetString(PyExc_RuntimeError, "My runtime error"); throw py::error_already_set(); }); - m.def("raise_value_error", []() { PyErr_SetString(PyExc_ValueError, "My value error"); throw py::error_already_set(); }); + py::register_local_exception(m, "LocalSimpleException"); + m.def("raise_runtime_error", []() { + PyErr_SetString(PyExc_RuntimeError, "My runtime error"); + throw py::error_already_set(); + }); + m.def("raise_value_error", []() { + PyErr_SetString(PyExc_ValueError, "My value error"); + throw py::error_already_set(); + }); m.def("throw_pybind_value_error", []() { throw py::value_error("pybind11 value error"); }); m.def("throw_pybind_type_error", []() { throw py::type_error("pybind11 type error"); }); m.def("throw_stop_iteration", []() { throw py::stop_iteration(); }); + m.def("throw_local_error", []() { throw LocalException("just local"); }); + m.def("throw_local_simple_error", []() { throw LocalSimpleException("external mod"); }); + py::register_exception_translator([](std::exception_ptr p) { + try { + if (p) { + std::rethrow_exception(p); + } + } catch (const shared_exception &e) { + PyErr_SetString(PyExc_KeyError, e.what()); + } + }); + + // translate the local exception into a key error but only in this module + py::register_local_exception_translator([](std::exception_ptr p) { + try { + if (p) { + std::rethrow_exception(p); + } + } catch (const LocalException &e) { + PyErr_SetString(PyExc_KeyError, e.what()); + } + }); // test_local_bindings.py // Local to both: - bind_local(m, "LocalType", py::module_local()) - .def("get2", [](LocalType &t) { return t.i + 2; }) - ; + bind_local(m, "LocalType", py::module_local()).def("get2", [](LocalType &t) { + return t.i + 2; + }); // Can only be called with our python type: m.def("local_value", [](LocalType &l) { return l.i; }); @@ -43,9 +76,7 @@ PYBIND11_MODULE(pybind11_cross_module_tests, m) { // test_nonlocal_failure // This registration will fail (global registration when LocalFail is already registered // globally in the main test module): - m.def("register_nonlocal", [m]() { - bind_local(m, "NonLocalType"); - }); + m.def("register_nonlocal", [m]() { bind_local(m, "NonLocalType"); }); // test_stl_bind_local // stl_bind.h binders defaults to py::module_local if the types are local or converting: @@ -55,27 +86,21 @@ PYBIND11_MODULE(pybind11_cross_module_tests, m) { // test_stl_bind_global // and global if the type (or one of the types, for the map) is global (so these will fail, // assuming pybind11_tests is already loaded): - m.def("register_nonlocal_vec", [m]() { - py::bind_vector(m, "NonLocalVec"); - }); - m.def("register_nonlocal_map", [m]() { - py::bind_map(m, "NonLocalMap"); - }); + m.def("register_nonlocal_vec", [m]() { py::bind_vector(m, "NonLocalVec"); }); + m.def("register_nonlocal_map", [m]() { py::bind_map(m, "NonLocalMap"); }); // The default can, however, be overridden to global using `py::module_local()` or // `py::module_local(false)`. // Explicitly made local: py::bind_vector(m, "NonLocalVec2", py::module_local()); // Explicitly made global (and so will fail to bind): - m.def("register_nonlocal_map2", [m]() { - py::bind_map(m, "NonLocalMap2", py::module_local(false)); - }); + m.def("register_nonlocal_map2", + [m]() { py::bind_map(m, "NonLocalMap2", py::module_local(false)); }); // test_mixed_local_global // We try this both with the global type registered first and vice versa (the order shouldn't // matter). - m.def("register_mixed_global_local", [m]() { - bind_local(m, "MixedGlobalLocal", py::module_local()); - }); + m.def("register_mixed_global_local", + [m]() { bind_local(m, "MixedGlobalLocal", py::module_local()); }); m.def("register_mixed_local_global", [m]() { bind_local(m, "MixedLocalGlobal", py::module_local(false)); }); @@ -83,25 +108,26 @@ PYBIND11_MODULE(pybind11_cross_module_tests, m) { m.def("get_mixed_lg", [](int i) { return MixedLocalGlobal(i); }); // test_internal_locals_differ - m.def("local_cpp_types_addr", []() { return (uintptr_t) &py::detail::registered_local_types_cpp(); }); + m.def("local_cpp_types_addr", + []() { return (uintptr_t) &py::detail::get_local_internals().registered_types_cpp; }); // test_stl_caster_vs_stl_bind py::bind_vector>(m, "VectorInt"); - m.def("load_vector_via_binding", [](std::vector &v) { - return std::accumulate(v.begin(), v.end(), 0); - }); + m.def("load_vector_via_binding", + [](std::vector &v) { return std::accumulate(v.begin(), v.end(), 0); }); // test_cross_module_calls m.def("return_self", [](LocalVec *v) { return v; }); m.def("return_copy", [](const LocalVec &v) { return LocalVec(v); }); - class Dog : public pets::Pet { public: Dog(std::string name) : Pet(name) {}; }; - py::class_(m, "Pet", py::module_local()) - .def("name", &pets::Pet::name); + class Dog : public pets::Pet { + public: + explicit Dog(std::string name) : Pet(std::move(name)) {} + }; + py::class_(m, "Pet", py::module_local()).def("name", &pets::Pet::name); // Binding for local extending class: - py::class_(m, "Dog") - .def(py::init()); + py::class_(m, "Dog").def(py::init()); m.def("pet_name", [](pets::Pet &p) { return p.name(); }); py::class_(m, "MixGL", py::module_local()).def(py::init()); @@ -118,6 +144,6 @@ PYBIND11_MODULE(pybind11_cross_module_tests, m) { // test_missing_header_message // The main module already includes stl.h, but we need to test the error message // which appears when this header is missing. - m.def("missing_header_arg", [](std::vector) { }); + m.def("missing_header_arg", [](const std::vector &) {}); m.def("missing_header_return", []() { return std::vector(); }); } diff --git a/3rdparty/pybind11/tests/pybind11_tests.cpp b/3rdparty/pybind11/tests/pybind11_tests.cpp index 439cd401..aa309559 100644 --- a/3rdparty/pybind11/tests/pybind11_tests.cpp +++ b/3rdparty/pybind11/tests/pybind11_tests.cpp @@ -8,6 +8,7 @@ */ #include "pybind11_tests.h" + #include "constructor_stats.h" #include @@ -31,9 +32,7 @@ std::list> &initializers() { return inits; } -test_initializer::test_initializer(Initializer init) { - initializers().emplace_back(init); -} +test_initializer::test_initializer(Initializer init) { initializers().emplace_back(init); } test_initializer::test_initializer(const char *submodule_name, Initializer init) { initializers().emplace_back([=](py::module_ &parent) { @@ -51,26 +50,52 @@ void bind_ConstructorStats(py::module_ &m) { .def_readwrite("move_assignments", &ConstructorStats::move_assignments) .def_readwrite("copy_constructions", &ConstructorStats::copy_constructions) .def_readwrite("move_constructions", &ConstructorStats::move_constructions) - .def_static("get", (ConstructorStats &(*)(py::object)) &ConstructorStats::get, py::return_value_policy::reference_internal) + .def_static("get", + (ConstructorStats & (*) (py::object)) & ConstructorStats::get, + py::return_value_policy::reference_internal) - // Not exactly ConstructorStats, but related: expose the internal pybind number of registered instances - // to allow instance cleanup checks (invokes a GC first) + // Not exactly ConstructorStats, but related: expose the internal pybind number of + // registered instances to allow instance cleanup checks (invokes a GC first) .def_static("detail_reg_inst", []() { ConstructorStats::gc(); return py::detail::get_internals().registered_instances.size(); - }) - ; + }); +} + +const char *cpp_std() { + return +#if defined(PYBIND11_CPP20) + "C++20"; +#elif defined(PYBIND11_CPP17) + "C++17"; +#elif defined(PYBIND11_CPP14) + "C++14"; +#else + "C++11"; +#endif } PYBIND11_MODULE(pybind11_tests, m) { m.doc() = "pybind11 test module"; + // Intentionally kept minimal to not create a maintenance chore + // ("just enough" to be conclusive). +#if defined(_MSC_FULL_VER) + m.attr("compiler_info") = "MSVC " PYBIND11_TOSTRING(_MSC_FULL_VER); +#elif defined(__VERSION__) + m.attr("compiler_info") = __VERSION__; +#else + m.attr("compiler_info") = py::none(); +#endif + m.attr("cpp_std") = cpp_std(); + m.attr("PYBIND11_INTERNALS_ID") = PYBIND11_INTERNALS_ID; + bind_ConstructorStats(m); -#if !defined(NDEBUG) - m.attr("debug_enabled") = true; +#if defined(PYBIND11_DETAILED_ERROR_MESSAGES) + m.attr("detailed_error_messages_enabled") = true; #else - m.attr("debug_enabled") = false; + m.attr("detailed_error_messages_enabled") = false; #endif py::class_(m, "UserType", "A `py::class_` type for testing") @@ -79,13 +104,14 @@ PYBIND11_MODULE(pybind11_tests, m) { .def("get_value", &UserType::value, "Get value using a method") .def("set_value", &UserType::set, "Set value using a method") .def_property("value", &UserType::value, &UserType::set, "Get/set value using a property") - .def("__repr__", [](const UserType& u) { return "UserType({})"_s.format(u.value()); }); + .def("__repr__", [](const UserType &u) { return "UserType({})"_s.format(u.value()); }); py::class_(m, "IncType") .def(py::init<>()) .def(py::init()) - .def("__repr__", [](const IncType& u) { return "IncType({})"_s.format(u.value()); }); + .def("__repr__", [](const IncType &u) { return "IncType({})"_s.format(u.value()); }); - for (const auto &initializer : initializers()) + for (const auto &initializer : initializers()) { initializer(m); + } } diff --git a/3rdparty/pybind11/tests/pybind11_tests.h b/3rdparty/pybind11/tests/pybind11_tests.h index 4ff56c07..a7c00c2f 100644 --- a/3rdparty/pybind11/tests/pybind11_tests.h +++ b/3rdparty/pybind11/tests/pybind11_tests.h @@ -1,10 +1,7 @@ #pragma once -#include -#if defined(_MSC_VER) && _MSC_VER < 1910 -// We get some really long type names here which causes MSVC 2015 to emit warnings -# pragma warning(disable: 4503) // warning C4503: decorated name length exceeded, name was truncated -#endif +#include +#include namespace py = pybind11; using namespace pybind11::literals; @@ -13,24 +10,23 @@ class test_initializer { using Initializer = void (*)(py::module_ &); public: - test_initializer(Initializer init); + explicit test_initializer(Initializer init); test_initializer(const char *submodule_name, Initializer init); }; -#define TEST_SUBMODULE(name, variable) \ - void test_submodule_##name(py::module_ &); \ - test_initializer name(#name, test_submodule_##name); \ - void test_submodule_##name(py::module_ &variable) - +#define TEST_SUBMODULE(name, variable) \ + void test_submodule_##name(py::module_ &); \ + test_initializer name(#name, test_submodule_##name); \ + void test_submodule_##name(py::module_ &(variable)) /// Dummy type which is not exported anywhere -- something to trigger a conversion error -struct UnregisteredType { }; +struct UnregisteredType {}; /// A user-defined type which is exported and can be used by any test class UserType { public: UserType() = default; - UserType(int i) : i(i) { } + explicit UserType(int i) : i(i) {} int value() const { return i; } void set(int set) { i = set; } @@ -44,7 +40,7 @@ class IncType : public UserType { public: using UserType::UserType; IncType() = default; - IncType(const IncType &other) : IncType(other.value() + 1) { } + IncType(const IncType &other) : IncType(other.value() + 1) {} IncType(IncType &&) = delete; IncType &operator=(const IncType &) = delete; IncType &operator=(IncType &&) = delete; @@ -56,16 +52,34 @@ union IntFloat { float f; }; -/// Custom cast-only type that casts to a string "rvalue" or "lvalue" depending on the cast context. -/// Used to test recursive casters (e.g. std::tuple, stl containers). +/// Custom cast-only type that casts to a string "rvalue" or "lvalue" depending on the cast +/// context. Used to test recursive casters (e.g. std::tuple, stl containers). struct RValueCaster {}; PYBIND11_NAMESPACE_BEGIN(pybind11) PYBIND11_NAMESPACE_BEGIN(detail) -template<> class type_caster { +template <> +class type_caster { public: - PYBIND11_TYPE_CASTER(RValueCaster, _("RValueCaster")); - static handle cast(RValueCaster &&, return_value_policy, handle) { return py::str("rvalue").release(); } - static handle cast(const RValueCaster &, return_value_policy, handle) { return py::str("lvalue").release(); } + PYBIND11_TYPE_CASTER(RValueCaster, const_name("RValueCaster")); + static handle cast(RValueCaster &&, return_value_policy, handle) { + return py::str("rvalue").release(); + } + static handle cast(const RValueCaster &, return_value_policy, handle) { + return py::str("lvalue").release(); + } }; PYBIND11_NAMESPACE_END(detail) PYBIND11_NAMESPACE_END(pybind11) + +template +void ignoreOldStyleInitWarnings(F &&body) { + py::exec(R"( + message = "pybind11-bound class '.+' is using an old-style placement-new '(?:__init__|__setstate__)' which has been deprecated" + + import warnings + with warnings.catch_warnings(): + warnings.filterwarnings("ignore", message=message, category=FutureWarning) + body() + )", + py::dict(py::arg("body") = py::cpp_function(body))); +} diff --git a/3rdparty/pybind11/tests/pytest.ini b/3rdparty/pybind11/tests/pytest.ini index c47cbe9c..792ba361 100644 --- a/3rdparty/pybind11/tests/pytest.ini +++ b/3rdparty/pybind11/tests/pytest.ini @@ -1,17 +1,20 @@ [pytest] -minversion = 3.1 +minversion = 3.10 norecursedirs = test_* extra_* xfail_strict = True addopts = - # show summary of skipped tests - -rs + # show summary of tests + -ra # capture only Python print and C++ py::print, but not C output (low-level Python errors) --capture=sys - # enable all warnings - -Wa + # Show local info when a failure occurs + --showlocals +log_cli_level = info filterwarnings = # make warnings into errors but ignore certain third-party extension issues error + # somehow, some DeprecationWarnings do not get turned into errors + always::DeprecationWarning # importing scipy submodules on some version of Python ignore::ImportWarning # bogus numpy ABI warning (see numpy/#432) diff --git a/3rdparty/pybind11/tests/requirements.txt b/3rdparty/pybind11/tests/requirements.txt index 80ed6171..04aafa8c 100644 --- a/3rdparty/pybind11/tests/requirements.txt +++ b/3rdparty/pybind11/tests/requirements.txt @@ -1,8 +1,9 @@ ---extra-index-url https://antocuni.github.io/pypy-wheels/manylinux2010/ -numpy==1.16.6; python_version<"3.6" and sys_platform!="win32" -numpy==1.18.0; platform_python_implementation=="PyPy" and sys_platform=="darwin" and python_version>="3.6" -numpy==1.19.3; (platform_python_implementation!="PyPy" or sys_platform=="linux") and python_version>="3.6" and python_version<"3.10" -pytest==4.6.9; python_version<"3.5" -pytest==5.4.3; python_version>="3.5" -scipy==1.2.3; (platform_python_implementation!="PyPy" or sys_platform=="linux") and python_version<"3.6" -scipy==1.5.2; (platform_python_implementation!="PyPy" or sys_platform=="linux") and python_version>="3.6" and python_version<"3.9" +build==0.8.0 +numpy==1.21.5; platform_python_implementation=="PyPy" and sys_platform=="linux" and python_version=="3.7" +numpy==1.19.3; platform_python_implementation!="PyPy" and python_version=="3.6" +numpy==1.21.5; platform_python_implementation!="PyPy" and python_version>="3.7" and python_version<"3.10" +numpy==1.22.2; platform_python_implementation!="PyPy" and python_version>="3.10" and python_version<"3.11" +pytest==7.0.0 +pytest-timeout +scipy==1.5.4; platform_python_implementation!="PyPy" and python_version<"3.10" +scipy==1.8.0; platform_python_implementation!="PyPy" and python_version=="3.10" diff --git a/3rdparty/pybind11/tests/test_async.cpp b/3rdparty/pybind11/tests/test_async.cpp index e6e01d72..a5d72246 100644 --- a/3rdparty/pybind11/tests/test_async.cpp +++ b/3rdparty/pybind11/tests/test_async.cpp @@ -11,12 +11,11 @@ TEST_SUBMODULE(async_module, m) { struct DoesNotSupportAsync {}; - py::class_(m, "DoesNotSupportAsync") - .def(py::init<>()); + py::class_(m, "DoesNotSupportAsync").def(py::init<>()); struct SupportsAsync {}; py::class_(m, "SupportsAsync") .def(py::init<>()) - .def("__await__", [](const SupportsAsync& self) -> py::object { + .def("__await__", [](const SupportsAsync &self) -> py::object { static_cast(self); py::object loop = py::module_::import("asyncio.events").attr("get_event_loop")(); py::object f = loop.attr("create_future")(); diff --git a/3rdparty/pybind11/tests/test_async.py b/3rdparty/pybind11/tests/test_async.py index df4489c4..b9ff9514 100644 --- a/3rdparty/pybind11/tests/test_async.py +++ b/3rdparty/pybind11/tests/test_async.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- import pytest asyncio = pytest.importorskip("asyncio") diff --git a/3rdparty/pybind11/tests/test_buffers.cpp b/3rdparty/pybind11/tests/test_buffers.cpp index 46eabf39..6b6e8cba 100644 --- a/3rdparty/pybind11/tests/test_buffers.cpp +++ b/3rdparty/pybind11/tests/test_buffers.cpp @@ -7,27 +7,31 @@ BSD-style license that can be found in the LICENSE file. */ -#include "pybind11_tests.h" -#include "constructor_stats.h" #include +#include "constructor_stats.h" +#include "pybind11_tests.h" + TEST_SUBMODULE(buffers, m) { // test_from_python / test_to_python: class Matrix { public: Matrix(py::ssize_t rows, py::ssize_t cols) : m_rows(rows), m_cols(cols) { print_created(this, std::to_string(m_rows) + "x" + std::to_string(m_cols) + " matrix"); - m_data = new float[(size_t) (rows*cols)]; + // NOLINTNEXTLINE(cppcoreguidelines-prefer-member-initializer) + m_data = new float[(size_t) (rows * cols)]; memset(m_data, 0, sizeof(float) * (size_t) (rows * cols)); } Matrix(const Matrix &s) : m_rows(s.m_rows), m_cols(s.m_cols) { - print_copy_created(this, std::to_string(m_rows) + "x" + std::to_string(m_cols) + " matrix"); + print_copy_created(this, + std::to_string(m_rows) + "x" + std::to_string(m_cols) + " matrix"); + // NOLINTNEXTLINE(cppcoreguidelines-prefer-member-initializer) m_data = new float[(size_t) (m_rows * m_cols)]; memcpy(m_data, s.m_data, sizeof(float) * (size_t) (m_rows * m_cols)); } - Matrix(Matrix &&s) : m_rows(s.m_rows), m_cols(s.m_cols), m_data(s.m_data) { + Matrix(Matrix &&s) noexcept : m_rows(s.m_rows), m_cols(s.m_cols), m_data(s.m_data) { print_move_created(this); s.m_rows = 0; s.m_cols = 0; @@ -35,12 +39,17 @@ TEST_SUBMODULE(buffers, m) { } ~Matrix() { - print_destroyed(this, std::to_string(m_rows) + "x" + std::to_string(m_cols) + " matrix"); + print_destroyed(this, + std::to_string(m_rows) + "x" + std::to_string(m_cols) + " matrix"); delete[] m_data; } Matrix &operator=(const Matrix &s) { - print_copy_assigned(this, std::to_string(m_rows) + "x" + std::to_string(m_cols) + " matrix"); + if (this == &s) { + return *this; + } + print_copy_assigned(this, + std::to_string(m_rows) + "x" + std::to_string(m_cols) + " matrix"); delete[] m_data; m_rows = s.m_rows; m_cols = s.m_cols; @@ -49,28 +58,34 @@ TEST_SUBMODULE(buffers, m) { return *this; } - Matrix &operator=(Matrix &&s) { - print_move_assigned(this, std::to_string(m_rows) + "x" + std::to_string(m_cols) + " matrix"); + Matrix &operator=(Matrix &&s) noexcept { + print_move_assigned(this, + std::to_string(m_rows) + "x" + std::to_string(m_cols) + " matrix"); if (&s != this) { delete[] m_data; - m_rows = s.m_rows; m_cols = s.m_cols; m_data = s.m_data; - s.m_rows = 0; s.m_cols = 0; s.m_data = nullptr; + m_rows = s.m_rows; + m_cols = s.m_cols; + m_data = s.m_data; + s.m_rows = 0; + s.m_cols = 0; + s.m_data = nullptr; } return *this; } float operator()(py::ssize_t i, py::ssize_t j) const { - return m_data[(size_t) (i*m_cols + j)]; + return m_data[(size_t) (i * m_cols + j)]; } float &operator()(py::ssize_t i, py::ssize_t j) { - return m_data[(size_t) (i*m_cols + j)]; + return m_data[(size_t) (i * m_cols + j)]; } float *data() { return m_data; } py::ssize_t rows() const { return m_rows; } py::ssize_t cols() const { return m_cols; } + private: py::ssize_t m_rows; py::ssize_t m_cols; @@ -79,51 +94,51 @@ TEST_SUBMODULE(buffers, m) { py::class_(m, "Matrix", py::buffer_protocol()) .def(py::init()) /// Construct from a buffer - .def(py::init([](py::buffer const b) { + .def(py::init([](const py::buffer &b) { py::buffer_info info = b.request(); - if (info.format != py::format_descriptor::format() || info.ndim != 2) + if (info.format != py::format_descriptor::format() || info.ndim != 2) { throw std::runtime_error("Incompatible buffer format!"); + } - auto v = new Matrix(info.shape[0], info.shape[1]); + auto *v = new Matrix(info.shape[0], info.shape[1]); memcpy(v->data(), info.ptr, sizeof(float) * (size_t) (v->rows() * v->cols())); return v; })) - .def("rows", &Matrix::rows) - .def("cols", &Matrix::cols) + .def("rows", &Matrix::rows) + .def("cols", &Matrix::cols) /// Bare bones interface - .def("__getitem__", [](const Matrix &m, std::pair i) { - if (i.first >= m.rows() || i.second >= m.cols()) - throw py::index_error(); - return m(i.first, i.second); - }) - .def("__setitem__", [](Matrix &m, std::pair i, float v) { - if (i.first >= m.rows() || i.second >= m.cols()) - throw py::index_error(); - m(i.first, i.second) = v; - }) - /// Provide buffer access - .def_buffer([](Matrix &m) -> py::buffer_info { + .def("__getitem__", + [](const Matrix &m, std::pair i) { + if (i.first >= m.rows() || i.second >= m.cols()) { + throw py::index_error(); + } + return m(i.first, i.second); + }) + .def("__setitem__", + [](Matrix &m, std::pair i, float v) { + if (i.first >= m.rows() || i.second >= m.cols()) { + throw py::index_error(); + } + m(i.first, i.second) = v; + }) + /// Provide buffer access + .def_buffer([](Matrix &m) -> py::buffer_info { return py::buffer_info( - m.data(), /* Pointer to buffer */ - { m.rows(), m.cols() }, /* Buffer dimensions */ - { sizeof(float) * size_t(m.cols()), /* Strides (in bytes) for each index */ - sizeof(float) } - ); - }) - ; - + m.data(), /* Pointer to buffer */ + {m.rows(), m.cols()}, /* Buffer dimensions */ + {sizeof(float) * size_t(m.cols()), /* Strides (in bytes) for each index */ + sizeof(float)}); + }); // test_inherited_protocol class SquareMatrix : public Matrix { public: - SquareMatrix(py::ssize_t n) : Matrix(n, n) { } + explicit SquareMatrix(py::ssize_t n) : Matrix(n, n) {} }; // Derived classes inherit the buffer protocol and the buffer access function - py::class_(m, "SquareMatrix") - .def(py::init()); - + py::class_(m, "SquareMatrix").def(py::init()); // test_pointer_to_member_fn // Tests that passing a pointer to member to the base class works in @@ -132,8 +147,8 @@ TEST_SUBMODULE(buffers, m) { int32_t value = 0; py::buffer_info get_buffer_info() { - return py::buffer_info(&value, sizeof(value), - py::format_descriptor::format(), 1); + return py::buffer_info( + &value, sizeof(value), py::format_descriptor::format(), 1); } }; py::class_(m, "Buffer", py::buffer_protocol()) @@ -141,7 +156,6 @@ TEST_SUBMODULE(buffers, m) { .def_readwrite("value", &Buffer::value) .def_buffer(&Buffer::get_buffer_info); - class ConstBuffer { std::unique_ptr value; @@ -150,18 +164,18 @@ TEST_SUBMODULE(buffers, m) { void set_value(int32_t v) { *value = v; } py::buffer_info get_buffer_info() const { - return py::buffer_info(value.get(), sizeof(*value), - py::format_descriptor::format(), 1); + return py::buffer_info( + value.get(), sizeof(*value), py::format_descriptor::format(), 1); } - ConstBuffer() : value(new int32_t{0}) { }; + ConstBuffer() : value(new int32_t{0}) {} }; py::class_(m, "ConstBuffer", py::buffer_protocol()) .def(py::init<>()) .def_property("value", &ConstBuffer::get_value, &ConstBuffer::set_value) .def_buffer(&ConstBuffer::get_buffer_info); - struct DerivedBuffer : public Buffer { }; + struct DerivedBuffer : public Buffer {}; py::class_(m, "DerivedBuffer", py::buffer_protocol()) .def(py::init<>()) .def_readwrite("value", (int32_t DerivedBuffer::*) &DerivedBuffer::value) @@ -169,11 +183,9 @@ TEST_SUBMODULE(buffers, m) { struct BufferReadOnly { const uint8_t value = 0; - BufferReadOnly(uint8_t value): value(value) {} + explicit BufferReadOnly(uint8_t value) : value(value) {} - py::buffer_info get_buffer_info() { - return py::buffer_info(&value, 1); - } + py::buffer_info get_buffer_info() { return py::buffer_info(&value, 1); } }; py::class_(m, "BufferReadOnly", py::buffer_protocol()) .def(py::init()) @@ -183,9 +195,7 @@ TEST_SUBMODULE(buffers, m) { uint8_t value = 0; bool readonly = false; - py::buffer_info get_buffer_info() { - return py::buffer_info(&value, 1, readonly); - } + py::buffer_info get_buffer_info() { return py::buffer_info(&value, 1, readonly); } }; py::class_(m, "BufferReadOnlySelect", py::buffer_protocol()) .def(py::init<>()) @@ -204,11 +214,11 @@ TEST_SUBMODULE(buffers, m) { .def_readonly("strides", &py::buffer_info::strides) .def_readonly("readonly", &py::buffer_info::readonly) .def("__repr__", [](py::handle self) { - return py::str("itemsize={0.itemsize!r}, size={0.size!r}, format={0.format!r}, ndim={0.ndim!r}, shape={0.shape!r}, strides={0.strides!r}, readonly={0.readonly!r}").format(self); - }) - ; + return py::str("itemsize={0.itemsize!r}, size={0.size!r}, format={0.format!r}, " + "ndim={0.ndim!r}, shape={0.shape!r}, strides={0.strides!r}, " + "readonly={0.readonly!r}") + .format(self); + }); - m.def("get_buffer_info", [](py::buffer buffer) { - return buffer.request(); - }); + m.def("get_buffer_info", [](const py::buffer &buffer) { return buffer.request(); }); } diff --git a/3rdparty/pybind11/tests/test_buffers.py b/3rdparty/pybind11/tests/test_buffers.py index f0f37084..8354b68c 100644 --- a/3rdparty/pybind11/tests/test_buffers.py +++ b/3rdparty/pybind11/tests/test_buffers.py @@ -1,14 +1,12 @@ -# -*- coding: utf-8 -*- +import ctypes import io import struct -import ctypes import pytest -import env # noqa: F401 - -from pybind11_tests import buffers as m +import env from pybind11_tests import ConstructorStats +from pybind11_tests import buffers as m np = pytest.importorskip("numpy") @@ -37,6 +35,10 @@ def test_from_python(): # https://foss.heptapod.net/pypy/pypy/-/issues/2444 +# TODO: fix on recent PyPy +@pytest.mark.xfail( + env.PYPY, reason="PyPy 7.3.7 doesn't clear this anymore", strict=False +) def test_to_python(): mat = m.Matrix(5, 4) assert memoryview(mat).shape == (5, 4) @@ -90,14 +92,16 @@ def test_pointer_to_member_fn(): def test_readonly_buffer(): buf = m.BufferReadOnly(0x64) view = memoryview(buf) - assert view[0] == b"d" if env.PY2 else 0x64 + assert view[0] == 0x64 assert view.readonly + with pytest.raises(TypeError): + view[0] = 0 def test_selective_readonly_buffer(): buf = m.BufferReadOnlySelect() - memoryview(buf)[0] = b"d" if env.PY2 else 0x64 + memoryview(buf)[0] = 0x64 assert buf.value == 0x64 io.BytesIO(b"A").readinto(buf) @@ -105,7 +109,7 @@ def test_selective_readonly_buffer(): buf.readonly = True with pytest.raises(TypeError): - memoryview(buf)[0] = b"\0" if env.PY2 else 0 + memoryview(buf)[0] = 0 with pytest.raises(TypeError): io.BytesIO(b"1").readinto(buf) @@ -140,9 +144,6 @@ def test_ctypes_array_2d(): assert not info.readonly -@pytest.mark.skipif( - "env.PYPY and env.PY2", reason="PyPy2 bytes buffer not reported as readonly" -) def test_ctypes_from_buffer(): test_pystr = b"0123456789" for pyarray in (test_pystr, bytearray(test_pystr)): diff --git a/3rdparty/pybind11/tests/test_builtin_casters.cpp b/3rdparty/pybind11/tests/test_builtin_casters.cpp index acc9f8fb..6ff63edf 100644 --- a/3rdparty/pybind11/tests/test_builtin_casters.cpp +++ b/3rdparty/pybind11/tests/test_builtin_casters.cpp @@ -7,40 +7,117 @@ BSD-style license that can be found in the LICENSE file. */ -#include "pybind11_tests.h" #include -#if defined(_MSC_VER) -# pragma warning(push) -# pragma warning(disable: 4127) // warning C4127: Conditional expression is constant -#endif +#include "pybind11_tests.h" + +struct ConstRefCasted { + int tag; +}; + +PYBIND11_NAMESPACE_BEGIN(pybind11) +PYBIND11_NAMESPACE_BEGIN(detail) +template <> +class type_caster { +public: + static constexpr auto name = const_name(); + + // Input is unimportant, a new value will always be constructed based on the + // cast operator. + bool load(handle, bool) { return true; } + + explicit operator ConstRefCasted &&() { + value = {1}; + // NOLINTNEXTLINE(performance-move-const-arg) + return std::move(value); + } + explicit operator ConstRefCasted &() { + value = {2}; + return value; + } + explicit operator ConstRefCasted *() { + value = {3}; + return &value; + } + + explicit operator const ConstRefCasted &() { + value = {4}; + return value; + } + explicit operator const ConstRefCasted *() { + value = {5}; + return &value; + } + + // custom cast_op to explicitly propagate types to the conversion operators. + template + using cast_op_type = + /// const + conditional_t< + std::is_same, const ConstRefCasted *>::value, + const ConstRefCasted *, + conditional_t< + std::is_same::value, + const ConstRefCasted &, + /// non-const + conditional_t, ConstRefCasted *>::value, + ConstRefCasted *, + conditional_t::value, + ConstRefCasted &, + /* else */ ConstRefCasted &&>>>>; + +private: + ConstRefCasted value = {0}; +}; +PYBIND11_NAMESPACE_END(detail) +PYBIND11_NAMESPACE_END(pybind11) TEST_SUBMODULE(builtin_casters, m) { // test_simple_string m.def("string_roundtrip", [](const char *s) { return s; }); // test_unicode_conversion - // Some test characters in utf16 and utf32 encodings. The last one (the 𝐀) contains a null byte - char32_t a32 = 0x61 /*a*/, z32 = 0x7a /*z*/, ib32 = 0x203d /*‽*/, cake32 = 0x1f382 /*🎂*/, mathbfA32 = 0x1d400 /*𝐀*/; - char16_t b16 = 0x62 /*b*/, z16 = 0x7a, ib16 = 0x203d, cake16_1 = 0xd83c, cake16_2 = 0xdf82, mathbfA16_1 = 0xd835, mathbfA16_2 = 0xdc00; + // Some test characters in utf16 and utf32 encodings. The last one (the 𝐀) contains a null + // byte + char32_t a32 = 0x61 /*a*/, z32 = 0x7a /*z*/, ib32 = 0x203d /*‽*/, cake32 = 0x1f382 /*🎂*/, + mathbfA32 = 0x1d400 /*𝐀*/; + char16_t b16 = 0x62 /*b*/, z16 = 0x7a, ib16 = 0x203d, cake16_1 = 0xd83c, cake16_2 = 0xdf82, + mathbfA16_1 = 0xd835, mathbfA16_2 = 0xdc00; std::wstring wstr; - wstr.push_back(0x61); // a + wstr.push_back(0x61); // a wstr.push_back(0x2e18); // ⸘ - if (sizeof(wchar_t) == 2) { wstr.push_back(mathbfA16_1); wstr.push_back(mathbfA16_2); } // 𝐀, utf16 - else { wstr.push_back((wchar_t) mathbfA32); } // 𝐀, utf32 + if (PYBIND11_SILENCE_MSVC_C4127(sizeof(wchar_t) == 2)) { + wstr.push_back(mathbfA16_1); + wstr.push_back(mathbfA16_2); + } // 𝐀, utf16 + else { + wstr.push_back((wchar_t) mathbfA32); + } // 𝐀, utf32 wstr.push_back(0x7a); // z - m.def("good_utf8_string", []() { return std::string((const char*)u8"Say utf8\u203d \U0001f382 \U0001d400"); }); // Say utf8‽ 🎂 𝐀 - m.def("good_utf16_string", [=]() { return std::u16string({ b16, ib16, cake16_1, cake16_2, mathbfA16_1, mathbfA16_2, z16 }); }); // b‽🎂𝐀z - m.def("good_utf32_string", [=]() { return std::u32string({ a32, mathbfA32, cake32, ib32, z32 }); }); // a𝐀🎂‽z + m.def("good_utf8_string", []() { + return std::string((const char *) u8"Say utf8\u203d \U0001f382 \U0001d400"); + }); // Say utf8‽ 🎂 𝐀 + m.def("good_utf16_string", [=]() { + return std::u16string({b16, ib16, cake16_1, cake16_2, mathbfA16_1, mathbfA16_2, z16}); + }); // b‽🎂𝐀z + m.def("good_utf32_string", [=]() { + return std::u32string({a32, mathbfA32, cake32, ib32, z32}); + }); // a𝐀🎂‽z m.def("good_wchar_string", [=]() { return wstr; }); // a‽𝐀z - m.def("bad_utf8_string", []() { return std::string("abc\xd0" "def"); }); - m.def("bad_utf16_string", [=]() { return std::u16string({ b16, char16_t(0xd800), z16 }); }); - // Under Python 2.7, invalid unicode UTF-32 characters don't appear to trigger UnicodeDecodeError - if (PY_MAJOR_VERSION >= 3) - m.def("bad_utf32_string", [=]() { return std::u32string({ a32, char32_t(0xd800), z32 }); }); - if (PY_MAJOR_VERSION >= 3 || sizeof(wchar_t) == 2) - m.def("bad_wchar_string", [=]() { return std::wstring({ wchar_t(0x61), wchar_t(0xd800) }); }); + m.def("bad_utf8_string", []() { + return std::string("abc\xd0" + "def"); + }); + m.def("bad_utf16_string", [=]() { return std::u16string({b16, char16_t(0xd800), z16}); }); + // Under Python 2.7, invalid unicode UTF-32 characters didn't appear to trigger + // UnicodeDecodeError + m.def("bad_utf32_string", [=]() { return std::u32string({a32, char32_t(0xd800), z32}); }); + if (PYBIND11_SILENCE_MSVC_C4127(sizeof(wchar_t) == 2)) { + m.def("bad_wchar_string", [=]() { + return std::wstring({wchar_t(0x61), wchar_t(0xd800)}); + }); + } m.def("u8_Z", []() -> char { return 'Z'; }); m.def("u8_eacute", []() -> char { return '\xe9'; }); m.def("u16_ibang", [=]() -> char16_t { return ib16; }); @@ -58,12 +135,17 @@ TEST_SUBMODULE(builtin_casters, m) { // test_bytes_to_string m.def("strlen", [](char *s) { return strlen(s); }); - m.def("string_length", [](std::string s) { return s.length(); }); + m.def("string_length", [](const std::string &s) { return s.length(); }); #ifdef PYBIND11_HAS_U8STRING m.attr("has_u8string") = true; - m.def("good_utf8_u8string", []() { return std::u8string(u8"Say utf8\u203d \U0001f382 \U0001d400"); }); // Say utf8‽ 🎂 𝐀 - m.def("bad_utf8_u8string", []() { return std::u8string((const char8_t*)"abc\xd0" "def"); }); + m.def("good_utf8_u8string", []() { + return std::u8string(u8"Say utf8\u203d \U0001f382 \U0001d400"); + }); // Say utf8‽ 🎂 𝐀 + m.def("bad_utf8_u8string", []() { + return std::u8string((const char8_t *) "abc\xd0" + "def"); + }); m.def("u8_char8_Z", []() -> char8_t { return u8'Z'; }); @@ -75,21 +157,72 @@ TEST_SUBMODULE(builtin_casters, m) { // test_string_view #ifdef PYBIND11_HAS_STRING_VIEW m.attr("has_string_view") = true; - m.def("string_view_print", [](std::string_view s) { py::print(s, s.size()); }); + m.def("string_view_print", [](std::string_view s) { py::print(s, s.size()); }); m.def("string_view16_print", [](std::u16string_view s) { py::print(s, s.size()); }); m.def("string_view32_print", [](std::u32string_view s) { py::print(s, s.size()); }); - m.def("string_view_chars", [](std::string_view s) { py::list l; for (auto c : s) l.append((std::uint8_t) c); return l; }); - m.def("string_view16_chars", [](std::u16string_view s) { py::list l; for (auto c : s) l.append((int) c); return l; }); - m.def("string_view32_chars", [](std::u32string_view s) { py::list l; for (auto c : s) l.append((int) c); return l; }); - m.def("string_view_return", []() { return std::string_view((const char*)u8"utf8 secret \U0001f382"); }); - m.def("string_view16_return", []() { return std::u16string_view(u"utf16 secret \U0001f382"); }); - m.def("string_view32_return", []() { return std::u32string_view(U"utf32 secret \U0001f382"); }); - -# ifdef PYBIND11_HAS_U8STRING - m.def("string_view8_print", [](std::u8string_view s) { py::print(s, s.size()); }); - m.def("string_view8_chars", [](std::u8string_view s) { py::list l; for (auto c : s) l.append((std::uint8_t) c); return l; }); + m.def("string_view_chars", [](std::string_view s) { + py::list l; + for (auto c : s) { + l.append((std::uint8_t) c); + } + return l; + }); + m.def("string_view16_chars", [](std::u16string_view s) { + py::list l; + for (auto c : s) { + l.append((int) c); + } + return l; + }); + m.def("string_view32_chars", [](std::u32string_view s) { + py::list l; + for (auto c : s) { + l.append((int) c); + } + return l; + }); + m.def("string_view_return", + []() { return std::string_view((const char *) u8"utf8 secret \U0001f382"); }); + m.def("string_view16_return", + []() { return std::u16string_view(u"utf16 secret \U0001f382"); }); + m.def("string_view32_return", + []() { return std::u32string_view(U"utf32 secret \U0001f382"); }); + + // The inner lambdas here are to also test implicit conversion + using namespace std::literals; + m.def("string_view_bytes", + []() { return [](py::bytes b) { return b; }("abc \x80\x80 def"sv); }); + m.def("string_view_str", + []() { return [](py::str s) { return s; }("abc \342\200\275 def"sv); }); + m.def("string_view_from_bytes", + [](const py::bytes &b) { return [](std::string_view s) { return s; }(b); }); + m.def("string_view_memoryview", []() { + static constexpr auto val = "Have some \360\237\216\202"sv; + return py::memoryview::from_memory(val); + }); + +# ifdef PYBIND11_HAS_U8STRING + m.def("string_view8_print", [](std::u8string_view s) { py::print(s, s.size()); }); + m.def("string_view8_chars", [](std::u8string_view s) { + py::list l; + for (auto c : s) + l.append((std::uint8_t) c); + return l; + }); m.def("string_view8_return", []() { return std::u8string_view(u8"utf8 secret \U0001f382"); }); -# endif + m.def("string_view8_str", []() { return py::str{std::u8string_view{u8"abc ‽ def"}}; }); +# endif + + struct TypeWithBothOperatorStringAndStringView { + // NOLINTNEXTLINE(google-explicit-constructor) + operator std::string() const { return "success"; } + // NOLINTNEXTLINE(google-explicit-constructor) + operator std::string_view() const { return "failure"; } + }; + m.def("bytes_from_type_with_both_operator_string_and_string_view", + []() { return py::bytes(TypeWithBothOperatorStringAndStringView()); }); + m.def("str_from_type_with_both_operator_string_and_string_view", + []() { return py::str(TypeWithBothOperatorStringAndStringView()); }); #endif // test_integer_casting @@ -98,71 +231,121 @@ TEST_SUBMODULE(builtin_casters, m) { m.def("i64_str", [](std::int64_t v) { return std::to_string(v); }); m.def("u64_str", [](std::uint64_t v) { return std::to_string(v); }); + // test_int_convert + m.def("int_passthrough", [](int arg) { return arg; }); + m.def( + "int_passthrough_noconvert", [](int arg) { return arg; }, py::arg{}.noconvert()); + // test_tuple - m.def("pair_passthrough", [](std::pair input) { - return std::make_pair(input.second, input.first); - }, "Return a pair in reversed order"); - m.def("tuple_passthrough", [](std::tuple input) { - return std::make_tuple(std::get<2>(input), std::get<1>(input), std::get<0>(input)); - }, "Return a triple in reversed order"); + m.def( + "pair_passthrough", + [](const std::pair &input) { + return std::make_pair(input.second, input.first); + }, + "Return a pair in reversed order"); + m.def( + "tuple_passthrough", + [](std::tuple input) { + return std::make_tuple(std::get<2>(input), std::get<1>(input), std::get<0>(input)); + }, + "Return a triple in reversed order"); m.def("empty_tuple", []() { return std::tuple<>(); }); static std::pair lvpair; static std::tuple lvtuple; - static std::pair>> lvnested; + static std::pair>> + lvnested; m.def("rvalue_pair", []() { return std::make_pair(RValueCaster{}, RValueCaster{}); }); m.def("lvalue_pair", []() -> const decltype(lvpair) & { return lvpair; }); - m.def("rvalue_tuple", []() { return std::make_tuple(RValueCaster{}, RValueCaster{}, RValueCaster{}); }); + m.def("rvalue_tuple", + []() { return std::make_tuple(RValueCaster{}, RValueCaster{}, RValueCaster{}); }); m.def("lvalue_tuple", []() -> const decltype(lvtuple) & { return lvtuple; }); m.def("rvalue_nested", []() { - return std::make_pair(RValueCaster{}, std::make_tuple(RValueCaster{}, std::make_pair(RValueCaster{}, RValueCaster{}))); }); + return std::make_pair( + RValueCaster{}, + std::make_tuple(RValueCaster{}, std::make_pair(RValueCaster{}, RValueCaster{}))); + }); m.def("lvalue_nested", []() -> const decltype(lvnested) & { return lvnested; }); - static std::pair int_string_pair{2, "items"}; - m.def("int_string_pair", []() { return &int_string_pair; }); + m.def( + "int_string_pair", + []() { + // Using no-destructor idiom to side-step warnings from overzealous compilers. + static auto *int_string_pair = new std::pair{2, "items"}; + return int_string_pair; + }, + py::return_value_policy::reference); // test_builtins_cast_return_none m.def("return_none_string", []() -> std::string * { return nullptr; }); - m.def("return_none_char", []() -> const char * { return nullptr; }); - m.def("return_none_bool", []() -> bool * { return nullptr; }); - m.def("return_none_int", []() -> int * { return nullptr; }); - m.def("return_none_float", []() -> float * { return nullptr; }); - m.def("return_none_pair", []() -> std::pair * { return nullptr; }); + m.def("return_none_char", []() -> const char * { return nullptr; }); + m.def("return_none_bool", []() -> bool * { return nullptr; }); + m.def("return_none_int", []() -> int * { return nullptr; }); + m.def("return_none_float", []() -> float * { return nullptr; }); + m.def("return_none_pair", []() -> std::pair * { return nullptr; }); // test_none_deferred m.def("defer_none_cstring", [](char *) { return false; }); - m.def("defer_none_cstring", [](py::none) { return true; }); + m.def("defer_none_cstring", [](const py::none &) { return true; }); m.def("defer_none_custom", [](UserType *) { return false; }); - m.def("defer_none_custom", [](py::none) { return true; }); + m.def("defer_none_custom", [](const py::none &) { return true; }); m.def("nodefer_none_void", [](void *) { return true; }); - m.def("nodefer_none_void", [](py::none) { return false; }); + m.def("nodefer_none_void", [](const py::none &) { return false; }); // test_void_caster m.def("load_nullptr_t", [](std::nullptr_t) {}); // not useful, but it should still compile m.def("cast_nullptr_t", []() { return std::nullptr_t{}; }); + // [workaround(intel)] ICC 20/21 breaks with py::arg().stuff, using py::arg{}.stuff works. + // test_bool_caster m.def("bool_passthrough", [](bool arg) { return arg; }); - m.def("bool_passthrough_noconvert", [](bool arg) { return arg; }, py::arg().noconvert()); + m.def( + "bool_passthrough_noconvert", [](bool arg) { return arg; }, py::arg{}.noconvert()); + + // TODO: This should be disabled and fixed in future Intel compilers +#if !defined(__INTEL_COMPILER) + // Test "bool_passthrough_noconvert" again, but using () instead of {} to construct py::arg + // When compiled with the Intel compiler, this results in segmentation faults when importing + // the module. Tested with icc (ICC) 2021.1 Beta 20200827, this should be tested again when + // a newer version of icc is available. + m.def( + "bool_passthrough_noconvert2", [](bool arg) { return arg; }, py::arg().noconvert()); +#endif // test_reference_wrapper m.def("refwrap_builtin", [](std::reference_wrapper p) { return 10 * p.get(); }); m.def("refwrap_usertype", [](std::reference_wrapper p) { return p.get().value(); }); + m.def("refwrap_usertype_const", + [](std::reference_wrapper p) { return p.get().value(); }); + + m.def("refwrap_lvalue", []() -> std::reference_wrapper { + static UserType x(1); + return std::ref(x); + }); + m.def("refwrap_lvalue_const", []() -> std::reference_wrapper { + static UserType x(1); + return std::cref(x); + }); + // Not currently supported (std::pair caster has return-by-value cast operator); // triggers static_assert failure. - //m.def("refwrap_pair", [](std::reference_wrapper>) { }); + // m.def("refwrap_pair", [](std::reference_wrapper>) { }); - m.def("refwrap_list", [](bool copy) { - static IncType x1(1), x2(2); - py::list l; - for (auto &f : {std::ref(x1), std::ref(x2)}) { - l.append(py::cast(f, copy ? py::return_value_policy::copy - : py::return_value_policy::reference)); - } - return l; - }, "copy"_a); + m.def( + "refwrap_list", + [](bool copy) { + static IncType x1(1), x2(2); + py::list l; + for (const auto &f : {std::ref(x1), std::ref(x2)}) { + l.append(py::cast( + f, copy ? py::return_value_policy::copy : py::return_value_policy::reference)); + } + return l; + }, + "copy"_a); m.def("refwrap_iiw", [](const IncType &w) { return w.value(); }); - m.def("refwrap_call_iiw", [](IncType &w, py::function f) { + m.def("refwrap_call_iiw", [](IncType &w, const py::function &f) { py::list l; l.append(f(std::ref(w))); l.append(f(std::cref(w))); @@ -176,12 +359,13 @@ TEST_SUBMODULE(builtin_casters, m) { // test_complex m.def("complex_cast", [](float x) { return "{}"_s.format(x); }); - m.def("complex_cast", [](std::complex x) { return "({}, {})"_s.format(x.real(), x.imag()); }); + m.def("complex_cast", + [](std::complex x) { return "({}, {})"_s.format(x.real(), x.imag()); }); // test int vs. long (Python 2) - m.def("int_cast", []() {return (int) 42;}); - m.def("long_cast", []() {return (long) 42;}); - m.def("longlong_cast", []() {return ULLONG_MAX;}); + m.def("int_cast", []() { return (int) 42; }); + m.def("long_cast", []() { return (long) 42; }); + m.def("longlong_cast", []() { return ULLONG_MAX; }); /// test void* cast operator m.def("test_void_caster", []() -> bool { @@ -189,4 +373,15 @@ TEST_SUBMODULE(builtin_casters, m) { py::object o = py::cast(v); return py::cast(o) == v; }); + + // Tests const/non-const propagation in cast_op. + m.def("takes", [](ConstRefCasted x) { return x.tag; }); + m.def("takes_move", [](ConstRefCasted &&x) { return x.tag; }); + m.def("takes_ptr", [](ConstRefCasted *x) { return x->tag; }); + m.def("takes_ref", [](ConstRefCasted &x) { return x.tag; }); + m.def("takes_ref_wrap", [](std::reference_wrapper x) { return x.get().tag; }); + m.def("takes_const_ptr", [](const ConstRefCasted *x) { return x->tag; }); + m.def("takes_const_ref", [](const ConstRefCasted &x) { return x.tag; }); + m.def("takes_const_ref_wrap", + [](std::reference_wrapper x) { return x.get().tag; }); } diff --git a/3rdparty/pybind11/tests/test_builtin_casters.py b/3rdparty/pybind11/tests/test_builtin_casters.py index bd7996b6..d38ae680 100644 --- a/3rdparty/pybind11/tests/test_builtin_casters.py +++ b/3rdparty/pybind11/tests/test_builtin_casters.py @@ -1,10 +1,10 @@ -# -*- coding: utf-8 -*- -import pytest +import sys -import env # noqa: F401 +import pytest +import env +from pybind11_tests import IncType, UserType from pybind11_tests import builtin_casters as m -from pybind11_tests import UserType, IncType def test_simple_string(): @@ -13,12 +13,12 @@ def test_simple_string(): def test_unicode_conversion(): """Tests unicode conversion and error reporting.""" - assert m.good_utf8_string() == u"Say utf8‽ 🎂 𝐀" - assert m.good_utf16_string() == u"b‽🎂𝐀z" - assert m.good_utf32_string() == u"a𝐀🎂‽z" - assert m.good_wchar_string() == u"a⸘𝐀z" + assert m.good_utf8_string() == "Say utf8‽ 🎂 𝐀" + assert m.good_utf16_string() == "b‽🎂𝐀z" + assert m.good_utf32_string() == "a𝐀🎂‽z" + assert m.good_wchar_string() == "a⸘𝐀z" if hasattr(m, "has_u8string"): - assert m.good_utf8_u8string() == u"Say utf8‽ 🎂 𝐀" + assert m.good_utf8_u8string() == "Say utf8‽ 🎂 𝐀" with pytest.raises(UnicodeDecodeError): m.bad_utf8_string() @@ -26,7 +26,7 @@ def test_unicode_conversion(): with pytest.raises(UnicodeDecodeError): m.bad_utf16_string() - # These are provided only if they actually fail (they don't when 32-bit and under Python 2.7) + # These are provided only if they actually fail (they don't when 32-bit) if hasattr(m, "bad_utf32_string"): with pytest.raises(UnicodeDecodeError): m.bad_utf32_string() @@ -38,10 +38,10 @@ def test_unicode_conversion(): m.bad_utf8_u8string() assert m.u8_Z() == "Z" - assert m.u8_eacute() == u"é" - assert m.u16_ibang() == u"‽" - assert m.u32_mathbfA() == u"𝐀" - assert m.wchar_heart() == u"♥" + assert m.u8_eacute() == "é" + assert m.u16_ibang() == "‽" + assert m.u32_mathbfA() == "𝐀" + assert m.wchar_heart() == "♥" if hasattr(m, "has_u8string"): assert m.u8_char8_Z() == "Z" @@ -50,72 +50,72 @@ def test_single_char_arguments(): """Tests failures for passing invalid inputs to char-accepting functions""" def toobig_message(r): - return "Character code point not in range({0:#x})".format(r) + return f"Character code point not in range({r:#x})" toolong_message = "Expected a character, but multi-character string found" - assert m.ord_char(u"a") == 0x61 # simple ASCII - assert m.ord_char_lv(u"b") == 0x62 + assert m.ord_char("a") == 0x61 # simple ASCII + assert m.ord_char_lv("b") == 0x62 assert ( - m.ord_char(u"é") == 0xE9 + m.ord_char("é") == 0xE9 ) # requires 2 bytes in utf-8, but can be stuffed in a char with pytest.raises(ValueError) as excinfo: - assert m.ord_char(u"Ā") == 0x100 # requires 2 bytes, doesn't fit in a char + assert m.ord_char("Ā") == 0x100 # requires 2 bytes, doesn't fit in a char assert str(excinfo.value) == toobig_message(0x100) with pytest.raises(ValueError) as excinfo: - assert m.ord_char(u"ab") + assert m.ord_char("ab") assert str(excinfo.value) == toolong_message - assert m.ord_char16(u"a") == 0x61 - assert m.ord_char16(u"é") == 0xE9 - assert m.ord_char16_lv(u"ê") == 0xEA - assert m.ord_char16(u"Ā") == 0x100 - assert m.ord_char16(u"‽") == 0x203D - assert m.ord_char16(u"♥") == 0x2665 - assert m.ord_char16_lv(u"♡") == 0x2661 + assert m.ord_char16("a") == 0x61 + assert m.ord_char16("é") == 0xE9 + assert m.ord_char16_lv("ê") == 0xEA + assert m.ord_char16("Ā") == 0x100 + assert m.ord_char16("‽") == 0x203D + assert m.ord_char16("♥") == 0x2665 + assert m.ord_char16_lv("♡") == 0x2661 with pytest.raises(ValueError) as excinfo: - assert m.ord_char16(u"🎂") == 0x1F382 # requires surrogate pair + assert m.ord_char16("🎂") == 0x1F382 # requires surrogate pair assert str(excinfo.value) == toobig_message(0x10000) with pytest.raises(ValueError) as excinfo: - assert m.ord_char16(u"aa") + assert m.ord_char16("aa") assert str(excinfo.value) == toolong_message - assert m.ord_char32(u"a") == 0x61 - assert m.ord_char32(u"é") == 0xE9 - assert m.ord_char32(u"Ā") == 0x100 - assert m.ord_char32(u"‽") == 0x203D - assert m.ord_char32(u"♥") == 0x2665 - assert m.ord_char32(u"🎂") == 0x1F382 + assert m.ord_char32("a") == 0x61 + assert m.ord_char32("é") == 0xE9 + assert m.ord_char32("Ā") == 0x100 + assert m.ord_char32("‽") == 0x203D + assert m.ord_char32("♥") == 0x2665 + assert m.ord_char32("🎂") == 0x1F382 with pytest.raises(ValueError) as excinfo: - assert m.ord_char32(u"aa") + assert m.ord_char32("aa") assert str(excinfo.value) == toolong_message - assert m.ord_wchar(u"a") == 0x61 - assert m.ord_wchar(u"é") == 0xE9 - assert m.ord_wchar(u"Ā") == 0x100 - assert m.ord_wchar(u"‽") == 0x203D - assert m.ord_wchar(u"♥") == 0x2665 + assert m.ord_wchar("a") == 0x61 + assert m.ord_wchar("é") == 0xE9 + assert m.ord_wchar("Ā") == 0x100 + assert m.ord_wchar("‽") == 0x203D + assert m.ord_wchar("♥") == 0x2665 if m.wchar_size == 2: with pytest.raises(ValueError) as excinfo: - assert m.ord_wchar(u"🎂") == 0x1F382 # requires surrogate pair + assert m.ord_wchar("🎂") == 0x1F382 # requires surrogate pair assert str(excinfo.value) == toobig_message(0x10000) else: - assert m.ord_wchar(u"🎂") == 0x1F382 + assert m.ord_wchar("🎂") == 0x1F382 with pytest.raises(ValueError) as excinfo: - assert m.ord_wchar(u"aa") + assert m.ord_wchar("aa") assert str(excinfo.value) == toolong_message if hasattr(m, "has_u8string"): - assert m.ord_char8(u"a") == 0x61 # simple ASCII - assert m.ord_char8_lv(u"b") == 0x62 + assert m.ord_char8("a") == 0x61 # simple ASCII + assert m.ord_char8_lv("b") == 0x62 assert ( - m.ord_char8(u"é") == 0xE9 + m.ord_char8("é") == 0xE9 ) # requires 2 bytes in utf-8, but can be stuffed in a char with pytest.raises(ValueError) as excinfo: - assert m.ord_char8(u"Ā") == 0x100 # requires 2 bytes, doesn't fit in a char + assert m.ord_char8("Ā") == 0x100 # requires 2 bytes, doesn't fit in a char assert str(excinfo.value) == toobig_message(0x100) with pytest.raises(ValueError) as excinfo: - assert m.ord_char8(u"ab") + assert m.ord_char8("ab") assert str(excinfo.value) == toolong_message @@ -124,18 +124,22 @@ def test_bytes_to_string(): one-way: the only way to return bytes to Python is via the pybind11::bytes class.""" # Issue #816 - def to_bytes(s): - b = s if env.PY2 else s.encode("utf8") - assert isinstance(b, bytes) - return b - - assert m.strlen(to_bytes("hi")) == 2 - assert m.string_length(to_bytes("world")) == 5 - assert m.string_length(to_bytes("a\x00b")) == 3 - assert m.strlen(to_bytes("a\x00b")) == 1 # C-string limitation + assert m.strlen(b"hi") == 2 + assert m.string_length(b"world") == 5 + assert m.string_length("a\x00b".encode()) == 3 + assert m.strlen("a\x00b".encode()) == 1 # C-string limitation # passing in a utf8 encoded string should work - assert m.string_length(u"💩".encode("utf8")) == 4 + assert m.string_length("💩".encode()) == 4 + + +def test_bytearray_to_string(): + """Tests the ability to pass bytearray to C++ string-accepting functions""" + assert m.string_length(bytearray(b"Hi")) == 2 + assert m.strlen(bytearray(b"bytearray")) == 9 + assert m.string_length(bytearray()) == 0 + assert m.string_length(bytearray("🦜", "utf-8", "strict")) == 4 + assert m.string_length(bytearray(b"\x80")) == 1 @pytest.mark.skipif(not hasattr(m, "has_string_view"), reason="no ") @@ -143,26 +147,26 @@ def test_string_view(capture): """Tests support for C++17 string_view arguments and return values""" assert m.string_view_chars("Hi") == [72, 105] assert m.string_view_chars("Hi 🎂") == [72, 105, 32, 0xF0, 0x9F, 0x8E, 0x82] - assert m.string_view16_chars(u"Hi 🎂") == [72, 105, 32, 0xD83C, 0xDF82] - assert m.string_view32_chars(u"Hi 🎂") == [72, 105, 32, 127874] + assert m.string_view16_chars("Hi 🎂") == [72, 105, 32, 0xD83C, 0xDF82] + assert m.string_view32_chars("Hi 🎂") == [72, 105, 32, 127874] if hasattr(m, "has_u8string"): assert m.string_view8_chars("Hi") == [72, 105] - assert m.string_view8_chars(u"Hi 🎂") == [72, 105, 32, 0xF0, 0x9F, 0x8E, 0x82] + assert m.string_view8_chars("Hi 🎂") == [72, 105, 32, 0xF0, 0x9F, 0x8E, 0x82] - assert m.string_view_return() == u"utf8 secret 🎂" - assert m.string_view16_return() == u"utf16 secret 🎂" - assert m.string_view32_return() == u"utf32 secret 🎂" + assert m.string_view_return() == "utf8 secret 🎂" + assert m.string_view16_return() == "utf16 secret 🎂" + assert m.string_view32_return() == "utf32 secret 🎂" if hasattr(m, "has_u8string"): - assert m.string_view8_return() == u"utf8 secret 🎂" + assert m.string_view8_return() == "utf8 secret 🎂" with capture: m.string_view_print("Hi") m.string_view_print("utf8 🎂") - m.string_view16_print(u"utf16 🎂") - m.string_view32_print(u"utf32 🎂") + m.string_view16_print("utf16 🎂") + m.string_view32_print("utf32 🎂") assert ( capture - == u""" + == """ Hi 2 utf8 🎂 9 utf16 🎂 8 @@ -172,10 +176,10 @@ def test_string_view(capture): if hasattr(m, "has_u8string"): with capture: m.string_view8_print("Hi") - m.string_view8_print(u"utf8 🎂") + m.string_view8_print("utf8 🎂") assert ( capture - == u""" + == """ Hi 2 utf8 🎂 9 """ @@ -184,11 +188,11 @@ def test_string_view(capture): with capture: m.string_view_print("Hi, ascii") m.string_view_print("Hi, utf8 🎂") - m.string_view16_print(u"Hi, utf16 🎂") - m.string_view32_print(u"Hi, utf32 🎂") + m.string_view16_print("Hi, utf16 🎂") + m.string_view32_print("Hi, utf32 🎂") assert ( capture - == u""" + == """ Hi, ascii 9 Hi, utf8 🎂 13 Hi, utf16 🎂 12 @@ -198,15 +202,25 @@ def test_string_view(capture): if hasattr(m, "has_u8string"): with capture: m.string_view8_print("Hi, ascii") - m.string_view8_print(u"Hi, utf8 🎂") + m.string_view8_print("Hi, utf8 🎂") assert ( capture - == u""" + == """ Hi, ascii 9 Hi, utf8 🎂 13 """ ) + assert m.string_view_bytes() == b"abc \x80\x80 def" + assert m.string_view_str() == "abc ‽ def" + assert m.string_view_from_bytes("abc ‽ def".encode()) == "abc ‽ def" + if hasattr(m, "has_u8string"): + assert m.string_view8_str() == "abc ‽ def" + assert m.string_view_memoryview() == "Have some 🎂".encode() + + assert m.bytes_from_type_with_both_operator_string_and_string_view() == b"success" + assert m.str_from_type_with_both_operator_string_and_string_view() == "success" + def test_integer_casting(): """Issue #929 - out-of-range integer values shouldn't be accepted""" @@ -214,20 +228,8 @@ def test_integer_casting(): assert m.i64_str(-1) == "-1" assert m.i32_str(2000000000) == "2000000000" assert m.u32_str(2000000000) == "2000000000" - if env.PY2: - assert m.i32_str(long(-1)) == "-1" # noqa: F821 undefined name 'long' - assert m.i64_str(long(-1)) == "-1" # noqa: F821 undefined name 'long' - assert ( - m.i64_str(long(-999999999999)) # noqa: F821 undefined name 'long' - == "-999999999999" - ) - assert ( - m.u64_str(long(999999999999)) # noqa: F821 undefined name 'long' - == "999999999999" - ) - else: - assert m.i64_str(-999999999999) == "-999999999999" - assert m.u64_str(999999999999) == "999999999999" + assert m.i64_str(-999999999999) == "-999999999999" + assert m.u64_str(999999999999) == "999999999999" with pytest.raises(TypeError) as excinfo: m.u32_str(-1) @@ -242,13 +244,100 @@ def test_integer_casting(): m.i32_str(3000000000) assert "incompatible function arguments" in str(excinfo.value) - if env.PY2: - with pytest.raises(TypeError) as excinfo: - m.u32_str(long(-1)) # noqa: F821 undefined name 'long' - assert "incompatible function arguments" in str(excinfo.value) - with pytest.raises(TypeError) as excinfo: - m.u64_str(long(-1)) # noqa: F821 undefined name 'long' - assert "incompatible function arguments" in str(excinfo.value) + +def test_int_convert(): + class Int: + def __int__(self): + return 42 + + class NotInt: + pass + + class Float: + def __float__(self): + return 41.99999 + + class Index: + def __index__(self): + return 42 + + class IntAndIndex: + def __int__(self): + return 42 + + def __index__(self): + return 0 + + class RaisingTypeErrorOnIndex: + def __index__(self): + raise TypeError + + def __int__(self): + return 42 + + class RaisingValueErrorOnIndex: + def __index__(self): + raise ValueError + + def __int__(self): + return 42 + + convert, noconvert = m.int_passthrough, m.int_passthrough_noconvert + + def requires_conversion(v): + pytest.raises(TypeError, noconvert, v) + + def cant_convert(v): + pytest.raises(TypeError, convert, v) + + assert convert(7) == 7 + assert noconvert(7) == 7 + cant_convert(3.14159) + # TODO: Avoid DeprecationWarning in `PyLong_AsLong` (and similar) + # TODO: PyPy 3.8 does not behave like CPython 3.8 here yet (7.3.7) + if (3, 8) <= sys.version_info < (3, 10) and env.CPYTHON: + with env.deprecated_call(): + assert convert(Int()) == 42 + else: + assert convert(Int()) == 42 + requires_conversion(Int()) + cant_convert(NotInt()) + cant_convert(Float()) + + # Before Python 3.8, `PyLong_AsLong` does not pick up on `obj.__index__`, + # but pybind11 "backports" this behavior. + assert convert(Index()) == 42 + assert noconvert(Index()) == 42 + assert convert(IntAndIndex()) == 0 # Fishy; `int(DoubleThought)` == 42 + assert noconvert(IntAndIndex()) == 0 + assert convert(RaisingTypeErrorOnIndex()) == 42 + requires_conversion(RaisingTypeErrorOnIndex()) + assert convert(RaisingValueErrorOnIndex()) == 42 + requires_conversion(RaisingValueErrorOnIndex()) + + +def test_numpy_int_convert(): + np = pytest.importorskip("numpy") + + convert, noconvert = m.int_passthrough, m.int_passthrough_noconvert + + def require_implicit(v): + pytest.raises(TypeError, noconvert, v) + + # `np.intc` is an alias that corresponds to a C++ `int` + assert convert(np.intc(42)) == 42 + assert noconvert(np.intc(42)) == 42 + + # The implicit conversion from np.float32 is undesirable but currently accepted. + # TODO: Avoid DeprecationWarning in `PyLong_AsLong` (and similar) + # TODO: PyPy 3.8 does not behave like CPython 3.8 here yet (7.3.7) + # https://github.com/pybind/pybind11/issues/3408 + if (3, 8) <= sys.version_info < (3, 10) and env.CPYTHON: + with env.deprecated_call(): + assert convert(np.float32(3.14159)) == 3 + else: + assert convert(np.float32(3.14159)) == 3 + require_implicit(np.float32(3.14159)) def test_tuple(doc): @@ -315,6 +404,7 @@ def test_reference_wrapper(): """std::reference_wrapper for builtin and user types""" assert m.refwrap_builtin(42) == 420 assert m.refwrap_usertype(UserType(42)) == 42 + assert m.refwrap_usertype_const(UserType(42)) == 42 with pytest.raises(TypeError) as excinfo: m.refwrap_builtin(None) @@ -324,6 +414,9 @@ def test_reference_wrapper(): m.refwrap_usertype(None) assert "incompatible function arguments" in str(excinfo.value) + assert m.refwrap_lvalue().value == 1 + assert m.refwrap_lvalue_const().value == 1 + a1 = m.refwrap_list(copy=True) a2 = m.refwrap_list(copy=True) assert [x.value for x in a1] == [2, 3] @@ -366,7 +459,7 @@ def test_bool_caster(): require_implicit(None) assert convert(None) is False - class A(object): + class A: def __init__(self, x): self.x = x @@ -376,7 +469,7 @@ def test_bool_caster(): def __bool__(self): return self.x - class B(object): + class B: pass # Arbitrary objects are not accepted @@ -406,18 +499,28 @@ def test_numpy_bool(): def test_int_long(): - """In Python 2, a C++ int should return a Python int rather than long - if possible: longs are not always accepted where ints are used (such - as the argument to sys.exit()). A C++ long long is always a Python - long.""" - - import sys - - must_be_long = type(getattr(sys, "maxint", 1) + 1) assert isinstance(m.int_cast(), int) assert isinstance(m.long_cast(), int) - assert isinstance(m.longlong_cast(), must_be_long) + assert isinstance(m.longlong_cast(), int) def test_void_caster_2(): assert m.test_void_caster() + + +def test_const_ref_caster(): + """Verifies that const-ref is propagated through type_caster cast_op. + The returned ConstRefCasted type is a minimal type that is constructed to + reference the casting mode used. + """ + x = False + assert m.takes(x) == 1 + assert m.takes_move(x) == 1 + + assert m.takes_ptr(x) == 3 + assert m.takes_ref(x) == 2 + assert m.takes_ref_wrap(x) == 2 + + assert m.takes_const_ptr(x) == 5 + assert m.takes_const_ref(x) == 4 + assert m.takes_const_ref_wrap(x) == 4 diff --git a/3rdparty/pybind11/tests/test_call_policies.cpp b/3rdparty/pybind11/tests/test_call_policies.cpp index 26c83f81..d177008c 100644 --- a/3rdparty/pybind11/tests/test_call_policies.cpp +++ b/3rdparty/pybind11/tests/test_call_policies.cpp @@ -40,17 +40,17 @@ TEST_SUBMODULE(call_policies, m) { Child(Child &&) = default; ~Child() { py::print("Releasing child."); } }; - py::class_(m, "Child") - .def(py::init<>()); + py::class_(m, "Child").def(py::init<>()); class Parent { public: Parent() { py::print("Allocating parent."); } - Parent(const Parent& parent) = default; + Parent(const Parent &parent) = default; ~Parent() { py::print("Releasing parent."); } - void addChild(Child *) { } + void addChild(Child *) {} Child *returnChild() { return new Child(); } Child *returnNullChild() { return nullptr; } + static Child *staticFunction(Parent *) { return new Child(); } }; py::class_(m, "Parent") .def(py::init<>()) @@ -60,7 +60,13 @@ TEST_SUBMODULE(call_policies, m) { .def("returnChild", &Parent::returnChild) .def("returnChildKeepAlive", &Parent::returnChild, py::keep_alive<1, 0>()) .def("returnNullChildKeepAliveChild", &Parent::returnNullChild, py::keep_alive<1, 0>()) - .def("returnNullChildKeepAliveParent", &Parent::returnNullChild, py::keep_alive<0, 1>()); + .def("returnNullChildKeepAliveParent", &Parent::returnNullChild, py::keep_alive<0, 1>()) + .def_static("staticFunction", &Parent::staticFunction, py::keep_alive<1, 0>()); + + m.def( + "free_function", [](Parent *, Child *) {}, py::keep_alive<1, 2>()); + m.def( + "invalid_arg_index", [] {}, py::keep_alive<0, 1>()); #if !defined(PYPY_VERSION) // test_alive_gc @@ -68,29 +74,37 @@ TEST_SUBMODULE(call_policies, m) { public: using Parent::Parent; }; - py::class_(m, "ParentGC", py::dynamic_attr()) - .def(py::init<>()); + py::class_(m, "ParentGC", py::dynamic_attr()).def(py::init<>()); #endif // test_call_guard m.def("unguarded_call", &CustomGuard::report_status); m.def("guarded_call", &CustomGuard::report_status, py::call_guard()); - m.def("multiple_guards_correct_order", []() { - return CustomGuard::report_status() + std::string(" & ") + DependentGuard::report_status(); - }, py::call_guard()); - - m.def("multiple_guards_wrong_order", []() { - return DependentGuard::report_status() + std::string(" & ") + CustomGuard::report_status(); - }, py::call_guard()); + m.def( + "multiple_guards_correct_order", + []() { + return CustomGuard::report_status() + std::string(" & ") + + DependentGuard::report_status(); + }, + py::call_guard()); + + m.def( + "multiple_guards_wrong_order", + []() { + return DependentGuard::report_status() + std::string(" & ") + + CustomGuard::report_status(); + }, + py::call_guard()); #if defined(WITH_THREAD) && !defined(PYPY_VERSION) // `py::call_guard()` should work in PyPy as well, // but it's unclear how to test it without `PyGILState_GetThisThreadState`. auto report_gil_status = []() { auto is_gil_held = false; - if (auto tstate = py::detail::get_thread_state_unchecked()) + if (auto *tstate = py::detail::get_thread_state_unchecked()) { is_gil_held = (tstate == PyGILState_GetThisThreadState()); + } return is_gil_held ? "GIL held" : "GIL released"; }; diff --git a/3rdparty/pybind11/tests/test_call_policies.py b/3rdparty/pybind11/tests/test_call_policies.py index e0413d14..61605641 100644 --- a/3rdparty/pybind11/tests/test_call_policies.py +++ b/3rdparty/pybind11/tests/test_call_policies.py @@ -1,10 +1,8 @@ -# -*- coding: utf-8 -*- import pytest import env # noqa: F401 - -from pybind11_tests import call_policies as m from pybind11_tests import ConstructorStats +from pybind11_tests import call_policies as m @pytest.mark.xfail("env.PYPY", reason="sometimes comes out 1 off on PyPy", strict=False) @@ -46,6 +44,19 @@ def test_keep_alive_argument(capture): """ ) + p = m.Parent() + c = m.Child() + assert ConstructorStats.detail_reg_inst() == n_inst + 2 + m.free_function(p, c) + del c + assert ConstructorStats.detail_reg_inst() == n_inst + 2 + del p + assert ConstructorStats.detail_reg_inst() == n_inst + + with pytest.raises(RuntimeError) as excinfo: + m.invalid_arg_index() + assert str(excinfo.value) == "Could not activate keep_alive!" + def test_keep_alive_return_value(capture): n_inst = ConstructorStats.detail_reg_inst() @@ -85,6 +96,23 @@ def test_keep_alive_return_value(capture): """ ) + p = m.Parent() + assert ConstructorStats.detail_reg_inst() == n_inst + 1 + with capture: + m.Parent.staticFunction(p) + assert ConstructorStats.detail_reg_inst() == n_inst + 2 + assert capture == "Allocating child." + with capture: + del p + assert ConstructorStats.detail_reg_inst() == n_inst + assert ( + capture + == """ + Releasing parent. + Releasing child. + """ + ) + # https://foss.heptapod.net/pypy/pypy/-/issues/2447 @pytest.mark.xfail("env.PYPY", reason="_PyObject_GetDictPtr is unimplemented") diff --git a/3rdparty/pybind11/tests/test_callbacks.cpp b/3rdparty/pybind11/tests/test_callbacks.cpp index 683dfb3e..92b8053d 100644 --- a/3rdparty/pybind11/tests/test_callbacks.cpp +++ b/3rdparty/pybind11/tests/test_callbacks.cpp @@ -7,72 +7,79 @@ BSD-style license that can be found in the LICENSE file. */ -#include "pybind11_tests.h" -#include "constructor_stats.h" #include -#include +#include "constructor_stats.h" +#include "pybind11_tests.h" + +#include int dummy_function(int i) { return i + 1; } TEST_SUBMODULE(callbacks, m) { // test_callbacks, test_function_signatures - m.def("test_callback1", [](py::object func) { return func(); }); - m.def("test_callback2", [](py::object func) { return func("Hello", 'x', true, 5); }); + m.def("test_callback1", [](const py::object &func) { return func(); }); + m.def("test_callback2", [](const py::object &func) { return func("Hello", 'x', true, 5); }); m.def("test_callback3", [](const std::function &func) { - return "func(43) = " + std::to_string(func(43)); }); - m.def("test_callback4", []() -> std::function { return [](int i) { return i+1; }; }); - m.def("test_callback5", []() { - return py::cpp_function([](int i) { return i+1; }, py::arg("number")); + return "func(43) = " + std::to_string(func(43)); }); + m.def("test_callback4", + []() -> std::function { return [](int i) { return i + 1; }; }); + m.def("test_callback5", + []() { return py::cpp_function([](int i) { return i + 1; }, py::arg("number")); }); // test_keyword_args_and_generalized_unpacking - m.def("test_tuple_unpacking", [](py::function f) { + m.def("test_tuple_unpacking", [](const py::function &f) { auto t1 = py::make_tuple(2, 3); auto t2 = py::make_tuple(5, 6); return f("positional", 1, *t1, 4, *t2); }); - m.def("test_dict_unpacking", [](py::function f) { - auto d1 = py::dict("key"_a="value", "a"_a=1); + m.def("test_dict_unpacking", [](const py::function &f) { + auto d1 = py::dict("key"_a = "value", "a"_a = 1); auto d2 = py::dict(); - auto d3 = py::dict("b"_a=2); + auto d3 = py::dict("b"_a = 2); return f("positional", 1, **d1, **d2, **d3); }); - m.def("test_keyword_args", [](py::function f) { - return f("x"_a=10, "y"_a=20); - }); + m.def("test_keyword_args", [](const py::function &f) { return f("x"_a = 10, "y"_a = 20); }); - m.def("test_unpacking_and_keywords1", [](py::function f) { + m.def("test_unpacking_and_keywords1", [](const py::function &f) { auto args = py::make_tuple(2); - auto kwargs = py::dict("d"_a=4); - return f(1, *args, "c"_a=3, **kwargs); + auto kwargs = py::dict("d"_a = 4); + return f(1, *args, "c"_a = 3, **kwargs); }); - m.def("test_unpacking_and_keywords2", [](py::function f) { - auto kwargs1 = py::dict("a"_a=1); - auto kwargs2 = py::dict("c"_a=3, "d"_a=4); - return f("positional", *py::make_tuple(1), 2, *py::make_tuple(3, 4), 5, - "key"_a="value", **kwargs1, "b"_a=2, **kwargs2, "e"_a=5); + m.def("test_unpacking_and_keywords2", [](const py::function &f) { + auto kwargs1 = py::dict("a"_a = 1); + auto kwargs2 = py::dict("c"_a = 3, "d"_a = 4); + return f("positional", + *py::make_tuple(1), + 2, + *py::make_tuple(3, 4), + 5, + "key"_a = "value", + **kwargs1, + "b"_a = 2, + **kwargs2, + "e"_a = 5); }); - m.def("test_unpacking_error1", [](py::function f) { - auto kwargs = py::dict("x"_a=3); - return f("x"_a=1, "y"_a=2, **kwargs); // duplicate ** after keyword + m.def("test_unpacking_error1", [](const py::function &f) { + auto kwargs = py::dict("x"_a = 3); + return f("x"_a = 1, "y"_a = 2, **kwargs); // duplicate ** after keyword }); - m.def("test_unpacking_error2", [](py::function f) { - auto kwargs = py::dict("x"_a=3); - return f(**kwargs, "x"_a=1); // duplicate keyword after ** + m.def("test_unpacking_error2", [](const py::function &f) { + auto kwargs = py::dict("x"_a = 3); + return f(**kwargs, "x"_a = 1); // duplicate keyword after ** }); - m.def("test_arg_conversion_error1", [](py::function f) { - f(234, UnregisteredType(), "kw"_a=567); - }); + m.def("test_arg_conversion_error1", + [](const py::function &f) { f(234, UnregisteredType(), "kw"_a = 567); }); - m.def("test_arg_conversion_error2", [](py::function f) { - f(234, "expected_name"_a=UnregisteredType(), "kw"_a=567); + m.def("test_arg_conversion_error2", [](const py::function &f) { + f(234, "expected_name"_a = UnregisteredType(), "kw"_a = 567); }); // test_lambda_closure_cleanup @@ -80,49 +87,99 @@ TEST_SUBMODULE(callbacks, m) { Payload() { print_default_created(this); } ~Payload() { print_destroyed(this); } Payload(const Payload &) { print_copy_created(this); } - Payload(Payload &&) { print_move_created(this); } + Payload(Payload &&) noexcept { print_move_created(this); } }; // Export the payload constructor statistics for testing purposes: m.def("payload_cstats", &ConstructorStats::get); - /* Test cleanup of lambda closure */ - m.def("test_cleanup", []() -> std::function { + m.def("test_lambda_closure_cleanup", []() -> std::function { Payload p; + // In this situation, `Func` in the implementation of + // `cpp_function::initialize` is NOT trivially destructible. return [p]() { /* p should be cleaned up when the returned function is garbage collected */ (void) p; }; }); + class CppCallable { + public: + CppCallable() { track_default_created(this); } + ~CppCallable() { track_destroyed(this); } + CppCallable(const CppCallable &) { track_copy_created(this); } + CppCallable(CppCallable &&) noexcept { track_move_created(this); } + void operator()() {} + }; + + m.def("test_cpp_callable_cleanup", []() { + // Related issue: https://github.com/pybind/pybind11/issues/3228 + // Related PR: https://github.com/pybind/pybind11/pull/3229 + py::list alive_counts; + ConstructorStats &stat = ConstructorStats::get(); + alive_counts.append(stat.alive()); + { + CppCallable cpp_callable; + alive_counts.append(stat.alive()); + { + // In this situation, `Func` in the implementation of + // `cpp_function::initialize` IS trivially destructible, + // only `capture` is not. + py::cpp_function py_func(cpp_callable); + py::detail::silence_unused_warnings(py_func); + alive_counts.append(stat.alive()); + } + alive_counts.append(stat.alive()); + { + py::cpp_function py_func(std::move(cpp_callable)); + py::detail::silence_unused_warnings(py_func); + alive_counts.append(stat.alive()); + } + alive_counts.append(stat.alive()); + } + alive_counts.append(stat.alive()); + return alive_counts; + }); + // test_cpp_function_roundtrip /* Test if passing a function pointer from C++ -> Python -> C++ yields the original pointer */ m.def("dummy_function", &dummy_function); + m.def("dummy_function_overloaded", [](int i, int j) { return i + j; }); + m.def("dummy_function_overloaded", &dummy_function); m.def("dummy_function2", [](int i, int j) { return i + j; }); - m.def("roundtrip", [](std::function f, bool expect_none = false) { - if (expect_none && f) - throw std::runtime_error("Expected None to be converted to empty std::function"); - return f; - }, py::arg("f"), py::arg("expect_none")=false); + m.def( + "roundtrip", + [](std::function f, bool expect_none = false) { + if (expect_none && f) { + throw std::runtime_error("Expected None to be converted to empty std::function"); + } + return f; + }, + py::arg("f"), + py::arg("expect_none") = false); m.def("test_dummy_function", [](const std::function &f) -> std::string { using fn_type = int (*)(int); - auto result = f.target(); + const auto *result = f.target(); if (!result) { auto r = f(1); return "can't convert to function pointer: eval(1) = " + std::to_string(r); - } else if (*result == dummy_function) { + } + if (*result == dummy_function) { auto r = (*result)(1); return "matches dummy_function: eval(1) = " + std::to_string(r); - } else { - return "argument does NOT match dummy_function. This should never happen!"; } + return "argument does NOT match dummy_function. This should never happen!"; }); class AbstractBase { public: - virtual ~AbstractBase() = default; + // [workaround(intel)] = default does not work here + // Defaulting this destructor results in linking errors with the Intel compiler + // (in Debug builds only, tested with icpc (ICC) 2021.1 Beta 20200827) + virtual ~AbstractBase() {} // NOLINT(modernize-use-equals-default) virtual unsigned int func() = 0; }; - m.def("func_accepting_func_accepting_base", [](std::function) { }); + m.def("func_accepting_func_accepting_base", + [](const std::function &) {}); struct MovableObject { bool valid = true; @@ -130,8 +187,8 @@ TEST_SUBMODULE(callbacks, m) { MovableObject() = default; MovableObject(const MovableObject &) = default; MovableObject &operator=(const MovableObject &) = default; - MovableObject(MovableObject &&o) : valid(o.valid) { o.valid = false; } - MovableObject &operator=(MovableObject &&o) { + MovableObject(MovableObject &&o) noexcept : valid(o.valid) { o.valid = false; } + MovableObject &operator=(MovableObject &&o) noexcept { valid = o.valid; o.valid = false; return *this; @@ -140,9 +197,9 @@ TEST_SUBMODULE(callbacks, m) { py::class_(m, "MovableObject"); // test_movable_object - m.def("callback_with_movable", [](std::function f) { + m.def("callback_with_movable", [](const std::function &f) { auto x = MovableObject(); - f(x); // lvalue reference shouldn't move out object + f(x); // lvalue reference shouldn't move out object return x.valid; // must still return `true` }); @@ -152,9 +209,16 @@ TEST_SUBMODULE(callbacks, m) { .def(py::init<>()) .def("triple", [](CppBoundMethodTest &, int val) { return 3 * val; }); + // This checks that builtin functions can be passed as callbacks + // rather than throwing RuntimeError due to trying to extract as capsule + m.def("test_sum_builtin", + [](const std::function &sum_builtin, const py::iterable &i) { + return sum_builtin(i); + }); + // test async Python callbacks using callback_f = std::function; - m.def("test_async_callback", [](callback_f f, py::list work) { + m.def("test_async_callback", [](const callback_f &f, const py::list &work) { // make detached thread that calls `f` with piece of work after a little delay auto start_f = [f](int j) { auto invoke_f = [f, j] { @@ -166,7 +230,14 @@ TEST_SUBMODULE(callbacks, m) { }; // spawn worker threads - for (auto i : work) + for (auto i : work) { start_f(py::cast(i)); + } + }); + + m.def("callback_num_times", [](const py::function &f, std::size_t num) { + for (std::size_t i = 0; i < num; i++) { + f(); + } }); } diff --git a/3rdparty/pybind11/tests/test_callbacks.py b/3rdparty/pybind11/tests/test_callbacks.py index 039b877c..0b1047bb 100644 --- a/3rdparty/pybind11/tests/test_callbacks.py +++ b/3rdparty/pybind11/tests/test_callbacks.py @@ -1,7 +1,10 @@ -# -*- coding: utf-8 -*- +import time +from threading import Thread + import pytest + +import env # noqa: F401 from pybind11_tests import callbacks as m -from threading import Thread def test_callbacks(): @@ -14,7 +17,7 @@ def test_callbacks(): return "func2", a, b, c, d def func3(a): - return "func3({})".format(a) + return f"func3({a})" assert m.test_callback1(func1) == "func1" assert m.test_callback2(func2) == ("func2", "Hello", "x", True, 5) @@ -75,13 +78,18 @@ def test_keyword_args_and_generalized_unpacking(): def test_lambda_closure_cleanup(): - m.test_cleanup() + m.test_lambda_closure_cleanup() cstats = m.payload_cstats() assert cstats.alive() == 0 assert cstats.copy_constructions == 1 assert cstats.move_constructions >= 1 +def test_cpp_callable_cleanup(): + alive_counts = m.test_cpp_callable_cleanup() + assert alive_counts == [0, 1, 2, 1, 2, 1, 0] + + def test_cpp_function_roundtrip(): """Test if passing a function pointer from C++ -> Python -> C++ yields the original pointer""" @@ -92,6 +100,10 @@ def test_cpp_function_roundtrip(): m.test_dummy_function(m.roundtrip(m.dummy_function)) == "matches dummy_function: eval(1) = 2" ) + assert ( + m.test_dummy_function(m.dummy_function_overloaded) + == "matches dummy_function: eval(1) = 2" + ) assert m.roundtrip(None, expect_none=True) is None assert ( m.test_dummy_function(lambda x: x + 2) @@ -119,6 +131,16 @@ def test_movable_object(): assert m.callback_with_movable(lambda _: None) is True +@pytest.mark.skipif( + "env.PYPY", + reason="PyPy segfaults on here. See discussion on #1413.", +) +def test_python_builtins(): + """Test if python builtins like sum() can be used as callbacks""" + assert m.test_sum_builtin(sum, [1, 2, 3]) == 6 + assert m.test_sum_builtin(sum, []) == 0 + + def test_async_callbacks(): # serves as state for async callback class Item: @@ -139,10 +161,35 @@ def test_async_callbacks(): from time import sleep sleep(0.5) - assert sum(res) == sum([x + 3 for x in work]) + assert sum(res) == sum(x + 3 for x in work) def test_async_async_callbacks(): t = Thread(target=test_async_callbacks) t.start() t.join() + + +def test_callback_num_times(): + # Super-simple micro-benchmarking related to PR #2919. + # Example runtimes (Intel Xeon 2.2GHz, fully optimized): + # num_millions 1, repeats 2: 0.1 secs + # num_millions 20, repeats 10: 11.5 secs + one_million = 1000000 + num_millions = 1 # Try 20 for actual micro-benchmarking. + repeats = 2 # Try 10. + rates = [] + for rep in range(repeats): + t0 = time.time() + m.callback_num_times(lambda: None, num_millions * one_million) + td = time.time() - t0 + rate = num_millions / td if td else 0 + rates.append(rate) + if not rep: + print() + print( + f"callback_num_times: {num_millions:d} million / {td:.3f} seconds = {rate:.3f} million / second" + ) + if len(rates) > 1: + print("Min Mean Max") + print(f"{min(rates):6.3f} {sum(rates) / len(rates):6.3f} {max(rates):6.3f}") diff --git a/3rdparty/pybind11/tests/test_chrono.cpp b/3rdparty/pybind11/tests/test_chrono.cpp index 65370508..8be0ffd1 100644 --- a/3rdparty/pybind11/tests/test_chrono.cpp +++ b/3rdparty/pybind11/tests/test_chrono.cpp @@ -8,21 +8,20 @@ BSD-style license that can be found in the LICENSE file. */ -#include "pybind11_tests.h" #include + +#include "pybind11_tests.h" + #include struct different_resolutions { - using time_point_h = std::chrono::time_point< - std::chrono::system_clock, std::chrono::hours>; - using time_point_m = std::chrono::time_point< - std::chrono::system_clock, std::chrono::minutes>; - using time_point_s = std::chrono::time_point< - std::chrono::system_clock, std::chrono::seconds>; - using time_point_ms = std::chrono::time_point< - std::chrono::system_clock, std::chrono::milliseconds>; - using time_point_us = std::chrono::time_point< - std::chrono::system_clock, std::chrono::microseconds>; + using time_point_h = std::chrono::time_point; + using time_point_m = std::chrono::time_point; + using time_point_s = std::chrono::time_point; + using time_point_ms + = std::chrono::time_point; + using time_point_us + = std::chrono::time_point; time_point_h timestamp_h; time_point_m timestamp_m; time_point_s timestamp_s; @@ -65,12 +64,11 @@ TEST_SUBMODULE(chrono, m) { // Roundtrip a duration in microseconds from a float argument m.def("test_chrono7", [](std::chrono::microseconds t) { return t; }); // Float durations (issue #719) - m.def("test_chrono_float_diff", [](std::chrono::duration a, std::chrono::duration b) { - return a - b; }); + m.def("test_chrono_float_diff", + [](std::chrono::duration a, std::chrono::duration b) { return a - b; }); - m.def("test_nano_timepoint", [](timestamp start, timespan delta) -> timestamp { - return start + delta; - }); + m.def("test_nano_timepoint", + [](timestamp start, timespan delta) -> timestamp { return start + delta; }); // Test different resolutions py::class_(m, "different_resolutions") @@ -79,6 +77,5 @@ TEST_SUBMODULE(chrono, m) { .def_readwrite("timestamp_m", &different_resolutions::timestamp_m) .def_readwrite("timestamp_s", &different_resolutions::timestamp_s) .def_readwrite("timestamp_ms", &different_resolutions::timestamp_ms) - .def_readwrite("timestamp_us", &different_resolutions::timestamp_us) - ; + .def_readwrite("timestamp_us", &different_resolutions::timestamp_us); } diff --git a/3rdparty/pybind11/tests/test_chrono.py b/3rdparty/pybind11/tests/test_chrono.py index e9e24e08..7f47b37a 100644 --- a/3rdparty/pybind11/tests/test_chrono.py +++ b/3rdparty/pybind11/tests/test_chrono.py @@ -1,9 +1,9 @@ -# -*- coding: utf-8 -*- -from pybind11_tests import chrono as m import datetime + import pytest import env # noqa: F401 +from pybind11_tests import chrono as m def test_chrono_system_clock(): @@ -39,9 +39,7 @@ def test_chrono_system_clock_roundtrip(): # They should be identical (no information lost on roundtrip) diff = abs(date1 - date2) - assert diff.days == 0 - assert diff.seconds == 0 - assert diff.microseconds == 0 + assert diff == datetime.timedelta(0) def test_chrono_system_clock_roundtrip_date(): @@ -64,9 +62,7 @@ def test_chrono_system_clock_roundtrip_date(): assert diff.microseconds == 0 # Year, Month & Day should be the same after the round trip - assert date1.year == date2.year - assert date1.month == date2.month - assert date1.day == date2.day + assert date1 == date2 # There should be no time information assert time2.hour == 0 @@ -104,7 +100,7 @@ SKIP_TZ_ENV_ON_WIN = pytest.mark.skipif( ) def test_chrono_system_clock_roundtrip_time(time1, tz, monkeypatch): if tz is not None: - monkeypatch.setenv("TZ", "/usr/share/zoneinfo/{}".format(tz)) + monkeypatch.setenv("TZ", f"/usr/share/zoneinfo/{tz}") # Roundtrip the time datetime2 = m.test_chrono2(time1) @@ -117,10 +113,7 @@ def test_chrono_system_clock_roundtrip_time(time1, tz, monkeypatch): assert isinstance(time2, datetime.time) # Hour, Minute, Second & Microsecond should be the same after the round trip - assert time1.hour == time2.hour - assert time1.minute == time2.minute - assert time1.second == time2.second - assert time1.microsecond == time2.microsecond + assert time1 == time2 # There should be no date information (i.e. date = python base date) assert date2.year == 1970 @@ -140,9 +133,13 @@ def test_chrono_duration_roundtrip(): cpp_diff = m.test_chrono3(diff) - assert cpp_diff.days == diff.days - assert cpp_diff.seconds == diff.seconds - assert cpp_diff.microseconds == diff.microseconds + assert cpp_diff == diff + + # Negative timedelta roundtrip + diff = datetime.timedelta(microseconds=-1) + cpp_diff = m.test_chrono3(diff) + + assert cpp_diff == diff def test_chrono_duration_subtraction_equivalence(): @@ -153,9 +150,7 @@ def test_chrono_duration_subtraction_equivalence(): diff = date2 - date1 cpp_diff = m.test_chrono4(date2, date1) - assert cpp_diff.days == diff.days - assert cpp_diff.seconds == diff.seconds - assert cpp_diff.microseconds == diff.microseconds + assert cpp_diff == diff def test_chrono_duration_subtraction_equivalence_date(): @@ -166,9 +161,7 @@ def test_chrono_duration_subtraction_equivalence_date(): diff = date2 - date1 cpp_diff = m.test_chrono4(date2, date1) - assert cpp_diff.days == diff.days - assert cpp_diff.seconds == diff.seconds - assert cpp_diff.microseconds == diff.microseconds + assert cpp_diff == diff def test_chrono_steady_clock(): @@ -183,9 +176,7 @@ def test_chrono_steady_clock_roundtrip(): assert isinstance(time2, datetime.timedelta) # They should be identical (no information lost on roundtrip) - assert time1.days == time2.days - assert time1.seconds == time2.seconds - assert time1.microseconds == time2.microseconds + assert time1 == time2 def test_floating_point_duration(): diff --git a/3rdparty/pybind11/tests/test_class.cpp b/3rdparty/pybind11/tests/test_class.cpp index 890fab73..c8b8071d 100644 --- a/3rdparty/pybind11/tests/test_class.cpp +++ b/3rdparty/pybind11/tests/test_class.cpp @@ -7,18 +7,29 @@ BSD-style license that can be found in the LICENSE file. */ -#include "pybind11_tests.h" +#if defined(__INTEL_COMPILER) && __cplusplus >= 201703L +// Intel compiler requires a separate header file to support aligned new operators +// and does not set the __cpp_aligned_new feature macro. +// This header needs to be included before pybind11. +# include +#endif + +#include + #include "constructor_stats.h" #include "local_bindings.h" -#include +#include "pybind11_tests.h" + +#include #if defined(_MSC_VER) -# pragma warning(disable: 4324) // warning C4324: structure was padded due to alignment specifier +# pragma warning(disable : 4324) +// warning C4324: structure was padded due to alignment specifier #endif // test_brace_initialization struct NoBraceInitialization { - NoBraceInitialization(std::vector v) : vec{std::move(v)} {} + explicit NoBraceInitialization(std::vector v) : vec{std::move(v)} {} template NoBraceInitialization(std::initializer_list l) : vec(l) {} @@ -38,10 +49,26 @@ TEST_SUBMODULE(class_, m) { } ~NoConstructor() { print_destroyed(this); } }; + struct NoConstructorNew { + NoConstructorNew() = default; + NoConstructorNew(const NoConstructorNew &) = default; + NoConstructorNew(NoConstructorNew &&) = default; + static NoConstructorNew *new_instance() { + auto *ptr = new NoConstructorNew(); + print_created(ptr, "via new_instance"); + return ptr; + } + ~NoConstructorNew() { print_destroyed(this); } + }; py::class_(m, "NoConstructor") .def_static("new_instance", &NoConstructor::new_instance, "Return an instance"); + py::class_(m, "NoConstructorNew") + .def(py::init([](const NoConstructorNew &self) { return self; })) // Need a NOOP __init__ + .def_static("__new__", + [](const py::object &) { return NoConstructorNew::new_instance(); }); + // test_inheritance class Pet { public: @@ -49,6 +76,7 @@ TEST_SUBMODULE(class_, m) { : m_name(name), m_species(species) {} std::string name() const { return m_name; } std::string species() const { return m_species; } + private: std::string m_name; std::string m_species; @@ -56,18 +84,18 @@ TEST_SUBMODULE(class_, m) { class Dog : public Pet { public: - Dog(const std::string &name) : Pet(name, "dog") {} + explicit Dog(const std::string &name) : Pet(name, "dog") {} std::string bark() const { return "Woof!"; } }; class Rabbit : public Pet { public: - Rabbit(const std::string &name) : Pet(name, "parrot") {} + explicit Rabbit(const std::string &name) : Pet(name, "parrot") {} }; class Hamster : public Pet { public: - Hamster(const std::string &name) : Pet(name, "rodent") {} + explicit Hamster(const std::string &name) : Pet(name, "rodent") {} }; class Chimera : public Pet { @@ -75,27 +103,24 @@ TEST_SUBMODULE(class_, m) { }; py::class_ pet_class(m, "Pet"); - pet_class - .def(py::init()) + pet_class.def(py::init()) .def("name", &Pet::name) .def("species", &Pet::species); /* One way of declaring a subclass relationship: reference parent's class_ object */ - py::class_(m, "Dog", pet_class) - .def(py::init()); + py::class_(m, "Dog", pet_class).def(py::init()); /* Another way of declaring a subclass relationship: reference parent's C++ type */ - py::class_(m, "Rabbit") - .def(py::init()); + py::class_(m, "Rabbit").def(py::init()); /* And another: list parent in class template arguments */ - py::class_(m, "Hamster") - .def(py::init()); + py::class_(m, "Hamster").def(py::init()); /* Constructors are not inherited by default */ py::class_(m, "Chimera"); - m.def("pet_name_species", [](const Pet &pet) { return pet.name() + " is a " + pet.species(); }); + m.def("pet_name_species", + [](const Pet &pet) { return pet.name() + " is a " + pet.species(); }); m.def("dog_bark", [](const Dog &dog) { return dog.bark(); }); // test_automatic_upcasting @@ -105,33 +130,35 @@ TEST_SUBMODULE(class_, m) { BaseClass(BaseClass &&) = default; virtual ~BaseClass() = default; }; - struct DerivedClass1 : BaseClass { }; - struct DerivedClass2 : BaseClass { }; + struct DerivedClass1 : BaseClass {}; + struct DerivedClass2 : BaseClass {}; py::class_(m, "BaseClass").def(py::init<>()); py::class_(m, "DerivedClass1").def(py::init<>()); py::class_(m, "DerivedClass2").def(py::init<>()); - m.def("return_class_1", []() -> BaseClass* { return new DerivedClass1(); }); - m.def("return_class_2", []() -> BaseClass* { return new DerivedClass2(); }); - m.def("return_class_n", [](int n) -> BaseClass* { - if (n == 1) return new DerivedClass1(); - if (n == 2) return new DerivedClass2(); + m.def("return_class_1", []() -> BaseClass * { return new DerivedClass1(); }); + m.def("return_class_2", []() -> BaseClass * { return new DerivedClass2(); }); + m.def("return_class_n", [](int n) -> BaseClass * { + if (n == 1) { + return new DerivedClass1(); + } + if (n == 2) { + return new DerivedClass2(); + } return new BaseClass(); }); - m.def("return_none", []() -> BaseClass* { return nullptr; }); + m.def("return_none", []() -> BaseClass * { return nullptr; }); // test_isinstance - m.def("check_instances", [](py::list l) { - return py::make_tuple( - py::isinstance(l[0]), - py::isinstance(l[1]), - py::isinstance(l[2]), - py::isinstance(l[3]), - py::isinstance(l[4]), - py::isinstance(l[5]), - py::isinstance(l[6]) - ); + m.def("check_instances", [](const py::list &l) { + return py::make_tuple(py::isinstance(l[0]), + py::isinstance(l[1]), + py::isinstance(l[2]), + py::isinstance(l[3]), + py::isinstance(l[4]), + py::isinstance(l[5]), + py::isinstance(l[6])); }); struct Invalid {}; @@ -142,30 +169,24 @@ TEST_SUBMODULE(class_, m) { // See https://github.com/pybind/pybind11/issues/2486 // if (category == 2) // return py::type::of(); - if (category == 1) + if (category == 1) { return py::type::of(); - else - return py::type::of(); + } + return py::type::of(); }); - m.def("get_type_of", [](py::object ob) { - return py::type::of(ob); - }); + m.def("get_type_of", [](py::object ob) { return py::type::of(std::move(ob)); }); - m.def("get_type_classic", [](py::handle h) { - return h.get_type(); - }); + m.def("get_type_classic", [](py::handle h) { return h.get_type(); }); - m.def("as_type", [](py::object ob) { - return py::type(ob); - }); + m.def("as_type", [](const py::object &ob) { return py::type(ob); }); // test_mismatched_holder - struct MismatchBase1 { }; - struct MismatchDerived1 : MismatchBase1 { }; + struct MismatchBase1 {}; + struct MismatchDerived1 : MismatchBase1 {}; - struct MismatchBase2 { }; - struct MismatchDerived2 : MismatchBase2 { }; + struct MismatchBase2 {}; + struct MismatchDerived2 : MismatchBase2 {}; m.def("mismatched_holder_1", []() { auto mod = py::module_::import("__main__"); @@ -175,16 +196,14 @@ TEST_SUBMODULE(class_, m) { m.def("mismatched_holder_2", []() { auto mod = py::module_::import("__main__"); py::class_(mod, "MismatchBase2"); - py::class_, - MismatchBase2>(mod, "MismatchDerived2"); + py::class_, MismatchBase2>( + mod, "MismatchDerived2"); }); // test_override_static // #511: problem with inheritance + overwritten def_static struct MyBase { - static std::unique_ptr make() { - return std::unique_ptr(new MyBase()); - } + static std::unique_ptr make() { return std::unique_ptr(new MyBase()); } }; struct MyDerived : MyBase { @@ -193,8 +212,7 @@ TEST_SUBMODULE(class_, m) { } }; - py::class_(m, "MyBase") - .def_static("make", &MyBase::make); + py::class_(m, "MyBase").def_static("make", &MyBase::make); py::class_(m, "MyDerived") .def_static("make", &MyDerived::make) @@ -204,15 +222,14 @@ TEST_SUBMODULE(class_, m) { struct ConvertibleFromUserType { int i; - ConvertibleFromUserType(UserType u) : i(u.value()) { } + explicit ConvertibleFromUserType(UserType u) : i(u.value()) {} }; - py::class_(m, "AcceptsUserType") - .def(py::init()); + py::class_(m, "AcceptsUserType").def(py::init()); py::implicitly_convertible(); m.def("implicitly_convert_argument", [](const ConvertibleFromUserType &r) { return r.i; }); - m.def("implicitly_convert_variable", [](py::object o) { + m.def("implicitly_convert_variable", [](const py::object &o) { // `o` is `UserType` and `r` is a reference to a temporary created by implicit // conversion. This is valid when called inside a bound function because the temp // object is attached to the same life support system as the arguments. @@ -230,48 +247,91 @@ TEST_SUBMODULE(class_, m) { return py::str().release().ptr(); }; - auto def = new PyMethodDef{"f", f, METH_VARARGS, nullptr}; - return py::reinterpret_steal(PyCFunction_NewEx(def, nullptr, m.ptr())); + auto *def = new PyMethodDef{"f", f, METH_VARARGS, nullptr}; + py::capsule def_capsule(def, + [](void *ptr) { delete reinterpret_cast(ptr); }); + return py::reinterpret_steal( + PyCFunction_NewEx(def, def_capsule.ptr(), m.ptr())); }()); // test_operator_new_delete struct HasOpNewDel { std::uint64_t i; - static void *operator new(size_t s) { py::print("A new", s); return ::operator new(s); } - static void *operator new(size_t s, void *ptr) { py::print("A placement-new", s); return ptr; } - static void operator delete(void *p) { py::print("A delete"); return ::operator delete(p); } + static void *operator new(size_t s) { + py::print("A new", s); + return ::operator new(s); + } + static void *operator new(size_t s, void *ptr) { + py::print("A placement-new", s); + return ptr; + } + static void operator delete(void *p) { + py::print("A delete"); + return ::operator delete(p); + } }; struct HasOpNewDelSize { std::uint32_t i; - static void *operator new(size_t s) { py::print("B new", s); return ::operator new(s); } - static void *operator new(size_t s, void *ptr) { py::print("B placement-new", s); return ptr; } - static void operator delete(void *p, size_t s) { py::print("B delete", s); return ::operator delete(p); } + static void *operator new(size_t s) { + py::print("B new", s); + return ::operator new(s); + } + static void *operator new(size_t s, void *ptr) { + py::print("B placement-new", s); + return ptr; + } + static void operator delete(void *p, size_t s) { + py::print("B delete", s); + return ::operator delete(p); + } }; struct AliasedHasOpNewDelSize { std::uint64_t i; - static void *operator new(size_t s) { py::print("C new", s); return ::operator new(s); } - static void *operator new(size_t s, void *ptr) { py::print("C placement-new", s); return ptr; } - static void operator delete(void *p, size_t s) { py::print("C delete", s); return ::operator delete(p); } + static void *operator new(size_t s) { + py::print("C new", s); + return ::operator new(s); + } + static void *operator new(size_t s, void *ptr) { + py::print("C placement-new", s); + return ptr; + } + static void operator delete(void *p, size_t s) { + py::print("C delete", s); + return ::operator delete(p); + } virtual ~AliasedHasOpNewDelSize() = default; AliasedHasOpNewDelSize() = default; - AliasedHasOpNewDelSize(const AliasedHasOpNewDelSize&) = delete; + AliasedHasOpNewDelSize(const AliasedHasOpNewDelSize &) = delete; }; struct PyAliasedHasOpNewDelSize : AliasedHasOpNewDelSize { PyAliasedHasOpNewDelSize() = default; - PyAliasedHasOpNewDelSize(int) { } + explicit PyAliasedHasOpNewDelSize(int) {} std::uint64_t j; }; struct HasOpNewDelBoth { std::uint32_t i[8]; - static void *operator new(size_t s) { py::print("D new", s); return ::operator new(s); } - static void *operator new(size_t s, void *ptr) { py::print("D placement-new", s); return ptr; } - static void operator delete(void *p) { py::print("D delete"); return ::operator delete(p); } - static void operator delete(void *p, size_t s) { py::print("D wrong delete", s); return ::operator delete(p); } + static void *operator new(size_t s) { + py::print("D new", s); + return ::operator new(s); + } + static void *operator new(size_t s, void *ptr) { + py::print("D placement-new", s); + return ptr; + } + static void operator delete(void *p) { + py::print("D delete"); + return ::operator delete(p); + } + static void operator delete(void *p, size_t s) { + py::print("D wrong delete", s); + return ::operator delete(p); + } }; py::class_(m, "HasOpNewDel").def(py::init<>()); py::class_(m, "HasOpNewDelSize").def(py::init<>()); py::class_(m, "HasOpNewDelBoth").def(py::init<>()); - py::class_ aliased(m, "AliasedHasOpNewDelSize"); + py::class_ aliased(m, + "AliasedHasOpNewDelSize"); aliased.def(py::init<>()); aliased.attr("size_noalias") = py::int_(sizeof(AliasedHasOpNewDelSize)); aliased.attr("size_alias") = py::int_(sizeof(PyAliasedHasOpNewDelSize)); @@ -294,13 +354,7 @@ TEST_SUBMODULE(class_, m) { using ProtectedA::foo; }; - py::class_(m, "ProtectedA") - .def(py::init<>()) -#if !defined(_MSC_VER) || _MSC_VER >= 1910 - .def("foo", &PublicistA::foo); -#else - .def("foo", static_cast(&PublicistA::foo)); -#endif + py::class_(m, "ProtectedA").def(py::init<>()).def("foo", &PublicistA::foo); class ProtectedB { public: @@ -322,16 +376,16 @@ TEST_SUBMODULE(class_, m) { class PublicistB : public ProtectedB { public: + // [workaround(intel)] = default does not work here + // Removing or defaulting this destructor results in linking errors with the Intel compiler + // (in Debug builds only, tested with icpc (ICC) 2021.1 Beta 20200827) + ~PublicistB() override{}; // NOLINT(modernize-use-equals-default) using ProtectedB::foo; }; py::class_(m, "ProtectedB") .def(py::init<>()) -#if !defined(_MSC_VER) || _MSC_VER >= 1910 .def("foo", &PublicistB::foo); -#else - .def("foo", static_cast(&PublicistB::foo)); -#endif // test_brace_initialization struct BraceInitialization { @@ -371,8 +425,8 @@ TEST_SUBMODULE(class_, m) { py::class_(base, "Nested") .def(py::init<>()) .def("fn", [](Nested &, int, NestBase &, Nested &) {}) - .def("fa", [](Nested &, int, NestBase &, Nested &) {}, - "a"_a, "b"_a, "c"_a); + .def( + "fa", [](Nested &, int, NestBase &, Nested &) {}, "a"_a, "b"_a, "c"_a); base.def("g", [](NestBase &, Nested &) {}); base.def("h", []() { return NestBase(); }); @@ -382,21 +436,21 @@ TEST_SUBMODULE(class_, m) { // generate a useful error message struct NotRegistered {}; - struct StringWrapper { std::string str; }; + struct StringWrapper { + std::string str; + }; m.def("test_error_after_conversions", [](int) {}); m.def("test_error_after_conversions", - [](StringWrapper) -> NotRegistered { return {}; }); + [](const StringWrapper &) -> NotRegistered { return {}; }); py::class_(m, "StringWrapper").def(py::init()); py::implicitly_convertible(); - #if defined(PYBIND11_CPP17) - struct alignas(1024) Aligned { - std::uintptr_t ptr() const { return (uintptr_t) this; } - }; - py::class_(m, "Aligned") - .def(py::init<>()) - .def("ptr", &Aligned::ptr); - #endif +#if defined(PYBIND11_CPP17) + struct alignas(1024) Aligned { + std::uintptr_t ptr() const { return (uintptr_t) this; } + }; + py::class_(m, "Aligned").def(py::init<>()).def("ptr", &Aligned::ptr); +#endif // test_final struct IsFinal final {}; @@ -409,9 +463,7 @@ TEST_SUBMODULE(class_, m) { // test_exception_rvalue_abort struct PyPrintDestructor { PyPrintDestructor() = default; - ~PyPrintDestructor() { - py::print("Print from destructor"); - } + ~PyPrintDestructor() { py::print("Print from destructor"); } void throw_something() { throw std::runtime_error("error"); } }; py::class_(m, "PyPrintDestructor") @@ -422,12 +474,10 @@ TEST_SUBMODULE(class_, m) { struct SamePointer {}; static SamePointer samePointer; py::class_>(m, "SamePointer") - .def(py::init([]() { return &samePointer; })) - .def("__del__", [](SamePointer&) { py::print("__del__ called"); }); + .def(py::init([]() { return &samePointer; })); struct Empty {}; - py::class_(m, "Empty") - .def(py::init<>()); + py::class_(m, "Empty").def(py::init<>()); // test_base_and_derived_nested_scope struct BaseWithNested { @@ -450,30 +500,34 @@ TEST_SUBMODULE(class_, m) { struct OtherDuplicate {}; struct DuplicateNested {}; struct OtherDuplicateNested {}; - m.def("register_duplicate_class_name", [](py::module_ m) { + + m.def("register_duplicate_class_name", [](const py::module_ &m) { py::class_(m, "Duplicate"); py::class_(m, "Duplicate"); }); - m.def("register_duplicate_class_type", [](py::module_ m) { + m.def("register_duplicate_class_type", [](const py::module_ &m) { py::class_(m, "OtherDuplicate"); py::class_(m, "YetAnotherDuplicate"); }); - m.def("register_duplicate_nested_class_name", [](py::object gt) { + m.def("register_duplicate_nested_class_name", [](const py::object >) { py::class_(gt, "DuplicateNested"); py::class_(gt, "DuplicateNested"); }); - m.def("register_duplicate_nested_class_type", [](py::object gt) { + m.def("register_duplicate_nested_class_type", [](const py::object >) { py::class_(gt, "OtherDuplicateNested"); py::class_(gt, "YetAnotherDuplicateNested"); }); } -template class BreaksBase { public: +template +class BreaksBase { +public: virtual ~BreaksBase() = default; BreaksBase() = default; - BreaksBase(const BreaksBase&) = delete; + BreaksBase(const BreaksBase &) = delete; }; -template class BreaksTramp : public BreaksBase {}; +template +class BreaksTramp : public BreaksBase {}; // These should all compile just fine: using DoesntBreak1 = py::class_, std::unique_ptr>, BreaksTramp<1>>; using DoesntBreak2 = py::class_, BreaksTramp<2>, std::unique_ptr>>; @@ -483,43 +537,83 @@ using DoesntBreak5 = py::class_>; using DoesntBreak6 = py::class_, std::shared_ptr>, BreaksTramp<6>>; using DoesntBreak7 = py::class_, BreaksTramp<7>, std::shared_ptr>>; using DoesntBreak8 = py::class_, std::shared_ptr>>; -#define CHECK_BASE(N) static_assert(std::is_same>::value, \ - "DoesntBreak" #N " has wrong type!") -CHECK_BASE(1); CHECK_BASE(2); CHECK_BASE(3); CHECK_BASE(4); CHECK_BASE(5); CHECK_BASE(6); CHECK_BASE(7); CHECK_BASE(8); -#define CHECK_ALIAS(N) static_assert(DoesntBreak##N::has_alias && std::is_same>::value, \ +#define CHECK_BASE(N) \ + static_assert(std::is_same>::value, \ + "DoesntBreak" #N " has wrong type!") +CHECK_BASE(1); +CHECK_BASE(2); +CHECK_BASE(3); +CHECK_BASE(4); +CHECK_BASE(5); +CHECK_BASE(6); +CHECK_BASE(7); +CHECK_BASE(8); +#define CHECK_ALIAS(N) \ + static_assert( \ + DoesntBreak##N::has_alias \ + && std::is_same>::value, \ "DoesntBreak" #N " has wrong type_alias!") -#define CHECK_NOALIAS(N) static_assert(!DoesntBreak##N::has_alias && std::is_void::value, \ - "DoesntBreak" #N " has type alias, but shouldn't!") -CHECK_ALIAS(1); CHECK_ALIAS(2); CHECK_NOALIAS(3); CHECK_ALIAS(4); CHECK_NOALIAS(5); CHECK_ALIAS(6); CHECK_ALIAS(7); CHECK_NOALIAS(8); -#define CHECK_HOLDER(N, TYPE) static_assert(std::is_same>>::value, \ - "DoesntBreak" #N " has wrong holder_type!") -CHECK_HOLDER(1, unique); CHECK_HOLDER(2, unique); CHECK_HOLDER(3, unique); CHECK_HOLDER(4, unique); CHECK_HOLDER(5, unique); -CHECK_HOLDER(6, shared); CHECK_HOLDER(7, shared); CHECK_HOLDER(8, shared); +#define CHECK_NOALIAS(N) \ + static_assert(!DoesntBreak##N::has_alias \ + && std::is_void::value, \ + "DoesntBreak" #N " has type alias, but shouldn't!") +CHECK_ALIAS(1); +CHECK_ALIAS(2); +CHECK_NOALIAS(3); +CHECK_ALIAS(4); +CHECK_NOALIAS(5); +CHECK_ALIAS(6); +CHECK_ALIAS(7); +CHECK_NOALIAS(8); +#define CHECK_HOLDER(N, TYPE) \ + static_assert(std::is_same>>::value, \ + "DoesntBreak" #N " has wrong holder_type!") +CHECK_HOLDER(1, unique); +CHECK_HOLDER(2, unique); +CHECK_HOLDER(3, unique); +CHECK_HOLDER(4, unique); +CHECK_HOLDER(5, unique); +CHECK_HOLDER(6, shared); +CHECK_HOLDER(7, shared); +CHECK_HOLDER(8, shared); // There's no nice way to test that these fail because they fail to compile; leave them here, // though, so that they can be manually tested by uncommenting them (and seeing that compilation // failures occurs). // We have to actually look into the type: the typedef alone isn't enough to instantiate the type: -#define CHECK_BROKEN(N) static_assert(std::is_same>::value, \ - "Breaks1 has wrong type!"); - -//// Two holder classes: -//typedef py::class_, std::unique_ptr>, std::unique_ptr>> Breaks1; -//CHECK_BROKEN(1); -//// Two aliases: -//typedef py::class_, BreaksTramp<-2>, BreaksTramp<-2>> Breaks2; -//CHECK_BROKEN(2); -//// Holder + 2 aliases -//typedef py::class_, std::unique_ptr>, BreaksTramp<-3>, BreaksTramp<-3>> Breaks3; -//CHECK_BROKEN(3); -//// Alias + 2 holders -//typedef py::class_, std::unique_ptr>, BreaksTramp<-4>, std::shared_ptr>> Breaks4; -//CHECK_BROKEN(4); -//// Invalid option (not a subclass or holder) -//typedef py::class_, BreaksTramp<-4>> Breaks5; -//CHECK_BROKEN(5); -//// Invalid option: multiple inheritance not supported: -//template <> struct BreaksBase<-8> : BreaksBase<-6>, BreaksBase<-7> {}; -//typedef py::class_, BreaksBase<-6>, BreaksBase<-7>> Breaks8; -//CHECK_BROKEN(8); +#define CHECK_BROKEN(N) \ + static_assert(std::is_same>::value, \ + "Breaks1 has wrong type!"); + +#ifdef PYBIND11_NEVER_DEFINED_EVER +// Two holder classes: +typedef py:: + class_, std::unique_ptr>, std::unique_ptr>> + Breaks1; +CHECK_BROKEN(1); +// Two aliases: +typedef py::class_, BreaksTramp<-2>, BreaksTramp<-2>> Breaks2; +CHECK_BROKEN(2); +// Holder + 2 aliases +typedef py:: + class_, std::unique_ptr>, BreaksTramp<-3>, BreaksTramp<-3>> + Breaks3; +CHECK_BROKEN(3); +// Alias + 2 holders +typedef py::class_, + std::unique_ptr>, + BreaksTramp<-4>, + std::shared_ptr>> + Breaks4; +CHECK_BROKEN(4); +// Invalid option (not a subclass or holder) +typedef py::class_, BreaksTramp<-4>> Breaks5; +CHECK_BROKEN(5); +// Invalid option: multiple inheritance not supported: +template <> +struct BreaksBase<-8> : BreaksBase<-6>, BreaksBase<-7> {}; +typedef py::class_, BreaksBase<-6>, BreaksBase<-7>> Breaks8; +CHECK_BROKEN(8); +#endif diff --git a/3rdparty/pybind11/tests/test_class.py b/3rdparty/pybind11/tests/test_class.py index bdcced96..ff9196f0 100644 --- a/3rdparty/pybind11/tests/test_class.py +++ b/3rdparty/pybind11/tests/test_class.py @@ -1,14 +1,11 @@ -# -*- coding: utf-8 -*- import pytest import env # noqa: F401 - +from pybind11_tests import ConstructorStats, UserType 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) @@ -26,6 +23,14 @@ def test_instance(msg): assert cstats.alive() == 0 +def test_instance_new(msg): + instance = m.NoConstructorNew() # .__new__(m.NoConstructor.__class__) + cstats = ConstructorStats.get(m.NoConstructorNew) + 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: @@ -96,8 +101,8 @@ def test_docstrings(doc): 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""" + """Tests that a properly qualified name is set in __qualname__ and that + generated docstrings properly use it and the module name""" assert m.NestBase.__qualname__ == "NestBase" assert m.NestBase.Nested.__qualname__ == "NestBase.Nested" @@ -123,13 +128,13 @@ def test_qualname(doc): 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_" @@ -321,7 +326,7 @@ def test_bind_protected_functions(): def test_brace_initialization(): - """ Tests that simple POD classes can be constructed using C++11 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" diff --git a/3rdparty/pybind11/tests/test_cmake_build/CMakeLists.txt b/3rdparty/pybind11/tests/test_cmake_build/CMakeLists.txt index 0c0578ad..8bfaa386 100644 --- a/3rdparty/pybind11/tests/test_cmake_build/CMakeLists.txt +++ b/3rdparty/pybind11/tests/test_cmake_build/CMakeLists.txt @@ -25,7 +25,7 @@ function(pybind11_add_build_test name) endif() if(NOT ARG_INSTALL) - list(APPEND build_options "-DPYBIND11_PROJECT_DIR=${pybind11_SOURCE_DIR}") + list(APPEND build_options "-Dpybind11_SOURCE_DIR=${pybind11_SOURCE_DIR}") else() list(APPEND build_options "-DCMAKE_PREFIX_PATH=${pybind11_BINARY_DIR}/mock_install") endif() @@ -55,6 +55,8 @@ function(pybind11_add_build_test name) add_dependencies(test_cmake_build test_build_${name}) endfunction() +possibly_uninitialized(PYTHON_MODULE_EXTENSION Python_INTERPRETER_ID) + pybind11_add_build_test(subdirectory_function) pybind11_add_build_test(subdirectory_target) if("${PYTHON_MODULE_EXTENSION}" MATCHES "pypy" OR "${Python_INTERPRETER_ID}" STREQUAL "PyPy") @@ -77,3 +79,6 @@ if(PYBIND11_INSTALL) endif() add_dependencies(check test_cmake_build) + +add_subdirectory(subdirectory_target EXCLUDE_FROM_ALL) +add_subdirectory(subdirectory_embed EXCLUDE_FROM_ALL) diff --git a/3rdparty/pybind11/tests/test_cmake_build/embed.cpp b/3rdparty/pybind11/tests/test_cmake_build/embed.cpp index a3abc8a8..30bc4f1e 100644 --- a/3rdparty/pybind11/tests/test_cmake_build/embed.cpp +++ b/3rdparty/pybind11/tests/test_cmake_build/embed.cpp @@ -6,15 +6,17 @@ PYBIND11_EMBEDDED_MODULE(test_cmake_build, m) { } int main(int argc, char *argv[]) { - if (argc != 2) + if (argc != 2) { throw std::runtime_error("Expected test.py file as the first argument"); - auto test_py_file = argv[1]; + } + auto *test_py_file = argv[1]; py::scoped_interpreter guard{}; auto m = py::module_::import("test_cmake_build"); - if (m.attr("add")(1, 2).cast() != 3) + if (m.attr("add")(1, 2).cast() != 3) { throw std::runtime_error("embed.cpp failed"); + } py::module_::import("sys").attr("argv") = py::make_tuple("test.py", "embed.cpp"); py::eval_file(test_py_file, py::globals()); diff --git a/3rdparty/pybind11/tests/test_cmake_build/installed_embed/CMakeLists.txt b/3rdparty/pybind11/tests/test_cmake_build/installed_embed/CMakeLists.txt index 64ae5c4b..f7d69399 100644 --- a/3rdparty/pybind11/tests/test_cmake_build/installed_embed/CMakeLists.txt +++ b/3rdparty/pybind11/tests/test_cmake_build/installed_embed/CMakeLists.txt @@ -22,5 +22,7 @@ set_target_properties(test_installed_embed PROPERTIES OUTPUT_NAME test_cmake_bui # This may be needed to resolve header conflicts, e.g. between Python release and debug headers. set_target_properties(test_installed_embed PROPERTIES NO_SYSTEM_FROM_IMPORTED ON) -add_custom_target(check_installed_embed $ - ${PROJECT_SOURCE_DIR}/../test.py) +add_custom_target( + check_installed_embed + $ ${PROJECT_SOURCE_DIR}/../test.py + DEPENDS test_installed_embed) diff --git a/3rdparty/pybind11/tests/test_cmake_build/installed_function/CMakeLists.txt b/3rdparty/pybind11/tests/test_cmake_build/installed_function/CMakeLists.txt index 1a502863..d7ca4db5 100644 --- a/3rdparty/pybind11/tests/test_cmake_build/installed_function/CMakeLists.txt +++ b/3rdparty/pybind11/tests/test_cmake_build/installed_function/CMakeLists.txt @@ -35,4 +35,5 @@ add_custom_target( PYTHONPATH=$ ${_Python_EXECUTABLE} ${PROJECT_SOURCE_DIR}/../test.py - ${PROJECT_NAME}) + ${PROJECT_NAME} + DEPENDS test_installed_function) diff --git a/3rdparty/pybind11/tests/test_cmake_build/installed_target/CMakeLists.txt b/3rdparty/pybind11/tests/test_cmake_build/installed_target/CMakeLists.txt index b38eb774..bc5e101f 100644 --- a/3rdparty/pybind11/tests/test_cmake_build/installed_target/CMakeLists.txt +++ b/3rdparty/pybind11/tests/test_cmake_build/installed_target/CMakeLists.txt @@ -42,4 +42,5 @@ add_custom_target( PYTHONPATH=$ ${_Python_EXECUTABLE} ${PROJECT_SOURCE_DIR}/../test.py - ${PROJECT_NAME}) + ${PROJECT_NAME} + DEPENDS test_installed_target) diff --git a/3rdparty/pybind11/tests/test_cmake_build/subdirectory_embed/CMakeLists.txt b/3rdparty/pybind11/tests/test_cmake_build/subdirectory_embed/CMakeLists.txt index c7df0cf7..58cdd7cf 100644 --- a/3rdparty/pybind11/tests/test_cmake_build/subdirectory_embed/CMakeLists.txt +++ b/3rdparty/pybind11/tests/test_cmake_build/subdirectory_embed/CMakeLists.txt @@ -16,15 +16,17 @@ set(PYBIND11_INSTALL CACHE BOOL "") set(PYBIND11_EXPORT_NAME test_export) -add_subdirectory(${PYBIND11_PROJECT_DIR} pybind11) +add_subdirectory("${pybind11_SOURCE_DIR}" pybind11) # Test basic target functionality add_executable(test_subdirectory_embed ../embed.cpp) target_link_libraries(test_subdirectory_embed PRIVATE pybind11::embed) set_target_properties(test_subdirectory_embed PROPERTIES OUTPUT_NAME test_cmake_build) -add_custom_target(check_subdirectory_embed $ - ${PROJECT_SOURCE_DIR}/../test.py) +add_custom_target( + check_subdirectory_embed + $ "${PROJECT_SOURCE_DIR}/../test.py" + DEPENDS test_subdirectory_embed) # Test custom export group -- PYBIND11_EXPORT_NAME add_library(test_embed_lib ../embed.cpp) diff --git a/3rdparty/pybind11/tests/test_cmake_build/subdirectory_function/CMakeLists.txt b/3rdparty/pybind11/tests/test_cmake_build/subdirectory_function/CMakeLists.txt index 624c600f..01557c43 100644 --- a/3rdparty/pybind11/tests/test_cmake_build/subdirectory_function/CMakeLists.txt +++ b/3rdparty/pybind11/tests/test_cmake_build/subdirectory_function/CMakeLists.txt @@ -11,7 +11,7 @@ endif() project(test_subdirectory_function CXX) -add_subdirectory("${PYBIND11_PROJECT_DIR}" pybind11) +add_subdirectory("${pybind11_SOURCE_DIR}" pybind11) pybind11_add_module(test_subdirectory_function ../main.cpp) set_target_properties(test_subdirectory_function PROPERTIES OUTPUT_NAME test_cmake_build) @@ -31,4 +31,5 @@ add_custom_target( PYTHONPATH=$ ${_Python_EXECUTABLE} ${PROJECT_SOURCE_DIR}/../test.py - ${PROJECT_NAME}) + ${PROJECT_NAME} + DEPENDS test_subdirectory_function) diff --git a/3rdparty/pybind11/tests/test_cmake_build/subdirectory_target/CMakeLists.txt b/3rdparty/pybind11/tests/test_cmake_build/subdirectory_target/CMakeLists.txt index 2471941f..ba82fdee 100644 --- a/3rdparty/pybind11/tests/test_cmake_build/subdirectory_target/CMakeLists.txt +++ b/3rdparty/pybind11/tests/test_cmake_build/subdirectory_target/CMakeLists.txt @@ -11,7 +11,7 @@ endif() project(test_subdirectory_target CXX) -add_subdirectory(${PYBIND11_PROJECT_DIR} pybind11) +add_subdirectory("${pybind11_SOURCE_DIR}" pybind11) add_library(test_subdirectory_target MODULE ../main.cpp) set_target_properties(test_subdirectory_target PROPERTIES OUTPUT_NAME test_cmake_build) @@ -37,4 +37,5 @@ add_custom_target( PYTHONPATH=$ ${_Python_EXECUTABLE} ${PROJECT_SOURCE_DIR}/../test.py - ${PROJECT_NAME}) + ${PROJECT_NAME} + DEPENDS test_subdirectory_target) diff --git a/3rdparty/pybind11/tests/test_cmake_build/test.py b/3rdparty/pybind11/tests/test_cmake_build/test.py index 87ed5135..807fd43b 100644 --- a/3rdparty/pybind11/tests/test_cmake_build/test.py +++ b/3rdparty/pybind11/tests/test_cmake_build/test.py @@ -1,6 +1,8 @@ -# -*- coding: utf-8 -*- import sys + import test_cmake_build +assert isinstance(__file__, str) # Test this is properly set + assert test_cmake_build.add(1, 2) == 3 -print("{} imports, runs, and adds: 1 + 2 = 3".format(sys.argv[1])) +print(f"{sys.argv[1]} imports, runs, and adds: 1 + 2 = 3") diff --git a/3rdparty/pybind11/tests/test_const_name.cpp b/3rdparty/pybind11/tests/test_const_name.cpp new file mode 100644 index 00000000..2ad01e68 --- /dev/null +++ b/3rdparty/pybind11/tests/test_const_name.cpp @@ -0,0 +1,55 @@ +// Copyright (c) 2021 The Pybind Development Team. +// All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include "pybind11_tests.h" + +// IUT = Implementation Under Test +#define CONST_NAME_TESTS(TEST_FUNC, IUT) \ + std::string TEST_FUNC(int selector) { \ + switch (selector) { \ + case 0: \ + return IUT("").text; \ + case 1: \ + return IUT("A").text; \ + case 2: \ + return IUT("Bd").text; \ + case 3: \ + return IUT("Cef").text; \ + case 4: \ + return IUT().text; /*NOLINT(bugprone-macro-parentheses)*/ \ + case 5: \ + return IUT().text; /*NOLINT(bugprone-macro-parentheses)*/ \ + case 6: \ + return IUT("T1", "T2").text; /*NOLINT(bugprone-macro-parentheses)*/ \ + case 7: \ + return IUT("U1", "U2").text; /*NOLINT(bugprone-macro-parentheses)*/ \ + case 8: \ + /*NOLINTNEXTLINE(bugprone-macro-parentheses)*/ \ + return IUT(IUT("D1"), IUT("D2")).text; \ + case 9: \ + /*NOLINTNEXTLINE(bugprone-macro-parentheses)*/ \ + return IUT(IUT("E1"), IUT("E2")).text; \ + case 10: \ + return IUT("KeepAtEnd").text; \ + default: \ + break; \ + } \ + throw std::runtime_error("Invalid selector value."); \ + } + +CONST_NAME_TESTS(const_name_tests, py::detail::const_name) + +#ifdef PYBIND11_DETAIL_UNDERSCORE_BACKWARD_COMPATIBILITY +CONST_NAME_TESTS(underscore_tests, py::detail::_) +#endif + +TEST_SUBMODULE(const_name, m) { + m.def("const_name_tests", const_name_tests); + +#if defined(PYBIND11_DETAIL_UNDERSCORE_BACKWARD_COMPATIBILITY) + m.def("underscore_tests", underscore_tests); +#else + m.attr("underscore_tests") = "PYBIND11_DETAIL_UNDERSCORE_BACKWARD_COMPATIBILITY not defined."; +#endif +} diff --git a/3rdparty/pybind11/tests/test_const_name.py b/3rdparty/pybind11/tests/test_const_name.py new file mode 100644 index 00000000..10b0caee --- /dev/null +++ b/3rdparty/pybind11/tests/test_const_name.py @@ -0,0 +1,29 @@ +import pytest + +from pybind11_tests import const_name as m + + +@pytest.mark.parametrize("func", (m.const_name_tests, m.underscore_tests)) +@pytest.mark.parametrize( + "selector, expected", + enumerate( + ( + "", + "A", + "Bd", + "Cef", + "%", + "%", + "T1", + "U2", + "D1", + "E2", + "KeepAtEnd", + ) + ), +) +def test_const_name(func, selector, expected): + if isinstance(func, str): + pytest.skip(func) + text = func(selector) + assert text == expected diff --git a/3rdparty/pybind11/tests/test_constants_and_functions.cpp b/3rdparty/pybind11/tests/test_constants_and_functions.cpp index f6077955..1918a429 100644 --- a/3rdparty/pybind11/tests/test_constants_and_functions.cpp +++ b/3rdparty/pybind11/tests/test_constants_and_functions.cpp @@ -1,5 +1,6 @@ /* - tests/test_constants_and_functions.cpp -- global constants and functions, enumerations, raw byte strings + tests/test_constants_and_functions.cpp -- global constants and functions, enumerations, raw + byte strings Copyright (c) 2016 Wenzel Jakob @@ -11,20 +12,14 @@ enum MyEnum { EFirstEntry = 1, ESecondEntry }; -std::string test_function1() { - return "test_function()"; -} +std::string test_function1() { return "test_function()"; } -std::string test_function2(MyEnum k) { - return "test_function(enum=" + std::to_string(k) + ")"; -} +std::string test_function2(MyEnum k) { return "test_function(enum=" + std::to_string(k) + ")"; } -std::string test_function3(int i) { - return "test_function(" + std::to_string(i) + ")"; -} +std::string test_function3(int i) { return "test_function(" + std::to_string(i) + ")"; } -py::str test_function4() { return "test_function()"; } -py::str test_function4(char *) { return "test_function(char *)"; } +py::str test_function4() { return "test_function()"; } +py::str test_function4(char *) { return "test_function(char *)"; } py::str test_function4(int, float) { return "test_function(int, float)"; } py::str test_function4(float, int) { return "test_function(float, int)"; } @@ -33,50 +28,60 @@ py::bytes return_bytes() { return std::string(data, 4); } -std::string print_bytes(py::bytes bytes) { +std::string print_bytes(const py::bytes &bytes) { std::string ret = "bytes["; const auto value = static_cast(bytes); - for (size_t i = 0; i < value.length(); ++i) { - ret += std::to_string(static_cast(value[i])) + " "; + for (char c : value) { + ret += std::to_string(static_cast(c)) + ' '; } ret.back() = ']'; return ret; } -// Test that we properly handle C++17 exception specifiers (which are part of the function signature -// in C++17). These should all still work before C++17, but don't affect the function signature. +// Test that we properly handle C++17 exception specifiers (which are part of the function +// signature in C++17). These should all still work before C++17, but don't affect the function +// signature. namespace test_exc_sp { -int f1(int x) noexcept { return x+1; } -int f2(int x) noexcept(true) { return x+2; } -int f3(int x) noexcept(false) { return x+3; } -#if defined(__GNUG__) -# pragma GCC diagnostic push -# pragma GCC diagnostic ignored "-Wdeprecated" +// [workaround(intel)] Unable to use noexcept instead of noexcept(true) +// Make the f1 test basically the same as the f2 test in C++17 mode for the Intel compiler as +// it fails to compile with a plain noexcept (tested with icc (ICC) 2021.1 Beta 20200827). +#if defined(__INTEL_COMPILER) && defined(PYBIND11_CPP17) +int f1(int x) noexcept(true) { return x + 1; } +#else +int f1(int x) noexcept { return x + 1; } +#endif +int f2(int x) noexcept(true) { return x + 2; } +int f3(int x) noexcept(false) { return x + 3; } +#if defined(__GNUG__) && !defined(__INTEL_COMPILER) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wdeprecated" #endif -int f4(int x) throw() { return x+4; } // Deprecated equivalent to noexcept(true) -#if defined(__GNUG__) -# pragma GCC diagnostic pop +// NOLINTNEXTLINE(modernize-use-noexcept) +int f4(int x) throw() { return x + 4; } // Deprecated equivalent to noexcept(true) +#if defined(__GNUG__) && !defined(__INTEL_COMPILER) +# pragma GCC diagnostic pop #endif struct C { - int m1(int x) noexcept { return x-1; } - int m2(int x) const noexcept { return x-2; } - int m3(int x) noexcept(true) { return x-3; } - int m4(int x) const noexcept(true) { return x-4; } - int m5(int x) noexcept(false) { return x-5; } - int m6(int x) const noexcept(false) { return x-6; } -#if defined(__GNUG__) -# pragma GCC diagnostic push -# pragma GCC diagnostic ignored "-Wdeprecated" + int m1(int x) noexcept { return x - 1; } + int m2(int x) const noexcept { return x - 2; } + int m3(int x) noexcept(true) { return x - 3; } + int m4(int x) const noexcept(true) { return x - 4; } + int m5(int x) noexcept(false) { return x - 5; } + int m6(int x) const noexcept(false) { return x - 6; } +#if defined(__GNUG__) && !defined(__INTEL_COMPILER) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wdeprecated" #endif - int m7(int x) throw() { return x-7; } - int m8(int x) const throw() { return x-8; } -#if defined(__GNUG__) -# pragma GCC diagnostic pop + // NOLINTNEXTLINE(modernize-use-noexcept) + int m7(int x) throw() { return x - 7; } + // NOLINTNEXTLINE(modernize-use-noexcept) + int m8(int x) const throw() { return x - 8; } +#if defined(__GNUG__) && !defined(__INTEL_COMPILER) +# pragma GCC diagnostic pop #endif }; } // namespace test_exc_sp - TEST_SUBMODULE(constants_and_functions, m) { // test_constants m.attr("some_constant") = py::int_(14); @@ -118,10 +123,37 @@ TEST_SUBMODULE(constants_and_functions, m) { .def("m5", &C::m5) .def("m6", &C::m6) .def("m7", &C::m7) - .def("m8", &C::m8) - ; + .def("m8", &C::m8); m.def("f1", f1); m.def("f2", f2); +#if defined(__INTEL_COMPILER) +# pragma warning push +# pragma warning disable 878 // incompatible exception specifications +#endif m.def("f3", f3); +#if defined(__INTEL_COMPILER) +# pragma warning pop +#endif m.def("f4", f4); + + // test_function_record_leaks + m.def("register_large_capture_with_invalid_arguments", [](py::module_ m) { + // This should always be enough to trigger the alternative branch + // where `sizeof(capture) > sizeof(rec->data)` + uint64_t capture[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; +#if defined(__GNUC__) && __GNUC__ == 4 // CentOS7 + py::detail::silence_unused_warnings(capture); +#endif + m.def( + "should_raise", [capture](int) { return capture[9] + 33; }, py::kw_only(), py::arg()); + }); + m.def("register_with_raising_repr", [](py::module_ m, const py::object &default_value) { + m.def( + "should_raise", + [](int, int, const py::object &) { return 42; }, + "some docstring", + py::arg_v("x", 42), + py::arg_v("y", 42, ""), + py::arg_v("z", default_value)); + }); } diff --git a/3rdparty/pybind11/tests/test_constants_and_functions.py b/3rdparty/pybind11/tests/test_constants_and_functions.py index b980ccf1..5da0b84b 100644 --- a/3rdparty/pybind11/tests/test_constants_and_functions.py +++ b/3rdparty/pybind11/tests/test_constants_and_functions.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- import pytest m = pytest.importorskip("pybind11_tests.constants_and_functions") @@ -40,3 +39,14 @@ def test_exception_specifiers(): assert m.f2(53) == 55 assert m.f3(86) == 89 assert m.f4(140) == 144 + + +def test_function_record_leaks(): + class RaisingRepr: + def __repr__(self): + raise RuntimeError("Surprise!") + + with pytest.raises(RuntimeError): + m.register_large_capture_with_invalid_arguments(m) + with pytest.raises(RuntimeError): + m.register_with_raising_repr(m, RaisingRepr()) diff --git a/3rdparty/pybind11/tests/test_copy_move.cpp b/3rdparty/pybind11/tests/test_copy_move.cpp index 2704217a..28c24456 100644 --- a/3rdparty/pybind11/tests/test_copy_move.cpp +++ b/3rdparty/pybind11/tests/test_copy_move.cpp @@ -8,38 +8,48 @@ BSD-style license that can be found in the LICENSE file. */ -#include "pybind11_tests.h" -#include "constructor_stats.h" #include +#include "constructor_stats.h" +#include "pybind11_tests.h" + template struct empty { - static const derived& get_one() { return instance_; } + static const derived &get_one() { return instance_; } static derived instance_; }; struct lacking_copy_ctor : public empty { lacking_copy_ctor() = default; - lacking_copy_ctor(const lacking_copy_ctor& other) = delete; + lacking_copy_ctor(const lacking_copy_ctor &other) = delete; }; -template <> lacking_copy_ctor empty::instance_ = {}; +template <> +lacking_copy_ctor empty::instance_ = {}; struct lacking_move_ctor : public empty { lacking_move_ctor() = default; - lacking_move_ctor(const lacking_move_ctor& other) = delete; - lacking_move_ctor(lacking_move_ctor&& other) = delete; + lacking_move_ctor(const lacking_move_ctor &other) = delete; + lacking_move_ctor(lacking_move_ctor &&other) = delete; }; -template <> lacking_move_ctor empty::instance_ = {}; +template <> +lacking_move_ctor empty::instance_ = {}; /* Custom type caster move/copy test classes */ class MoveOnlyInt { public: MoveOnlyInt() { print_default_created(this); } - MoveOnlyInt(int v) : value{std::move(v)} { print_created(this, value); } - MoveOnlyInt(MoveOnlyInt &&m) { print_move_created(this, m.value); std::swap(value, m.value); } - MoveOnlyInt &operator=(MoveOnlyInt &&m) { print_move_assigned(this, m.value); std::swap(value, m.value); return *this; } + explicit MoveOnlyInt(int v) : value{v} { print_created(this, value); } + MoveOnlyInt(MoveOnlyInt &&m) noexcept { + print_move_created(this, m.value); + std::swap(value, m.value); + } + MoveOnlyInt &operator=(MoveOnlyInt &&m) noexcept { + print_move_assigned(this, m.value); + std::swap(value, m.value); + return *this; + } MoveOnlyInt(const MoveOnlyInt &) = delete; MoveOnlyInt &operator=(const MoveOnlyInt &) = delete; ~MoveOnlyInt() { print_destroyed(this); } @@ -49,11 +59,26 @@ public: class MoveOrCopyInt { public: MoveOrCopyInt() { print_default_created(this); } - MoveOrCopyInt(int v) : value{std::move(v)} { print_created(this, value); } - MoveOrCopyInt(MoveOrCopyInt &&m) { print_move_created(this, m.value); std::swap(value, m.value); } - MoveOrCopyInt &operator=(MoveOrCopyInt &&m) { print_move_assigned(this, m.value); std::swap(value, m.value); return *this; } - MoveOrCopyInt(const MoveOrCopyInt &c) { print_copy_created(this, c.value); value = c.value; } - MoveOrCopyInt &operator=(const MoveOrCopyInt &c) { print_copy_assigned(this, c.value); value = c.value; return *this; } + explicit MoveOrCopyInt(int v) : value{v} { print_created(this, value); } + MoveOrCopyInt(MoveOrCopyInt &&m) noexcept { + print_move_created(this, m.value); + std::swap(value, m.value); + } + MoveOrCopyInt &operator=(MoveOrCopyInt &&m) noexcept { + print_move_assigned(this, m.value); + std::swap(value, m.value); + return *this; + } + MoveOrCopyInt(const MoveOrCopyInt &c) { + print_copy_created(this, c.value); + // NOLINTNEXTLINE(cppcoreguidelines-prefer-member-initializer) + value = c.value; + } + MoveOrCopyInt &operator=(const MoveOrCopyInt &c) { + print_copy_assigned(this, c.value); + value = c.value; + return *this; + } ~MoveOrCopyInt() { print_destroyed(this); } int value; @@ -61,41 +86,71 @@ public: class CopyOnlyInt { public: CopyOnlyInt() { print_default_created(this); } - CopyOnlyInt(int v) : value{std::move(v)} { print_created(this, value); } - CopyOnlyInt(const CopyOnlyInt &c) { print_copy_created(this, c.value); value = c.value; } - CopyOnlyInt &operator=(const CopyOnlyInt &c) { print_copy_assigned(this, c.value); value = c.value; return *this; } + explicit CopyOnlyInt(int v) : value{v} { print_created(this, value); } + CopyOnlyInt(const CopyOnlyInt &c) { + print_copy_created(this, c.value); + // NOLINTNEXTLINE(cppcoreguidelines-prefer-member-initializer) + value = c.value; + } + CopyOnlyInt &operator=(const CopyOnlyInt &c) { + print_copy_assigned(this, c.value); + value = c.value; + return *this; + } ~CopyOnlyInt() { print_destroyed(this); } int value; }; PYBIND11_NAMESPACE_BEGIN(pybind11) PYBIND11_NAMESPACE_BEGIN(detail) -template <> struct type_caster { - PYBIND11_TYPE_CASTER(MoveOnlyInt, _("MoveOnlyInt")); - bool load(handle src, bool) { value = MoveOnlyInt(src.cast()); return true; } - static handle cast(const MoveOnlyInt &m, return_value_policy r, handle p) { return pybind11::cast(m.value, r, p); } +template <> +struct type_caster { + PYBIND11_TYPE_CASTER(MoveOnlyInt, const_name("MoveOnlyInt")); + bool load(handle src, bool) { + value = MoveOnlyInt(src.cast()); + return true; + } + static handle cast(const MoveOnlyInt &m, return_value_policy r, handle p) { + return pybind11::cast(m.value, r, p); + } }; -template <> struct type_caster { - PYBIND11_TYPE_CASTER(MoveOrCopyInt, _("MoveOrCopyInt")); - bool load(handle src, bool) { value = MoveOrCopyInt(src.cast()); return true; } - static handle cast(const MoveOrCopyInt &m, return_value_policy r, handle p) { return pybind11::cast(m.value, r, p); } +template <> +struct type_caster { + PYBIND11_TYPE_CASTER(MoveOrCopyInt, const_name("MoveOrCopyInt")); + bool load(handle src, bool) { + value = MoveOrCopyInt(src.cast()); + return true; + } + static handle cast(const MoveOrCopyInt &m, return_value_policy r, handle p) { + return pybind11::cast(m.value, r, p); + } }; -template <> struct type_caster { +template <> +struct type_caster { protected: CopyOnlyInt value; + public: - static constexpr auto name = _("CopyOnlyInt"); - bool load(handle src, bool) { value = CopyOnlyInt(src.cast()); return true; } - static handle cast(const CopyOnlyInt &m, return_value_policy r, handle p) { return pybind11::cast(m.value, r, p); } + static constexpr auto name = const_name("CopyOnlyInt"); + bool load(handle src, bool) { + value = CopyOnlyInt(src.cast()); + return true; + } + static handle cast(const CopyOnlyInt &m, return_value_policy r, handle p) { + return pybind11::cast(m.value, r, p); + } static handle cast(const CopyOnlyInt *src, return_value_policy policy, handle parent) { - if (!src) return none().release(); + if (!src) { + return none().release(); + } return cast(*src, policy, parent); } - operator CopyOnlyInt*() { return &value; } - operator CopyOnlyInt&() { return value; } - template using cast_op_type = pybind11::detail::cast_op_type; + explicit operator CopyOnlyInt *() { return &value; } + explicit operator CopyOnlyInt &() { return value; } + template + using cast_op_type = pybind11::detail::cast_op_type; }; PYBIND11_NAMESPACE_END(detail) PYBIND11_NAMESPACE_END(pybind11) @@ -103,22 +158,21 @@ PYBIND11_NAMESPACE_END(pybind11) TEST_SUBMODULE(copy_move_policies, m) { // test_lacking_copy_ctor py::class_(m, "lacking_copy_ctor") - .def_static("get_one", &lacking_copy_ctor::get_one, - py::return_value_policy::copy); + .def_static("get_one", &lacking_copy_ctor::get_one, py::return_value_policy::copy); // test_lacking_move_ctor py::class_(m, "lacking_move_ctor") - .def_static("get_one", &lacking_move_ctor::get_one, - py::return_value_policy::move); + .def_static("get_one", &lacking_move_ctor::get_one, py::return_value_policy::move); // test_move_and_copy_casts - m.def("move_and_copy_casts", [](py::object o) { + // NOLINTNEXTLINE(performance-unnecessary-value-param) + m.def("move_and_copy_casts", [](const py::object &o) { int r = 0; r += py::cast(o).value; /* moves */ - r += py::cast(o).value; /* moves */ - r += py::cast(o).value; /* copies */ - auto m1(py::cast(o)); /* moves */ - auto m2(py::cast(o)); /* moves */ - auto m3(py::cast(o)); /* copies */ + r += py::cast(o).value; /* moves */ + r += py::cast(o).value; /* copies */ + auto m1(py::cast(o)); /* moves */ + auto m2(py::cast(o)); /* moves */ + auto m3(py::cast(o)); /* copies */ r += m1.value + m2.value + m3.value; return r; @@ -126,30 +180,40 @@ TEST_SUBMODULE(copy_move_policies, m) { // test_move_and_copy_loads m.def("move_only", [](MoveOnlyInt m) { return m.value; }); + // Changing this breaks the existing test: needs careful review. + // NOLINTNEXTLINE(performance-unnecessary-value-param) m.def("move_or_copy", [](MoveOrCopyInt m) { return m.value; }); + // Changing this breaks the existing test: needs careful review. + // NOLINTNEXTLINE(performance-unnecessary-value-param) m.def("copy_only", [](CopyOnlyInt m) { return m.value; }); - m.def("move_pair", [](std::pair p) { - return p.first.value + p.second.value; - }); + m.def("move_pair", + [](std::pair p) { return p.first.value + p.second.value; }); m.def("move_tuple", [](std::tuple t) { return std::get<0>(t).value + std::get<1>(t).value + std::get<2>(t).value; }); m.def("copy_tuple", [](std::tuple t) { return std::get<0>(t).value + std::get<1>(t).value; }); - m.def("move_copy_nested", [](std::pair>, MoveOrCopyInt>> x) { - return x.first.value + std::get<0>(x.second.first).value + std::get<1>(x.second.first).value + - std::get<0>(std::get<2>(x.second.first)).value + x.second.second.value; - }); + m.def("move_copy_nested", + [](std::pair>, + MoveOrCopyInt>> x) { + return x.first.value + std::get<0>(x.second.first).value + + std::get<1>(x.second.first).value + + std::get<0>(std::get<2>(x.second.first)).value + x.second.second.value; + }); m.def("move_and_copy_cstats", []() { ConstructorStats::gc(); // Reset counts to 0 so that previous tests don't affect later ones: auto &mc = ConstructorStats::get(); - mc.move_assignments = mc.move_constructions = mc.copy_assignments = mc.copy_constructions = 0; + mc.move_assignments = mc.move_constructions = mc.copy_assignments = mc.copy_constructions + = 0; auto &mo = ConstructorStats::get(); - mo.move_assignments = mo.move_constructions = mo.copy_assignments = mo.copy_constructions = 0; + mo.move_assignments = mo.move_constructions = mo.copy_assignments = mo.copy_constructions + = 0; auto &co = ConstructorStats::get(); - co.move_assignments = co.move_constructions = co.copy_assignments = co.copy_constructions = 0; + co.move_assignments = co.move_constructions = co.copy_assignments = co.copy_constructions + = 0; py::dict d; d["MoveOrCopyInt"] = py::cast(mc, py::return_value_policy::reference); d["MoveOnlyInt"] = py::cast(mo, py::return_value_policy::reference); @@ -159,18 +223,13 @@ TEST_SUBMODULE(copy_move_policies, m) { #ifdef PYBIND11_HAS_OPTIONAL // test_move_and_copy_load_optional m.attr("has_optional") = true; - m.def("move_optional", [](std::optional o) { - return o->value; - }); - m.def("move_or_copy_optional", [](std::optional o) { - return o->value; - }); - m.def("copy_optional", [](std::optional o) { - return o->value; - }); - m.def("move_optional_tuple", [](std::optional> x) { - return std::get<0>(*x).value + std::get<1>(*x).value + std::get<2>(*x).value; - }); + m.def("move_optional", [](std::optional o) { return o->value; }); + m.def("move_or_copy_optional", [](std::optional o) { return o->value; }); + m.def("copy_optional", [](std::optional o) { return o->value; }); + m.def("move_optional_tuple", + [](std::optional> x) { + return std::get<0>(*x).value + std::get<1>(*x).value + std::get<2>(*x).value; + }); #else m.attr("has_optional") = false; #endif @@ -181,39 +240,56 @@ TEST_SUBMODULE(copy_move_policies, m) { // added later. struct PrivateOpNew { int value = 1; + private: void *operator new(size_t bytes) { void *ptr = std::malloc(bytes); - if (ptr) + if (ptr) { return ptr; - else - throw std::bad_alloc{}; + } + throw std::bad_alloc{}; } }; py::class_(m, "PrivateOpNew").def_readonly("value", &PrivateOpNew::value); m.def("private_op_new_value", []() { return PrivateOpNew(); }); - m.def("private_op_new_reference", []() -> const PrivateOpNew & { - static PrivateOpNew x{}; - return x; - }, py::return_value_policy::reference); + m.def( + "private_op_new_reference", + []() -> const PrivateOpNew & { + static PrivateOpNew x{}; + return x; + }, + py::return_value_policy::reference); // test_move_fallback // #389: rvp::move should fall-through to copy on non-movable objects struct MoveIssue1 { int v; - MoveIssue1(int v) : v{v} {} + explicit MoveIssue1(int v) : v{v} {} MoveIssue1(const MoveIssue1 &c) = default; MoveIssue1(MoveIssue1 &&) = delete; }; - py::class_(m, "MoveIssue1").def(py::init()).def_readwrite("value", &MoveIssue1::v); + py::class_(m, "MoveIssue1") + .def(py::init()) + .def_readwrite("value", &MoveIssue1::v); struct MoveIssue2 { int v; - MoveIssue2(int v) : v{v} {} + explicit MoveIssue2(int v) : v{v} {} MoveIssue2(MoveIssue2 &&) = default; }; - py::class_(m, "MoveIssue2").def(py::init()).def_readwrite("value", &MoveIssue2::v); + py::class_(m, "MoveIssue2") + .def(py::init()) + .def_readwrite("value", &MoveIssue2::v); + + // #2742: Don't expect ownership of raw pointer to `new`ed object to be transferred with + // `py::return_value_policy::move` + m.def( + "get_moveissue1", + [](int i) { return std::unique_ptr(new MoveIssue1(i)); }, + py::return_value_policy::move); + m.def( + "get_moveissue2", [](int i) { return MoveIssue2(i); }, py::return_value_policy::move); - m.def("get_moveissue1", [](int i) { return new MoveIssue1(i); }, py::return_value_policy::move); - m.def("get_moveissue2", [](int i) { return MoveIssue2(i); }, py::return_value_policy::move); + // Make sure that cast from pytype rvalue to other pytype works + m.def("get_pytype_rvalue_castissue", [](double i) { return py::float_(i).cast(); }); } diff --git a/3rdparty/pybind11/tests/test_copy_move.py b/3rdparty/pybind11/tests/test_copy_move.py index 7e3cc168..9fef0893 100644 --- a/3rdparty/pybind11/tests/test_copy_move.py +++ b/3rdparty/pybind11/tests/test_copy_move.py @@ -1,5 +1,5 @@ -# -*- coding: utf-8 -*- import pytest + from pybind11_tests import copy_move_policies as m @@ -119,7 +119,14 @@ def test_private_op_new(): def test_move_fallback(): """#389: rvp::move should fall-through to copy on non-movable objects""" - m2 = m.get_moveissue2(2) - assert m2.value == 2 m1 = m.get_moveissue1(1) assert m1.value == 1 + m2 = m.get_moveissue2(2) + assert m2.value == 2 + + +def test_pytype_rvalue_cast(): + """Make sure that cast from pytype rvalue to other pytype works""" + + value = m.get_pytype_rvalue_castissue(1.0) + assert value == 1 diff --git a/3rdparty/pybind11/tests/test_custom_type_casters.cpp b/3rdparty/pybind11/tests/test_custom_type_casters.cpp index d565add2..b4af02a4 100644 --- a/3rdparty/pybind11/tests/test_custom_type_casters.cpp +++ b/3rdparty/pybind11/tests/test_custom_type_casters.cpp @@ -7,23 +7,37 @@ BSD-style license that can be found in the LICENSE file. */ -#include "pybind11_tests.h" #include "constructor_stats.h" - +#include "pybind11_tests.h" // py::arg/py::arg_v testing: these arguments just record their argument when invoked -class ArgInspector1 { public: std::string arg = "(default arg inspector 1)"; }; -class ArgInspector2 { public: std::string arg = "(default arg inspector 2)"; }; -class ArgAlwaysConverts { }; -namespace pybind11 { namespace detail { -template <> struct type_caster { +class ArgInspector1 { +public: + std::string arg = "(default arg inspector 1)"; +}; +class ArgInspector2 { +public: + std::string arg = "(default arg inspector 2)"; +}; +class ArgAlwaysConverts {}; + +namespace PYBIND11_NAMESPACE { +namespace detail { +template <> +struct type_caster { public: + // Classic +#ifdef PYBIND11_DETAIL_UNDERSCORE_BACKWARD_COMPATIBILITY PYBIND11_TYPE_CASTER(ArgInspector1, _("ArgInspector1")); +#else + PYBIND11_TYPE_CASTER(ArgInspector1, const_name("ArgInspector1")); +#endif bool load(handle src, bool convert) { - value.arg = "loading ArgInspector1 argument " + - std::string(convert ? "WITH" : "WITHOUT") + " conversion allowed. " - "Argument value = " + (std::string) str(src); + value.arg = "loading ArgInspector1 argument " + std::string(convert ? "WITH" : "WITHOUT") + + " conversion allowed. " + "Argument value = " + + (std::string) str(src); return true; } @@ -31,14 +45,16 @@ public: return str(src.arg).release(); } }; -template <> struct type_caster { +template <> +struct type_caster { public: - PYBIND11_TYPE_CASTER(ArgInspector2, _("ArgInspector2")); + PYBIND11_TYPE_CASTER(ArgInspector2, const_name("ArgInspector2")); bool load(handle src, bool convert) { - value.arg = "loading ArgInspector2 argument " + - std::string(convert ? "WITH" : "WITHOUT") + " conversion allowed. " - "Argument value = " + (std::string) str(src); + value.arg = "loading ArgInspector2 argument " + std::string(convert ? "WITH" : "WITHOUT") + + " conversion allowed. " + "Argument value = " + + (std::string) str(src); return true; } @@ -46,20 +62,19 @@ public: return str(src.arg).release(); } }; -template <> struct type_caster { +template <> +struct type_caster { public: - PYBIND11_TYPE_CASTER(ArgAlwaysConverts, _("ArgAlwaysConverts")); + PYBIND11_TYPE_CASTER(ArgAlwaysConverts, const_name("ArgAlwaysConverts")); - bool load(handle, bool convert) { - return convert; - } + bool load(handle, bool convert) { return convert; } static handle cast(const ArgAlwaysConverts &, return_value_policy, handle) { return py::none().release(); } }; } // namespace detail -} // namespace pybind11 +} // namespace PYBIND11_NAMESPACE // test_custom_caster_destruction class DestructionTester { @@ -67,13 +82,21 @@ public: DestructionTester() { print_default_created(this); } ~DestructionTester() { print_destroyed(this); } DestructionTester(const DestructionTester &) { print_copy_created(this); } - DestructionTester(DestructionTester &&) { print_move_created(this); } - DestructionTester &operator=(const DestructionTester &) { print_copy_assigned(this); return *this; } - DestructionTester &operator=(DestructionTester &&) { print_move_assigned(this); return *this; } + DestructionTester(DestructionTester &&) noexcept { print_move_created(this); } + DestructionTester &operator=(const DestructionTester &) { + print_copy_assigned(this); + return *this; + } + DestructionTester &operator=(DestructionTester &&) noexcept { + print_move_assigned(this); + return *this; + } }; -namespace pybind11 { namespace detail { -template <> struct type_caster { - PYBIND11_TYPE_CASTER(DestructionTester, _("DestructionTester")); +namespace PYBIND11_NAMESPACE { +namespace detail { +template <> +struct type_caster { + PYBIND11_TYPE_CASTER(DestructionTester, const_name("DestructionTester")); bool load(handle, bool) { return true; } static handle cast(const DestructionTester &, return_value_policy, handle) { @@ -81,7 +104,35 @@ template <> struct type_caster { } }; } // namespace detail -} // namespace pybind11 +} // namespace PYBIND11_NAMESPACE + +// Define type caster outside of `pybind11::detail` and then alias it. +namespace other_lib { +struct MyType {}; +// Corrupt `py` shorthand alias for surrounding context. +namespace py {} +// Corrupt unqualified relative `pybind11` namespace. +namespace PYBIND11_NAMESPACE {} +// Correct alias. +namespace py_ = ::pybind11; +// Define caster. This is effectively no-op, we only ensure it compiles and we +// don't have any symbol collision when using macro mixin. +struct my_caster { + PYBIND11_TYPE_CASTER(MyType, py_::detail::const_name("MyType")); + bool load(py_::handle, bool) { return true; } + + static py_::handle cast(const MyType &, py_::return_value_policy, py_::handle) { + return py_::bool_(true).release(); + } +}; +} // namespace other_lib +// Effectively "alias" it into correct namespace (via inheritance). +namespace PYBIND11_NAMESPACE { +namespace detail { +template <> +struct type_caster : public other_lib::my_caster {}; +} // namespace detail +} // namespace PYBIND11_NAMESPACE TEST_SUBMODULE(custom_type_casters, m) { // test_custom_type_casters @@ -94,34 +145,65 @@ TEST_SUBMODULE(custom_type_casters, m) { class ArgInspector { public: ArgInspector1 f(ArgInspector1 a, ArgAlwaysConverts) { return a; } - std::string g(ArgInspector1 a, const ArgInspector1 &b, int c, ArgInspector2 *d, ArgAlwaysConverts) { + std::string g(const ArgInspector1 &a, + const ArgInspector1 &b, + int c, + ArgInspector2 *d, + ArgAlwaysConverts) { return a.arg + "\n" + b.arg + "\n" + std::to_string(c) + "\n" + d->arg; } static ArgInspector2 h(ArgInspector2 a, ArgAlwaysConverts) { return a; } }; + // [workaround(intel)] ICC 20/21 breaks with py::arg().stuff, using py::arg{}.stuff works. py::class_(m, "ArgInspector") .def(py::init<>()) .def("f", &ArgInspector::f, py::arg(), py::arg() = ArgAlwaysConverts()) - .def("g", &ArgInspector::g, "a"_a.noconvert(), "b"_a, "c"_a.noconvert()=13, "d"_a=ArgInspector2(), py::arg() = ArgAlwaysConverts()) - .def_static("h", &ArgInspector::h, py::arg().noconvert(), py::arg() = ArgAlwaysConverts()) - ; - m.def("arg_inspect_func", [](ArgInspector2 a, ArgInspector1 b, ArgAlwaysConverts) { return a.arg + "\n" + b.arg; }, - py::arg().noconvert(false), py::arg_v(nullptr, ArgInspector1()).noconvert(true), py::arg() = ArgAlwaysConverts()); - - m.def("floats_preferred", [](double f) { return 0.5 * f; }, py::arg("f")); - m.def("floats_only", [](double f) { return 0.5 * f; }, py::arg("f").noconvert()); - m.def("ints_preferred", [](int i) { return i / 2; }, py::arg("i")); - m.def("ints_only", [](int i) { return i / 2; }, py::arg("i").noconvert()); + .def("g", + &ArgInspector::g, + "a"_a.noconvert(), + "b"_a, + "c"_a.noconvert() = 13, + "d"_a = ArgInspector2(), + py::arg() = ArgAlwaysConverts()) + .def_static("h", &ArgInspector::h, py::arg{}.noconvert(), py::arg() = ArgAlwaysConverts()); + m.def( + "arg_inspect_func", + [](const ArgInspector2 &a, const ArgInspector1 &b, ArgAlwaysConverts) { + return a.arg + "\n" + b.arg; + }, + py::arg{}.noconvert(false), + py::arg_v(nullptr, ArgInspector1()).noconvert(true), + py::arg() = ArgAlwaysConverts()); + + m.def( + "floats_preferred", [](double f) { return 0.5 * f; }, "f"_a); + m.def( + "floats_only", [](double f) { return 0.5 * f; }, "f"_a.noconvert()); + m.def( + "ints_preferred", [](int i) { return i / 2; }, "i"_a); + m.def( + "ints_only", [](int i) { return i / 2; }, "i"_a.noconvert()); // test_custom_caster_destruction // Test that `take_ownership` works on types with a custom type caster when given a pointer // default policy: don't take ownership: - m.def("custom_caster_no_destroy", []() { static auto *dt = new DestructionTester(); return dt; }); - - m.def("custom_caster_destroy", []() { return new DestructionTester(); }, - py::return_value_policy::take_ownership); // Takes ownership: destroy when finished - m.def("custom_caster_destroy_const", []() -> const DestructionTester * { return new DestructionTester(); }, - py::return_value_policy::take_ownership); // Likewise (const doesn't inhibit destruction) - m.def("destruction_tester_cstats", &ConstructorStats::get, py::return_value_policy::reference); + m.def("custom_caster_no_destroy", []() { + static auto *dt = new DestructionTester(); + return dt; + }); + + m.def( + "custom_caster_destroy", + []() { return new DestructionTester(); }, + py::return_value_policy::take_ownership); // Takes ownership: destroy when finished + m.def( + "custom_caster_destroy_const", + []() -> const DestructionTester * { return new DestructionTester(); }, + py::return_value_policy::take_ownership); // Likewise (const doesn't inhibit destruction) + m.def("destruction_tester_cstats", + &ConstructorStats::get, + py::return_value_policy::reference); + + m.def("other_lib_type", [](other_lib::MyType x) { return x; }); } diff --git a/3rdparty/pybind11/tests/test_custom_type_casters.py b/3rdparty/pybind11/tests/test_custom_type_casters.py index bb74d54e..adfa6cf8 100644 --- a/3rdparty/pybind11/tests/test_custom_type_casters.py +++ b/3rdparty/pybind11/tests/test_custom_type_casters.py @@ -1,5 +1,5 @@ -# -*- coding: utf-8 -*- import pytest + from pybind11_tests import custom_type_casters as m @@ -18,7 +18,7 @@ def test_noconvert_args(msg): loading ArgInspector1 argument WITH conversion allowed. Argument value = this is b 13 loading ArgInspector2 argument WITH conversion allowed. Argument value = (default arg inspector 2) - """ # noqa: E501 line too long + """ ) assert ( msg(a.g("this is a", "this is b", 42)) @@ -27,7 +27,7 @@ def test_noconvert_args(msg): loading ArgInspector1 argument WITH conversion allowed. Argument value = this is b 42 loading ArgInspector2 argument WITH conversion allowed. Argument value = (default arg inspector 2) - """ # noqa: E501 line too long + """ ) assert ( msg(a.g("this is a", "this is b", 42, "this is d")) @@ -75,7 +75,7 @@ def test_noconvert_args(msg): 1. (i: int) -> int Invoked with: 4.0 - """ # noqa: E501 line too long + """ ) assert m.ints_only(4) == 2 @@ -114,3 +114,7 @@ def test_custom_caster_destruction(): # Make sure we still only have the original object (from ..._no_destroy()) alive: assert cstats.alive() == 1 + + +def test_custom_caster_other_lib(): + assert m.other_lib_type(True) diff --git a/3rdparty/pybind11/tests/test_custom_type_setup.cpp b/3rdparty/pybind11/tests/test_custom_type_setup.cpp new file mode 100644 index 00000000..42fae05d --- /dev/null +++ b/3rdparty/pybind11/tests/test_custom_type_setup.cpp @@ -0,0 +1,41 @@ +/* + tests/test_custom_type_setup.cpp -- Tests `pybind11::custom_type_setup` + + Copyright (c) Google LLC + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#include + +#include "pybind11_tests.h" + +namespace py = pybind11; + +namespace { + +struct OwnsPythonObjects { + py::object value = py::none(); +}; +} // namespace + +TEST_SUBMODULE(custom_type_setup, m) { + py::class_ cls( + m, "OwnsPythonObjects", py::custom_type_setup([](PyHeapTypeObject *heap_type) { + auto *type = &heap_type->ht_type; + type->tp_flags |= Py_TPFLAGS_HAVE_GC; + type->tp_traverse = [](PyObject *self_base, visitproc visit, void *arg) { + auto &self = py::cast(py::handle(self_base)); + Py_VISIT(self.value.ptr()); + return 0; + }; + type->tp_clear = [](PyObject *self_base) { + auto &self = py::cast(py::handle(self_base)); + self.value = py::none(); + return 0; + }; + })); + cls.def(py::init<>()); + cls.def_readwrite("value", &OwnsPythonObjects::value); +} diff --git a/3rdparty/pybind11/tests/test_custom_type_setup.py b/3rdparty/pybind11/tests/test_custom_type_setup.py new file mode 100644 index 00000000..19b44c9d --- /dev/null +++ b/3rdparty/pybind11/tests/test_custom_type_setup.py @@ -0,0 +1,48 @@ +import gc +import weakref + +import pytest + +import env # noqa: F401 +from pybind11_tests import custom_type_setup as m + + +@pytest.fixture +def gc_tester(): + """Tests that an object is garbage collected. + + Assumes that any unreferenced objects are fully collected after calling + `gc.collect()`. That is true on CPython, but does not appear to reliably + hold on PyPy. + """ + + weak_refs = [] + + def add_ref(obj): + # PyPy does not support `gc.is_tracked`. + if hasattr(gc, "is_tracked"): + assert gc.is_tracked(obj) + weak_refs.append(weakref.ref(obj)) + + yield add_ref + + gc.collect() + for ref in weak_refs: + assert ref() is None + + +# PyPy does not seem to reliably garbage collect. +@pytest.mark.skipif("env.PYPY") +def test_self_cycle(gc_tester): + obj = m.OwnsPythonObjects() + obj.value = obj + gc_tester(obj) + + +# PyPy does not seem to reliably garbage collect. +@pytest.mark.skipif("env.PYPY") +def test_indirect_cycle(gc_tester): + obj = m.OwnsPythonObjects() + obj_list = [obj] + obj.value = obj_list + gc_tester(obj) diff --git a/3rdparty/pybind11/tests/test_docstring_options.cpp b/3rdparty/pybind11/tests/test_docstring_options.cpp index 8c8f79fd..4d44f4e2 100644 --- a/3rdparty/pybind11/tests/test_docstring_options.cpp +++ b/3rdparty/pybind11/tests/test_docstring_options.cpp @@ -15,35 +15,60 @@ TEST_SUBMODULE(docstring_options, m) { py::options options; options.disable_function_signatures(); - m.def("test_function1", [](int, int) {}, py::arg("a"), py::arg("b")); - m.def("test_function2", [](int, int) {}, py::arg("a"), py::arg("b"), "A custom docstring"); - - m.def("test_overloaded1", [](int) {}, py::arg("i"), "Overload docstring"); - m.def("test_overloaded1", [](double) {}, py::arg("d")); - - m.def("test_overloaded2", [](int) {}, py::arg("i"), "overload docstring 1"); - m.def("test_overloaded2", [](double) {}, py::arg("d"), "overload docstring 2"); - - m.def("test_overloaded3", [](int) {}, py::arg("i")); - m.def("test_overloaded3", [](double) {}, py::arg("d"), "Overload docstr"); + m.def( + "test_function1", [](int, int) {}, py::arg("a"), py::arg("b")); + m.def( + "test_function2", [](int, int) {}, py::arg("a"), py::arg("b"), "A custom docstring"); + + m.def( + "test_overloaded1", [](int) {}, py::arg("i"), "Overload docstring"); + m.def( + "test_overloaded1", [](double) {}, py::arg("d")); + + m.def( + "test_overloaded2", [](int) {}, py::arg("i"), "overload docstring 1"); + m.def( + "test_overloaded2", [](double) {}, py::arg("d"), "overload docstring 2"); + + m.def( + "test_overloaded3", [](int) {}, py::arg("i")); + m.def( + "test_overloaded3", [](double) {}, py::arg("d"), "Overload docstr"); options.enable_function_signatures(); - m.def("test_function3", [](int, int) {}, py::arg("a"), py::arg("b")); - m.def("test_function4", [](int, int) {}, py::arg("a"), py::arg("b"), "A custom docstring"); + m.def( + "test_function3", [](int, int) {}, py::arg("a"), py::arg("b")); + m.def( + "test_function4", [](int, int) {}, py::arg("a"), py::arg("b"), "A custom docstring"); options.disable_function_signatures().disable_user_defined_docstrings(); - m.def("test_function5", [](int, int) {}, py::arg("a"), py::arg("b"), "A custom docstring"); + m.def( + "test_function5", [](int, int) {}, py::arg("a"), py::arg("b"), "A custom docstring"); { py::options nested_options; nested_options.enable_user_defined_docstrings(); - m.def("test_function6", [](int, int) {}, py::arg("a"), py::arg("b"), "A custom docstring"); + m.def( + "test_function6", + [](int, int) {}, + py::arg("a"), + py::arg("b"), + "A custom docstring"); } } - m.def("test_function7", [](int, int) {}, py::arg("a"), py::arg("b"), "A custom docstring"); + m.def( + "test_function7", [](int, int) {}, py::arg("a"), py::arg("b"), "A custom docstring"); + + { + py::options options; + options.disable_user_defined_docstrings(); + options.disable_function_signatures(); + + m.def("test_function8", []() {}); + } { py::options options; @@ -55,7 +80,9 @@ TEST_SUBMODULE(docstring_options, m) { int getValue() const { return value; } }; py::class_(m, "DocstringTestFoo", "This is a class docstring") - .def_property("value_prop", &DocstringTestFoo::getValue, &DocstringTestFoo::setValue, "This is a property docstring") - ; + .def_property("value_prop", + &DocstringTestFoo::getValue, + &DocstringTestFoo::setValue, + "This is a property docstring"); } } diff --git a/3rdparty/pybind11/tests/test_docstring_options.py b/3rdparty/pybind11/tests/test_docstring_options.py index 87d80d2d..fcd16b89 100644 --- a/3rdparty/pybind11/tests/test_docstring_options.py +++ b/3rdparty/pybind11/tests/test_docstring_options.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- from pybind11_tests import docstring_options as m @@ -34,6 +33,9 @@ def test_docstring_options(): assert m.test_function7.__doc__.startswith("test_function7(a: int, b: int) -> None") assert m.test_function7.__doc__.endswith("A custom docstring\n") + # when all options are disabled, no docstring (instead of an empty one) should be generated + assert m.test_function8.__doc__ is None + # Suppression of user-defined docstrings for non-function objects assert not m.DocstringTestFoo.__doc__ assert not m.DocstringTestFoo.value_prop.__doc__ diff --git a/3rdparty/pybind11/tests/test_eigen.cpp b/3rdparty/pybind11/tests/test_eigen.cpp index 2cc2243d..591dacc6 100644 --- a/3rdparty/pybind11/tests/test_eigen.cpp +++ b/3rdparty/pybind11/tests/test_eigen.cpp @@ -7,26 +7,29 @@ BSD-style license that can be found in the LICENSE file. */ -#include "pybind11_tests.h" -#include "constructor_stats.h" #include #include +#include "constructor_stats.h" +#include "pybind11_tests.h" + #if defined(_MSC_VER) -# pragma warning(disable: 4996) // C4996: std::unary_negation is deprecated +# pragma warning(disable : 4996) // C4996: std::unary_negation is deprecated #endif #include using MatrixXdR = Eigen::Matrix; - - // Sets/resets a testing reference matrix to have values of 10*r + c, where r and c are the // (1-based) row/column number. -template void reset_ref(M &x) { - for (int i = 0; i < x.rows(); i++) for (int j = 0; j < x.cols(); j++) - x(i, j) = 11 + 10*i + j; +template +void reset_ref(M &x) { + for (int i = 0; i < x.rows(); i++) { + for (int j = 0; j < x.cols(); j++) { + x(i, j) = 11 + 10 * i + j; + } + } } // Returns a static, column-major matrix @@ -54,16 +57,18 @@ void reset_refs() { } // Returns element 2,1 from a matrix (used to test copy/nocopy) -double get_elem(Eigen::Ref m) { return m(2, 1); }; - +double get_elem(const Eigen::Ref &m) { return m(2, 1); }; // Returns a matrix with 10*r + 100*c added to each matrix element (to help test that the matrix // reference is referencing rows/columns correctly). -template Eigen::MatrixXd adjust_matrix(MatrixArgType m) { +template +Eigen::MatrixXd adjust_matrix(MatrixArgType m) { Eigen::MatrixXd ret(m); - for (int c = 0; c < m.cols(); c++) - for (int r = 0; r < m.rows(); r++) - ret(r, c) += 10*r + 100*c; // NOLINT(clang-analyzer-core.uninitialized.Assign) + for (int c = 0; c < m.cols(); c++) { + for (int r = 0; r < m.rows(); r++) { + ret(r, c) += 10 * r + 100 * c; // NOLINT(clang-analyzer-core.uninitialized.Assign) + } + } return ret; } @@ -90,27 +95,35 @@ TEST_SUBMODULE(eigen, m) { // various tests m.def("double_col", [](const Eigen::VectorXf &x) -> Eigen::VectorXf { return 2.0f * x; }); - m.def("double_row", [](const Eigen::RowVectorXf &x) -> Eigen::RowVectorXf { return 2.0f * x; }); - m.def("double_complex", [](const Eigen::VectorXcf &x) -> Eigen::VectorXcf { return 2.0f * x; }); + m.def("double_row", + [](const Eigen::RowVectorXf &x) -> Eigen::RowVectorXf { return 2.0f * x; }); + m.def("double_complex", + [](const Eigen::VectorXcf &x) -> Eigen::VectorXcf { return 2.0f * x; }); m.def("double_threec", [](py::EigenDRef x) { x *= 2; }); m.def("double_threer", [](py::EigenDRef x) { x *= 2; }); - m.def("double_mat_cm", [](Eigen::MatrixXf x) -> Eigen::MatrixXf { return 2.0f * x; }); - m.def("double_mat_rm", [](DenseMatrixR x) -> DenseMatrixR { return 2.0f * x; }); + m.def("double_mat_cm", [](const Eigen::MatrixXf &x) -> Eigen::MatrixXf { return 2.0f * x; }); + m.def("double_mat_rm", [](const DenseMatrixR &x) -> DenseMatrixR { return 2.0f * x; }); // test_eigen_ref_to_python // Different ways of passing via Eigen::Ref; the first and second are the Eigen-recommended - m.def("cholesky1", [](Eigen::Ref x) -> Eigen::MatrixXd { return x.llt().matrixL(); }); - m.def("cholesky2", [](const Eigen::Ref &x) -> Eigen::MatrixXd { return x.llt().matrixL(); }); - m.def("cholesky3", [](const Eigen::Ref &x) -> Eigen::MatrixXd { return x.llt().matrixL(); }); - m.def("cholesky4", [](Eigen::Ref x) -> Eigen::MatrixXd { return x.llt().matrixL(); }); + m.def("cholesky1", + [](const Eigen::Ref &x) -> Eigen::MatrixXd { return x.llt().matrixL(); }); + m.def("cholesky2", [](const Eigen::Ref &x) -> Eigen::MatrixXd { + return x.llt().matrixL(); + }); + m.def("cholesky3", + [](const Eigen::Ref &x) -> Eigen::MatrixXd { return x.llt().matrixL(); }); + m.def("cholesky4", [](const Eigen::Ref &x) -> Eigen::MatrixXd { + return x.llt().matrixL(); + }); // test_eigen_ref_mutators - // Mutators: these add some value to the given element using Eigen, but Eigen should be mapping into - // the numpy array data and so the result should show up there. There are three versions: one that - // works on a contiguous-row matrix (numpy's default), one for a contiguous-column matrix, and one - // for any matrix. - auto add_rm = [](Eigen::Ref x, int r, int c, double v) { x(r,c) += v; }; - auto add_cm = [](Eigen::Ref x, int r, int c, double v) { x(r,c) += v; }; + // Mutators: these add some value to the given element using Eigen, but Eigen should be mapping + // into the numpy array data and so the result should show up there. There are three versions: + // one that works on a contiguous-row matrix (numpy's default), one for a contiguous-column + // matrix, and one for any matrix. + auto add_rm = [](Eigen::Ref x, int r, int c, double v) { x(r, c) += v; }; + auto add_cm = [](Eigen::Ref x, int r, int c, double v) { x(r, c) += v; }; // Mutators (Eigen maps into numpy variables): m.def("add_rm", add_rm); // Only takes row-contiguous @@ -121,7 +134,8 @@ TEST_SUBMODULE(eigen, m) { m.def("add2", add_cm); m.def("add2", add_rm); // This one accepts a matrix of any stride: - m.def("add_any", [](py::EigenDRef x, int r, int c, double v) { x(r,c) += v; }); + m.def("add_any", + [](py::EigenDRef x, int r, int c, double v) { x(r, c) += v; }); // Return mutable references (numpy maps into eigen variables) m.def("get_cm_ref", []() { return Eigen::Ref(get_cm()); }); @@ -133,49 +147,72 @@ TEST_SUBMODULE(eigen, m) { m.def("reset_refs", reset_refs); // Restores get_{cm,rm}_ref to original values // Increments and returns ref to (same) matrix - m.def("incr_matrix", [](Eigen::Ref m, double v) { - m += Eigen::MatrixXd::Constant(m.rows(), m.cols(), v); - return m; - }, py::return_value_policy::reference); + m.def( + "incr_matrix", + [](Eigen::Ref m, double v) { + m += Eigen::MatrixXd::Constant(m.rows(), m.cols(), v); + return m; + }, + py::return_value_policy::reference); // Same, but accepts a matrix of any strides - m.def("incr_matrix_any", [](py::EigenDRef m, double v) { - m += Eigen::MatrixXd::Constant(m.rows(), m.cols(), v); - return m; - }, py::return_value_policy::reference); + m.def( + "incr_matrix_any", + [](py::EigenDRef m, double v) { + m += Eigen::MatrixXd::Constant(m.rows(), m.cols(), v); + return m; + }, + py::return_value_policy::reference); // Returns an eigen slice of even rows - m.def("even_rows", [](py::EigenDRef m) { - return py::EigenDMap( - m.data(), (m.rows() + 1) / 2, m.cols(), + m.def( + "even_rows", + [](py::EigenDRef m) { + return py::EigenDMap( + m.data(), + (m.rows() + 1) / 2, + m.cols(), py::EigenDStride(m.outerStride(), 2 * m.innerStride())); - }, py::return_value_policy::reference); + }, + py::return_value_policy::reference); // Returns an eigen slice of even columns - m.def("even_cols", [](py::EigenDRef m) { - return py::EigenDMap( - m.data(), m.rows(), (m.cols() + 1) / 2, + m.def( + "even_cols", + [](py::EigenDRef m) { + return py::EigenDMap( + m.data(), + m.rows(), + (m.cols() + 1) / 2, py::EigenDStride(2 * m.outerStride(), m.innerStride())); - }, py::return_value_policy::reference); + }, + py::return_value_policy::reference); // Returns diagonals: a vector-like object with an inner stride != 1 m.def("diagonal", [](const Eigen::Ref &x) { return x.diagonal(); }); - m.def("diagonal_1", [](const Eigen::Ref &x) { return x.diagonal<1>(); }); - m.def("diagonal_n", [](const Eigen::Ref &x, int index) { return x.diagonal(index); }); + m.def("diagonal_1", + [](const Eigen::Ref &x) { return x.diagonal<1>(); }); + m.def("diagonal_n", + [](const Eigen::Ref &x, int index) { return x.diagonal(index); }); // Return a block of a matrix (gives non-standard strides) - m.def("block", [](const Eigen::Ref &x, int start_row, int start_col, int block_rows, int block_cols) { - return x.block(start_row, start_col, block_rows, block_cols); - }); + m.def("block", + [](const Eigen::Ref &x, + int start_row, + int start_col, + int block_rows, + int block_cols) { return x.block(start_row, start_col, block_rows, block_cols); }); // test_eigen_return_references, test_eigen_keepalive // return value referencing/copying tests: class ReturnTester { Eigen::MatrixXd mat = create(); + public: ReturnTester() { print_created(this); } ~ReturnTester() { print_destroyed(this); } static Eigen::MatrixXd create() { return Eigen::MatrixXd::Ones(10, 10); } + // NOLINTNEXTLINE(readability-const-return-type) static const Eigen::MatrixXd createConst() { return Eigen::MatrixXd::Ones(10, 10); } Eigen::MatrixXd &get() { return mat; } Eigen::MatrixXd *getPtr() { return &mat; } @@ -183,12 +220,24 @@ TEST_SUBMODULE(eigen, m) { const Eigen::MatrixXd *viewPtr() { return &mat; } Eigen::Ref ref() { return mat; } Eigen::Ref refConst() { return mat; } - Eigen::Block block(int r, int c, int nrow, int ncol) { return mat.block(r, c, nrow, ncol); } - Eigen::Block blockConst(int r, int c, int nrow, int ncol) const { return mat.block(r, c, nrow, ncol); } - py::EigenDMap corners() { return py::EigenDMap(mat.data(), - py::EigenDStride(mat.outerStride() * (mat.outerSize()-1), mat.innerStride() * (mat.innerSize()-1))); } - py::EigenDMap cornersConst() const { return py::EigenDMap(mat.data(), - py::EigenDStride(mat.outerStride() * (mat.outerSize()-1), mat.innerStride() * (mat.innerSize()-1))); } + Eigen::Block block(int r, int c, int nrow, int ncol) { + return mat.block(r, c, nrow, ncol); + } + Eigen::Block blockConst(int r, int c, int nrow, int ncol) const { + return mat.block(r, c, nrow, ncol); + } + py::EigenDMap corners() { + return py::EigenDMap( + mat.data(), + py::EigenDStride(mat.outerStride() * (mat.outerSize() - 1), + mat.innerStride() * (mat.innerSize() - 1))); + } + py::EigenDMap cornersConst() const { + return py::EigenDMap( + mat.data(), + py::EigenDStride(mat.outerStride() * (mat.outerSize() - 1), + mat.innerStride() * (mat.innerSize() - 1))); + } }; using rvp = py::return_value_policy; py::class_(m, "ReturnTester") @@ -199,9 +248,9 @@ TEST_SUBMODULE(eigen, m) { .def("get_ptr", &ReturnTester::getPtr, rvp::reference_internal) .def("view", &ReturnTester::view, rvp::reference_internal) .def("view_ptr", &ReturnTester::view, rvp::reference_internal) - .def("copy_get", &ReturnTester::get) // Default rvp: copy - .def("copy_view", &ReturnTester::view) // " - .def("ref", &ReturnTester::ref) // Default for Ref is to reference + .def("copy_get", &ReturnTester::get) // Default rvp: copy + .def("copy_view", &ReturnTester::view) // " + .def("ref", &ReturnTester::ref) // Default for Ref is to reference .def("ref_const", &ReturnTester::refConst) // Likewise, but const .def("ref_safe", &ReturnTester::ref, rvp::reference_internal) .def("ref_const_safe", &ReturnTester::refConst, rvp::reference_internal) @@ -212,52 +261,55 @@ TEST_SUBMODULE(eigen, m) { .def("block_const", &ReturnTester::blockConst, rvp::reference_internal) .def("copy_block", &ReturnTester::block, rvp::copy) .def("corners", &ReturnTester::corners, rvp::reference_internal) - .def("corners_const", &ReturnTester::cornersConst, rvp::reference_internal) - ; + .def("corners_const", &ReturnTester::cornersConst, rvp::reference_internal); // test_special_matrix_objects // Returns a DiagonalMatrix with diagonal (1,2,3,...) m.def("incr_diag", [](int k) { Eigen::DiagonalMatrix m(k); - for (int i = 0; i < k; i++) m.diagonal()[i] = i+1; + for (int i = 0; i < k; i++) { + m.diagonal()[i] = i + 1; + } return m; }); // Returns a SelfAdjointView referencing the lower triangle of m - m.def("symmetric_lower", [](const Eigen::MatrixXi &m) { - return m.selfadjointView(); - }); + m.def("symmetric_lower", + [](const Eigen::MatrixXi &m) { return m.selfadjointView(); }); // Returns a SelfAdjointView referencing the lower triangle of m - m.def("symmetric_upper", [](const Eigen::MatrixXi &m) { - return m.selfadjointView(); - }); + m.def("symmetric_upper", + [](const Eigen::MatrixXi &m) { return m.selfadjointView(); }); // Test matrix for various functions below. Eigen::MatrixXf mat(5, 6); - mat << 0, 3, 0, 0, 0, 11, - 22, 0, 0, 0, 17, 11, - 7, 5, 0, 1, 0, 11, - 0, 0, 0, 0, 0, 11, - 0, 0, 14, 0, 8, 11; + mat << 0, 3, 0, 0, 0, 11, 22, 0, 0, 0, 17, 11, 7, 5, 0, 1, 0, 11, 0, 0, 0, 0, 0, 11, 0, 0, 14, + 0, 8, 11; // test_fixed, and various other tests m.def("fixed_r", [mat]() -> FixedMatrixR { return FixedMatrixR(mat); }); + // Our Eigen does a hack which respects constness through the numpy writeable flag. + // Therefore, the const return actually affects this type despite being an rvalue. + // NOLINTNEXTLINE(readability-const-return-type) m.def("fixed_r_const", [mat]() -> const FixedMatrixR { return FixedMatrixR(mat); }); m.def("fixed_c", [mat]() -> FixedMatrixC { return FixedMatrixC(mat); }); m.def("fixed_copy_r", [](const FixedMatrixR &m) -> FixedMatrixR { return m; }); m.def("fixed_copy_c", [](const FixedMatrixC &m) -> FixedMatrixC { return m; }); // test_mutator_descriptors - m.def("fixed_mutator_r", [](Eigen::Ref) {}); - m.def("fixed_mutator_c", [](Eigen::Ref) {}); - m.def("fixed_mutator_a", [](py::EigenDRef) {}); + m.def("fixed_mutator_r", [](const Eigen::Ref &) {}); + m.def("fixed_mutator_c", [](const Eigen::Ref &) {}); + m.def("fixed_mutator_a", [](const py::EigenDRef &) {}); // test_dense m.def("dense_r", [mat]() -> DenseMatrixR { return DenseMatrixR(mat); }); m.def("dense_c", [mat]() -> DenseMatrixC { return DenseMatrixC(mat); }); m.def("dense_copy_r", [](const DenseMatrixR &m) -> DenseMatrixR { return m; }); m.def("dense_copy_c", [](const DenseMatrixC &m) -> DenseMatrixC { return m; }); // test_sparse, test_sparse_signature - m.def("sparse_r", [mat]() -> SparseMatrixR { return Eigen::SparseView(mat); }); //NOLINT(clang-analyzer-core.uninitialized.UndefReturn) - m.def("sparse_c", [mat]() -> SparseMatrixC { return Eigen::SparseView(mat); }); + m.def("sparse_r", [mat]() -> SparseMatrixR { + // NOLINTNEXTLINE(clang-analyzer-core.uninitialized.UndefReturn) + return Eigen::SparseView(mat); + }); + m.def("sparse_c", + [mat]() -> SparseMatrixC { return Eigen::SparseView(mat); }); m.def("sparse_copy_r", [](const SparseMatrixR &m) -> SparseMatrixR { return m; }); m.def("sparse_copy_c", [](const SparseMatrixC &m) -> SparseMatrixC { return m; }); // test_partially_fixed @@ -271,41 +323,62 @@ TEST_SUBMODULE(eigen, m) { m.def("cpp_copy", [](py::handle m) { return m.cast()(1, 0); }); m.def("cpp_ref_c", [](py::handle m) { return m.cast>()(1, 0); }); m.def("cpp_ref_r", [](py::handle m) { return m.cast>()(1, 0); }); - m.def("cpp_ref_any", [](py::handle m) { return m.cast>()(1, 0); }); + m.def("cpp_ref_any", + [](py::handle m) { return m.cast>()(1, 0); }); + // [workaround(intel)] ICC 20/21 breaks with py::arg().stuff, using py::arg{}.stuff works. // test_nocopy_wrapper // Test that we can prevent copying into an argument that would normally copy: First a version // that would allow copying (if types or strides don't match) for comparison: m.def("get_elem", &get_elem); // Now this alternative that calls the tells pybind to fail rather than copy: - m.def("get_elem_nocopy", [](Eigen::Ref m) -> double { return get_elem(m); }, - py::arg().noconvert()); + m.def( + "get_elem_nocopy", + [](const Eigen::Ref &m) -> double { return get_elem(m); }, + py::arg{}.noconvert()); // Also test a row-major-only no-copy const ref: - m.def("get_elem_rm_nocopy", [](Eigen::Ref> &m) -> long { return m(2, 1); }, - py::arg().noconvert()); - - // test_issue738 - // Issue #738: 1xN or Nx1 2D matrices were neither accepted nor properly copied with an + m.def( + "get_elem_rm_nocopy", + [](Eigen::Ref> &m) -> long { + return m(2, 1); + }, + py::arg{}.noconvert()); + + // test_issue738, test_zero_length + // Issue #738: 1×N or N×1 2D matrices were neither accepted nor properly copied with an // incompatible stride value on the length-1 dimension--but that should be allowed (without // requiring a copy!) because the stride value can be safely ignored on a size-1 dimension. - m.def("iss738_f1", &adjust_matrix &>, py::arg().noconvert()); - m.def("iss738_f2", &adjust_matrix> &>, py::arg().noconvert()); + // Similarly, 0×N or N×0 matrices were not accepted--again, these should be allowed since + // they contain no data. This particularly affects numpy ≥ 1.23, which sets the strides to + // 0 if any dimension size is 0. + m.def("iss738_f1", + &adjust_matrix &>, + py::arg{}.noconvert()); + m.def("iss738_f2", + &adjust_matrix> &>, + py::arg{}.noconvert()); // test_issue1105 // Issue #1105: when converting from a numpy two-dimensional (Nx1) or (1xN) value into a dense - // eigen Vector or RowVector, the argument would fail to load because the numpy copy would fail: - // numpy won't broadcast a Nx1 into a 1-dimensional vector. - m.def("iss1105_col", [](Eigen::VectorXd) { return true; }); - m.def("iss1105_row", [](Eigen::RowVectorXd) { return true; }); + // eigen Vector or RowVector, the argument would fail to load because the numpy copy would + // fail: numpy won't broadcast a Nx1 into a 1-dimensional vector. + m.def("iss1105_col", [](const Eigen::VectorXd &) { return true; }); + m.def("iss1105_row", [](const Eigen::RowVectorXd &) { return true; }); // test_named_arguments // Make sure named arguments are working properly: - m.def("matrix_multiply", [](const py::EigenDRef A, const py::EigenDRef B) - -> Eigen::MatrixXd { - if (A.cols() != B.rows()) throw std::domain_error("Nonconformable matrices!"); - return A * B; - }, py::arg("A"), py::arg("B")); + m.def( + "matrix_multiply", + [](const py::EigenDRef &A, + const py::EigenDRef &B) -> Eigen::MatrixXd { + if (A.cols() != B.rows()) { + throw std::domain_error("Nonconformable matrices!"); + } + return A * B; + }, + py::arg("A"), + py::arg("B")); // test_custom_operator_new py::class_(m, "CustomOperatorNew") @@ -317,7 +390,7 @@ TEST_SUBMODULE(eigen, m) { // In case of a failure (the caster's temp array does not live long enough), creating // a new array (np.ones(10)) increases the chances that the temp array will be garbage // collected and/or that its memory will be overridden with different values. - m.def("get_elem_direct", [](Eigen::Ref v) { + m.def("get_elem_direct", [](const Eigen::Ref &v) { py::module_::import("numpy").attr("ones")(10); return v(5); }); diff --git a/3rdparty/pybind11/tests/test_eigen.py b/3rdparty/pybind11/tests/test_eigen.py index a131dc15..9c6485de 100644 --- a/3rdparty/pybind11/tests/test_eigen.py +++ b/3rdparty/pybind11/tests/test_eigen.py @@ -1,5 +1,5 @@ -# -*- coding: utf-8 -*- import pytest + from pybind11_tests import ConstructorStats np = pytest.importorskip("numpy") @@ -200,7 +200,7 @@ def test_negative_stride_from_python(msg): double_threer(): incompatible function arguments. The following argument types are supported: 1. (arg0: numpy.ndarray[numpy.float32[1, 3], flags.writeable]) -> None - Invoked with: """ # noqa: E501 line too long + Invoked with: """ + repr(np.array([5.0, 4.0, 3.0], dtype="float32")) ) @@ -212,7 +212,7 @@ def test_negative_stride_from_python(msg): double_threec(): incompatible function arguments. The following argument types are supported: 1. (arg0: numpy.ndarray[numpy.float32[3, 1], flags.writeable]) -> None - Invoked with: """ # noqa: E501 line too long + Invoked with: """ + repr(np.array([7.0, 4.0, 1.0], dtype="float32")) ) @@ -221,9 +221,7 @@ def test_nonunit_stride_to_python(): assert np.all(m.diagonal(ref) == ref.diagonal()) assert np.all(m.diagonal_1(ref) == ref.diagonal(1)) for i in range(-5, 7): - assert np.all( - m.diagonal_n(ref, i) == ref.diagonal(i) - ), "m.diagonal_n({})".format(i) + assert np.all(m.diagonal_n(ref, i) == ref.diagonal(i)), f"m.diagonal_n({i})" assert np.all(m.block(ref, 2, 1, 3, 3) == ref[2:5, 1:4]) assert np.all(m.block(ref, 1, 4, 4, 2) == ref[1:, 4:]) @@ -236,7 +234,7 @@ def test_eigen_ref_to_python(): mymat = chol(np.array([[1.0, 2, 4], [2, 13, 23], [4, 23, 77]])) assert np.all( mymat == np.array([[1, 0, 0], [2, 3, 0], [4, 5, 6]]) - ), "cholesky{}".format(i) + ), f"cholesky{i}" def assign_both(a1, a2, r, c, v): @@ -253,14 +251,14 @@ def array_copy_but_one(a, r, c, v): def test_eigen_return_references(): """Tests various ways of returning references and non-referencing copies""" - master = np.ones((10, 10)) + primary = np.ones((10, 10)) a = m.ReturnTester() a_get1 = a.get() assert not a_get1.flags.owndata and a_get1.flags.writeable - assign_both(a_get1, master, 3, 3, 5) + assign_both(a_get1, primary, 3, 3, 5) a_get2 = a.get_ptr() assert not a_get2.flags.owndata and a_get2.flags.writeable - assign_both(a_get1, master, 2, 3, 6) + assign_both(a_get1, primary, 2, 3, 6) a_view1 = a.view() assert not a_view1.flags.owndata and not a_view1.flags.writeable @@ -273,25 +271,25 @@ def test_eigen_return_references(): a_copy1 = a.copy_get() assert a_copy1.flags.owndata and a_copy1.flags.writeable - np.testing.assert_array_equal(a_copy1, master) + np.testing.assert_array_equal(a_copy1, primary) a_copy1[7, 7] = -44 # Shouldn't affect anything else - c1want = array_copy_but_one(master, 7, 7, -44) + c1want = array_copy_but_one(primary, 7, 7, -44) a_copy2 = a.copy_view() assert a_copy2.flags.owndata and a_copy2.flags.writeable - np.testing.assert_array_equal(a_copy2, master) + np.testing.assert_array_equal(a_copy2, primary) a_copy2[4, 4] = -22 # Shouldn't affect anything else - c2want = array_copy_but_one(master, 4, 4, -22) + c2want = array_copy_but_one(primary, 4, 4, -22) a_ref1 = a.ref() assert not a_ref1.flags.owndata and a_ref1.flags.writeable - assign_both(a_ref1, master, 1, 1, 15) + assign_both(a_ref1, primary, 1, 1, 15) a_ref2 = a.ref_const() assert not a_ref2.flags.owndata and not a_ref2.flags.writeable with pytest.raises(ValueError): a_ref2[5, 5] = 33 a_ref3 = a.ref_safe() assert not a_ref3.flags.owndata and a_ref3.flags.writeable - assign_both(a_ref3, master, 0, 7, 99) + assign_both(a_ref3, primary, 0, 7, 99) a_ref4 = a.ref_const_safe() assert not a_ref4.flags.owndata and not a_ref4.flags.writeable with pytest.raises(ValueError): @@ -299,23 +297,23 @@ def test_eigen_return_references(): a_copy3 = a.copy_ref() assert a_copy3.flags.owndata and a_copy3.flags.writeable - np.testing.assert_array_equal(a_copy3, master) + np.testing.assert_array_equal(a_copy3, primary) a_copy3[8, 1] = 11 - c3want = array_copy_but_one(master, 8, 1, 11) + c3want = array_copy_but_one(primary, 8, 1, 11) a_copy4 = a.copy_ref_const() assert a_copy4.flags.owndata and a_copy4.flags.writeable - np.testing.assert_array_equal(a_copy4, master) + np.testing.assert_array_equal(a_copy4, primary) a_copy4[8, 4] = 88 - c4want = array_copy_but_one(master, 8, 4, 88) + c4want = array_copy_but_one(primary, 8, 4, 88) a_block1 = a.block(3, 3, 2, 2) assert not a_block1.flags.owndata and a_block1.flags.writeable a_block1[0, 0] = 55 - master[3, 3] = 55 + primary[3, 3] = 55 a_block2 = a.block_safe(2, 2, 3, 2) assert not a_block2.flags.owndata and a_block2.flags.writeable a_block2[2, 1] = -123 - master[4, 3] = -123 + primary[4, 3] = -123 a_block3 = a.block_const(6, 7, 4, 3) assert not a_block3.flags.owndata and not a_block3.flags.writeable with pytest.raises(ValueError): @@ -323,18 +321,18 @@ def test_eigen_return_references(): a_copy5 = a.copy_block(2, 2, 2, 3) assert a_copy5.flags.owndata and a_copy5.flags.writeable - np.testing.assert_array_equal(a_copy5, master[2:4, 2:5]) + np.testing.assert_array_equal(a_copy5, primary[2:4, 2:5]) a_copy5[1, 1] = 777 - c5want = array_copy_but_one(master[2:4, 2:5], 1, 1, 777) + c5want = array_copy_but_one(primary[2:4, 2:5], 1, 1, 777) a_corn1 = a.corners() assert not a_corn1.flags.owndata and a_corn1.flags.writeable a_corn1 *= 50 a_corn1[1, 1] = 999 - master[0, 0] = 50 - master[0, 9] = 50 - master[9, 0] = 50 - master[9, 9] = 999 + primary[0, 0] = 50 + primary[0, 9] = 50 + primary[9, 0] = 50 + primary[9, 9] = 999 a_corn2 = a.corners_const() assert not a_corn2.flags.owndata and not a_corn2.flags.writeable with pytest.raises(ValueError): @@ -342,22 +340,22 @@ def test_eigen_return_references(): # All of the changes made all the way along should be visible everywhere # now (except for the copies, of course) - np.testing.assert_array_equal(a_get1, master) - np.testing.assert_array_equal(a_get2, master) - np.testing.assert_array_equal(a_view1, master) - np.testing.assert_array_equal(a_view2, master) - np.testing.assert_array_equal(a_ref1, master) - np.testing.assert_array_equal(a_ref2, master) - np.testing.assert_array_equal(a_ref3, master) - np.testing.assert_array_equal(a_ref4, master) - np.testing.assert_array_equal(a_block1, master[3:5, 3:5]) - np.testing.assert_array_equal(a_block2, master[2:5, 2:4]) - np.testing.assert_array_equal(a_block3, master[6:10, 7:10]) + np.testing.assert_array_equal(a_get1, primary) + np.testing.assert_array_equal(a_get2, primary) + np.testing.assert_array_equal(a_view1, primary) + np.testing.assert_array_equal(a_view2, primary) + np.testing.assert_array_equal(a_ref1, primary) + np.testing.assert_array_equal(a_ref2, primary) + np.testing.assert_array_equal(a_ref3, primary) + np.testing.assert_array_equal(a_ref4, primary) + np.testing.assert_array_equal(a_block1, primary[3:5, 3:5]) + np.testing.assert_array_equal(a_block2, primary[2:5, 2:4]) + np.testing.assert_array_equal(a_block3, primary[6:10, 7:10]) np.testing.assert_array_equal( - a_corn1, master[0 :: master.shape[0] - 1, 0 :: master.shape[1] - 1] + a_corn1, primary[0 :: primary.shape[0] - 1, 0 :: primary.shape[1] - 1] ) np.testing.assert_array_equal( - a_corn2, master[0 :: master.shape[0] - 1, 0 :: master.shape[1] - 1] + a_corn2, primary[0 :: primary.shape[0] - 1, 0 :: primary.shape[1] - 1] ) np.testing.assert_array_equal(a_copy1, c1want) @@ -723,13 +721,13 @@ def test_sparse_signature(doc): doc(m.sparse_copy_r) == """ sparse_copy_r(arg0: scipy.sparse.csr_matrix[numpy.float32]) -> scipy.sparse.csr_matrix[numpy.float32] - """ # noqa: E501 line too long + """ ) assert ( doc(m.sparse_copy_c) == """ sparse_copy_c(arg0: scipy.sparse.csc_matrix[numpy.float32]) -> scipy.sparse.csc_matrix[numpy.float32] - """ # noqa: E501 line too long + """ ) @@ -746,6 +744,13 @@ def test_issue738(): ) +@pytest.mark.parametrize("func", [m.iss738_f1, m.iss738_f2]) +@pytest.mark.parametrize("sizes", [(0, 2), (2, 0)]) +def test_zero_length(func, sizes): + """Ignore strides on a length-0 dimension (even if they would be incompatible length > 1)""" + assert np.all(func(np.zeros(sizes)) == np.zeros(sizes)) + + def test_issue1105(): """Issue 1105: 1xN or Nx1 input arrays weren't accepted for eigen compile-time row vectors or column vector""" diff --git a/3rdparty/pybind11/tests/test_embed/CMakeLists.txt b/3rdparty/pybind11/tests/test_embed/CMakeLists.txt index fabcb24e..09a36939 100644 --- a/3rdparty/pybind11/tests/test_embed/CMakeLists.txt +++ b/3rdparty/pybind11/tests/test_embed/CMakeLists.txt @@ -1,10 +1,13 @@ +possibly_uninitialized(PYTHON_MODULE_EXTENSION Python_INTERPRETER_ID) + if("${PYTHON_MODULE_EXTENSION}" MATCHES "pypy" OR "${Python_INTERPRETER_ID}" STREQUAL "PyPy") + message(STATUS "Skipping embed test on PyPy") add_custom_target(cpptest) # Dummy target on PyPy. Embedding is not supported. set(_suppress_unused_variable_warning "${DOWNLOAD_CATCH}") return() endif() -find_package(Catch 2.13.2) +find_package(Catch 2.13.9) if(CATCH_FOUND) message(STATUS "Building interpreter tests using Catch v${CATCH_VERSION}") @@ -22,12 +25,13 @@ pybind11_enable_warnings(test_embed) target_link_libraries(test_embed PRIVATE pybind11::embed Catch2::Catch2 Threads::Threads) if(NOT CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_CURRENT_BINARY_DIR) - file(COPY test_interpreter.py DESTINATION "${CMAKE_CURRENT_BINARY_DIR}") + file(COPY test_interpreter.py test_trampoline.py DESTINATION "${CMAKE_CURRENT_BINARY_DIR}") endif() add_custom_target( cpptest COMMAND "$" + DEPENDS test_embed WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}") pybind11_add_module(external_module THIN_LTO external_module.cpp) diff --git a/3rdparty/pybind11/tests/test_embed/catch.cpp b/3rdparty/pybind11/tests/test_embed/catch.cpp index dd137385..a03a8b37 100644 --- a/3rdparty/pybind11/tests/test_embed/catch.cpp +++ b/3rdparty/pybind11/tests/test_embed/catch.cpp @@ -4,9 +4,14 @@ #include #ifdef _MSC_VER -// Silence MSVC C++17 deprecation warning from Catch regarding std::uncaught_exceptions (up to catch -// 2.0.1; this should be fixed in the next catch release after 2.0.1). -# pragma warning(disable: 4996) +// Silence MSVC C++17 deprecation warning from Catch regarding std::uncaught_exceptions (up to +// catch 2.0.1; this should be fixed in the next catch release after 2.0.1). +# pragma warning(disable : 4996) +#endif + +// Catch uses _ internally, which breaks gettext style defines +#ifdef _ +# undef _ #endif #define CATCH_CONFIG_RUNNER @@ -15,7 +20,25 @@ namespace py = pybind11; int main(int argc, char *argv[]) { + // Setup for TEST_CASE in test_interpreter.cpp, tagging on a large random number: + std::string updated_pythonpath("pybind11_test_embed_PYTHONPATH_2099743835476552"); + const char *preexisting_pythonpath = getenv("PYTHONPATH"); + if (preexisting_pythonpath != nullptr) { +#if defined(_WIN32) + updated_pythonpath += ';'; +#else + updated_pythonpath += ':'; +#endif + updated_pythonpath += preexisting_pythonpath; + } +#if defined(_WIN32) + _putenv_s("PYTHONPATH", updated_pythonpath.c_str()); +#else + setenv("PYTHONPATH", updated_pythonpath.c_str(), /*replace=*/1); +#endif + py::scoped_interpreter guard{}; + auto result = Catch::Session().run(argc, argv); return result < 0xff ? result : 0xff; diff --git a/3rdparty/pybind11/tests/test_embed/external_module.cpp b/3rdparty/pybind11/tests/test_embed/external_module.cpp index e9a6058b..5c482fe0 100644 --- a/3rdparty/pybind11/tests/test_embed/external_module.cpp +++ b/3rdparty/pybind11/tests/test_embed/external_module.cpp @@ -9,15 +9,12 @@ namespace py = pybind11; PYBIND11_MODULE(external_module, m) { class A { public: - A(int value) : v{value} {}; + explicit A(int value) : v{value} {}; int v; }; - py::class_(m, "A") - .def(py::init()) - .def_readwrite("value", &A::v); + py::class_(m, "A").def(py::init()).def_readwrite("value", &A::v); - m.def("internals_at", []() { - return reinterpret_cast(&py::detail::get_internals()); - }); + m.def("internals_at", + []() { return reinterpret_cast(&py::detail::get_internals()); }); } diff --git a/3rdparty/pybind11/tests/test_embed/test_interpreter.cpp b/3rdparty/pybind11/tests/test_embed/test_interpreter.cpp index 944334ce..6299293b 100644 --- a/3rdparty/pybind11/tests/test_embed/test_interpreter.cpp +++ b/3rdparty/pybind11/tests/test_embed/test_interpreter.cpp @@ -1,27 +1,29 @@ #include #ifdef _MSC_VER -// Silence MSVC C++17 deprecation warning from Catch regarding std::uncaught_exceptions (up to catch -// 2.0.1; this should be fixed in the next catch release after 2.0.1). -# pragma warning(disable: 4996) +// Silence MSVC C++17 deprecation warning from Catch regarding std::uncaught_exceptions (up to +// catch 2.0.1; this should be fixed in the next catch release after 2.0.1). +# pragma warning(disable : 4996) #endif #include - -#include +#include #include #include +#include +#include namespace py = pybind11; using namespace py::literals; class Widget { public: - Widget(std::string message) : message(message) { } + explicit Widget(std::string message) : message(std::move(message)) {} virtual ~Widget() = default; std::string the_message() const { return message; } virtual int the_answer() const = 0; + virtual std::string argv0() const = 0; private: std::string message; @@ -31,6 +33,23 @@ class PyWidget final : public Widget { using Widget::Widget; int the_answer() const override { PYBIND11_OVERRIDE_PURE(int, Widget, the_answer); } + std::string argv0() const override { PYBIND11_OVERRIDE_PURE(std::string, Widget, argv0); } +}; + +class test_override_cache_helper { + +public: + virtual int func() { return 0; } + + test_override_cache_helper() = default; + virtual ~test_override_cache_helper() = default; + // Non-copyable + test_override_cache_helper &operator=(test_override_cache_helper const &Right) = delete; + test_override_cache_helper(test_override_cache_helper const &Copy) = delete; +}; + +class test_override_cache_helper_trampoline : public test_override_cache_helper { + int func() override { PYBIND11_OVERRIDE(int, test_override_cache_helper, func); } }; PYBIND11_EMBEDDED_MODULE(widget_module, m) { @@ -41,24 +60,39 @@ PYBIND11_EMBEDDED_MODULE(widget_module, m) { m.def("add", [](int i, int j) { return i + j; }); } -PYBIND11_EMBEDDED_MODULE(throw_exception, ) { - throw std::runtime_error("C++ Error"); +PYBIND11_EMBEDDED_MODULE(trampoline_module, m) { + py::class_>(m, "test_override_cache_helper") + .def(py::init_alias<>()) + .def("func", &test_override_cache_helper::func); } +PYBIND11_EMBEDDED_MODULE(throw_exception, ) { throw std::runtime_error("C++ Error"); } + PYBIND11_EMBEDDED_MODULE(throw_error_already_set, ) { auto d = py::dict(); d["missing"].cast(); } +TEST_CASE("PYTHONPATH is used to update sys.path") { + // The setup for this TEST_CASE is in catch.cpp! + auto sys_path = py::str(py::module_::import("sys").attr("path")).cast(); + REQUIRE_THAT(sys_path, + Catch::Matchers::Contains("pybind11_test_embed_PYTHONPATH_2099743835476552")); +} + TEST_CASE("Pass classes and data between modules defined in C++ and Python") { auto module_ = py::module_::import("test_interpreter"); REQUIRE(py::hasattr(module_, "DerivedWidget")); - auto locals = py::dict("hello"_a="Hello, World!", "x"_a=5, **module_.attr("__dict__")); + auto locals = py::dict("hello"_a = "Hello, World!", "x"_a = 5, **module_.attr("__dict__")); py::exec(R"( widget = DerivedWidget("{} - {}".format(hello, x)) message = widget.the_message - )", py::globals(), locals); + )", + py::globals(), + locals); REQUIRE(locals["message"].cast() == "Hello, World! - 5"); auto py_widget = module_.attr("DerivedWidget")("The question"); @@ -69,12 +103,51 @@ TEST_CASE("Pass classes and data between modules defined in C++ and Python") { REQUIRE(cpp_widget.the_answer() == 42); } +TEST_CASE("Override cache") { + auto module_ = py::module_::import("test_trampoline"); + REQUIRE(py::hasattr(module_, "func")); + REQUIRE(py::hasattr(module_, "func2")); + + auto locals = py::dict(**module_.attr("__dict__")); + + int i = 0; + for (; i < 1500; ++i) { + std::shared_ptr p_obj; + std::shared_ptr p_obj2; + + py::object loc_inst = locals["func"](); + p_obj = py::cast>(loc_inst); + + int ret = p_obj->func(); + + REQUIRE(ret == 42); + + loc_inst = locals["func2"](); + + p_obj2 = py::cast>(loc_inst); + + p_obj2->func(); + } +} + TEST_CASE("Import error handling") { REQUIRE_NOTHROW(py::module_::import("widget_module")); - REQUIRE_THROWS_WITH(py::module_::import("throw_exception"), - "ImportError: C++ Error"); + REQUIRE_THROWS_WITH(py::module_::import("throw_exception"), "ImportError: C++ Error"); REQUIRE_THROWS_WITH(py::module_::import("throw_error_already_set"), - Catch::Contains("ImportError: KeyError")); + Catch::Contains("ImportError: initialization failed")); + + auto locals = py::dict("is_keyerror"_a = false, "message"_a = "not set"); + py::exec(R"( + try: + import throw_error_already_set + except ImportError as e: + is_keyerror = type(e.__cause__) == KeyError + message = str(e.__cause__) + )", + py::globals(), + locals); + REQUIRE(locals["is_keyerror"].cast() == true); + REQUIRE(locals["message"].cast() == "'missing'"); } TEST_CASE("There can be only one interpreter") { @@ -102,7 +175,7 @@ bool has_pybind11_internals_builtin() { bool has_pybind11_internals_static() { auto **&ipp = py::detail::get_internals_pp(); - return ipp && *ipp; + return (ipp != nullptr) && (*ipp != nullptr); } TEST_CASE("Restart the interpreter") { @@ -110,11 +183,12 @@ TEST_CASE("Restart the interpreter") { REQUIRE(py::module_::import("widget_module").attr("add")(1, 2).cast() == 3); REQUIRE(has_pybind11_internals_builtin()); REQUIRE(has_pybind11_internals_static()); - REQUIRE(py::module_::import("external_module").attr("A")(123).attr("value").cast() == 123); + REQUIRE(py::module_::import("external_module").attr("A")(123).attr("value").cast() + == 123); // local and foreign module internals should point to the same internals: - REQUIRE(reinterpret_cast(*py::detail::get_internals_pp()) == - py::module_::import("external_module").attr("internals_at")().cast()); + REQUIRE(reinterpret_cast(*py::detail::get_internals_pp()) + == py::module_::import("external_module").attr("internals_at")().cast()); // Restart the interpreter. py::finalize_interpreter(); @@ -129,16 +203,19 @@ TEST_CASE("Restart the interpreter") { pybind11::detail::get_internals(); REQUIRE(has_pybind11_internals_builtin()); REQUIRE(has_pybind11_internals_static()); - REQUIRE(reinterpret_cast(*py::detail::get_internals_pp()) == - py::module_::import("external_module").attr("internals_at")().cast()); + REQUIRE(reinterpret_cast(*py::detail::get_internals_pp()) + == py::module_::import("external_module").attr("internals_at")().cast()); // Make sure that an interpreter with no get_internals() created until finalize still gets the // internals destroyed py::finalize_interpreter(); py::initialize_interpreter(); bool ran = false; - py::module_::import("__main__").attr("internals_destroy_test") = - py::capsule(&ran, [](void *ran) { py::detail::get_internals(); *static_cast(ran) = true; }); + py::module_::import("__main__").attr("internals_destroy_test") + = py::capsule(&ran, [](void *ran) { + py::detail::get_internals(); + *static_cast(ran) = true; + }); REQUIRE_FALSE(has_pybind11_internals_builtin()); REQUIRE_FALSE(has_pybind11_internals_static()); REQUIRE_FALSE(ran); @@ -171,8 +248,8 @@ TEST_CASE("Subinterpreter") { REQUIRE(has_pybind11_internals_static()); /// Create and switch to a subinterpreter. - auto main_tstate = PyThreadState_Get(); - auto sub_tstate = Py_NewInterpreter(); + auto *main_tstate = PyThreadState_Get(); + auto *sub_tstate = Py_NewInterpreter(); // Subinterpreters get their own copy of builtins. detail::get_internals() still // works by returning from the static variable, i.e. all interpreters share a single @@ -212,7 +289,7 @@ TEST_CASE("Threads") { REQUIRE_FALSE(has_pybind11_internals_static()); constexpr auto num_threads = 10; - auto locals = py::dict("count"_a=0); + auto locals = py::dict("count"_a = 0); { py::gil_scoped_release gil_release{}; @@ -238,7 +315,11 @@ TEST_CASE("Threads") { struct scope_exit { std::function f_; explicit scope_exit(std::function f) noexcept : f_(std::move(f)) {} - ~scope_exit() { if (f_) f_(); } + ~scope_exit() { + if (f_) { + f_(); + } + } }; TEST_CASE("Reload module from file") { @@ -249,9 +330,8 @@ TEST_CASE("Reload module from file") { bool dont_write_bytecode = sys.attr("dont_write_bytecode").cast(); sys.attr("dont_write_bytecode") = true; // Reset the value at scope exit - scope_exit reset_dont_write_bytecode([&]() { - sys.attr("dont_write_bytecode") = dont_write_bytecode; - }); + scope_exit reset_dont_write_bytecode( + [&]() { sys.attr("dont_write_bytecode") = dont_write_bytecode; }); std::string module_name = "test_module_reload"; std::string module_file = module_name + ".py"; @@ -262,9 +342,7 @@ TEST_CASE("Reload module from file") { test_module << " return 1\n"; test_module.close(); // Delete the file at scope exit - scope_exit delete_module_file([&]() { - std::remove(module_file.c_str()); - }); + scope_exit delete_module_file([&]() { std::remove(module_file.c_str()); }); // Import the module from file auto module_ = py::module_::import(module_name.c_str()); @@ -282,3 +360,43 @@ TEST_CASE("Reload module from file") { result = module_.attr("test")().cast(); REQUIRE(result == 2); } + +TEST_CASE("sys.argv gets initialized properly") { + py::finalize_interpreter(); + { + py::scoped_interpreter default_scope; + auto module = py::module::import("test_interpreter"); + auto py_widget = module.attr("DerivedWidget")("The question"); + const auto &cpp_widget = py_widget.cast(); + REQUIRE(cpp_widget.argv0().empty()); + } + + { + char *argv[] = {strdup("a.out")}; + py::scoped_interpreter argv_scope(true, 1, argv); + std::free(argv[0]); + auto module = py::module::import("test_interpreter"); + auto py_widget = module.attr("DerivedWidget")("The question"); + const auto &cpp_widget = py_widget.cast(); + REQUIRE(cpp_widget.argv0() == "a.out"); + } + py::initialize_interpreter(); +} + +TEST_CASE("make_iterator can be called before then after finalizing an interpreter") { + // Reproduction of issue #2101 (https://github.com/pybind/pybind11/issues/2101) + py::finalize_interpreter(); + + std::vector container; + { + pybind11::scoped_interpreter g; + auto iter = pybind11::make_iterator(container.begin(), container.end()); + } + + REQUIRE_NOTHROW([&]() { + pybind11::scoped_interpreter g; + auto iter = pybind11::make_iterator(container.begin(), container.end()); + }()); + + py::initialize_interpreter(); +} diff --git a/3rdparty/pybind11/tests/test_embed/test_interpreter.py b/3rdparty/pybind11/tests/test_embed/test_interpreter.py index 6174ede4..f2794497 100644 --- a/3rdparty/pybind11/tests/test_embed/test_interpreter.py +++ b/3rdparty/pybind11/tests/test_embed/test_interpreter.py @@ -1,10 +1,14 @@ -# -*- coding: utf-8 -*- +import sys + from widget_module import Widget class DerivedWidget(Widget): def __init__(self, message): - super(DerivedWidget, self).__init__(message) + super().__init__(message) def the_answer(self): return 42 + + def argv0(self): + return sys.argv[0] diff --git a/3rdparty/pybind11/tests/test_embed/test_trampoline.py b/3rdparty/pybind11/tests/test_embed/test_trampoline.py new file mode 100644 index 00000000..8e14e8ef --- /dev/null +++ b/3rdparty/pybind11/tests/test_embed/test_trampoline.py @@ -0,0 +1,16 @@ +import trampoline_module + + +def func(): + class Test(trampoline_module.test_override_cache_helper): + def func(self): + return 42 + + return Test() + + +def func2(): + class Test(trampoline_module.test_override_cache_helper): + pass + + return Test() diff --git a/3rdparty/pybind11/tests/test_enum.cpp b/3rdparty/pybind11/tests/test_enum.cpp index 31530892..2597b275 100644 --- a/3rdparty/pybind11/tests/test_enum.cpp +++ b/3rdparty/pybind11/tests/test_enum.cpp @@ -11,11 +11,7 @@ TEST_SUBMODULE(enums, m) { // test_unscoped_enum - enum UnscopedEnum { - EOne = 1, - ETwo, - EThree - }; + enum UnscopedEnum { EOne = 1, ETwo, EThree }; py::enum_(m, "UnscopedEnum", py::arithmetic(), "An unscoped enumeration") .value("EOne", EOne, "Docstring for EOne") .value("ETwo", ETwo, "Docstring for ETwo") @@ -23,10 +19,7 @@ TEST_SUBMODULE(enums, m) { .export_values(); // test_scoped_enum - enum class ScopedEnum { - Two = 2, - Three - }; + enum class ScopedEnum { Two = 2, Three }; py::enum_(m, "ScopedEnum", py::arithmetic()) .value("Two", ScopedEnum::Two) .value("Three", ScopedEnum::Three); @@ -36,11 +29,7 @@ TEST_SUBMODULE(enums, m) { }); // test_binary_operators - enum Flags { - Read = 4, - Write = 2, - Execute = 1 - }; + enum Flags { Read = 4, Write = 2, Execute = 1 }; py::enum_(m, "Flags", py::arithmetic()) .value("Read", Flags::Read) .value("Write", Flags::Write) @@ -50,14 +39,9 @@ TEST_SUBMODULE(enums, m) { // test_implicit_conversion class ClassWithUnscopedEnum { public: - enum EMode { - EFirstMode = 1, - ESecondMode - }; - - static EMode test_function(EMode mode) { - return mode; - } + enum EMode { EFirstMode = 1, ESecondMode }; + + static EMode test_function(EMode mode) { return mode; } }; py::class_ exenum_class(m, "ClassWithUnscopedEnum"); exenum_class.def_static("test_function", &ClassWithUnscopedEnum::test_function); @@ -67,21 +51,83 @@ TEST_SUBMODULE(enums, m) { .export_values(); // test_enum_to_int - m.def("test_enum_to_int", [](int) { }); - m.def("test_enum_to_uint", [](uint32_t) { }); - m.def("test_enum_to_long_long", [](long long) { }); + m.def("test_enum_to_int", [](int) {}); + m.def("test_enum_to_uint", [](uint32_t) {}); + m.def("test_enum_to_long_long", [](long long) {}); // test_duplicate_enum_name - enum SimpleEnum - { - ONE, TWO, THREE - }; + enum SimpleEnum { ONE, TWO, THREE }; m.def("register_bad_enum", [m]() { py::enum_(m, "SimpleEnum") - .value("ONE", SimpleEnum::ONE) //NOTE: all value function calls are called with the same first parameter value + .value("ONE", SimpleEnum::ONE) // NOTE: all value function calls are called with the + // same first parameter value .value("ONE", SimpleEnum::TWO) .value("ONE", SimpleEnum::THREE) .export_values(); }); + + // test_enum_scalar + enum UnscopedUCharEnum : unsigned char {}; + enum class ScopedShortEnum : short {}; + enum class ScopedLongEnum : long {}; + enum UnscopedUInt64Enum : std::uint64_t {}; + static_assert( + py::detail::all_of< + std::is_same::Scalar, unsigned char>, + std::is_same::Scalar, short>, + std::is_same::Scalar, long>, + std::is_same::Scalar, std::uint64_t>>::value, + "Error during the deduction of enum's scalar type with normal integer underlying"); + + // test_enum_scalar_with_char_underlying + enum class ScopedCharEnum : char { Zero, Positive }; + enum class ScopedWCharEnum : wchar_t { Zero, Positive }; + enum class ScopedChar32Enum : char32_t { Zero, Positive }; + enum class ScopedChar16Enum : char16_t { Zero, Positive }; + + // test the scalar of char type enums according to chapter 'Character types' + // from https://en.cppreference.com/w/cpp/language/types + static_assert( + py::detail::any_of< + std::is_same::Scalar, signed char>, // e.g. gcc on x86 + std::is_same::Scalar, unsigned char> // e.g. arm linux + >::value, + "char should be cast to either signed char or unsigned char"); + static_assert(sizeof(py::enum_::Scalar) == 2 + || sizeof(py::enum_::Scalar) == 4, + "wchar_t should be either 16 bits (Windows) or 32 (everywhere else)"); + static_assert( + py::detail::all_of< + std::is_same::Scalar, std::uint_least32_t>, + std::is_same::Scalar, std::uint_least16_t>>::value, + "char32_t, char16_t (and char8_t)'s size, signedness, and alignment is determined"); +#if defined(PYBIND11_HAS_U8STRING) + enum class ScopedChar8Enum : char8_t { Zero, Positive }; + static_assert(std::is_same::Scalar, unsigned char>::value); +#endif + + // test_char_underlying_enum + py::enum_(m, "ScopedCharEnum") + .value("Zero", ScopedCharEnum::Zero) + .value("Positive", ScopedCharEnum::Positive); + py::enum_(m, "ScopedWCharEnum") + .value("Zero", ScopedWCharEnum::Zero) + .value("Positive", ScopedWCharEnum::Positive); + py::enum_(m, "ScopedChar32Enum") + .value("Zero", ScopedChar32Enum::Zero) + .value("Positive", ScopedChar32Enum::Positive); + py::enum_(m, "ScopedChar16Enum") + .value("Zero", ScopedChar16Enum::Zero) + .value("Positive", ScopedChar16Enum::Positive); + + // test_bool_underlying_enum + enum class ScopedBoolEnum : bool { FALSE, TRUE }; + + // bool is unsigned (std::is_signed returns false) and 1-byte long, so represented with u8 + static_assert(std::is_same::Scalar, std::uint8_t>::value, ""); + + py::enum_(m, "ScopedBoolEnum") + .value("FALSE", ScopedBoolEnum::FALSE) + .value("TRUE", ScopedBoolEnum::TRUE); } diff --git a/3rdparty/pybind11/tests/test_enum.py b/3rdparty/pybind11/tests/test_enum.py index f6b24fc2..f14a7239 100644 --- a/3rdparty/pybind11/tests/test_enum.py +++ b/3rdparty/pybind11/tests/test_enum.py @@ -1,5 +1,5 @@ -# -*- coding: utf-8 -*- import pytest + from pybind11_tests import enums as m @@ -13,15 +13,24 @@ def test_unscoped_enum(): # name property assert m.UnscopedEnum.EOne.name == "EOne" + assert m.UnscopedEnum.EOne.value == 1 assert m.UnscopedEnum.ETwo.name == "ETwo" - assert m.EOne.name == "EOne" - # name readonly + assert m.UnscopedEnum.ETwo.value == 2 + assert m.EOne is m.UnscopedEnum.EOne + # name, value readonly with pytest.raises(AttributeError): m.UnscopedEnum.EOne.name = "" - # name returns a copy - foo = m.UnscopedEnum.EOne.name - foo = "bar" + with pytest.raises(AttributeError): + m.UnscopedEnum.EOne.value = 10 + # name, value returns a copy + # TODO: Neither the name nor value tests actually check against aliasing. + # Use a mutable type that has reference semantics. + nonaliased_name = m.UnscopedEnum.EOne.name + nonaliased_name = "bar" # noqa: F841 assert m.UnscopedEnum.EOne.name == "EOne" + nonaliased_value = m.UnscopedEnum.EOne.value + nonaliased_value = 10 # noqa: F841 + assert m.UnscopedEnum.EOne.value == 1 # __members__ property assert m.UnscopedEnum.__members__ == { @@ -33,8 +42,8 @@ def test_unscoped_enum(): with pytest.raises(AttributeError): m.UnscopedEnum.__members__ = {} # __members__ returns a copy - foo = m.UnscopedEnum.__members__ - foo["bar"] = "baz" + nonaliased_members = m.UnscopedEnum.__members__ + nonaliased_members["bar"] = "baz" assert m.UnscopedEnum.__members__ == { "EOne": m.UnscopedEnum.EOne, "ETwo": m.UnscopedEnum.ETwo, @@ -73,16 +82,16 @@ Members: assert not (y == "2") with pytest.raises(TypeError): - y < object() + y < object() # noqa: B015 with pytest.raises(TypeError): - y <= object() + y <= object() # noqa: B015 with pytest.raises(TypeError): - y > object() + y > object() # noqa: B015 with pytest.raises(TypeError): - y >= object() + y >= object() # noqa: B015 with pytest.raises(TypeError): y | object() @@ -134,13 +143,13 @@ def test_scoped_enum(): assert not (z == object()) # Scoped enums will *NOT* accept >, <, >= and <= int comparisons (Will throw exceptions) with pytest.raises(TypeError): - z > 3 + z > 3 # noqa: B015 with pytest.raises(TypeError): - z < 3 + z < 3 # noqa: B015 with pytest.raises(TypeError): - z >= 3 + z >= 3 # noqa: B015 with pytest.raises(TypeError): - z <= 3 + z <= 3 # noqa: B015 # order assert m.ScopedEnum.Two < m.ScopedEnum.Three @@ -208,10 +217,16 @@ def test_binary_operators(): def test_enum_to_int(): m.test_enum_to_int(m.Flags.Read) m.test_enum_to_int(m.ClassWithUnscopedEnum.EMode.EFirstMode) + m.test_enum_to_int(m.ScopedCharEnum.Positive) + m.test_enum_to_int(m.ScopedBoolEnum.TRUE) m.test_enum_to_uint(m.Flags.Read) m.test_enum_to_uint(m.ClassWithUnscopedEnum.EMode.EFirstMode) + m.test_enum_to_uint(m.ScopedCharEnum.Positive) + m.test_enum_to_uint(m.ScopedBoolEnum.TRUE) m.test_enum_to_long_long(m.Flags.Read) m.test_enum_to_long_long(m.ClassWithUnscopedEnum.EMode.EFirstMode) + m.test_enum_to_long_long(m.ScopedCharEnum.Positive) + m.test_enum_to_long_long(m.ScopedBoolEnum.TRUE) def test_duplicate_enum_name(): @@ -220,6 +235,28 @@ def test_duplicate_enum_name(): assert str(excinfo.value) == 'SimpleEnum: element "ONE" already exists!' +def test_char_underlying_enum(): # Issue #1331/PR #1334: + assert type(m.ScopedCharEnum.Positive.__int__()) is int + assert int(m.ScopedChar16Enum.Zero) == 0 + assert hash(m.ScopedChar32Enum.Positive) == 1 + assert type(m.ScopedCharEnum.Positive.__getstate__()) is int + assert m.ScopedWCharEnum(1) == m.ScopedWCharEnum.Positive + with pytest.raises(TypeError): + # Even if the underlying type is char, only an int can be used to construct the enum: + m.ScopedCharEnum("0") + + +def test_bool_underlying_enum(): + assert type(m.ScopedBoolEnum.TRUE.__int__()) is int + assert int(m.ScopedBoolEnum.FALSE) == 0 + assert hash(m.ScopedBoolEnum.TRUE) == 1 + assert type(m.ScopedBoolEnum.TRUE.__getstate__()) is int + assert m.ScopedBoolEnum(1) == m.ScopedBoolEnum.TRUE + # Enum could construct with a bool + # (bool is a strict subclass of int, and False will be converted to 0) + assert m.ScopedBoolEnum(False) == m.ScopedBoolEnum.FALSE + + def test_docstring_signatures(): for enum_type in [m.ScopedEnum, m.UnscopedEnum]: for attr in enum_type.__dict__.values(): diff --git a/3rdparty/pybind11/tests/test_eval.cpp b/3rdparty/pybind11/tests/test_eval.cpp index 5416c2ec..cd2903f0 100644 --- a/3rdparty/pybind11/tests/test_eval.cpp +++ b/3rdparty/pybind11/tests/test_eval.cpp @@ -7,10 +7,12 @@ BSD-style license that can be found in the LICENSE file. */ - #include + #include "pybind11_tests.h" +#include + TEST_SUBMODULE(eval_, m) { // test_evals @@ -18,16 +20,13 @@ TEST_SUBMODULE(eval_, m) { m.def("test_eval_statements", [global]() { auto local = py::dict(); - local["call_test"] = py::cpp_function([&]() -> int { - return 42; - }); + local["call_test"] = py::cpp_function([&]() -> int { return 42; }); // Regular string literal - py::exec( - "message = 'Hello World!'\n" - "x = call_test()", - global, local - ); + py::exec("message = 'Hello World!'\n" + "x = call_test()", + global, + local); // Multi-line raw string literal py::exec(R"( @@ -35,8 +34,9 @@ TEST_SUBMODULE(eval_, m) { print(message) else: raise RuntimeError - )", global, local - ); + )", + global, + local); auto x = local["x"].cast(); return x == 42; @@ -51,9 +51,7 @@ TEST_SUBMODULE(eval_, m) { m.def("test_eval_single_statement", []() { auto local = py::dict(); - local["call_test"] = py::cpp_function([&]() -> int { - return 42; - }); + local["call_test"] = py::cpp_function([&]() -> int { return 42; }); auto result = py::eval("x = call_test()", py::dict(), local); auto x = local["x"].cast(); @@ -64,10 +62,10 @@ TEST_SUBMODULE(eval_, m) { auto local = py::dict(); local["y"] = py::int_(43); - int val_out; + int val_out = 0; local["call_test2"] = py::cpp_function([&](int value) { val_out = value; }); - auto result = py::eval_file(filename, global, local); + auto result = py::eval_file(std::move(filename), global, local); return val_out == 43 && result.is_none(); }); @@ -91,9 +89,30 @@ TEST_SUBMODULE(eval_, m) { // test_eval_empty_globals m.def("eval_empty_globals", [](py::object global) { - if (global.is_none()) + if (global.is_none()) { global = py::dict(); + } auto int_class = py::eval("isinstance(42, int)", global); return global; }); + + // test_eval_closure + m.def("test_eval_closure", []() { + py::dict global; + global["closure_value"] = 42; + py::dict local; + local["closure_value"] = 0; + py::exec(R"( + local_value = closure_value + + def func_global(): + return closure_value + + def func_local(): + return local_value + )", + global, + local); + return std::make_pair(global, local); + }); } diff --git a/3rdparty/pybind11/tests/test_eval.py b/3rdparty/pybind11/tests/test_eval.py index 1bb05af0..51b6b796 100644 --- a/3rdparty/pybind11/tests/test_eval.py +++ b/3rdparty/pybind11/tests/test_eval.py @@ -1,10 +1,8 @@ -# -*- coding: utf-8 -*- import os import pytest import env # noqa: F401 - from pybind11_tests import eval_ as m @@ -19,7 +17,7 @@ def test_evals(capture): assert m.test_eval_failure() -@pytest.mark.xfail("env.PYPY and not env.PY2", raises=RuntimeError) +@pytest.mark.xfail("env.PYPY", raises=RuntimeError) def test_eval_file(): filename = os.path.join(os.path.dirname(__file__), "test_eval_call.py") assert m.test_eval_file(filename) @@ -33,3 +31,20 @@ def test_eval_empty_globals(): g = {} assert "__builtins__" in m.eval_empty_globals(g) assert "__builtins__" in g + + +def test_eval_closure(): + global_, local = m.test_eval_closure() + + assert global_["closure_value"] == 42 + assert local["closure_value"] == 0 + + assert "local_value" not in global_ + assert local["local_value"] == 0 + + assert "func_global" not in global_ + assert local["func_global"]() == 42 + + assert "func_local" not in global_ + with pytest.raises(NameError): + local["func_local"]() diff --git a/3rdparty/pybind11/tests/test_eval_call.py b/3rdparty/pybind11/tests/test_eval_call.py index 373b67ba..fd1da2a5 100644 --- a/3rdparty/pybind11/tests/test_eval_call.py +++ b/3rdparty/pybind11/tests/test_eval_call.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # This file is called from 'test_eval.py' if "call_test2" in locals(): diff --git a/3rdparty/pybind11/tests/test_exceptions.cpp b/3rdparty/pybind11/tests/test_exceptions.cpp index e27c16df..3583f22a 100644 --- a/3rdparty/pybind11/tests/test_exceptions.cpp +++ b/3rdparty/pybind11/tests/test_exceptions.cpp @@ -6,14 +6,21 @@ All rights reserved. Use of this source code is governed by a BSD-style license that can be found in the LICENSE file. */ +#include "test_exceptions.h" +#include "local_bindings.h" #include "pybind11_tests.h" +#include +#include +#include + // A type that should be raised as an exception in Python class MyException : public std::exception { public: - explicit MyException(const char * m) : message{m} {} - const char * what() const noexcept override {return message.c_str();} + explicit MyException(const char *m) : message{m} {} + const char *what() const noexcept override { return message.c_str(); } + private: std::string message = ""; }; @@ -21,8 +28,9 @@ private: // A type that should be translated to a standard Python exception class MyException2 : public std::exception { public: - explicit MyException2(const char * m) : message{m} {} - const char * what() const noexcept override {return message.c_str();} + explicit MyException2(const char *m) : message{m} {} + const char *what() const noexcept override { return message.c_str(); } + private: std::string message = ""; }; @@ -30,13 +38,13 @@ private: // A type that is not derived from std::exception (and is thus unknown) class MyException3 { public: - explicit MyException3(const char * m) : message{m} {} - virtual const char * what() const noexcept {return message.c_str();} + explicit MyException3(const char *m) : message{m} {} + virtual const char *what() const noexcept { return message.c_str(); } // Rule of 5 BEGIN: to preempt compiler warnings. - MyException3(const MyException3&) = default; - MyException3(MyException3&&) = default; - MyException3& operator=(const MyException3&) = default; - MyException3& operator=(MyException3&&) = default; + MyException3(const MyException3 &) = default; + MyException3(MyException3 &&) = default; + MyException3 &operator=(const MyException3 &) = default; + MyException3 &operator=(MyException3 &&) = default; virtual ~MyException3() = default; // Rule of 5 END. private: @@ -47,13 +55,13 @@ private: // and delegated to its exception translator class MyException4 : public std::exception { public: - explicit MyException4(const char * m) : message{m} {} - const char * what() const noexcept override {return message.c_str();} + explicit MyException4(const char *m) : message{m} {} + const char *what() const noexcept override { return message.c_str(); } + private: std::string message = ""; }; - // Like the above, but declared via the helper function class MyException5 : public std::logic_error { public: @@ -65,24 +73,31 @@ class MyException5_1 : public MyException5 { using MyException5::MyException5; }; +// Exception that will be caught via the module local translator. +class MyException6 : public std::exception { +public: + explicit MyException6(const char *m) : message{m} {} + const char *what() const noexcept override { return message.c_str(); } + +private: + std::string message = ""; +}; + struct PythonCallInDestructor { - PythonCallInDestructor(const py::dict &d) : d(d) {} + explicit PythonCallInDestructor(const py::dict &d) : d(d) {} ~PythonCallInDestructor() { d["good"] = true; } py::dict d; }; - - struct PythonAlreadySetInDestructor { - PythonAlreadySetInDestructor(const py::str &s) : s(s) {} + explicit PythonAlreadySetInDestructor(const py::str &s) : s(s) {} ~PythonAlreadySetInDestructor() { py::dict foo; try { // Assign to a py::object to force read access of nonexistent dict entry py::object o = foo["bar"]; - } - catch (py::error_already_set& ex) { + } catch (py::error_already_set &ex) { ex.discard_as_unraisable(s); } } @@ -90,17 +105,22 @@ struct PythonAlreadySetInDestructor { py::str s; }; +std::string error_already_set_what(const py::object &exc_type, const py::object &exc_value) { + PyErr_SetObject(exc_type.ptr(), exc_value.ptr()); + return py::error_already_set().what(); +} TEST_SUBMODULE(exceptions, m) { - m.def("throw_std_exception", []() { - throw std::runtime_error("This exception was intentionally thrown."); - }); + m.def("throw_std_exception", + []() { throw std::runtime_error("This exception was intentionally thrown."); }); // make a new custom exception and use it as a translation target static py::exception ex(m, "MyException"); py::register_exception_translator([](std::exception_ptr p) { try { - if (p) std::rethrow_exception(p); + if (p) { + std::rethrow_exception(p); + } } catch (const MyException &e) { // Set MyException as the active python error ex(e.what()); @@ -112,7 +132,9 @@ TEST_SUBMODULE(exceptions, m) { // never by visible from Python py::register_exception_translator([](std::exception_ptr p) { try { - if (p) std::rethrow_exception(p); + if (p) { + std::rethrow_exception(p); + } } catch (const MyException2 &e) { // Translate this exception to a standard RuntimeError PyErr_SetString(PyExc_RuntimeError, e.what()); @@ -124,7 +146,9 @@ TEST_SUBMODULE(exceptions, m) { // translator for MyException by throwing a new exception py::register_exception_translator([](std::exception_ptr p) { try { - if (p) std::rethrow_exception(p); + if (p) { + std::rethrow_exception(p); + } } catch (const MyException4 &e) { throw MyException(e.what()); } @@ -135,22 +159,42 @@ TEST_SUBMODULE(exceptions, m) { // A slightly more complicated one that declares MyException5_1 as a subclass of MyException5 py::register_exception(m, "MyException5_1", ex5.ptr()); + // py::register_local_exception(m, "LocalSimpleException") + + py::register_local_exception_translator([](std::exception_ptr p) { + try { + if (p) { + std::rethrow_exception(p); + } + } catch (const MyException6 &e) { + PyErr_SetString(PyExc_RuntimeError, e.what()); + } + }); + m.def("throws1", []() { throw MyException("this error should go to a custom type"); }); - m.def("throws2", []() { throw MyException2("this error should go to a standard Python exception"); }); + m.def("throws2", + []() { throw MyException2("this error should go to a standard Python exception"); }); m.def("throws3", []() { throw MyException3("this error cannot be translated"); }); m.def("throws4", []() { throw MyException4("this error is rethrown"); }); - m.def("throws5", []() { throw MyException5("this is a helper-defined translated exception"); }); + m.def("throws5", + []() { throw MyException5("this is a helper-defined translated exception"); }); m.def("throws5_1", []() { throw MyException5_1("MyException5 subclass"); }); - m.def("throws_logic_error", []() { throw std::logic_error("this error should fall through to the standard handler"); }); - m.def("throws_overflow_error", []() {throw std::overflow_error(""); }); + m.def("throws6", []() { throw MyException6("MyException6 only handled in this module"); }); + m.def("throws_logic_error", []() { + throw std::logic_error("this error should fall through to the standard handler"); + }); + m.def("throws_overflow_error", []() { throw std::overflow_error(""); }); + m.def("throws_local_error", []() { throw LocalException("never caught"); }); + m.def("throws_local_simple_error", []() { throw LocalSimpleException("this mod"); }); m.def("exception_matches", []() { py::dict foo; try { // Assign to a py::object to force read access of nonexistent dict entry py::object o = foo["bar"]; - } - catch (py::error_already_set& ex) { - if (!ex.matches(PyExc_KeyError)) throw; + } catch (py::error_already_set &ex) { + if (!ex.matches(PyExc_KeyError)) { + throw; + } return true; } return false; @@ -160,9 +204,10 @@ TEST_SUBMODULE(exceptions, m) { try { // Assign to a py::object to force read access of nonexistent dict entry py::object o = foo["bar"]; - } - catch (py::error_already_set &ex) { - if (!ex.matches(PyExc_Exception)) throw; + } catch (py::error_already_set &ex) { + if (!ex.matches(PyExc_Exception)) { + throw; + } return true; } return false; @@ -171,61 +216,132 @@ TEST_SUBMODULE(exceptions, m) { try { // On Python >= 3.6, this raises a ModuleNotFoundError, a subclass of ImportError py::module_::import("nonexistent"); - } - catch (py::error_already_set &ex) { - if (!ex.matches(PyExc_ImportError)) throw; + } catch (py::error_already_set &ex) { + if (!ex.matches(PyExc_ImportError)) { + throw; + } return true; } return false; }); m.def("throw_already_set", [](bool err) { - if (err) + if (err) { PyErr_SetString(PyExc_ValueError, "foo"); + } try { throw py::error_already_set(); - } catch (const std::runtime_error& e) { - if ((err && e.what() != std::string("ValueError: foo")) || - (!err && e.what() != std::string("Unknown internal error occurred"))) - { + } catch (const std::runtime_error &e) { + if ((err && e.what() != std::string("ValueError: foo")) + || (!err + && e.what() + != std::string("Internal error: pybind11::error_already_set called " + "while Python error indicator not set."))) { PyErr_Clear(); throw std::runtime_error("error message mismatch"); } } PyErr_Clear(); - if (err) + if (err) { PyErr_SetString(PyExc_ValueError, "foo"); + } throw py::error_already_set(); }); - m.def("python_call_in_destructor", [](py::dict d) { + m.def("python_call_in_destructor", [](const py::dict &d) { + bool retval = false; try { PythonCallInDestructor set_dict_in_destructor(d); PyErr_SetString(PyExc_ValueError, "foo"); throw py::error_already_set(); - } catch (const py::error_already_set&) { - return true; + } catch (const py::error_already_set &) { + retval = true; } - return false; + return retval; }); - m.def("python_alreadyset_in_destructor", [](py::str s) { + m.def("python_alreadyset_in_destructor", [](const py::str &s) { PythonAlreadySetInDestructor alreadyset_in_destructor(s); return true; }); // test_nested_throws - m.def("try_catch", [m](py::object exc_type, py::function f, py::args args) { - try { f(*args); } - catch (py::error_already_set &ex) { - if (ex.matches(exc_type)) - py::print(ex.what()); - else - throw; + m.def("try_catch", + [m](const py::object &exc_type, const py::function &f, const py::args &args) { + try { + f(*args); + } catch (py::error_already_set &ex) { + if (ex.matches(exc_type)) { + py::print(ex.what()); + } else { + // Simply `throw;` also works and is better, but using `throw ex;` + // here to cover that situation (as observed in the wild). + throw ex; // Invokes the copy ctor. + } + } + }); + + // Test repr that cannot be displayed + m.def("simple_bool_passthrough", [](bool x) { return x; }); + + m.def("throw_should_be_translated_to_key_error", []() { throw shared_exception(); }); + + m.def("raise_from", []() { + PyErr_SetString(PyExc_ValueError, "inner"); + py::raise_from(PyExc_ValueError, "outer"); + throw py::error_already_set(); + }); + + m.def("raise_from_already_set", []() { + try { + PyErr_SetString(PyExc_ValueError, "inner"); + throw py::error_already_set(); + } catch (py::error_already_set &e) { + py::raise_from(e, PyExc_ValueError, "outer"); + throw py::error_already_set(); } }); - // Test repr that cannot be displayed - m.def("simple_bool_passthrough", [](bool x) {return x;}); + m.def("throw_nested_exception", []() { + try { + throw std::runtime_error("Inner Exception"); + } catch (const std::runtime_error &) { + std::throw_with_nested(std::runtime_error("Outer Exception")); + } + }); + + m.def("error_already_set_what", [](const py::object &exc_type, const py::object &exc_value) { + PyErr_SetObject(exc_type.ptr(), exc_value.ptr()); + std::string what = py::error_already_set().what(); + bool py_err_set_after_what = (PyErr_Occurred() != nullptr); + PyErr_Clear(); + return py::make_tuple(std::move(what), py_err_set_after_what); + }); + + m.def("test_cross_module_interleaved_error_already_set", []() { + auto cm = py::module_::import("cross_module_interleaved_error_already_set"); + auto interleaved_error_already_set + = reinterpret_cast(PyLong_AsVoidPtr(cm.attr("funcaddr").ptr())); + interleaved_error_already_set(); + }); + + m.def("test_error_already_set_double_restore", [](bool dry_run) { + PyErr_SetString(PyExc_ValueError, "Random error."); + py::error_already_set e; + e.restore(); + PyErr_Clear(); + if (!dry_run) { + e.restore(); + } + }); + // https://github.com/pybind/pybind11/issues/4075 + m.def("test_pypy_oserror_normalization", []() { + try { + py::module_::import("io").attr("open")("this_filename_must_not_exist", "r"); + } catch (const py::error_already_set &e) { + return py::str(e.what()); // str must be built before e goes out of scope. + } + return py::str("UNEXPECTED"); + }); } diff --git a/3rdparty/pybind11/tests/test_exceptions.h b/3rdparty/pybind11/tests/test_exceptions.h new file mode 100644 index 00000000..03684b89 --- /dev/null +++ b/3rdparty/pybind11/tests/test_exceptions.h @@ -0,0 +1,13 @@ +#pragma once +#include "pybind11_tests.h" + +#include + +// shared exceptions for cross_module_tests + +class PYBIND11_EXPORT_EXCEPTION shared_exception : public pybind11::builtin_exception { +public: + using builtin_exception::builtin_exception; + explicit shared_exception() : shared_exception("") {} + void set_error() const override { PyErr_SetString(PyExc_RuntimeError, what()); } +}; diff --git a/3rdparty/pybind11/tests/test_exceptions.py b/3rdparty/pybind11/tests/test_exceptions.py index 95eac709..70b6ffea 100644 --- a/3rdparty/pybind11/tests/test_exceptions.py +++ b/3rdparty/pybind11/tests/test_exceptions.py @@ -1,10 +1,10 @@ -# -*- coding: utf-8 -*- import sys import pytest -from pybind11_tests import exceptions as m +import env import pybind11_cross_module_tests as cm +from pybind11_tests import exceptions as m def test_std_exception(msg): @@ -16,14 +16,31 @@ def test_std_exception(msg): def test_error_already_set(msg): with pytest.raises(RuntimeError) as excinfo: m.throw_already_set(False) - assert msg(excinfo.value) == "Unknown internal error occurred" + assert ( + msg(excinfo.value) + == "Internal error: pybind11::error_already_set called while Python error indicator not set." + ) with pytest.raises(ValueError) as excinfo: m.throw_already_set(True) assert msg(excinfo.value) == "foo" -def test_cross_module_exceptions(): +def test_raise_from(msg): + with pytest.raises(ValueError) as excinfo: + m.raise_from() + assert msg(excinfo.value) == "outer" + assert msg(excinfo.value.__cause__) == "inner" + + +def test_raise_from_already_set(msg): + with pytest.raises(ValueError) as excinfo: + m.raise_from_already_set() + assert msg(excinfo.value) == "outer" + assert msg(excinfo.value.__cause__) == "inner" + + +def test_cross_module_exceptions(msg): with pytest.raises(RuntimeError) as excinfo: cm.raise_runtime_error() assert str(excinfo.value) == "My runtime error" @@ -43,6 +60,27 @@ def test_cross_module_exceptions(): with pytest.raises(StopIteration) as excinfo: cm.throw_stop_iteration() + with pytest.raises(cm.LocalSimpleException) as excinfo: + cm.throw_local_simple_error() + assert msg(excinfo.value) == "external mod" + + with pytest.raises(KeyError) as excinfo: + cm.throw_local_error() + # KeyError is a repr of the key, so it has an extra set of quotes + assert str(excinfo.value) == "'just local'" + + +# TODO: FIXME +@pytest.mark.xfail( + "env.PYPY and env.MACOS", + raises=RuntimeError, + reason="Expected failure with PyPy and libc++ (Issue #2847 & PR #2999)", +) +def test_cross_module_exception_translator(): + with pytest.raises(KeyError): + # translator registered in cross_module_tests + m.throw_should_be_translated_to_key_error() + def test_python_call_in_catch(): d = {} @@ -50,18 +88,32 @@ def test_python_call_in_catch(): assert d["good"] is True +def ignore_pytest_unraisable_warning(f): + unraisable = "PytestUnraisableExceptionWarning" + if hasattr(pytest, unraisable): # Python >= 3.8 and pytest >= 6 + dec = pytest.mark.filterwarnings(f"ignore::pytest.{unraisable}") + return dec(f) + else: + return f + + +# TODO: find out why this fails on PyPy, https://foss.heptapod.net/pypy/pypy/-/issues/3583 +@pytest.mark.xfail(env.PYPY, reason="Failure on PyPy 3.8 (7.3.7)", strict=False) +@ignore_pytest_unraisable_warning def test_python_alreadyset_in_destructor(monkeypatch, capsys): hooked = False - triggered = [False] # mutable, so Python 2.7 closure can modify it + triggered = False if hasattr(sys, "unraisablehook"): # Python 3.8+ hooked = True - default_hook = sys.unraisablehook + # Don't take `sys.unraisablehook`, as that's overwritten by pytest + default_hook = sys.__unraisablehook__ def hook(unraisable_hook_args): exc_type, exc_value, exc_tb, err_msg, obj = unraisable_hook_args if obj == "already_set demo": - triggered[0] = True + nonlocal triggered + triggered = True default_hook(unraisable_hook_args) return @@ -70,11 +122,11 @@ def test_python_alreadyset_in_destructor(monkeypatch, capsys): assert m.python_alreadyset_in_destructor("already_set demo") is True if hooked: - assert triggered[0] is True + assert triggered is True _, captured_stderr = capsys.readouterr() - # Error message is different in Python 2 and 3, check for words that appear in both - assert "ignored" in captured_stderr and "already_set demo" in captured_stderr + assert captured_stderr.startswith("Exception ignored in: 'already_set demo'") + assert captured_stderr.rstrip().endswith("KeyError: 'bar'") def test_exception_matches(): @@ -133,8 +185,8 @@ def test_custom(msg): with pytest.raises(m.MyException5) as excinfo: try: m.throws5() - except m.MyException5_1: - raise RuntimeError("Exception error: caught child from parent") + except m.MyException5_1 as err: + raise RuntimeError("Exception error: caught child from parent") from err assert msg(excinfo.value) == "this is a helper-defined translated exception" @@ -188,11 +240,129 @@ def test_nested_throws(capture): assert str(excinfo.value) == "this is a helper-defined translated exception" +def test_throw_nested_exception(): + with pytest.raises(RuntimeError) as excinfo: + m.throw_nested_exception() + assert str(excinfo.value) == "Outer Exception" + assert str(excinfo.value.__cause__) == "Inner Exception" + + # This can often happen if you wrap a pybind11 class in a Python wrapper def test_invalid_repr(): - class MyRepr(object): + class MyRepr: def __repr__(self): raise AttributeError("Example error") with pytest.raises(TypeError): m.simple_bool_passthrough(MyRepr()) + + +def test_local_translator(msg): + """Tests that a local translator works and that the local translator from + the cross module is not applied""" + with pytest.raises(RuntimeError) as excinfo: + m.throws6() + assert msg(excinfo.value) == "MyException6 only handled in this module" + + with pytest.raises(RuntimeError) as excinfo: + m.throws_local_error() + assert not isinstance(excinfo.value, KeyError) + assert msg(excinfo.value) == "never caught" + + with pytest.raises(Exception) as excinfo: + m.throws_local_simple_error() + assert not isinstance(excinfo.value, cm.LocalSimpleException) + assert msg(excinfo.value) == "this mod" + + +class FlakyException(Exception): + def __init__(self, failure_point): + if failure_point == "failure_point_init": + raise ValueError("triggered_failure_point_init") + self.failure_point = failure_point + + def __str__(self): + if self.failure_point == "failure_point_str": + raise ValueError("triggered_failure_point_str") + return "FlakyException.__str__" + + +@pytest.mark.parametrize( + "exc_type, exc_value, expected_what", + ( + (ValueError, "plain_str", "ValueError: plain_str"), + (ValueError, ("tuple_elem",), "ValueError: tuple_elem"), + (FlakyException, ("happy",), "FlakyException: FlakyException.__str__"), + ), +) +def test_error_already_set_what_with_happy_exceptions( + exc_type, exc_value, expected_what +): + what, py_err_set_after_what = m.error_already_set_what(exc_type, exc_value) + assert not py_err_set_after_what + assert what == expected_what + + +@pytest.mark.skipif("env.PYPY", reason="PyErr_NormalizeException Segmentation fault") +def test_flaky_exception_failure_point_init(): + with pytest.raises(RuntimeError) as excinfo: + m.error_already_set_what(FlakyException, ("failure_point_init",)) + lines = str(excinfo.value).splitlines() + # PyErr_NormalizeException replaces the original FlakyException with ValueError: + assert lines[:3] == [ + "pybind11::error_already_set: MISMATCH of original and normalized active exception types:" + " ORIGINAL FlakyException REPLACED BY ValueError: triggered_failure_point_init", + "", + "At:", + ] + # Checking the first two lines of the traceback as formatted in error_string(): + assert "test_exceptions.py(" in lines[3] + assert lines[3].endswith("): __init__") + assert lines[4].endswith("): test_flaky_exception_failure_point_init") + + +def test_flaky_exception_failure_point_str(): + what, py_err_set_after_what = m.error_already_set_what( + FlakyException, ("failure_point_str",) + ) + assert not py_err_set_after_what + lines = what.splitlines() + if env.PYPY and len(lines) == 3: + n = 3 # Traceback is missing. + else: + n = 5 + assert ( + lines[:n] + == [ + "FlakyException: ", + "", + "MESSAGE UNAVAILABLE DUE TO EXCEPTION: ValueError: triggered_failure_point_str", + "", + "At:", + ][:n] + ) + + +def test_cross_module_interleaved_error_already_set(): + with pytest.raises(RuntimeError) as excinfo: + m.test_cross_module_interleaved_error_already_set() + assert str(excinfo.value) in ( + "2nd error.", # Almost all platforms. + "RuntimeError: 2nd error.", # Some PyPy builds (seen under macOS). + ) + + +def test_error_already_set_double_restore(): + m.test_error_already_set_double_restore(True) # dry_run + with pytest.raises(RuntimeError) as excinfo: + m.test_error_already_set_double_restore(False) + assert str(excinfo.value) == ( + "Internal error: pybind11::detail::error_fetch_and_normalize::restore()" + " called a second time. ORIGINAL ERROR: ValueError: Random error." + ) + + +def test_pypy_oserror_normalization(): + # https://github.com/pybind/pybind11/issues/4075 + what = m.test_pypy_oserror_normalization() + assert "this_filename_must_not_exist" in what diff --git a/3rdparty/pybind11/tests/test_factory_constructors.cpp b/3rdparty/pybind11/tests/test_factory_constructors.cpp index f42d1f29..a387cd2e 100644 --- a/3rdparty/pybind11/tests/test_factory_constructors.cpp +++ b/3rdparty/pybind11/tests/test_factory_constructors.cpp @@ -8,35 +8,45 @@ BSD-style license that can be found in the LICENSE file. */ -#include "pybind11_tests.h" #include "constructor_stats.h" +#include "pybind11_tests.h" + #include #include +#include // Classes for testing python construction via C++ factory function: // Not publicly constructible, copyable, or movable: class TestFactory1 { friend class TestFactoryHelper; TestFactory1() : value("(empty)") { print_default_created(this); } - TestFactory1(int v) : value(std::to_string(v)) { print_created(this, value); } - TestFactory1(std::string v) : value(std::move(v)) { print_created(this, value); } + explicit TestFactory1(int v) : value(std::to_string(v)) { print_created(this, value); } + explicit TestFactory1(std::string v) : value(std::move(v)) { print_created(this, value); } + +public: + std::string value; TestFactory1(TestFactory1 &&) = delete; TestFactory1(const TestFactory1 &) = delete; TestFactory1 &operator=(TestFactory1 &&) = delete; TestFactory1 &operator=(const TestFactory1 &) = delete; -public: - std::string value; ~TestFactory1() { print_destroyed(this); } }; // Non-public construction, but moveable: class TestFactory2 { friend class TestFactoryHelper; TestFactory2() : value("(empty2)") { print_default_created(this); } - TestFactory2(int v) : value(std::to_string(v)) { print_created(this, value); } - TestFactory2(std::string v) : value(std::move(v)) { print_created(this, value); } + explicit TestFactory2(int v) : value(std::to_string(v)) { print_created(this, value); } + explicit TestFactory2(std::string v) : value(std::move(v)) { print_created(this, value); } + public: - TestFactory2(TestFactory2 &&m) { value = std::move(m.value); print_move_created(this); } - TestFactory2 &operator=(TestFactory2 &&m) { value = std::move(m.value); print_move_assigned(this); return *this; } + TestFactory2(TestFactory2 &&m) noexcept : value{std::move(m.value)} { + print_move_created(this); + } + TestFactory2 &operator=(TestFactory2 &&m) noexcept { + value = std::move(m.value); + print_move_assigned(this); + return *this; + } std::string value; ~TestFactory2() { print_destroyed(this); } }; @@ -45,11 +55,18 @@ class TestFactory3 { protected: friend class TestFactoryHelper; TestFactory3() : value("(empty3)") { print_default_created(this); } - TestFactory3(int v) : value(std::to_string(v)) { print_created(this, value); } + explicit TestFactory3(int v) : value(std::to_string(v)) { print_created(this, value); } + public: - TestFactory3(std::string v) : value(std::move(v)) { print_created(this, value); } - TestFactory3(TestFactory3 &&m) { value = std::move(m.value); print_move_created(this); } - TestFactory3 &operator=(TestFactory3 &&m) { value = std::move(m.value); print_move_assigned(this); return *this; } + explicit TestFactory3(std::string v) : value(std::move(v)) { print_created(this, value); } + TestFactory3(TestFactory3 &&m) noexcept : value{std::move(m.value)} { + print_move_created(this); + } + TestFactory3 &operator=(TestFactory3 &&m) noexcept { + value = std::move(m.value); + print_move_assigned(this); + return *this; + } std::string value; virtual ~TestFactory3() { print_destroyed(this); } }; @@ -57,13 +74,13 @@ public: class TestFactory4 : public TestFactory3 { public: TestFactory4() : TestFactory3() { print_default_created(this); } - TestFactory4(int v) : TestFactory3(v) { print_created(this, v); } + explicit TestFactory4(int v) : TestFactory3(v) { print_created(this, v); } ~TestFactory4() override { print_destroyed(this); } }; // Another class for an invalid downcast test class TestFactory5 : public TestFactory3 { public: - TestFactory5(int i) : TestFactory3(i) { print_created(this, i); } + explicit TestFactory5(int i) : TestFactory3(i) { print_created(this, i); } ~TestFactory5() override { print_destroyed(this); } }; @@ -71,23 +88,45 @@ class TestFactory6 { protected: int value; bool alias = false; + public: - TestFactory6(int i) : value{i} { print_created(this, i); } - TestFactory6(TestFactory6 &&f) { print_move_created(this); value = f.value; alias = f.alias; } - TestFactory6(const TestFactory6 &f) { print_copy_created(this); value = f.value; alias = f.alias; } + explicit TestFactory6(int i) : value{i} { print_created(this, i); } + TestFactory6(TestFactory6 &&f) noexcept { + print_move_created(this); + // NOLINTNEXTLINE(cppcoreguidelines-prefer-member-initializer) + value = f.value; + // NOLINTNEXTLINE(cppcoreguidelines-prefer-member-initializer) + alias = f.alias; + } + TestFactory6(const TestFactory6 &f) { + print_copy_created(this); + // NOLINTNEXTLINE(cppcoreguidelines-prefer-member-initializer) + value = f.value; + // NOLINTNEXTLINE(cppcoreguidelines-prefer-member-initializer) + alias = f.alias; + } virtual ~TestFactory6() { print_destroyed(this); } virtual int get() { return value; } - bool has_alias() { return alias; } + bool has_alias() const { return alias; } }; class PyTF6 : public TestFactory6 { public: // Special constructor that allows the factory to construct a PyTF6 from a TestFactory6 only // when an alias is needed: - PyTF6(TestFactory6 &&base) : TestFactory6(std::move(base)) { alias = true; print_created(this, "move", value); } - PyTF6(int i) : TestFactory6(i) { alias = true; print_created(this, i); } - PyTF6(PyTF6 &&f) : TestFactory6(std::move(f)) { print_move_created(this); } + explicit PyTF6(TestFactory6 &&base) : TestFactory6(std::move(base)) { + alias = true; + print_created(this, "move", value); + } + explicit PyTF6(int i) : TestFactory6(i) { + alias = true; + print_created(this, i); + } + PyTF6(PyTF6 &&f) noexcept : TestFactory6(std::move(f)) { print_move_created(this); } PyTF6(const PyTF6 &f) : TestFactory6(f) { print_copy_created(this); } - PyTF6(std::string s) : TestFactory6((int) s.size()) { alias = true; print_created(this, s); } + explicit PyTF6(std::string s) : TestFactory6((int) s.size()) { + alias = true; + print_created(this, s); + } ~PyTF6() override { print_destroyed(this); } int get() override { PYBIND11_OVERRIDE(int, TestFactory6, get, /*no args*/); } }; @@ -96,56 +135,80 @@ class TestFactory7 { protected: int value; bool alias = false; + public: - TestFactory7(int i) : value{i} { print_created(this, i); } - TestFactory7(TestFactory7 &&f) { print_move_created(this); value = f.value; alias = f.alias; } - TestFactory7(const TestFactory7 &f) { print_copy_created(this); value = f.value; alias = f.alias; } + explicit TestFactory7(int i) : value{i} { print_created(this, i); } + TestFactory7(TestFactory7 &&f) noexcept { + print_move_created(this); + // NOLINTNEXTLINE(cppcoreguidelines-prefer-member-initializer) + value = f.value; + // NOLINTNEXTLINE(cppcoreguidelines-prefer-member-initializer) + alias = f.alias; + } + TestFactory7(const TestFactory7 &f) { + print_copy_created(this); + // NOLINTNEXTLINE(cppcoreguidelines-prefer-member-initializer) + value = f.value; + // NOLINTNEXTLINE(cppcoreguidelines-prefer-member-initializer) + alias = f.alias; + } virtual ~TestFactory7() { print_destroyed(this); } virtual int get() { return value; } - bool has_alias() { return alias; } + bool has_alias() const { return alias; } }; class PyTF7 : public TestFactory7 { public: - PyTF7(int i) : TestFactory7(i) { alias = true; print_created(this, i); } - PyTF7(PyTF7 &&f) : TestFactory7(std::move(f)) { print_move_created(this); } + explicit PyTF7(int i) : TestFactory7(i) { + // NOLINTNEXTLINE(cppcoreguidelines-prefer-member-initializer) + alias = true; + print_created(this, i); + } + PyTF7(PyTF7 &&f) noexcept : TestFactory7(std::move(f)) { print_move_created(this); } PyTF7(const PyTF7 &f) : TestFactory7(f) { print_copy_created(this); } ~PyTF7() override { print_destroyed(this); } int get() override { PYBIND11_OVERRIDE(int, TestFactory7, get, /*no args*/); } }; - class TestFactoryHelper { public: // Non-movable, non-copyable type: // Return via pointer: static TestFactory1 *construct1() { return new TestFactory1(); } // Holder: - static std::unique_ptr construct1(int a) { return std::unique_ptr(new TestFactory1(a)); } + static std::unique_ptr construct1(int a) { + return std::unique_ptr(new TestFactory1(a)); + } // pointer again - static TestFactory1 *construct1_string(std::string a) { return new TestFactory1(a); } + static TestFactory1 *construct1_string(std::string a) { + return new TestFactory1(std::move(a)); + } // Moveable type: // pointer: static TestFactory2 *construct2() { return new TestFactory2(); } // holder: - static std::unique_ptr construct2(int a) { return std::unique_ptr(new TestFactory2(a)); } + static std::unique_ptr construct2(int a) { + return std::unique_ptr(new TestFactory2(a)); + } // by value moving: - static TestFactory2 construct2(std::string a) { return TestFactory2(a); } + static TestFactory2 construct2(std::string a) { return TestFactory2(std::move(a)); } // shared_ptr holder type: // pointer: static TestFactory3 *construct3() { return new TestFactory3(); } // holder: - static std::shared_ptr construct3(int a) { return std::shared_ptr(new TestFactory3(a)); } + static std::shared_ptr construct3(int a) { + return std::shared_ptr(new TestFactory3(a)); + } }; TEST_SUBMODULE(factory_constructors, m) { // Define various trivial types to allow simpler overload resolution: py::module_ m_tag = m.def_submodule("tag"); -#define MAKE_TAG_TYPE(Name) \ - struct Name##_tag {}; \ - py::class_(m_tag, #Name "_tag").def(py::init<>()); \ +#define MAKE_TAG_TYPE(Name) \ + struct Name##_tag {}; \ + py::class_(m_tag, #Name "_tag").def(py::init<>()); \ m_tag.attr(#Name) = py::cast(Name##_tag{}) MAKE_TAG_TYPE(pointer); MAKE_TAG_TYPE(unique_ptr); @@ -168,40 +231,50 @@ TEST_SUBMODULE(factory_constructors, m) { .def(py::init([](unique_ptr_tag, int v) { return TestFactoryHelper::construct1(v); })) .def(py::init(&TestFactoryHelper::construct1_string)) // raw function pointer .def(py::init([](pointer_tag) { return TestFactoryHelper::construct1(); })) - .def(py::init([](py::handle, int v, py::handle) { return TestFactoryHelper::construct1(v); })) - .def_readwrite("value", &TestFactory1::value) - ; + .def(py::init( + [](py::handle, int v, py::handle) { return TestFactoryHelper::construct1(v); })) + .def_readwrite("value", &TestFactory1::value); py::class_(m, "TestFactory2") .def(py::init([](pointer_tag, int v) { return TestFactoryHelper::construct2(v); })) - .def(py::init([](unique_ptr_tag, std::string v) { return TestFactoryHelper::construct2(v); })) + .def(py::init([](unique_ptr_tag, std::string v) { + return TestFactoryHelper::construct2(std::move(v)); + })) .def(py::init([](move_tag) { return TestFactoryHelper::construct2(); })) - .def_readwrite("value", &TestFactory2::value) - ; + .def_readwrite("value", &TestFactory2::value); // Stateful & reused: int c = 1; - auto c4a = [c](pointer_tag, TF4_tag, int a) { (void) c; return new TestFactory4(a);}; + auto c4a = [c](pointer_tag, TF4_tag, int a) { + (void) c; + return new TestFactory4(a); + }; // test_init_factory_basic, test_init_factory_casting - py::class_>(m, "TestFactory3") + py::class_> pyTestFactory3(m, "TestFactory3"); + pyTestFactory3 .def(py::init([](pointer_tag, int v) { return TestFactoryHelper::construct3(v); })) - .def(py::init([](shared_ptr_tag) { return TestFactoryHelper::construct3(); })) - .def("__init__", [](TestFactory3 &self, std::string v) { new (&self) TestFactory3(v); }) // placement-new ctor - + .def(py::init([](shared_ptr_tag) { return TestFactoryHelper::construct3(); })); + ignoreOldStyleInitWarnings([&pyTestFactory3]() { + pyTestFactory3.def("__init__", [](TestFactory3 &self, std::string v) { + new (&self) TestFactory3(std::move(v)); + }); // placement-new ctor + }); + pyTestFactory3 // factories returning a derived type: .def(py::init(c4a)) // derived ptr .def(py::init([](pointer_tag, TF5_tag, int a) { return new TestFactory5(a); })) // derived shared ptr: - .def(py::init([](shared_ptr_tag, TF4_tag, int a) { return std::make_shared(a); })) - .def(py::init([](shared_ptr_tag, TF5_tag, int a) { return std::make_shared(a); })) + .def(py::init( + [](shared_ptr_tag, TF4_tag, int a) { return std::make_shared(a); })) + .def(py::init( + [](shared_ptr_tag, TF5_tag, int a) { return std::make_shared(a); })) // Returns nullptr: .def(py::init([](null_ptr_tag) { return (TestFactory3 *) nullptr; })) .def(py::init([](null_unique_ptr_tag) { return std::unique_ptr(); })) .def(py::init([](null_shared_ptr_tag) { return std::shared_ptr(); })) - .def_readwrite("value", &TestFactory3::value) - ; + .def_readwrite("value", &TestFactory3::value); // test_init_factory_casting py::class_>(m, "TestFactory4") @@ -216,58 +289,60 @@ TEST_SUBMODULE(factory_constructors, m) { py::class_(m, "TestFactory6") .def(py::init([](base_tag, int i) { return TestFactory6(i); })) .def(py::init([](alias_tag, int i) { return PyTF6(i); })) - .def(py::init([](alias_tag, std::string s) { return PyTF6(s); })) + .def(py::init([](alias_tag, std::string s) { return PyTF6(std::move(s)); })) .def(py::init([](alias_tag, pointer_tag, int i) { return new PyTF6(i); })) .def(py::init([](base_tag, pointer_tag, int i) { return new TestFactory6(i); })) - .def(py::init([](base_tag, alias_tag, pointer_tag, int i) { return (TestFactory6 *) new PyTF6(i); })) + .def(py::init( + [](base_tag, alias_tag, pointer_tag, int i) { return (TestFactory6 *) new PyTF6(i); })) .def("get", &TestFactory6::get) .def("has_alias", &TestFactory6::has_alias) - .def_static("get_cstats", &ConstructorStats::get, py::return_value_policy::reference) - .def_static("get_alias_cstats", &ConstructorStats::get, py::return_value_policy::reference) - ; + .def_static( + "get_cstats", &ConstructorStats::get, py::return_value_policy::reference) + .def_static( + "get_alias_cstats", &ConstructorStats::get, py::return_value_policy::reference); // test_init_factory_dual // Separate alias constructor testing py::class_>(m, "TestFactory7") - .def(py::init( - [](int i) { return TestFactory7(i); }, - [](int i) { return PyTF7(i); })) - .def(py::init( - [](pointer_tag, int i) { return new TestFactory7(i); }, - [](pointer_tag, int i) { return new PyTF7(i); })) - .def(py::init( - [](mixed_tag, int i) { return new TestFactory7(i); }, - [](mixed_tag, int i) { return PyTF7(i); })) - .def(py::init( - [](mixed_tag, std::string s) { return TestFactory7((int) s.size()); }, - [](mixed_tag, std::string s) { return new PyTF7((int) s.size()); })) - .def(py::init( - [](base_tag, pointer_tag, int i) { return new TestFactory7(i); }, - [](base_tag, pointer_tag, int i) { return (TestFactory7 *) new PyTF7(i); })) - .def(py::init( - [](alias_tag, pointer_tag, int i) { return new PyTF7(i); }, - [](alias_tag, pointer_tag, int i) { return new PyTF7(10*i); })) + .def(py::init([](int i) { return TestFactory7(i); }, [](int i) { return PyTF7(i); })) + .def(py::init([](pointer_tag, int i) { return new TestFactory7(i); }, + [](pointer_tag, int i) { return new PyTF7(i); })) + .def(py::init([](mixed_tag, int i) { return new TestFactory7(i); }, + [](mixed_tag, int i) { return PyTF7(i); })) + .def(py::init([](mixed_tag, const std::string &s) { return TestFactory7((int) s.size()); }, + [](mixed_tag, const std::string &s) { return new PyTF7((int) s.size()); })) + .def(py::init([](base_tag, pointer_tag, int i) { return new TestFactory7(i); }, + [](base_tag, pointer_tag, int i) { return (TestFactory7 *) new PyTF7(i); })) + .def(py::init([](alias_tag, pointer_tag, int i) { return new PyTF7(i); }, + [](alias_tag, pointer_tag, int i) { return new PyTF7(10 * i); })) .def(py::init( [](shared_ptr_tag, base_tag, int i) { return std::make_shared(i); }, - [](shared_ptr_tag, base_tag, int i) { auto *p = new PyTF7(i); return std::shared_ptr(p); })) - .def(py::init( - [](shared_ptr_tag, invalid_base_tag, int i) { return std::make_shared(i); }, - [](shared_ptr_tag, invalid_base_tag, int i) { return std::make_shared(i); })) // <-- invalid alias factory + [](shared_ptr_tag, base_tag, int i) { + auto *p = new PyTF7(i); + return std::shared_ptr(p); + })) + .def(py::init([](shared_ptr_tag, + invalid_base_tag, + int i) { return std::make_shared(i); }, + [](shared_ptr_tag, invalid_base_tag, int i) { + return std::make_shared(i); + })) // <-- invalid alias factory .def("get", &TestFactory7::get) .def("has_alias", &TestFactory7::has_alias) - .def_static("get_cstats", &ConstructorStats::get, py::return_value_policy::reference) - .def_static("get_alias_cstats", &ConstructorStats::get, py::return_value_policy::reference) - ; + .def_static( + "get_cstats", &ConstructorStats::get, py::return_value_policy::reference) + .def_static( + "get_alias_cstats", &ConstructorStats::get, py::return_value_policy::reference); // test_placement_new_alternative // Class with a custom new operator but *without* a placement new operator (issue #948) class NoPlacementNew { public: - NoPlacementNew(int i) : i(i) { } + explicit NoPlacementNew(int i) : i(i) {} static void *operator new(std::size_t s) { auto *p = ::operator new(s); py::print("operator new called, returning", reinterpret_cast(p)); @@ -283,50 +358,62 @@ TEST_SUBMODULE(factory_constructors, m) { py::class_(m, "NoPlacementNew") .def(py::init()) .def(py::init([]() { return new NoPlacementNew(100); })) - .def_readwrite("i", &NoPlacementNew::i) - ; - + .def_readwrite("i", &NoPlacementNew::i); // test_reallocations // Class that has verbose operator_new/operator_delete calls struct NoisyAlloc { NoisyAlloc(const NoisyAlloc &) = default; - NoisyAlloc(int i) { py::print(py::str("NoisyAlloc(int {})").format(i)); } - NoisyAlloc(double d) { py::print(py::str("NoisyAlloc(double {})").format(d)); } + explicit NoisyAlloc(int i) { py::print(py::str("NoisyAlloc(int {})").format(i)); } + explicit NoisyAlloc(double d) { py::print(py::str("NoisyAlloc(double {})").format(d)); } ~NoisyAlloc() { py::print("~NoisyAlloc()"); } - static void *operator new(size_t s) { py::print("noisy new"); return ::operator new(s); } - static void *operator new(size_t, void *p) { py::print("noisy placement new"); return p; } - static void operator delete(void *p, size_t) { py::print("noisy delete"); ::operator delete(p); } + static void *operator new(size_t s) { + py::print("noisy new"); + return ::operator new(s); + } + static void *operator new(size_t, void *p) { + py::print("noisy placement new"); + return p; + } + static void operator delete(void *p, size_t) { + py::print("noisy delete"); + ::operator delete(p); + } static void operator delete(void *, void *) { py::print("noisy placement delete"); } -#if defined(_MSC_VER) && _MSC_VER < 1910 - // MSVC 2015 bug: the above "noisy delete" isn't invoked (fixed in MSVC 2017) - static void operator delete(void *p) { py::print("noisy delete"); ::operator delete(p); } -#endif }; - py::class_(m, "NoisyAlloc") - // Since these overloads have the same number of arguments, the dispatcher will try each of - // them until the arguments convert. Thus we can get a pre-allocation here when passing a - // single non-integer: - .def("__init__", [](NoisyAlloc *a, int i) { new (a) NoisyAlloc(i); }) // Regular constructor, runs first, requires preallocation - .def(py::init([](double d) { return new NoisyAlloc(d); })) - - // The two-argument version: first the factory pointer overload. - .def(py::init([](int i, int) { return new NoisyAlloc(i); })) - // Return-by-value: - .def(py::init([](double d, int) { return NoisyAlloc(d); })) - // Old-style placement new init; requires preallocation - .def("__init__", [](NoisyAlloc &a, double d, double) { new (&a) NoisyAlloc(d); }) - // Requires deallocation of previous overload preallocated value: - .def(py::init([](int i, double) { return new NoisyAlloc(i); })) - // Regular again: requires yet another preallocation - .def("__init__", [](NoisyAlloc &a, int i, std::string) { new (&a) NoisyAlloc(i); }) - ; - - - - // static_assert testing (the following def's should all fail with appropriate compilation errors): + py::class_ pyNoisyAlloc(m, "NoisyAlloc"); + // Since these overloads have the same number of arguments, the dispatcher will try each of + // them until the arguments convert. Thus we can get a pre-allocation here when passing a + // single non-integer: + ignoreOldStyleInitWarnings([&pyNoisyAlloc]() { + pyNoisyAlloc.def("__init__", [](NoisyAlloc *a, int i) { + new (a) NoisyAlloc(i); + }); // Regular constructor, runs first, requires preallocation + }); + + pyNoisyAlloc.def(py::init([](double d) { return new NoisyAlloc(d); })); + + // The two-argument version: first the factory pointer overload. + pyNoisyAlloc.def(py::init([](int i, int) { return new NoisyAlloc(i); })); + // Return-by-value: + pyNoisyAlloc.def(py::init([](double d, int) { return NoisyAlloc(d); })); + // Old-style placement new init; requires preallocation + ignoreOldStyleInitWarnings([&pyNoisyAlloc]() { + pyNoisyAlloc.def("__init__", + [](NoisyAlloc &a, double d, double) { new (&a) NoisyAlloc(d); }); + }); + // Requires deallocation of previous overload preallocated value: + pyNoisyAlloc.def(py::init([](int i, double) { return new NoisyAlloc(i); })); + // Regular again: requires yet another preallocation + ignoreOldStyleInitWarnings([&pyNoisyAlloc]() { + pyNoisyAlloc.def( + "__init__", [](NoisyAlloc &a, int i, const std::string &) { new (&a) NoisyAlloc(i); }); + }); + + // static_assert testing (the following def's should all fail with appropriate compilation + // errors): #if 0 struct BadF1Base {}; struct BadF1 : BadF1Base {}; diff --git a/3rdparty/pybind11/tests/test_factory_constructors.py b/3rdparty/pybind11/tests/test_factory_constructors.py index ffcce6fd..120a587c 100644 --- a/3rdparty/pybind11/tests/test_factory_constructors.py +++ b/3rdparty/pybind11/tests/test_factory_constructors.py @@ -1,12 +1,10 @@ -# -*- coding: utf-8 -*- -import pytest import re -import env # noqa: F401 +import pytest +from pybind11_tests import ConstructorStats from pybind11_tests import factory_constructors as m from pybind11_tests.factory_constructors import tag -from pybind11_tests import ConstructorStats def test_init_factory_basic(): @@ -82,7 +80,7 @@ def test_init_factory_signature(msg): 4. m.factory_constructors.TestFactory1(arg0: handle, arg1: int, arg2: handle) Invoked with: 'invalid', 'constructor', 'arguments' - """ # noqa: E501 line too long + """ ) assert ( @@ -465,12 +463,10 @@ def test_reallocation_g(capture, msg): ) -@pytest.mark.skipif("env.PY2") def test_invalid_self(): - """Tests invocation of the pybind-registered base class with an invalid `self` argument. You - can only actually do this on Python 3: Python 2 raises an exception itself if you try.""" + """Tests invocation of the pybind-registered base class with an invalid `self` argument.""" - class NotPybindDerived(object): + class NotPybindDerived: pass # Attempts to initialize with an invalid type passed as `self`: @@ -486,7 +482,9 @@ def test_invalid_self(): # Same as above, but for a class with an alias: class BrokenTF6(m.TestFactory6): def __init__(self, bad): - if bad == 1: + if bad == 0: + m.TestFactory6.__init__() + elif bad == 1: a = m.TestFactory2(tag.pointer, 1) m.TestFactory6.__init__(a, tag.base, 1) elif bad == 2: @@ -506,13 +504,13 @@ def test_invalid_self(): BrokenTF1(arg) assert ( str(excinfo.value) - == "__init__(self, ...) called with invalid `self` argument" + == "__init__(self, ...) called with invalid or missing `self` argument" ) - for arg in (1, 2, 3, 4): + for arg in (0, 1, 2, 3, 4): with pytest.raises(TypeError) as excinfo: BrokenTF6(arg) assert ( str(excinfo.value) - == "__init__(self, ...) called with invalid `self` argument" + == "__init__(self, ...) called with invalid or missing `self` argument" ) diff --git a/3rdparty/pybind11/tests/test_gil_scoped.cpp b/3rdparty/pybind11/tests/test_gil_scoped.cpp index b6a45a5f..97efdc16 100644 --- a/3rdparty/pybind11/tests/test_gil_scoped.cpp +++ b/3rdparty/pybind11/tests/test_gil_scoped.cpp @@ -7,48 +7,41 @@ BSD-style license that can be found in the LICENSE file. */ -#include "pybind11_tests.h" #include +#include "pybind11_tests.h" -class VirtClass { +class VirtClass { public: virtual ~VirtClass() = default; VirtClass() = default; - VirtClass(const VirtClass&) = delete; + VirtClass(const VirtClass &) = delete; virtual void virtual_func() {} virtual void pure_virtual_func() = 0; }; class PyVirtClass : public VirtClass { - void virtual_func() override { - PYBIND11_OVERRIDE(void, VirtClass, virtual_func,); - } + void virtual_func() override { PYBIND11_OVERRIDE(void, VirtClass, virtual_func, ); } void pure_virtual_func() override { - PYBIND11_OVERRIDE_PURE(void, VirtClass, pure_virtual_func,); + PYBIND11_OVERRIDE_PURE(void, VirtClass, pure_virtual_func, ); } }; TEST_SUBMODULE(gil_scoped, m) { - py::class_(m, "VirtClass") - .def(py::init<>()) - .def("virtual_func", &VirtClass::virtual_func) - .def("pure_virtual_func", &VirtClass::pure_virtual_func); - - m.def("test_callback_py_obj", - [](py::object func) { func(); }); - m.def("test_callback_std_func", - [](const std::function &func) { func(); }); - m.def("test_callback_virtual_func", - [](VirtClass &virt) { virt.virtual_func(); }); - m.def("test_callback_pure_virtual_func", - [](VirtClass &virt) { virt.pure_virtual_func(); }); - m.def("test_cross_module_gil", - []() { - auto cm = py::module_::import("cross_module_gil_utils"); - auto gil_acquire = reinterpret_cast( - PyLong_AsVoidPtr(cm.attr("gil_acquire_funcaddr").ptr())); - py::gil_scoped_release gil_release; - gil_acquire(); - }); + py::class_(m, "VirtClass") + .def(py::init<>()) + .def("virtual_func", &VirtClass::virtual_func) + .def("pure_virtual_func", &VirtClass::pure_virtual_func); + + m.def("test_callback_py_obj", [](py::object &func) { func(); }); + m.def("test_callback_std_func", [](const std::function &func) { func(); }); + m.def("test_callback_virtual_func", [](VirtClass &virt) { virt.virtual_func(); }); + m.def("test_callback_pure_virtual_func", [](VirtClass &virt) { virt.pure_virtual_func(); }); + m.def("test_cross_module_gil", []() { + auto cm = py::module_::import("cross_module_gil_utils"); + auto gil_acquire = reinterpret_cast( + PyLong_AsVoidPtr(cm.attr("gil_acquire_funcaddr").ptr())); + py::gil_scoped_release gil_release; + gil_acquire(); + }); } diff --git a/3rdparty/pybind11/tests/test_gil_scoped.py b/3rdparty/pybind11/tests/test_gil_scoped.py index 0a1d6274..52374b0c 100644 --- a/3rdparty/pybind11/tests/test_gil_scoped.py +++ b/3rdparty/pybind11/tests/test_gil_scoped.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- import multiprocessing import threading diff --git a/3rdparty/pybind11/tests/test_iostream.cpp b/3rdparty/pybind11/tests/test_iostream.cpp index e9161505..421eaa2d 100644 --- a/3rdparty/pybind11/tests/test_iostream.cpp +++ b/3rdparty/pybind11/tests/test_iostream.cpp @@ -7,67 +7,120 @@ BSD-style license that can be found in the LICENSE file. */ - #include + #include "pybind11_tests.h" -#include +#include +#include +#include +#include +#include -void noisy_function(std::string msg, bool flush) { +void noisy_function(const std::string &msg, bool flush) { std::cout << msg; - if (flush) + if (flush) { std::cout << std::flush; + } } -void noisy_funct_dual(std::string msg, std::string emsg) { +void noisy_funct_dual(const std::string &msg, const std::string &emsg) { std::cout << msg; std::cerr << emsg; } +// object to manage C++ thread +// simply repeatedly write to std::cerr until stopped +// redirect is called at some point to test the safety of scoped_estream_redirect +struct TestThread { + TestThread() : stop_{false} { + auto thread_f = [this] { + static std::mutex cout_mutex; + while (!stop_) { + { + // #HelpAppreciated: Work on iostream.h thread safety. + // Without this lock, the clang ThreadSanitizer (tsan) reliably reports a + // data race, and this test is predictably flakey on Windows. + // For more background see the discussion under + // https://github.com/pybind/pybind11/pull/2982 and + // https://github.com/pybind/pybind11/pull/2995. + const std::lock_guard lock(cout_mutex); + std::cout << "x" << std::flush; + } + std::this_thread::sleep_for(std::chrono::microseconds(50)); + } + }; + t_ = new std::thread(std::move(thread_f)); + } + + ~TestThread() { delete t_; } + + void stop() { stop_ = true; } + + void join() const { + py::gil_scoped_release gil_lock; + t_->join(); + } + + void sleep() { + py::gil_scoped_release gil_lock; + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + } + + std::thread *t_{nullptr}; + std::atomic stop_; +}; + TEST_SUBMODULE(iostream, m) { add_ostream_redirect(m); // test_evals - m.def("captured_output_default", [](std::string msg) { + m.def("captured_output_default", [](const std::string &msg) { py::scoped_ostream_redirect redir; std::cout << msg << std::flush; }); - m.def("captured_output", [](std::string msg) { + m.def("captured_output", [](const std::string &msg) { py::scoped_ostream_redirect redir(std::cout, py::module_::import("sys").attr("stdout")); std::cout << msg << std::flush; }); - m.def("guard_output", &noisy_function, - py::call_guard(), - py::arg("msg"), py::arg("flush")=true); + m.def("guard_output", + &noisy_function, + py::call_guard(), + py::arg("msg"), + py::arg("flush") = true); - m.def("captured_err", [](std::string msg) { + m.def("captured_err", [](const std::string &msg) { py::scoped_ostream_redirect redir(std::cerr, py::module_::import("sys").attr("stderr")); std::cerr << msg << std::flush; }); m.def("noisy_function", &noisy_function, py::arg("msg"), py::arg("flush") = true); - m.def("dual_guard", &noisy_funct_dual, - py::call_guard(), - py::arg("msg"), py::arg("emsg")); + m.def("dual_guard", + &noisy_funct_dual, + py::call_guard(), + py::arg("msg"), + py::arg("emsg")); - m.def("raw_output", [](std::string msg) { - std::cout << msg << std::flush; - }); + m.def("raw_output", [](const std::string &msg) { std::cout << msg << std::flush; }); - m.def("raw_err", [](std::string msg) { - std::cerr << msg << std::flush; - }); + m.def("raw_err", [](const std::string &msg) { std::cerr << msg << std::flush; }); - m.def("captured_dual", [](std::string msg, std::string emsg) { + m.def("captured_dual", [](const std::string &msg, const std::string &emsg) { py::scoped_ostream_redirect redirout(std::cout, py::module_::import("sys").attr("stdout")); py::scoped_ostream_redirect redirerr(std::cerr, py::module_::import("sys").attr("stderr")); std::cout << msg << std::flush; std::cerr << emsg << std::flush; }); + + py::class_(m, "TestThread") + .def(py::init<>()) + .def("stop", &TestThread::stop) + .def("join", &TestThread::join) + .def("sleep", &TestThread::sleep); } diff --git a/3rdparty/pybind11/tests/test_iostream.py b/3rdparty/pybind11/tests/test_iostream.py index 506db42e..5bbdf695 100644 --- a/3rdparty/pybind11/tests/test_iostream.py +++ b/3rdparty/pybind11/tests/test_iostream.py @@ -1,43 +1,7 @@ -# -*- coding: utf-8 -*- +from contextlib import redirect_stderr, redirect_stdout +from io import StringIO + from pybind11_tests import iostream as m -import sys - -from contextlib import contextmanager - -try: - # Python 3 - from io import StringIO -except ImportError: - # Python 2 - try: - from cStringIO import StringIO - except ImportError: - from StringIO import StringIO - -try: - # Python 3.4 - from contextlib import redirect_stdout -except ImportError: - - @contextmanager - def redirect_stdout(target): - original = sys.stdout - sys.stdout = target - yield - sys.stdout = original - - -try: - # Python 3.5 - from contextlib import redirect_stderr -except ImportError: - - @contextmanager - def redirect_stderr(target): - original = sys.stderr - sys.stderr = target - yield - sys.stderr = original def test_captured(capsys): @@ -69,6 +33,96 @@ def test_captured_large_string(capsys): assert stderr == "" +def test_captured_utf8_2byte_offset0(capsys): + msg = "\u07FF" + msg = "" + msg * (1024 // len(msg) + 1) + + m.captured_output_default(msg) + stdout, stderr = capsys.readouterr() + assert stdout == msg + assert stderr == "" + + +def test_captured_utf8_2byte_offset1(capsys): + msg = "\u07FF" + msg = "1" + msg * (1024 // len(msg) + 1) + + m.captured_output_default(msg) + stdout, stderr = capsys.readouterr() + assert stdout == msg + assert stderr == "" + + +def test_captured_utf8_3byte_offset0(capsys): + msg = "\uFFFF" + msg = "" + msg * (1024 // len(msg) + 1) + + m.captured_output_default(msg) + stdout, stderr = capsys.readouterr() + assert stdout == msg + assert stderr == "" + + +def test_captured_utf8_3byte_offset1(capsys): + msg = "\uFFFF" + msg = "1" + msg * (1024 // len(msg) + 1) + + m.captured_output_default(msg) + stdout, stderr = capsys.readouterr() + assert stdout == msg + assert stderr == "" + + +def test_captured_utf8_3byte_offset2(capsys): + msg = "\uFFFF" + msg = "12" + msg * (1024 // len(msg) + 1) + + m.captured_output_default(msg) + stdout, stderr = capsys.readouterr() + assert stdout == msg + assert stderr == "" + + +def test_captured_utf8_4byte_offset0(capsys): + msg = "\U0010FFFF" + msg = "" + msg * (1024 // len(msg) + 1) + + m.captured_output_default(msg) + stdout, stderr = capsys.readouterr() + assert stdout == msg + assert stderr == "" + + +def test_captured_utf8_4byte_offset1(capsys): + msg = "\U0010FFFF" + msg = "1" + msg * (1024 // len(msg) + 1) + + m.captured_output_default(msg) + stdout, stderr = capsys.readouterr() + assert stdout == msg + assert stderr == "" + + +def test_captured_utf8_4byte_offset2(capsys): + msg = "\U0010FFFF" + msg = "12" + msg * (1024 // len(msg) + 1) + + m.captured_output_default(msg) + stdout, stderr = capsys.readouterr() + assert stdout == msg + assert stderr == "" + + +def test_captured_utf8_4byte_offset3(capsys): + msg = "\U0010FFFF" + msg = "123" + msg * (1024 // len(msg) + 1) + + m.captured_output_default(msg) + stdout, stderr = capsys.readouterr() + assert stdout == msg + assert stderr == "" + + def test_guard_capture(capsys): msg = "I've been redirected to Python, I hope!" m.guard_output(msg) @@ -216,3 +270,26 @@ def test_redirect_both(capfd): assert stderr == "" assert stream.getvalue() == msg assert stream2.getvalue() == msg2 + + +def test_threading(): + with m.ostream_redirect(stdout=True, stderr=False): + # start some threads + threads = [] + + # start some threads + for _j in range(20): + threads.append(m.TestThread()) + + # give the threads some time to fail + threads[0].sleep() + + # stop all the threads + for t in threads: + t.stop() + + for t in threads: + t.join() + + # if a thread segfaults, we don't get here + assert True diff --git a/3rdparty/pybind11/tests/test_kwargs_and_defaults.cpp b/3rdparty/pybind11/tests/test_kwargs_and_defaults.cpp index 627a7969..2f3cabaf 100644 --- a/3rdparty/pybind11/tests/test_kwargs_and_defaults.cpp +++ b/3rdparty/pybind11/tests/test_kwargs_and_defaults.cpp @@ -7,136 +7,276 @@ BSD-style license that can be found in the LICENSE file. */ -#include "pybind11_tests.h" -#include "constructor_stats.h" #include +#include "constructor_stats.h" +#include "pybind11_tests.h" + +#include + TEST_SUBMODULE(kwargs_and_defaults, m) { - auto kw_func = [](int x, int y) { return "x=" + std::to_string(x) + ", y=" + std::to_string(y); }; + auto kw_func + = [](int x, int y) { return "x=" + std::to_string(x) + ", y=" + std::to_string(y); }; // test_named_arguments m.def("kw_func0", kw_func); m.def("kw_func1", kw_func, py::arg("x"), py::arg("y")); m.def("kw_func2", kw_func, py::arg("x") = 100, py::arg("y") = 200); - m.def("kw_func3", [](const char *) { }, py::arg("data") = std::string("Hello world!")); + m.def( + "kw_func3", [](const char *) {}, py::arg("data") = std::string("Hello world!")); /* A fancier default argument */ std::vector list{{13, 17}}; - m.def("kw_func4", [](const std::vector &entries) { - std::string ret = "{"; - for (int i : entries) - ret += std::to_string(i) + " "; - ret.back() = '}'; - return ret; - }, py::arg("myList") = list); + m.def( + "kw_func4", + [](const std::vector &entries) { + std::string ret = "{"; + for (int i : entries) { + ret += std::to_string(i) + " "; + } + ret.back() = '}'; + return ret; + }, + py::arg("myList") = list); - m.def("kw_func_udl", kw_func, "x"_a, "y"_a=300); - m.def("kw_func_udl_z", kw_func, "x"_a, "y"_a=0); + m.def("kw_func_udl", kw_func, "x"_a, "y"_a = 300); + m.def("kw_func_udl_z", kw_func, "x"_a, "y"_a = 0); // test_args_and_kwargs m.def("args_function", [](py::args args) -> py::tuple { - return std::move(args); +#ifdef PYBIND11_DETECTED_CLANG_WITH_MISLEADING_CALL_STD_MOVE_EXPLICITLY_WARNING +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wreturn-std-move" +#endif + return args; +#ifdef PYBIND11_DETECTED_CLANG_WITH_MISLEADING_CALL_STD_MOVE_EXPLICITLY_WARNING +# pragma clang diagnostic pop +#endif }); - m.def("args_kwargs_function", [](py::args args, py::kwargs kwargs) { + m.def("args_kwargs_function", [](const py::args &args, const py::kwargs &kwargs) { return py::make_tuple(args, kwargs); }); // test_mixed_args_and_kwargs - m.def("mixed_plus_args", [](int i, double j, py::args args) { - return py::make_tuple(i, j, args); - }); - m.def("mixed_plus_kwargs", [](int i, double j, py::kwargs kwargs) { - return py::make_tuple(i, j, kwargs); - }); - auto mixed_plus_both = [](int i, double j, py::args args, py::kwargs kwargs) { + m.def("mixed_plus_args", + [](int i, double j, const py::args &args) { return py::make_tuple(i, j, args); }); + m.def("mixed_plus_kwargs", + [](int i, double j, const py::kwargs &kwargs) { return py::make_tuple(i, j, kwargs); }); + auto mixed_plus_both = [](int i, double j, const py::args &args, const py::kwargs &kwargs) { return py::make_tuple(i, j, args, kwargs); }; m.def("mixed_plus_args_kwargs", mixed_plus_both); - m.def("mixed_plus_args_kwargs_defaults", mixed_plus_both, - py::arg("i") = 1, py::arg("j") = 3.14159); - - // test_args_refcount - // PyPy needs a garbage collection to get the reference count values to match CPython's behaviour - #ifdef PYPY_VERSION - #define GC_IF_NEEDED ConstructorStats::gc() - #else - #define GC_IF_NEEDED - #endif - m.def("arg_refcount_h", [](py::handle h) { GC_IF_NEEDED; return h.ref_count(); }); - m.def("arg_refcount_h", [](py::handle h, py::handle, py::handle) { GC_IF_NEEDED; return h.ref_count(); }); - m.def("arg_refcount_o", [](py::object o) { GC_IF_NEEDED; return o.ref_count(); }); + m.def("mixed_plus_args_kwargs_defaults", + mixed_plus_both, + py::arg("i") = 1, + py::arg("j") = 3.14159); + + m.def( + "args_kwonly", + [](int i, double j, const py::args &args, int z) { return py::make_tuple(i, j, args, z); }, + "i"_a, + "j"_a, + "z"_a); + m.def( + "args_kwonly_kwargs", + [](int i, double j, const py::args &args, int z, const py::kwargs &kwargs) { + return py::make_tuple(i, j, args, z, kwargs); + }, + "i"_a, + "j"_a, + py::kw_only{}, + "z"_a); + m.def( + "args_kwonly_kwargs_defaults", + [](int i, double j, const py::args &args, int z, const py::kwargs &kwargs) { + return py::make_tuple(i, j, args, z, kwargs); + }, + "i"_a = 1, + "j"_a = 3.14159, + "z"_a = 42); + m.def( + "args_kwonly_full_monty", + [](int h, int i, double j, const py::args &args, int z, const py::kwargs &kwargs) { + return py::make_tuple(h, i, j, args, z, kwargs); + }, + py::arg() = 1, + py::arg() = 2, + py::pos_only{}, + "j"_a = 3.14159, + "z"_a = 42); + +// test_args_refcount +// PyPy needs a garbage collection to get the reference count values to match CPython's behaviour +#ifdef PYPY_VERSION +# define GC_IF_NEEDED ConstructorStats::gc() +#else +# define GC_IF_NEEDED +#endif + m.def("arg_refcount_h", [](py::handle h) { + GC_IF_NEEDED; + return h.ref_count(); + }); + m.def("arg_refcount_h", [](py::handle h, py::handle, py::handle) { + GC_IF_NEEDED; + return h.ref_count(); + }); + m.def("arg_refcount_o", [](const py::object &o) { + GC_IF_NEEDED; + return o.ref_count(); + }); m.def("args_refcount", [](py::args a) { GC_IF_NEEDED; py::tuple t(a.size()); - for (size_t i = 0; i < a.size(); i++) + for (size_t i = 0; i < a.size(); i++) { // Use raw Python API here to avoid an extra, intermediate incref on the tuple item: t[i] = (int) Py_REFCNT(PyTuple_GET_ITEM(a.ptr(), static_cast(i))); + } return t; }); - m.def("mixed_args_refcount", [](py::object o, py::args a) { + m.def("mixed_args_refcount", [](const py::object &o, py::args a) { GC_IF_NEEDED; py::tuple t(a.size() + 1); t[0] = o.ref_count(); - for (size_t i = 0; i < a.size(); i++) + for (size_t i = 0; i < a.size(); i++) { // Use raw Python API here to avoid an extra, intermediate incref on the tuple item: t[i + 1] = (int) Py_REFCNT(PyTuple_GET_ITEM(a.ptr(), static_cast(i))); + } return t; }); // pybind11 won't allow these to be bound: args and kwargs, if present, must be at the end. // Uncomment these to test that the static_assert is indeed working: -// m.def("bad_args1", [](py::args, int) {}); -// m.def("bad_args2", [](py::kwargs, int) {}); -// m.def("bad_args3", [](py::kwargs, py::args) {}); -// m.def("bad_args4", [](py::args, int, py::kwargs) {}); -// m.def("bad_args5", [](py::args, py::kwargs, int) {}); -// m.def("bad_args6", [](py::args, py::args) {}); -// m.def("bad_args7", [](py::kwargs, py::kwargs) {}); + // m.def("bad_args1", [](py::args, int) {}); + // m.def("bad_args2", [](py::kwargs, int) {}); + // m.def("bad_args3", [](py::kwargs, py::args) {}); + // m.def("bad_args4", [](py::args, int, py::kwargs) {}); + // m.def("bad_args5", [](py::args, py::kwargs, int) {}); + // m.def("bad_args6", [](py::args, py::args) {}); + // m.def("bad_args7", [](py::kwargs, py::kwargs) {}); // test_keyword_only_args - m.def("kw_only_all", [](int i, int j) { return py::make_tuple(i, j); }, - py::kw_only(), py::arg("i"), py::arg("j")); - m.def("kw_only_some", [](int i, int j, int k) { return py::make_tuple(i, j, k); }, - py::arg(), py::kw_only(), py::arg("j"), py::arg("k")); - m.def("kw_only_with_defaults", [](int i, int j, int k, int z) { return py::make_tuple(i, j, k, z); }, - py::arg() = 3, "j"_a = 4, py::kw_only(), "k"_a = 5, "z"_a); - m.def("kw_only_mixed", [](int i, int j) { return py::make_tuple(i, j); }, - "i"_a, py::kw_only(), "j"_a); - m.def("kw_only_plus_more", [](int i, int j, int k, py::kwargs kwargs) { - return py::make_tuple(i, j, k, kwargs); }, - py::arg() /* positional */, py::arg("j") = -1 /* both */, py::kw_only(), py::arg("k") /* kw-only */); + m.def( + "kw_only_all", + [](int i, int j) { return py::make_tuple(i, j); }, + py::kw_only(), + py::arg("i"), + py::arg("j")); + m.def( + "kw_only_some", + [](int i, int j, int k) { return py::make_tuple(i, j, k); }, + py::arg(), + py::kw_only(), + py::arg("j"), + py::arg("k")); + m.def( + "kw_only_with_defaults", + [](int i, int j, int k, int z) { return py::make_tuple(i, j, k, z); }, + py::arg() = 3, + "j"_a = 4, + py::kw_only(), + "k"_a = 5, + "z"_a); + m.def( + "kw_only_mixed", + [](int i, int j) { return py::make_tuple(i, j); }, + "i"_a, + py::kw_only(), + "j"_a); + m.def( + "kw_only_plus_more", + [](int i, int j, int k, const py::kwargs &kwargs) { + return py::make_tuple(i, j, k, kwargs); + }, + py::arg() /* positional */, + py::arg("j") = -1 /* both */, + py::kw_only(), + py::arg("k") /* kw-only */); m.def("register_invalid_kw_only", [](py::module_ m) { - m.def("bad_kw_only", [](int i, int j) { return py::make_tuple(i, j); }, - py::kw_only(), py::arg() /* invalid unnamed argument */, "j"_a); + m.def( + "bad_kw_only", + [](int i, int j) { return py::make_tuple(i, j); }, + py::kw_only(), + py::arg() /* invalid unnamed argument */, + "j"_a); }); // test_positional_only_args - m.def("pos_only_all", [](int i, int j) { return py::make_tuple(i, j); }, - py::arg("i"), py::arg("j"), py::pos_only()); - m.def("pos_only_mix", [](int i, int j) { return py::make_tuple(i, j); }, - py::arg("i"), py::pos_only(), py::arg("j")); - m.def("pos_kw_only_mix", [](int i, int j, int k) { return py::make_tuple(i, j, k); }, - py::arg("i"), py::pos_only(), py::arg("j"), py::kw_only(), py::arg("k")); - m.def("pos_only_def_mix", [](int i, int j, int k) { return py::make_tuple(i, j, k); }, - py::arg("i"), py::arg("j") = 2, py::pos_only(), py::arg("k") = 3); - + m.def( + "pos_only_all", + [](int i, int j) { return py::make_tuple(i, j); }, + py::arg("i"), + py::arg("j"), + py::pos_only()); + m.def( + "pos_only_mix", + [](int i, int j) { return py::make_tuple(i, j); }, + py::arg("i"), + py::pos_only(), + py::arg("j")); + m.def( + "pos_kw_only_mix", + [](int i, int j, int k) { return py::make_tuple(i, j, k); }, + py::arg("i"), + py::pos_only(), + py::arg("j"), + py::kw_only(), + py::arg("k")); + m.def( + "pos_only_def_mix", + [](int i, int j, int k) { return py::make_tuple(i, j, k); }, + py::arg("i"), + py::arg("j") = 2, + py::pos_only(), + py::arg("k") = 3); // These should fail to compile: +#ifdef PYBIND11_NEVER_DEFINED_EVER // argument annotations are required when using kw_only -// m.def("bad_kw_only1", [](int) {}, py::kw_only()); + m.def( + "bad_kw_only1", [](int) {}, py::kw_only()); // can't specify both `py::kw_only` and a `py::args` argument -// m.def("bad_kw_only2", [](int i, py::args) {}, py::kw_only(), "i"_a); + m.def( + "bad_kw_only2", [](int i, py::args) {}, py::kw_only(), "i"_a); +#endif // test_function_signatures (along with most of the above) - struct KWClass { void foo(int, float) {} }; + struct KWClass { + void foo(int, float) {} + }; py::class_(m, "KWClass") .def("foo0", &KWClass::foo) .def("foo1", &KWClass::foo, "x"_a, "y"_a); // Make sure a class (not an instance) can be used as a default argument. // The return value doesn't matter, only that the module is importable. - m.def("class_default_argument", [](py::object a) { return py::repr(a); }, + m.def( + "class_default_argument", + [](py::object a) { return py::repr(std::move(a)); }, "a"_a = py::module_::import("decimal").attr("Decimal")); + + // Initial implementation of kw_only was broken when used on a method/constructor before any + // other arguments + // https://github.com/pybind/pybind11/pull/3402#issuecomment-963341987 + + struct first_arg_kw_only {}; + py::class_(m, "first_arg_kw_only") + .def(py::init([](int) { return first_arg_kw_only(); }), + py::kw_only(), // This being before any args was broken + py::arg("i") = 0) + .def( + "method", + [](first_arg_kw_only &, int, int) {}, + py::kw_only(), // and likewise here + py::arg("i") = 1, + py::arg("j") = 2) + // Closely related: pos_only marker didn't show up properly when it was before any other + // arguments (although that is fairly useless in practice). + .def( + "pos_only", + [](first_arg_kw_only &, int, int) {}, + py::pos_only{}, + py::arg("i"), + py::arg("j")); } diff --git a/3rdparty/pybind11/tests/test_kwargs_and_defaults.py b/3rdparty/pybind11/tests/test_kwargs_and_defaults.py index 12fe705b..ab701788 100644 --- a/3rdparty/pybind11/tests/test_kwargs_and_defaults.py +++ b/3rdparty/pybind11/tests/test_kwargs_and_defaults.py @@ -1,8 +1,5 @@ -# -*- coding: utf-8 -*- import pytest -import env # noqa: F401 - from pybind11_tests import kwargs_and_defaults as m @@ -83,7 +80,7 @@ def test_mixed_args_and_kwargs(msg): 1. (arg0: int, arg1: float, *args) -> tuple Invoked with: 1 - """ # noqa: E501 line too long + """ ) with pytest.raises(TypeError) as excinfo: assert mpa() @@ -94,7 +91,7 @@ def test_mixed_args_and_kwargs(msg): 1. (arg0: int, arg1: float, *args) -> tuple Invoked with: - """ # noqa: E501 line too long + """ ) assert mpk(-2, 3.5, pi=3.14159, e=2.71828) == ( @@ -128,7 +125,7 @@ def test_mixed_args_and_kwargs(msg): 1. (i: int = 1, j: float = 3.14159, *args, **kwargs) -> tuple Invoked with: 1; kwargs: i=1 - """ # noqa: E501 line too long + """ ) with pytest.raises(TypeError) as excinfo: assert mpakd(1, 2, j=1) @@ -139,9 +136,56 @@ def test_mixed_args_and_kwargs(msg): 1. (i: int = 1, j: float = 3.14159, *args, **kwargs) -> tuple Invoked with: 1, 2; kwargs: j=1 - """ # noqa: E501 line too long + """ + ) + + # Arguments after a py::args are automatically keyword-only (pybind 2.9+) + assert m.args_kwonly(2, 2.5, z=22) == (2, 2.5, (), 22) + assert m.args_kwonly(2, 2.5, "a", "b", "c", z=22) == (2, 2.5, ("a", "b", "c"), 22) + assert m.args_kwonly(z=22, i=4, j=16) == (4, 16, (), 22) + + with pytest.raises(TypeError) as excinfo: + assert m.args_kwonly(2, 2.5, 22) # missing z= keyword + assert ( + msg(excinfo.value) + == """ + args_kwonly(): incompatible function arguments. The following argument types are supported: + 1. (i: int, j: float, *args, z: int) -> tuple + + Invoked with: 2, 2.5, 22 + """ ) + assert m.args_kwonly_kwargs(i=1, k=4, j=10, z=-1, y=9) == ( + 1, + 10, + (), + -1, + {"k": 4, "y": 9}, + ) + assert m.args_kwonly_kwargs(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, z=11, y=12) == ( + 1, + 2, + (3, 4, 5, 6, 7, 8, 9, 10), + 11, + {"y": 12}, + ) + assert ( + m.args_kwonly_kwargs.__doc__ + == "args_kwonly_kwargs(i: int, j: float, *args, z: int, **kwargs) -> tuple\n" + ) + + assert ( + m.args_kwonly_kwargs_defaults.__doc__ + == "args_kwonly_kwargs_defaults(i: int = 1, j: float = 3.14159, *args, z: int = 42, **kwargs) -> tuple\n" # noqa: E501 line too long + ) + assert m.args_kwonly_kwargs_defaults() == (1, 3.14159, (), 42, {}) + assert m.args_kwonly_kwargs_defaults(2) == (2, 3.14159, (), 42, {}) + assert m.args_kwonly_kwargs_defaults(z=-99) == (1, 3.14159, (), -99, {}) + assert m.args_kwonly_kwargs_defaults(5, 6, 7, 8) == (5, 6, (7, 8), 42, {}) + assert m.args_kwonly_kwargs_defaults(5, 6, 7, m=8) == (5, 6, (7,), 42, {"m": 8}) + assert m.args_kwonly_kwargs_defaults(5, 6, 7, m=8, z=9) == (5, 6, (7,), 9, {"m": 8}) + def test_keyword_only_args(msg): assert m.kw_only_all(i=1, j=2) == (1, 2) @@ -179,10 +223,23 @@ def test_keyword_only_args(msg): assert ( msg(excinfo.value) == """ - arg(): cannot specify an unnamed argument after an kw_only() annotation + arg(): cannot specify an unnamed argument after a kw_only() annotation or args() argument """ ) + # https://github.com/pybind/pybind11/pull/3402#issuecomment-963341987 + x = m.first_arg_kw_only(i=1) + x.method() + x.method(i=1, j=2) + assert ( + m.first_arg_kw_only.__init__.__doc__ + == "__init__(self: pybind11_tests.kwargs_and_defaults.first_arg_kw_only, *, i: int = 0) -> None\n" # noqa: E501 line too long + ) + assert ( + m.first_arg_kw_only.method.__doc__ + == "method(self: pybind11_tests.kwargs_and_defaults.first_arg_kw_only, *, i: int = 1, j: int = 2) -> None\n" # noqa: E501 line too long + ) + def test_positional_only_args(msg): assert m.pos_only_all(1, 2) == (1, 2) @@ -223,6 +280,55 @@ def test_positional_only_args(msg): m.pos_only_def_mix(1, j=4) assert "incompatible function arguments" in str(excinfo.value) + # Mix it with args and kwargs: + assert ( + m.args_kwonly_full_monty.__doc__ + == "args_kwonly_full_monty(arg0: int = 1, arg1: int = 2, /, j: float = 3.14159, *args, z: int = 42, **kwargs) -> tuple\n" # noqa: E501 line too long + ) + assert m.args_kwonly_full_monty() == (1, 2, 3.14159, (), 42, {}) + assert m.args_kwonly_full_monty(8) == (8, 2, 3.14159, (), 42, {}) + assert m.args_kwonly_full_monty(8, 9) == (8, 9, 3.14159, (), 42, {}) + assert m.args_kwonly_full_monty(8, 9, 10) == (8, 9, 10.0, (), 42, {}) + assert m.args_kwonly_full_monty(3, 4, 5, 6, 7, m=8, z=9) == ( + 3, + 4, + 5.0, + ( + 6, + 7, + ), + 9, + {"m": 8}, + ) + assert m.args_kwonly_full_monty(3, 4, 5, 6, 7, m=8, z=9) == ( + 3, + 4, + 5.0, + ( + 6, + 7, + ), + 9, + {"m": 8}, + ) + assert m.args_kwonly_full_monty(5, j=7, m=8, z=9) == (5, 2, 7.0, (), 9, {"m": 8}) + assert m.args_kwonly_full_monty(i=5, j=7, m=8, z=9) == ( + 1, + 2, + 7.0, + (), + 9, + {"i": 5, "m": 8}, + ) + + # pos_only at the beginning of the argument list was "broken" in how it was displayed (though + # this is fairly useless in practice). Related to: + # https://github.com/pybind/pybind11/pull/3402#issuecomment-963341987 + assert ( + m.first_arg_kw_only.pos_only.__doc__ + == "pos_only(self: pybind11_tests.kwargs_and_defaults.first_arg_kw_only, /, i: int, j: int) -> None\n" # noqa: E501 line too long + ) + def test_signatures(): assert "kw_only_all(*, i: int, j: int) -> tuple\n" == m.kw_only_all.__doc__ @@ -235,7 +341,6 @@ def test_signatures(): ) -@pytest.mark.xfail("env.PYPY and env.PY2", reason="PyPy2 doesn't double count") def test_args_refcount(): """Issue/PR #1216 - py::args elements get double-inc_ref()ed when combined with regular arguments""" diff --git a/3rdparty/pybind11/tests/test_local_bindings.cpp b/3rdparty/pybind11/tests/test_local_bindings.cpp index c61e3888..13736774 100644 --- a/3rdparty/pybind11/tests/test_local_bindings.cpp +++ b/3rdparty/pybind11/tests/test_local_bindings.cpp @@ -8,11 +8,14 @@ BSD-style license that can be found in the LICENSE file. */ -#include "pybind11_tests.h" -#include "local_bindings.h" #include #include + +#include "local_bindings.h" +#include "pybind11_tests.h" + #include +#include TEST_SUBMODULE(local_bindings, m) { // test_load_external @@ -21,9 +24,9 @@ TEST_SUBMODULE(local_bindings, m) { // test_local_bindings // Register a class with py::module_local: - bind_local(m, "LocalType", py::module_local()) - .def("get3", [](LocalType &t) { return t.i + 3; }) - ; + bind_local(m, "LocalType", py::module_local()).def("get3", [](LocalType &t) { + return t.i + 3; + }); m.def("local_value", [](LocalType &l) { return l.i; }); @@ -32,20 +35,21 @@ TEST_SUBMODULE(local_bindings, m) { // one, in pybind11_cross_module_tests.cpp, is designed to fail): bind_local(m, "NonLocalType") .def(py::init()) - .def("get", [](LocalType &i) { return i.i; }) - ; + .def("get", [](LocalType &i) { return i.i; }); // test_duplicate_local - // py::module_local declarations should be visible across compilation units that get linked together; - // this tries to register a duplicate local. It depends on a definition in test_class.cpp and - // should raise a runtime error from the duplicate definition attempt. If test_class isn't - // available it *also* throws a runtime error (with "test_class not enabled" as value). + // py::module_local declarations should be visible across compilation units that get linked + // together; this tries to register a duplicate local. It depends on a definition in + // test_class.cpp and should raise a runtime error from the duplicate definition attempt. If + // test_class isn't available it *also* throws a runtime error (with "test_class not enabled" + // as value). m.def("register_local_external", [m]() { auto main = py::module_::import("pybind11_tests"); if (py::hasattr(main, "class_")) { bind_local(m, "LocalExternal", py::module_local()); + } else { + throw std::runtime_error("test_class not enabled"); } - else throw std::runtime_error("test_class not enabled"); }); // test_stl_bind_local @@ -75,23 +79,24 @@ TEST_SUBMODULE(local_bindings, m) { m.def("get_mixed_lg", [](int i) { return MixedLocalGlobal(i); }); // test_internal_locals_differ - m.def("local_cpp_types_addr", []() { return (uintptr_t) &py::detail::registered_local_types_cpp(); }); + m.def("local_cpp_types_addr", + []() { return (uintptr_t) &py::detail::get_local_internals().registered_types_cpp; }); // test_stl_caster_vs_stl_bind - m.def("load_vector_via_caster", [](std::vector v) { - return std::accumulate(v.begin(), v.end(), 0); - }); + m.def("load_vector_via_caster", + [](std::vector v) { return std::accumulate(v.begin(), v.end(), 0); }); // test_cross_module_calls m.def("return_self", [](LocalVec *v) { return v; }); m.def("return_copy", [](const LocalVec &v) { return LocalVec(v); }); - class Cat : public pets::Pet { public: Cat(std::string name) : Pet(name) {}; }; - py::class_(m, "Pet", py::module_local()) - .def("get_name", &pets::Pet::name); + class Cat : public pets::Pet { + public: + explicit Cat(std::string name) : Pet(std::move(name)) {} + }; + py::class_(m, "Pet", py::module_local()).def("get_name", &pets::Pet::name); // Binding for local extending class: - py::class_(m, "Cat") - .def(py::init()); + py::class_(m, "Cat").def(py::init()); m.def("pet_name", [](pets::Pet &p) { return p.name(); }); py::class_(m, "MixGL").def(py::init()); diff --git a/3rdparty/pybind11/tests/test_local_bindings.py b/3rdparty/pybind11/tests/test_local_bindings.py index d23c4675..654d96d4 100644 --- a/3rdparty/pybind11/tests/test_local_bindings.py +++ b/3rdparty/pybind11/tests/test_local_bindings.py @@ -1,8 +1,6 @@ -# -*- coding: utf-8 -*- import pytest import env # noqa: F401 - from pybind11_tests import local_bindings as m @@ -193,7 +191,7 @@ def test_stl_caster_vs_stl_bind(msg): v2 = [1, 2, 3] assert m.load_vector_via_caster(v2) == 6 with pytest.raises(TypeError) as excinfo: - cm.load_vector_via_binding(v2) == 6 + cm.load_vector_via_binding(v2) assert ( msg(excinfo.value) == """ @@ -201,7 +199,7 @@ def test_stl_caster_vs_stl_bind(msg): 1. (arg0: pybind11_cross_module_tests.VectorInt) -> int Invoked with: [1, 2, 3] - """ # noqa: E501 line too long + """ ) diff --git a/3rdparty/pybind11/tests/test_methods_and_attributes.cpp b/3rdparty/pybind11/tests/test_methods_and_attributes.cpp index 6a2cfb6f..815dd5e9 100644 --- a/3rdparty/pybind11/tests/test_methods_and_attributes.cpp +++ b/3rdparty/pybind11/tests/test_methods_and_attributes.cpp @@ -8,8 +8,8 @@ BSD-style license that can be found in the LICENSE file. */ -#include "pybind11_tests.h" #include "constructor_stats.h" +#include "pybind11_tests.h" #if !defined(PYBIND11_OVERLOAD_CAST) template @@ -19,55 +19,61 @@ using overload_cast_ = pybind11::detail::overload_cast_impl; class ExampleMandA { public: ExampleMandA() { print_default_created(this); } - ExampleMandA(int value) : value(value) { print_created(this, value); } + explicit ExampleMandA(int value) : value(value) { print_created(this, value); } ExampleMandA(const ExampleMandA &e) : value(e.value) { print_copy_created(this); } - ExampleMandA(std::string&&) {} - ExampleMandA(ExampleMandA &&e) : value(e.value) { print_move_created(this); } + explicit ExampleMandA(std::string &&) {} + ExampleMandA(ExampleMandA &&e) noexcept : value(e.value) { print_move_created(this); } ~ExampleMandA() { print_destroyed(this); } - std::string toString() { - return "ExampleMandA[value=" + std::to_string(value) + "]"; - } + std::string toString() const { return "ExampleMandA[value=" + std::to_string(value) + "]"; } - void operator=(const ExampleMandA &e) { print_copy_assigned(this); value = e.value; } - void operator=(ExampleMandA &&e) { print_move_assigned(this); value = e.value; } + void operator=(const ExampleMandA &e) { + print_copy_assigned(this); + value = e.value; + } + void operator=(ExampleMandA &&e) noexcept { + print_move_assigned(this); + value = e.value; + } + // NOLINTNEXTLINE(performance-unnecessary-value-param) void add1(ExampleMandA other) { value += other.value; } // passing by value void add2(ExampleMandA &other) { value += other.value; } // passing by reference void add3(const ExampleMandA &other) { value += other.value; } // passing by const reference void add4(ExampleMandA *other) { value += other->value; } // passing by pointer void add5(const ExampleMandA *other) { value += other->value; } // passing by const pointer - void add6(int other) { value += other; } // passing by value - void add7(int &other) { value += other; } // passing by reference - void add8(const int &other) { value += other; } // passing by const reference - void add9(int *other) { value += *other; } // passing by pointer - void add10(const int *other) { value += *other; } // passing by const pointer - - void consume_str(std::string&&) {} - - ExampleMandA self1() { return *this; } // return by value - ExampleMandA &self2() { return *this; } // return by reference - const ExampleMandA &self3() { return *this; } // return by const reference - ExampleMandA *self4() { return this; } // return by pointer - const ExampleMandA *self5() { return this; } // return by const pointer - - int internal1() { return value; } // return by value - int &internal2() { return value; } // return by reference - const int &internal3() { return value; } // return by const reference - int *internal4() { return &value; } // return by pointer - const int *internal5() { return &value; } // return by const pointer - - py::str overloaded() { return "()"; } - py::str overloaded(int) { return "(int)"; } - py::str overloaded(int, float) { return "(int, float)"; } - py::str overloaded(float, int) { return "(float, int)"; } - py::str overloaded(int, int) { return "(int, int)"; } + void add6(int other) { value += other; } // passing by value + void add7(int &other) { value += other; } // passing by reference + void add8(const int &other) { value += other; } // passing by const reference + // NOLINTNEXTLINE(readability-non-const-parameter) Deliberately non-const for testing + void add9(int *other) { value += *other; } // passing by pointer + void add10(const int *other) { value += *other; } // passing by const pointer + + void consume_str(std::string &&) {} + + ExampleMandA self1() { return *this; } // return by value + ExampleMandA &self2() { return *this; } // return by reference + const ExampleMandA &self3() const { return *this; } // return by const reference + ExampleMandA *self4() { return this; } // return by pointer + const ExampleMandA *self5() const { return this; } // return by const pointer + + int internal1() const { return value; } // return by value + int &internal2() { return value; } // return by reference + const int &internal3() const { return value; } // return by const reference + int *internal4() { return &value; } // return by pointer + const int *internal5() { return &value; } // return by const pointer + + py::str overloaded() { return "()"; } + py::str overloaded(int) { return "(int)"; } + py::str overloaded(int, float) { return "(int, float)"; } + py::str overloaded(float, int) { return "(float, int)"; } + py::str overloaded(int, int) { return "(int, int)"; } py::str overloaded(float, float) { return "(float, float)"; } - py::str overloaded(int) const { return "(int) const"; } - py::str overloaded(int, float) const { return "(int, float) const"; } - py::str overloaded(float, int) const { return "(float, int) const"; } - py::str overloaded(int, int) const { return "(int, int) const"; } + py::str overloaded(int) const { return "(int) const"; } + py::str overloaded(int, float) const { return "(int, float) const"; } + py::str overloaded(float, int) const { return "(float, int) const"; } + py::str overloaded(int, int) const { return "(int, int) const"; } py::str overloaded(float, float) const { return "(float, float) const"; } static py::str overloaded(float) { return "static float"; } @@ -109,25 +115,40 @@ UserType TestPropRVP::sv1(1); UserType TestPropRVP::sv2(1); // Test None-allowed py::arg argument policy -class NoneTester { public: int answer = 42; }; +class NoneTester { +public: + int answer = 42; +}; int none1(const NoneTester &obj) { return obj.answer; } int none2(NoneTester *obj) { return obj ? obj->answer : -1; } int none3(std::shared_ptr &obj) { return obj ? obj->answer : -1; } int none4(std::shared_ptr *obj) { return obj && *obj ? (*obj)->answer : -1; } -int none5(std::shared_ptr obj) { return obj ? obj->answer : -1; } +int none5(const std::shared_ptr &obj) { return obj ? obj->answer : -1; } + +// Issue #2778: implicit casting from None to object (not pointer) +class NoneCastTester { +public: + int answer = -1; + NoneCastTester() = default; + explicit NoneCastTester(int v) : answer(v) {} +}; struct StrIssue { int val = -1; StrIssue() = default; - StrIssue(int i) : val{i} {} + explicit StrIssue(int i) : val{i} {} }; -// Issues #854, #910: incompatible function args when member function/pointer is in unregistered base class +// Issues #854, #910: incompatible function args when member function/pointer is in unregistered +// base class class UnregisteredBase { public: void do_nothing() const {} - void increase_value() { rw_value++; ro_value += 0.25; } + void increase_value() { + rw_value++; + ro_value += 0.25; + } void set_int(int v) { rw_value = v; } int get_int() const { return rw_value; } double get_double() const { return ro_value; } @@ -148,13 +169,21 @@ struct RefQualified { int constRefQualified(int other) const & { return value + other; } }; +// Test rvalue ref param +struct RValueRefParam { + std::size_t func1(std::string &&s) { return s.size(); } + std::size_t func2(std::string &&s) const { return s.size(); } + std::size_t func3(std::string &&s) & { return s.size(); } + std::size_t func4(std::string &&s) const & { return s.size(); } +}; + TEST_SUBMODULE(methods_and_attributes, m) { // test_methods_and_attributes py::class_ emna(m, "ExampleMandA"); emna.def(py::init<>()) .def(py::init()) - .def(py::init()) - .def(py::init()) + .def(py::init()) + .def(py::init()) .def("add1", &ExampleMandA::add1) .def("add2", &ExampleMandA::add2) .def("add3", &ExampleMandA::add3) @@ -179,16 +208,20 @@ TEST_SUBMODULE(methods_and_attributes, m) { #if defined(PYBIND11_OVERLOAD_CAST) .def("overloaded", py::overload_cast<>(&ExampleMandA::overloaded)) .def("overloaded", py::overload_cast(&ExampleMandA::overloaded)) - .def("overloaded", py::overload_cast(&ExampleMandA::overloaded)) - .def("overloaded", py::overload_cast(&ExampleMandA::overloaded)) - .def("overloaded", py::overload_cast(&ExampleMandA::overloaded)) + .def("overloaded", py::overload_cast(&ExampleMandA::overloaded)) + .def("overloaded", py::overload_cast(&ExampleMandA::overloaded)) + .def("overloaded", py::overload_cast(&ExampleMandA::overloaded)) .def("overloaded", py::overload_cast(&ExampleMandA::overloaded)) .def("overloaded_float", py::overload_cast(&ExampleMandA::overloaded)) - .def("overloaded_const", py::overload_cast(&ExampleMandA::overloaded, py::const_)) - .def("overloaded_const", py::overload_cast(&ExampleMandA::overloaded, py::const_)) - .def("overloaded_const", py::overload_cast(&ExampleMandA::overloaded, py::const_)) - .def("overloaded_const", py::overload_cast(&ExampleMandA::overloaded, py::const_)) - .def("overloaded_const", py::overload_cast(&ExampleMandA::overloaded, py::const_)) + .def("overloaded_const", py::overload_cast(&ExampleMandA::overloaded, py::const_)) + .def("overloaded_const", + py::overload_cast(&ExampleMandA::overloaded, py::const_)) + .def("overloaded_const", + py::overload_cast(&ExampleMandA::overloaded, py::const_)) + .def("overloaded_const", + py::overload_cast(&ExampleMandA::overloaded, py::const_)) + .def("overloaded_const", + py::overload_cast(&ExampleMandA::overloaded, py::const_)) #else // Use both the traditional static_cast method and the C++11 compatible overload_cast_ .def("overloaded", overload_cast_<>()(&ExampleMandA::overloaded)) @@ -206,16 +239,29 @@ TEST_SUBMODULE(methods_and_attributes, m) { #endif // test_no_mixed_overloads // Raise error if trying to mix static/non-static overloads on the same name: - .def_static("add_mixed_overloads1", []() { - auto emna = py::reinterpret_borrow>(py::module_::import("pybind11_tests.methods_and_attributes").attr("ExampleMandA")); - emna.def ("overload_mixed1", static_cast(&ExampleMandA::overloaded)) - .def_static("overload_mixed1", static_cast(&ExampleMandA::overloaded)); - }) - .def_static("add_mixed_overloads2", []() { - auto emna = py::reinterpret_borrow>(py::module_::import("pybind11_tests.methods_and_attributes").attr("ExampleMandA")); - emna.def_static("overload_mixed2", static_cast(&ExampleMandA::overloaded)) - .def ("overload_mixed2", static_cast(&ExampleMandA::overloaded)); - }) + .def_static("add_mixed_overloads1", + []() { + auto emna = py::reinterpret_borrow>( + py::module_::import("pybind11_tests.methods_and_attributes") + .attr("ExampleMandA")); + emna.def("overload_mixed1", + static_cast( + &ExampleMandA::overloaded)) + .def_static( + "overload_mixed1", + static_cast(&ExampleMandA::overloaded)); + }) + .def_static("add_mixed_overloads2", + []() { + auto emna = py::reinterpret_borrow>( + py::module_::import("pybind11_tests.methods_and_attributes") + .attr("ExampleMandA")); + emna.def_static("overload_mixed2", + static_cast(&ExampleMandA::overloaded)) + .def("overload_mixed2", + static_cast( + &ExampleMandA::overloaded)); + }) .def("__str__", &ExampleMandA::toString) .def_readwrite("value", &ExampleMandA::value); @@ -228,36 +274,41 @@ TEST_SUBMODULE(methods_and_attributes, m) { .def(py::init<>()) .def_readonly("def_readonly", &TestProperties::value) .def_readwrite("def_readwrite", &TestProperties::value) - .def_property("def_writeonly", nullptr, - [](TestProperties& s,int v) { s.value = v; } ) + .def_property("def_writeonly", nullptr, [](TestProperties &s, int v) { s.value = v; }) .def_property("def_property_writeonly", nullptr, &TestProperties::set) .def_property_readonly("def_property_readonly", &TestProperties::get) .def_property("def_property", &TestProperties::get, &TestProperties::set) .def_property("def_property_impossible", nullptr, nullptr) .def_readonly_static("def_readonly_static", &TestProperties::static_value) .def_readwrite_static("def_readwrite_static", &TestProperties::static_value) - .def_property_static("def_writeonly_static", nullptr, - [](py::object, int v) { TestProperties::static_value = v; }) - .def_property_readonly_static("def_property_readonly_static", - [](py::object) { return TestProperties::static_get(); }) - .def_property_static("def_property_writeonly_static", nullptr, - [](py::object, int v) { return TestProperties::static_set(v); }) - .def_property_static("def_property_static", - [](py::object) { return TestProperties::static_get(); }, - [](py::object, int v) { TestProperties::static_set(v); }) - .def_property_static("static_cls", - [](py::object cls) { return cls; }, - [](py::object cls, py::function f) { f(cls); }); + .def_property_static("def_writeonly_static", + nullptr, + [](const py::object &, int v) { TestProperties::static_value = v; }) + .def_property_readonly_static( + "def_property_readonly_static", + [](const py::object &) { return TestProperties::static_get(); }) + .def_property_static( + "def_property_writeonly_static", + nullptr, + [](const py::object &, int v) { return TestProperties::static_set(v); }) + .def_property_static( + "def_property_static", + [](const py::object &) { return TestProperties::static_get(); }, + [](const py::object &, int v) { TestProperties::static_set(v); }) + .def_property_static( + "static_cls", + [](py::object cls) { return cls; }, + [](const py::object &cls, const py::function &f) { f(cls); }); py::class_(m, "TestPropertiesOverride") .def(py::init<>()) .def_readonly("def_readonly", &TestPropertiesOverride::value) .def_readonly_static("def_readonly_static", &TestPropertiesOverride::static_value); - auto static_get1 = [](py::object) -> const UserType & { return TestPropRVP::sv1; }; - auto static_get2 = [](py::object) -> const UserType & { return TestPropRVP::sv2; }; - auto static_set1 = [](py::object, int v) { TestPropRVP::sv1.set(v); }; - auto static_set2 = [](py::object, int v) { TestPropRVP::sv2.set(v); }; + auto static_get1 = [](const py::object &) -> const UserType & { return TestPropRVP::sv1; }; + auto static_get2 = [](const py::object &) -> const UserType & { return TestPropRVP::sv2; }; + auto static_set1 = [](const py::object &, int v) { TestPropRVP::sv1.set(v); }; + auto static_set2 = [](const py::object &, int v) { TestPropRVP::sv2.set(v); }; auto rvp_copy = py::return_value_policy::copy; // test_property_return_value_policies @@ -268,85 +319,104 @@ TEST_SUBMODULE(methods_and_attributes, m) { .def_property_readonly("ro_func", py::cpp_function(&TestPropRVP::get2, rvp_copy)) .def_property("rw_ref", &TestPropRVP::get1, &TestPropRVP::set1) .def_property("rw_copy", &TestPropRVP::get2, &TestPropRVP::set2, rvp_copy) - .def_property("rw_func", py::cpp_function(&TestPropRVP::get2, rvp_copy), &TestPropRVP::set2) + .def_property( + "rw_func", py::cpp_function(&TestPropRVP::get2, rvp_copy), &TestPropRVP::set2) .def_property_readonly_static("static_ro_ref", static_get1) .def_property_readonly_static("static_ro_copy", static_get2, rvp_copy) .def_property_readonly_static("static_ro_func", py::cpp_function(static_get2, rvp_copy)) .def_property_static("static_rw_ref", static_get1, static_set1) .def_property_static("static_rw_copy", static_get2, static_set2, rvp_copy) - .def_property_static("static_rw_func", py::cpp_function(static_get2, rvp_copy), static_set2) + .def_property_static( + "static_rw_func", py::cpp_function(static_get2, rvp_copy), static_set2) // test_property_rvalue_policy .def_property_readonly("rvalue", &TestPropRVP::get_rvalue) - .def_property_readonly_static("static_rvalue", [](py::object) { return UserType(1); }); + .def_property_readonly_static("static_rvalue", + [](const py::object &) { return UserType(1); }); // test_metaclass_override - struct MetaclassOverride { }; + struct MetaclassOverride {}; py::class_(m, "MetaclassOverride", py::metaclass((PyObject *) &PyType_Type)) - .def_property_readonly_static("readonly", [](py::object) { return 1; }); + .def_property_readonly_static("readonly", [](const py::object &) { return 1; }); // test_overload_ordering - m.def("overload_order", [](std::string) { return 1; }); - m.def("overload_order", [](std::string) { return 2; }); + m.def("overload_order", [](const std::string &) { return 1; }); + m.def("overload_order", [](const std::string &) { return 2; }); m.def("overload_order", [](int) { return 3; }); - m.def("overload_order", [](int) { return 4; }, py::prepend{}); + m.def( + "overload_order", [](int) { return 4; }, py::prepend{}); #if !defined(PYPY_VERSION) // test_dynamic_attributes class DynamicClass { public: DynamicClass() { print_default_created(this); } - DynamicClass(const DynamicClass&) = delete; + DynamicClass(const DynamicClass &) = delete; ~DynamicClass() { print_destroyed(this); } }; - py::class_(m, "DynamicClass", py::dynamic_attr()) - .def(py::init()); + py::class_(m, "DynamicClass", py::dynamic_attr()).def(py::init()); - class CppDerivedDynamicClass : public DynamicClass { }; - py::class_(m, "CppDerivedDynamicClass") - .def(py::init()); + class CppDerivedDynamicClass : public DynamicClass {}; + py::class_(m, "CppDerivedDynamicClass").def(py::init()); #endif // test_bad_arg_default // Issue/PR #648: bad arg default debugging output -#if !defined(NDEBUG) - m.attr("debug_enabled") = true; +#if defined(PYBIND11_DETAILED_ERROR_MESSAGES) + m.attr("detailed_error_messages_enabled") = true; #else - m.attr("debug_enabled") = false; + m.attr("detailed_error_messages_enabled") = false; #endif - m.def("bad_arg_def_named", []{ + m.def("bad_arg_def_named", [] { auto m = py::module_::import("pybind11_tests"); - m.def("should_fail", [](int, UnregisteredType) {}, py::arg(), py::arg("a") = UnregisteredType()); + m.def( + "should_fail", + [](int, UnregisteredType) {}, + py::arg(), + py::arg("a") = UnregisteredType()); }); - m.def("bad_arg_def_unnamed", []{ + m.def("bad_arg_def_unnamed", [] { auto m = py::module_::import("pybind11_tests"); - m.def("should_fail", [](int, UnregisteredType) {}, py::arg(), py::arg() = UnregisteredType()); + m.def( + "should_fail", + [](int, UnregisteredType) {}, + py::arg(), + py::arg() = UnregisteredType()); }); + // [workaround(intel)] ICC 20/21 breaks with py::arg().stuff, using py::arg{}.stuff works. + // test_accepts_none - py::class_>(m, "NoneTester") - .def(py::init<>()); - m.def("no_none1", &none1, py::arg().none(false)); - m.def("no_none2", &none2, py::arg().none(false)); - m.def("no_none3", &none3, py::arg().none(false)); - m.def("no_none4", &none4, py::arg().none(false)); - m.def("no_none5", &none5, py::arg().none(false)); + py::class_>(m, "NoneTester").def(py::init<>()); + m.def("no_none1", &none1, py::arg{}.none(false)); + m.def("no_none2", &none2, py::arg{}.none(false)); + m.def("no_none3", &none3, py::arg{}.none(false)); + m.def("no_none4", &none4, py::arg{}.none(false)); + m.def("no_none5", &none5, py::arg{}.none(false)); m.def("ok_none1", &none1); - m.def("ok_none2", &none2, py::arg().none(true)); + m.def("ok_none2", &none2, py::arg{}.none(true)); m.def("ok_none3", &none3); - m.def("ok_none4", &none4, py::arg().none(true)); + m.def("ok_none4", &none4, py::arg{}.none(true)); m.def("ok_none5", &none5); - m.def("no_none_kwarg", &none2, py::arg("a").none(false)); - m.def("no_none_kwarg_kw_only", &none2, py::kw_only(), py::arg("a").none(false)); + m.def("no_none_kwarg", &none2, "a"_a.none(false)); + m.def("no_none_kwarg_kw_only", &none2, py::kw_only(), "a"_a.none(false)); + + // test_casts_none + // Issue #2778: implicit casting from None to object (not pointer) + py::class_(m, "NoneCastTester") + .def(py::init<>()) + .def(py::init()) + .def(py::init([](py::none const &) { return NoneCastTester{}; })); + py::implicitly_convertible(); + m.def("ok_obj_or_none", [](NoneCastTester const &foo) { return foo.answer; }); // test_str_issue // Issue #283: __str__ called on uninitialized instance when constructor arguments invalid py::class_(m, "StrIssue") .def(py::init()) .def(py::init<>()) - .def("__str__", [](const StrIssue &si) { - return "StrIssue[" + std::to_string(si.val) + "]"; } - ); + .def("__str__", + [](const StrIssue &si) { return "StrIssue[" + std::to_string(si.val) + "]"; }); // test_unregistered_base_implementations // @@ -360,16 +430,17 @@ TEST_SUBMODULE(methods_and_attributes, m) { .def("increase_value", &RegisteredDerived::increase_value) .def_readwrite("rw_value", &RegisteredDerived::rw_value) .def_readonly("ro_value", &RegisteredDerived::ro_value) - // These should trigger a static_assert if uncommented - //.def_readwrite("fails", &UserType::value) // should trigger a static_assert if uncommented - //.def_readonly("fails", &UserType::value) // should trigger a static_assert if uncommented + // Uncommenting the next line should trigger a static_assert: + // .def_readwrite("fails", &UserType::value) + // Uncommenting the next line should trigger a static_assert: + // .def_readonly("fails", &UserType::value) .def_property("rw_value_prop", &RegisteredDerived::get_int, &RegisteredDerived::set_int) .def_property_readonly("ro_value_prop", &RegisteredDerived::get_double) // This one is in the registered class: - .def("sum", &RegisteredDerived::sum) - ; + .def("sum", &RegisteredDerived::sum); - using Adapted = decltype(py::method_adaptor(&RegisteredDerived::do_nothing)); + using Adapted + = decltype(py::method_adaptor(&RegisteredDerived::do_nothing)); static_assert(std::is_same::value, ""); // test_methods_and_attributes @@ -378,4 +449,11 @@ TEST_SUBMODULE(methods_and_attributes, m) { .def_readonly("value", &RefQualified::value) .def("refQualified", &RefQualified::refQualified) .def("constRefQualified", &RefQualified::constRefQualified); + + py::class_(m, "RValueRefParam") + .def(py::init<>()) + .def("func1", &RValueRefParam::func1) + .def("func2", &RValueRefParam::func2) + .def("func3", &RValueRefParam::func3) + .def("func4", &RValueRefParam::func4); } diff --git a/3rdparty/pybind11/tests/test_methods_and_attributes.py b/3rdparty/pybind11/tests/test_methods_and_attributes.py index 2aaf9331..0a2ae123 100644 --- a/3rdparty/pybind11/tests/test_methods_and_attributes.py +++ b/3rdparty/pybind11/tests/test_methods_and_attributes.py @@ -1,10 +1,20 @@ -# -*- coding: utf-8 -*- +import sys + import pytest import env # noqa: F401 - -from pybind11_tests import methods_and_attributes as m from pybind11_tests import ConstructorStats +from pybind11_tests import methods_and_attributes as m + +NO_GETTER_MSG = ( + "unreadable attribute" if sys.version_info < (3, 11) else "object has no getter" +) +NO_SETTER_MSG = ( + "can't set attribute" if sys.version_info < (3, 11) else "object has no setter" +) +NO_DELETER_MSG = ( + "can't delete attribute" if sys.version_info < (3, 11) else "object has no deleter" +) def test_methods_and_attributes(): @@ -103,33 +113,33 @@ def test_properties(): assert instance.def_property == 3 with pytest.raises(AttributeError) as excinfo: - dummy = instance.def_property_writeonly # noqa: F841 unused var - assert "unreadable attribute" in str(excinfo.value) + dummy = instance.def_property_writeonly # unused var + assert NO_GETTER_MSG in str(excinfo.value) instance.def_property_writeonly = 4 assert instance.def_property_readonly == 4 with pytest.raises(AttributeError) as excinfo: dummy = instance.def_property_impossible # noqa: F841 unused var - assert "unreadable attribute" in str(excinfo.value) + assert NO_GETTER_MSG in str(excinfo.value) with pytest.raises(AttributeError) as excinfo: instance.def_property_impossible = 5 - assert "can't set attribute" in str(excinfo.value) + assert NO_SETTER_MSG in str(excinfo.value) def test_static_properties(): assert m.TestProperties.def_readonly_static == 1 with pytest.raises(AttributeError) as excinfo: m.TestProperties.def_readonly_static = 2 - assert "can't set attribute" in str(excinfo.value) + assert NO_SETTER_MSG in str(excinfo.value) m.TestProperties.def_readwrite_static = 2 assert m.TestProperties.def_readwrite_static == 2 with pytest.raises(AttributeError) as excinfo: - dummy = m.TestProperties.def_writeonly_static # noqa: F841 unused var - assert "unreadable attribute" in str(excinfo.value) + dummy = m.TestProperties.def_writeonly_static # unused var + assert NO_GETTER_MSG in str(excinfo.value) m.TestProperties.def_writeonly_static = 3 assert m.TestProperties.def_readonly_static == 3 @@ -137,14 +147,14 @@ def test_static_properties(): assert m.TestProperties.def_property_readonly_static == 3 with pytest.raises(AttributeError) as excinfo: m.TestProperties.def_property_readonly_static = 99 - assert "can't set attribute" in str(excinfo.value) + assert NO_SETTER_MSG in str(excinfo.value) m.TestProperties.def_property_static = 4 assert m.TestProperties.def_property_static == 4 with pytest.raises(AttributeError) as excinfo: dummy = m.TestProperties.def_property_writeonly_static - assert "unreadable attribute" in str(excinfo.value) + assert NO_GETTER_MSG in str(excinfo.value) m.TestProperties.def_property_writeonly_static = 5 assert m.TestProperties.def_property_static == 5 @@ -162,7 +172,7 @@ def test_static_properties(): with pytest.raises(AttributeError) as excinfo: dummy = instance.def_property_writeonly_static # noqa: F841 unused var - assert "unreadable attribute" in str(excinfo.value) + assert NO_GETTER_MSG in str(excinfo.value) instance.def_property_writeonly_static = 4 assert instance.def_property_static == 4 @@ -182,7 +192,7 @@ def test_static_properties(): properties_override = m.TestPropertiesOverride() with pytest.raises(AttributeError) as excinfo: del properties_override.def_readonly - assert "can't delete attribute" in str(excinfo.value) + assert NO_DELETER_MSG in str(excinfo.value) def test_static_cls(): @@ -218,15 +228,15 @@ def test_metaclass_override(): def test_no_mixed_overloads(): - from pybind11_tests import debug_enabled + from pybind11_tests import detailed_error_messages_enabled with pytest.raises(RuntimeError) as excinfo: m.ExampleMandA.add_mixed_overloads1() assert str( excinfo.value ) == "overloading a method with both static and instance methods is not supported; " + ( - "compile in debug mode for more details" - if not debug_enabled + "#define PYBIND11_DETAILED_ERROR_MESSAGES or compile in debug mode for more details" + if not detailed_error_messages_enabled else "error while attempting to bind static method ExampleMandA.overload_mixed1" "(arg0: float) -> str" ) @@ -236,8 +246,8 @@ def test_no_mixed_overloads(): assert str( excinfo.value ) == "overloading a method with both static and instance methods is not supported; " + ( - "compile in debug mode for more details" - if not debug_enabled + "#define PYBIND11_DETAILED_ERROR_MESSAGES or compile in debug mode for more details" + if not detailed_error_messages_enabled else "error while attempting to bind instance method ExampleMandA.overload_mixed2" "(self: pybind11_tests.methods_and_attributes.ExampleMandA, arg0: int, arg1: int)" " -> str" @@ -346,16 +356,16 @@ def test_cyclic_gc(): def test_bad_arg_default(msg): - from pybind11_tests import debug_enabled + from pybind11_tests import detailed_error_messages_enabled with pytest.raises(RuntimeError) as excinfo: m.bad_arg_def_named() assert msg(excinfo.value) == ( "arg(): could not convert default argument 'a: UnregisteredType' in function " "'should_fail' into a Python object (type not registered yet?)" - if debug_enabled + if detailed_error_messages_enabled else "arg(): could not convert default argument into a Python object (type not registered " - "yet?). Compile in debug mode for more information." + "yet?). #define PYBIND11_DETAILED_ERROR_MESSAGES or compile in debug mode for more information." ) with pytest.raises(RuntimeError) as excinfo: @@ -363,9 +373,9 @@ def test_bad_arg_default(msg): assert msg(excinfo.value) == ( "arg(): could not convert default argument 'UnregisteredType' in function " "'should_fail' into a Python object (type not registered yet?)" - if debug_enabled + if detailed_error_messages_enabled else "arg(): could not convert default argument into a Python object (type not registered " - "yet?). Compile in debug mode for more information." + "yet?). #define PYBIND11_DETAILED_ERROR_MESSAGES or compile in debug mode for more information." ) @@ -431,6 +441,17 @@ def test_accepts_none(msg): assert "incompatible function arguments" in str(excinfo.value) +def test_casts_none(): + """#2778: implicit casting from None to object (not pointer)""" + a = m.NoneCastTester() + assert m.ok_obj_or_none(a) == -1 + a = m.NoneCastTester(4) + assert m.ok_obj_or_none(a) == 4 + a = m.NoneCastTester(None) + assert m.ok_obj_or_none(a) == -1 + assert m.ok_obj_or_none(None) == -1 + + def test_str_issue(msg): """#283: __str__ called on uninitialized instance when constructor arguments invalid""" @@ -484,24 +505,23 @@ def test_overload_ordering(): assert m.overload_order("string") == 1 assert m.overload_order(0) == 4 - # Different for Python 2 vs. 3 - uni_name = type(u"").__name__ - assert "1. overload_order(arg0: int) -> int" in m.overload_order.__doc__ - assert ( - "2. overload_order(arg0: {}) -> int".format(uni_name) - in m.overload_order.__doc__ - ) - assert ( - "3. overload_order(arg0: {}) -> int".format(uni_name) - in m.overload_order.__doc__ - ) + assert "2. overload_order(arg0: str) -> int" in m.overload_order.__doc__ + assert "3. overload_order(arg0: str) -> int" in m.overload_order.__doc__ assert "4. overload_order(arg0: int) -> int" in m.overload_order.__doc__ with pytest.raises(TypeError) as err: m.overload_order(1.1) assert "1. (arg0: int) -> int" in str(err.value) - assert "2. (arg0: {}) -> int".format(uni_name) in str(err.value) - assert "3. (arg0: {}) -> int".format(uni_name) in str(err.value) + assert "2. (arg0: str) -> int" in str(err.value) + assert "3. (arg0: str) -> int" in str(err.value) assert "4. (arg0: int) -> int" in str(err.value) + + +def test_rvalue_ref_param(): + r = m.RValueRefParam() + assert r.func1("123") == 3 + assert r.func2("1234") == 4 + assert r.func3("12345") == 5 + assert r.func4("123456") == 6 diff --git a/3rdparty/pybind11/tests/test_modules.cpp b/3rdparty/pybind11/tests/test_modules.cpp index 67387e80..18a7ec74 100644 --- a/3rdparty/pybind11/tests/test_modules.cpp +++ b/3rdparty/pybind11/tests/test_modules.cpp @@ -8,8 +8,8 @@ BSD-style license that can be found in the LICENSE file. */ -#include "pybind11_tests.h" #include "constructor_stats.h" +#include "pybind11_tests.h" TEST_SUBMODULE(modules, m) { // test_nested_modules @@ -20,24 +20,32 @@ TEST_SUBMODULE(modules, m) { // test_reference_internal class A { public: - A(int v) : v(v) { print_created(this, v); } + explicit A(int v) : v(v) { print_created(this, v); } ~A() { print_destroyed(this); } - A(const A&) { print_copy_created(this); } - A& operator=(const A ©) { print_copy_assigned(this); v = copy.v; return *this; } - std::string toString() { return "A[" + std::to_string(v) + "]"; } + A(const A &) { print_copy_created(this); } + A &operator=(const A ©) { + print_copy_assigned(this); + v = copy.v; + return *this; + } + std::string toString() const { return "A[" + std::to_string(v) + "]"; } + private: int v; }; - py::class_(m_sub, "A") - .def(py::init()) - .def("__repr__", &A::toString); + py::class_(m_sub, "A").def(py::init()).def("__repr__", &A::toString); class B { public: B() { print_default_created(this); } ~B() { print_destroyed(this); } - B(const B&) { print_copy_created(this); } - B& operator=(const B ©) { print_copy_assigned(this); a1 = copy.a1; a2 = copy.a2; return *this; } + B(const B &) { print_copy_created(this); } + B &operator=(const B ©) { + print_copy_assigned(this); + a1 = copy.a1; + a2 = copy.a2; + return *this; + } A &get_a1() { return a1; } A &get_a2() { return a2; } @@ -46,9 +54,16 @@ TEST_SUBMODULE(modules, m) { }; py::class_(m_sub, "B") .def(py::init<>()) - .def("get_a1", &B::get_a1, "Return the internal A 1", py::return_value_policy::reference_internal) - .def("get_a2", &B::get_a2, "Return the internal A 2", py::return_value_policy::reference_internal) - .def_readwrite("a1", &B::a1) // def_readonly uses an internal reference return policy by default + .def("get_a1", + &B::get_a1, + "Return the internal A 1", + py::return_value_policy::reference_internal) + .def("get_a2", + &B::get_a2, + "Return the internal A 2", + py::return_value_policy::reference_internal) + .def_readwrite("a1", &B::a1) // def_readonly uses an internal + // reference return policy by default .def_readwrite("a2", &B::a2); // This is intentionally "py::module" to verify it still can be used in place of "py::module_" @@ -57,13 +72,14 @@ TEST_SUBMODULE(modules, m) { // test_duplicate_registration // Registering two things with the same name m.def("duplicate_registration", []() { - class Dupe1 { }; - class Dupe2 { }; - class Dupe3 { }; - class DupeException { }; + class Dupe1 {}; + class Dupe2 {}; + class Dupe3 {}; + class DupeException {}; // Go ahead and leak, until we have a non-leaking py::module_ constructor - auto dm = py::module_::create_extension_module("dummy", nullptr, new py::module_::module_def); + auto dm + = py::module_::create_extension_module("dummy", nullptr, new py::module_::module_def); auto failures = py::list(); py::class_(dm, "Dupe1"); @@ -74,28 +90,36 @@ TEST_SUBMODULE(modules, m) { try { py::class_(dm, "Dupe1"); failures.append("Dupe1 class"); - } catch (std::runtime_error &) {} + } catch (std::runtime_error &) { + } try { dm.def("Dupe1", []() { return Dupe1(); }); failures.append("Dupe1 function"); - } catch (std::runtime_error &) {} + } catch (std::runtime_error &) { + } try { py::class_(dm, "dupe1_factory"); failures.append("dupe1_factory"); - } catch (std::runtime_error &) {} + } catch (std::runtime_error &) { + } try { py::exception(dm, "Dupe2"); failures.append("Dupe2"); - } catch (std::runtime_error &) {} + } catch (std::runtime_error &) { + } try { dm.def("DupeException", []() { return 30; }); failures.append("DupeException1"); - } catch (std::runtime_error &) {} + } catch (std::runtime_error &) { + } try { py::class_(dm, "DupeException"); failures.append("DupeException2"); - } catch (std::runtime_error &) {} + } catch (std::runtime_error &) { + } return failures; }); + + m.def("def_submodule", [](py::module_ m, const char *name) { return m.def_submodule(name); }); } diff --git a/3rdparty/pybind11/tests/test_modules.py b/3rdparty/pybind11/tests/test_modules.py index 5630ccf9..e11d68e7 100644 --- a/3rdparty/pybind11/tests/test_modules.py +++ b/3rdparty/pybind11/tests/test_modules.py @@ -1,7 +1,9 @@ -# -*- coding: utf-8 -*- +import pytest + +import env +from pybind11_tests import ConstructorStats from pybind11_tests import modules as m from pybind11_tests.modules import subsubmodule as ms -from pybind11_tests import ConstructorStats def test_nested_modules(): @@ -54,18 +56,20 @@ def test_reference_internal(): def test_importing(): - from pybind11_tests.modules import OD from collections import OrderedDict + from pybind11_tests.modules import OD + assert OD is OrderedDict assert str(OD([(1, "a"), (2, "b")])) == "OrderedDict([(1, 'a'), (2, 'b')])" def test_pydoc(): """Pydoc needs to be able to provide help() for everything inside a pybind11 module""" - import pybind11_tests import pydoc + import pybind11_tests + assert pybind11_tests.__name__ == "pybind11_tests" assert pybind11_tests.__doc__ == "pybind11 test module" assert pydoc.text.docmodule(pybind11_tests) @@ -75,3 +79,43 @@ def test_duplicate_registration(): """Registering two things with the same name""" assert m.duplicate_registration() == [] + + +def test_builtin_key_type(): + """Test that all the keys in the builtin modules have type str. + + Previous versions of pybind11 would add a unicode key in python 2. + """ + if hasattr(__builtins__, "keys"): + keys = __builtins__.keys() + else: # this is to make pypy happy since builtins is different there. + keys = __builtins__.__dict__.keys() + + assert {type(k) for k in keys} == {str} + + +@pytest.mark.xfail("env.PYPY", reason="PyModule_GetName()") +def test_def_submodule_failures(): + sm = m.def_submodule(m, b"ScratchSubModuleName") # Using bytes to show it works. + assert sm.__name__ == m.__name__ + "." + "ScratchSubModuleName" + malformed_utf8 = b"\x80" + if env.PYPY: + # It is not worth the effort finding a trigger for a failure when running with PyPy. + pytest.skip("Sufficiently exercised on platforms other than PyPy.") + else: + # Meant to trigger PyModule_GetName() failure: + sm_name_orig = sm.__name__ + sm.__name__ = malformed_utf8 + try: + with pytest.raises(Exception): + # Seen with Python 3.9: SystemError: nameless module + # But we do not want to exercise the internals of PyModule_GetName(), which could + # change in future versions of Python, but a bad __name__ is very likely to cause + # some kind of failure indefinitely. + m.def_submodule(sm, b"SubSubModuleName") + finally: + # Clean up to ensure nothing gets upset by a module with an invalid __name__. + sm.__name__ = sm_name_orig # Purely precautionary. + # Meant to trigger PyImport_AddModule() failure: + with pytest.raises(UnicodeDecodeError): + m.def_submodule(sm, malformed_utf8) diff --git a/3rdparty/pybind11/tests/test_multiple_inheritance.cpp b/3rdparty/pybind11/tests/test_multiple_inheritance.cpp index e6720080..5916ae90 100644 --- a/3rdparty/pybind11/tests/test_multiple_inheritance.cpp +++ b/3rdparty/pybind11/tests/test_multiple_inheritance.cpp @@ -8,13 +8,16 @@ BSD-style license that can be found in the LICENSE file. */ -#include "pybind11_tests.h" #include "constructor_stats.h" +#include "pybind11_tests.h" + +namespace { // Many bases for testing that multiple inheritance from many classes (i.e. requiring extra // space for holder constructed flags) works. -template struct BaseN { - BaseN(int i) : i(i) { } +template +struct BaseN { + explicit BaseN(int i) : i(i) {} int i; }; @@ -43,103 +46,139 @@ int WithStatic2::static_value2 = 2; int VanillaStaticMix1::static_value = 12; int VanillaStaticMix2::static_value = 12; +// test_multiple_inheritance_virtbase +struct Base1a { + explicit Base1a(int i) : i(i) {} + int foo() const { return i; } + int i; +}; +struct Base2a { + explicit Base2a(int i) : i(i) {} + int bar() const { return i; } + int i; +}; +struct Base12a : Base1a, Base2a { + Base12a(int i, int j) : Base1a(i), Base2a(j) {} +}; + +// test_mi_unaligned_base +// test_mi_base_return +struct I801B1 { + int a = 1; + I801B1() = default; + I801B1(const I801B1 &) = default; + virtual ~I801B1() = default; +}; +struct I801B2 { + int b = 2; + I801B2() = default; + I801B2(const I801B2 &) = default; + virtual ~I801B2() = default; +}; +struct I801C : I801B1, I801B2 {}; +struct I801D : I801C {}; // Indirect MI + +} // namespace + TEST_SUBMODULE(multiple_inheritance, m) { + // Please do not interleave `struct` and `class` definitions with bindings code, + // but implement `struct`s and `class`es in the anonymous namespace above. + // This helps keeping the smart_holder branch in sync with master. // test_multiple_inheritance_mix1 // test_multiple_inheritance_mix2 struct Base1 { - Base1(int i) : i(i) { } - int foo() { return i; } + explicit Base1(int i) : i(i) {} + int foo() const { return i; } int i; }; py::class_ b1(m, "Base1"); - b1.def(py::init()) - .def("foo", &Base1::foo); + b1.def(py::init()).def("foo", &Base1::foo); struct Base2 { - Base2(int i) : i(i) { } - int bar() { return i; } + explicit Base2(int i) : i(i) {} + int bar() const { return i; } int i; }; py::class_ b2(m, "Base2"); - b2.def(py::init()) - .def("bar", &Base2::bar); - + b2.def(py::init()).def("bar", &Base2::bar); // test_multiple_inheritance_cpp struct Base12 : Base1, Base2 { - Base12(int i, int j) : Base1(i), Base2(j) { } + Base12(int i, int j) : Base1(i), Base2(j) {} }; struct MIType : Base12 { - MIType(int i, int j) : Base12(i, j) { } + MIType(int i, int j) : Base12(i, j) {} }; py::class_(m, "Base12"); - py::class_(m, "MIType") - .def(py::init()); - + py::class_(m, "MIType").def(py::init()); // test_multiple_inheritance_python_many_bases - #define PYBIND11_BASEN(N) py::class_>(m, "BaseN" #N).def(py::init()).def("f" #N, [](BaseN &b) { return b.i + N; }) - PYBIND11_BASEN( 1); PYBIND11_BASEN( 2); PYBIND11_BASEN( 3); PYBIND11_BASEN( 4); - PYBIND11_BASEN( 5); PYBIND11_BASEN( 6); PYBIND11_BASEN( 7); PYBIND11_BASEN( 8); - PYBIND11_BASEN( 9); PYBIND11_BASEN(10); PYBIND11_BASEN(11); PYBIND11_BASEN(12); - PYBIND11_BASEN(13); PYBIND11_BASEN(14); PYBIND11_BASEN(15); PYBIND11_BASEN(16); +#define PYBIND11_BASEN(N) \ + py::class_>(m, "BaseN" #N).def(py::init()).def("f" #N, [](BaseN &b) { \ + return b.i + (N); \ + }) + PYBIND11_BASEN(1); + PYBIND11_BASEN(2); + PYBIND11_BASEN(3); + PYBIND11_BASEN(4); + PYBIND11_BASEN(5); + PYBIND11_BASEN(6); + PYBIND11_BASEN(7); + PYBIND11_BASEN(8); + PYBIND11_BASEN(9); + PYBIND11_BASEN(10); + PYBIND11_BASEN(11); + PYBIND11_BASEN(12); + PYBIND11_BASEN(13); + PYBIND11_BASEN(14); + PYBIND11_BASEN(15); + PYBIND11_BASEN(16); PYBIND11_BASEN(17); // Uncommenting this should result in a compile time failure (MI can only be specified via - // template parameters because pybind has to know the types involved; see discussion in #742 for - // details). -// struct Base12v2 : Base1, Base2 { -// Base12v2(int i, int j) : Base1(i), Base2(j) { } -// }; -// py::class_(m, "Base12v2", b1, b2) -// .def(py::init()); - + // template parameters because pybind has to know the types involved; see discussion in #742 + // for details). + // struct Base12v2 : Base1, Base2 { + // Base12v2(int i, int j) : Base1(i), Base2(j) { } + // }; + // py::class_(m, "Base12v2", b1, b2) + // .def(py::init()); // test_multiple_inheritance_virtbase // Test the case where not all base classes are specified, and where pybind11 requires the // py::multiple_inheritance flag to perform proper casting between types. - struct Base1a { - Base1a(int i) : i(i) { } - int foo() { return i; } - int i; - }; py::class_>(m, "Base1a") .def(py::init()) .def("foo", &Base1a::foo); - struct Base2a { - Base2a(int i) : i(i) { } - int bar() { return i; } - int i; - }; py::class_>(m, "Base2a") .def(py::init()) .def("bar", &Base2a::bar); - struct Base12a : Base1a, Base2a { - Base12a(int i, int j) : Base1a(i), Base2a(j) { } - }; - py::class_>(m, "Base12a", py::multiple_inheritance()) + py::class_>( + m, "Base12a", py::multiple_inheritance()) .def(py::init()); m.def("bar_base2a", [](Base2a *b) { return b->bar(); }); - m.def("bar_base2a_sharedptr", [](std::shared_ptr b) { return b->bar(); }); + m.def("bar_base2a_sharedptr", [](const std::shared_ptr &b) { return b->bar(); }); // test_mi_unaligned_base // test_mi_base_return // Issue #801: invalid casting to derived type with MI bases - struct I801B1 { int a = 1; I801B1() = default; I801B1(const I801B1 &) = default; virtual ~I801B1() = default; }; - struct I801B2 { int b = 2; I801B2() = default; I801B2(const I801B2 &) = default; virtual ~I801B2() = default; }; - struct I801C : I801B1, I801B2 {}; - struct I801D : I801C {}; // Indirect MI // Unregistered classes: - struct I801B3 { int c = 3; virtual ~I801B3() = default; }; + struct I801B3 { + int c = 3; + virtual ~I801B3() = default; + }; struct I801E : I801B3, I801D {}; - py::class_>(m, "I801B1").def(py::init<>()).def_readonly("a", &I801B1::a); - py::class_>(m, "I801B2").def(py::init<>()).def_readonly("b", &I801B2::b); + py::class_>(m, "I801B1") + .def(py::init<>()) + .def_readonly("a", &I801B1::a); + py::class_>(m, "I801B2") + .def(py::init<>()) + .def_readonly("b", &I801B2::b); py::class_>(m, "I801C").def(py::init<>()); py::class_>(m, "I801D").def(py::init<>()); @@ -164,11 +203,8 @@ TEST_SUBMODULE(multiple_inheritance, m) { m.def("i801e_c", []() -> I801C * { return new I801E(); }); m.def("i801e_b2", []() -> I801B2 * { return new I801E(); }); - // test_mi_static_properties - py::class_(m, "Vanilla") - .def(py::init<>()) - .def("vanilla", &Vanilla::vanilla); + py::class_(m, "Vanilla").def(py::init<>()).def("vanilla", &Vanilla::vanilla); py::class_(m, "WithStatic1") .def(py::init<>()) @@ -180,22 +216,19 @@ TEST_SUBMODULE(multiple_inheritance, m) { .def_static("static_func2", &WithStatic2::static_func2) .def_readwrite_static("static_value2", &WithStatic2::static_value2); - py::class_( - m, "VanillaStaticMix1") + py::class_(m, "VanillaStaticMix1") .def(py::init<>()) .def_static("static_func", &VanillaStaticMix1::static_func) .def_readwrite_static("static_value", &VanillaStaticMix1::static_value); - py::class_( - m, "VanillaStaticMix2") + py::class_(m, "VanillaStaticMix2") .def(py::init<>()) .def_static("static_func", &VanillaStaticMix2::static_func) .def_readwrite_static("static_value", &VanillaStaticMix2::static_value); - - struct WithDict { }; - struct VanillaDictMix1 : Vanilla, WithDict { }; - struct VanillaDictMix2 : WithDict, Vanilla { }; + struct WithDict {}; + struct VanillaDictMix1 : Vanilla, WithDict {}; + struct VanillaDictMix2 : WithDict, Vanilla {}; py::class_(m, "WithDict", py::dynamic_attr()).def(py::init<>()); py::class_(m, "VanillaDictMix1").def(py::init<>()); py::class_(m, "VanillaDictMix2").def(py::init<>()); @@ -203,16 +236,106 @@ TEST_SUBMODULE(multiple_inheritance, m) { // test_diamond_inheritance // Issue #959: segfault when constructing diamond inheritance instance // All of these have int members so that there will be various unequal pointers involved. - struct B { int b; B() = default; B(const B&) = default; virtual ~B() = default; }; - struct C0 : public virtual B { int c0; }; - struct C1 : public virtual B { int c1; }; - struct D : public C0, public C1 { int d; }; - py::class_(m, "B") - .def("b", [](B *self) { return self; }); - py::class_(m, "C0") - .def("c0", [](C0 *self) { return self; }); - py::class_(m, "C1") - .def("c1", [](C1 *self) { return self; }); - py::class_(m, "D") - .def(py::init<>()); + struct B { + int b; + B() = default; + B(const B &) = default; + virtual ~B() = default; + }; + struct C0 : public virtual B { + int c0; + }; + struct C1 : public virtual B { + int c1; + }; + struct D : public C0, public C1 { + int d; + }; + py::class_(m, "B").def("b", [](B *self) { return self; }); + py::class_(m, "C0").def("c0", [](C0 *self) { return self; }); + py::class_(m, "C1").def("c1", [](C1 *self) { return self; }); + py::class_(m, "D").def(py::init<>()); + + // test_pr3635_diamond_* + // - functions are get_{base}_{var}, return {var} + struct MVB { + MVB() = default; + MVB(const MVB &) = default; + virtual ~MVB() = default; + + int b = 1; + int get_b_b() const { return b; } + }; + struct MVC : virtual MVB { + int c = 2; + int get_c_b() const { return b; } + int get_c_c() const { return c; } + }; + struct MVD0 : virtual MVC { + int d0 = 3; + int get_d0_b() const { return b; } + int get_d0_c() const { return c; } + int get_d0_d0() const { return d0; } + }; + struct MVD1 : virtual MVC { + int d1 = 4; + int get_d1_b() const { return b; } + int get_d1_c() const { return c; } + int get_d1_d1() const { return d1; } + }; + struct MVE : virtual MVD0, virtual MVD1 { + int e = 5; + int get_e_b() const { return b; } + int get_e_c() const { return c; } + int get_e_d0() const { return d0; } + int get_e_d1() const { return d1; } + int get_e_e() const { return e; } + }; + struct MVF : virtual MVE { + int f = 6; + int get_f_b() const { return b; } + int get_f_c() const { return c; } + int get_f_d0() const { return d0; } + int get_f_d1() const { return d1; } + int get_f_e() const { return e; } + int get_f_f() const { return f; } + }; + py::class_(m, "MVB") + .def(py::init<>()) + .def("get_b_b", &MVB::get_b_b) + .def_readwrite("b", &MVB::b); + py::class_(m, "MVC") + .def(py::init<>()) + .def("get_c_b", &MVC::get_c_b) + .def("get_c_c", &MVC::get_c_c) + .def_readwrite("c", &MVC::c); + py::class_(m, "MVD0") + .def(py::init<>()) + .def("get_d0_b", &MVD0::get_d0_b) + .def("get_d0_c", &MVD0::get_d0_c) + .def("get_d0_d0", &MVD0::get_d0_d0) + .def_readwrite("d0", &MVD0::d0); + py::class_(m, "MVD1") + .def(py::init<>()) + .def("get_d1_b", &MVD1::get_d1_b) + .def("get_d1_c", &MVD1::get_d1_c) + .def("get_d1_d1", &MVD1::get_d1_d1) + .def_readwrite("d1", &MVD1::d1); + py::class_(m, "MVE") + .def(py::init<>()) + .def("get_e_b", &MVE::get_e_b) + .def("get_e_c", &MVE::get_e_c) + .def("get_e_d0", &MVE::get_e_d0) + .def("get_e_d1", &MVE::get_e_d1) + .def("get_e_e", &MVE::get_e_e) + .def_readwrite("e", &MVE::e); + py::class_(m, "MVF") + .def(py::init<>()) + .def("get_f_b", &MVF::get_f_b) + .def("get_f_c", &MVF::get_f_c) + .def("get_f_d0", &MVF::get_f_d0) + .def("get_f_d1", &MVF::get_f_d1) + .def("get_f_e", &MVF::get_f_e) + .def("get_f_f", &MVF::get_f_f) + .def_readwrite("f", &MVF::f); } diff --git a/3rdparty/pybind11/tests/test_multiple_inheritance.py b/3rdparty/pybind11/tests/test_multiple_inheritance.py index e6a7f976..3a1d88d7 100644 --- a/3rdparty/pybind11/tests/test_multiple_inheritance.py +++ b/3rdparty/pybind11/tests/test_multiple_inheritance.py @@ -1,8 +1,6 @@ -# -*- coding: utf-8 -*- import pytest import env # noqa: F401 - from pybind11_tests import ConstructorStats from pybind11_tests import multiple_inheritance as m @@ -14,8 +12,7 @@ def test_multiple_inheritance_cpp(): assert mt.bar() == 4 -@pytest.mark.skipif("env.PYPY and env.PY2") -@pytest.mark.xfail("env.PYPY and not env.PY2") +@pytest.mark.xfail("env.PYPY") def test_multiple_inheritance_mix1(): class Base1: def __init__(self, i): @@ -54,15 +51,14 @@ def test_multiple_inheritance_mix2(): assert mt.bar() == 4 -@pytest.mark.skipif("env.PYPY and env.PY2") -@pytest.mark.xfail("env.PYPY and not env.PY2") +@pytest.mark.xfail("env.PYPY") def test_multiple_inheritance_python(): class MI1(m.Base1, m.Base2): def __init__(self, i, j): m.Base1.__init__(self, i) m.Base2.__init__(self, j) - class B1(object): + class B1: def v(self): return 1 @@ -97,7 +93,7 @@ def test_multiple_inheritance_python(): def v(self): return 2 - class B3(object): + class B3: def v(self): return 3 @@ -359,3 +355,139 @@ def test_diamond_inheritance(): assert d is d.c0().b() assert d is d.c1().b() assert d is d.c0().c1().b().c0().b() + + +def test_pr3635_diamond_b(): + o = m.MVB() + assert o.b == 1 + + assert o.get_b_b() == 1 + + +def test_pr3635_diamond_c(): + o = m.MVC() + assert o.b == 1 + assert o.c == 2 + + assert o.get_b_b() == 1 + assert o.get_c_b() == 1 + + assert o.get_c_c() == 2 + + +def test_pr3635_diamond_d0(): + o = m.MVD0() + assert o.b == 1 + assert o.c == 2 + assert o.d0 == 3 + + assert o.get_b_b() == 1 + assert o.get_c_b() == 1 + assert o.get_d0_b() == 1 + + assert o.get_c_c() == 2 + assert o.get_d0_c() == 2 + + assert o.get_d0_d0() == 3 + + +def test_pr3635_diamond_d1(): + o = m.MVD1() + assert o.b == 1 + assert o.c == 2 + assert o.d1 == 4 + + assert o.get_b_b() == 1 + assert o.get_c_b() == 1 + assert o.get_d1_b() == 1 + + assert o.get_c_c() == 2 + assert o.get_d1_c() == 2 + + assert o.get_d1_d1() == 4 + + +def test_pr3635_diamond_e(): + o = m.MVE() + assert o.b == 1 + assert o.c == 2 + assert o.d0 == 3 + assert o.d1 == 4 + assert o.e == 5 + + assert o.get_b_b() == 1 + assert o.get_c_b() == 1 + assert o.get_d0_b() == 1 + assert o.get_d1_b() == 1 + assert o.get_e_b() == 1 + + assert o.get_c_c() == 2 + assert o.get_d0_c() == 2 + assert o.get_d1_c() == 2 + assert o.get_e_c() == 2 + + assert o.get_d0_d0() == 3 + assert o.get_e_d0() == 3 + + assert o.get_d1_d1() == 4 + assert o.get_e_d1() == 4 + + assert o.get_e_e() == 5 + + +def test_pr3635_diamond_f(): + o = m.MVF() + assert o.b == 1 + assert o.c == 2 + assert o.d0 == 3 + assert o.d1 == 4 + assert o.e == 5 + assert o.f == 6 + + assert o.get_b_b() == 1 + assert o.get_c_b() == 1 + assert o.get_d0_b() == 1 + assert o.get_d1_b() == 1 + assert o.get_e_b() == 1 + assert o.get_f_b() == 1 + + assert o.get_c_c() == 2 + assert o.get_d0_c() == 2 + assert o.get_d1_c() == 2 + assert o.get_e_c() == 2 + assert o.get_f_c() == 2 + + assert o.get_d0_d0() == 3 + assert o.get_e_d0() == 3 + assert o.get_f_d0() == 3 + + assert o.get_d1_d1() == 4 + assert o.get_e_d1() == 4 + assert o.get_f_d1() == 4 + + assert o.get_e_e() == 5 + assert o.get_f_e() == 5 + + assert o.get_f_f() == 6 + + +def test_python_inherit_from_mi(): + """Tests extending a Python class from a single inheritor of a MI class""" + + class PyMVF(m.MVF): + g = 7 + + def get_g_g(self): + return self.g + + o = PyMVF() + + assert o.b == 1 + assert o.c == 2 + assert o.d0 == 3 + assert o.d1 == 4 + assert o.e == 5 + assert o.f == 6 + assert o.g == 7 + + assert o.get_g_g() == 7 diff --git a/3rdparty/pybind11/tests/test_numpy_array.cpp b/3rdparty/pybind11/tests/test_numpy_array.cpp index a84de77f..69ddbe1e 100644 --- a/3rdparty/pybind11/tests/test_numpy_array.cpp +++ b/3rdparty/pybind11/tests/test_numpy_array.cpp @@ -7,12 +7,13 @@ BSD-style license that can be found in the LICENSE file. */ -#include "pybind11_tests.h" - #include #include +#include "pybind11_tests.h" + #include +#include // Size / dtype checks. struct DtypeCheck { @@ -21,7 +22,7 @@ struct DtypeCheck { }; template -DtypeCheck get_dtype_check(const char* name) { +DtypeCheck get_dtype_check(const char *name) { py::module_ np = py::module_::import("numpy"); DtypeCheck check{}; check.numpy = np.attr("dtype")(np.attr(name)); @@ -30,17 +31,15 @@ DtypeCheck get_dtype_check(const char* name) { } std::vector get_concrete_dtype_checks() { - return { - // Normalization - get_dtype_check("int8"), - get_dtype_check("uint8"), - get_dtype_check("int16"), - get_dtype_check("uint16"), - get_dtype_check("int32"), - get_dtype_check("uint32"), - get_dtype_check("int64"), - get_dtype_check("uint64") - }; + return {// Normalization + get_dtype_check("int8"), + get_dtype_check("uint8"), + get_dtype_check("int16"), + get_dtype_check("uint16"), + get_dtype_check("int32"), + get_dtype_check("uint32"), + get_dtype_check("int64"), + get_dtype_check("uint64")}; } struct DtypeSizeCheck { @@ -79,43 +78,71 @@ using arr = py::array; using arr_t = py::array_t; static_assert(std::is_same::value, ""); -template arr data(const arr& a, Ix... index) { +template +arr data(const arr &a, Ix... index) { return arr(a.nbytes() - a.offset_at(index...), (const uint8_t *) a.data(index...)); } -template arr data_t(const arr_t& a, Ix... index) { +template +arr data_t(const arr_t &a, Ix... index) { return arr(a.size() - a.index_at(index...), a.data(index...)); } -template arr& mutate_data(arr& a, Ix... index) { - auto ptr = (uint8_t *) a.mutable_data(index...); - for (py::ssize_t i = 0; i < a.nbytes() - a.offset_at(index...); i++) +template +arr &mutate_data(arr &a, Ix... index) { + auto *ptr = (uint8_t *) a.mutable_data(index...); + for (py::ssize_t i = 0; i < a.nbytes() - a.offset_at(index...); i++) { ptr[i] = (uint8_t) (ptr[i] * 2); + } return a; } -template arr_t& mutate_data_t(arr_t& a, Ix... index) { +template +arr_t &mutate_data_t(arr_t &a, Ix... index) { auto ptr = a.mutable_data(index...); - for (py::ssize_t i = 0; i < a.size() - a.index_at(index...); i++) + for (py::ssize_t i = 0; i < a.size() - a.index_at(index...); i++) { ptr[i]++; + } + return a; +} + +template +py::ssize_t index_at(const arr &a, Ix... idx) { + return a.index_at(idx...); +} +template +py::ssize_t index_at_t(const arr_t &a, Ix... idx) { + return a.index_at(idx...); +} +template +py::ssize_t offset_at(const arr &a, Ix... idx) { + return a.offset_at(idx...); +} +template +py::ssize_t offset_at_t(const arr_t &a, Ix... idx) { + return a.offset_at(idx...); +} +template +py::ssize_t at_t(const arr_t &a, Ix... idx) { + return a.at(idx...); +} +template +arr_t &mutate_at_t(arr_t &a, Ix... idx) { + a.mutable_at(idx...)++; return a; } -template py::ssize_t index_at(const arr& a, Ix... idx) { return a.index_at(idx...); } -template py::ssize_t index_at_t(const arr_t& a, Ix... idx) { return a.index_at(idx...); } -template py::ssize_t offset_at(const arr& a, Ix... idx) { return a.offset_at(idx...); } -template py::ssize_t offset_at_t(const arr_t& a, Ix... idx) { return a.offset_at(idx...); } -template py::ssize_t at_t(const arr_t& a, Ix... idx) { return a.at(idx...); } -template arr_t& mutate_at_t(arr_t& a, Ix... idx) { a.mutable_at(idx...)++; return a; } - -#define def_index_fn(name, type) \ - sm.def(#name, [](type a) { return name(a); }); \ - sm.def(#name, [](type a, int i) { return name(a, i); }); \ - sm.def(#name, [](type a, int i, int j) { return name(a, i, j); }); \ +#define def_index_fn(name, type) \ + sm.def(#name, [](type a) { return name(a); }); \ + sm.def(#name, [](type a, int i) { return name(a, i); }); \ + sm.def(#name, [](type a, int i, int j) { return name(a, i, j); }); \ sm.def(#name, [](type a, int i, int j, int k) { return name(a, i, j, k); }); -template py::handle auxiliaries(T &&r, T2 &&r2) { - if (r.ndim() != 2) throw std::domain_error("error: ndim != 2"); +template +py::handle auxiliaries(T &&r, T2 &&r2) { + if (r.ndim() != 2) { + throw std::domain_error("error: ndim != 2"); + } py::list l; l.append(*r.data(0, 0)); l.append(*r2.mutable_data(0, 0)); @@ -133,16 +160,18 @@ template py::handle auxiliaries(T &&r, T2 &&r2) { static int data_i = 42; TEST_SUBMODULE(numpy_array, sm) { - try { py::module_::import("numpy"); } - catch (...) { return; } + try { + py::module_::import("numpy"); + } catch (const py::error_already_set &) { + return; + } // test_dtypes py::class_(sm, "DtypeCheck") .def_readonly("numpy", &DtypeCheck::numpy) .def_readonly("pybind11", &DtypeCheck::pybind11) - .def("__repr__", [](const DtypeCheck& self) { - return py::str("").format( - self.numpy, self.pybind11); + .def("__repr__", [](const DtypeCheck &self) { + return py::str("").format(self.numpy, self.pybind11); }); sm.def("get_concrete_dtype_checks", &get_concrete_dtype_checks); @@ -150,41 +179,41 @@ TEST_SUBMODULE(numpy_array, sm) { .def_readonly("name", &DtypeSizeCheck::name) .def_readonly("size_cpp", &DtypeSizeCheck::size_cpp) .def_readonly("size_numpy", &DtypeSizeCheck::size_numpy) - .def("__repr__", [](const DtypeSizeCheck& self) { - return py::str("").format( - self.name, self.size_cpp, self.size_numpy, self.dtype); + .def("__repr__", [](const DtypeSizeCheck &self) { + return py::str("") + .format(self.name, self.size_cpp, self.size_numpy, self.dtype); }); sm.def("get_platform_dtype_size_checks", &get_platform_dtype_size_checks); // test_array_attributes - sm.def("ndim", [](const arr& a) { return a.ndim(); }); - sm.def("shape", [](const arr& a) { return arr(a.ndim(), a.shape()); }); - sm.def("shape", [](const arr& a, py::ssize_t dim) { return a.shape(dim); }); - sm.def("strides", [](const arr& a) { return arr(a.ndim(), a.strides()); }); - sm.def("strides", [](const arr& a, py::ssize_t dim) { return a.strides(dim); }); - sm.def("writeable", [](const arr& a) { return a.writeable(); }); - sm.def("size", [](const arr& a) { return a.size(); }); - sm.def("itemsize", [](const arr& a) { return a.itemsize(); }); - sm.def("nbytes", [](const arr& a) { return a.nbytes(); }); - sm.def("owndata", [](const arr& a) { return a.owndata(); }); + sm.def("ndim", [](const arr &a) { return a.ndim(); }); + sm.def("shape", [](const arr &a) { return arr(a.ndim(), a.shape()); }); + sm.def("shape", [](const arr &a, py::ssize_t dim) { return a.shape(dim); }); + sm.def("strides", [](const arr &a) { return arr(a.ndim(), a.strides()); }); + sm.def("strides", [](const arr &a, py::ssize_t dim) { return a.strides(dim); }); + sm.def("writeable", [](const arr &a) { return a.writeable(); }); + sm.def("size", [](const arr &a) { return a.size(); }); + sm.def("itemsize", [](const arr &a) { return a.itemsize(); }); + sm.def("nbytes", [](const arr &a) { return a.nbytes(); }); + sm.def("owndata", [](const arr &a) { return a.owndata(); }); // test_index_offset - def_index_fn(index_at, const arr&); - def_index_fn(index_at_t, const arr_t&); - def_index_fn(offset_at, const arr&); - def_index_fn(offset_at_t, const arr_t&); + def_index_fn(index_at, const arr &); + def_index_fn(index_at_t, const arr_t &); + def_index_fn(offset_at, const arr &); + def_index_fn(offset_at_t, const arr_t &); // test_data - def_index_fn(data, const arr&); - def_index_fn(data_t, const arr_t&); + def_index_fn(data, const arr &); + def_index_fn(data_t, const arr_t &); // test_mutate_data, test_mutate_readonly - def_index_fn(mutate_data, arr&); - def_index_fn(mutate_data_t, arr_t&); - def_index_fn(at_t, const arr_t&); - def_index_fn(mutate_at_t, arr_t&); + def_index_fn(mutate_data, arr &); + def_index_fn(mutate_data_t, arr_t &); + def_index_fn(at_t, const arr_t &); + def_index_fn(mutate_at_t, arr_t &); // test_make_c_f_array - sm.def("make_f_array", [] { return py::array_t({ 2, 2 }, { 4, 8 }); }); - sm.def("make_c_array", [] { return py::array_t({ 2, 2 }, { 8, 4 }); }); + sm.def("make_f_array", [] { return py::array_t({2, 2}, {4, 8}); }); + sm.def("make_c_array", [] { return py::array_t({2, 2}, {8, 4}); }); // test_empty_shaped_array sm.def("make_empty_shaped_array", [] { return py::array(py::dtype("f"), {}, {}); }); @@ -192,19 +221,17 @@ TEST_SUBMODULE(numpy_array, sm) { sm.def("scalar_int", []() { return py::array(py::dtype("i"), {}, {}, &data_i); }); // test_wrap - sm.def("wrap", [](py::array a) { - return py::array( - a.dtype(), - {a.shape(), a.shape() + a.ndim()}, - {a.strides(), a.strides() + a.ndim()}, - a.data(), - a - ); + sm.def("wrap", [](const py::array &a) { + return py::array(a.dtype(), + {a.shape(), a.shape() + a.ndim()}, + {a.strides(), a.strides() + a.ndim()}, + a.data(), + a); }); // test_numpy_view struct ArrayClass { - int data[2] = { 1, 2 }; + int data[2] = {1, 2}; ArrayClass() { py::print("ArrayClass()"); } ~ArrayClass() { py::print("~ArrayClass()"); } }; @@ -212,103 +239,121 @@ TEST_SUBMODULE(numpy_array, sm) { .def(py::init<>()) .def("numpy_view", [](py::object &obj) { py::print("ArrayClass::numpy_view()"); - auto &a = obj.cast(); + auto &a = obj.cast(); return py::array_t({2}, {4}, a.data, obj); - } - ); + }); // test_cast_numpy_int64_to_uint64 - sm.def("function_taking_uint64", [](uint64_t) { }); + sm.def("function_taking_uint64", [](uint64_t) {}); // test_isinstance sm.def("isinstance_untyped", [](py::object yes, py::object no) { - return py::isinstance(yes) && !py::isinstance(no); + return py::isinstance(std::move(yes)) + && !py::isinstance(std::move(no)); }); - sm.def("isinstance_typed", [](py::object o) { + sm.def("isinstance_typed", [](const py::object &o) { return py::isinstance>(o) && !py::isinstance>(o); }); // test_constructors sm.def("default_constructors", []() { - return py::dict( - "array"_a=py::array(), - "array_t"_a=py::array_t(), - "array_t"_a=py::array_t() - ); + return py::dict("array"_a = py::array(), + "array_t"_a = py::array_t(), + "array_t"_a = py::array_t()); }); - sm.def("converting_constructors", [](py::object o) { - return py::dict( - "array"_a=py::array(o), - "array_t"_a=py::array_t(o), - "array_t"_a=py::array_t(o) - ); + sm.def("converting_constructors", [](const py::object &o) { + return py::dict("array"_a = py::array(o), + "array_t"_a = py::array_t(o), + "array_t"_a = py::array_t(o)); }); // test_overload_resolution - sm.def("overloaded", [](py::array_t) { return "double"; }); - sm.def("overloaded", [](py::array_t) { return "float"; }); - sm.def("overloaded", [](py::array_t) { return "int"; }); - sm.def("overloaded", [](py::array_t) { return "unsigned short"; }); - sm.def("overloaded", [](py::array_t) { return "long long"; }); - sm.def("overloaded", [](py::array_t>) { return "double complex"; }); - sm.def("overloaded", [](py::array_t>) { return "float complex"; }); - - sm.def("overloaded2", [](py::array_t>) { return "double complex"; }); - sm.def("overloaded2", [](py::array_t) { return "double"; }); - sm.def("overloaded2", [](py::array_t>) { return "float complex"; }); - sm.def("overloaded2", [](py::array_t) { return "float"; }); + sm.def("overloaded", [](const py::array_t &) { return "double"; }); + sm.def("overloaded", [](const py::array_t &) { return "float"; }); + sm.def("overloaded", [](const py::array_t &) { return "int"; }); + sm.def("overloaded", [](const py::array_t &) { return "unsigned short"; }); + sm.def("overloaded", [](const py::array_t &) { return "long long"; }); + sm.def("overloaded", + [](const py::array_t> &) { return "double complex"; }); + sm.def("overloaded", [](const py::array_t> &) { return "float complex"; }); + + sm.def("overloaded2", + [](const py::array_t> &) { return "double complex"; }); + sm.def("overloaded2", [](const py::array_t &) { return "double"; }); + sm.def("overloaded2", + [](const py::array_t> &) { return "float complex"; }); + sm.def("overloaded2", [](const py::array_t &) { return "float"; }); + + // [workaround(intel)] ICC 20/21 breaks with py::arg().stuff, using py::arg{}.stuff works. // Only accept the exact types: - sm.def("overloaded3", [](py::array_t) { return "int"; }, py::arg().noconvert()); - sm.def("overloaded3", [](py::array_t) { return "double"; }, py::arg().noconvert()); + sm.def( + "overloaded3", [](const py::array_t &) { return "int"; }, py::arg{}.noconvert()); + sm.def( + "overloaded3", + [](const py::array_t &) { return "double"; }, + py::arg{}.noconvert()); // Make sure we don't do unsafe coercion (e.g. float to int) when not using forcecast, but // rather that float gets converted via the safe (conversion to double) overload: - sm.def("overloaded4", [](py::array_t) { return "long long"; }); - sm.def("overloaded4", [](py::array_t) { return "double"; }); + sm.def("overloaded4", [](const py::array_t &) { return "long long"; }); + sm.def("overloaded4", [](const py::array_t &) { return "double"; }); // But we do allow conversion to int if forcecast is enabled (but only if no overload matches // without conversion) - sm.def("overloaded5", [](py::array_t) { return "unsigned int"; }); - sm.def("overloaded5", [](py::array_t) { return "double"; }); + sm.def("overloaded5", [](const py::array_t &) { return "unsigned int"; }); + sm.def("overloaded5", [](const py::array_t &) { return "double"; }); // test_greedy_string_overload // Issue 685: ndarray shouldn't go to std::string overload - sm.def("issue685", [](std::string) { return "string"; }); - sm.def("issue685", [](py::array) { return "array"; }); - sm.def("issue685", [](py::object) { return "other"; }); + sm.def("issue685", [](const std::string &) { return "string"; }); + sm.def("issue685", [](const py::array &) { return "array"; }); + sm.def("issue685", [](const py::object &) { return "other"; }); // test_array_unchecked_fixed_dims - sm.def("proxy_add2", [](py::array_t a, double v) { - auto r = a.mutable_unchecked<2>(); - for (py::ssize_t i = 0; i < r.shape(0); i++) - for (py::ssize_t j = 0; j < r.shape(1); j++) - r(i, j) += v; - }, py::arg().noconvert(), py::arg()); + sm.def( + "proxy_add2", + [](py::array_t a, double v) { + auto r = a.mutable_unchecked<2>(); + for (py::ssize_t i = 0; i < r.shape(0); i++) { + for (py::ssize_t j = 0; j < r.shape(1); j++) { + r(i, j) += v; + } + } + }, + py::arg{}.noconvert(), + py::arg()); sm.def("proxy_init3", [](double start) { - py::array_t a({ 3, 3, 3 }); + py::array_t a({3, 3, 3}); auto r = a.mutable_unchecked<3>(); - for (py::ssize_t i = 0; i < r.shape(0); i++) - for (py::ssize_t j = 0; j < r.shape(1); j++) - for (py::ssize_t k = 0; k < r.shape(2); k++) - r(i, j, k) = start++; + for (py::ssize_t i = 0; i < r.shape(0); i++) { + for (py::ssize_t j = 0; j < r.shape(1); j++) { + for (py::ssize_t k = 0; k < r.shape(2); k++) { + r(i, j, k) = start++; + } + } + } return a; }); sm.def("proxy_init3F", [](double start) { - py::array_t a({ 3, 3, 3 }); + py::array_t a({3, 3, 3}); auto r = a.mutable_unchecked<3>(); - for (py::ssize_t k = 0; k < r.shape(2); k++) - for (py::ssize_t j = 0; j < r.shape(1); j++) - for (py::ssize_t i = 0; i < r.shape(0); i++) - r(i, j, k) = start++; + for (py::ssize_t k = 0; k < r.shape(2); k++) { + for (py::ssize_t j = 0; j < r.shape(1); j++) { + for (py::ssize_t i = 0; i < r.shape(0); i++) { + r(i, j, k) = start++; + } + } + } return a; }); - sm.def("proxy_squared_L2_norm", [](py::array_t a) { + sm.def("proxy_squared_L2_norm", [](const py::array_t &a) { auto r = a.unchecked<1>(); double sumsq = 0; - for (py::ssize_t i = 0; i < r.shape(0); i++) + for (py::ssize_t i = 0; i < r.shape(0); i++) { sumsq += r[i] * r(i); // Either notation works for a 1D array + } return sumsq; }); @@ -332,51 +377,69 @@ TEST_SUBMODULE(numpy_array, sm) { // test_array_unchecked_dyn_dims // Same as the above, but without a compile-time dimensions specification: - sm.def("proxy_add2_dyn", [](py::array_t a, double v) { - auto r = a.mutable_unchecked(); - if (r.ndim() != 2) throw std::domain_error("error: ndim != 2"); - for (py::ssize_t i = 0; i < r.shape(0); i++) - for (py::ssize_t j = 0; j < r.shape(1); j++) - r(i, j) += v; - }, py::arg().noconvert(), py::arg()); + sm.def( + "proxy_add2_dyn", + [](py::array_t a, double v) { + auto r = a.mutable_unchecked(); + if (r.ndim() != 2) { + throw std::domain_error("error: ndim != 2"); + } + for (py::ssize_t i = 0; i < r.shape(0); i++) { + for (py::ssize_t j = 0; j < r.shape(1); j++) { + r(i, j) += v; + } + } + }, + py::arg{}.noconvert(), + py::arg()); sm.def("proxy_init3_dyn", [](double start) { - py::array_t a({ 3, 3, 3 }); + py::array_t a({3, 3, 3}); auto r = a.mutable_unchecked(); - if (r.ndim() != 3) throw std::domain_error("error: ndim != 3"); - for (py::ssize_t i = 0; i < r.shape(0); i++) - for (py::ssize_t j = 0; j < r.shape(1); j++) - for (py::ssize_t k = 0; k < r.shape(2); k++) - r(i, j, k) = start++; + if (r.ndim() != 3) { + throw std::domain_error("error: ndim != 3"); + } + for (py::ssize_t i = 0; i < r.shape(0); i++) { + for (py::ssize_t j = 0; j < r.shape(1); j++) { + for (py::ssize_t k = 0; k < r.shape(2); k++) { + r(i, j, k) = start++; + } + } + } return a; }); sm.def("proxy_auxiliaries2_dyn", [](py::array_t a) { return auxiliaries(a.unchecked(), a.mutable_unchecked()); }); - sm.def("array_auxiliaries2", [](py::array_t a) { - return auxiliaries(a, a); - }); + sm.def("array_auxiliaries2", [](py::array_t a) { return auxiliaries(a, a); }); // test_array_failures - // Issue #785: Uninformative "Unknown internal error" exception when constructing array from empty object: + // Issue #785: Uninformative "Unknown internal error" exception when constructing array from + // empty object: sm.def("array_fail_test", []() { return py::array(py::object()); }); sm.def("array_t_fail_test", []() { return py::array_t(py::object()); }); // Make sure the error from numpy is being passed through: - sm.def("array_fail_test_negative_size", []() { int c = 0; return py::array(-1, &c); }); + sm.def("array_fail_test_negative_size", []() { + int c = 0; + return py::array(-1, &c); + }); // test_initializer_list // Issue (unnumbered; reported in #788): regression: initializer lists can be ambiguous - sm.def("array_initializer_list1", []() { return py::array_t(1); }); // { 1 } also works, but clang warns about it - sm.def("array_initializer_list2", []() { return py::array_t({ 1, 2 }); }); - sm.def("array_initializer_list3", []() { return py::array_t({ 1, 2, 3 }); }); - sm.def("array_initializer_list4", []() { return py::array_t({ 1, 2, 3, 4 }); }); + sm.def("array_initializer_list1", []() { return py::array_t(1); }); + // { 1 } also works for the above, but clang warns about it + sm.def("array_initializer_list2", []() { return py::array_t({1, 2}); }); + sm.def("array_initializer_list3", []() { return py::array_t({1, 2, 3}); }); + sm.def("array_initializer_list4", []() { return py::array_t({1, 2, 3, 4}); }); // test_array_resize // reshape array to 2D without changing size sm.def("array_reshape2", [](py::array_t a) { - const auto dim_sz = (py::ssize_t)std::sqrt(a.size()); - if (dim_sz * dim_sz != a.size()) - throw std::domain_error("array_reshape2: input array total size is not a squared integer"); + const auto dim_sz = (py::ssize_t) std::sqrt(a.size()); + if (dim_sz * dim_sz != a.size()) { + throw std::domain_error( + "array_reshape2: input array total size is not a squared integer"); + } a.resize({dim_sz, dim_sz}); }); @@ -394,45 +457,68 @@ TEST_SUBMODULE(numpy_array, sm) { return a; }); - sm.def("index_using_ellipsis", [](py::array a) { - return a[py::make_tuple(0, py::ellipsis(), 0)]; + sm.def("array_view", + [](py::array_t a, const std::string &dtype) { return a.view(dtype); }); + + sm.def("reshape_initializer_list", [](py::array_t a, size_t N, size_t M, size_t O) { + return a.reshape({N, M, O}); + }); + sm.def("reshape_tuple", [](py::array_t a, const std::vector &new_shape) { + return a.reshape(new_shape); }); + sm.def("index_using_ellipsis", + [](const py::array &a) { return a[py::make_tuple(0, py::ellipsis(), 0)]; }); + // test_argument_conversions - sm.def("accept_double", - [](py::array_t) {}, - py::arg("a")); - sm.def("accept_double_forcecast", - [](py::array_t) {}, - py::arg("a")); - sm.def("accept_double_c_style", - [](py::array_t) {}, - py::arg("a")); - sm.def("accept_double_c_style_forcecast", - [](py::array_t) {}, - py::arg("a")); - sm.def("accept_double_f_style", - [](py::array_t) {}, - py::arg("a")); - sm.def("accept_double_f_style_forcecast", - [](py::array_t) {}, - py::arg("a")); - sm.def("accept_double_noconvert", - [](py::array_t) {}, - py::arg("a").noconvert()); - sm.def("accept_double_forcecast_noconvert", - [](py::array_t) {}, - py::arg("a").noconvert()); - sm.def("accept_double_c_style_noconvert", - [](py::array_t) {}, - py::arg("a").noconvert()); - sm.def("accept_double_c_style_forcecast_noconvert", - [](py::array_t) {}, - py::arg("a").noconvert()); - sm.def("accept_double_f_style_noconvert", - [](py::array_t) {}, - py::arg("a").noconvert()); - sm.def("accept_double_f_style_forcecast_noconvert", - [](py::array_t) {}, - py::arg("a").noconvert()); + sm.def( + "accept_double", [](const py::array_t &) {}, py::arg("a")); + sm.def( + "accept_double_forcecast", + [](const py::array_t &) {}, + py::arg("a")); + sm.def( + "accept_double_c_style", + [](const py::array_t &) {}, + py::arg("a")); + sm.def( + "accept_double_c_style_forcecast", + [](const py::array_t &) {}, + py::arg("a")); + sm.def( + "accept_double_f_style", + [](const py::array_t &) {}, + py::arg("a")); + sm.def( + "accept_double_f_style_forcecast", + [](const py::array_t &) {}, + py::arg("a")); + sm.def( + "accept_double_noconvert", [](const py::array_t &) {}, "a"_a.noconvert()); + sm.def( + "accept_double_forcecast_noconvert", + [](const py::array_t &) {}, + "a"_a.noconvert()); + sm.def( + "accept_double_c_style_noconvert", + [](const py::array_t &) {}, + "a"_a.noconvert()); + sm.def( + "accept_double_c_style_forcecast_noconvert", + [](const py::array_t &) {}, + "a"_a.noconvert()); + sm.def( + "accept_double_f_style_noconvert", + [](const py::array_t &) {}, + "a"_a.noconvert()); + sm.def( + "accept_double_f_style_forcecast_noconvert", + [](const py::array_t &) {}, + "a"_a.noconvert()); + + // Check that types returns correct npy format descriptor + sm.def("test_fmt_desc_float", [](const py::array_t &) {}); + sm.def("test_fmt_desc_double", [](const py::array_t &) {}); + sm.def("test_fmt_desc_const_float", [](const py::array_t &) {}); + sm.def("test_fmt_desc_const_double", [](const py::array_t &) {}); } diff --git a/3rdparty/pybind11/tests/test_numpy_array.py b/3rdparty/pybind11/tests/test_numpy_array.py index 02f3ecfc..504963b1 100644 --- a/3rdparty/pybind11/tests/test_numpy_array.py +++ b/3rdparty/pybind11/tests/test_numpy_array.py @@ -1,8 +1,6 @@ -# -*- coding: utf-8 -*- import pytest import env # noqa: F401 - from pybind11_tests import numpy_array as m np = pytest.importorskip("numpy") @@ -20,9 +18,7 @@ def test_dtypes(): assert check.numpy == check.pybind11, check if check.numpy.num != check.pybind11.num: print( - "NOTE: typenum mismatch for {}: {} != {}".format( - check, check.numpy.num, check.pybind11.num - ) + f"NOTE: typenum mismatch for {check}: {check.numpy.num} != {check.pybind11.num}" ) @@ -118,9 +114,7 @@ def test_at_fail(arr, dim): for func in m.at_t, m.mutate_at_t: with pytest.raises(IndexError) as excinfo: func(arr, *([0] * dim)) - assert str(excinfo.value) == "index dimension mismatch: {} (ndim = 2)".format( - dim - ) + assert str(excinfo.value) == f"index dimension mismatch: {dim} (ndim = 2)" def test_at(arr): @@ -194,8 +188,6 @@ def test_make_empty_shaped_array(): def test_wrap(): def assert_references(a, b, base=None): - from distutils.version import LooseVersion - if base is None: base = a assert a is not b @@ -206,7 +198,8 @@ def test_wrap(): assert a.flags.f_contiguous == b.flags.f_contiguous assert a.flags.writeable == b.flags.writeable assert a.flags.aligned == b.flags.aligned - if LooseVersion(np.__version__) >= LooseVersion("1.14.0"): + # 1.13 supported Python 3.6 + if tuple(int(x) for x in np.__version__.split(".")[:2]) >= (1, 14): assert a.flags.writebackifcopy == b.flags.writebackifcopy else: assert a.flags.updateifcopy == b.flags.updateifcopy @@ -412,7 +405,7 @@ def test_array_unchecked_fixed_dims(msg): assert m.proxy_auxiliaries2_const_ref(z1) -def test_array_unchecked_dyn_dims(msg): +def test_array_unchecked_dyn_dims(): z1 = np.array([[1, 2], [3, 4]], dtype="float64") m.proxy_add2_dyn(z1, 10) assert np.all(z1 == [[11, 12], [13, 14]]) @@ -445,7 +438,7 @@ def test_initializer_list(): assert m.array_initializer_list4().shape == (1, 2, 3, 4) -def test_array_resize(msg): +def test_array_resize(): a = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9], dtype="float64") m.array_reshape2(a) assert a.size == 9 @@ -471,17 +464,70 @@ def test_array_resize(msg): @pytest.mark.xfail("env.PYPY") -def test_array_create_and_resize(msg): +def test_array_create_and_resize(): a = m.create_and_resize(2) assert a.size == 4 assert np.all(a == 42.0) +def test_array_view(): + a = np.ones(100 * 4).astype("uint8") + a_float_view = m.array_view(a, "float32") + assert a_float_view.shape == (100 * 1,) # 1 / 4 bytes = 8 / 32 + + a_int16_view = m.array_view(a, "int16") # 1 / 2 bytes = 16 / 32 + assert a_int16_view.shape == (100 * 2,) + + +def test_array_view_invalid(): + a = np.ones(100 * 4).astype("uint8") + with pytest.raises(TypeError): + m.array_view(a, "deadly_dtype") + + +def test_reshape_initializer_list(): + a = np.arange(2 * 7 * 3) + 1 + x = m.reshape_initializer_list(a, 2, 7, 3) + assert x.shape == (2, 7, 3) + assert list(x[1][4]) == [34, 35, 36] + with pytest.raises(ValueError) as excinfo: + m.reshape_initializer_list(a, 1, 7, 3) + assert str(excinfo.value) == "cannot reshape array of size 42 into shape (1,7,3)" + + +def test_reshape_tuple(): + a = np.arange(3 * 7 * 2) + 1 + x = m.reshape_tuple(a, (3, 7, 2)) + assert x.shape == (3, 7, 2) + assert list(x[1][4]) == [23, 24] + y = m.reshape_tuple(x, (x.size,)) + assert y.shape == (42,) + with pytest.raises(ValueError) as excinfo: + m.reshape_tuple(a, (3, 7, 1)) + assert str(excinfo.value) == "cannot reshape array of size 42 into shape (3,7,1)" + with pytest.raises(ValueError) as excinfo: + m.reshape_tuple(a, ()) + assert str(excinfo.value) == "cannot reshape array of size 42 into shape ()" + + def test_index_using_ellipsis(): a = m.index_using_ellipsis(np.zeros((5, 6, 7))) assert a.shape == (6,) +@pytest.mark.parametrize( + "test_func", + [ + m.test_fmt_desc_float, + m.test_fmt_desc_double, + m.test_fmt_desc_const_float, + m.test_fmt_desc_const_double, + ], +) +def test_format_descriptors_for_floating_point_types(test_func): + assert "numpy.ndarray[numpy.float" in test_func.__doc__ + + @pytest.mark.parametrize("forcecast", [False, True]) @pytest.mark.parametrize("contiguity", [None, "C", "F"]) @pytest.mark.parametrize("noconvert", [False, True]) diff --git a/3rdparty/pybind11/tests/test_numpy_dtypes.cpp b/3rdparty/pybind11/tests/test_numpy_dtypes.cpp index b2e5e607..6654f9ed 100644 --- a/3rdparty/pybind11/tests/test_numpy_dtypes.cpp +++ b/3rdparty/pybind11/tests/test_numpy_dtypes.cpp @@ -7,13 +7,14 @@ BSD-style license that can be found in the LICENSE file. */ -#include "pybind11_tests.h" #include +#include "pybind11_tests.h" + #ifdef __GNUC__ -#define PYBIND11_PACKED(cls) cls __attribute__((__packed__)) +# define PYBIND11_PACKED(cls) cls __attribute__((__packed__)) #else -#define PYBIND11_PACKED(cls) __pragma(pack(push, 1)) cls __pragma(pack(pop)) +# define PYBIND11_PACKED(cls) __pragma(pack(push, 1)) cls __pragma(pack(pop)) #endif namespace py = pybind11; @@ -25,7 +26,7 @@ struct SimpleStruct { long double ldbl_; }; -std::ostream& operator<<(std::ostream& os, const SimpleStruct& v) { +std::ostream &operator<<(std::ostream &os, const SimpleStruct &v) { return os << "s:" << v.bool_ << "," << v.uint_ << "," << v.float_ << "," << v.ldbl_; } @@ -43,7 +44,7 @@ PYBIND11_PACKED(struct PackedStruct { long double ldbl_; }); -std::ostream& operator<<(std::ostream& os, const PackedStruct& v) { +std::ostream &operator<<(std::ostream &os, const PackedStruct &v) { return os << "p:" << v.bool_ << "," << v.uint_ << "," << v.float_ << "," << v.ldbl_; } @@ -52,7 +53,7 @@ PYBIND11_PACKED(struct NestedStruct { PackedStruct b; }); -std::ostream& operator<<(std::ostream& os, const NestedStruct& v) { +std::ostream &operator<<(std::ostream &os, const NestedStruct &v) { return os << "n:a=" << v.a << ";b=" << v.b; } @@ -70,7 +71,7 @@ struct PartialNestedStruct { uint64_t dummy2; }; -struct UnboundStruct { }; +struct UnboundStruct {}; struct StringStruct { char a[3]; @@ -82,7 +83,7 @@ struct ComplexStruct { std::complex cdbl; }; -std::ostream& operator<<(std::ostream& os, const ComplexStruct& v) { +std::ostream &operator<<(std::ostream &os, const ComplexStruct &v) { return os << "c:" << v.cflt << "," << v.cdbl; } @@ -106,57 +107,65 @@ PYBIND11_PACKED(struct EnumStruct { E2 e2; }); -std::ostream& operator<<(std::ostream& os, const StringStruct& v) { +std::ostream &operator<<(std::ostream &os, const StringStruct &v) { os << "a='"; - for (size_t i = 0; i < 3 && v.a[i]; i++) os << v.a[i]; + for (size_t i = 0; i < 3 && (v.a[i] != 0); i++) { + os << v.a[i]; + } os << "',b='"; - for (size_t i = 0; i < 3 && v.b[i]; i++) os << v.b[i]; + for (size_t i = 0; i < 3 && (v.b[i] != 0); i++) { + os << v.b[i]; + } return os << "'"; } -std::ostream& operator<<(std::ostream& os, const ArrayStruct& v) { +std::ostream &operator<<(std::ostream &os, const ArrayStruct &v) { os << "a={"; for (int i = 0; i < 3; i++) { - if (i > 0) + if (i > 0) { os << ','; + } os << '{'; - for (int j = 0; j < 3; j++) + for (int j = 0; j < 3; j++) { os << v.a[i][j] << ','; + } os << v.a[i][3] << '}'; } os << "},b={" << v.b[0] << ',' << v.b[1]; os << "},c={" << int(v.c[0]) << ',' << int(v.c[1]) << ',' << int(v.c[2]); os << "},d={"; for (int i = 0; i < 4; i++) { - if (i > 0) + if (i > 0) { os << ','; + } os << '{' << v.d[i][0] << ',' << v.d[i][1] << '}'; } return os << '}'; } -std::ostream& operator<<(std::ostream& os, const EnumStruct& v) { +std::ostream &operator<<(std::ostream &os, const EnumStruct &v) { return os << "e1=" << (v.e1 == E1::A ? "A" : "B") << ",e2=" << (v.e2 == E2::X ? "X" : "Y"); } template py::array mkarray_via_buffer(size_t n) { - return py::array(py::buffer_info(nullptr, sizeof(T), - py::format_descriptor::format(), - 1, { n }, { sizeof(T) })); + return py::array(py::buffer_info( + nullptr, sizeof(T), py::format_descriptor::format(), 1, {n}, {sizeof(T)})); } -#define SET_TEST_VALS(s, i) do { \ - s.bool_ = (i) % 2 != 0; \ - s.uint_ = (uint32_t) (i); \ - s.float_ = (float) (i) * 1.5f; \ - s.ldbl_ = (long double) (i) * -2.5L; } while (0) +#define SET_TEST_VALS(s, i) \ + do { \ + (s).bool_ = (i) % 2 != 0; \ + (s).uint_ = (uint32_t) (i); \ + (s).float_ = (float) (i) *1.5f; \ + (s).ldbl_ = (long double) (i) * -2.5L; \ + } while (0) template py::array_t create_recarray(size_t n) { auto arr = mkarray_via_buffer(n); auto req = arr.request(); - auto ptr = static_cast(req.ptr); + auto *ptr = static_cast(req.ptr); for (size_t i = 0; i < n; i++) { SET_TEST_VALS(ptr[i], i); } @@ -166,7 +175,7 @@ py::array_t create_recarray(size_t n) { template py::list print_recarray(py::array_t arr) { const auto req = arr.request(); - const auto ptr = static_cast(req.ptr); + auto *const ptr = static_cast(req.ptr); auto l = py::list(); for (py::ssize_t i = 0; i < req.size; i++) { std::stringstream ss; @@ -179,12 +188,12 @@ py::list print_recarray(py::array_t arr) { py::array_t test_array_ctors(int i) { using arr_t = py::array_t; - std::vector data { 1, 2, 3, 4, 5, 6 }; - std::vector shape { 3, 2 }; - std::vector strides { 8, 4 }; + std::vector data{1, 2, 3, 4, 5, 6}; + std::vector shape{3, 2}; + std::vector strides{8, 4}; - auto ptr = data.data(); - auto vptr = (void *) ptr; + auto *ptr = data.data(); + auto *vptr = (void *) ptr; auto dtype = py::dtype("int32"); py::buffer_info buf_ndim1(vptr, 4, "i", 6); @@ -194,41 +203,69 @@ py::array_t test_array_ctors(int i) { auto fill = [](py::array arr) { auto req = arr.request(); - for (int i = 0; i < 6; i++) ((int32_t *) req.ptr)[i] = i + 1; + for (int i = 0; i < 6; i++) { + ((int32_t *) req.ptr)[i] = i + 1; + } return arr; }; switch (i) { - // shape: (3, 2) - case 10: return arr_t(shape, strides, ptr); - case 11: return py::array(shape, strides, ptr); - case 12: return py::array(dtype, shape, strides, vptr); - case 13: return arr_t(shape, ptr); - case 14: return py::array(shape, ptr); - case 15: return py::array(dtype, shape, vptr); - case 16: return arr_t(buf_ndim2); - case 17: return py::array(buf_ndim2); - // shape: (3, 2) - post-fill - case 20: return fill(arr_t(shape, strides)); - case 21: return py::array(shape, strides, ptr); // can't have nullptr due to templated ctor - case 22: return fill(py::array(dtype, shape, strides)); - case 23: return fill(arr_t(shape)); - case 24: return py::array(shape, ptr); // can't have nullptr due to templated ctor - case 25: return fill(py::array(dtype, shape)); - case 26: return fill(arr_t(buf_ndim2_null)); - case 27: return fill(py::array(buf_ndim2_null)); - // shape: (6, ) - case 30: return arr_t(6, ptr); - case 31: return py::array(6, ptr); - case 32: return py::array(dtype, 6, vptr); - case 33: return arr_t(buf_ndim1); - case 34: return py::array(buf_ndim1); - // shape: (6, ) - case 40: return fill(arr_t(6)); - case 41: return py::array(6, ptr); // can't have nullptr due to templated ctor - case 42: return fill(py::array(dtype, 6)); - case 43: return fill(arr_t(buf_ndim1_null)); - case 44: return fill(py::array(buf_ndim1_null)); + // shape: (3, 2) + case 10: + return arr_t(shape, strides, ptr); + case 11: + return py::array(shape, strides, ptr); + case 12: + return py::array(dtype, shape, strides, vptr); + case 13: + return arr_t(shape, ptr); + case 14: + return py::array(shape, ptr); + case 15: + return py::array(dtype, shape, vptr); + case 16: + return arr_t(buf_ndim2); + case 17: + return py::array(buf_ndim2); + // shape: (3, 2) - post-fill + case 20: + return fill(arr_t(shape, strides)); + case 21: + return py::array(shape, strides, ptr); // can't have nullptr due to templated ctor + case 22: + return fill(py::array(dtype, shape, strides)); + case 23: + return fill(arr_t(shape)); + case 24: + return py::array(shape, ptr); // can't have nullptr due to templated ctor + case 25: + return fill(py::array(dtype, shape)); + case 26: + return fill(arr_t(buf_ndim2_null)); + case 27: + return fill(py::array(buf_ndim2_null)); + // shape: (6, ) + case 30: + return arr_t(6, ptr); + case 31: + return py::array(6, ptr); + case 32: + return py::array(dtype, 6, vptr); + case 33: + return arr_t(buf_ndim1); + case 34: + return py::array(buf_ndim1); + // shape: (6, ) + case 40: + return fill(arr_t(6)); + case 41: + return py::array(6, ptr); // can't have nullptr due to templated ctor + case 42: + return fill(py::array(dtype, 6)); + case 43: + return fill(arr_t(buf_ndim1_null)); + case 44: + return fill(py::array(buf_ndim1_null)); } return arr_t(); } @@ -240,14 +277,21 @@ py::list test_dtype_ctors() { list.append(py::dtype::from_args(py::str("bool"))); py::list names, offsets, formats; py::dict dict; - names.append(py::str("a")); names.append(py::str("b")); dict["names"] = names; - offsets.append(py::int_(1)); offsets.append(py::int_(10)); dict["offsets"] = offsets; - formats.append(py::dtype("int32")); formats.append(py::dtype("float64")); dict["formats"] = formats; + names.append(py::str("a")); + names.append(py::str("b")); + dict["names"] = names; + offsets.append(py::int_(1)); + offsets.append(py::int_(10)); + dict["offsets"] = offsets; + formats.append(py::dtype("int32")); + formats.append(py::dtype("float64")); + dict["formats"] = formats; dict["itemsize"] = py::int_(20); list.append(py::dtype::from_args(dict)); list.append(py::dtype(names, formats, offsets, 20)); - list.append(py::dtype(py::buffer_info((void *) 0, sizeof(unsigned int), "I", 1))); - list.append(py::dtype(py::buffer_info((void *) 0, 0, "T{i:a:f:b:}", 1))); + list.append(py::dtype(py::buffer_info((void *) nullptr, sizeof(unsigned int), "I", 1))); + list.append(py::dtype(py::buffer_info((void *) nullptr, 0, "T{i:a:f:b:}", 1))); + list.append(py::dtype(py::detail::npy_api::NPY_DOUBLE_)); return list; } @@ -255,8 +299,11 @@ struct A {}; struct B {}; TEST_SUBMODULE(numpy_dtypes, m) { - try { py::module_::import("numpy"); } - catch (...) { return; } + try { + py::module_::import("numpy"); + } catch (const py::error_already_set &) { + return; + } // typeinfo may be registered before the dtype descriptor for scalar casts to work... py::class_(m, "SimpleStruct") @@ -266,18 +313,18 @@ TEST_SUBMODULE(numpy_dtypes, m) { .def_readwrite("uint_", &SimpleStruct::uint_) .def_readwrite("float_", &SimpleStruct::float_) .def_readwrite("ldbl_", &SimpleStruct::ldbl_) - .def("astuple", [](const SimpleStruct& self) { - return py::make_tuple(self.bool_, self.uint_, self.float_, self.ldbl_); - }) - .def_static("fromtuple", [](const py::tuple tup) { + .def("astuple", + [](const SimpleStruct &self) { + return py::make_tuple(self.bool_, self.uint_, self.float_, self.ldbl_); + }) + .def_static("fromtuple", [](const py::tuple &tup) { if (py::len(tup) != 4) { throw py::cast_error("Invalid size"); } - return SimpleStruct{ - tup[0].cast(), - tup[1].cast(), - tup[2].cast(), - tup[3].cast()}; + return SimpleStruct{tup[0].cast(), + tup[1].cast(), + tup[2].cast(), + tup[3].cast()}; }); PYBIND11_NUMPY_DTYPE(SimpleStruct, bool_, uint_, float_, ldbl_); @@ -296,19 +343,21 @@ TEST_SUBMODULE(numpy_dtypes, m) { PYBIND11_NUMPY_DTYPE_EX(StructWithUglyNames, __x__, "x", __y__, "y"); - // If uncommented, this should produce a static_assert failure telling the user that the struct +#ifdef PYBIND11_NEVER_DEFINED_EVER + // If enabled, this should produce a static_assert failure telling the user that the struct // is not a POD type -// struct NotPOD { std::string v; NotPOD() : v("hi") {}; }; -// PYBIND11_NUMPY_DTYPE(NotPOD, v); + struct NotPOD { + std::string v; + NotPOD() : v("hi"){}; + }; + PYBIND11_NUMPY_DTYPE(NotPOD, v); +#endif // Check that dtypes can be registered programmatically, both from // initializer lists of field descriptors and from other containers. - py::detail::npy_format_descriptor::register_dtype( - {} - ); + py::detail::npy_format_descriptor::register_dtype({}); py::detail::npy_format_descriptor::register_dtype( - std::vector{} - ); + std::vector{}); // test_recarray, test_scalar_conversion m.def("create_rec_simple", &create_recarray); @@ -316,7 +365,7 @@ TEST_SUBMODULE(numpy_dtypes, m) { m.def("create_rec_nested", [](size_t n) { // test_signature py::array_t arr = mkarray_via_buffer(n); auto req = arr.request(); - auto ptr = static_cast(req.ptr); + auto *ptr = static_cast(req.ptr); for (size_t i = 0; i < n; i++) { SET_TEST_VALS(ptr[i].a, i); SET_TEST_VALS(ptr[i].b, i + 1); @@ -327,7 +376,7 @@ TEST_SUBMODULE(numpy_dtypes, m) { m.def("create_rec_partial_nested", [](size_t n) { py::array_t arr = mkarray_via_buffer(n); auto req = arr.request(); - auto ptr = static_cast(req.ptr); + auto *ptr = static_cast(req.ptr); for (size_t i = 0; i < n; i++) { SET_TEST_VALS(ptr[i].a, i); } @@ -341,48 +390,95 @@ TEST_SUBMODULE(numpy_dtypes, m) { m.def("get_format_unbound", []() { return py::format_descriptor::format(); }); m.def("print_format_descriptors", []() { py::list l; - for (const auto &fmt : { - py::format_descriptor::format(), - py::format_descriptor::format(), - py::format_descriptor::format(), - py::format_descriptor::format(), - py::format_descriptor::format(), - py::format_descriptor::format(), - py::format_descriptor::format(), - py::format_descriptor::format(), - py::format_descriptor::format() - }) { + for (const auto &fmt : {py::format_descriptor::format(), + py::format_descriptor::format(), + py::format_descriptor::format(), + py::format_descriptor::format(), + py::format_descriptor::format(), + py::format_descriptor::format(), + py::format_descriptor::format(), + py::format_descriptor::format(), + py::format_descriptor::format()}) { l.append(py::cast(fmt)); } return l; }); // test_dtype + std::vector dtype_names{ + "byte", "short", "intc", "int_", "longlong", "ubyte", "ushort", + "uintc", "uint", "ulonglong", "half", "single", "double", "longdouble", + "csingle", "cdouble", "clongdouble", "bool_", "datetime64", "timedelta64", "object_"}; + m.def("print_dtypes", []() { py::list l; - for (const py::handle &d : { - py::dtype::of(), - py::dtype::of(), - py::dtype::of(), - py::dtype::of(), - py::dtype::of(), - py::dtype::of(), - py::dtype::of(), - py::dtype::of(), - py::dtype::of(), - py::dtype::of() - }) + for (const py::handle &d : {py::dtype::of(), + py::dtype::of(), + py::dtype::of(), + py::dtype::of(), + py::dtype::of(), + py::dtype::of(), + py::dtype::of(), + py::dtype::of(), + py::dtype::of(), + py::dtype::of()}) { l.append(py::str(d)); + } return l; }); m.def("test_dtype_ctors", &test_dtype_ctors); + m.def("test_dtype_kind", [dtype_names]() { + py::list list; + for (const auto &dt_name : dtype_names) { + list.append(py::dtype(dt_name).kind()); + } + return list; + }); + m.def("test_dtype_char_", [dtype_names]() { + py::list list; + for (const auto &dt_name : dtype_names) { + list.append(py::dtype(dt_name).char_()); + } + return list; + }); + m.def("test_dtype_num", [dtype_names]() { + py::list list; + for (const auto &dt_name : dtype_names) { + list.append(py::dtype(dt_name).num()); + } + return list; + }); + m.def("test_dtype_byteorder", [dtype_names]() { + py::list list; + for (const auto &dt_name : dtype_names) { + list.append(py::dtype(dt_name).byteorder()); + } + return list; + }); + m.def("test_dtype_alignment", [dtype_names]() { + py::list list; + for (const auto &dt_name : dtype_names) { + list.append(py::dtype(dt_name).alignment()); + } + return list; + }); + m.def("test_dtype_flags", [dtype_names]() { + py::list list; + for (const auto &dt_name : dtype_names) { + list.append(py::dtype(dt_name).flags()); + } + return list; + }); m.def("test_dtype_methods", []() { py::list list; auto dt1 = py::dtype::of(); auto dt2 = py::dtype::of(); - list.append(dt1); list.append(dt2); - list.append(py::bool_(dt1.has_fields())); list.append(py::bool_(dt2.has_fields())); - list.append(py::int_(dt1.itemsize())); list.append(py::int_(dt2.itemsize())); + list.append(dt1); + list.append(dt2); + list.append(py::bool_(dt1.has_fields())); + list.append(py::bool_(dt2.has_fields())); + list.append(py::int_(dt1.itemsize())); + list.append(py::int_(dt2.itemsize())); return list; }); struct TrailingPaddingStruct { @@ -397,17 +493,24 @@ TEST_SUBMODULE(numpy_dtypes, m) { py::array_t arr = mkarray_via_buffer(non_empty ? 4 : 0); if (non_empty) { auto req = arr.request(); - auto ptr = static_cast(req.ptr); - for (py::ssize_t i = 0; i < req.size * req.itemsize; i++) - static_cast(req.ptr)[i] = 0; - ptr[1].a[0] = 'a'; ptr[1].b[0] = 'a'; - ptr[2].a[0] = 'a'; ptr[2].b[0] = 'a'; - ptr[3].a[0] = 'a'; ptr[3].b[0] = 'a'; - - ptr[2].a[1] = 'b'; ptr[2].b[1] = 'b'; - ptr[3].a[1] = 'b'; ptr[3].b[1] = 'b'; - - ptr[3].a[2] = 'c'; ptr[3].b[2] = 'c'; + auto *ptr = static_cast(req.ptr); + for (py::ssize_t i = 0; i < req.size * req.itemsize; i++) { + static_cast(req.ptr)[i] = 0; + } + ptr[1].a[0] = 'a'; + ptr[1].b[0] = 'a'; + ptr[2].a[0] = 'a'; + ptr[2].b[0] = 'a'; + ptr[3].a[0] = 'a'; + ptr[3].b[0] = 'a'; + + ptr[2].a[1] = 'b'; + ptr[2].b[1] = 'b'; + ptr[3].a[1] = 'b'; + ptr[3].b[1] = 'b'; + + ptr[3].a[2] = 'c'; + ptr[3].b[2] = 'c'; } return arr; }); @@ -416,18 +519,24 @@ TEST_SUBMODULE(numpy_dtypes, m) { // test_array_array m.def("create_array_array", [](size_t n) { py::array_t arr = mkarray_via_buffer(n); - auto ptr = (ArrayStruct *) arr.mutable_data(); + auto *ptr = (ArrayStruct *) arr.mutable_data(); for (size_t i = 0; i < n; i++) { - for (size_t j = 0; j < 3; j++) - for (size_t k = 0; k < 4; k++) + for (size_t j = 0; j < 3; j++) { + for (size_t k = 0; k < 4; k++) { ptr[i].a[j][k] = char('A' + (i * 100 + j * 10 + k) % 26); - for (size_t j = 0; j < 2; j++) + } + } + for (size_t j = 0; j < 2; j++) { ptr[i].b[j] = int32_t(i * 1000 + j); - for (size_t j = 0; j < 3; j++) + } + for (size_t j = 0; j < 3; j++) { ptr[i].c[j] = uint8_t(i * 10 + j); - for (size_t j = 0; j < 4; j++) - for (size_t k = 0; k < 2; k++) + } + for (size_t j = 0; j < 4; j++) { + for (size_t k = 0; k < 2; k++) { ptr[i].d[j][k] = float(i) * 100.0f + float(j) * 10.0f + float(k); + } + } } return arr; }); @@ -436,7 +545,7 @@ TEST_SUBMODULE(numpy_dtypes, m) { // test_enum_array m.def("create_enum_array", [](size_t n) { py::array_t arr = mkarray_via_buffer(n); - auto ptr = (EnumStruct *) arr.mutable_data(); + auto *ptr = (EnumStruct *) arr.mutable_data(); for (size_t i = 0; i < n; i++) { ptr[i].e1 = static_cast(-1 + ((int) i % 2) * 2); ptr[i].e2 = static_cast(1 + (i % 2)); @@ -448,7 +557,7 @@ TEST_SUBMODULE(numpy_dtypes, m) { // test_complex_array m.def("create_complex_array", [](size_t n) { py::array_t arr = mkarray_via_buffer(n); - auto ptr = (ComplexStruct *) arr.mutable_data(); + auto *ptr = (ComplexStruct *) arr.mutable_data(); for (size_t i = 0; i < n; i++) { ptr[i].cflt.real(float(i)); ptr[i].cflt.imag(float(i) + 0.25f); @@ -471,14 +580,19 @@ TEST_SUBMODULE(numpy_dtypes, m) { PYBIND11_NUMPY_DTYPE(CompareStruct, x, y, z); m.def("compare_buffer_info", []() { py::list list; - list.append(py::bool_(py::detail::compare_buffer_info::compare(py::buffer_info(nullptr, sizeof(float), "f", 1)))); - list.append(py::bool_(py::detail::compare_buffer_info::compare(py::buffer_info(nullptr, sizeof(int), "I", 1)))); - list.append(py::bool_(py::detail::compare_buffer_info::compare(py::buffer_info(nullptr, sizeof(long), "l", 1)))); - list.append(py::bool_(py::detail::compare_buffer_info::compare(py::buffer_info(nullptr, sizeof(long), sizeof(long) == sizeof(int) ? "i" : "q", 1)))); - list.append(py::bool_(py::detail::compare_buffer_info::compare(py::buffer_info(nullptr, sizeof(CompareStruct), "T{?:x:3xI:y:f:z:}", 1)))); + list.append(py::bool_(py::detail::compare_buffer_info::compare( + py::buffer_info(nullptr, sizeof(float), "f", 1)))); + list.append(py::bool_(py::detail::compare_buffer_info::compare( + py::buffer_info(nullptr, sizeof(int), "I", 1)))); + list.append(py::bool_(py::detail::compare_buffer_info::compare( + py::buffer_info(nullptr, sizeof(long), "l", 1)))); + list.append(py::bool_(py::detail::compare_buffer_info::compare( + py::buffer_info(nullptr, sizeof(long), sizeof(long) == sizeof(int) ? "i" : "q", 1)))); + list.append(py::bool_(py::detail::compare_buffer_info::compare( + py::buffer_info(nullptr, sizeof(CompareStruct), "T{?:x:3xI:y:f:z:}", 1)))); return list; }); - m.def("buffer_to_dtype", [](py::buffer& buf) { return py::dtype(buf.request()); }); + m.def("buffer_to_dtype", [](py::buffer &buf) { return py::dtype(buf.request()); }); // test_scalar_conversion auto f_simple = [](SimpleStruct s) { return s.uint_ * 10; }; @@ -492,8 +606,9 @@ TEST_SUBMODULE(numpy_dtypes, m) { m.def("f_simple_pass_thru_vectorized", py::vectorize(f_simple_pass_thru)); // test_register_dtype - m.def("register_dtype", []() { PYBIND11_NUMPY_DTYPE(SimpleStruct, bool_, uint_, float_, ldbl_); }); + m.def("register_dtype", + []() { PYBIND11_NUMPY_DTYPE(SimpleStruct, bool_, uint_, float_, ldbl_); }); // test_str_leak - m.def("dtype_wrapper", [](py::object d) { return py::dtype::from_args(std::move(d)); }); + m.def("dtype_wrapper", [](const py::object &d) { return py::dtype::from_args(d); }); } diff --git a/3rdparty/pybind11/tests/test_numpy_dtypes.py b/3rdparty/pybind11/tests/test_numpy_dtypes.py index f56b776a..fcfd587b 100644 --- a/3rdparty/pybind11/tests/test_numpy_dtypes.py +++ b/3rdparty/pybind11/tests/test_numpy_dtypes.py @@ -1,10 +1,8 @@ -# -*- coding: utf-8 -*- import re import pytest import env # noqa: F401 - from pybind11_tests import numpy_dtypes as m np = pytest.importorskip("numpy") @@ -16,7 +14,7 @@ def simple_dtype(): return np.dtype( { "names": ["bool_", "uint_", "float_", "ldbl_"], - "formats": ["?", "u4", "f4", "f{}".format(ld.itemsize)], + "formats": ["?", "u4", "f4", f"f{ld.itemsize}"], "offsets": [0, 4, 8, (16 if ld.alignment > 4 else 12)], } ) @@ -33,8 +31,8 @@ def dt_fmt(): e = "<" if byteorder == "little" else ">" return ( "{{'names':['bool_','uint_','float_','ldbl_']," - " 'formats':['?','" + e + "u4','" + e + "f4','" + e + "f{}']," - " 'offsets':[0,4,8,{}], 'itemsize':{}}}" + "'formats':['?','" + e + "u4','" + e + "f4','" + e + "f{}']," + "'offsets':[0,4,8,{}],'itemsize':{}}}" ) @@ -47,7 +45,7 @@ def simple_dtype_fmt(): def packed_dtype_fmt(): from sys import byteorder - return "[('bool_', '?'), ('uint_', '{e}u4'), ('float_', '{e}f4'), ('ldbl_', '{e}f{}')]".format( + return "[('bool_','?'),('uint_','{e}u4'),('float_','{e}f4'),('ldbl_','{e}f{}')]".format( np.dtype("longdouble").itemsize, e="<" if byteorder == "little" else ">" ) @@ -64,15 +62,21 @@ def partial_ld_offset(): def partial_dtype_fmt(): ld = np.dtype("longdouble") partial_ld_off = partial_ld_offset() - return dt_fmt().format(ld.itemsize, partial_ld_off, partial_ld_off + ld.itemsize) + partial_size = partial_ld_off + ld.itemsize + partial_end_padding = partial_size % np.dtype("uint64").alignment + return dt_fmt().format( + ld.itemsize, partial_ld_off, partial_size + partial_end_padding + ) def partial_nested_fmt(): ld = np.dtype("longdouble") partial_nested_off = 8 + 8 * (ld.alignment > 8) partial_ld_off = partial_ld_offset() - partial_nested_size = partial_nested_off * 2 + partial_ld_off + ld.itemsize - return "{{'names':['a'], 'formats':[{}], 'offsets':[{}], 'itemsize':{}}}".format( + partial_size = partial_ld_off + ld.itemsize + partial_end_padding = partial_size % np.dtype("uint64").alignment + partial_nested_size = partial_nested_off * 2 + partial_size + partial_end_padding + return "{{'names':['a'],'formats':[{}],'offsets':[{}],'itemsize':{}}}".format( partial_dtype_fmt(), partial_nested_off, partial_nested_size ) @@ -92,10 +96,12 @@ def test_format_descriptors(): ldbl_fmt = ("4x" if ld.alignment > 4 else "") + ld.char ss_fmt = "^T{?:bool_:3xI:uint_:f:float_:" + ldbl_fmt + ":ldbl_:}" dbl = np.dtype("double") + end_padding = ld.itemsize % np.dtype("uint64").alignment partial_fmt = ( "^T{?:bool_:3xI:uint_:f:float_:" + str(4 * (dbl.alignment > 4) + dbl.itemsize + 8 * (ld.alignment > 8)) - + "xg:ldbl_:}" + + "xg:ldbl_:" + + (str(end_padding) + "x}" if end_padding > 0 else "}") ) nested_extra = str(max(8, ld.alignment)) assert m.print_format_descriptors() == [ @@ -116,25 +122,25 @@ def test_dtype(simple_dtype): e = "<" if byteorder == "little" else ">" - assert m.print_dtypes() == [ + assert [x.replace(" ", "") for x in m.print_dtypes()] == [ simple_dtype_fmt(), packed_dtype_fmt(), - "[('a', {}), ('b', {})]".format(simple_dtype_fmt(), packed_dtype_fmt()), + f"[('a',{simple_dtype_fmt()}),('b',{packed_dtype_fmt()})]", partial_dtype_fmt(), partial_nested_fmt(), - "[('a', 'S3'), ('b', 'S3')]", + "[('a','S3'),('b','S3')]", ( - "{{'names':['a','b','c','d'], " - + "'formats':[('S4', (3,)),('" + "{{'names':['a','b','c','d']," + + "'formats':[('S4',(3,)),('" + e - + "i4', (2,)),('u1', (3,)),('" + + "i4',(2,)),('u1',(3,)),('" + e - + "f4', (4, 2))], " - + "'offsets':[0,12,20,24], 'itemsize':56}}" + + "f4',(4,2))]," + + "'offsets':[0,12,20,24],'itemsize':56}}" ).format(e=e), - "[('e1', '" + e + "i8'), ('e2', 'u1')]", - "[('x', 'i1'), ('y', '" + e + "u8')]", - "[('cflt', '" + e + "c8'), ('cdbl', '" + e + "c16')]", + "[('e1','" + e + "i8'),('e2','u1')]", + "[('x','i1'),('y','" + e + "u8')]", + "[('cflt','" + e + "c8'),('cdbl','" + e + "c16')]", ] d1 = np.dtype( @@ -154,6 +160,7 @@ def test_dtype(simple_dtype): d1, np.dtype("uint32"), d2, + np.dtype("d"), ] assert m.test_dtype_methods() == [ @@ -169,6 +176,14 @@ def test_dtype(simple_dtype): np.zeros(1, m.trailing_padding_dtype()) ) + expected_chars = "bhilqBHILQefdgFDG?MmO" + assert m.test_dtype_kind() == list("iiiiiuuuuuffffcccbMmO") + assert m.test_dtype_char_() == list(expected_chars) + assert m.test_dtype_num() == [np.dtype(ch).num for ch in expected_chars] + assert m.test_dtype_byteorder() == [np.dtype(ch).byteorder for ch in expected_chars] + assert m.test_dtype_alignment() == [np.dtype(ch).alignment for ch in expected_chars] + assert m.test_dtype_flags() == [chr(np.dtype(ch).flags) for ch in expected_chars] + def test_recarray(simple_dtype, packed_dtype): elements = [(False, 0, 0.0, -0.0), (True, 1, 1.5, -2.5), (False, 2, 3.0, -5.0)] @@ -228,7 +243,7 @@ def test_recarray(simple_dtype, packed_dtype): ] arr = m.create_rec_partial(3) - assert str(arr.dtype) == partial_dtype_fmt() + assert str(arr.dtype).replace(" ", "") == partial_dtype_fmt() partial_dtype = arr.dtype assert "" not in arr.dtype.fields assert partial_dtype.itemsize > simple_dtype.itemsize @@ -236,7 +251,7 @@ def test_recarray(simple_dtype, packed_dtype): assert_equal(arr, elements, packed_dtype) arr = m.create_rec_partial_nested(3) - assert str(arr.dtype) == partial_nested_fmt() + assert str(arr.dtype).replace(" ", "") == partial_nested_fmt() assert "" not in arr.dtype.fields assert "" not in arr.dtype.fields["a"][0].fields assert arr.dtype.itemsize > partial_dtype.itemsize @@ -275,12 +290,12 @@ def test_array_array(): e = "<" if byteorder == "little" else ">" arr = m.create_array_array(3) - assert str(arr.dtype) == ( - "{{'names':['a','b','c','d'], " - + "'formats':[('S4', (3,)),('" + assert str(arr.dtype).replace(" ", "") == ( + "{{'names':['a','b','c','d']," + + "'formats':[('S4',(3,)),('" + e - + "i4', (2,)),('u1', (3,)),('{e}f4', (4, 2))], " - + "'offsets':[0,12,20,24], 'itemsize':56}}" + + "i4',(2,)),('u1',(3,)),('{e}f4',(4,2))]," + + "'offsets':[0,12,20,24],'itemsize':56}}" ).format(e=e) assert m.print_array_array(arr) == [ "a={{A,B,C,D},{K,L,M,N},{U,V,W,X}},b={0,1}," diff --git a/3rdparty/pybind11/tests/test_numpy_vectorize.cpp b/3rdparty/pybind11/tests/test_numpy_vectorize.cpp index 274b7558..dcc4c6ac 100644 --- a/3rdparty/pybind11/tests/test_numpy_vectorize.cpp +++ b/3rdparty/pybind11/tests/test_numpy_vectorize.cpp @@ -8,66 +8,80 @@ BSD-style license that can be found in the LICENSE file. */ -#include "pybind11_tests.h" #include +#include "pybind11_tests.h" + +#include + double my_func(int x, float y, double z) { py::print("my_func(x:int={}, y:float={:.0f}, z:float={:.0f})"_s.format(x, y, z)); - return (float) x*y*z; + return (float) x * y * z; } TEST_SUBMODULE(numpy_vectorize, m) { - try { py::module_::import("numpy"); } - catch (...) { return; } + try { + py::module_::import("numpy"); + } catch (const py::error_already_set &) { + return; + } // test_vectorize, test_docs, test_array_collapse // Vectorize all arguments of a function (though non-vector arguments are also allowed) m.def("vectorized_func", py::vectorize(my_func)); - // Vectorize a lambda function with a capture object (e.g. to exclude some arguments from the vectorization) - m.def("vectorized_func2", - [](py::array_t x, py::array_t y, float z) { - return py::vectorize([z](int x, float y) { return my_func(x, y, z); })(x, y); - } - ); + // Vectorize a lambda function with a capture object (e.g. to exclude some arguments from the + // vectorization) + m.def("vectorized_func2", [](py::array_t x, py::array_t y, float z) { + return py::vectorize([z](int x, float y) { return my_func(x, y, z); })(std::move(x), + std::move(y)); + }); // Vectorize a complex-valued function - m.def("vectorized_func3", py::vectorize( - [](std::complex c) { return c * std::complex(2.f); } - )); + m.def("vectorized_func3", + py::vectorize([](std::complex c) { return c * std::complex(2.f); })); // test_type_selection // NumPy function which only accepts specific data types - m.def("selective_func", [](py::array_t) { return "Int branch taken."; }); - m.def("selective_func", [](py::array_t) { return "Float branch taken."; }); - m.def("selective_func", [](py::array_t, py::array::c_style>) { return "Complex float branch taken."; }); - + // A lot of these no lints could be replaced with const refs, and probably should at some + // point. + m.def("selective_func", + [](const py::array_t &) { return "Int branch taken."; }); + m.def("selective_func", + [](const py::array_t &) { return "Float branch taken."; }); + m.def("selective_func", [](const py::array_t, py::array::c_style> &) { + return "Complex float branch taken."; + }); // test_passthrough_arguments - // Passthrough test: references and non-pod types should be automatically passed through (in the - // function definition below, only `b`, `d`, and `g` are vectorized): + // Passthrough test: references and non-pod types should be automatically passed through (in + // the function definition below, only `b`, `d`, and `g` are vectorized): struct NonPODClass { - NonPODClass(int v) : value{v} {} + explicit NonPODClass(int v) : value{v} {} int value; }; py::class_(m, "NonPODClass") .def(py::init()) .def_readwrite("value", &NonPODClass::value); - m.def("vec_passthrough", py::vectorize( - [](double *a, double b, py::array_t c, const int &d, int &e, NonPODClass f, const double g) { - return *a + b + c.at(0) + d + e + f.value + g; - } - )); + m.def("vec_passthrough", + py::vectorize([](const double *a, + double b, + // Changing this broke things + // NOLINTNEXTLINE(performance-unnecessary-value-param) + py::array_t c, + const int &d, + int &e, + NonPODClass f, + const double g) { return *a + b + c.at(0) + d + e + f.value + g; })); // test_method_vectorization struct VectorizeTestClass { - VectorizeTestClass(int v) : value{v} {}; - float method(int x, float y) { return y + (float) (x + value); } + explicit VectorizeTestClass(int v) : value{v} {}; + float method(int x, float y) const { return y + (float) (x + value); } int value = 0; }; py::class_ vtc(m, "VectorizeTestClass"); - vtc .def(py::init()) - .def_readwrite("value", &VectorizeTestClass::value); + vtc.def(py::init()).def_readwrite("value", &VectorizeTestClass::value); // Automatic vectorizing of methods vtc.def("method", py::vectorize(&VectorizeTestClass::method)); @@ -78,16 +92,16 @@ TEST_SUBMODULE(numpy_vectorize, m) { .value("f_trivial", py::detail::broadcast_trivial::f_trivial) .value("c_trivial", py::detail::broadcast_trivial::c_trivial) .value("non_trivial", py::detail::broadcast_trivial::non_trivial); - m.def("vectorized_is_trivial", []( - py::array_t arg1, - py::array_t arg2, - py::array_t arg3 - ) { - py::ssize_t ndim; - std::vector shape; - std::array buffers {{ arg1.request(), arg2.request(), arg3.request() }}; - return py::detail::broadcast(buffers, ndim, shape); - }); + m.def("vectorized_is_trivial", + [](const py::array_t &arg1, + const py::array_t &arg2, + const py::array_t &arg3) { + py::ssize_t ndim = 0; + std::vector shape; + std::array buffers{ + {arg1.request(), arg2.request(), arg3.request()}}; + return py::detail::broadcast(buffers, ndim, shape); + }); - m.def("add_to", py::vectorize([](NonPODClass& x, int a) { x.value += a; })); + m.def("add_to", py::vectorize([](NonPODClass &x, int a) { x.value += a; })); } diff --git a/3rdparty/pybind11/tests/test_numpy_vectorize.py b/3rdparty/pybind11/tests/test_numpy_vectorize.py index 4e6b2d19..7e8c015c 100644 --- a/3rdparty/pybind11/tests/test_numpy_vectorize.py +++ b/3rdparty/pybind11/tests/test_numpy_vectorize.py @@ -1,5 +1,5 @@ -# -*- coding: utf-8 -*- import pytest + from pybind11_tests import numpy_vectorize as m np = pytest.importorskip("numpy") diff --git a/3rdparty/pybind11/tests/test_opaque_types.cpp b/3rdparty/pybind11/tests/test_opaque_types.cpp index 5a234316..0386dba0 100644 --- a/3rdparty/pybind11/tests/test_opaque_types.cpp +++ b/3rdparty/pybind11/tests/test_opaque_types.cpp @@ -7,8 +7,10 @@ BSD-style license that can be found in the LICENSE file. */ -#include "pybind11_tests.h" #include + +#include "pybind11_tests.h" + #include // IMPORTANT: Disable internal pybind11 translation mechanisms for STL data structures @@ -26,12 +28,13 @@ TEST_SUBMODULE(opaque_types, m) { .def(py::init<>()) .def("pop_back", &StringList::pop_back) /* There are multiple versions of push_back(), etc. Select the right ones. */ - .def("push_back", (void (StringList::*)(const std::string &)) &StringList::push_back) - .def("back", (std::string &(StringList::*)()) &StringList::back) + .def("push_back", (void(StringList::*)(const std::string &)) & StringList::push_back) + .def("back", (std::string & (StringList::*) ()) & StringList::back) .def("__len__", [](const StringList &v) { return v.size(); }) - .def("__iter__", [](StringList &v) { - return py::make_iterator(v.begin(), v.end()); - }, py::keep_alive<0, 1>()); + .def( + "__iter__", + [](StringList &v) { return py::make_iterator(v.begin(), v.end()); }, + py::keep_alive<0, 1>()); class ClassWithSTLVecProperty { public: @@ -44,9 +47,10 @@ TEST_SUBMODULE(opaque_types, m) { m.def("print_opaque_list", [](const StringList &l) { std::string ret = "Opaque list: ["; bool first = true; - for (auto entry : l) { - if (!first) + for (const auto &entry : l) { + if (!first) { ret += ", "; + } ret += entry; first = false; } diff --git a/3rdparty/pybind11/tests/test_opaque_types.py b/3rdparty/pybind11/tests/test_opaque_types.py index 77379463..5d4f2a1b 100644 --- a/3rdparty/pybind11/tests/test_opaque_types.py +++ b/3rdparty/pybind11/tests/test_opaque_types.py @@ -1,7 +1,7 @@ -# -*- coding: utf-8 -*- import pytest -from pybind11_tests import opaque_types as m + from pybind11_tests import ConstructorStats, UserType +from pybind11_tests import opaque_types as m def test_string_list(): @@ -12,7 +12,7 @@ def test_string_list(): assert lst.back() == "Element 2" for i, k in enumerate(lst, start=1): - assert k == "Element {}".format(i) + assert k == f"Element {i}" lst.pop_back() assert m.print_opaque_list(lst) == "Opaque list: [Element 1]" @@ -39,7 +39,7 @@ def test_pointers(msg): 1. (arg0: capsule) -> int Invoked with: [1, 2, 3] - """ # noqa: E501 line too long + """ ) assert m.return_null_str() is None diff --git a/3rdparty/pybind11/tests/test_operator_overloading.cpp b/3rdparty/pybind11/tests/test_operator_overloading.cpp index 0a27bfd5..a4b895a8 100644 --- a/3rdparty/pybind11/tests/test_operator_overloading.cpp +++ b/3rdparty/pybind11/tests/test_operator_overloading.cpp @@ -7,21 +7,40 @@ BSD-style license that can be found in the LICENSE file. */ -#include "pybind11_tests.h" -#include "constructor_stats.h" #include +#include + +#include "constructor_stats.h" +#include "pybind11_tests.h" + #include class Vector2 { public: Vector2(float x, float y) : x(x), y(y) { print_created(this, toString()); } Vector2(const Vector2 &v) : x(v.x), y(v.y) { print_copy_created(this); } - Vector2(Vector2 &&v) : x(v.x), y(v.y) { print_move_created(this); v.x = v.y = 0; } - Vector2 &operator=(const Vector2 &v) { x = v.x; y = v.y; print_copy_assigned(this); return *this; } - Vector2 &operator=(Vector2 &&v) { x = v.x; y = v.y; v.x = v.y = 0; print_move_assigned(this); return *this; } + Vector2(Vector2 &&v) noexcept : x(v.x), y(v.y) { + print_move_created(this); + v.x = v.y = 0; + } + Vector2 &operator=(const Vector2 &v) { + x = v.x; + y = v.y; + print_copy_assigned(this); + return *this; + } + Vector2 &operator=(Vector2 &&v) noexcept { + x = v.x; + y = v.y; + v.x = v.y = 0; + print_move_assigned(this); + return *this; + } ~Vector2() { print_destroyed(this); } - std::string toString() const { return "[" + std::to_string(x) + ", " + std::to_string(y) + "]"; } + std::string toString() const { + return "[" + std::to_string(x) + ", " + std::to_string(y) + "]"; + } Vector2 operator-() const { return Vector2(-x, -y); } Vector2 operator+(const Vector2 &v) const { return Vector2(x + v.x, y + v.y); } @@ -32,71 +51,104 @@ public: Vector2 operator/(float value) const { return Vector2(x / value, y / value); } Vector2 operator*(const Vector2 &v) const { return Vector2(x * v.x, y * v.y); } Vector2 operator/(const Vector2 &v) const { return Vector2(x / v.x, y / v.y); } - Vector2& operator+=(const Vector2 &v) { x += v.x; y += v.y; return *this; } - Vector2& operator-=(const Vector2 &v) { x -= v.x; y -= v.y; return *this; } - Vector2& operator*=(float v) { x *= v; y *= v; return *this; } - Vector2& operator/=(float v) { x /= v; y /= v; return *this; } - Vector2& operator*=(const Vector2 &v) { x *= v.x; y *= v.y; return *this; } - Vector2& operator/=(const Vector2 &v) { x /= v.x; y /= v.y; return *this; } + Vector2 &operator+=(const Vector2 &v) { + x += v.x; + y += v.y; + return *this; + } + Vector2 &operator-=(const Vector2 &v) { + x -= v.x; + y -= v.y; + return *this; + } + Vector2 &operator*=(float v) { + x *= v; + y *= v; + return *this; + } + Vector2 &operator/=(float v) { + x /= v; + y /= v; + return *this; + } + Vector2 &operator*=(const Vector2 &v) { + x *= v.x; + y *= v.y; + return *this; + } + Vector2 &operator/=(const Vector2 &v) { + x /= v.x; + y /= v.y; + return *this; + } friend Vector2 operator+(float f, const Vector2 &v) { return Vector2(f + v.x, f + v.y); } friend Vector2 operator-(float f, const Vector2 &v) { return Vector2(f - v.x, f - v.y); } friend Vector2 operator*(float f, const Vector2 &v) { return Vector2(f * v.x, f * v.y); } friend Vector2 operator/(float f, const Vector2 &v) { return Vector2(f / v.x, f / v.y); } - bool operator==(const Vector2 &v) const { - return x == v.x && y == v.y; - } - bool operator!=(const Vector2 &v) const { - return x != v.x || y != v.y; - } + bool operator==(const Vector2 &v) const { return x == v.x && y == v.y; } + bool operator!=(const Vector2 &v) const { return x != v.x || y != v.y; } + private: float x, y; }; -class C1 { }; -class C2 { }; +class C1 {}; +class C2 {}; int operator+(const C1 &, const C1 &) { return 11; } int operator+(const C2 &, const C2 &) { return 22; } int operator+(const C2 &, const C1 &) { return 21; } int operator+(const C1 &, const C2 &) { return 12; } +struct HashMe { + std::string member; +}; + +bool operator==(const HashMe &lhs, const HashMe &rhs) { return lhs.member == rhs.member; } + // Note: Specializing explicit within `namespace std { ... }` is done due to a // bug in GCC<7. If you are supporting compilers later than this, consider // specializing `using template<> struct std::hash<...>` in the global // namespace instead, per this recommendation: // https://en.cppreference.com/w/cpp/language/extending_std#Adding_template_specializations namespace std { - template<> - struct hash { - // Not a good hash function, but easy to test - size_t operator()(const Vector2 &) { return 4; } - }; +template <> +struct hash { + // Not a good hash function, but easy to test + size_t operator()(const Vector2 &) { return 4; } +}; + +// HashMe has a hash function in C++ but no `__hash__` for Python. +template <> +struct hash { + std::size_t operator()(const HashMe &selector) const { + return std::hash()(selector.member); + } +}; } // namespace std // Not a good abs function, but easy to test. -std::string abs(const Vector2&) { - return "abs(Vector2)"; -} +std::string abs(const Vector2 &) { return "abs(Vector2)"; } -// MSVC warns about unknown pragmas, and warnings are errors. -#ifndef _MSC_VER - #pragma GCC diagnostic push - // clang 7.0.0 and Apple LLVM 10.0.1 introduce `-Wself-assign-overloaded` to - // `-Wall`, which is used here for overloading (e.g. `py::self += py::self `). - // Here, we suppress the warning using `#pragma diagnostic`. - // Taken from: https://github.com/RobotLocomotion/drake/commit/aaf84b46 - // TODO(eric): This could be resolved using a function / functor (e.g. `py::self()`). - #if defined(__APPLE__) && defined(__clang__) - #if (__clang_major__ >= 10) - #pragma GCC diagnostic ignored "-Wself-assign-overloaded" - #endif - #elif defined(__clang__) - #if (__clang_major__ >= 7) - #pragma GCC diagnostic ignored "-Wself-assign-overloaded" - #endif - #endif +// MSVC & Intel warns about unknown pragmas, and warnings are errors. +#if !defined(_MSC_VER) && !defined(__INTEL_COMPILER) +# pragma GCC diagnostic push +// clang 7.0.0 and Apple LLVM 10.0.1 introduce `-Wself-assign-overloaded` to +// `-Wall`, which is used here for overloading (e.g. `py::self += py::self `). +// Here, we suppress the warning using `#pragma diagnostic`. +// Taken from: https://github.com/RobotLocomotion/drake/commit/aaf84b46 +// TODO(eric): This could be resolved using a function / functor (e.g. `py::self()`). +# if defined(__APPLE__) && defined(__clang__) +# if (__clang_major__ >= 10) +# pragma GCC diagnostic ignored "-Wself-assign-overloaded" +# endif +# elif defined(__clang__) +# if (__clang_major__ >= 7) +# pragma GCC diagnostic ignored "-Wself-assign-overloaded" +# endif +# endif #endif TEST_SUBMODULE(operators, m) { @@ -130,46 +182,52 @@ TEST_SUBMODULE(operators, m) { .def(py::hash(py::self)) // N.B. See warning about usage of `py::detail::abs(py::self)` in // `operators.h`. - .def("__abs__", [](const Vector2& v) { return abs(v); }) - ; + .def("__abs__", [](const Vector2 &v) { return abs(v); }); m.attr("Vector") = m.attr("Vector2"); // test_operators_notimplemented // #393: need to return NotSupported to ensure correct arithmetic operator behavior - py::class_(m, "C1") - .def(py::init<>()) - .def(py::self + py::self); + py::class_(m, "C1").def(py::init<>()).def(py::self + py::self); py::class_(m, "C2") .def(py::init<>()) .def(py::self + py::self) - .def("__add__", [](const C2& c2, const C1& c1) { return c2 + c1; }) - .def("__radd__", [](const C2& c2, const C1& c1) { return c1 + c2; }); + .def("__add__", [](const C2 &c2, const C1 &c1) { return c2 + c1; }) + .def("__radd__", [](const C2 &c2, const C1 &c1) { return c1 + c2; }); // test_nested // #328: first member in a class can't be used in operators - struct NestABase { int value = -2; }; + struct NestABase { + int value = -2; + }; py::class_(m, "NestABase") .def(py::init<>()) .def_readwrite("value", &NestABase::value); struct NestA : NestABase { int value = 3; - NestA& operator+=(int i) { value += i; return *this; } + NestA &operator+=(int i) { + value += i; + return *this; + } }; py::class_(m, "NestA") .def(py::init<>()) .def(py::self += int()) - .def("as_base", [](NestA &a) -> NestABase& { - return (NestABase&) a; - }, py::return_value_policy::reference_internal); + .def( + "as_base", + [](NestA &a) -> NestABase & { return (NestABase &) a; }, + py::return_value_policy::reference_internal); m.def("get_NestA", [](const NestA &a) { return a.value; }); struct NestB { NestA a; int value = 4; - NestB& operator-=(int i) { value -= i; return *this; } + NestB &operator-=(int i) { + value -= i; + return *this; + } }; py::class_(m, "NestB") .def(py::init<>()) @@ -180,7 +238,10 @@ TEST_SUBMODULE(operators, m) { struct NestC { NestB b; int value = 5; - NestC& operator*=(int i) { value *= i; return *this; } + NestC &operator*=(int i) { + value *= i; + return *this; + } }; py::class_(m, "NestC") .def(py::init<>()) @@ -188,16 +249,15 @@ TEST_SUBMODULE(operators, m) { .def_readwrite("b", &NestC::b); m.def("get_NestC", [](const NestC &c) { return c.value; }); - // test_overriding_eq_reset_hash // #2191 Overriding __eq__ should set __hash__ to None struct Comparable { int value; - bool operator==(const Comparable& rhs) const {return value == rhs.value;} + bool operator==(const Comparable &rhs) const { return value == rhs.value; } }; struct Hashable : Comparable { - explicit Hashable(int value): Comparable{value}{}; + explicit Hashable(int value) : Comparable{value} {}; size_t hash() const { return static_cast(value); } }; @@ -205,9 +265,7 @@ TEST_SUBMODULE(operators, m) { using Hashable::Hashable; }; - py::class_(m, "Comparable") - .def(py::init()) - .def(py::self == py::self); + py::class_(m, "Comparable").def(py::init()).def(py::self == py::self); py::class_(m, "Hashable") .def(py::init()) @@ -219,8 +277,12 @@ TEST_SUBMODULE(operators, m) { .def("__hash__", &Hashable::hash) .def(py::init()) .def(py::self == py::self); -} -#ifndef _MSC_VER - #pragma GCC diagnostic pop + // define __eq__ but not __hash__ + py::class_(m, "HashMe").def(py::self == py::self); + + m.def("get_unhashable_HashMe_set", []() { return std::unordered_set{{"one"}}; }); +} +#if !defined(_MSC_VER) && !defined(__INTEL_COMPILER) +# pragma GCC diagnostic pop #endif diff --git a/3rdparty/pybind11/tests/test_operator_overloading.py b/3rdparty/pybind11/tests/test_operator_overloading.py index 5dbfb32c..b228da3c 100644 --- a/3rdparty/pybind11/tests/test_operator_overloading.py +++ b/3rdparty/pybind11/tests/test_operator_overloading.py @@ -1,7 +1,7 @@ -# -*- coding: utf-8 -*- import pytest -from pybind11_tests import operators as m + from pybind11_tests import ConstructorStats +from pybind11_tests import operators as m def test_operator_overloading(): @@ -134,8 +134,9 @@ def test_overriding_eq_reset_hash(): assert m.Comparable(15) is not m.Comparable(15) assert m.Comparable(15) == m.Comparable(15) - with pytest.raises(TypeError): - hash(m.Comparable(15)) # TypeError: unhashable type: 'm.Comparable' + with pytest.raises(TypeError) as excinfo: + hash(m.Comparable(15)) + assert str(excinfo.value).startswith("unhashable type:") for hashable in (m.Hashable, m.Hashable2): assert hashable(15) is not hashable(15) @@ -143,3 +144,9 @@ def test_overriding_eq_reset_hash(): assert hash(hashable(15)) == 15 assert hash(hashable(15)) == hash(hashable(15)) + + +def test_return_set_of_unhashable(): + with pytest.raises(TypeError) as excinfo: + m.get_unhashable_HashMe_set() + assert str(excinfo.value.__cause__).startswith("unhashable type:") diff --git a/3rdparty/pybind11/tests/test_pickling.cpp b/3rdparty/pybind11/tests/test_pickling.cpp index 9dc63bda..e154bc48 100644 --- a/3rdparty/pybind11/tests/test_pickling.cpp +++ b/3rdparty/pybind11/tests/test_pickling.cpp @@ -2,6 +2,7 @@ tests/test_pickling.cpp -- pickle support Copyright (c) 2016 Wenzel Jakob + Copyright (c) 2021 The Pybind Development Team. All rights reserved. Use of this source code is governed by a BSD-style license that can be found in the LICENSE file. @@ -9,17 +10,70 @@ #include "pybind11_tests.h" +#include +#include +#include + +namespace exercise_trampoline { + +struct SimpleBase { + int num = 0; + virtual ~SimpleBase() = default; + + // For compatibility with old clang versions: + SimpleBase() = default; + SimpleBase(const SimpleBase &) = default; +}; + +struct SimpleBaseTrampoline : SimpleBase {}; + +struct SimpleCppDerived : SimpleBase {}; + +void wrap(py::module m) { + py::class_(m, "SimpleBase") + .def(py::init<>()) + .def_readwrite("num", &SimpleBase::num) + .def(py::pickle( + [](const py::object &self) { + py::dict d; + if (py::hasattr(self, "__dict__")) { + d = self.attr("__dict__"); + } + return py::make_tuple(self.attr("num"), d); + }, + [](const py::tuple &t) { + if (t.size() != 2) { + throw std::runtime_error("Invalid state!"); + } + auto cpp_state = std::unique_ptr(new SimpleBaseTrampoline); + cpp_state->num = t[0].cast(); + auto py_state = t[1].cast(); + return std::make_pair(std::move(cpp_state), py_state); + })); + + m.def("make_SimpleCppDerivedAsBase", + []() { return std::unique_ptr(new SimpleCppDerived); }); + m.def("check_dynamic_cast_SimpleCppDerived", [](const SimpleBase *base_ptr) { + return dynamic_cast(base_ptr) != nullptr; + }); +} + +} // namespace exercise_trampoline + TEST_SUBMODULE(pickling, m) { + m.def("simple_callable", []() { return 20220426; }); + // test_roundtrip class Pickleable { public: - Pickleable(const std::string &value) : m_value(value) { } + explicit Pickleable(const std::string &value) : m_value(value) {} const std::string &value() const { return m_value; } void setExtra1(int extra1) { m_extra1 = extra1; } void setExtra2(int extra2) { m_extra2 = extra2; } int extra1() const { return m_extra1; } int extra2() const { return m_extra2; } + private: std::string m_value; int m_extra1 = 0; @@ -31,8 +85,8 @@ TEST_SUBMODULE(pickling, m) { using Pickleable::Pickleable; }; - py::class_(m, "Pickleable") - .def(py::init()) + py::class_ pyPickleable(m, "Pickleable"); + pyPickleable.def(py::init()) .def("value", &Pickleable::value) .def("extra1", &Pickleable::extra1) .def("extra2", &Pickleable::extra2) @@ -43,10 +97,12 @@ TEST_SUBMODULE(pickling, m) { .def("__getstate__", [](const Pickleable &p) { /* Return a tuple that fully encodes the state of the object */ return py::make_tuple(p.value(), p.extra1(), p.extra2()); - }) - .def("__setstate__", [](Pickleable &p, py::tuple t) { - if (t.size() != 3) + }); + ignoreOldStyleInitWarnings([&pyPickleable]() { + pyPickleable.def("__setstate__", [](Pickleable &p, const py::tuple &t) { + if (t.size() != 3) { throw std::runtime_error("Invalid state!"); + } /* Invoke the constructor (need to use in-place version) */ new (&p) Pickleable(t[0].cast()); @@ -54,6 +110,7 @@ TEST_SUBMODULE(pickling, m) { p.setExtra1(t[1].cast()); p.setExtra2(t[2].cast()); }); + }); py::class_(m, "PickleableNew") .def(py::init()) @@ -61,22 +118,22 @@ TEST_SUBMODULE(pickling, m) { [](const PickleableNew &p) { return py::make_tuple(p.value(), p.extra1(), p.extra2()); }, - [](py::tuple t) { - if (t.size() != 3) + [](const py::tuple &t) { + if (t.size() != 3) { throw std::runtime_error("Invalid state!"); + } auto p = PickleableNew(t[0].cast()); p.setExtra1(t[1].cast()); p.setExtra2(t[2].cast()); return p; - } - )); + })); #if !defined(PYPY_VERSION) // test_roundtrip_with_dict class PickleableWithDict { public: - PickleableWithDict(const std::string &value) : value(value) { } + explicit PickleableWithDict(const std::string &value) : value(value) {} std::string value; int extra; @@ -87,19 +144,22 @@ TEST_SUBMODULE(pickling, m) { using PickleableWithDict::PickleableWithDict; }; - py::class_(m, "PickleableWithDict", py::dynamic_attr()) - .def(py::init()) + py::class_ pyPickleableWithDict( + m, "PickleableWithDict", py::dynamic_attr()); + pyPickleableWithDict.def(py::init()) .def_readwrite("value", &PickleableWithDict::value) .def_readwrite("extra", &PickleableWithDict::extra) - .def("__getstate__", [](py::object self) { + .def("__getstate__", [](const py::object &self) { /* Also include __dict__ in state */ return py::make_tuple(self.attr("value"), self.attr("extra"), self.attr("__dict__")); - }) - .def("__setstate__", [](py::object self, py::tuple t) { - if (t.size() != 3) + }); + ignoreOldStyleInitWarnings([&pyPickleableWithDict]() { + pyPickleableWithDict.def("__setstate__", [](const py::object &self, const py::tuple &t) { + if (t.size() != 3) { throw std::runtime_error("Invalid state!"); + } /* Cast and construct */ - auto& p = self.cast(); + auto &p = self.cast(); new (&p) PickleableWithDict(t[0].cast()); /* Assign C++ state */ @@ -108,23 +168,27 @@ TEST_SUBMODULE(pickling, m) { /* Assign Python state */ self.attr("__dict__") = t[2]; }); + }); py::class_(m, "PickleableWithDictNew") .def(py::init()) .def(py::pickle( - [](py::object self) { - return py::make_tuple(self.attr("value"), self.attr("extra"), self.attr("__dict__")); + [](const py::object &self) { + return py::make_tuple( + self.attr("value"), self.attr("extra"), self.attr("__dict__")); }, [](const py::tuple &t) { - if (t.size() != 3) + if (t.size() != 3) { throw std::runtime_error("Invalid state!"); + } auto cpp_state = PickleableWithDictNew(t[0].cast()); cpp_state.extra = t[1].cast(); auto py_state = t[2].cast(); return std::make_pair(cpp_state, py_state); - } - )); + })); #endif + + exercise_trampoline::wrap(m); } diff --git a/3rdparty/pybind11/tests/test_pickling.py b/3rdparty/pybind11/tests/test_pickling.py index 6b27a73a..12361a66 100644 --- a/3rdparty/pybind11/tests/test_pickling.py +++ b/3rdparty/pybind11/tests/test_pickling.py @@ -1,14 +1,24 @@ -# -*- coding: utf-8 -*- -import pytest +import pickle +import re -import env # noqa: F401 +import pytest +import env from pybind11_tests import pickling as m -try: - import cPickle as pickle # Use cPickle on Python 2.7 -except ImportError: - import pickle + +def test_pickle_simple_callable(): + assert m.simple_callable() == 20220426 + if env.PYPY: + serialized = pickle.dumps(m.simple_callable) + deserialized = pickle.loads(serialized) + assert deserialized() == 20220426 + else: + # To document broken behavior: currently it fails universally with + # all C Python versions. + with pytest.raises(TypeError) as excinfo: + pickle.dumps(m.simple_callable) + assert re.search("can.*t pickle .*PyCapsule.* object", str(excinfo.value)) @pytest.mark.parametrize("cls_name", ["Pickleable", "PickleableNew"]) @@ -45,3 +55,39 @@ def test_enum_pickle(): data = pickle.dumps(e.EOne, 2) assert e.EOne == pickle.loads(data) + + +# +# exercise_trampoline +# +class SimplePyDerived(m.SimpleBase): + pass + + +def test_roundtrip_simple_py_derived(): + p = SimplePyDerived() + p.num = 202 + p.stored_in_dict = 303 + data = pickle.dumps(p, pickle.HIGHEST_PROTOCOL) + p2 = pickle.loads(data) + assert isinstance(p2, SimplePyDerived) + assert p2.num == 202 + assert p2.stored_in_dict == 303 + + +def test_roundtrip_simple_cpp_derived(): + p = m.make_SimpleCppDerivedAsBase() + assert m.check_dynamic_cast_SimpleCppDerived(p) + p.num = 404 + if not env.PYPY: + # To ensure that this unit test is not accidentally invalidated. + with pytest.raises(AttributeError): + # Mimics the `setstate` C++ implementation. + setattr(p, "__dict__", {}) # noqa: B010 + data = pickle.dumps(p, pickle.HIGHEST_PROTOCOL) + p2 = pickle.loads(data) + assert isinstance(p2, m.SimpleBase) + assert p2.num == 404 + # Issue #3062: pickleable base C++ classes can incur object slicing + # if derived typeid is not registered with pybind11 + assert not m.check_dynamic_cast_SimpleCppDerived(p2) diff --git a/3rdparty/pybind11/tests/test_pytypes.cpp b/3rdparty/pybind11/tests/test_pytypes.cpp index 113cf5cb..da0dc8f6 100644 --- a/3rdparty/pybind11/tests/test_pytypes.cpp +++ b/3rdparty/pybind11/tests/test_pytypes.cpp @@ -9,15 +9,119 @@ #include "pybind11_tests.h" +#include + +namespace external { +namespace detail { +bool check(PyObject *o) { return PyFloat_Check(o) != 0; } + +PyObject *conv(PyObject *o) { + PyObject *ret = nullptr; + if (PyLong_Check(o)) { + double v = PyLong_AsDouble(o); + if (!(v == -1.0 && PyErr_Occurred())) { + ret = PyFloat_FromDouble(v); + } + } else { + PyErr_SetString(PyExc_TypeError, "Unexpected type"); + } + return ret; +} + +PyObject *default_constructed() { return PyFloat_FromDouble(0.0); } +} // namespace detail +class float_ : public py::object { + PYBIND11_OBJECT_CVT(float_, py::object, external::detail::check, external::detail::conv) + + float_() : py::object(external::detail::default_constructed(), stolen_t{}) {} + + double get_value() const { return PyFloat_AsDouble(this->ptr()); } +}; +} // namespace external + +namespace implicit_conversion_from_0_to_handle { +// Uncomment to trigger compiler error. Note: Before PR #4008 this used to compile successfully. +// void expected_to_trigger_compiler_error() { py::handle(0); } +} // namespace implicit_conversion_from_0_to_handle + +// Used to validate systematically that PR #4008 does/did NOT change the behavior. +void pure_compile_tests_for_handle_from_PyObject_pointers() { + { + PyObject *ptr = Py_None; + py::handle{ptr}; + } + { + PyObject *const ptr = Py_None; + py::handle{ptr}; + } + // Uncomment to trigger compiler errors. + // PyObject const * ptr = Py_None; py::handle{ptr}; + // PyObject const *const ptr = Py_None; py::handle{ptr}; + // PyObject volatile * ptr = Py_None; py::handle{ptr}; + // PyObject volatile *const ptr = Py_None; py::handle{ptr}; + // PyObject const volatile * ptr = Py_None; py::handle{ptr}; + // PyObject const volatile *const ptr = Py_None; py::handle{ptr}; +} + +namespace handle_from_move_only_type_with_operator_PyObject { + +// Reduced from +// https://github.com/pytorch/pytorch/blob/279634f384662b7c3a9f8bf7ccc3a6afd2f05657/torch/csrc/utils/object_ptr.h +struct operator_ncnst { + operator_ncnst() = default; + operator_ncnst(operator_ncnst &&) = default; + operator PyObject *() /* */ { return Py_None; } // NOLINT(google-explicit-constructor) +}; + +struct operator_const { + operator_const() = default; + operator_const(operator_const &&) = default; + operator PyObject *() const { return Py_None; } // NOLINT(google-explicit-constructor) +}; + +bool from_ncnst() { + operator_ncnst obj; + auto h = py::handle(obj); // Critical part of test: does this compile? + return h.ptr() == Py_None; // Just something. +} + +bool from_const() { + operator_const obj; + auto h = py::handle(obj); // Critical part of test: does this compile? + return h.ptr() == Py_None; // Just something. +} + +void m_defs(py::module_ &m) { + m.def("handle_from_move_only_type_with_operator_PyObject_ncnst", from_ncnst); + m.def("handle_from_move_only_type_with_operator_PyObject_const", from_const); +} + +} // namespace handle_from_move_only_type_with_operator_PyObject TEST_SUBMODULE(pytypes, m) { + handle_from_move_only_type_with_operator_PyObject::m_defs(m); + + // test_bool + m.def("get_bool", [] { return py::bool_(false); }); // test_int - m.def("get_int", []{return py::int_(0);}); + m.def("get_int", [] { return py::int_(0); }); // test_iterator - m.def("get_iterator", []{return py::iterator();}); + m.def("get_iterator", [] { return py::iterator(); }); // test_iterable - m.def("get_iterable", []{return py::iterable();}); + m.def("get_iterable", [] { return py::iterable(); }); + m.def("get_frozenset_from_iterable", + [](const py::iterable &iter) { return py::frozenset(iter); }); + m.def("get_list_from_iterable", [](const py::iterable &iter) { return py::list(iter); }); + m.def("get_set_from_iterable", [](const py::iterable &iter) { return py::set(iter); }); + m.def("get_tuple_from_iterable", [](const py::iterable &iter) { return py::tuple(iter); }); + // test_float + m.def("get_float", [] { return py::float_(0.0f); }); // test_list + m.def("list_no_args", []() { return py::list{}; }); + m.def("list_ssize_t", []() { return py::list{(py::ssize_t) 0}; }); + m.def("list_size_t", []() { return py::list{(py::size_t) 0}; }); + m.def("list_insert_ssize_t", [](py::list *l) { return l->insert((py::ssize_t) 1, 83); }); + m.def("list_insert_size_t", [](py::list *l) { return l->insert((py::size_t) 3, 57); }); m.def("get_list", []() { py::list list; list.append("value"); @@ -27,18 +131,17 @@ TEST_SUBMODULE(pytypes, m) { list.insert(2, "inserted-2"); return list; }); - m.def("print_list", [](py::list list) { + m.def("print_list", [](const py::list &list) { int index = 0; - for (auto item : list) + for (auto item : list) { py::print("list item {}: {}"_s.format(index++, item)); + } }); // test_none - m.def("get_none", []{return py::none();}); - m.def("print_none", [](py::none none) { - py::print("none: {}"_s.format(none)); - }); + m.def("get_none", [] { return py::none(); }); + m.def("print_none", [](const py::none &none) { py::print("none: {}"_s.format(none)); }); - // test_set + // test_set, test_frozenset m.def("get_set", []() { py::set set; set.add(py::str("key1")); @@ -46,58 +149,101 @@ TEST_SUBMODULE(pytypes, m) { set.add(std::string("key3")); return set; }); - m.def("print_set", [](py::set set) { - for (auto item : set) - py::print("key:", item); - }); - m.def("set_contains", [](py::set set, py::object key) { - return set.contains(key); + m.def("get_frozenset", []() { + py::set set; + set.add(py::str("key1")); + set.add("key2"); + set.add(std::string("key3")); + return py::frozenset(set); }); - m.def("set_contains", [](py::set set, const char* key) { - return set.contains(key); + m.def("print_anyset", [](const py::anyset &set) { + for (auto item : set) { + py::print("key:", item); + } }); + m.def("anyset_size", [](const py::anyset &set) { return set.size(); }); + m.def("anyset_empty", [](const py::anyset &set) { return set.empty(); }); + m.def("anyset_contains", + [](const py::anyset &set, const py::object &key) { return set.contains(key); }); + m.def("anyset_contains", + [](const py::anyset &set, const char *key) { return set.contains(key); }); + m.def("set_add", [](py::set &set, const py::object &key) { set.add(key); }); + m.def("set_clear", [](py::set &set) { set.clear(); }); // test_dict - m.def("get_dict", []() { return py::dict("key"_a="value"); }); - m.def("print_dict", [](py::dict dict) { - for (auto item : dict) + m.def("get_dict", []() { return py::dict("key"_a = "value"); }); + m.def("print_dict", [](const py::dict &dict) { + for (auto item : dict) { py::print("key: {}, value={}"_s.format(item.first, item.second)); + } }); m.def("dict_keyword_constructor", []() { - auto d1 = py::dict("x"_a=1, "y"_a=2); - auto d2 = py::dict("z"_a=3, **d1); + auto d1 = py::dict("x"_a = 1, "y"_a = 2); + auto d2 = py::dict("z"_a = 3, **d1); return d2; }); - m.def("dict_contains", [](py::dict dict, py::object val) { - return dict.contains(val); - }); - m.def("dict_contains", [](py::dict dict, const char* val) { - return dict.contains(val); + m.def("dict_contains", + [](const py::dict &dict, py::object val) { return dict.contains(val); }); + m.def("dict_contains", + [](const py::dict &dict, const char *val) { return dict.contains(val); }); + + // test_tuple + m.def("tuple_no_args", []() { return py::tuple{}; }); + m.def("tuple_ssize_t", []() { return py::tuple{(py::ssize_t) 0}; }); + m.def("tuple_size_t", []() { return py::tuple{(py::size_t) 0}; }); + m.def("get_tuple", []() { return py::make_tuple(42, py::none(), "spam"); }); + + // test_simple_namespace + m.def("get_simple_namespace", []() { + auto ns = py::module_::import("types").attr("SimpleNamespace")( + "attr"_a = 42, "x"_a = "foo", "wrong"_a = 1); + py::delattr(ns, "wrong"); + py::setattr(ns, "right", py::int_(2)); + return ns; }); // test_str + m.def("str_from_char_ssize_t", []() { return py::str{"red", (py::ssize_t) 3}; }); + m.def("str_from_char_size_t", []() { return py::str{"blue", (py::size_t) 4}; }); m.def("str_from_string", []() { return py::str(std::string("baz")); }); m.def("str_from_bytes", []() { return py::str(py::bytes("boo", 3)); }); - m.def("str_from_object", [](const py::object& obj) { return py::str(obj); }); - m.def("repr_from_object", [](const py::object& obj) { return py::repr(obj); }); + m.def("str_from_object", [](const py::object &obj) { return py::str(obj); }); + m.def("repr_from_object", [](const py::object &obj) { return py::repr(obj); }); m.def("str_from_handle", [](py::handle h) { return py::str(h); }); + m.def("str_from_string_from_str", + [](const py::str &obj) { return py::str(static_cast(obj)); }); m.def("str_format", []() { auto s1 = "{} + {} = {}"_s.format(1, 2, 3); - auto s2 = "{a} + {b} = {c}"_s.format("a"_a=1, "b"_a=2, "c"_a=3); + auto s2 = "{a} + {b} = {c}"_s.format("a"_a = 1, "b"_a = 2, "c"_a = 3); return py::make_tuple(s1, s2); }); // test_bytes + m.def("bytes_from_char_ssize_t", []() { return py::bytes{"green", (py::ssize_t) 5}; }); + m.def("bytes_from_char_size_t", []() { return py::bytes{"purple", (py::size_t) 6}; }); m.def("bytes_from_string", []() { return py::bytes(std::string("foo")); }); m.def("bytes_from_str", []() { return py::bytes(py::str("bar", 3)); }); + // test bytearray + m.def("bytearray_from_char_ssize_t", []() { return py::bytearray{"$%", (py::ssize_t) 2}; }); + m.def("bytearray_from_char_size_t", []() { return py::bytearray{"@$!", (py::size_t) 3}; }); + m.def("bytearray_from_string", []() { return py::bytearray(std::string("foo")); }); + m.def("bytearray_size", []() { return py::bytearray("foo").size(); }); + // test_capsule m.def("return_capsule_with_destructor", []() { py::print("creating capsule"); - return py::capsule([]() { - py::print("destructing capsule"); - }); + return py::capsule([]() { py::print("destructing capsule"); }); + }); + + m.def("return_renamed_capsule_with_destructor", []() { + py::print("creating capsule"); + auto cap = py::capsule([]() { py::print("destructing capsule"); }); + static const char *capsule_name = "test_name1"; + py::print("renaming capsule"); + cap.set_name(capsule_name); + return cap; }); m.def("return_capsule_with_destructor_2", []() { @@ -107,33 +253,44 @@ TEST_SUBMODULE(pytypes, m) { }); }); + m.def("return_renamed_capsule_with_destructor_2", []() { + py::print("creating capsule"); + auto cap = py::capsule((void *) 1234, [](void *ptr) { + py::print("destructing capsule: {}"_s.format((size_t) ptr)); + }); + static const char *capsule_name = "test_name2"; + py::print("renaming capsule"); + cap.set_name(capsule_name); + return cap; + }); + m.def("return_capsule_with_name_and_destructor", []() { auto capsule = py::capsule((void *) 12345, "pointer type description", [](PyObject *ptr) { if (ptr) { - auto name = PyCapsule_GetName(ptr); + const auto *name = PyCapsule_GetName(ptr); py::print("destructing capsule ({}, '{}')"_s.format( - (size_t) PyCapsule_GetPointer(ptr, name), name - )); + (size_t) PyCapsule_GetPointer(ptr, name), name)); } }); capsule.set_pointer((void *) 1234); // Using get_pointer() - void* contents1 = static_cast(capsule); - void* contents2 = capsule.get_pointer(); - void* contents3 = capsule.get_pointer(); + void *contents1 = static_cast(capsule); + void *contents2 = capsule.get_pointer(); + void *contents3 = capsule.get_pointer(); auto result1 = reinterpret_cast(contents1); auto result2 = reinterpret_cast(contents2); auto result3 = reinterpret_cast(contents3); - py::print("created capsule ({}, '{}')"_s.format(result1 & result2 & result3, capsule.name())); + py::print( + "created capsule ({}, '{}')"_s.format(result1 & result2 & result3, capsule.name())); return capsule; }); // test_accessors - m.def("accessor_api", [](py::object o) { + m.def("accessor_api", [](const py::object &o) { auto d = py::dict(); d["basic_attr"] = o.attr("basic_attr"); @@ -174,7 +331,7 @@ TEST_SUBMODULE(pytypes, m) { return d; }); - m.def("tuple_accessor", [](py::tuple existing_t) { + m.def("tuple_accessor", [](const py::tuple &existing_t) { try { existing_t[0] = 1; } catch (const py::error_already_set &) { @@ -206,66 +363,118 @@ TEST_SUBMODULE(pytypes, m) { return d; }); + m.def("accessor_moves", []() { // See PR #3970 + py::list return_list; +#ifdef PYBIND11_HANDLE_REF_DEBUG + py::int_ py_int_0(0); + py::int_ py_int_42(42); + py::str py_str_count("count"); + + auto tup = py::make_tuple(0); + + py::sequence seq(tup); + + py::list lst; + lst.append(0); + +# define PYBIND11_LOCAL_DEF(...) \ + { \ + std::size_t inc_refs = py::handle::inc_ref_counter(); \ + __VA_ARGS__; \ + inc_refs = py::handle::inc_ref_counter() - inc_refs; \ + return_list.append(inc_refs); \ + } + + PYBIND11_LOCAL_DEF(tup[py_int_0]) // l-value (to have a control) + PYBIND11_LOCAL_DEF(tup[py::int_(0)]) // r-value + + PYBIND11_LOCAL_DEF(tup.attr(py_str_count)) // l-value + PYBIND11_LOCAL_DEF(tup.attr(py::str("count"))) // r-value + + PYBIND11_LOCAL_DEF(seq[py_int_0]) // l-value + PYBIND11_LOCAL_DEF(seq[py::int_(0)]) // r-value + + PYBIND11_LOCAL_DEF(seq.attr(py_str_count)) // l-value + PYBIND11_LOCAL_DEF(seq.attr(py::str("count"))) // r-value + + PYBIND11_LOCAL_DEF(lst[py_int_0]) // l-value + PYBIND11_LOCAL_DEF(lst[py::int_(0)]) // r-value + + PYBIND11_LOCAL_DEF(lst.attr(py_str_count)) // l-value + PYBIND11_LOCAL_DEF(lst.attr(py::str("count"))) // r-value + + auto lst_acc = lst[py::int_(0)]; + lst_acc = py::int_(42); // Detaches lst_acc from lst. + PYBIND11_LOCAL_DEF(lst_acc = py_int_42) // l-value + PYBIND11_LOCAL_DEF(lst_acc = py::int_(42)) // r-value +# undef PYBIND11_LOCAL_DEF +#endif + return return_list; + }); + // test_constructors m.def("default_constructors", []() { - return py::dict( - "bytes"_a=py::bytes(), - "str"_a=py::str(), - "bool"_a=py::bool_(), - "int"_a=py::int_(), - "float"_a=py::float_(), - "tuple"_a=py::tuple(), - "list"_a=py::list(), - "dict"_a=py::dict(), - "set"_a=py::set() - ); - }); - - m.def("converting_constructors", [](py::dict d) { - return py::dict( - "bytes"_a=py::bytes(d["bytes"]), - "str"_a=py::str(d["str"]), - "bool"_a=py::bool_(d["bool"]), - "int"_a=py::int_(d["int"]), - "float"_a=py::float_(d["float"]), - "tuple"_a=py::tuple(d["tuple"]), - "list"_a=py::list(d["list"]), - "dict"_a=py::dict(d["dict"]), - "set"_a=py::set(d["set"]), - "memoryview"_a=py::memoryview(d["memoryview"]) - ); - }); - - m.def("cast_functions", [](py::dict d) { + return py::dict("bytes"_a = py::bytes(), + "bytearray"_a = py::bytearray(), + "str"_a = py::str(), + "bool"_a = py::bool_(), + "int"_a = py::int_(), + "float"_a = py::float_(), + "tuple"_a = py::tuple(), + "list"_a = py::list(), + "dict"_a = py::dict(), + "set"_a = py::set()); + }); + + m.def("converting_constructors", [](const py::dict &d) { + return py::dict("bytes"_a = py::bytes(d["bytes"]), + "bytearray"_a = py::bytearray(d["bytearray"]), + "str"_a = py::str(d["str"]), + "bool"_a = py::bool_(d["bool"]), + "int"_a = py::int_(d["int"]), + "float"_a = py::float_(d["float"]), + "tuple"_a = py::tuple(d["tuple"]), + "list"_a = py::list(d["list"]), + "dict"_a = py::dict(d["dict"]), + "set"_a = py::set(d["set"]), + "frozenset"_a = py::frozenset(d["frozenset"]), + "memoryview"_a = py::memoryview(d["memoryview"])); + }); + + m.def("cast_functions", [](const py::dict &d) { // When converting between Python types, obj.cast() should be the same as T(obj) - return py::dict( - "bytes"_a=d["bytes"].cast(), - "str"_a=d["str"].cast(), - "bool"_a=d["bool"].cast(), - "int"_a=d["int"].cast(), - "float"_a=d["float"].cast(), - "tuple"_a=d["tuple"].cast(), - "list"_a=d["list"].cast(), - "dict"_a=d["dict"].cast(), - "set"_a=d["set"].cast(), - "memoryview"_a=d["memoryview"].cast() - ); - }); - - m.def("convert_to_pybind11_str", [](py::object o) { return py::str(o); }); - - m.def("nonconverting_constructor", [](std::string type, py::object value) -> py::object { - if (type == "bytes") { - return py::bytes(value); - } - else if (type == "none") { - return py::none(value); - } - else if (type == "ellipsis") { - return py::ellipsis(value); - } - throw std::runtime_error("Invalid type"); - }); + return py::dict("bytes"_a = d["bytes"].cast(), + "bytearray"_a = d["bytearray"].cast(), + "str"_a = d["str"].cast(), + "bool"_a = d["bool"].cast(), + "int"_a = d["int"].cast(), + "float"_a = d["float"].cast(), + "tuple"_a = d["tuple"].cast(), + "list"_a = d["list"].cast(), + "dict"_a = d["dict"].cast(), + "set"_a = d["set"].cast(), + "frozenset"_a = d["frozenset"].cast(), + "memoryview"_a = d["memoryview"].cast()); + }); + + m.def("convert_to_pybind11_str", [](const py::object &o) { return py::str(o); }); + + m.def("nonconverting_constructor", + [](const std::string &type, py::object value, bool move) -> py::object { + if (type == "bytes") { + return move ? py::bytes(std::move(value)) : py::bytes(value); + } + if (type == "none") { + return move ? py::none(std::move(value)) : py::none(value); + } + if (type == "ellipsis") { + return move ? py::ellipsis(std::move(value)) : py::ellipsis(value); + } + if (type == "type") { + return move ? py::type(std::move(value)) : py::type(value); + } + throw std::runtime_error("Invalid type"); + }); m.def("get_implicit_casting", []() { py::dict d; @@ -298,10 +507,7 @@ TEST_SUBMODULE(pytypes, m) { l.append(py::cast(12)); l.append(py::int_(15)); - return py::dict( - "d"_a=d, - "l"_a=l - ); + return py::dict("d"_a = d, "l"_a = l); }); // test_print @@ -309,23 +515,24 @@ TEST_SUBMODULE(pytypes, m) { py::print("Hello, World!"); py::print(1, 2.0, "three", true, std::string("-- multiple args")); auto args = py::make_tuple("and", "a", "custom", "separator"); - py::print("*args", *args, "sep"_a="-"); - py::print("no new line here", "end"_a=" -- "); + py::print("*args", *args, "sep"_a = "-"); + py::print("no new line here", "end"_a = " -- "); py::print("next print"); auto py_stderr = py::module_::import("sys").attr("stderr"); - py::print("this goes to stderr", "file"_a=py_stderr); + py::print("this goes to stderr", "file"_a = py_stderr); - py::print("flush", "flush"_a=true); + py::print("flush", "flush"_a = true); - py::print("{a} + {b} = {c}"_s.format("a"_a="py::print", "b"_a="str.format", "c"_a="this")); + py::print( + "{a} + {b} = {c}"_s.format("a"_a = "py::print", "b"_a = "str.format", "c"_a = "this")); }); m.def("print_failure", []() { py::print(42, UnregisteredType()); }); - m.def("hash_function", [](py::object obj) { return py::hash(obj); }); + m.def("hash_function", [](py::object obj) { return py::hash(std::move(obj)); }); - m.def("test_number_protocol", [](py::object a, py::object b) { + m.def("test_number_protocol", [](const py::object &a, const py::object &b) { py::list l; l.append(a.equal(b)); l.append(a.not_equal(b)); @@ -345,9 +552,7 @@ TEST_SUBMODULE(pytypes, m) { return l; }); - m.def("test_list_slicing", [](py::list a) { - return a[py::slice(0, -1, 2)]; - }); + m.def("test_list_slicing", [](const py::list &a) { return a[py::slice(0, -1, 2)]; }); // See #2361 m.def("issue2361_str_implicit_copy_none", []() { @@ -359,55 +564,235 @@ TEST_SUBMODULE(pytypes, m) { return is_this_none; }); - m.def("test_memoryview_object", [](py::buffer b) { - return py::memoryview(b); - }); + m.def("test_memoryview_object", [](const py::buffer &b) { return py::memoryview(b); }); - m.def("test_memoryview_buffer_info", [](py::buffer b) { - return py::memoryview(b.request()); - }); + m.def("test_memoryview_buffer_info", + [](const py::buffer &b) { return py::memoryview(b.request()); }); m.def("test_memoryview_from_buffer", [](bool is_unsigned) { - static const int16_t si16[] = { 3, 1, 4, 1, 5 }; - static const uint16_t ui16[] = { 2, 7, 1, 8 }; - if (is_unsigned) - return py::memoryview::from_buffer( - ui16, { 4 }, { sizeof(uint16_t) }); - else - return py::memoryview::from_buffer( - si16, { 5 }, { sizeof(int16_t) }); + static const int16_t si16[] = {3, 1, 4, 1, 5}; + static const uint16_t ui16[] = {2, 7, 1, 8}; + if (is_unsigned) { + return py::memoryview::from_buffer(ui16, {4}, {sizeof(uint16_t)}); + } + return py::memoryview::from_buffer(si16, {5}, {sizeof(int16_t)}); }); m.def("test_memoryview_from_buffer_nativeformat", []() { - static const char* format = "@i"; - static const int32_t arr[] = { 4, 7, 5 }; - return py::memoryview::from_buffer( - arr, sizeof(int32_t), format, { 3 }, { sizeof(int32_t) }); + static const char *format = "@i"; + static const int32_t arr[] = {4, 7, 5}; + return py::memoryview::from_buffer(arr, sizeof(int32_t), format, {3}, {sizeof(int32_t)}); }); m.def("test_memoryview_from_buffer_empty_shape", []() { - static const char* buf = ""; - return py::memoryview::from_buffer(buf, 1, "B", { }, { }); + static const char *buf = ""; + return py::memoryview::from_buffer(buf, 1, "B", {}, {}); }); m.def("test_memoryview_from_buffer_invalid_strides", []() { - static const char* buf = "\x02\x03\x04"; - return py::memoryview::from_buffer(buf, 1, "B", { 3 }, { }); + static const char *buf = "\x02\x03\x04"; + return py::memoryview::from_buffer(buf, 1, "B", {3}, {}); }); m.def("test_memoryview_from_buffer_nullptr", []() { - return py::memoryview::from_buffer( - static_cast(nullptr), 1, "B", { }, { }); + return py::memoryview::from_buffer(static_cast(nullptr), 1, "B", {}, {}); }); -#if PY_MAJOR_VERSION >= 3 m.def("test_memoryview_from_memory", []() { - const char* buf = "\xff\xe1\xab\x37"; - return py::memoryview::from_memory( - buf, static_cast(strlen(buf))); + const char *buf = "\xff\xe1\xab\x37"; + return py::memoryview::from_memory(buf, static_cast(strlen(buf))); }); -#endif // test_builtin_functions m.def("get_len", [](py::handle h) { return py::len(h); }); + +#ifdef PYBIND11_STR_LEGACY_PERMISSIVE + m.attr("PYBIND11_STR_LEGACY_PERMISSIVE") = true; +#endif + + m.def("isinstance_pybind11_bytes", + [](py::object o) { return py::isinstance(std::move(o)); }); + m.def("isinstance_pybind11_str", + [](py::object o) { return py::isinstance(std::move(o)); }); + + m.def("pass_to_pybind11_bytes", [](py::bytes b) { return py::len(std::move(b)); }); + m.def("pass_to_pybind11_str", [](py::str s) { return py::len(std::move(s)); }); + m.def("pass_to_std_string", [](const std::string &s) { return s.size(); }); + + // test_weakref + m.def("weakref_from_handle", [](py::handle h) { return py::weakref(h); }); + m.def("weakref_from_handle_and_function", + [](py::handle h, py::function f) { return py::weakref(h, std::move(f)); }); + m.def("weakref_from_object", [](const py::object &o) { return py::weakref(o); }); + m.def("weakref_from_object_and_function", + [](py::object o, py::function f) { return py::weakref(std::move(o), std::move(f)); }); + +// See PR #3263 for background (https://github.com/pybind/pybind11/pull/3263): +// pytypes.h could be changed to enforce the "most correct" user code below, by removing +// `const` from iterator `reference` using type aliases, but that will break existing +// user code. +#if (defined(__APPLE__) && defined(__clang__)) || defined(PYPY_VERSION) +// This is "most correct" and enforced on these platforms. +# define PYBIND11_AUTO_IT auto it +#else +// This works on many platforms and is (unfortunately) reflective of existing user code. +// NOLINTNEXTLINE(bugprone-macro-parentheses) +# define PYBIND11_AUTO_IT auto &it +#endif + + m.def("tuple_iterator", []() { + auto tup = py::make_tuple(5, 7); + int tup_sum = 0; + for (PYBIND11_AUTO_IT : tup) { + tup_sum += it.cast(); + } + return tup_sum; + }); + + m.def("dict_iterator", []() { + py::dict dct; + dct[py::int_(3)] = 5; + dct[py::int_(7)] = 11; + int kv_sum = 0; + for (PYBIND11_AUTO_IT : dct) { + kv_sum += it.first.cast() * 100 + it.second.cast(); + } + return kv_sum; + }); + + m.def("passed_iterator", [](const py::iterator &py_it) { + int elem_sum = 0; + for (PYBIND11_AUTO_IT : py_it) { + elem_sum += it.cast(); + } + return elem_sum; + }); + +#undef PYBIND11_AUTO_IT + + // Tests below this line are for pybind11 IMPLEMENTATION DETAILS: + + m.def("sequence_item_get_ssize_t", [](const py::object &o) { + return py::detail::accessor_policies::sequence_item::get(o, (py::ssize_t) 1); + }); + m.def("sequence_item_set_ssize_t", [](const py::object &o) { + auto s = py::str{"peppa", 5}; + py::detail::accessor_policies::sequence_item::set(o, (py::ssize_t) 1, s); + }); + m.def("sequence_item_get_size_t", [](const py::object &o) { + return py::detail::accessor_policies::sequence_item::get(o, (py::size_t) 2); + }); + m.def("sequence_item_set_size_t", [](const py::object &o) { + auto s = py::str{"george", 6}; + py::detail::accessor_policies::sequence_item::set(o, (py::size_t) 2, s); + }); + m.def("list_item_get_ssize_t", [](const py::object &o) { + return py::detail::accessor_policies::list_item::get(o, (py::ssize_t) 3); + }); + m.def("list_item_set_ssize_t", [](const py::object &o) { + auto s = py::str{"rebecca", 7}; + py::detail::accessor_policies::list_item::set(o, (py::ssize_t) 3, s); + }); + m.def("list_item_get_size_t", [](const py::object &o) { + return py::detail::accessor_policies::list_item::get(o, (py::size_t) 4); + }); + m.def("list_item_set_size_t", [](const py::object &o) { + auto s = py::str{"richard", 7}; + py::detail::accessor_policies::list_item::set(o, (py::size_t) 4, s); + }); + m.def("tuple_item_get_ssize_t", [](const py::object &o) { + return py::detail::accessor_policies::tuple_item::get(o, (py::ssize_t) 5); + }); + m.def("tuple_item_set_ssize_t", []() { + auto s0 = py::str{"emely", 5}; + auto s1 = py::str{"edmond", 6}; + auto o = py::tuple{2}; + py::detail::accessor_policies::tuple_item::set(o, (py::ssize_t) 0, s0); + py::detail::accessor_policies::tuple_item::set(o, (py::ssize_t) 1, s1); + return o; + }); + m.def("tuple_item_get_size_t", [](const py::object &o) { + return py::detail::accessor_policies::tuple_item::get(o, (py::size_t) 6); + }); + m.def("tuple_item_set_size_t", []() { + auto s0 = py::str{"candy", 5}; + auto s1 = py::str{"cat", 3}; + auto o = py::tuple{2}; + py::detail::accessor_policies::tuple_item::set(o, (py::size_t) 1, s1); + py::detail::accessor_policies::tuple_item::set(o, (py::size_t) 0, s0); + return o; + }); + + m.def("square_float_", [](const external::float_ &x) -> double { + double v = x.get_value(); + return v * v; + }); + + m.def("tuple_rvalue_getter", [](const py::tuple &tup) { + // tests accessing tuple object with rvalue int + for (size_t i = 0; i < tup.size(); i++) { + auto o = py::handle(tup[py::int_(i)]); + if (!o) { + throw py::value_error("tuple is malformed"); + } + } + return tup; + }); + m.def("list_rvalue_getter", [](const py::list &l) { + // tests accessing list with rvalue int + for (size_t i = 0; i < l.size(); i++) { + auto o = py::handle(l[py::int_(i)]); + if (!o) { + throw py::value_error("list is malformed"); + } + } + return l; + }); + m.def("populate_dict_rvalue", [](int population) { + auto d = py::dict(); + for (int i = 0; i < population; i++) { + d[py::int_(i)] = py::int_(i); + } + return d; + }); + m.def("populate_obj_str_attrs", [](py::object &o, int population) { + for (int i = 0; i < population; i++) { + o.attr(py::str(py::int_(i))) = py::str(py::int_(i)); + } + return o; + }); + + // testing immutable object augmented assignment: #issue 3812 + m.def("inplace_append", [](py::object &a, const py::object &b) { + a += b; + return a; + }); + m.def("inplace_subtract", [](py::object &a, const py::object &b) { + a -= b; + return a; + }); + m.def("inplace_multiply", [](py::object &a, const py::object &b) { + a *= b; + return a; + }); + m.def("inplace_divide", [](py::object &a, const py::object &b) { + a /= b; + return a; + }); + m.def("inplace_or", [](py::object &a, const py::object &b) { + a |= b; + return a; + }); + m.def("inplace_and", [](py::object &a, const py::object &b) { + a &= b; + return a; + }); + m.def("inplace_lshift", [](py::object &a, const py::object &b) { + a <<= b; + return a; + }); + m.def("inplace_rshift", [](py::object &a, const py::object &b) { + a >>= b; + return a; + }); } diff --git a/3rdparty/pybind11/tests/test_pytypes.py b/3rdparty/pybind11/tests/test_pytypes.py index 9e5c302e..a34eaa59 100644 --- a/3rdparty/pybind11/tests/test_pytypes.py +++ b/3rdparty/pybind11/tests/test_pytypes.py @@ -1,12 +1,21 @@ -# -*- coding: utf-8 -*- -from __future__ import division -import pytest +import contextlib import sys +import types -import env # noqa: F401 +import pytest +import env +from pybind11_tests import detailed_error_messages_enabled from pybind11_tests import pytypes as m -from pybind11_tests import debug_enabled + + +def test_handle_from_move_only_type_with_operator_PyObject(): # noqa: N802 + assert m.handle_from_move_only_type_with_operator_PyObject_ncnst() + assert m.handle_from_move_only_type_with_operator_PyObject_const() + + +def test_bool(doc): + assert doc(m.get_bool) == "get_bool() -> bool" def test_int(doc): @@ -17,11 +26,40 @@ def test_iterator(doc): assert doc(m.get_iterator) == "get_iterator() -> Iterator" +@pytest.mark.parametrize( + "pytype, from_iter_func", + [ + (frozenset, m.get_frozenset_from_iterable), + (list, m.get_list_from_iterable), + (set, m.get_set_from_iterable), + (tuple, m.get_tuple_from_iterable), + ], +) +def test_from_iterable(pytype, from_iter_func): + my_iter = iter(range(10)) + s = from_iter_func(my_iter) + assert type(s) == pytype + assert s == pytype(range(10)) + + def test_iterable(doc): assert doc(m.get_iterable) == "get_iterable() -> Iterable" +def test_float(doc): + assert doc(m.get_float) == "get_float() -> float" + + def test_list(capture, doc): + assert m.list_no_args() == [] + assert m.list_ssize_t() == [] + assert m.list_size_t() == [] + lins = [1, 2] + m.list_insert_ssize_t(lins) + assert lins == [1, 83, 2] + m.list_insert_size_t(lins) + assert lins == [1, 83, 2, 57] + with capture: lst = m.get_list() assert lst == ["inserted-0", "overwritten", "inserted-2"] @@ -50,11 +88,12 @@ def test_none(capture, doc): def test_set(capture, doc): s = m.get_set() + assert isinstance(s, set) assert s == {"key1", "key2", "key3"} + s.add("key4") with capture: - s.add("key4") - m.print_set(s) + m.print_anyset(s) assert ( capture.unordered == """ @@ -65,12 +104,43 @@ def test_set(capture, doc): """ ) - assert not m.set_contains(set([]), 42) - assert m.set_contains({42}, 42) - assert m.set_contains({"foo"}, "foo") + m.set_add(s, "key5") + assert m.anyset_size(s) == 5 - assert doc(m.get_list) == "get_list() -> list" - assert doc(m.print_list) == "print_list(arg0: list) -> None" + m.set_clear(s) + assert m.anyset_empty(s) + + assert not m.anyset_contains(set(), 42) + assert m.anyset_contains({42}, 42) + assert m.anyset_contains({"foo"}, "foo") + + assert doc(m.get_set) == "get_set() -> set" + assert doc(m.print_anyset) == "print_anyset(arg0: anyset) -> None" + + +def test_frozenset(capture, doc): + s = m.get_frozenset() + assert isinstance(s, frozenset) + assert s == frozenset({"key1", "key2", "key3"}) + + with capture: + m.print_anyset(s) + assert ( + capture.unordered + == """ + key: key1 + key: key2 + key: key3 + """ + ) + assert m.anyset_size(s) == 3 + assert not m.anyset_empty(s) + + assert not m.anyset_contains(frozenset(), 42) + assert m.anyset_contains(frozenset({42}), 42) + assert m.anyset_contains(frozenset({"foo"}), "foo") + + assert doc(m.get_frozenset) == "get_frozenset() -> frozenset" def test_dict(capture, doc): @@ -98,13 +168,30 @@ def test_dict(capture, doc): assert m.dict_keyword_constructor() == {"x": 1, "y": 2, "z": 3} +def test_tuple(): + assert m.tuple_no_args() == () + assert m.tuple_ssize_t() == () + assert m.tuple_size_t() == () + assert m.get_tuple() == (42, None, "spam") + + +def test_simple_namespace(): + ns = m.get_simple_namespace() + assert ns.attr == 42 + assert ns.x == "foo" + assert ns.right == 2 + assert not hasattr(ns, "wrong") + + def test_str(doc): + assert m.str_from_char_ssize_t().encode().decode() == "red" + assert m.str_from_char_size_t().encode().decode() == "blue" assert m.str_from_string().encode().decode() == "baz" assert m.str_from_bytes().encode().decode() == "boo" assert doc(m.str_from_bytes) == "str_from_bytes() -> str" - class A(object): + class A: def __str__(self): return "this is a str" @@ -120,24 +207,32 @@ def test_str(doc): assert s1 == s2 malformed_utf8 = b"\x80" - assert m.str_from_object(malformed_utf8) is malformed_utf8 # To be fixed; see #2380 - if env.PY2: - # with pytest.raises(UnicodeDecodeError): - # m.str_from_object(malformed_utf8) - with pytest.raises(UnicodeDecodeError): - m.str_from_handle(malformed_utf8) + if hasattr(m, "PYBIND11_STR_LEGACY_PERMISSIVE"): + assert m.str_from_object(malformed_utf8) is malformed_utf8 else: - # assert m.str_from_object(malformed_utf8) == "b'\\x80'" - assert m.str_from_handle(malformed_utf8) == "b'\\x80'" + assert m.str_from_object(malformed_utf8) == "b'\\x80'" + assert m.str_from_handle(malformed_utf8) == "b'\\x80'" + + assert m.str_from_string_from_str("this is a str") == "this is a str" + ucs_surrogates_str = "\udcc3" + with pytest.raises(UnicodeEncodeError): + m.str_from_string_from_str(ucs_surrogates_str) def test_bytes(doc): + assert m.bytes_from_char_ssize_t().decode() == "green" + assert m.bytes_from_char_size_t().decode() == "purple" assert m.bytes_from_string().decode() == "foo" assert m.bytes_from_str().decode() == "bar" - assert doc(m.bytes_from_str) == "bytes_from_str() -> {}".format( - "str" if env.PY2 else "bytes" - ) + assert doc(m.bytes_from_str) == "bytes_from_str() -> bytes" + + +def test_bytearray(doc): + assert m.bytearray_from_char_ssize_t().decode() == "$%" + assert m.bytearray_from_char_size_t().decode() == "@$!" + assert m.bytearray_from_string().decode() == "foo" + assert m.bytearray_size() == len("foo") def test_capsule(capture): @@ -154,6 +249,19 @@ def test_capsule(capture): """ ) + with capture: + a = m.return_renamed_capsule_with_destructor() + del a + pytest.gc_collect() + assert ( + capture.unordered + == """ + creating capsule + renaming capsule + destructing capsule + """ + ) + with capture: a = m.return_capsule_with_destructor_2() del a @@ -166,6 +274,19 @@ def test_capsule(capture): """ ) + with capture: + a = m.return_renamed_capsule_with_destructor_2() + del a + pytest.gc_collect() + assert ( + capture.unordered + == """ + creating capsule + renaming capsule + destructing capsule: 1234 + """ + ) + with capture: a = m.return_capsule_with_name_and_destructor() del a @@ -218,19 +339,23 @@ def test_accessors(): assert d["var"] == 99 +def test_accessor_moves(): + inc_refs = m.accessor_moves() + if inc_refs: + assert inc_refs == [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0] + else: + pytest.skip("Not defined: PYBIND11_HANDLE_REF_DEBUG") + + def test_constructors(): """C++ default and converting constructors are equivalent to type calls in Python""" - types = [bytes, str, bool, int, float, tuple, list, dict, set] + types = [bytes, bytearray, str, bool, int, float, tuple, list, dict, set] expected = {t.__name__: t() for t in types} - if env.PY2: - # Note that bytes.__name__ == 'str' in Python 2. - # pybind11::str is unicode even under Python 2. - expected["bytes"] = bytes() - expected["str"] = unicode() # noqa: F821 assert m.default_constructors() == expected data = { bytes: b"41", # Currently no supported or working conversions. + bytearray: bytearray(b"41"), str: 42, bool: "Not empty", int: "42", @@ -239,15 +364,11 @@ def test_constructors(): list: range(3), dict: [("two", 2), ("one", 1), ("three", 3)], set: [4, 4, 5, 6, 6, 6], + frozenset: [4, 4, 5, 6, 6, 6], memoryview: b"abc", } inputs = {k.__name__: v for k, v in data.items()} expected = {k.__name__: k(v) for k, v in data.items()} - if env.PY2: # Similar to the above. See comments above. - inputs["bytes"] = b"41" - inputs["str"] = 42 - expected["bytes"] = b"41" - expected["str"] = u"42" assert m.converting_constructors(inputs) == expected assert m.cast_functions(inputs) == expected @@ -268,46 +389,54 @@ def test_non_converting_constructors(): ("bytes", range(10)), ("none", 42), ("ellipsis", 42), + ("type", 42), ] for t, v in non_converting_test_cases: - with pytest.raises(TypeError) as excinfo: - m.nonconverting_constructor(t, v) - expected_error = "Object of type '{}' is not an instance of '{}'".format( - type(v).__name__, t - ) - assert str(excinfo.value) == expected_error + for move in [True, False]: + with pytest.raises(TypeError) as excinfo: + m.nonconverting_constructor(t, v, move) + expected_error = ( + f"Object of type '{type(v).__name__}' is not an instance of '{t}'" + ) + assert str(excinfo.value) == expected_error def test_pybind11_str_raw_str(): # specifically to exercise pybind11::str::raw_str cvt = m.convert_to_pybind11_str - assert cvt(u"Str") == u"Str" - assert cvt(b"Bytes") == u"Bytes" if env.PY2 else "b'Bytes'" - assert cvt(None) == u"None" - assert cvt(False) == u"False" - assert cvt(True) == u"True" - assert cvt(42) == u"42" - assert cvt(2 ** 65) == u"36893488147419103232" - assert cvt(-1.50) == u"-1.5" - assert cvt(()) == u"()" - assert cvt((18,)) == u"(18,)" - assert cvt([]) == u"[]" - assert cvt([28]) == u"[28]" - assert cvt({}) == u"{}" - assert cvt({3: 4}) == u"{3: 4}" - assert cvt(set()) == u"set([])" if env.PY2 else "set()" - assert cvt({3, 3}) == u"set([3])" if env.PY2 else "{3}" - - valid_orig = u"DZ" + assert cvt("Str") == "Str" + assert cvt(b"Bytes") == "b'Bytes'" + assert cvt(None) == "None" + assert cvt(False) == "False" + assert cvt(True) == "True" + assert cvt(42) == "42" + assert cvt(2**65) == "36893488147419103232" + assert cvt(-1.50) == "-1.5" + assert cvt(()) == "()" + assert cvt((18,)) == "(18,)" + assert cvt([]) == "[]" + assert cvt([28]) == "[28]" + assert cvt({}) == "{}" + assert cvt({3: 4}) == "{3: 4}" + assert cvt(set()) == "set()" + assert cvt({3, 3}) == "{3}" + + valid_orig = "DZ" valid_utf8 = valid_orig.encode("utf-8") valid_cvt = cvt(valid_utf8) - assert type(valid_cvt) == bytes # Probably surprising. - assert valid_cvt == b"\xc7\xb1" + if hasattr(m, "PYBIND11_STR_LEGACY_PERMISSIVE"): + assert valid_cvt is valid_utf8 + else: + assert type(valid_cvt) is str + assert valid_cvt == "b'\\xc7\\xb1'" malformed_utf8 = b"\x80" - malformed_cvt = cvt(malformed_utf8) - assert type(malformed_cvt) == bytes # Probably surprising. - assert malformed_cvt == b"\x80" + if hasattr(m, "PYBIND11_STR_LEGACY_PERMISSIVE"): + assert cvt(malformed_utf8) is malformed_utf8 + else: + malformed_cvt = cvt(malformed_utf8) + assert type(malformed_cvt) is str + assert malformed_cvt == "b'\\x80'" def test_implicit_casting(): @@ -348,22 +477,22 @@ def test_print(capture): with pytest.raises(RuntimeError) as excinfo: m.print_failure() - assert str(excinfo.value) == "make_tuple(): unable to convert " + ( - "argument of type 'UnregisteredType' to Python object" - if debug_enabled - else "arguments to Python object (compile in debug mode for details)" + assert str(excinfo.value) == "Unable to convert call argument " + ( + "'1' of type 'UnregisteredType' to Python object" + if detailed_error_messages_enabled + else "to Python object (#define PYBIND11_DETAILED_ERROR_MESSAGES or compile in debug mode for details)" ) def test_hash(): - class Hashable(object): + class Hashable: def __init__(self, value): self.value = value def __hash__(self): return self.value - class Unhashable(object): + class Unhashable: __hash__ = None assert m.hash_function(Hashable(42)) == 42 @@ -403,7 +532,8 @@ def test_issue2361(): assert m.issue2361_str_implicit_copy_none() == "None" with pytest.raises(TypeError) as excinfo: assert m.issue2361_dict_implicit_copy_none() - assert "'NoneType' object is not iterable" in str(excinfo.value) + assert "NoneType" in str(excinfo.value) + assert "iterable" in str(excinfo.value) @pytest.mark.parametrize( @@ -420,12 +550,7 @@ def test_memoryview(method, args, fmt, expected_view): view = method(*args) assert isinstance(view, memoryview) assert view.format == fmt - if isinstance(expected_view, bytes) or not env.PY2: - view_as_list = list(view) - else: - # Using max to pick non-zero byte (big-endian vs little-endian). - view_as_list = [max([ord(c) for c in s]) for s in view] - assert view_as_list == list(expected_view) + assert list(view) == list(expected_view) @pytest.mark.xfail("env.PYPY", reason="getrefcount is not available") @@ -449,12 +574,7 @@ def test_memoryview_from_buffer_empty_shape(): view = m.test_memoryview_from_buffer_empty_shape() assert isinstance(view, memoryview) assert view.format == "B" - if env.PY2: - # Python 2 behavior is weird, but Python 3 (the future) is fine. - # PyPy3 has > b + assert m.inplace_rshift(a, b) == expected diff --git a/3rdparty/pybind11/tests/test_sequences_and_iterators.cpp b/3rdparty/pybind11/tests/test_sequences_and_iterators.cpp index d318052a..b867f49a 100644 --- a/3rdparty/pybind11/tests/test_sequences_and_iterators.cpp +++ b/3rdparty/pybind11/tests/test_sequences_and_iterators.cpp @@ -8,38 +8,91 @@ BSD-style license that can be found in the LICENSE file. */ -#include "pybind11_tests.h" -#include "constructor_stats.h" #include #include +#include "constructor_stats.h" +#include "pybind11_tests.h" + #include +#include +#include -template +#ifdef PYBIND11_HAS_OPTIONAL +# include +#endif // PYBIND11_HAS_OPTIONAL + +template class NonZeroIterator { - const T* ptr_; + const T *ptr_; + public: - NonZeroIterator(const T* ptr) : ptr_(ptr) {} - const T& operator*() const { return *ptr_; } - NonZeroIterator& operator++() { ++ptr_; return *this; } + explicit NonZeroIterator(const T *ptr) : ptr_(ptr) {} + const T &operator*() const { return *ptr_; } + NonZeroIterator &operator++() { + ++ptr_; + return *this; + } }; class NonZeroSentinel {}; -template -bool operator==(const NonZeroIterator>& it, const NonZeroSentinel&) { +template +bool operator==(const NonZeroIterator> &it, const NonZeroSentinel &) { return !(*it).first || !(*it).second; } +/* Iterator where dereferencing returns prvalues instead of references. */ +template +class NonRefIterator { + const T *ptr_; + +public: + explicit NonRefIterator(const T *ptr) : ptr_(ptr) {} + T operator*() const { return T(*ptr_); } + NonRefIterator &operator++() { + ++ptr_; + return *this; + } + bool operator==(const NonRefIterator &other) const { return ptr_ == other.ptr_; } +}; + +class NonCopyableInt { +public: + explicit NonCopyableInt(int value) : value_(value) {} + NonCopyableInt(const NonCopyableInt &) = delete; + NonCopyableInt(NonCopyableInt &&other) noexcept : value_(other.value_) { + other.value_ = -1; // detect when an unwanted move occurs + } + NonCopyableInt &operator=(const NonCopyableInt &) = delete; + NonCopyableInt &operator=(NonCopyableInt &&other) noexcept { + value_ = other.value_; + other.value_ = -1; // detect when an unwanted move occurs + return *this; + } + int get() const { return value_; } + void set(int value) { value_ = value; } + ~NonCopyableInt() = default; + +private: + int value_; +}; +using NonCopyableIntPair = std::pair; +PYBIND11_MAKE_OPAQUE(std::vector); +PYBIND11_MAKE_OPAQUE(std::vector); + template py::list test_random_access_iterator(PythonType x) { - if (x.size() < 5) + if (x.size() < 5) { throw py::value_error("Please provide at least 5 elements for testing."); + } auto checks = py::list(); auto assert_equal = [&checks](py::handle a, py::handle b) { auto result = PyObject_RichCompareBool(a.ptr(), b.ptr(), Py_EQ); - if (result == -1) { throw py::error_already_set(); } + if (result == -1) { + throw py::error_already_set(); + } checks.append(result != 0); }; @@ -74,63 +127,83 @@ py::list test_random_access_iterator(PythonType x) { TEST_SUBMODULE(sequences_and_iterators, m) { // test_sliceable - class Sliceable{ + class Sliceable { public: - Sliceable(int n): size(n) {} - int start,stop,step; - int size; + explicit Sliceable(int n) : size(n) {} + int start, stop, step; + int size; }; - py::class_(m,"Sliceable") + py::class_(m, "Sliceable") .def(py::init()) - .def("__getitem__",[](const Sliceable &s, py::slice slice) { - py::ssize_t start, stop, step, slicelength; - if (!slice.compute(s.size, &start, &stop, &step, &slicelength)) - throw py::error_already_set(); - int istart = static_cast(start); - int istop = static_cast(stop); - int istep = static_cast(step); - return std::make_tuple(istart,istop,istep); - }) - ; + .def("__getitem__", [](const Sliceable &s, const py::slice &slice) { + py::ssize_t start = 0, stop = 0, step = 0, slicelength = 0; + if (!slice.compute(s.size, &start, &stop, &step, &slicelength)) { + throw py::error_already_set(); + } + int istart = static_cast(start); + int istop = static_cast(stop); + int istep = static_cast(step); + return std::make_tuple(istart, istop, istep); + }); + + m.def("make_forward_slice_size_t", []() { return py::slice(0, -1, 1); }); + m.def("make_reversed_slice_object", + []() { return py::slice(py::none(), py::none(), py::int_(-1)); }); +#ifdef PYBIND11_HAS_OPTIONAL + m.attr("has_optional") = true; + m.def("make_reversed_slice_size_t_optional_verbose", + []() { return py::slice(std::nullopt, std::nullopt, -1); }); + // Warning: The following spelling may still compile if optional<> is not present and give + // wrong answers. Please use with caution. + m.def("make_reversed_slice_size_t_optional", []() { return py::slice({}, {}, -1); }); +#else + m.attr("has_optional") = false; +#endif // test_sequence class Sequence { public: - Sequence(size_t size) : m_size(size) { + explicit Sequence(size_t size) : m_size(size) { print_created(this, "of size", m_size); + // NOLINTNEXTLINE(cppcoreguidelines-prefer-member-initializer) m_data = new float[size]; memset(m_data, 0, sizeof(float) * size); } - Sequence(const std::vector &value) : m_size(value.size()) { + explicit Sequence(const std::vector &value) : m_size(value.size()) { print_created(this, "of size", m_size, "from std::vector"); + // NOLINTNEXTLINE(cppcoreguidelines-prefer-member-initializer) m_data = new float[m_size]; memcpy(m_data, &value[0], sizeof(float) * m_size); } Sequence(const Sequence &s) : m_size(s.m_size) { print_copy_created(this); + // NOLINTNEXTLINE(cppcoreguidelines-prefer-member-initializer) m_data = new float[m_size]; - memcpy(m_data, s.m_data, sizeof(float)*m_size); + memcpy(m_data, s.m_data, sizeof(float) * m_size); } - Sequence(Sequence &&s) : m_size(s.m_size), m_data(s.m_data) { + Sequence(Sequence &&s) noexcept : m_size(s.m_size), m_data(s.m_data) { print_move_created(this); s.m_size = 0; s.m_data = nullptr; } - ~Sequence() { print_destroyed(this); delete[] m_data; } + ~Sequence() { + print_destroyed(this); + delete[] m_data; + } Sequence &operator=(const Sequence &s) { if (&s != this) { delete[] m_data; m_size = s.m_size; m_data = new float[m_size]; - memcpy(m_data, s.m_data, sizeof(float)*m_size); + memcpy(m_data, s.m_data, sizeof(float) * m_size); } print_copy_assigned(this); return *this; } - Sequence &operator=(Sequence &&s) { + Sequence &operator=(Sequence &&s) noexcept { if (&s != this) { delete[] m_data; m_size = s.m_size; @@ -143,10 +216,14 @@ TEST_SUBMODULE(sequences_and_iterators, m) { } bool operator==(const Sequence &s) const { - if (m_size != s.size()) return false; - for (size_t i = 0; i < m_size; ++i) - if (m_data[i] != s[i]) + if (m_size != s.size()) { + return false; + } + for (size_t i = 0; i < m_size; ++i) { + if (m_data[i] != s[i]) { return false; + } + } return true; } bool operator!=(const Sequence &s) const { return !operator==(s); } @@ -155,23 +232,26 @@ TEST_SUBMODULE(sequences_and_iterators, m) { float &operator[](size_t index) { return m_data[index]; } bool contains(float v) const { - for (size_t i = 0; i < m_size; ++i) - if (v == m_data[i]) + for (size_t i = 0; i < m_size; ++i) { + if (v == m_data[i]) { return true; + } + } return false; } Sequence reversed() const { Sequence result(m_size); - for (size_t i = 0; i < m_size; ++i) + for (size_t i = 0; i < m_size; ++i) { result[m_size - i - 1] = m_data[i]; + } return result; } size_t size() const { return m_size; } const float *begin() const { return m_data; } - const float *end() const { return m_data+m_size; } + const float *end() const { return m_data + m_size; } private: size_t m_size; @@ -179,43 +259,59 @@ TEST_SUBMODULE(sequences_and_iterators, m) { }; py::class_(m, "Sequence") .def(py::init()) - .def(py::init&>()) + .def(py::init &>()) /// Bare bones interface - .def("__getitem__", [](const Sequence &s, size_t i) { - if (i >= s.size()) throw py::index_error(); - return s[i]; - }) - .def("__setitem__", [](Sequence &s, size_t i, float v) { - if (i >= s.size()) throw py::index_error(); - s[i] = v; - }) + .def("__getitem__", + [](const Sequence &s, size_t i) { + if (i >= s.size()) { + throw py::index_error(); + } + return s[i]; + }) + .def("__setitem__", + [](Sequence &s, size_t i, float v) { + if (i >= s.size()) { + throw py::index_error(); + } + s[i] = v; + }) .def("__len__", &Sequence::size) /// Optional sequence protocol operations - .def("__iter__", [](const Sequence &s) { return py::make_iterator(s.begin(), s.end()); }, - py::keep_alive<0, 1>() /* Essential: keep object alive while iterator exists */) + .def( + "__iter__", + [](const Sequence &s) { return py::make_iterator(s.begin(), s.end()); }, + py::keep_alive<0, 1>() /* Essential: keep object alive while iterator exists */) .def("__contains__", [](const Sequence &s, float v) { return s.contains(v); }) .def("__reversed__", [](const Sequence &s) -> Sequence { return s.reversed(); }) /// Slicing protocol (optional) - .def("__getitem__", [](const Sequence &s, py::slice slice) -> Sequence* { - size_t start, stop, step, slicelength; - if (!slice.compute(s.size(), &start, &stop, &step, &slicelength)) - throw py::error_already_set(); - auto *seq = new Sequence(slicelength); - for (size_t i = 0; i < slicelength; ++i) { - (*seq)[i] = s[start]; start += step; - } - return seq; - }) - .def("__setitem__", [](Sequence &s, py::slice slice, const Sequence &value) { - size_t start, stop, step, slicelength; - if (!slice.compute(s.size(), &start, &stop, &step, &slicelength)) - throw py::error_already_set(); - if (slicelength != value.size()) - throw std::runtime_error("Left and right hand size of slice assignment have different sizes!"); - for (size_t i = 0; i < slicelength; ++i) { - s[start] = value[i]; start += step; - } - }) + .def("__getitem__", + [](const Sequence &s, const py::slice &slice) -> Sequence * { + size_t start = 0, stop = 0, step = 0, slicelength = 0; + if (!slice.compute(s.size(), &start, &stop, &step, &slicelength)) { + throw py::error_already_set(); + } + auto *seq = new Sequence(slicelength); + for (size_t i = 0; i < slicelength; ++i) { + (*seq)[i] = s[start]; + start += step; + } + return seq; + }) + .def("__setitem__", + [](Sequence &s, const py::slice &slice, const Sequence &value) { + size_t start = 0, stop = 0, step = 0, slicelength = 0; + if (!slice.compute(s.size(), &start, &stop, &step, &slicelength)) { + throw py::error_already_set(); + } + if (slicelength != value.size()) { + throw std::runtime_error( + "Left and right hand size of slice assignment have different sizes!"); + } + for (size_t i = 0; i < slicelength; ++i) { + s[start] = value[i]; + start += step; + } + }) /// Comparisons .def(py::self == py::self) .def(py::self != py::self) @@ -223,19 +319,21 @@ TEST_SUBMODULE(sequences_and_iterators, m) { ; // test_map_iterator - // Interface of a map-like object that isn't (directly) an unordered_map, but provides some basic - // map-like functionality. + // Interface of a map-like object that isn't (directly) an unordered_map, but provides some + // basic map-like functionality. class StringMap { public: StringMap() = default; - StringMap(std::unordered_map init) + explicit StringMap(std::unordered_map init) : map(std::move(init)) {} - void set(std::string key, std::string val) { map[key] = val; } - std::string get(std::string key) const { return map.at(key); } + void set(const std::string &key, std::string val) { map[key] = std::move(val); } + std::string get(const std::string &key) const { return map.at(key); } size_t size() const { return map.size(); } + private: std::unordered_map map; + public: decltype(map.cbegin()) begin() const { return map.cbegin(); } decltype(map.cend()) end() const { return map.cend(); } @@ -243,38 +341,142 @@ TEST_SUBMODULE(sequences_and_iterators, m) { py::class_(m, "StringMap") .def(py::init<>()) .def(py::init>()) - .def("__getitem__", [](const StringMap &map, std::string key) { - try { return map.get(key); } - catch (const std::out_of_range&) { - throw py::key_error("key '" + key + "' does not exist"); - } - }) + .def("__getitem__", + [](const StringMap &map, const std::string &key) { + try { + return map.get(key); + } catch (const std::out_of_range &) { + throw py::key_error("key '" + key + "' does not exist"); + } + }) .def("__setitem__", &StringMap::set) .def("__len__", &StringMap::size) - .def("__iter__", [](const StringMap &map) { return py::make_key_iterator(map.begin(), map.end()); }, - py::keep_alive<0, 1>()) - .def("items", [](const StringMap &map) { return py::make_iterator(map.begin(), map.end()); }, - py::keep_alive<0, 1>()) - ; + .def( + "__iter__", + [](const StringMap &map) { return py::make_key_iterator(map.begin(), map.end()); }, + py::keep_alive<0, 1>()) + .def( + "items", + [](const StringMap &map) { return py::make_iterator(map.begin(), map.end()); }, + py::keep_alive<0, 1>()) + .def( + "values", + [](const StringMap &map) { return py::make_value_iterator(map.begin(), map.end()); }, + py::keep_alive<0, 1>()); // test_generalized_iterators class IntPairs { public: - IntPairs(std::vector> data) : data_(std::move(data)) {} - const std::pair* begin() const { return data_.data(); } + explicit IntPairs(std::vector> data) : data_(std::move(data)) {} + const std::pair *begin() const { return data_.data(); } + // .end() only required for py::make_iterator(self) overload + const std::pair *end() const { return data_.data() + data_.size(); } + private: std::vector> data_; }; py::class_(m, "IntPairs") .def(py::init>>()) - .def("nonzero", [](const IntPairs& s) { - return py::make_iterator(NonZeroIterator>(s.begin()), NonZeroSentinel()); - }, py::keep_alive<0, 1>()) - .def("nonzero_keys", [](const IntPairs& s) { - return py::make_key_iterator(NonZeroIterator>(s.begin()), NonZeroSentinel()); - }, py::keep_alive<0, 1>()) - ; - + .def( + "nonzero", + [](const IntPairs &s) { + return py::make_iterator(NonZeroIterator>(s.begin()), + NonZeroSentinel()); + }, + py::keep_alive<0, 1>()) + .def( + "nonzero_keys", + [](const IntPairs &s) { + return py::make_key_iterator(NonZeroIterator>(s.begin()), + NonZeroSentinel()); + }, + py::keep_alive<0, 1>()) + .def( + "nonzero_values", + [](const IntPairs &s) { + return py::make_value_iterator(NonZeroIterator>(s.begin()), + NonZeroSentinel()); + }, + py::keep_alive<0, 1>()) + + // test iterator that returns values instead of references + .def( + "nonref", + [](const IntPairs &s) { + return py::make_iterator(NonRefIterator>(s.begin()), + NonRefIterator>(s.end())); + }, + py::keep_alive<0, 1>()) + .def( + "nonref_keys", + [](const IntPairs &s) { + return py::make_key_iterator(NonRefIterator>(s.begin()), + NonRefIterator>(s.end())); + }, + py::keep_alive<0, 1>()) + .def( + "nonref_values", + [](const IntPairs &s) { + return py::make_value_iterator(NonRefIterator>(s.begin()), + NonRefIterator>(s.end())); + }, + py::keep_alive<0, 1>()) + + // test single-argument make_iterator + .def( + "simple_iterator", + [](IntPairs &self) { return py::make_iterator(self); }, + py::keep_alive<0, 1>()) + .def( + "simple_keys", + [](IntPairs &self) { return py::make_key_iterator(self); }, + py::keep_alive<0, 1>()) + .def( + "simple_values", + [](IntPairs &self) { return py::make_value_iterator(self); }, + py::keep_alive<0, 1>()) + + // Test iterator with an Extra (doesn't do anything useful, so not used + // at runtime, but tests need to be able to compile with the correct + // overload. See PR #3293. + .def( + "_make_iterator_extras", + [](IntPairs &self) { return py::make_iterator(self, py::call_guard()); }, + py::keep_alive<0, 1>()) + .def( + "_make_key_extras", + [](IntPairs &self) { return py::make_key_iterator(self, py::call_guard()); }, + py::keep_alive<0, 1>()) + .def( + "_make_value_extras", + [](IntPairs &self) { return py::make_value_iterator(self, py::call_guard()); }, + py::keep_alive<0, 1>()); + + // test_iterator_referencing + py::class_(m, "NonCopyableInt") + .def(py::init()) + .def("set", &NonCopyableInt::set) + .def("__int__", &NonCopyableInt::get); + py::class_>(m, "VectorNonCopyableInt") + .def(py::init<>()) + .def("append", + [](std::vector &vec, int value) { vec.emplace_back(value); }) + .def("__iter__", [](std::vector &vec) { + return py::make_iterator(vec.begin(), vec.end()); + }); + py::class_>(m, "VectorNonCopyableIntPair") + .def(py::init<>()) + .def("append", + [](std::vector &vec, const std::pair &value) { + vec.emplace_back(NonCopyableInt(value.first), NonCopyableInt(value.second)); + }) + .def("keys", + [](std::vector &vec) { + return py::make_key_iterator(vec.begin(), vec.end()); + }) + .def("values", [](std::vector &vec) { + return py::make_value_iterator(vec.begin(), vec.end()); + }); #if 0 // Obsolete: special data structure for exposing custom iterator types to python @@ -304,7 +506,7 @@ TEST_SUBMODULE(sequences_and_iterators, m) { #endif // test_python_iterator_in_cpp - m.def("object_to_list", [](py::object o) { + m.def("object_to_list", [](const py::object &o) { auto l = py::list(); for (auto item : o) { l.append(item); @@ -322,22 +524,22 @@ TEST_SUBMODULE(sequences_and_iterators, m) { }); // test_sequence_length: check that Python sequences can be converted to py::sequence. - m.def("sequence_length", [](py::sequence seq) { return seq.size(); }); + m.def("sequence_length", [](const py::sequence &seq) { return seq.size(); }); // Make sure that py::iterator works with std algorithms - m.def("count_none", [](py::object o) { + m.def("count_none", [](const py::object &o) { return std::count_if(o.begin(), o.end(), [](py::handle h) { return h.is_none(); }); }); - m.def("find_none", [](py::object o) { + m.def("find_none", [](const py::object &o) { auto it = std::find_if(o.begin(), o.end(), [](py::handle h) { return h.is_none(); }); return it->is_none(); }); - m.def("count_nonzeros", [](py::dict d) { - return std::count_if(d.begin(), d.end(), [](std::pair p) { - return p.second.cast() != 0; - }); + m.def("count_nonzeros", [](const py::dict &d) { + return std::count_if(d.begin(), d.end(), [](std::pair p) { + return p.second.cast() != 0; + }); }); m.def("tuple_iterator", &test_random_access_iterator); @@ -352,7 +554,9 @@ TEST_SUBMODULE(sequences_and_iterators, m) { // test_iterator_rvp // #388: Can't make iterators via make_iterator() with different r/v policies - static std::vector list = { 1, 2, 3 }; - m.def("make_iterator_1", []() { return py::make_iterator(list); }); - m.def("make_iterator_2", []() { return py::make_iterator(list); }); + static std::vector list = {1, 2, 3}; + m.def("make_iterator_1", + []() { return py::make_iterator(list); }); + m.def("make_iterator_2", + []() { return py::make_iterator(list); }); } diff --git a/3rdparty/pybind11/tests/test_sequences_and_iterators.py b/3rdparty/pybind11/tests/test_sequences_and_iterators.py index c3b608c4..062e3b3d 100644 --- a/3rdparty/pybind11/tests/test_sequences_and_iterators.py +++ b/3rdparty/pybind11/tests/test_sequences_and_iterators.py @@ -1,18 +1,19 @@ -# -*- coding: utf-8 -*- import pytest -from pybind11_tests import sequences_and_iterators as m +from pytest import approx + from pybind11_tests import ConstructorStats +from pybind11_tests import sequences_and_iterators as m -def isclose(a, b, rel_tol=1e-05, abs_tol=0.0): - """Like math.isclose() from Python 3.5""" - return abs(a - b) <= max(rel_tol * max(abs(a), abs(b)), abs_tol) +def test_slice_constructors(): + assert m.make_forward_slice_size_t() == slice(0, -1, 1) + assert m.make_reversed_slice_object() == slice(None, None, -1) -def allclose(a_list, b_list, rel_tol=1e-05, abs_tol=0.0): - return all( - isclose(a, b, rel_tol=rel_tol, abs_tol=abs_tol) for a, b in zip(a_list, b_list) - ) +@pytest.mark.skipif(not m.has_optional, reason="no ") +def test_slice_constructors_explicit_optional(): + assert m.make_reversed_slice_size_t_optional() == slice(None, None, -1) + assert m.make_reversed_slice_size_t_optional_verbose() == slice(None, None, -1) def test_generalized_iterators(): @@ -24,6 +25,10 @@ def test_generalized_iterators(): assert list(m.IntPairs([(1, 2), (2, 0), (0, 3), (4, 5)]).nonzero_keys()) == [1] assert list(m.IntPairs([(0, 3), (1, 2), (3, 4)]).nonzero_keys()) == [] + assert list(m.IntPairs([(1, 2), (3, 4), (0, 5)]).nonzero_values()) == [2, 4] + assert list(m.IntPairs([(1, 2), (2, 0), (0, 3), (4, 5)]).nonzero_values()) == [2] + assert list(m.IntPairs([(0, 3), (1, 2), (3, 4)]).nonzero_values()) == [] + # __next__ must continue to raise StopIteration it = m.IntPairs([(0, 0)]).nonzero() for _ in range(3): @@ -36,6 +41,47 @@ def test_generalized_iterators(): next(it) +def test_nonref_iterators(): + pairs = m.IntPairs([(1, 2), (3, 4), (0, 5)]) + assert list(pairs.nonref()) == [(1, 2), (3, 4), (0, 5)] + assert list(pairs.nonref_keys()) == [1, 3, 0] + assert list(pairs.nonref_values()) == [2, 4, 5] + + +def test_generalized_iterators_simple(): + assert list(m.IntPairs([(1, 2), (3, 4), (0, 5)]).simple_iterator()) == [ + (1, 2), + (3, 4), + (0, 5), + ] + assert list(m.IntPairs([(1, 2), (3, 4), (0, 5)]).simple_keys()) == [1, 3, 0] + assert list(m.IntPairs([(1, 2), (3, 4), (0, 5)]).simple_values()) == [2, 4, 5] + + +def test_iterator_referencing(): + """Test that iterators reference rather than copy their referents.""" + vec = m.VectorNonCopyableInt() + vec.append(3) + vec.append(5) + assert [int(x) for x in vec] == [3, 5] + # Increment everything to make sure the referents can be mutated + for x in vec: + x.set(int(x) + 1) + assert [int(x) for x in vec] == [4, 6] + + vec = m.VectorNonCopyableIntPair() + vec.append([3, 4]) + vec.append([5, 7]) + assert [int(x) for x in vec.keys()] == [3, 5] + assert [int(x) for x in vec.values()] == [4, 7] + for x in vec.keys(): + x.set(int(x) + 1) + for x in vec.values(): + x.set(int(x) + 10) + assert [int(x) for x in vec.keys()] == [4, 6] + assert [int(x) for x in vec.values()] == [14, 17] + + def test_sliceable(): sliceable = m.Sliceable(100) assert sliceable[::] == (0, 100, 1) @@ -61,7 +107,8 @@ def test_sequence(): assert 12.34 not in s s[0], s[3] = 12.34, 56.78 assert 12.34 in s - assert isclose(s[0], 12.34) and isclose(s[3], 56.78) + assert s[0] == approx(12.34, rel=1e-05) + assert s[3] == approx(56.78, rel=1e-05) rev = reversed(s) assert cstats.values() == ["of size", "5"] @@ -76,14 +123,14 @@ def test_sequence(): assert cstats.values() == ["of size", "0"] expected = [0, 56.78, 0, 0, 12.34] - assert allclose(rev, expected) - assert allclose(rev2, expected) + assert rev == approx(expected, rel=1e-05) + assert rev2 == approx(expected, rel=1e-05) assert rev == rev2 rev[0::2] = m.Sequence([2.0, 2.0, 2.0]) assert cstats.values() == ["of size", "3", "from std::vector"] - assert allclose(rev, [2, 56.78, 2, 0, 2]) + assert rev == approx([2, 56.78, 2, 0, 2], rel=1e-05) assert cstats.alive() == 4 del it @@ -104,7 +151,7 @@ def test_sequence(): def test_sequence_length(): - """#2076: Exception raised by len(arg) should be propagated """ + """#2076: Exception raised by len(arg) should be propagated""" class BadLen(RuntimeError): pass @@ -139,6 +186,7 @@ def test_map_iterator(): assert sm[k] == expected[k] for k, v in sm.items(): assert v == expected[k] + assert list(sm.values()) == [expected[k] for k in sm] it = iter(m.StringMap({})) for _ in range(3): # __next__ must continue to raise StopIteration @@ -187,7 +235,7 @@ def test_iterator_passthrough(): def test_iterator_rvp(): - """#388: Can't make iterators via make_iterator() with different r/v policies """ + """#388: Can't make iterators via make_iterator() with different r/v policies""" import pybind11_tests.sequences_and_iterators as m assert list(m.make_iterator_1()) == [1, 2, 3] diff --git a/3rdparty/pybind11/tests/test_smart_ptr.cpp b/3rdparty/pybind11/tests/test_smart_ptr.cpp index 60c2e692..6d9efced 100644 --- a/3rdparty/pybind11/tests/test_smart_ptr.cpp +++ b/3rdparty/pybind11/tests/test_smart_ptr.cpp @@ -8,53 +8,34 @@ BSD-style license that can be found in the LICENSE file. */ -#if defined(_MSC_VER) && _MSC_VER < 1910 -# pragma warning(disable: 4702) // unreachable code in system header -#endif - -#include "pybind11_tests.h" #include "object.h" +#include "pybind11_tests.h" -// Make pybind aware of the ref-counted wrapper type (s): - -// ref is a wrapper for 'Object' which uses intrusive reference counting -// It is always possible to construct a ref from an Object* pointer without -// possible inconsistencies, hence the 'true' argument at the end. -PYBIND11_DECLARE_HOLDER_TYPE(T, ref, true); -// Make pybind11 aware of the non-standard getter member function -namespace pybind11 { namespace detail { - template - struct holder_helper> { - static const T *get(const ref &p) { return p.get_ptr(); } - }; -} // namespace detail -} // namespace pybind11 - -// The following is not required anymore for std::shared_ptr, but it should compile without error: -PYBIND11_DECLARE_HOLDER_TYPE(T, std::shared_ptr); +namespace { // This is just a wrapper around unique_ptr, but with extra fields to deliberately bloat up the -// holder size to trigger the non-simple-layout internal instance layout for single inheritance with -// large holder type: -template class huge_unique_ptr { +// holder size to trigger the non-simple-layout internal instance layout for single inheritance +// with large holder type: +template +class huge_unique_ptr { std::unique_ptr ptr; uint64_t padding[10]; + public: - huge_unique_ptr(T *p) : ptr(p) {}; + explicit huge_unique_ptr(T *p) : ptr(p) {} T *get() { return ptr.get(); } }; -PYBIND11_DECLARE_HOLDER_TYPE(T, huge_unique_ptr); // Simple custom holder that works like unique_ptr template class custom_unique_ptr { std::unique_ptr impl; + public: - custom_unique_ptr(T* p) : impl(p) { } - T* get() const { return impl.get(); } - T* release_ptr() { return impl.release(); } + explicit custom_unique_ptr(T *p) : impl(p) {} + T *get() const { return impl.get(); } + T *release_ptr() { return impl.release(); } }; -PYBIND11_DECLARE_HOLDER_TYPE(T, custom_unique_ptr); // Simple custom holder that works like shared_ptr and has operator& overload // To obtain address of an instance of this holder pybind should use std::addressof @@ -62,13 +43,13 @@ PYBIND11_DECLARE_HOLDER_TYPE(T, custom_unique_ptr); template class shared_ptr_with_addressof_operator { std::shared_ptr impl; + public: - shared_ptr_with_addressof_operator( ) = default; - shared_ptr_with_addressof_operator(T* p) : impl(p) { } - T* get() const { return impl.get(); } - T** operator&() { throw std::logic_error("Call of overloaded operator& is not expected"); } + shared_ptr_with_addressof_operator() = default; + explicit shared_ptr_with_addressof_operator(T *p) : impl(p) {} + T *get() const { return impl.get(); } + T **operator&() { throw std::logic_error("Call of overloaded operator& is not expected"); } }; -PYBIND11_DECLARE_HOLDER_TYPE(T, shared_ptr_with_addressof_operator); // Simple custom holder that works like unique_ptr and has operator& overload // To obtain address of an instance of this holder pybind should use std::addressof @@ -76,17 +57,237 @@ PYBIND11_DECLARE_HOLDER_TYPE(T, shared_ptr_with_addressof_operator); template class unique_ptr_with_addressof_operator { std::unique_ptr impl; + public: unique_ptr_with_addressof_operator() = default; - unique_ptr_with_addressof_operator(T* p) : impl(p) { } - T* get() const { return impl.get(); } - T* release_ptr() { return impl.release(); } - T** operator&() { throw std::logic_error("Call of overloaded operator& is not expected"); } + explicit unique_ptr_with_addressof_operator(T *p) : impl(p) {} + T *get() const { return impl.get(); } + T *release_ptr() { return impl.release(); } + T **operator&() { throw std::logic_error("Call of overloaded operator& is not expected"); } +}; + +// Custom object with builtin reference counting (see 'object.h' for the implementation) +class MyObject1 : public Object { +public: + explicit MyObject1(int value) : value(value) { print_created(this, toString()); } + std::string toString() const override { return "MyObject1[" + std::to_string(value) + "]"; } + +protected: + ~MyObject1() override { print_destroyed(this); } + +private: + int value; +}; + +// Object managed by a std::shared_ptr<> +class MyObject2 { +public: + MyObject2(const MyObject2 &) = default; + explicit MyObject2(int value) : value(value) { print_created(this, toString()); } + std::string toString() const { return "MyObject2[" + std::to_string(value) + "]"; } + virtual ~MyObject2() { print_destroyed(this); } + +private: + int value; +}; + +// Object managed by a std::shared_ptr<>, additionally derives from std::enable_shared_from_this<> +class MyObject3 : public std::enable_shared_from_this { +public: + MyObject3(const MyObject3 &) = default; + explicit MyObject3(int value) : value(value) { print_created(this, toString()); } + std::string toString() const { return "MyObject3[" + std::to_string(value) + "]"; } + virtual ~MyObject3() { print_destroyed(this); } + +private: + int value; +}; + +// test_unique_nodelete +// Object with a private destructor +class MyObject4; +std::unordered_set myobject4_instances; +class MyObject4 { +public: + explicit MyObject4(int value) : value{value} { + print_created(this); + myobject4_instances.insert(this); + } + int value; + + static void cleanupAllInstances() { + auto tmp = std::move(myobject4_instances); + myobject4_instances.clear(); + for (auto *o : tmp) { + delete o; + } + } + +private: + ~MyObject4() { + myobject4_instances.erase(this); + print_destroyed(this); + } +}; + +// test_unique_deleter +// Object with std::unique_ptr where D is not matching the base class +// Object with a protected destructor +class MyObject4a; +std::unordered_set myobject4a_instances; +class MyObject4a { +public: + explicit MyObject4a(int i) : value{i} { + print_created(this); + myobject4a_instances.insert(this); + }; + int value; + + static void cleanupAllInstances() { + auto tmp = std::move(myobject4a_instances); + myobject4a_instances.clear(); + for (auto *o : tmp) { + delete o; + } + } + +protected: + virtual ~MyObject4a() { + myobject4a_instances.erase(this); + print_destroyed(this); + } +}; + +// Object derived but with public destructor and no Deleter in default holder +class MyObject4b : public MyObject4a { +public: + explicit MyObject4b(int i) : MyObject4a(i) { print_created(this); } + ~MyObject4b() override { print_destroyed(this); } +}; + +// test_large_holder +class MyObject5 { // managed by huge_unique_ptr +public: + explicit MyObject5(int value) : value{value} { print_created(this); } + ~MyObject5() { print_destroyed(this); } + int value; +}; + +// test_shared_ptr_and_references +struct SharedPtrRef { + struct A { + A() { print_created(this); } + A(const A &) { print_copy_created(this); } + A(A &&) noexcept { print_move_created(this); } + ~A() { print_destroyed(this); } + }; + + A value = {}; + std::shared_ptr shared = std::make_shared(); }; -PYBIND11_DECLARE_HOLDER_TYPE(T, unique_ptr_with_addressof_operator); +// test_shared_ptr_from_this_and_references +struct SharedFromThisRef { + struct B : std::enable_shared_from_this { + B() { print_created(this); } + // NOLINTNEXTLINE(bugprone-copy-constructor-init) + B(const B &) : std::enable_shared_from_this() { print_copy_created(this); } + B(B &&) noexcept : std::enable_shared_from_this() { print_move_created(this); } + ~B() { print_destroyed(this); } + }; + + B value = {}; + std::shared_ptr shared = std::make_shared(); +}; + +// Issue #865: shared_from_this doesn't work with virtual inheritance +struct SharedFromThisVBase : std::enable_shared_from_this { + SharedFromThisVBase() = default; + SharedFromThisVBase(const SharedFromThisVBase &) = default; + virtual ~SharedFromThisVBase() = default; +}; +struct SharedFromThisVirt : virtual SharedFromThisVBase {}; + +// test_move_only_holder +struct C { + C() { print_created(this); } + ~C() { print_destroyed(this); } +}; + +// test_holder_with_addressof_operator +struct TypeForHolderWithAddressOf { + TypeForHolderWithAddressOf() { print_created(this); } + TypeForHolderWithAddressOf(const TypeForHolderWithAddressOf &) { print_copy_created(this); } + TypeForHolderWithAddressOf(TypeForHolderWithAddressOf &&) noexcept { + print_move_created(this); + } + ~TypeForHolderWithAddressOf() { print_destroyed(this); } + std::string toString() const { + return "TypeForHolderWithAddressOf[" + std::to_string(value) + "]"; + } + int value = 42; +}; + +// test_move_only_holder_with_addressof_operator +struct TypeForMoveOnlyHolderWithAddressOf { + explicit TypeForMoveOnlyHolderWithAddressOf(int value) : value{value} { print_created(this); } + ~TypeForMoveOnlyHolderWithAddressOf() { print_destroyed(this); } + std::string toString() const { + return "MoveOnlyHolderWithAddressOf[" + std::to_string(value) + "]"; + } + int value; +}; + +// test_smart_ptr_from_default +struct HeldByDefaultHolder {}; + +// test_shared_ptr_gc +// #187: issue involving std::shared_ptr<> return value policy & garbage collection +struct ElementBase { + virtual ~ElementBase() = default; /* Force creation of virtual table */ + ElementBase() = default; + ElementBase(const ElementBase &) = delete; +}; + +struct ElementA : ElementBase { + explicit ElementA(int v) : v(v) {} + int value() const { return v; } + int v; +}; + +struct ElementList { + void add(const std::shared_ptr &e) { l.push_back(e); } + std::vector> l; +}; + +} // namespace + +// ref is a wrapper for 'Object' which uses intrusive reference counting +// It is always possible to construct a ref from an Object* pointer without +// possible inconsistencies, hence the 'true' argument at the end. +// Make pybind11 aware of the non-standard getter member function +namespace PYBIND11_NAMESPACE { +namespace detail { +template +struct holder_helper> { + static const T *get(const ref &p) { return p.get_ptr(); } +}; +} // namespace detail +} // namespace PYBIND11_NAMESPACE + +// Make pybind aware of the ref-counted wrapper type (s): +PYBIND11_DECLARE_HOLDER_TYPE(T, ref, true); +// The following is not required anymore for std::shared_ptr, but it should compile without error: +PYBIND11_DECLARE_HOLDER_TYPE(T, std::shared_ptr); +PYBIND11_DECLARE_HOLDER_TYPE(T, huge_unique_ptr); +PYBIND11_DECLARE_HOLDER_TYPE(T, custom_unique_ptr); +PYBIND11_DECLARE_HOLDER_TYPE(T, shared_ptr_with_addressof_operator); +PYBIND11_DECLARE_HOLDER_TYPE(T, unique_ptr_with_addressof_operator); TEST_SUBMODULE(smart_ptr, m) { + // Please do not interleave `struct` and `class` definitions with bindings code, + // but implement `struct`s and `class`es in the anonymous namespace above. + // This helps keeping the smart_holder branch in sync with master. // test_smart_ptr @@ -94,24 +295,13 @@ TEST_SUBMODULE(smart_ptr, m) { py::class_> obj(m, "Object"); obj.def("getRefCount", &Object::getRefCount); - // Custom object with builtin reference counting (see 'object.h' for the implementation) - class MyObject1 : public Object { - public: - MyObject1(int value) : value(value) { print_created(this, toString()); } - std::string toString() const override { return "MyObject1[" + std::to_string(value) + "]"; } - protected: - ~MyObject1() override { print_destroyed(this); } - private: - int value; - }; - py::class_>(m, "MyObject1", obj) - .def(py::init()); + py::class_>(m, "MyObject1", obj).def(py::init()); py::implicitly_convertible(); m.def("make_object_1", []() -> Object * { return new MyObject1(1); }); - m.def("make_object_2", []() -> ref { return new MyObject1(2); }); + m.def("make_object_2", []() -> ref { return ref(new MyObject1(2)); }); m.def("make_myobject1_1", []() -> MyObject1 * { return new MyObject1(4); }); - m.def("make_myobject1_2", []() -> ref { return new MyObject1(5); }); + m.def("make_myobject1_2", []() -> ref { return ref(new MyObject1(5)); }); m.def("print_object_1", [](const Object *obj) { py::print(obj->toString()); }); m.def("print_object_2", [](ref obj) { py::print(obj->toString()); }); m.def("print_object_3", [](const ref &obj) { py::print(obj->toString()); }); @@ -124,48 +314,31 @@ TEST_SUBMODULE(smart_ptr, m) { // Expose constructor stats for the ref type m.def("cstats_ref", &ConstructorStats::get); - - // Object managed by a std::shared_ptr<> - class MyObject2 { - public: - MyObject2(const MyObject2 &) = default; - MyObject2(int value) : value(value) { print_created(this, toString()); } - std::string toString() const { return "MyObject2[" + std::to_string(value) + "]"; } - virtual ~MyObject2() { print_destroyed(this); } - private: - int value; - }; - py::class_>(m, "MyObject2") - .def(py::init()); + py::class_>(m, "MyObject2").def(py::init()); m.def("make_myobject2_1", []() { return new MyObject2(6); }); m.def("make_myobject2_2", []() { return std::make_shared(7); }); m.def("print_myobject2_1", [](const MyObject2 *obj) { py::print(obj->toString()); }); + // NOLINTNEXTLINE(performance-unnecessary-value-param) m.def("print_myobject2_2", [](std::shared_ptr obj) { py::print(obj->toString()); }); - m.def("print_myobject2_3", [](const std::shared_ptr &obj) { py::print(obj->toString()); }); - m.def("print_myobject2_4", [](const std::shared_ptr *obj) { py::print((*obj)->toString()); }); - - // Object managed by a std::shared_ptr<>, additionally derives from std::enable_shared_from_this<> - class MyObject3 : public std::enable_shared_from_this { - public: - MyObject3(const MyObject3 &) = default; - MyObject3(int value) : value(value) { print_created(this, toString()); } - std::string toString() const { return "MyObject3[" + std::to_string(value) + "]"; } - virtual ~MyObject3() { print_destroyed(this); } - private: - int value; - }; - py::class_>(m, "MyObject3") - .def(py::init()); + m.def("print_myobject2_3", + [](const std::shared_ptr &obj) { py::print(obj->toString()); }); + m.def("print_myobject2_4", + [](const std::shared_ptr *obj) { py::print((*obj)->toString()); }); + + py::class_>(m, "MyObject3").def(py::init()); m.def("make_myobject3_1", []() { return new MyObject3(8); }); m.def("make_myobject3_2", []() { return std::make_shared(9); }); m.def("print_myobject3_1", [](const MyObject3 *obj) { py::print(obj->toString()); }); + // NOLINTNEXTLINE(performance-unnecessary-value-param) m.def("print_myobject3_2", [](std::shared_ptr obj) { py::print(obj->toString()); }); - m.def("print_myobject3_3", [](const std::shared_ptr &obj) { py::print(obj->toString()); }); - m.def("print_myobject3_4", [](const std::shared_ptr *obj) { py::print((*obj)->toString()); }); + m.def("print_myobject3_3", + [](const std::shared_ptr &obj) { py::print(obj->toString()); }); + m.def("print_myobject3_4", + [](const std::shared_ptr *obj) { py::print((*obj)->toString()); }); // test_smart_ptr_refcounting m.def("test_object1_refcounting", []() { - ref o = new MyObject1(0); + auto o = ref(new MyObject1(0)); bool good = o->getRefCount() == 1; py::object o2 = py::cast(o, py::return_value_policy::reference); // always request (partial) ownership for objects with intrusive @@ -175,196 +348,123 @@ TEST_SUBMODULE(smart_ptr, m) { }); // test_unique_nodelete - // Object with a private destructor - class MyObject4 { - public: - MyObject4(int value) : value{value} { print_created(this); } - int value; - private: - ~MyObject4() { print_destroyed(this); } - }; py::class_>(m, "MyObject4") .def(py::init()) - .def_readwrite("value", &MyObject4::value); + .def_readwrite("value", &MyObject4::value) + .def_static("cleanup_all_instances", &MyObject4::cleanupAllInstances); // test_unique_deleter - // Object with std::unique_ptr where D is not matching the base class - // Object with a protected destructor - class MyObject4a { - public: - MyObject4a(int i) { - value = i; - print_created(this); - }; - int value; - protected: - virtual ~MyObject4a() { print_destroyed(this); } - }; py::class_>(m, "MyObject4a") .def(py::init()) - .def_readwrite("value", &MyObject4a::value); + .def_readwrite("value", &MyObject4a::value) + .def_static("cleanup_all_instances", &MyObject4a::cleanupAllInstances); - // Object derived but with public destructor and no Deleter in default holder - class MyObject4b : public MyObject4a { - public: - MyObject4b(int i) : MyObject4a(i) { print_created(this); } - ~MyObject4b() override { print_destroyed(this); } - }; - py::class_(m, "MyObject4b") + py::class_>(m, "MyObject4b") .def(py::init()); // test_large_holder - class MyObject5 { // managed by huge_unique_ptr - public: - MyObject5(int value) : value{value} { print_created(this); } - ~MyObject5() { print_destroyed(this); } - int value; - }; py::class_>(m, "MyObject5") .def(py::init()) .def_readwrite("value", &MyObject5::value); // test_shared_ptr_and_references - struct SharedPtrRef { - struct A { - A() { print_created(this); } - A(const A &) { print_copy_created(this); } - A(A &&) { print_move_created(this); } - ~A() { print_destroyed(this); } - }; - - A value = {}; - std::shared_ptr shared = std::make_shared(); - }; using A = SharedPtrRef::A; py::class_>(m, "A"); - py::class_(m, "SharedPtrRef") + py::class_>(m, "SharedPtrRef") .def(py::init<>()) .def_readonly("ref", &SharedPtrRef::value) - .def_property_readonly("copy", [](const SharedPtrRef &s) { return s.value; }, - py::return_value_policy::copy) + .def_property_readonly( + "copy", [](const SharedPtrRef &s) { return s.value; }, py::return_value_policy::copy) .def_readonly("holder_ref", &SharedPtrRef::shared) - .def_property_readonly("holder_copy", [](const SharedPtrRef &s) { return s.shared; }, - py::return_value_policy::copy) + .def_property_readonly( + "holder_copy", + [](const SharedPtrRef &s) { return s.shared; }, + py::return_value_policy::copy) .def("set_ref", [](SharedPtrRef &, const A &) { return true; }) + // NOLINTNEXTLINE(performance-unnecessary-value-param) .def("set_holder", [](SharedPtrRef &, std::shared_ptr) { return true; }); // test_shared_ptr_from_this_and_references - struct SharedFromThisRef { - struct B : std::enable_shared_from_this { - B() { print_created(this); } - B(const B &) : std::enable_shared_from_this() { print_copy_created(this); } - B(B &&) : std::enable_shared_from_this() { print_move_created(this); } - ~B() { print_destroyed(this); } - }; - - B value = {}; - std::shared_ptr shared = std::make_shared(); - }; using B = SharedFromThisRef::B; py::class_>(m, "B"); - py::class_(m, "SharedFromThisRef") + py::class_>(m, "SharedFromThisRef") .def(py::init<>()) .def_readonly("bad_wp", &SharedFromThisRef::value) - .def_property_readonly("ref", [](const SharedFromThisRef &s) -> const B & { return *s.shared; }) - .def_property_readonly("copy", [](const SharedFromThisRef &s) { return s.value; }, - py::return_value_policy::copy) + .def_property_readonly("ref", + [](const SharedFromThisRef &s) -> const B & { return *s.shared; }) + .def_property_readonly( + "copy", + [](const SharedFromThisRef &s) { return s.value; }, + py::return_value_policy::copy) .def_readonly("holder_ref", &SharedFromThisRef::shared) - .def_property_readonly("holder_copy", [](const SharedFromThisRef &s) { return s.shared; }, - py::return_value_policy::copy) + .def_property_readonly( + "holder_copy", + [](const SharedFromThisRef &s) { return s.shared; }, + py::return_value_policy::copy) .def("set_ref", [](SharedFromThisRef &, const B &) { return true; }) + // NOLINTNEXTLINE(performance-unnecessary-value-param) .def("set_holder", [](SharedFromThisRef &, std::shared_ptr) { return true; }); // Issue #865: shared_from_this doesn't work with virtual inheritance - struct SharedFromThisVBase : std::enable_shared_from_this { - SharedFromThisVBase() = default; - SharedFromThisVBase(const SharedFromThisVBase &) = default; - virtual ~SharedFromThisVBase() = default; - }; - struct SharedFromThisVirt : virtual SharedFromThisVBase {}; static std::shared_ptr sft(new SharedFromThisVirt()); py::class_>(m, "SharedFromThisVirt") .def_static("get", []() { return sft.get(); }); // test_move_only_holder - struct C { - C() { print_created(this); } - ~C() { print_destroyed(this); } - }; py::class_>(m, "TypeWithMoveOnlyHolder") .def_static("make", []() { return custom_unique_ptr(new C); }) .def_static("make_as_object", []() { return py::cast(custom_unique_ptr(new C)); }); // test_holder_with_addressof_operator - struct TypeForHolderWithAddressOf { - TypeForHolderWithAddressOf() { print_created(this); } - TypeForHolderWithAddressOf(const TypeForHolderWithAddressOf &) { print_copy_created(this); } - TypeForHolderWithAddressOf(TypeForHolderWithAddressOf &&) { print_move_created(this); } - ~TypeForHolderWithAddressOf() { print_destroyed(this); } - std::string toString() const { - return "TypeForHolderWithAddressOf[" + std::to_string(value) + "]"; - } - int value = 42; - }; using HolderWithAddressOf = shared_ptr_with_addressof_operator; py::class_(m, "TypeForHolderWithAddressOf") .def_static("make", []() { return HolderWithAddressOf(new TypeForHolderWithAddressOf); }) .def("get", [](const HolderWithAddressOf &self) { return self.get(); }) - .def("print_object_1", [](const TypeForHolderWithAddressOf *obj) { py::print(obj->toString()); }) + .def("print_object_1", + [](const TypeForHolderWithAddressOf *obj) { py::print(obj->toString()); }) + // NOLINTNEXTLINE(performance-unnecessary-value-param) .def("print_object_2", [](HolderWithAddressOf obj) { py::print(obj.get()->toString()); }) - .def("print_object_3", [](const HolderWithAddressOf &obj) { py::print(obj.get()->toString()); }) - .def("print_object_4", [](const HolderWithAddressOf *obj) { py::print((*obj).get()->toString()); }); + .def("print_object_3", + [](const HolderWithAddressOf &obj) { py::print(obj.get()->toString()); }) + .def("print_object_4", + [](const HolderWithAddressOf *obj) { py::print((*obj).get()->toString()); }); // test_move_only_holder_with_addressof_operator - struct TypeForMoveOnlyHolderWithAddressOf { - TypeForMoveOnlyHolderWithAddressOf(int value) : value{value} { print_created(this); } - ~TypeForMoveOnlyHolderWithAddressOf() { print_destroyed(this); } - std::string toString() const { - return "MoveOnlyHolderWithAddressOf[" + std::to_string(value) + "]"; - } - int value; - }; - using MoveOnlyHolderWithAddressOf = unique_ptr_with_addressof_operator; - py::class_(m, "TypeForMoveOnlyHolderWithAddressOf") - .def_static("make", []() { return MoveOnlyHolderWithAddressOf(new TypeForMoveOnlyHolderWithAddressOf(0)); }) + using MoveOnlyHolderWithAddressOf + = unique_ptr_with_addressof_operator; + py::class_( + m, "TypeForMoveOnlyHolderWithAddressOf") + .def_static("make", + []() { + return MoveOnlyHolderWithAddressOf( + new TypeForMoveOnlyHolderWithAddressOf(0)); + }) .def_readwrite("value", &TypeForMoveOnlyHolderWithAddressOf::value) - .def("print_object", [](const TypeForMoveOnlyHolderWithAddressOf *obj) { py::print(obj->toString()); }); + .def("print_object", + [](const TypeForMoveOnlyHolderWithAddressOf *obj) { py::print(obj->toString()); }); // test_smart_ptr_from_default - struct HeldByDefaultHolder { }; - py::class_(m, "HeldByDefaultHolder") + py::class_>(m, "HeldByDefaultHolder") .def(py::init<>()) + // NOLINTNEXTLINE(performance-unnecessary-value-param) .def_static("load_shared_ptr", [](std::shared_ptr) {}); // test_shared_ptr_gc // #187: issue involving std::shared_ptr<> return value policy & garbage collection - struct ElementBase { - virtual ~ElementBase() = default; /* Force creation of virtual table */ - ElementBase() = default; - ElementBase(const ElementBase&) = delete; - }; py::class_>(m, "ElementBase"); - struct ElementA : ElementBase { - ElementA(int v) : v(v) { } - int value() { return v; } - int v; - }; py::class_>(m, "ElementA") .def(py::init()) .def("value", &ElementA::value); - struct ElementList { - void add(std::shared_ptr e) { l.push_back(e); } - std::vector> l; - }; py::class_>(m, "ElementList") .def(py::init<>()) .def("add", &ElementList::add) .def("get", [](ElementList &el) { py::list list; - for (auto &e : el.l) + for (auto &e : el.l) { list.append(py::cast(e)); + } return list; }); } diff --git a/3rdparty/pybind11/tests/test_smart_ptr.py b/3rdparty/pybind11/tests/test_smart_ptr.py index c55bffba..2f204e01 100644 --- a/3rdparty/pybind11/tests/test_smart_ptr.py +++ b/3rdparty/pybind11/tests/test_smart_ptr.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- import pytest m = pytest.importorskip("pybind11_tests.smart_ptr") @@ -16,7 +15,7 @@ def test_smart_ptr(capture): m.print_object_2(o) m.print_object_3(o) m.print_object_4(o) - assert capture == "MyObject1[{i}]\n".format(i=i) * 4 + assert capture == f"MyObject1[{i}]\n" * 4 for i, o in enumerate( [m.make_myobject1_1(), m.make_myobject1_2(), m.MyObject1(6), 7], start=4 @@ -34,13 +33,11 @@ def test_smart_ptr(capture): m.print_myobject1_4(o) times = 4 if isinstance(o, int) else 8 - assert capture == "MyObject1[{i}]\n".format(i=i) * times + assert capture == f"MyObject1[{i}]\n" * times cstats = ConstructorStats.get(m.MyObject1) assert cstats.alive() == 0 - expected_values = ["MyObject1[{}]".format(i) for i in range(1, 7)] + [ - "MyObject1[7]" - ] * 4 + expected_values = [f"MyObject1[{i}]" for i in range(1, 7)] + ["MyObject1[7]"] * 4 assert cstats.values() == expected_values assert cstats.default_constructions == 0 assert cstats.copy_constructions == 0 @@ -58,7 +55,7 @@ def test_smart_ptr(capture): m.print_myobject2_2(o) m.print_myobject2_3(o) m.print_myobject2_4(o) - assert capture == "MyObject2[{i}]\n".format(i=i) * 4 + assert capture == f"MyObject2[{i}]\n" * 4 cstats = ConstructorStats.get(m.MyObject2) assert cstats.alive() == 1 @@ -81,7 +78,7 @@ def test_smart_ptr(capture): m.print_myobject3_2(o) m.print_myobject3_3(o) m.print_myobject3_4(o) - assert capture == "MyObject3[{i}]\n".format(i=i) * 4 + assert capture == f"MyObject3[{i}]\n" * 4 cstats = ConstructorStats.get(m.MyObject3) assert cstats.alive() == 1 @@ -125,7 +122,9 @@ def test_unique_nodelete(): cstats = ConstructorStats.get(m.MyObject4) assert cstats.alive() == 1 del o - assert cstats.alive() == 1 # Leak, but that's intentional + assert cstats.alive() == 1 + m.MyObject4.cleanup_all_instances() + assert cstats.alive() == 0 def test_unique_nodelete4a(): @@ -134,19 +133,25 @@ def test_unique_nodelete4a(): cstats = ConstructorStats.get(m.MyObject4a) assert cstats.alive() == 1 del o - assert cstats.alive() == 1 # Leak, but that's intentional + assert cstats.alive() == 1 + m.MyObject4a.cleanup_all_instances() + assert cstats.alive() == 0 def test_unique_deleter(): + m.MyObject4a(0) o = m.MyObject4b(23) assert o.value == 23 cstats4a = ConstructorStats.get(m.MyObject4a) - assert cstats4a.alive() == 2 # Two because of previous test + assert cstats4a.alive() == 2 cstats4b = ConstructorStats.get(m.MyObject4b) assert cstats4b.alive() == 1 del o - assert cstats4a.alive() == 1 # Should now only be one leftover from previous test + assert cstats4a.alive() == 1 # Should now only be one leftover assert cstats4b.alive() == 0 # Should be deleted + m.MyObject4a.cleanup_all_instances() + assert cstats4a.alive() == 0 + assert cstats4b.alive() == 0 def test_large_holder(): diff --git a/3rdparty/pybind11/tests/test_stl.cpp b/3rdparty/pybind11/tests/test_stl.cpp index 05901627..d45465d6 100644 --- a/3rdparty/pybind11/tests/test_stl.cpp +++ b/3rdparty/pybind11/tests/test_stl.cpp @@ -7,22 +7,44 @@ BSD-style license that can be found in the LICENSE file. */ -#include "pybind11_tests.h" -#include "constructor_stats.h" #include -#include +#include "constructor_stats.h" +#include "pybind11_tests.h" + +#ifndef PYBIND11_HAS_FILESYSTEM_IS_OPTIONAL +# define PYBIND11_HAS_FILESYSTEM_IS_OPTIONAL +#endif +#include + #include +#include + +#if defined(PYBIND11_TEST_BOOST) +# include + +namespace PYBIND11_NAMESPACE { +namespace detail { +template +struct type_caster> : optional_caster> {}; + +template <> +struct type_caster : void_caster {}; +} // namespace detail +} // namespace PYBIND11_NAMESPACE +#endif // Test with `std::variant` in C++17 mode, or with `boost::variant` in C++11/14 #if defined(PYBIND11_HAS_VARIANT) using std::variant; -#elif defined(PYBIND11_TEST_BOOST) && (!defined(_MSC_VER) || _MSC_VER >= 1910) -# include -# define PYBIND11_HAS_VARIANT 1 +# define PYBIND11_TEST_VARIANT 1 +#elif defined(PYBIND11_TEST_BOOST) +# include +# define PYBIND11_TEST_VARIANT 1 using boost::variant; -namespace pybind11 { namespace detail { +namespace PYBIND11_NAMESPACE { +namespace detail { template struct type_caster> : variant_caster> {}; @@ -33,33 +55,117 @@ struct visit_helper { return boost::apply_visitor(args...); } }; -}} // namespace pybind11::detail +} // namespace detail +} // namespace PYBIND11_NAMESPACE #endif PYBIND11_MAKE_OPAQUE(std::vector>); /// Issue #528: templated constructor struct TplCtorClass { - template TplCtorClass(const T &) { } + template + explicit TplCtorClass(const T &) {} bool operator==(const TplCtorClass &) const { return true; } }; namespace std { - template <> - struct hash { size_t operator()(const TplCtorClass &) const { return 0; } }; +template <> +struct hash { + size_t operator()(const TplCtorClass &) const { return 0; } +}; } // namespace std - template