diff options
Diffstat (limited to '3rdparty/pybind11/tests')
96 files changed, 4431 insertions, 1554 deletions
diff --git a/3rdparty/pybind11/tests/CMakeLists.txt b/3rdparty/pybind11/tests/CMakeLists.txt index 765c47ad..dae8b5ad 100644 --- a/3rdparty/pybind11/tests/CMakeLists.txt +++ b/3rdparty/pybind11/tests/CMakeLists.txt @@ -5,78 +5,150 @@ # All rights reserved. Use of this source code is governed by a # BSD-style license that can be found in the LICENSE file. -cmake_minimum_required(VERSION 2.8.12) +cmake_minimum_required(VERSION 3.4) -option(PYBIND11_WERROR "Report all warnings as errors" OFF) +# 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) + cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}) +else() + cmake_policy(VERSION 3.18) +endif() + +# Only needed for CMake < 3.5 support +include(CMakeParseArguments) + +# Filter out items; print an optional message if any items filtered +# +# Usage: +# pybind11_filter_tests(LISTNAME file1.cpp file2.cpp ... MESSAGE "") +# +macro(PYBIND11_FILTER_TESTS LISTNAME) + cmake_parse_arguments(ARG "" "MESSAGE" "" ${ARGN}) + set(PYBIND11_FILTER_TESTS_FOUND OFF) + foreach(filename IN LISTS ARG_UNPARSED_ARGUMENTS) + list(FIND ${LISTNAME} ${filename} _FILE_FOUND) + if(_FILE_FOUND GREATER -1) + list(REMOVE_AT ${LISTNAME} ${_FILE_FOUND}) + set(PYBIND11_FILTER_TESTS_FOUND ON) + endif() + endforeach() + if(PYBIND11_FILTER_TESTS_FOUND AND ARG_MESSAGE) + message(STATUS "${ARG_MESSAGE}") + endif() +endmacro() -if (CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR) - # We're being loaded directly, i.e. not via add_subdirectory, so make this - # work as its own project and load the pybind11Config to get the tools we need - project(pybind11_tests CXX) +# New Python support +if(DEFINED Python_EXECUTABLE) + set(PYTHON_EXECUTABLE "${Python_EXECUTABLE}") + set(PYTHON_VERSION "${Python_VERSION}") +endif() - find_package(pybind11 REQUIRED CONFIG) +# There's no harm in including a project in a project +project(pybind11_tests CXX) + +# Access FindCatch and more +list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/../tools") + +option(PYBIND11_WERROR "Report all warnings as errors" OFF) +option(DOWNLOAD_EIGEN "Download EIGEN (requires CMake 3.11+)" OFF) +option(PYBIND11_CUDA_TESTS "Enable building CUDA tests (requires CMake 3.12+)" OFF) +set(PYBIND11_TEST_OVERRIDE + "" + CACHE STRING "Tests from ;-separated list of *.cpp files will be built instead of all tests") +set(PYBIND11_TEST_FILTER + "" + CACHE STRING "Tests from ;-separated list of *.cpp files will be removed from all tests") + +if(CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR) + # We're being loaded directly, i.e. not via add_subdirectory, so make this + # work as its own project and load the pybind11Config to get the tools we need + find_package(pybind11 REQUIRED CONFIG) endif() if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) message(STATUS "Setting tests build type to MinSizeRel as none was specified") - set(CMAKE_BUILD_TYPE MinSizeRel CACHE STRING "Choose the type of build." FORCE) - set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" - "MinSizeRel" "RelWithDebInfo") + set(CMAKE_BUILD_TYPE + MinSizeRel + CACHE STRING "Choose the type of build." FORCE) + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" "MinSizeRel" + "RelWithDebInfo") +endif() + +if(PYBIND11_CUDA_TESTS) + enable_language(CUDA) + if(DEFINED CMAKE_CXX_STANDARD) + set(CMAKE_CUDA_STANDARD ${CMAKE_CXX_STANDARD}) + endif() + set(CMAKE_CUDA_STANDARD_REQUIRED ON) endif() # Full set of test files (you can override these; see below) 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_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.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) # Invoking cmake with something like: -# cmake -DPYBIND11_TEST_OVERRIDE="test_callbacks.cpp;test_picking.cpp" .. +# 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) +if(PYBIND11_TEST_OVERRIDE) set(PYBIND11_TEST_FILES ${PYBIND11_TEST_OVERRIDE}) endif() -# Skip test_async for Python < 3.5 -list(FIND PYBIND11_TEST_FILES test_async.cpp PYBIND11_TEST_FILES_ASYNC_I) -if((PYBIND11_TEST_FILES_ASYNC_I GREATER -1) AND ("${PYTHON_VERSION_MAJOR}.${PYTHON_VERSION_MINOR}" VERSION_LESS 3.5)) - message(STATUS "Skipping test_async because Python version ${PYTHON_VERSION_MAJOR}.${PYTHON_VERSION_MINOR} < 3.5") - list(REMOVE_AT PYBIND11_TEST_FILES ${PYBIND11_TEST_FILES_ASYNC_I}) +# You can also filter tests: +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 +if(PYBIND11_CUDA_TESTS) + pybind11_filter_tests( + PYBIND11_TEST_FILES test_constants_and_functions.cpp MESSAGE + "Skipping test_constants_and_functions due to incompatible exception specifications") endif() string(REPLACE ".cpp" ".py" PYBIND11_PYTEST_FILES "${PYBIND11_TEST_FILES}") @@ -84,16 +156,10 @@ string(REPLACE ".cpp" ".py" PYBIND11_PYTEST_FILES "${PYBIND11_TEST_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 -) +set(PYBIND11_CROSS_MODULE_TESTS test_exceptions.py test_local_bindings.py test_stl.py + test_stl_binders.py) -set(PYBIND11_CROSS_MODULE_GIL_TESTS - test_gil_scoped.py -) +set(PYBIND11_CROSS_MODULE_GIL_TESTS test_gil_scoped.py) # 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" @@ -103,21 +169,45 @@ if(PYBIND11_TEST_FILES_EIGEN_I GREATER -1) # Try loading via newer Eigen's Eigen3Config first (bypassing tools/FindEigen3.cmake). # Eigen 3.3.1+ exports a cmake 3.0+ target for handling dependency requirements, but also # produces a fatal error if loaded from a pre-3.0 cmake. - if (NOT CMAKE_VERSION VERSION_LESS 3.0) + if(DOWNLOAD_EIGEN) + if(CMAKE_VERSION VERSION_LESS 3.11) + 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}) + + FetchContent_GetProperties(eigen) + if(NOT eigen_POPULATED) + message(STATUS "Downloading Eigen") + FetchContent_Populate(eigen) + endif() + + set(EIGEN3_INCLUDE_DIR ${eigen_SOURCE_DIR}) + set(EIGEN3_FOUND TRUE) + + else() find_package(Eigen3 3.2.7 QUIET CONFIG) - if (EIGEN3_FOUND) - if (EIGEN3_VERSION_STRING AND NOT EIGEN3_VERSION_STRING VERSION_LESS 3.3.1) - set(PYBIND11_EIGEN_VIA_TARGET 1) - endif() + + if(NOT EIGEN3_FOUND) + # Couldn't load via target, so fall back to allowing module mode finding, which will pick up + # tools/FindEigen3.cmake + find_package(Eigen3 3.2.7 QUIET) endif() endif() - if (NOT EIGEN3_FOUND) - # Couldn't load via target, so fall back to allowing module mode finding, which will pick up - # tools/FindEigen3.cmake - find_package(Eigen3 3.2.7 QUIET) - endif() if(EIGEN3_FOUND) + if(NOT TARGET Eigen3::Eigen) + add_library(Eigen3::Eigen IMPORTED INTERFACE) + set_property(TARGET Eigen3::Eigen PROPERTY INTERFACE_INCLUDE_DIRECTORIES + "${EIGEN3_INCLUDE_DIR}") + endif() + # Eigen 3.3.1+ cmake sets EIGEN3_VERSION_STRING (and hard codes the version when installed # rather than looking it up in the cmake script); older versions, and the # tools/FindEigen3.cmake, set EIGEN3_VERSION instead. @@ -127,28 +217,63 @@ 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") + message(STATUS "Building tests WITHOUT Eigen, use -DDOWNLOAD_EIGEN on CMake 3.11+ to download") endif() endif() # Optional dependency for some tests (boost::variant is only supported with version >= 1.56) find_package(Boost 1.56) +if(Boost_FOUND) + if(NOT TARGET Boost::headers) + add_library(Boost::headers IMPORTED INTERFACE) + if(TARGET Boost::boost) + # Classic FindBoost + set_property(TARGET Boost::boost PROPERTY INTERFACE_LINK_LIBRARIES Boost::boost) + else() + # Very old FindBoost, or newer Boost than CMake in older CMakes + set_property(TARGET Boost::headers PROPERTY INTERFACE_INCLUDE_DIRECTORIES + ${Boost_INCLUDE_DIRS}) + endif() + endif() +endif() + # Compile with compiler warnings turned on function(pybind11_enable_warnings target_name) if(MSVC) target_compile_options(${target_name} PRIVATE /W4) - elseif(CMAKE_CXX_COMPILER_ID MATCHES "(GNU|Intel|Clang)") - target_compile_options(${target_name} PRIVATE -Wall -Wextra -Wconversion -Wcast-qual -Wdeprecated) + elseif(CMAKE_CXX_COMPILER_ID MATCHES "(GNU|Intel|Clang)" AND NOT PYBIND11_CUDA_TESTS) + target_compile_options( + ${target_name} + PRIVATE -Wall + -Wextra + -Wconversion + -Wcast-qual + -Wdeprecated + -Wundef + -Wnon-virtual-dtor) endif() if(PYBIND11_WERROR) if(MSVC) 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)") 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) + endif() + endif() endfunction() set(test_targets pybind11_tests) @@ -156,7 +281,7 @@ 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) + if(i GREATER -1) list(APPEND test_targets pybind11_cross_module_tests) break() endif() @@ -164,78 +289,101 @@ endforeach() foreach(t ${PYBIND11_CROSS_MODULE_GIL_TESTS}) list(FIND PYBIND11_PYTEST_FILES ${t} i) - if (i GREATER -1) + if(i GREATER -1) list(APPEND test_targets cross_module_gil_utils) break() endif() endforeach() -set(testdir ${CMAKE_CURRENT_SOURCE_DIR}) +# Support CUDA testing by forcing the target file to compile with NVCC +if(PYBIND11_CUDA_TESTS) + set_property(SOURCE ${PYBIND11_TEST_FILES} PROPERTY LANGUAGE CUDA) +endif() + foreach(target ${test_targets}) set(test_files ${PYBIND11_TEST_FILES}) - if(NOT target STREQUAL "pybind11_tests") + if(NOT "${target}" STREQUAL "pybind11_tests") set(test_files "") endif() + # Support CUDA testing by forcing the target file to compile with NVCC + if(PYBIND11_CUDA_TESTS) + set_property(SOURCE ${target}.cpp PROPERTY LANGUAGE CUDA) + endif() + # Create the binding library pybind11_add_module(${target} THIN_LTO ${target}.cpp ${test_files} ${PYBIND11_HEADERS}) pybind11_enable_warnings(${target}) + if(NOT CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_CURRENT_BINARY_DIR) + get_property( + suffix + TARGET ${target} + PROPERTY SUFFIX) + set(source_output "${CMAKE_CURRENT_SOURCE_DIR}/${target}${suffix}") + if(suffix AND EXISTS "${source_output}") + message(WARNING "Output file also in source directory; " + "please remove to avoid confusion: ${source_output}") + endif() + endif() + if(MSVC) target_compile_options(${target} PRIVATE /utf-8) endif() if(EIGEN3_FOUND) - if (PYBIND11_EIGEN_VIA_TARGET) - target_link_libraries(${target} PRIVATE Eigen3::Eigen) - else() - target_include_directories(${target} PRIVATE ${EIGEN3_INCLUDE_DIR}) - endif() + target_link_libraries(${target} PRIVATE Eigen3::Eigen) target_compile_definitions(${target} PRIVATE -DPYBIND11_TEST_EIGEN) endif() if(Boost_FOUND) - target_include_directories(${target} PRIVATE ${Boost_INCLUDE_DIRS}) + target_link_libraries(${target} PRIVATE Boost::headers) target_compile_definitions(${target} PRIVATE -DPYBIND11_TEST_BOOST) endif() # 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 ${testdir}) + 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} ${testdir}) + set_target_properties(${target} PROPERTIES LIBRARY_OUTPUT_DIRECTORY_${config} + "${CMAKE_CURRENT_BINARY_DIR}") endforeach() endif() endforeach() -# Make sure pytest is found or produce a fatal error -if(NOT PYBIND11_PYTEST_FOUND) - execute_process(COMMAND ${PYTHON_EXECUTABLE} -c "import pytest; print(pytest.__version__)" - RESULT_VARIABLE pytest_not_found OUTPUT_VARIABLE pytest_version ERROR_QUIET) - if(pytest_not_found) - message(FATAL_ERROR "Running the tests requires pytest. Please install it manually" - " (try: ${PYTHON_EXECUTABLE} -m pip install pytest)") - elseif(pytest_version VERSION_LESS 3.0) - message(FATAL_ERROR "Running the tests requires pytest >= 3.0. Found: ${pytest_version}" - "Please update it (try: ${PYTHON_EXECUTABLE} -m pip install -U pytest)") - endif() - set(PYBIND11_PYTEST_FOUND TRUE CACHE INTERNAL "") -endif() +# Make sure pytest is found or produce a warning +pybind11_find_import(pytest VERSION 3.1) + +if(NOT CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_CURRENT_BINARY_DIR) + # This is not used later in the build, so it's okay to regenerate each time. + configure_file("${CMAKE_CURRENT_SOURCE_DIR}/pytest.ini" "${CMAKE_CURRENT_BINARY_DIR}/pytest.ini" + COPYONLY) + file(APPEND "${CMAKE_CURRENT_BINARY_DIR}/pytest.ini" + "\ntestpaths = \"${CMAKE_CURRENT_SOURCE_DIR}\"") -if(CMAKE_VERSION VERSION_LESS 3.2) - set(PYBIND11_USES_TERMINAL "") -else() - set(PYBIND11_USES_TERMINAL "USES_TERMINAL") endif() +# cmake 3.12 added list(transform <list> prepend +# but we can't use it yet +string(REPLACE "test_" "${CMAKE_CURRENT_SOURCE_DIR}/test_" PYBIND11_ABS_PYTEST_FILES + "${PYBIND11_PYTEST_FILES}") + # A single command to compile and run the tests -add_custom_target(pytest COMMAND ${PYTHON_EXECUTABLE} -m pytest ${PYBIND11_PYTEST_FILES} - DEPENDS ${test_targets} WORKING_DIRECTORY ${testdir} ${PYBIND11_USES_TERMINAL}) +add_custom_target( + pytest + COMMAND ${PYTHON_EXECUTABLE} -m pytest ${PYBIND11_ABS_PYTEST_FILES} + DEPENDS ${test_targets} + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + USES_TERMINAL) if(PYBIND11_TEST_OVERRIDE) - add_custom_command(TARGET pytest POST_BUILD - COMMAND ${CMAKE_COMMAND} -E echo "Note: not all tests run: -DPYBIND11_TEST_OVERRIDE is in effect") + add_custom_command( + TARGET pytest + POST_BUILD + COMMAND ${CMAKE_COMMAND} -E echo + "Note: not all tests run: -DPYBIND11_TEST_OVERRIDE is in effect") endif() # Add a check target to run all the tests, starting with pytest (we add dependencies to this below) @@ -243,17 +391,23 @@ add_custom_target(check DEPENDS pytest) # The remaining tests only apply when being built as part of the pybind11 project, but not if the # tests are being built independently. -if (NOT PROJECT_NAME STREQUAL "pybind11") +if(CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR) return() endif() # Add a post-build comment to show the primary test suite .so size and, if a previous size, compare it: -add_custom_command(TARGET pybind11_tests POST_BUILD - COMMAND ${PYTHON_EXECUTABLE} ${PROJECT_SOURCE_DIR}/tools/libsize.py - $<TARGET_FILE:pybind11_tests> ${CMAKE_CURRENT_BINARY_DIR}/sosize-$<TARGET_FILE_NAME:pybind11_tests>.txt) - -# Test embedding the interpreter. Provides the `cpptest` target. -add_subdirectory(test_embed) - -# Test CMake build using functions and targets from subdirectory or installed location -add_subdirectory(test_cmake_build) +add_custom_command( + TARGET pybind11_tests + POST_BUILD + COMMAND + ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/../tools/libsize.py + $<TARGET_FILE:pybind11_tests> + ${CMAKE_CURRENT_BINARY_DIR}/sosize-$<TARGET_FILE_NAME:pybind11_tests>.txt) + +if(NOT PYBIND11_CUDA_TESTS) + # Test embedding the interpreter. Provides the `cpptest` target. + add_subdirectory(test_embed) + + # Test CMake build using functions and targets from subdirectory or installed location + add_subdirectory(test_cmake_build) +endif() diff --git a/3rdparty/pybind11/tests/conftest.py b/3rdparty/pybind11/tests/conftest.py index 57f681c6..362eb806 100644 --- a/3rdparty/pybind11/tests/conftest.py +++ b/3rdparty/pybind11/tests/conftest.py @@ -1,31 +1,36 @@ +# -*- 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. """ -import pytest -import textwrap -import difflib -import re -import sys import contextlib -import platform +import difflib import gc +import re +import textwrap + +import pytest -_unicode_marker = re.compile(r'u(\'[^\']*\')') -_long_marker = re.compile(r'([0-9])L') -_hexadecimal = re.compile(r'0x[0-9a-fA-F]+') +import env -# test_async.py requires support for async and await +# Early diagnostic for failed imports +import pybind11_tests # noqa: F401 + +_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 sys.version_info[:2] < (3, 5): +if env.PY2: collect_ignore.append("test_async.py") def _strip_and_dedent(s): """For triple-quote strings""" - return textwrap.dedent(s.lstrip('\n').rstrip()) + return textwrap.dedent(s.lstrip("\n").rstrip()) def _split_and_sort(s): @@ -35,11 +40,14 @@ def _split_and_sort(s): def _make_explanation(a, b): """Explanation for a failed assert -- the a and b arguments are List[str]""" - return ["--- actual / +++ expected"] + [line.strip('\n') for line in difflib.ndiff(a, b)] + return ["--- actual / +++ expected"] + [ + line.strip("\n") for line in difflib.ndiff(a, b) + ] class Output(object): """Basic output post-processing and comparison""" + def __init__(self, string): self.string = string self.explanation = [] @@ -49,7 +57,11 @@ class Output(object): def __eq__(self, other): # Ignore constructor/destructor output which is prefixed with "###" - a = [line for line in self.string.strip().splitlines() if not line.startswith("###")] + a = [ + line + for line in self.string.strip().splitlines() + if not line.startswith("###") + ] b = _strip_and_dedent(other).splitlines() if a == b: return True @@ -60,6 +72,7 @@ class Output(object): class Unordered(Output): """Custom comparison for output without strict line ordering""" + def __eq__(self, other): a = _split_and_sort(self.string) b = _split_and_sort(other) @@ -170,7 +183,7 @@ def msg(): # noinspection PyUnusedLocal def pytest_assertrepr_compare(op, left, right): """Hook to insert custom failure explanation""" - if hasattr(left, 'explanation'): + if hasattr(left, "explanation"): return left.explanation @@ -184,61 +197,12 @@ def suppress(exception): def gc_collect(): - ''' Run the garbage collector twice (needed when running - reference counting tests with PyPy) ''' + """Run the garbage collector twice (needed when running + reference counting tests with PyPy)""" gc.collect() gc.collect() def pytest_configure(): - """Add import suppression and test requirements to `pytest` namespace""" - try: - import numpy as np - except ImportError: - np = None - try: - import scipy - except ImportError: - scipy = None - try: - from pybind11_tests.eigen import have_eigen - except ImportError: - have_eigen = False - pypy = platform.python_implementation() == "PyPy" - - skipif = pytest.mark.skipif pytest.suppress = suppress - pytest.requires_numpy = skipif(not np, reason="numpy is not installed") - pytest.requires_scipy = skipif(not np, reason="scipy is not installed") - pytest.requires_eigen_and_numpy = skipif(not have_eigen or not np, - reason="eigen and/or numpy are not installed") - pytest.requires_eigen_and_scipy = skipif( - not have_eigen or not scipy, reason="eigen and/or scipy are not installed") - pytest.unsupported_on_pypy = skipif(pypy, reason="unsupported on PyPy") - pytest.unsupported_on_py2 = skipif(sys.version_info.major < 3, - reason="unsupported on Python 2.x") pytest.gc_collect = gc_collect - - -def _test_import_pybind11(): - """Early diagnostic for test module initialization errors - - When there is an error during initialization, the first import will report the - real error while all subsequent imports will report nonsense. This import test - is done early (in the pytest configuration file, before any tests) in order to - avoid the noise of having all tests fail with identical error messages. - - Any possible exception is caught here and reported manually *without* the stack - trace. This further reduces noise since the trace would only show pytest internals - which are not useful for debugging pybind11 module issues. - """ - # noinspection PyBroadException - try: - import pybind11_tests # noqa: F401 imported but unused - except Exception as e: - print("Failed to import pybind11_tests from pytest:") - print(" {}: {}".format(type(e).__name__, e)) - sys.exit(1) - - -_test_import_pybind11() diff --git a/3rdparty/pybind11/tests/constructor_stats.h b/3rdparty/pybind11/tests/constructor_stats.h index 431e5ace..805968a0 100644 --- a/3rdparty/pybind11/tests/constructor_stats.h +++ b/3rdparty/pybind11/tests/constructor_stats.h @@ -120,7 +120,7 @@ public: throw py::error_already_set(); Py_DECREF(result); #else - py::module::import("gc").attr("collect")(); + py::module_::import("gc").attr("collect")(); #endif } @@ -273,4 +273,3 @@ template <class T, typename... Values> void print_values(T *inst, Values &&...va print_constr_details(inst, ":", values...); track_values(inst, values...); } - diff --git a/3rdparty/pybind11/tests/env.py b/3rdparty/pybind11/tests/env.py new file mode 100644 index 00000000..5cded441 --- /dev/null +++ b/3rdparty/pybind11/tests/env.py @@ -0,0 +1,14 @@ +# -*- coding: utf-8 -*- +import platform +import sys + +LINUX = sys.platform.startswith("linux") +MACOS = sys.platform.startswith("darwin") +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 diff --git a/3rdparty/pybind11/tests/extra_python_package/pytest.ini b/3rdparty/pybind11/tests/extra_python_package/pytest.ini new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/3rdparty/pybind11/tests/extra_python_package/pytest.ini diff --git a/3rdparty/pybind11/tests/extra_python_package/test_files.py b/3rdparty/pybind11/tests/extra_python_package/test_files.py new file mode 100644 index 00000000..cbd4bff1 --- /dev/null +++ b/3rdparty/pybind11/tests/extra_python_package/test_files.py @@ -0,0 +1,262 @@ +# -*- coding: utf-8 -*- +import contextlib +import os +import string +import subprocess +import sys +import tarfile +import zipfile + +# These tests must be run explicitly +# They require CMake 3.15+ (--install) + +DIR = os.path.abspath(os.path.dirname(__file__)) +MAIN_DIR = os.path.dirname(os.path.dirname(DIR)) + + +main_headers = { + "include/pybind11/attr.h", + "include/pybind11/buffer_info.h", + "include/pybind11/cast.h", + "include/pybind11/chrono.h", + "include/pybind11/common.h", + "include/pybind11/complex.h", + "include/pybind11/eigen.h", + "include/pybind11/embed.h", + "include/pybind11/eval.h", + "include/pybind11/functional.h", + "include/pybind11/iostream.h", + "include/pybind11/numpy.h", + "include/pybind11/operators.h", + "include/pybind11/options.h", + "include/pybind11/pybind11.h", + "include/pybind11/pytypes.h", + "include/pybind11/stl.h", + "include/pybind11/stl_bind.h", +} + +detail_headers = { + "include/pybind11/detail/class.h", + "include/pybind11/detail/common.h", + "include/pybind11/detail/descr.h", + "include/pybind11/detail/init.h", + "include/pybind11/detail/internals.h", + "include/pybind11/detail/typeid.h", +} + +cmake_files = { + "share/cmake/pybind11/FindPythonLibsNew.cmake", + "share/cmake/pybind11/pybind11Common.cmake", + "share/cmake/pybind11/pybind11Config.cmake", + "share/cmake/pybind11/pybind11ConfigVersion.cmake", + "share/cmake/pybind11/pybind11NewTools.cmake", + "share/cmake/pybind11/pybind11Targets.cmake", + "share/cmake/pybind11/pybind11Tools.cmake", +} + +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 +all_files = src_files | py_files + + +sdist_files = { + "pybind11", + "pybind11/include", + "pybind11/include/pybind11", + "pybind11/include/pybind11/detail", + "pybind11/share", + "pybind11/share/cmake", + "pybind11/share/cmake/pybind11", + "pyproject.toml", + "setup.cfg", + "setup.py", + "LICENSE", + "MANIFEST.in", + "README.rst", + "PKG-INFO", +} + +local_sdist_files = { + ".egg-info", + ".egg-info/PKG-INFO", + ".egg-info/SOURCES.txt", + ".egg-info/dependency_links.txt", + ".egg-info/not-zip-safe", + ".egg-info/top_level.txt", +} + + +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), + ] + ) + if hasattr(out, "decode"): + out = out.decode() + + (sdist,) = tmpdir.visit("*.tar") + + with tarfile.open(str(sdist)) as tar: + start = tar.getnames()[0] + "/" + version = start[9:-1] + simpler = set(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() + + with contextlib.closing( + tar.extractfile(tar.getmember(start + "pyproject.toml")) + ) as f: + pyproject_toml = f.read() + + files = set("pybind11/{}".format(n) for n in all_files) + files |= sdist_files + files |= set("pybind11{}".format(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()) + .substitute(version=version, extra_cmd="") + .encode() + ) + assert setup_py == contents + + with open(os.path.join(MAIN_DIR, "tools", "pyproject.toml"), "rb") as f: + contents = f.read() + assert pyproject_toml == contents + + +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), + ] + ) + if hasattr(out, "decode"): + out = out.decode() + + (sdist,) = tmpdir.visit("*.tar") + + with tarfile.open(str(sdist)) as tar: + start = tar.getnames()[0] + "/" + version = start[16:-1] + simpler = set(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() + + with contextlib.closing( + tar.extractfile(tar.getmember(start + "pyproject.toml")) + ) as f: + pyproject_toml = f.read() + + files = set("pybind11/{}".format(n) for n in all_files) + files |= sdist_files + files |= set("pybind11_global{}".format(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() + ) + assert setup_py == contents + + with open(os.path.join(MAIN_DIR, "tools", "pyproject.toml"), "rb") as f: + contents = f.read() + assert pyproject_toml == contents + + +def tests_build_wheel(monkeypatch, tmpdir): + monkeypatch.chdir(MAIN_DIR) + + subprocess.check_output( + [sys.executable, "-m", "pip", "wheel", ".", "-w", str(tmpdir)] + ) + + (wheel,) = tmpdir.visit("*.whl") + + files = set("pybind11/{}".format(n) for n in all_files) + files |= { + "dist-info/LICENSE", + "dist-info/METADATA", + "dist-info/RECORD", + "dist-info/WHEEL", + "dist-info/entry_points.txt", + "dist-info/top_level.txt", + } + + 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 + ) + assert files == trimmed + + +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)] + ) + + (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 |= { + "dist-info/LICENSE", + "dist-info/METADATA", + "dist-info/WHEEL", + "dist-info/top_level.txt", + "dist-info/RECORD", + } + + with zipfile.ZipFile(str(wheel)) as z: + names = z.namelist() + + beginning = names[0].split("/", 1)[0].rsplit(".", 1)[0] + trimmed = set(n[len(beginning) + 1 :] for n in names) + + assert files == trimmed diff --git a/3rdparty/pybind11/tests/extra_setuptools/pytest.ini b/3rdparty/pybind11/tests/extra_setuptools/pytest.ini new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/3rdparty/pybind11/tests/extra_setuptools/pytest.ini diff --git a/3rdparty/pybind11/tests/extra_setuptools/test_setuphelper.py b/3rdparty/pybind11/tests/extra_setuptools/test_setuphelper.py new file mode 100644 index 00000000..0d8bd0e4 --- /dev/null +++ b/3rdparty/pybind11/tests/extra_setuptools/test_setuphelper.py @@ -0,0 +1,101 @@ +# -*- coding: utf-8 -*- +import os +import sys +import subprocess +from textwrap import dedent + +import pytest + +DIR = os.path.abspath(os.path.dirname(__file__)) +MAIN_DIR = os.path.dirname(os.path.dirname(DIR)) + + +@pytest.mark.parametrize("parallel", [False, True]) +@pytest.mark.parametrize("std", [11, 0]) +def test_simple_setup_py(monkeypatch, tmpdir, parallel, std): + monkeypatch.chdir(tmpdir) + monkeypatch.syspath_prepend(MAIN_DIR) + + (tmpdir / "setup.py").write_text( + dedent( + u"""\ + import sys + sys.path.append({MAIN_DIR!r}) + + from setuptools import setup, Extension + from pybind11.setup_helpers import build_ext, Pybind11Extension + + std = {std} + + ext_modules = [ + Pybind11Extension( + "simple_setup", + sorted(["main.cpp"]), + cxx_std=std, + ), + ] + + cmdclass = dict() + if std == 0: + cmdclass["build_ext"] = build_ext + + + parallel = {parallel} + if parallel: + from pybind11.setup_helpers import ParallelCompile + ParallelCompile().install() + + setup( + name="simple_setup_package", + cmdclass=cmdclass, + ext_modules=ext_modules, + ) + """ + ).format(MAIN_DIR=MAIN_DIR, std=std, parallel=parallel), + encoding="ascii", + ) + + (tmpdir / "main.cpp").write_text( + dedent( + u"""\ + #include <pybind11/pybind11.h> + + int f(int x) { + return x * 3; + } + PYBIND11_MODULE(simple_setup, m) { + m.def("f", &f); + } + """ + ), + encoding="ascii", + ) + + subprocess.check_call( + [sys.executable, "setup.py", "build_ext", "--inplace"], + stdout=sys.stdout, + stderr=sys.stderr, + ) + + # Debug helper printout, normally hidden + for item in tmpdir.listdir(): + print(item.basename) + + assert ( + len([f for f in tmpdir.listdir() if f.basename.startswith("simple_setup")]) == 1 + ) + assert len(list(tmpdir.listdir())) == 4 # two files + output + build_dir + + (tmpdir / "test.py").write_text( + dedent( + u"""\ + import simple_setup + assert simple_setup.f(3) == 9 + """ + ), + encoding="ascii", + ) + + subprocess.check_call( + [sys.executable, "test.py"], stdout=sys.stdout, stderr=sys.stderr + ) diff --git a/3rdparty/pybind11/tests/local_bindings.h b/3rdparty/pybind11/tests/local_bindings.h index b6afb808..22537b13 100644 --- a/3rdparty/pybind11/tests/local_bindings.h +++ b/3rdparty/pybind11/tests/local_bindings.h @@ -58,7 +58,7 @@ public: std::string name_; const std::string &name() { return name_; } }; -} +} // namespace pets struct MixGL { int i; MixGL(int i) : i{i} {} }; struct MixGL2 { int i; MixGL2(int i) : i{i} {} }; diff --git a/3rdparty/pybind11/tests/pybind11_tests.cpp b/3rdparty/pybind11/tests/pybind11_tests.cpp index bc7d2c3e..439cd401 100644 --- a/3rdparty/pybind11/tests/pybind11_tests.cpp +++ b/3rdparty/pybind11/tests/pybind11_tests.cpp @@ -26,23 +26,23 @@ productively. Instead, see the "How can I reduce the build time?" question in the "Frequently asked questions" section of the documentation for good practice on splitting binding code over multiple files. */ -std::list<std::function<void(py::module &)>> &initializers() { - static std::list<std::function<void(py::module &)>> inits; +std::list<std::function<void(py::module_ &)>> &initializers() { + static std::list<std::function<void(py::module_ &)>> inits; return inits; } test_initializer::test_initializer(Initializer init) { - initializers().push_back(init); + initializers().emplace_back(init); } test_initializer::test_initializer(const char *submodule_name, Initializer init) { - initializers().push_back([=](py::module &parent) { + initializers().emplace_back([=](py::module_ &parent) { auto m = parent.def_submodule(submodule_name); init(m); }); } -void bind_ConstructorStats(py::module &m) { +void bind_ConstructorStats(py::module_ &m) { py::class_<ConstructorStats>(m, "ConstructorStats") .def("alive", &ConstructorStats::alive) .def("values", &ConstructorStats::values) @@ -88,6 +88,4 @@ PYBIND11_MODULE(pybind11_tests, m) { for (const auto &initializer : initializers()) initializer(m); - - if (!py::hasattr(m, "have_eigen")) m.attr("have_eigen") = false; } diff --git a/3rdparty/pybind11/tests/pybind11_tests.h b/3rdparty/pybind11/tests/pybind11_tests.h index 90963a5d..4ff56c07 100644 --- a/3rdparty/pybind11/tests/pybind11_tests.h +++ b/3rdparty/pybind11/tests/pybind11_tests.h @@ -10,7 +10,7 @@ namespace py = pybind11; using namespace pybind11::literals; class test_initializer { - using Initializer = void (*)(py::module &); + using Initializer = void (*)(py::module_ &); public: test_initializer(Initializer init); @@ -18,9 +18,9 @@ public: }; #define TEST_SUBMODULE(name, variable) \ - void test_submodule_##name(py::module &); \ + void test_submodule_##name(py::module_ &); \ test_initializer name(#name, test_submodule_##name); \ - void test_submodule_##name(py::module &variable) + void test_submodule_##name(py::module_ &variable) /// Dummy type which is not exported anywhere -- something to trigger a conversion error @@ -50,16 +50,22 @@ public: IncType &operator=(IncType &&) = delete; }; +/// A simple union for basic testing +union IntFloat { + int i; + 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). struct RValueCaster {}; -NAMESPACE_BEGIN(pybind11) -NAMESPACE_BEGIN(detail) +PYBIND11_NAMESPACE_BEGIN(pybind11) +PYBIND11_NAMESPACE_BEGIN(detail) template<> class type_caster<RValueCaster> { 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(); } }; -NAMESPACE_END(detail) -NAMESPACE_END(pybind11) +PYBIND11_NAMESPACE_END(detail) +PYBIND11_NAMESPACE_END(pybind11) diff --git a/3rdparty/pybind11/tests/pytest.ini b/3rdparty/pybind11/tests/pytest.ini index f209964a..c47cbe9c 100644 --- a/3rdparty/pybind11/tests/pytest.ini +++ b/3rdparty/pybind11/tests/pytest.ini @@ -1,11 +1,14 @@ [pytest] -minversion = 3.0 -norecursedirs = test_cmake_build test_embed +minversion = 3.1 +norecursedirs = test_* extra_* +xfail_strict = True addopts = # show summary of skipped tests -rs # capture only Python print and C++ py::print, but not C output (low-level Python errors) --capture=sys + # enable all warnings + -Wa filterwarnings = # make warnings into errors but ignore certain third-party extension issues error diff --git a/3rdparty/pybind11/tests/requirements.txt b/3rdparty/pybind11/tests/requirements.txt new file mode 100644 index 00000000..80ed6171 --- /dev/null +++ b/3rdparty/pybind11/tests/requirements.txt @@ -0,0 +1,8 @@ +--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" diff --git a/3rdparty/pybind11/tests/test_async.cpp b/3rdparty/pybind11/tests/test_async.cpp index f0ad0d53..e6e01d72 100644 --- a/3rdparty/pybind11/tests/test_async.cpp +++ b/3rdparty/pybind11/tests/test_async.cpp @@ -18,7 +18,7 @@ TEST_SUBMODULE(async_module, m) { .def(py::init<>()) .def("__await__", [](const SupportsAsync& self) -> py::object { static_cast<void>(self); - py::object loop = py::module::import("asyncio.events").attr("get_event_loop")(); + py::object loop = py::module_::import("asyncio.events").attr("get_event_loop")(); py::object f = loop.attr("create_future")(); f.attr("set_result")(5); return f.attr("__await__")(); diff --git a/3rdparty/pybind11/tests/test_async.py b/3rdparty/pybind11/tests/test_async.py index e1c959d6..df4489c4 100644 --- a/3rdparty/pybind11/tests/test_async.py +++ b/3rdparty/pybind11/tests/test_async.py @@ -1,6 +1,8 @@ -import asyncio +# -*- coding: utf-8 -*- import pytest -from pybind11_tests import async_module as m + +asyncio = pytest.importorskip("asyncio") +m = pytest.importorskip("pybind11_tests.async_module") @pytest.fixture diff --git a/3rdparty/pybind11/tests/test_buffers.cpp b/3rdparty/pybind11/tests/test_buffers.cpp index 1bc67ff7..46eabf39 100644 --- a/3rdparty/pybind11/tests/test_buffers.cpp +++ b/3rdparty/pybind11/tests/test_buffers.cpp @@ -9,12 +9,13 @@ #include "pybind11_tests.h" #include "constructor_stats.h" +#include <pybind11/stl.h> TEST_SUBMODULE(buffers, m) { // test_from_python / test_to_python: class Matrix { public: - Matrix(ssize_t rows, ssize_t cols) : m_rows(rows), m_cols(cols) { + 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)]; memset(m_data, 0, sizeof(float) * (size_t) (rows * cols)); @@ -58,25 +59,25 @@ TEST_SUBMODULE(buffers, m) { return *this; } - float operator()(ssize_t i, ssize_t j) const { + float operator()(py::ssize_t i, py::ssize_t j) const { return m_data[(size_t) (i*m_cols + j)]; } - float &operator()(ssize_t i, ssize_t j) { + float &operator()(py::ssize_t i, py::ssize_t j) { return m_data[(size_t) (i*m_cols + j)]; } float *data() { return m_data; } - ssize_t rows() const { return m_rows; } - ssize_t cols() const { return m_cols; } + py::ssize_t rows() const { return m_rows; } + py::ssize_t cols() const { return m_cols; } private: - ssize_t m_rows; - ssize_t m_cols; + py::ssize_t m_rows; + py::ssize_t m_cols; float *m_data; }; py::class_<Matrix>(m, "Matrix", py::buffer_protocol()) - .def(py::init<ssize_t, ssize_t>()) + .def(py::init<py::ssize_t, py::ssize_t>()) /// Construct from a buffer .def(py::init([](py::buffer const b) { py::buffer_info info = b.request(); @@ -92,12 +93,12 @@ TEST_SUBMODULE(buffers, m) { .def("cols", &Matrix::cols) /// Bare bones interface - .def("__getitem__", [](const Matrix &m, std::pair<ssize_t, ssize_t> i) { + .def("__getitem__", [](const Matrix &m, std::pair<py::ssize_t, py::ssize_t> 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<ssize_t, ssize_t> i, float v) { + .def("__setitem__", [](Matrix &m, std::pair<py::ssize_t, py::ssize_t> i, float v) { if (i.first >= m.rows() || i.second >= m.cols()) throw py::index_error(); m(i.first, i.second) = v; @@ -117,11 +118,11 @@ TEST_SUBMODULE(buffers, m) { // test_inherited_protocol class SquareMatrix : public Matrix { public: - SquareMatrix(ssize_t n) : Matrix(n, n) { } + SquareMatrix(py::ssize_t n) : Matrix(n, n) { } }; // Derived classes inherit the buffer protocol and the buffer access function py::class_<SquareMatrix, Matrix>(m, "SquareMatrix") - .def(py::init<ssize_t>()); + .def(py::init<py::ssize_t>()); // test_pointer_to_member_fn @@ -192,4 +193,22 @@ TEST_SUBMODULE(buffers, m) { .def_readwrite("readonly", &BufferReadOnlySelect::readonly) .def_buffer(&BufferReadOnlySelect::get_buffer_info); + // Expose buffer_info for testing. + py::class_<py::buffer_info>(m, "buffer_info") + .def(py::init<>()) + .def_readonly("itemsize", &py::buffer_info::itemsize) + .def_readonly("size", &py::buffer_info::size) + .def_readonly("format", &py::buffer_info::format) + .def_readonly("ndim", &py::buffer_info::ndim) + .def_readonly("shape", &py::buffer_info::shape) + .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); + }) + ; + + m.def("get_buffer_info", [](py::buffer buffer) { + return buffer.request(); + }); } diff --git a/3rdparty/pybind11/tests/test_buffers.py b/3rdparty/pybind11/tests/test_buffers.py index bf7aaed7..f0f37084 100644 --- a/3rdparty/pybind11/tests/test_buffers.py +++ b/3rdparty/pybind11/tests/test_buffers.py @@ -1,18 +1,16 @@ +# -*- coding: utf-8 -*- import io import struct -import sys +import ctypes import pytest +import env # noqa: F401 + from pybind11_tests import buffers as m from pybind11_tests import ConstructorStats -PY3 = sys.version_info[0] >= 3 - -pytestmark = pytest.requires_numpy - -with pytest.suppress(ImportError): - import numpy as np +np = pytest.importorskip("numpy") def test_from_python(): @@ -38,9 +36,7 @@ def test_from_python(): assert cstats.move_assignments == 0 -# PyPy: Memory leak in the "np.array(m, copy=False)" call -# https://bitbucket.org/pypy/pypy/issues/2444 -@pytest.unsupported_on_pypy +# https://foss.heptapod.net/pypy/pypy/-/issues/2444 def test_to_python(): mat = m.Matrix(5, 4) assert memoryview(mat).shape == (5, 4) @@ -50,8 +46,8 @@ def test_to_python(): mat[3, 2] = 7.0 assert mat[2, 3] == 4 assert mat[3, 2] == 7 - assert struct.unpack_from('f', mat, (3 * 4 + 2) * 4) == (7, ) - assert struct.unpack_from('f', mat, (2 * 4 + 3) * 4) == (4, ) + assert struct.unpack_from("f", mat, (3 * 4 + 2) * 4) == (7,) + assert struct.unpack_from("f", mat, (2 * 4 + 3) * 4) == (4,) mat2 = np.array(mat, copy=False) assert mat2.shape == (5, 4) @@ -75,7 +71,6 @@ def test_to_python(): assert cstats.move_assignments == 0 -@pytest.unsupported_on_pypy def test_inherited_protocol(): """SquareMatrix is derived from Matrix and inherits the buffer protocol""" @@ -84,35 +79,84 @@ def test_inherited_protocol(): assert np.asarray(matrix).shape == (5, 5) -@pytest.unsupported_on_pypy def test_pointer_to_member_fn(): for cls in [m.Buffer, m.ConstBuffer, m.DerivedBuffer]: buf = cls() buf.value = 0x12345678 - value = struct.unpack('i', bytearray(buf))[0] + value = struct.unpack("i", bytearray(buf))[0] assert value == 0x12345678 -@pytest.unsupported_on_pypy def test_readonly_buffer(): buf = m.BufferReadOnly(0x64) view = memoryview(buf) - assert view[0] == 0x64 if PY3 else b'd' + assert view[0] == b"d" if env.PY2 else 0x64 assert view.readonly -@pytest.unsupported_on_pypy def test_selective_readonly_buffer(): buf = m.BufferReadOnlySelect() - memoryview(buf)[0] = 0x64 if PY3 else b'd' + memoryview(buf)[0] = b"d" if env.PY2 else 0x64 assert buf.value == 0x64 - io.BytesIO(b'A').readinto(buf) - assert buf.value == ord(b'A') + io.BytesIO(b"A").readinto(buf) + assert buf.value == ord(b"A") buf.readonly = True with pytest.raises(TypeError): - memoryview(buf)[0] = 0 if PY3 else b'\0' + memoryview(buf)[0] = b"\0" if env.PY2 else 0 with pytest.raises(TypeError): - io.BytesIO(b'1').readinto(buf) + io.BytesIO(b"1").readinto(buf) + + +def test_ctypes_array_1d(): + char1d = (ctypes.c_char * 10)() + int1d = (ctypes.c_int * 15)() + long1d = (ctypes.c_long * 7)() + + for carray in (char1d, int1d, long1d): + info = m.get_buffer_info(carray) + assert info.itemsize == ctypes.sizeof(carray._type_) + assert info.size == len(carray) + assert info.ndim == 1 + assert info.shape == [info.size] + assert info.strides == [info.itemsize] + assert not info.readonly + + +def test_ctypes_array_2d(): + char2d = ((ctypes.c_char * 10) * 4)() + int2d = ((ctypes.c_int * 15) * 3)() + long2d = ((ctypes.c_long * 7) * 2)() + + for carray in (char2d, int2d, long2d): + info = m.get_buffer_info(carray) + assert info.itemsize == ctypes.sizeof(carray[0]._type_) + assert info.size == len(carray) * len(carray[0]) + assert info.ndim == 2 + assert info.shape == [len(carray), len(carray[0])] + assert info.strides == [info.itemsize * len(carray[0]), info.itemsize] + 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)): + pyinfo = m.get_buffer_info(pyarray) + + if pyinfo.readonly: + cbytes = (ctypes.c_char * len(pyarray)).from_buffer_copy(pyarray) + cinfo = m.get_buffer_info(cbytes) + else: + cbytes = (ctypes.c_char * len(pyarray)).from_buffer(pyarray) + cinfo = m.get_buffer_info(cbytes) + + assert cinfo.size == pyinfo.size + assert cinfo.ndim == pyinfo.ndim + assert cinfo.shape == pyinfo.shape + assert cinfo.strides == pyinfo.strides + assert not cinfo.readonly diff --git a/3rdparty/pybind11/tests/test_builtin_casters.cpp b/3rdparty/pybind11/tests/test_builtin_casters.cpp index acb24469..acc9f8fb 100644 --- a/3rdparty/pybind11/tests/test_builtin_casters.cpp +++ b/3rdparty/pybind11/tests/test_builtin_casters.cpp @@ -117,12 +117,16 @@ TEST_SUBMODULE(builtin_casters, m) { 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, std::string> int_string_pair{2, "items"}; + m.def("int_string_pair", []() { return &int_string_pair; }); + // 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<int,int> * { return nullptr; }); // test_none_deferred m.def("defer_none_cstring", [](char *) { return false; }); diff --git a/3rdparty/pybind11/tests/test_builtin_casters.py b/3rdparty/pybind11/tests/test_builtin_casters.py index 91422588..bd7996b6 100644 --- a/3rdparty/pybind11/tests/test_builtin_casters.py +++ b/3rdparty/pybind11/tests/test_builtin_casters.py @@ -1,6 +1,8 @@ -# Python < 3 needs this: coding=utf-8 +# -*- coding: utf-8 -*- import pytest +import env # noqa: F401 + from pybind11_tests import builtin_casters as m from pybind11_tests import UserType, IncType @@ -35,79 +37,85 @@ def test_unicode_conversion(): with pytest.raises(UnicodeDecodeError): 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_Z() == "Z" + assert m.u8_eacute() == u"é" + assert m.u16_ibang() == u"‽" + assert m.u32_mathbfA() == u"𝐀" + assert m.wchar_heart() == u"♥" if hasattr(m, "has_u8string"): - assert m.u8_char8_Z() == 'Z' + assert m.u8_char8_Z() == "Z" 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) + 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(u'é') == 0xE9 # requires 2 bytes in utf-8, but can be stuffed in a char + assert m.ord_char(u"a") == 0x61 # simple ASCII + assert m.ord_char_lv(u"b") == 0x62 + assert ( + m.ord_char(u"é") == 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(u"Ā") == 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(u"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(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 with pytest.raises(ValueError) as excinfo: - assert m.ord_char16(u'🎂') == 0x1F382 # requires surrogate pair + assert m.ord_char16(u"🎂") == 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(u"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(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 with pytest.raises(ValueError) as excinfo: - assert m.ord_char32(u'aa') + assert m.ord_char32(u"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(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 if m.wchar_size == 2: with pytest.raises(ValueError) as excinfo: - assert m.ord_wchar(u'🎂') == 0x1F382 # requires surrogate pair + assert m.ord_wchar(u"🎂") == 0x1F382 # requires surrogate pair assert str(excinfo.value) == toobig_message(0x10000) else: - assert m.ord_wchar(u'🎂') == 0x1F382 + assert m.ord_wchar(u"🎂") == 0x1F382 with pytest.raises(ValueError) as excinfo: - assert m.ord_wchar(u'aa') + assert m.ord_wchar(u"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(u'é') == 0xE9 # requires 2 bytes in utf-8, but can be stuffed in a char + assert m.ord_char8(u"a") == 0x61 # simple ASCII + assert m.ord_char8_lv(u"b") == 0x62 + assert ( + m.ord_char8(u"é") == 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(u"Ā") == 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(u"ab") assert str(excinfo.value) == toolong_message @@ -115,88 +123,108 @@ def test_bytes_to_string(): """Tests the ability to pass bytes to C++ string-accepting functions. Note that this is one-way: the only way to return bytes to Python is via the pybind11::bytes class.""" # Issue #816 - import sys - byte = bytes if sys.version_info[0] < 3 else str - assert m.strlen(byte("hi")) == 2 - assert m.string_length(byte("world")) == 5 - assert m.string_length(byte("a\x00b")) == 3 - assert m.strlen(byte("a\x00b")) == 1 # C-string limitation + 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 # passing in a utf8 encoded string should work - assert m.string_length(u'💩'.encode("utf8")) == 4 + assert m.string_length(u"💩".encode("utf8")) == 4 @pytest.mark.skipif(not hasattr(m, "has_string_view"), reason="no <string_view>") 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("Hi 🎂") == [72, 105, 32, 0xd83c, 0xdf82] - assert m.string_view32_chars("Hi 🎂") == [72, 105, 32, 127874] + 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] if hasattr(m, "has_u8string"): assert m.string_view8_chars("Hi") == [72, 105] - assert m.string_view8_chars("Hi 🎂") == [72, 105, 32, 0xf0, 0x9f, 0x8e, 0x82] + assert m.string_view8_chars(u"Hi 🎂") == [72, 105, 32, 0xF0, 0x9F, 0x8E, 0x82] - assert m.string_view_return() == "utf8 secret 🎂" - assert m.string_view16_return() == "utf16 secret 🎂" - assert m.string_view32_return() == "utf32 secret 🎂" + assert m.string_view_return() == u"utf8 secret 🎂" + assert m.string_view16_return() == u"utf16 secret 🎂" + assert m.string_view32_return() == u"utf32 secret 🎂" if hasattr(m, "has_u8string"): - assert m.string_view8_return() == "utf8 secret 🎂" + assert m.string_view8_return() == u"utf8 secret 🎂" with capture: m.string_view_print("Hi") m.string_view_print("utf8 🎂") - m.string_view16_print("utf16 🎂") - m.string_view32_print("utf32 🎂") - assert capture == """ + m.string_view16_print(u"utf16 🎂") + m.string_view32_print(u"utf32 🎂") + assert ( + capture + == u""" Hi 2 utf8 🎂 9 utf16 🎂 8 utf32 🎂 7 """ + ) if hasattr(m, "has_u8string"): with capture: m.string_view8_print("Hi") - m.string_view8_print("utf8 🎂") - assert capture == """ + m.string_view8_print(u"utf8 🎂") + assert ( + capture + == u""" Hi 2 utf8 🎂 9 """ + ) with capture: m.string_view_print("Hi, ascii") m.string_view_print("Hi, utf8 🎂") - m.string_view16_print("Hi, utf16 🎂") - m.string_view32_print("Hi, utf32 🎂") - assert capture == """ + m.string_view16_print(u"Hi, utf16 🎂") + m.string_view32_print(u"Hi, utf32 🎂") + assert ( + capture + == u""" Hi, ascii 9 Hi, utf8 🎂 13 Hi, utf16 🎂 12 Hi, utf32 🎂 11 """ + ) if hasattr(m, "has_u8string"): with capture: m.string_view8_print("Hi, ascii") - m.string_view8_print("Hi, utf8 🎂") - assert capture == """ + m.string_view8_print(u"Hi, utf8 🎂") + assert ( + capture + == u""" Hi, ascii 9 Hi, utf8 🎂 13 """ + ) def test_integer_casting(): """Issue #929 - out-of-range integer values shouldn't be accepted""" - import sys assert m.i32_str(-1) == "-1" assert m.i64_str(-1) == "-1" assert m.i32_str(2000000000) == "2000000000" assert m.u32_str(2000000000) == "2000000000" - if sys.version_info < (3,): + 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)) == "-999999999999" # noqa: F821 undefined name - assert m.u64_str(long(999999999999)) == "999999999999" # 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" @@ -214,7 +242,7 @@ def test_integer_casting(): m.i32_str(3000000000) assert "incompatible function arguments" in str(excinfo.value) - if sys.version_info < (3,): + 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) @@ -232,16 +260,22 @@ def test_tuple(doc): assert m.tuple_passthrough([True, "test", 5]) == (5, "test", True) assert m.empty_tuple() == () - assert doc(m.pair_passthrough) == """ + assert ( + doc(m.pair_passthrough) + == """ pair_passthrough(arg0: Tuple[bool, str]) -> Tuple[str, bool] Return a pair in reversed order """ - assert doc(m.tuple_passthrough) == """ + ) + assert ( + doc(m.tuple_passthrough) + == """ tuple_passthrough(arg0: Tuple[bool, str, int]) -> Tuple[int, str, bool] Return a triple in reversed order """ + ) assert m.rvalue_pair() == ("rvalue", "rvalue") assert m.lvalue_pair() == ("lvalue", "lvalue") @@ -250,6 +284,8 @@ def test_tuple(doc): assert m.rvalue_nested() == ("rvalue", ("rvalue", ("rvalue", "rvalue"))) assert m.lvalue_nested() == ("lvalue", ("lvalue", ("lvalue", "lvalue"))) + assert m.int_string_pair() == (2, "items") + def test_builtins_cast_return_none(): """Casters produced with PYBIND11_TYPE_CASTER() should convert nullptr to None""" @@ -258,6 +294,7 @@ def test_builtins_cast_return_none(): assert m.return_none_bool() is None assert m.return_none_int() is None assert m.return_none_float() is None + assert m.return_none_pair() is None def test_none_deferred(): @@ -352,9 +389,9 @@ def test_bool_caster(): assert convert(A(False)) is False -@pytest.requires_numpy def test_numpy_bool(): - import numpy as np + np = pytest.importorskip("numpy") + convert, noconvert = m.bool_passthrough, m.bool_passthrough_noconvert def cant_convert(v): @@ -365,7 +402,7 @@ def test_numpy_bool(): assert convert(np.bool_(False)) is False assert noconvert(np.bool_(True)) is True assert noconvert(np.bool_(False)) is False - cant_convert(np.zeros(2, dtype='int')) + cant_convert(np.zeros(2, dtype="int")) def test_int_long(): @@ -375,7 +412,8 @@ def test_int_long(): long.""" import sys - must_be_long = type(getattr(sys, 'maxint', 1) + 1) + + 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) diff --git a/3rdparty/pybind11/tests/test_call_policies.cpp b/3rdparty/pybind11/tests/test_call_policies.cpp index fd245578..26c83f81 100644 --- a/3rdparty/pybind11/tests/test_call_policies.cpp +++ b/3rdparty/pybind11/tests/test_call_policies.cpp @@ -46,6 +46,7 @@ TEST_SUBMODULE(call_policies, m) { class Parent { public: Parent() { py::print("Allocating parent."); } + Parent(const Parent& parent) = default; ~Parent() { py::print("Releasing parent."); } void addChild(Child *) { } Child *returnChild() { return new Child(); } diff --git a/3rdparty/pybind11/tests/test_call_policies.py b/3rdparty/pybind11/tests/test_call_policies.py index 7c835599..e0413d14 100644 --- a/3rdparty/pybind11/tests/test_call_policies.py +++ b/3rdparty/pybind11/tests/test_call_policies.py @@ -1,8 +1,13 @@ +# -*- coding: utf-8 -*- import pytest + +import env # noqa: F401 + from pybind11_tests import call_policies as m from pybind11_tests import ConstructorStats +@pytest.mark.xfail("env.PYPY", reason="sometimes comes out 1 off on PyPy", strict=False) def test_keep_alive_argument(capture): n_inst = ConstructorStats.detail_reg_inst() with capture: @@ -11,10 +16,13 @@ def test_keep_alive_argument(capture): with capture: p.addChild(m.Child()) assert ConstructorStats.detail_reg_inst() == n_inst + 1 - assert capture == """ + assert ( + capture + == """ Allocating child. Releasing child. """ + ) with capture: del p assert ConstructorStats.detail_reg_inst() == n_inst @@ -30,10 +38,13 @@ def test_keep_alive_argument(capture): with capture: del p assert ConstructorStats.detail_reg_inst() == n_inst - assert capture == """ + assert ( + capture + == """ Releasing parent. Releasing child. """ + ) def test_keep_alive_return_value(capture): @@ -44,10 +55,13 @@ def test_keep_alive_return_value(capture): with capture: p.returnChild() assert ConstructorStats.detail_reg_inst() == n_inst + 1 - assert capture == """ + assert ( + capture + == """ Allocating child. Releasing child. """ + ) with capture: del p assert ConstructorStats.detail_reg_inst() == n_inst @@ -63,28 +77,34 @@ def test_keep_alive_return_value(capture): with capture: del p assert ConstructorStats.detail_reg_inst() == n_inst - assert capture == """ + assert ( + capture + == """ Releasing parent. Releasing child. """ + ) -# https://bitbucket.org/pypy/pypy/issues/2447 -@pytest.unsupported_on_pypy +# https://foss.heptapod.net/pypy/pypy/-/issues/2447 +@pytest.mark.xfail("env.PYPY", reason="_PyObject_GetDictPtr is unimplemented") def test_alive_gc(capture): n_inst = ConstructorStats.detail_reg_inst() p = m.ParentGC() p.addChildKeepAlive(m.Child()) assert ConstructorStats.detail_reg_inst() == n_inst + 2 lst = [p] - lst.append(lst) # creates a circular reference + lst.append(lst) # creates a circular reference with capture: del p, lst assert ConstructorStats.detail_reg_inst() == n_inst - assert capture == """ + assert ( + capture + == """ Releasing parent. Releasing child. """ + ) def test_alive_gc_derived(capture): @@ -96,14 +116,17 @@ def test_alive_gc_derived(capture): p.addChildKeepAlive(m.Child()) assert ConstructorStats.detail_reg_inst() == n_inst + 2 lst = [p] - lst.append(lst) # creates a circular reference + lst.append(lst) # creates a circular reference with capture: del p, lst assert ConstructorStats.detail_reg_inst() == n_inst - assert capture == """ + assert ( + capture + == """ Releasing parent. Releasing child. """ + ) def test_alive_gc_multi_derived(capture): @@ -118,15 +141,18 @@ def test_alive_gc_multi_derived(capture): # +3 rather than +2 because Derived corresponds to two registered instances assert ConstructorStats.detail_reg_inst() == n_inst + 3 lst = [p] - lst.append(lst) # creates a circular reference + lst.append(lst) # creates a circular reference with capture: del p, lst assert ConstructorStats.detail_reg_inst() == n_inst - assert capture == """ + assert ( + capture + == """ Releasing parent. Releasing child. Releasing child. """ + ) def test_return_none(capture): @@ -162,17 +188,23 @@ def test_keep_alive_constructor(capture): with capture: p = m.Parent(m.Child()) assert ConstructorStats.detail_reg_inst() == n_inst + 2 - assert capture == """ + assert ( + capture + == """ Allocating child. Allocating parent. """ + ) with capture: del p assert ConstructorStats.detail_reg_inst() == n_inst - assert capture == """ + assert ( + capture + == """ Releasing parent. Releasing child. """ + ) def test_call_guard(): diff --git a/3rdparty/pybind11/tests/test_callbacks.cpp b/3rdparty/pybind11/tests/test_callbacks.cpp index 71b88c44..683dfb3e 100644 --- a/3rdparty/pybind11/tests/test_callbacks.cpp +++ b/3rdparty/pybind11/tests/test_callbacks.cpp @@ -117,7 +117,11 @@ TEST_SUBMODULE(callbacks, m) { } }); - class AbstractBase { public: virtual unsigned int func() = 0; }; + class AbstractBase { + public: + virtual ~AbstractBase() = default; + virtual unsigned int func() = 0; + }; m.def("func_accepting_func_accepting_base", [](std::function<double(AbstractBase&)>) { }); struct MovableObject { diff --git a/3rdparty/pybind11/tests/test_callbacks.py b/3rdparty/pybind11/tests/test_callbacks.py index 6439c8e7..039b877c 100644 --- a/3rdparty/pybind11/tests/test_callbacks.py +++ b/3rdparty/pybind11/tests/test_callbacks.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- import pytest from pybind11_tests import callbacks as m from threading import Thread @@ -41,17 +42,19 @@ def test_bound_method_callback(): def test_keyword_args_and_generalized_unpacking(): - def f(*args, **kwargs): return args, kwargs assert m.test_tuple_unpacking(f) == (("positional", 1, 2, 3, 4, 5, 6), {}) - assert m.test_dict_unpacking(f) == (("positional", 1), {"key": "value", "a": 1, "b": 2}) + assert m.test_dict_unpacking(f) == ( + ("positional", 1), + {"key": "value", "a": 1, "b": 2}, + ) assert m.test_keyword_args(f) == ((), {"x": 10, "y": 20}) assert m.test_unpacking_and_keywords1(f) == ((1, 2), {"c": 3, "d": 4}) assert m.test_unpacking_and_keywords2(f) == ( ("positional", 1, 2, 3, 4, 5), - {"key": "value", "a": 1, "b": 2, "c": 3, "d": 4, "e": 5} + {"key": "value", "a": 1, "b": 2, "c": 3, "d": 4, "e": 5}, ) with pytest.raises(TypeError) as excinfo: @@ -82,12 +85,18 @@ def test_lambda_closure_cleanup(): def test_cpp_function_roundtrip(): """Test if passing a function pointer from C++ -> Python -> C++ yields the original pointer""" - assert m.test_dummy_function(m.dummy_function) == "matches dummy_function: eval(1) = 2" - assert (m.test_dummy_function(m.roundtrip(m.dummy_function)) == - "matches dummy_function: eval(1) = 2") + assert ( + m.test_dummy_function(m.dummy_function) == "matches dummy_function: eval(1) = 2" + ) + assert ( + m.test_dummy_function(m.roundtrip(m.dummy_function)) + == "matches dummy_function: eval(1) = 2" + ) assert m.roundtrip(None, expect_none=True) is None - assert (m.test_dummy_function(lambda x: x + 2) == - "can't convert to function pointer: eval(1) = 3") + assert ( + m.test_dummy_function(lambda x: x + 2) + == "can't convert to function pointer: eval(1) = 3" + ) with pytest.raises(TypeError) as excinfo: m.test_dummy_function(m.dummy_function2) @@ -95,8 +104,10 @@ def test_cpp_function_roundtrip(): with pytest.raises(TypeError) as excinfo: m.test_dummy_function(lambda x, y: x + y) - assert any(s in str(excinfo.value) for s in ("missing 1 required positional argument", - "takes exactly 2 arguments")) + assert any( + s in str(excinfo.value) + for s in ("missing 1 required positional argument", "takes exactly 2 arguments") + ) def test_function_signatures(doc): @@ -126,6 +137,7 @@ def test_async_callbacks(): m.test_async_callback(gen_f(), work) # wait until work is done from time import sleep + sleep(0.5) assert sum(res) == sum([x + 3 for x in work]) diff --git a/3rdparty/pybind11/tests/test_chrono.cpp b/3rdparty/pybind11/tests/test_chrono.cpp index 899d08d8..65370508 100644 --- a/3rdparty/pybind11/tests/test_chrono.cpp +++ b/3rdparty/pybind11/tests/test_chrono.cpp @@ -10,6 +10,25 @@ #include "pybind11_tests.h" #include <pybind11/chrono.h> +#include <chrono> + +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>; + time_point_h timestamp_h; + time_point_m timestamp_m; + time_point_s timestamp_s; + time_point_ms timestamp_ms; + time_point_us timestamp_us; +}; TEST_SUBMODULE(chrono, m) { using system_time = std::chrono::system_clock::time_point; @@ -52,4 +71,14 @@ TEST_SUBMODULE(chrono, m) { m.def("test_nano_timepoint", [](timestamp start, timespan delta) -> timestamp { return start + delta; }); + + // Test different resolutions + py::class_<different_resolutions>(m, "different_resolutions") + .def(py::init<>()) + .def_readwrite("timestamp_h", &different_resolutions::timestamp_h) + .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) + ; } diff --git a/3rdparty/pybind11/tests/test_chrono.py b/3rdparty/pybind11/tests/test_chrono.py index 55c95440..e9e24e08 100644 --- a/3rdparty/pybind11/tests/test_chrono.py +++ b/3rdparty/pybind11/tests/test_chrono.py @@ -1,10 +1,15 @@ +# -*- coding: utf-8 -*- from pybind11_tests import chrono as m import datetime +import pytest + +import env # noqa: F401 def test_chrono_system_clock(): # Get the time from both c++ and datetime + date0 = datetime.datetime.today() date1 = m.test_chrono1() date2 = datetime.datetime.today() @@ -12,16 +17,15 @@ def test_chrono_system_clock(): assert isinstance(date1, datetime.datetime) # The numbers should vary by a very small amount (time it took to execute) + diff_python = abs(date2 - date0) diff = abs(date1 - date2) - # There should never be a days/seconds difference + # There should never be a days difference assert diff.days == 0 - assert diff.seconds == 0 - # We test that no more than about 0.5 seconds passes here - # This makes sure that the dates created are very close to the same - # but if the testing system is incredibly overloaded this should still pass - assert diff.microseconds < 500000 + # Since datetime.datetime.today() calls time.time(), and on some platforms + # that has 1 second accuracy, we compare this way + assert diff.seconds <= diff_python.seconds def test_chrono_system_clock_roundtrip(): @@ -71,8 +75,36 @@ def test_chrono_system_clock_roundtrip_date(): assert time2.microsecond == 0 -def test_chrono_system_clock_roundtrip_time(): - time1 = datetime.datetime.today().time() +SKIP_TZ_ENV_ON_WIN = pytest.mark.skipif( + "env.WIN", reason="TZ environment variable only supported on POSIX" +) + + +@pytest.mark.parametrize( + "time1", + [ + datetime.datetime.today().time(), + datetime.time(0, 0, 0), + datetime.time(0, 0, 0, 1), + datetime.time(0, 28, 45, 109827), + datetime.time(0, 59, 59, 999999), + datetime.time(1, 0, 0), + datetime.time(5, 59, 59, 0), + datetime.time(5, 59, 59, 1), + ], +) +@pytest.mark.parametrize( + "tz", + [ + None, + pytest.param("Europe/Brussels", marks=SKIP_TZ_ENV_ON_WIN), + pytest.param("Asia/Pyongyang", marks=SKIP_TZ_ENV_ON_WIN), + pytest.param("America/New_York", marks=SKIP_TZ_ENV_ON_WIN), + ], +) +def test_chrono_system_clock_roundtrip_time(time1, tz, monkeypatch): + if tz is not None: + monkeypatch.setenv("TZ", "/usr/share/zoneinfo/{}".format(tz)) # Roundtrip the time datetime2 = m.test_chrono2(time1) @@ -173,4 +205,14 @@ def test_floating_point_duration(): def test_nano_timepoint(): time = datetime.datetime.now() time1 = m.test_nano_timepoint(time, datetime.timedelta(seconds=60)) - assert(time1 == time + datetime.timedelta(seconds=60)) + assert time1 == time + datetime.timedelta(seconds=60) + + +def test_chrono_different_resolutions(): + resolutions = m.different_resolutions() + time = datetime.datetime.now() + resolutions.timestamp_h = time + resolutions.timestamp_m = time + resolutions.timestamp_s = time + resolutions.timestamp_ms = time + resolutions.timestamp_us = time diff --git a/3rdparty/pybind11/tests/test_class.cpp b/3rdparty/pybind11/tests/test_class.cpp index 499d0cc5..890fab73 100644 --- a/3rdparty/pybind11/tests/test_class.cpp +++ b/3rdparty/pybind11/tests/test_class.cpp @@ -103,7 +103,7 @@ TEST_SUBMODULE(class_, m) { BaseClass() = default; BaseClass(const BaseClass &) = default; BaseClass(BaseClass &&) = default; - virtual ~BaseClass() {} + virtual ~BaseClass() = default; }; struct DerivedClass1 : BaseClass { }; struct DerivedClass2 : BaseClass { }; @@ -134,6 +134,32 @@ TEST_SUBMODULE(class_, m) { ); }); + struct Invalid {}; + + // test_type + m.def("check_type", [](int category) { + // Currently not supported (via a fail at compile time) + // See https://github.com/pybind/pybind11/issues/2486 + // if (category == 2) + // return py::type::of<int>(); + if (category == 1) + return py::type::of<DerivedClass1>(); + else + return py::type::of<Invalid>(); + }); + + m.def("get_type_of", [](py::object ob) { + return py::type::of(ob); + }); + + m.def("get_type_classic", [](py::handle h) { + return h.get_type(); + }); + + m.def("as_type", [](py::object ob) { + return py::type(ob); + }); + // test_mismatched_holder struct MismatchBase1 { }; struct MismatchDerived1 : MismatchBase1 { }; @@ -142,12 +168,12 @@ TEST_SUBMODULE(class_, m) { struct MismatchDerived2 : MismatchBase2 { }; m.def("mismatched_holder_1", []() { - auto mod = py::module::import("__main__"); + auto mod = py::module_::import("__main__"); py::class_<MismatchBase1, std::shared_ptr<MismatchBase1>>(mod, "MismatchBase1"); py::class_<MismatchDerived1, MismatchBase1>(mod, "MismatchDerived1"); }); m.def("mismatched_holder_2", []() { - auto mod = py::module::import("__main__"); + auto mod = py::module_::import("__main__"); py::class_<MismatchBase2>(mod, "MismatchBase2"); py::class_<MismatchDerived2, std::shared_ptr<MismatchDerived2>, MismatchBase2>(mod, "MismatchDerived2"); @@ -227,6 +253,8 @@ TEST_SUBMODULE(class_, m) { 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; }; struct PyAliasedHasOpNewDelSize : AliasedHasOpNewDelSize { PyAliasedHasOpNewDelSize() = default; @@ -277,6 +305,8 @@ TEST_SUBMODULE(class_, m) { class ProtectedB { public: virtual ~ProtectedB() = default; + ProtectedB() = default; + ProtectedB(const ProtectedB &) = delete; protected: virtual int foo() const { return value; } @@ -287,7 +317,7 @@ TEST_SUBMODULE(class_, m) { class TrampolineB : public ProtectedB { public: - int foo() const override { PYBIND11_OVERLOAD(int, ProtectedB, foo, ); } + int foo() const override { PYBIND11_OVERRIDE(int, ProtectedB, foo, ); } }; class PublicistB : public ProtectedB { @@ -323,7 +353,7 @@ TEST_SUBMODULE(class_, m) { // test_reentrant_implicit_conversion_failure // #1035: issue with runaway reentrant implicit conversion struct BogusImplicitConversion { - BogusImplicitConversion(const BogusImplicitConversion &) { } + BogusImplicitConversion(const BogusImplicitConversion &) = default; }; py::class_<BogusImplicitConversion>(m, "BogusImplicitConversion") @@ -367,19 +397,92 @@ TEST_SUBMODULE(class_, m) { .def(py::init<>()) .def("ptr", &Aligned::ptr); #endif + + // test_final + struct IsFinal final {}; + py::class_<IsFinal>(m, "IsFinal", py::is_final()); + + // test_non_final_final + struct IsNonFinalFinal {}; + py::class_<IsNonFinalFinal>(m, "IsNonFinalFinal", py::is_final()); + + // test_exception_rvalue_abort + struct PyPrintDestructor { + PyPrintDestructor() = default; + ~PyPrintDestructor() { + py::print("Print from destructor"); + } + void throw_something() { throw std::runtime_error("error"); } + }; + py::class_<PyPrintDestructor>(m, "PyPrintDestructor") + .def(py::init<>()) + .def("throw_something", &PyPrintDestructor::throw_something); + + // test_multiple_instances_with_same_pointer + struct SamePointer {}; + static SamePointer samePointer; + py::class_<SamePointer, std::unique_ptr<SamePointer, py::nodelete>>(m, "SamePointer") + .def(py::init([]() { return &samePointer; })) + .def("__del__", [](SamePointer&) { py::print("__del__ called"); }); + + struct Empty {}; + py::class_<Empty>(m, "Empty") + .def(py::init<>()); + + // test_base_and_derived_nested_scope + struct BaseWithNested { + struct Nested {}; + }; + + struct DerivedWithNested : BaseWithNested { + struct Nested {}; + }; + + py::class_<BaseWithNested> baseWithNested_class(m, "BaseWithNested"); + py::class_<DerivedWithNested, BaseWithNested> derivedWithNested_class(m, "DerivedWithNested"); + py::class_<BaseWithNested::Nested>(baseWithNested_class, "Nested") + .def_static("get_name", []() { return "BaseWithNested::Nested"; }); + py::class_<DerivedWithNested::Nested>(derivedWithNested_class, "Nested") + .def_static("get_name", []() { return "DerivedWithNested::Nested"; }); + + // test_register_duplicate_class + struct Duplicate {}; + struct OtherDuplicate {}; + struct DuplicateNested {}; + struct OtherDuplicateNested {}; + m.def("register_duplicate_class_name", [](py::module_ m) { + py::class_<Duplicate>(m, "Duplicate"); + py::class_<OtherDuplicate>(m, "Duplicate"); + }); + m.def("register_duplicate_class_type", [](py::module_ m) { + py::class_<OtherDuplicate>(m, "OtherDuplicate"); + py::class_<OtherDuplicate>(m, "YetAnotherDuplicate"); + }); + m.def("register_duplicate_nested_class_name", [](py::object gt) { + py::class_<DuplicateNested>(gt, "DuplicateNested"); + py::class_<OtherDuplicateNested>(gt, "DuplicateNested"); + }); + m.def("register_duplicate_nested_class_type", [](py::object gt) { + py::class_<OtherDuplicateNested>(gt, "OtherDuplicateNested"); + py::class_<OtherDuplicateNested>(gt, "YetAnotherDuplicateNested"); + }); } -template <int N> class BreaksBase { public: virtual ~BreaksBase() = default; }; +template <int N> class BreaksBase { public: + virtual ~BreaksBase() = default; + BreaksBase() = default; + BreaksBase(const BreaksBase&) = delete; +}; template <int N> class BreaksTramp : public BreaksBase<N> {}; // These should all compile just fine: -typedef py::class_<BreaksBase<1>, std::unique_ptr<BreaksBase<1>>, BreaksTramp<1>> DoesntBreak1; -typedef py::class_<BreaksBase<2>, BreaksTramp<2>, std::unique_ptr<BreaksBase<2>>> DoesntBreak2; -typedef py::class_<BreaksBase<3>, std::unique_ptr<BreaksBase<3>>> DoesntBreak3; -typedef py::class_<BreaksBase<4>, BreaksTramp<4>> DoesntBreak4; -typedef py::class_<BreaksBase<5>> DoesntBreak5; -typedef py::class_<BreaksBase<6>, std::shared_ptr<BreaksBase<6>>, BreaksTramp<6>> DoesntBreak6; -typedef py::class_<BreaksBase<7>, BreaksTramp<7>, std::shared_ptr<BreaksBase<7>>> DoesntBreak7; -typedef py::class_<BreaksBase<8>, std::shared_ptr<BreaksBase<8>>> DoesntBreak8; +using DoesntBreak1 = py::class_<BreaksBase<1>, std::unique_ptr<BreaksBase<1>>, BreaksTramp<1>>; +using DoesntBreak2 = py::class_<BreaksBase<2>, BreaksTramp<2>, std::unique_ptr<BreaksBase<2>>>; +using DoesntBreak3 = py::class_<BreaksBase<3>, std::unique_ptr<BreaksBase<3>>>; +using DoesntBreak4 = py::class_<BreaksBase<4>, BreaksTramp<4>>; +using DoesntBreak5 = py::class_<BreaksBase<5>>; +using DoesntBreak6 = py::class_<BreaksBase<6>, std::shared_ptr<BreaksBase<6>>, BreaksTramp<6>>; +using DoesntBreak7 = py::class_<BreaksBase<7>, BreaksTramp<7>, std::shared_ptr<BreaksBase<7>>>; +using DoesntBreak8 = py::class_<BreaksBase<8>, std::shared_ptr<BreaksBase<8>>>; #define CHECK_BASE(N) static_assert(std::is_same<typename DoesntBreak##N::type, BreaksBase<N>>::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); diff --git a/3rdparty/pybind11/tests/test_class.py b/3rdparty/pybind11/tests/test_class.py index ed63ca85..bdcced96 100644 --- a/3rdparty/pybind11/tests/test_class.py +++ b/3rdparty/pybind11/tests/test_class.py @@ -1,5 +1,8 @@ +# -*- coding: utf-8 -*- import pytest +import env # noqa: F401 + from pybind11_tests import class_ as m from pybind11_tests import UserType, ConstructorStats @@ -23,6 +26,48 @@ def test_instance(msg): assert cstats.alive() == 0 +def test_type(): + assert m.check_type(1) == m.DerivedClass1 + with pytest.raises(RuntimeError) as execinfo: + m.check_type(0) + + assert "pybind11::detail::get_type_info: unable to find type info" in str( + execinfo.value + ) + assert "Invalid" in str(execinfo.value) + + # Currently not supported + # See https://github.com/pybind/pybind11/issues/2486 + # assert m.check_type(2) == int + + +def test_type_of_py(): + assert m.get_type_of(1) == int + assert m.get_type_of(m.DerivedClass1()) == m.DerivedClass1 + assert m.get_type_of(int) == type + + +def test_type_of_classic(): + assert m.get_type_classic(1) == int + assert m.get_type_classic(m.DerivedClass1()) == m.DerivedClass1 + assert m.get_type_classic(int) == type + + +def test_type_of_py_nodelete(): + # If the above test deleted the class, this will segfault + assert m.get_type_of(m.DerivedClass1()) == m.DerivedClass1 + + +def test_as_type_py(): + assert m.as_type(int) == int + + with pytest.raises(TypeError): + assert m.as_type(1) == int + + with pytest.raises(TypeError): + assert m.as_type(m.DerivedClass1()) == m.DerivedClass1 + + def test_docstrings(doc): assert doc(UserType) == "A `py::class_` type for testing" assert UserType.__name__ == "UserType" @@ -30,18 +75,24 @@ def test_docstrings(doc): assert UserType.get_value.__name__ == "get_value" assert UserType.get_value.__module__ == "pybind11_tests" - assert doc(UserType.get_value) == """ + assert ( + doc(UserType.get_value) + == """ get_value(self: m.UserType) -> int Get value using a method """ + ) assert doc(UserType.value) == "Get/set value using a property" - assert doc(m.NoConstructor.new_instance) == """ + assert ( + doc(m.NoConstructor.new_instance) + == """ new_instance() -> m.class_.NoConstructor Return an instance """ + ) def test_qualname(doc): @@ -50,57 +101,98 @@ def test_qualname(doc): assert m.NestBase.__qualname__ == "NestBase" assert m.NestBase.Nested.__qualname__ == "NestBase.Nested" - assert doc(m.NestBase.__init__) == """ + assert ( + doc(m.NestBase.__init__) + == """ __init__(self: m.class_.NestBase) -> None """ - assert doc(m.NestBase.g) == """ + ) + assert ( + doc(m.NestBase.g) + == """ g(self: m.class_.NestBase, arg0: m.class_.NestBase.Nested) -> None """ - assert doc(m.NestBase.Nested.__init__) == """ + ) + assert ( + doc(m.NestBase.Nested.__init__) + == """ __init__(self: m.class_.NestBase.Nested) -> None """ - assert doc(m.NestBase.Nested.fn) == """ + ) + assert ( + doc(m.NestBase.Nested.fn) + == """ fn(self: m.class_.NestBase.Nested, arg0: int, arg1: m.class_.NestBase, arg2: m.class_.NestBase.Nested) -> None """ # noqa: E501 line too long - assert doc(m.NestBase.Nested.fa) == """ + ) + assert ( + doc(m.NestBase.Nested.fa) + == """ fa(self: m.class_.NestBase.Nested, a: int, b: m.class_.NestBase, c: m.class_.NestBase.Nested) -> None """ # noqa: E501 line too long + ) assert m.NestBase.__module__ == "pybind11_tests.class_" assert m.NestBase.Nested.__module__ == "pybind11_tests.class_" def test_inheritance(msg): - roger = m.Rabbit('Rabbit') + roger = m.Rabbit("Rabbit") assert roger.name() + " is a " + roger.species() == "Rabbit is a parrot" assert m.pet_name_species(roger) == "Rabbit is a parrot" - polly = m.Pet('Polly', 'parrot') + polly = m.Pet("Polly", "parrot") assert polly.name() + " is a " + polly.species() == "Polly is a parrot" assert m.pet_name_species(polly) == "Polly is a parrot" - molly = m.Dog('Molly') + molly = m.Dog("Molly") assert molly.name() + " is a " + molly.species() == "Molly is a dog" assert m.pet_name_species(molly) == "Molly is a dog" - fred = m.Hamster('Fred') + fred = m.Hamster("Fred") assert fred.name() + " is a " + fred.species() == "Fred is a rodent" assert m.dog_bark(molly) == "Woof!" with pytest.raises(TypeError) as excinfo: m.dog_bark(polly) - assert msg(excinfo.value) == """ + assert ( + msg(excinfo.value) + == """ dog_bark(): incompatible function arguments. The following argument types are supported: 1. (arg0: m.class_.Dog) -> str Invoked with: <m.class_.Pet object at 0> """ + ) with pytest.raises(TypeError) as excinfo: m.Chimera("lion", "goat") assert "No constructor defined!" in str(excinfo.value) +def test_inheritance_init(msg): + + # Single base + class Python(m.Pet): + def __init__(self): + pass + + with pytest.raises(TypeError) as exc_info: + Python() + expected = "m.class_.Pet.__init__() must be called when overriding __init__" + assert msg(exc_info.value) == expected + + # Multiple bases + class RabbitHamster(m.Rabbit, m.Hamster): + def __init__(self): + m.Rabbit.__init__(self, "RabbitHamster") + + with pytest.raises(TypeError) as exc_info: + RabbitHamster() + expected = "m.class_.Hamster.__init__() must be called when overriding __init__" + assert msg(exc_info.value) == expected + + def test_automatic_upcasting(): assert type(m.return_class_1()).__name__ == "DerivedClass1" assert type(m.return_class_2()).__name__ == "DerivedClass2" @@ -126,13 +218,19 @@ def test_mismatched_holder(): with pytest.raises(RuntimeError) as excinfo: m.mismatched_holder_1() - assert re.match('generic_type: type ".*MismatchDerived1" does not have a non-default ' - 'holder type while its base ".*MismatchBase1" does', str(excinfo.value)) + assert re.match( + 'generic_type: type ".*MismatchDerived1" does not have a non-default ' + 'holder type while its base ".*MismatchBase1" does', + str(excinfo.value), + ) with pytest.raises(RuntimeError) as excinfo: m.mismatched_holder_2() - assert re.match('generic_type: type ".*MismatchDerived2" has a non-default holder type ' - 'while its base ".*MismatchBase2" does not', str(excinfo.value)) + assert re.match( + 'generic_type: type ".*MismatchDerived2" has a non-default holder type ' + 'while its base ".*MismatchBase2" does not', + str(excinfo.value), + ) def test_override_static(): @@ -164,20 +262,20 @@ def test_operator_new_delete(capture): a = m.HasOpNewDel() b = m.HasOpNewDelSize() d = m.HasOpNewDelBoth() - assert capture == """ + assert ( + capture + == """ A new 8 B new 4 D new 32 """ + ) sz_alias = str(m.AliasedHasOpNewDelSize.size_alias) sz_noalias = str(m.AliasedHasOpNewDelSize.size_noalias) with capture: c = m.AliasedHasOpNewDelSize() c2 = SubAliased() - assert capture == ( - "C new " + sz_noalias + "\n" + - "C new " + sz_alias + "\n" - ) + assert capture == ("C new " + sz_noalias + "\n" + "C new " + sz_alias + "\n") with capture: del a @@ -186,21 +284,21 @@ def test_operator_new_delete(capture): pytest.gc_collect() del d pytest.gc_collect() - assert capture == """ + assert ( + capture + == """ A delete B delete 4 D delete """ + ) with capture: del c pytest.gc_collect() del c2 pytest.gc_collect() - assert capture == ( - "C delete " + sz_noalias + "\n" + - "C delete " + sz_alias + "\n" - ) + assert capture == ("C delete " + sz_noalias + "\n" + "C delete " + sz_alias + "\n") def test_bind_protected_functions(): @@ -235,7 +333,7 @@ def test_brace_initialization(): assert b.vec == [123, 456] -@pytest.unsupported_on_pypy +@pytest.mark.xfail("env.PYPY") def test_class_refcount(): """Instances must correctly increase/decrease the reference count of their types (#1029)""" from sys import getrefcount @@ -260,22 +358,109 @@ def test_reentrant_implicit_conversion_failure(msg): # ensure that there is no runaway reentrant implicit conversion (#1035) with pytest.raises(TypeError) as excinfo: m.BogusImplicitConversion(0) - assert msg(excinfo.value) == ''' + assert ( + msg(excinfo.value) + == """ __init__(): incompatible constructor arguments. The following argument types are supported: 1. m.class_.BogusImplicitConversion(arg0: m.class_.BogusImplicitConversion) Invoked with: 0 - ''' + """ + ) def test_error_after_conversions(): with pytest.raises(TypeError) as exc_info: m.test_error_after_conversions("hello") assert str(exc_info.value).startswith( - "Unable to convert function return value to a Python type!") + "Unable to convert function return value to a Python type!" + ) def test_aligned(): if hasattr(m, "Aligned"): p = m.Aligned().ptr() assert p % 1024 == 0 + + +# https://foss.heptapod.net/pypy/pypy/-/issues/2742 +@pytest.mark.xfail("env.PYPY") +def test_final(): + with pytest.raises(TypeError) as exc_info: + + class PyFinalChild(m.IsFinal): + pass + + assert str(exc_info.value).endswith("is not an acceptable base type") + + +# https://foss.heptapod.net/pypy/pypy/-/issues/2742 +@pytest.mark.xfail("env.PYPY") +def test_non_final_final(): + with pytest.raises(TypeError) as exc_info: + + class PyNonFinalFinalChild(m.IsNonFinalFinal): + pass + + assert str(exc_info.value).endswith("is not an acceptable base type") + + +# https://github.com/pybind/pybind11/issues/1878 +def test_exception_rvalue_abort(): + with pytest.raises(RuntimeError): + m.PyPrintDestructor().throw_something() + + +# https://github.com/pybind/pybind11/issues/1568 +def test_multiple_instances_with_same_pointer(capture): + n = 100 + instances = [m.SamePointer() for _ in range(n)] + for i in range(n): + # We need to reuse the same allocated memory for with a different type, + # to ensure the bug in `deregister_instance_impl` is detected. Otherwise + # `Py_TYPE(self) == Py_TYPE(it->second)` will still succeed, even though + # the `instance` is already deleted. + instances[i] = m.Empty() + # No assert: if this does not trigger the error + # pybind11_fail("pybind11_object_dealloc(): Tried to deallocate unregistered instance!"); + # and just completes without crashing, we're good. + + +# https://github.com/pybind/pybind11/issues/1624 +def test_base_and_derived_nested_scope(): + assert issubclass(m.DerivedWithNested, m.BaseWithNested) + assert m.BaseWithNested.Nested != m.DerivedWithNested.Nested + assert m.BaseWithNested.Nested.get_name() == "BaseWithNested::Nested" + assert m.DerivedWithNested.Nested.get_name() == "DerivedWithNested::Nested" + + +def test_register_duplicate_class(): + import types + + module_scope = types.ModuleType("module_scope") + with pytest.raises(RuntimeError) as exc_info: + m.register_duplicate_class_name(module_scope) + expected = ( + 'generic_type: cannot initialize type "Duplicate": ' + "an object with that name is already defined" + ) + assert str(exc_info.value) == expected + with pytest.raises(RuntimeError) as exc_info: + m.register_duplicate_class_type(module_scope) + expected = 'generic_type: type "YetAnotherDuplicate" is already registered!' + assert str(exc_info.value) == expected + + class ClassScope: + pass + + with pytest.raises(RuntimeError) as exc_info: + m.register_duplicate_nested_class_name(ClassScope) + expected = ( + 'generic_type: cannot initialize type "DuplicateNested": ' + "an object with that name is already defined" + ) + assert str(exc_info.value) == expected + with pytest.raises(RuntimeError) as exc_info: + m.register_duplicate_nested_class_type(ClassScope) + expected = 'generic_type: type "YetAnotherDuplicateNested" is already registered!' + assert str(exc_info.value) == expected diff --git a/3rdparty/pybind11/tests/test_cmake_build/CMakeLists.txt b/3rdparty/pybind11/tests/test_cmake_build/CMakeLists.txt index c9b5fcb2..0c0578ad 100644 --- a/3rdparty/pybind11/tests/test_cmake_build/CMakeLists.txt +++ b/3rdparty/pybind11/tests/test_cmake_build/CMakeLists.txt @@ -1,56 +1,77 @@ -add_custom_target(test_cmake_build) +# Built-in in CMake 3.5+ +include(CMakeParseArguments) -if(CMAKE_VERSION VERSION_LESS 3.1) - # 3.0 needed for interface library for subdirectory_target/installed_target - # 3.1 needed for cmake -E env for testing - return() -endif() +add_custom_target(test_cmake_build) -include(CMakeParseArguments) function(pybind11_add_build_test name) cmake_parse_arguments(ARG "INSTALL" "" "" ${ARGN}) - set(build_options "-DCMAKE_PREFIX_PATH=${PROJECT_BINARY_DIR}/mock_install" - "-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}" - "-DPYTHON_EXECUTABLE:FILEPATH=${PYTHON_EXECUTABLE}" - "-DPYBIND11_CPP_STANDARD=${PYBIND11_CPP_STANDARD}") + set(build_options "-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}") + + if(PYBIND11_FINDPYTHON) + list(APPEND build_options "-DPYBIND11_FINDPYTHON=${PYBIND11_FINDPYTHON}") + + if(DEFINED Python_ROOT_DIR) + list(APPEND build_options "-DPython_ROOT_DIR=${Python_ROOT_DIR}") + endif() + + list(APPEND build_options "-DPython_EXECUTABLE=${Python_EXECUTABLE}") + else() + list(APPEND build_options "-DPYTHON_EXECUTABLE=${PYTHON_EXECUTABLE}") + endif() + + if(DEFINED CMAKE_CXX_STANDARD) + list(APPEND build_options "-DCMAKE_CXX_STANDARD=${CMAKE_CXX_STANDARD}") + endif() + if(NOT ARG_INSTALL) - list(APPEND build_options "-DPYBIND11_PROJECT_DIR=${PROJECT_SOURCE_DIR}") + list(APPEND build_options "-DPYBIND11_PROJECT_DIR=${pybind11_SOURCE_DIR}") + else() + list(APPEND build_options "-DCMAKE_PREFIX_PATH=${pybind11_BINARY_DIR}/mock_install") endif() - add_custom_target(test_${name} ${CMAKE_CTEST_COMMAND} - --quiet --output-log ${name}.log - --build-and-test "${CMAKE_CURRENT_SOURCE_DIR}/${name}" - "${CMAKE_CURRENT_BINARY_DIR}/${name}" - --build-config Release + add_custom_target( + test_build_${name} + ${CMAKE_CTEST_COMMAND} + --build-and-test + "${CMAKE_CURRENT_SOURCE_DIR}/${name}" + "${CMAKE_CURRENT_BINARY_DIR}/${name}" + --build-config + Release --build-noclean - --build-generator ${CMAKE_GENERATOR} - $<$<BOOL:${CMAKE_GENERATOR_PLATFORM}>:--build-generator-platform> ${CMAKE_GENERATOR_PLATFORM} - --build-makeprogram ${CMAKE_MAKE_PROGRAM} - --build-target check - --build-options ${build_options} - ) + --build-generator + ${CMAKE_GENERATOR} + $<$<BOOL:${CMAKE_GENERATOR_PLATFORM}>:--build-generator-platform> + ${CMAKE_GENERATOR_PLATFORM} + --build-makeprogram + ${CMAKE_MAKE_PROGRAM} + --build-target + check_${name} + --build-options + ${build_options}) if(ARG_INSTALL) - add_dependencies(test_${name} mock_install) + add_dependencies(test_build_${name} mock_install) endif() - add_dependencies(test_cmake_build test_${name}) + add_dependencies(test_cmake_build test_build_${name}) endfunction() pybind11_add_build_test(subdirectory_function) pybind11_add_build_test(subdirectory_target) -if(NOT ${PYTHON_MODULE_EXTENSION} MATCHES "pypy") +if("${PYTHON_MODULE_EXTENSION}" MATCHES "pypy" OR "${Python_INTERPRETER_ID}" STREQUAL "PyPy") + message(STATUS "Skipping embed test on PyPy") +else() pybind11_add_build_test(subdirectory_embed) endif() if(PYBIND11_INSTALL) - add_custom_target(mock_install ${CMAKE_COMMAND} - "-DCMAKE_INSTALL_PREFIX=${PROJECT_BINARY_DIR}/mock_install" - -P "${PROJECT_BINARY_DIR}/cmake_install.cmake" - ) + add_custom_target( + mock_install ${CMAKE_COMMAND} "-DCMAKE_INSTALL_PREFIX=${pybind11_BINARY_DIR}/mock_install" -P + "${pybind11_BINARY_DIR}/cmake_install.cmake") pybind11_add_build_test(installed_function INSTALL) pybind11_add_build_test(installed_target INSTALL) - if(NOT ${PYTHON_MODULE_EXTENSION} MATCHES "pypy") + if(NOT ("${PYTHON_MODULE_EXTENSION}" MATCHES "pypy" OR "${Python_INTERPRETER_ID}" STREQUAL "PyPy" + )) pybind11_add_build_test(installed_embed INSTALL) endif() endif() diff --git a/3rdparty/pybind11/tests/test_cmake_build/embed.cpp b/3rdparty/pybind11/tests/test_cmake_build/embed.cpp index b9581d2f..a3abc8a8 100644 --- a/3rdparty/pybind11/tests/test_cmake_build/embed.cpp +++ b/3rdparty/pybind11/tests/test_cmake_build/embed.cpp @@ -12,10 +12,10 @@ int main(int argc, char *argv[]) { py::scoped_interpreter guard{}; - auto m = py::module::import("test_cmake_build"); + auto m = py::module_::import("test_cmake_build"); if (m.attr("add")(1, 2).cast<int>() != 3) throw std::runtime_error("embed.cpp failed"); - py::module::import("sys").attr("argv") = py::make_tuple("test.py", "embed.cpp"); + 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 f7fc09c2..64ae5c4b 100644 --- a/3rdparty/pybind11/tests/test_cmake_build/installed_embed/CMakeLists.txt +++ b/3rdparty/pybind11/tests/test_cmake_build/installed_embed/CMakeLists.txt @@ -1,15 +1,26 @@ -cmake_minimum_required(VERSION 3.0) +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) + cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}) +else() + cmake_policy(VERSION 3.18) +endif() + project(test_installed_embed CXX) -set(CMAKE_MODULE_PATH "") find_package(pybind11 CONFIG REQUIRED) message(STATUS "Found pybind11 v${pybind11_VERSION}: ${pybind11_INCLUDE_DIRS}") -add_executable(test_cmake_build ../embed.cpp) -target_link_libraries(test_cmake_build PRIVATE pybind11::embed) +add_executable(test_installed_embed ../embed.cpp) +target_link_libraries(test_installed_embed PRIVATE pybind11::embed) +set_target_properties(test_installed_embed PROPERTIES OUTPUT_NAME test_cmake_build) # Do not treat includes from IMPORTED target as SYSTEM (Python headers in pybind11::embed). # This may be needed to resolve header conflicts, e.g. between Python release and debug headers. -set_target_properties(test_cmake_build PROPERTIES NO_SYSTEM_FROM_IMPORTED ON) +set_target_properties(test_installed_embed PROPERTIES NO_SYSTEM_FROM_IMPORTED ON) -add_custom_target(check $<TARGET_FILE:test_cmake_build> ${PROJECT_SOURCE_DIR}/../test.py) +add_custom_target(check_installed_embed $<TARGET_FILE:test_installed_embed> + ${PROJECT_SOURCE_DIR}/../test.py) 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 e0c20a8a..1a502863 100644 --- a/3rdparty/pybind11/tests/test_cmake_build/installed_function/CMakeLists.txt +++ b/3rdparty/pybind11/tests/test_cmake_build/installed_function/CMakeLists.txt @@ -1,12 +1,38 @@ -cmake_minimum_required(VERSION 2.8.12) +cmake_minimum_required(VERSION 3.4) project(test_installed_module CXX) -set(CMAKE_MODULE_PATH "") +# 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) + cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}) +else() + cmake_policy(VERSION 3.18) +endif() + +project(test_installed_function CXX) find_package(pybind11 CONFIG REQUIRED) -message(STATUS "Found pybind11 v${pybind11_VERSION}: ${pybind11_INCLUDE_DIRS}") +message( + STATUS "Found pybind11 v${pybind11_VERSION} ${pybind11_VERSION_TYPE}: ${pybind11_INCLUDE_DIRS}") + +pybind11_add_module(test_installed_function SHARED NO_EXTRAS ../main.cpp) +set_target_properties(test_installed_function PROPERTIES OUTPUT_NAME test_cmake_build) -pybind11_add_module(test_cmake_build SHARED NO_EXTRAS ../main.cpp) +if(DEFINED Python_EXECUTABLE) + set(_Python_EXECUTABLE "${Python_EXECUTABLE}") +elseif(DEFINED PYTHON_EXECUTABLE) + set(_Python_EXECUTABLE "${PYTHON_EXECUTABLE}") +else() + message(FATAL_ERROR "No Python executable defined (should not be possible at this stage)") +endif() -add_custom_target(check ${CMAKE_COMMAND} -E env PYTHONPATH=$<TARGET_FILE_DIR:test_cmake_build> - ${PYTHON_EXECUTABLE} ${PROJECT_SOURCE_DIR}/../test.py ${PROJECT_NAME}) +add_custom_target( + check_installed_function + ${CMAKE_COMMAND} + -E + env + PYTHONPATH=$<TARGET_FILE_DIR:test_installed_function> + ${_Python_EXECUTABLE} + ${PROJECT_SOURCE_DIR}/../test.py + ${PROJECT_NAME}) 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 cd3ae6f7..b38eb774 100644 --- a/3rdparty/pybind11/tests/test_cmake_build/installed_target/CMakeLists.txt +++ b/3rdparty/pybind11/tests/test_cmake_build/installed_target/CMakeLists.txt @@ -1,22 +1,45 @@ -cmake_minimum_required(VERSION 3.0) -project(test_installed_target CXX) +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) + cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}) +else() + cmake_policy(VERSION 3.18) +endif() -set(CMAKE_MODULE_PATH "") +project(test_installed_target CXX) find_package(pybind11 CONFIG REQUIRED) message(STATUS "Found pybind11 v${pybind11_VERSION}: ${pybind11_INCLUDE_DIRS}") -add_library(test_cmake_build MODULE ../main.cpp) +add_library(test_installed_target MODULE ../main.cpp) -target_link_libraries(test_cmake_build PRIVATE pybind11::module) +target_link_libraries(test_installed_target PRIVATE pybind11::module) +set_target_properties(test_installed_target PROPERTIES OUTPUT_NAME test_cmake_build) -# make sure result is, for example, test_installed_target.so, not libtest_installed_target.dylib -set_target_properties(test_cmake_build PROPERTIES PREFIX "${PYTHON_MODULE_PREFIX}" - SUFFIX "${PYTHON_MODULE_EXTENSION}") +# Make sure result is, for example, test_installed_target.so, not libtest_installed_target.dylib +pybind11_extension(test_installed_target) # Do not treat includes from IMPORTED target as SYSTEM (Python headers in pybind11::module). # This may be needed to resolve header conflicts, e.g. between Python release and debug headers. -set_target_properties(test_cmake_build PROPERTIES NO_SYSTEM_FROM_IMPORTED ON) +set_target_properties(test_installed_target PROPERTIES NO_SYSTEM_FROM_IMPORTED ON) + +if(DEFINED Python_EXECUTABLE) + set(_Python_EXECUTABLE "${Python_EXECUTABLE}") +elseif(DEFINED PYTHON_EXECUTABLE) + set(_Python_EXECUTABLE "${PYTHON_EXECUTABLE}") +else() + message(FATAL_ERROR "No Python executable defined (should not be possible at this stage)") +endif() -add_custom_target(check ${CMAKE_COMMAND} -E env PYTHONPATH=$<TARGET_FILE_DIR:test_cmake_build> - ${PYTHON_EXECUTABLE} ${PROJECT_SOURCE_DIR}/../test.py ${PROJECT_NAME}) +add_custom_target( + check_installed_target + ${CMAKE_COMMAND} + -E + env + PYTHONPATH=$<TARGET_FILE_DIR:test_installed_target> + ${_Python_EXECUTABLE} + ${PROJECT_SOURCE_DIR}/../test.py + ${PROJECT_NAME}) 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 88ba60dd..c7df0cf7 100644 --- a/3rdparty/pybind11/tests/test_cmake_build/subdirectory_embed/CMakeLists.txt +++ b/3rdparty/pybind11/tests/test_cmake_build/subdirectory_embed/CMakeLists.txt @@ -1,25 +1,39 @@ -cmake_minimum_required(VERSION 3.0) +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) + cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}) +else() + cmake_policy(VERSION 3.18) +endif() + project(test_subdirectory_embed CXX) -set(PYBIND11_INSTALL ON CACHE BOOL "") +set(PYBIND11_INSTALL + ON + CACHE BOOL "") set(PYBIND11_EXPORT_NAME test_export) add_subdirectory(${PYBIND11_PROJECT_DIR} pybind11) # Test basic target functionality -add_executable(test_cmake_build ../embed.cpp) -target_link_libraries(test_cmake_build PRIVATE pybind11::embed) +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 $<TARGET_FILE:test_cmake_build> ${PROJECT_SOURCE_DIR}/../test.py) +add_custom_target(check_subdirectory_embed $<TARGET_FILE:test_subdirectory_embed> + ${PROJECT_SOURCE_DIR}/../test.py) # Test custom export group -- PYBIND11_EXPORT_NAME add_library(test_embed_lib ../embed.cpp) target_link_libraries(test_embed_lib PRIVATE pybind11::embed) -install(TARGETS test_embed_lib - EXPORT test_export - ARCHIVE DESTINATION bin - LIBRARY DESTINATION lib - RUNTIME DESTINATION lib) -install(EXPORT test_export - DESTINATION lib/cmake/test_export/test_export-Targets.cmake) +install( + TARGETS test_embed_lib + EXPORT test_export + ARCHIVE DESTINATION bin + LIBRARY DESTINATION lib + RUNTIME DESTINATION lib) +install(EXPORT test_export DESTINATION lib/cmake/test_export/test_export-Targets.cmake) 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 278007ae..624c600f 100644 --- a/3rdparty/pybind11/tests/test_cmake_build/subdirectory_function/CMakeLists.txt +++ b/3rdparty/pybind11/tests/test_cmake_build/subdirectory_function/CMakeLists.txt @@ -1,8 +1,34 @@ -cmake_minimum_required(VERSION 2.8.12) -project(test_subdirectory_module CXX) +cmake_minimum_required(VERSION 3.4) -add_subdirectory(${PYBIND11_PROJECT_DIR} pybind11) -pybind11_add_module(test_cmake_build THIN_LTO ../main.cpp) +# 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) + cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}) +else() + cmake_policy(VERSION 3.18) +endif() -add_custom_target(check ${CMAKE_COMMAND} -E env PYTHONPATH=$<TARGET_FILE_DIR:test_cmake_build> - ${PYTHON_EXECUTABLE} ${PROJECT_SOURCE_DIR}/../test.py ${PROJECT_NAME}) +project(test_subdirectory_function CXX) + +add_subdirectory("${PYBIND11_PROJECT_DIR}" pybind11) +pybind11_add_module(test_subdirectory_function ../main.cpp) +set_target_properties(test_subdirectory_function PROPERTIES OUTPUT_NAME test_cmake_build) + +if(DEFINED Python_EXECUTABLE) + set(_Python_EXECUTABLE "${Python_EXECUTABLE}") +elseif(DEFINED PYTHON_EXECUTABLE) + set(_Python_EXECUTABLE "${PYTHON_EXECUTABLE}") +else() + message(FATAL_ERROR "No Python executable defined (should not be possible at this stage)") +endif() + +add_custom_target( + check_subdirectory_function + ${CMAKE_COMMAND} + -E + env + PYTHONPATH=$<TARGET_FILE_DIR:test_subdirectory_function> + ${_Python_EXECUTABLE} + ${PROJECT_SOURCE_DIR}/../test.py + ${PROJECT_NAME}) 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 6b142d62..2471941f 100644 --- a/3rdparty/pybind11/tests/test_cmake_build/subdirectory_target/CMakeLists.txt +++ b/3rdparty/pybind11/tests/test_cmake_build/subdirectory_target/CMakeLists.txt @@ -1,15 +1,40 @@ -cmake_minimum_required(VERSION 3.0) +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) + cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}) +else() + cmake_policy(VERSION 3.18) +endif() + project(test_subdirectory_target CXX) add_subdirectory(${PYBIND11_PROJECT_DIR} pybind11) -add_library(test_cmake_build MODULE ../main.cpp) +add_library(test_subdirectory_target MODULE ../main.cpp) +set_target_properties(test_subdirectory_target PROPERTIES OUTPUT_NAME test_cmake_build) + +target_link_libraries(test_subdirectory_target PRIVATE pybind11::module) -target_link_libraries(test_cmake_build PRIVATE pybind11::module) +# Make sure result is, for example, test_installed_target.so, not libtest_installed_target.dylib +pybind11_extension(test_subdirectory_target) -# make sure result is, for example, test_installed_target.so, not libtest_installed_target.dylib -set_target_properties(test_cmake_build PROPERTIES PREFIX "${PYTHON_MODULE_PREFIX}" - SUFFIX "${PYTHON_MODULE_EXTENSION}") +if(DEFINED Python_EXECUTABLE) + set(_Python_EXECUTABLE "${Python_EXECUTABLE}") +elseif(DEFINED PYTHON_EXECUTABLE) + set(_Python_EXECUTABLE "${PYTHON_EXECUTABLE}") +else() + message(FATAL_ERROR "No Python executable defined (should not be possible at this stage)") +endif() -add_custom_target(check ${CMAKE_COMMAND} -E env PYTHONPATH=$<TARGET_FILE_DIR:test_cmake_build> - ${PYTHON_EXECUTABLE} ${PROJECT_SOURCE_DIR}/../test.py ${PROJECT_NAME}) +add_custom_target( + check_subdirectory_target + ${CMAKE_COMMAND} + -E + env + PYTHONPATH=$<TARGET_FILE_DIR:test_subdirectory_target> + ${_Python_EXECUTABLE} + ${PROJECT_SOURCE_DIR}/../test.py + ${PROJECT_NAME}) diff --git a/3rdparty/pybind11/tests/test_cmake_build/test.py b/3rdparty/pybind11/tests/test_cmake_build/test.py index 1467a61d..87ed5135 100644 --- a/3rdparty/pybind11/tests/test_cmake_build/test.py +++ b/3rdparty/pybind11/tests/test_cmake_build/test.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- import sys import test_cmake_build diff --git a/3rdparty/pybind11/tests/test_constants_and_functions.cpp b/3rdparty/pybind11/tests/test_constants_and_functions.cpp index e8ec74b7..f6077955 100644 --- a/3rdparty/pybind11/tests/test_constants_and_functions.cpp +++ b/3rdparty/pybind11/tests/test_constants_and_functions.cpp @@ -74,7 +74,7 @@ struct C { # pragma GCC diagnostic pop #endif }; -} +} // namespace test_exc_sp TEST_SUBMODULE(constants_and_functions, m) { diff --git a/3rdparty/pybind11/tests/test_constants_and_functions.py b/3rdparty/pybind11/tests/test_constants_and_functions.py index 472682d6..b980ccf1 100644 --- a/3rdparty/pybind11/tests/test_constants_and_functions.py +++ b/3rdparty/pybind11/tests/test_constants_and_functions.py @@ -1,4 +1,7 @@ -from pybind11_tests import constants_and_functions as m +# -*- coding: utf-8 -*- +import pytest + +m = pytest.importorskip("pybind11_tests.constants_and_functions") def test_constants(): diff --git a/3rdparty/pybind11/tests/test_copy_move.cpp b/3rdparty/pybind11/tests/test_copy_move.cpp index 98d5e0a0..2704217a 100644 --- a/3rdparty/pybind11/tests/test_copy_move.cpp +++ b/3rdparty/pybind11/tests/test_copy_move.cpp @@ -19,14 +19,14 @@ struct empty { }; struct lacking_copy_ctor : public empty<lacking_copy_ctor> { - lacking_copy_ctor() {} + lacking_copy_ctor() = default; lacking_copy_ctor(const lacking_copy_ctor& other) = delete; }; template <> lacking_copy_ctor empty<lacking_copy_ctor>::instance_ = {}; struct lacking_move_ctor : public empty<lacking_move_ctor> { - lacking_move_ctor() {} + lacking_move_ctor() = default; lacking_move_ctor(const lacking_move_ctor& other) = delete; lacking_move_ctor(lacking_move_ctor&& other) = delete; }; @@ -68,8 +68,8 @@ public: int value; }; -NAMESPACE_BEGIN(pybind11) -NAMESPACE_BEGIN(detail) +PYBIND11_NAMESPACE_BEGIN(pybind11) +PYBIND11_NAMESPACE_BEGIN(detail) template <> struct type_caster<MoveOnlyInt> { PYBIND11_TYPE_CASTER(MoveOnlyInt, _("MoveOnlyInt")); bool load(handle src, bool) { value = MoveOnlyInt(src.cast<int>()); return true; } @@ -97,8 +97,8 @@ public: operator CopyOnlyInt&() { return value; } template <typename T> using cast_op_type = pybind11::detail::cast_op_type<T>; }; -NAMESPACE_END(detail) -NAMESPACE_END(pybind11) +PYBIND11_NAMESPACE_END(detail) +PYBIND11_NAMESPACE_END(pybind11) TEST_SUBMODULE(copy_move_policies, m) { // test_lacking_copy_ctor @@ -116,9 +116,9 @@ TEST_SUBMODULE(copy_move_policies, m) { r += py::cast<MoveOrCopyInt>(o).value; /* moves */ r += py::cast<MoveOnlyInt>(o).value; /* moves */ r += py::cast<CopyOnlyInt>(o).value; /* copies */ - MoveOrCopyInt m1(py::cast<MoveOrCopyInt>(o)); /* moves */ - MoveOnlyInt m2(py::cast<MoveOnlyInt>(o)); /* moves */ - CopyOnlyInt m3(py::cast<CopyOnlyInt>(o)); /* copies */ + auto m1(py::cast<MoveOrCopyInt>(o)); /* moves */ + auto m2(py::cast<MoveOnlyInt>(o)); /* moves */ + auto m3(py::cast<CopyOnlyInt>(o)); /* copies */ r += m1.value + m2.value + m3.value; return r; @@ -175,14 +175,20 @@ TEST_SUBMODULE(copy_move_policies, m) { m.attr("has_optional") = false; #endif - // #70 compilation issue if operator new is not public + // #70 compilation issue if operator new is not public - simple body added + // but not needed on most compilers; MSVC and nvcc don't like a local + // struct not having a method defined when declared, since it can not be + // added later. struct PrivateOpNew { int value = 1; private: -#if defined(_MSC_VER) -# pragma warning(disable: 4822) // warning C4822: local class member function does not have a body -#endif - void *operator new(size_t bytes); + void *operator new(size_t bytes) { + void *ptr = std::malloc(bytes); + if (ptr) + return ptr; + else + throw std::bad_alloc{}; + } }; py::class_<PrivateOpNew>(m, "PrivateOpNew").def_readonly("value", &PrivateOpNew::value); m.def("private_op_new_value", []() { return PrivateOpNew(); }); diff --git a/3rdparty/pybind11/tests/test_copy_move.py b/3rdparty/pybind11/tests/test_copy_move.py index 0e671d96..7e3cc168 100644 --- a/3rdparty/pybind11/tests/test_copy_move.py +++ b/3rdparty/pybind11/tests/test_copy_move.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- import pytest from pybind11_tests import copy_move_policies as m @@ -18,7 +19,11 @@ def test_move_and_copy_casts(): """Cast some values in C++ via custom type casters and count the number of moves/copies.""" cstats = m.move_and_copy_cstats() - c_m, c_mc, c_c = cstats["MoveOnlyInt"], cstats["MoveOrCopyInt"], cstats["CopyOnlyInt"] + c_m, c_mc, c_c = ( + cstats["MoveOnlyInt"], + cstats["MoveOrCopyInt"], + cstats["CopyOnlyInt"], + ) # The type move constructions/assignments below each get incremented: the move assignment comes # from the type_caster load; the move construction happens when extracting that via a cast or @@ -42,7 +47,11 @@ def test_move_and_copy_loads(): moves/copies.""" cstats = m.move_and_copy_cstats() - c_m, c_mc, c_c = cstats["MoveOnlyInt"], cstats["MoveOrCopyInt"], cstats["CopyOnlyInt"] + c_m, c_mc, c_c = ( + cstats["MoveOnlyInt"], + cstats["MoveOrCopyInt"], + cstats["CopyOnlyInt"], + ) assert m.move_only(10) == 10 # 1 move, c_m assert m.move_or_copy(11) == 11 # 1 move, c_mc @@ -65,12 +74,16 @@ def test_move_and_copy_loads(): assert c_m.alive() + c_mc.alive() + c_c.alive() == 0 -@pytest.mark.skipif(not m.has_optional, reason='no <optional>') +@pytest.mark.skipif(not m.has_optional, reason="no <optional>") def test_move_and_copy_load_optional(): """Tests move/copy loads of std::optional arguments""" cstats = m.move_and_copy_cstats() - c_m, c_mc, c_c = cstats["MoveOnlyInt"], cstats["MoveOrCopyInt"], cstats["CopyOnlyInt"] + c_m, c_mc, c_c = ( + cstats["MoveOnlyInt"], + cstats["MoveOrCopyInt"], + cstats["CopyOnlyInt"], + ) # The extra move/copy constructions below come from the std::optional move (which has to move # its arguments): diff --git a/3rdparty/pybind11/tests/test_custom_type_casters.cpp b/3rdparty/pybind11/tests/test_custom_type_casters.cpp new file mode 100644 index 00000000..d565add2 --- /dev/null +++ b/3rdparty/pybind11/tests/test_custom_type_casters.cpp @@ -0,0 +1,127 @@ +/* + tests/test_custom_type_casters.cpp -- tests type_caster<T> + + Copyright (c) 2016 Wenzel Jakob <wenzel.jakob@epfl.ch> + + 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" +#include "constructor_stats.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<ArgInspector1> { +public: + PYBIND11_TYPE_CASTER(ArgInspector1, _("ArgInspector1")); + + bool load(handle src, bool convert) { + value.arg = "loading ArgInspector1 argument " + + std::string(convert ? "WITH" : "WITHOUT") + " conversion allowed. " + "Argument value = " + (std::string) str(src); + return true; + } + + static handle cast(const ArgInspector1 &src, return_value_policy, handle) { + return str(src.arg).release(); + } +}; +template <> struct type_caster<ArgInspector2> { +public: + PYBIND11_TYPE_CASTER(ArgInspector2, _("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); + return true; + } + + static handle cast(const ArgInspector2 &src, return_value_policy, handle) { + return str(src.arg).release(); + } +}; +template <> struct type_caster<ArgAlwaysConverts> { +public: + PYBIND11_TYPE_CASTER(ArgAlwaysConverts, _("ArgAlwaysConverts")); + + bool load(handle, bool convert) { + return convert; + } + + static handle cast(const ArgAlwaysConverts &, return_value_policy, handle) { + return py::none().release(); + } +}; +} // namespace detail +} // namespace pybind11 + +// test_custom_caster_destruction +class DestructionTester { +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; } +}; +namespace pybind11 { namespace detail { +template <> struct type_caster<DestructionTester> { + PYBIND11_TYPE_CASTER(DestructionTester, _("DestructionTester")); + bool load(handle, bool) { return true; } + + static handle cast(const DestructionTester &, return_value_policy, handle) { + return py::bool_(true).release(); + } +}; +} // namespace detail +} // namespace pybind11 + +TEST_SUBMODULE(custom_type_casters, m) { + // test_custom_type_casters + + // test_noconvert_args + // + // Test converting. The ArgAlwaysConverts is just there to make the first no-conversion pass + // fail so that our call always ends up happening via the second dispatch (the one that allows + // some conversion). + class ArgInspector { + public: + ArgInspector1 f(ArgInspector1 a, ArgAlwaysConverts) { return a; } + std::string g(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; } + }; + py::class_<ArgInspector>(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()); + + // 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<DestructionTester>, py::return_value_policy::reference); +} diff --git a/3rdparty/pybind11/tests/test_custom_type_casters.py b/3rdparty/pybind11/tests/test_custom_type_casters.py new file mode 100644 index 00000000..bb74d54e --- /dev/null +++ b/3rdparty/pybind11/tests/test_custom_type_casters.py @@ -0,0 +1,116 @@ +# -*- coding: utf-8 -*- +import pytest +from pybind11_tests import custom_type_casters as m + + +def test_noconvert_args(msg): + a = m.ArgInspector() + assert ( + msg(a.f("hi")) + == """ + loading ArgInspector1 argument WITH conversion allowed. Argument value = hi + """ + ) + assert ( + msg(a.g("this is a", "this is b")) + == """ + loading ArgInspector1 argument WITHOUT conversion allowed. Argument value = this is a + 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)) + == """ + loading ArgInspector1 argument WITHOUT conversion allowed. Argument value = this is a + 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")) + == """ + loading ArgInspector1 argument WITHOUT conversion allowed. Argument value = this is a + loading ArgInspector1 argument WITH conversion allowed. Argument value = this is b + 42 + loading ArgInspector2 argument WITH conversion allowed. Argument value = this is d + """ + ) + assert ( + a.h("arg 1") + == "loading ArgInspector2 argument WITHOUT conversion allowed. Argument value = arg 1" + ) + assert ( + msg(m.arg_inspect_func("A1", "A2")) + == """ + loading ArgInspector2 argument WITH conversion allowed. Argument value = A1 + loading ArgInspector1 argument WITHOUT conversion allowed. Argument value = A2 + """ + ) + + assert m.floats_preferred(4) == 2.0 + assert m.floats_only(4.0) == 2.0 + with pytest.raises(TypeError) as excinfo: + m.floats_only(4) + assert ( + msg(excinfo.value) + == """ + floats_only(): incompatible function arguments. The following argument types are supported: + 1. (f: float) -> float + + Invoked with: 4 + """ + ) + + assert m.ints_preferred(4) == 2 + assert m.ints_preferred(True) == 0 + with pytest.raises(TypeError) as excinfo: + m.ints_preferred(4.0) + assert ( + msg(excinfo.value) + == """ + ints_preferred(): incompatible function arguments. The following argument types are supported: + 1. (i: int) -> int + + Invoked with: 4.0 + """ # noqa: E501 line too long + ) + + assert m.ints_only(4) == 2 + with pytest.raises(TypeError) as excinfo: + m.ints_only(4.0) + assert ( + msg(excinfo.value) + == """ + ints_only(): incompatible function arguments. The following argument types are supported: + 1. (i: int) -> int + + Invoked with: 4.0 + """ + ) + + +def test_custom_caster_destruction(): + """Tests that returning a pointer to a type that gets converted with a custom type caster gets + destroyed when the function has py::return_value_policy::take_ownership policy applied.""" + + cstats = m.destruction_tester_cstats() + # This one *doesn't* have take_ownership: the pointer should be used but not destroyed: + z = m.custom_caster_no_destroy() + assert cstats.alive() == 1 and cstats.default_constructions == 1 + assert z + + # take_ownership applied: this constructs a new object, casts it, then destroys it: + z = m.custom_caster_destroy() + assert z + assert cstats.default_constructions == 2 + + # Same, but with a const pointer return (which should *not* inhibit destruction): + z = m.custom_caster_destroy_const() + assert z + assert cstats.default_constructions == 3 + + # Make sure we still only have the original object (from ..._no_destroy()) alive: + assert cstats.alive() == 1 diff --git a/3rdparty/pybind11/tests/test_docstring_options.py b/3rdparty/pybind11/tests/test_docstring_options.py index 0dbca609..87d80d2d 100644 --- a/3rdparty/pybind11/tests/test_docstring_options.py +++ b/3rdparty/pybind11/tests/test_docstring_options.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- from pybind11_tests import docstring_options as m @@ -17,10 +18,10 @@ def test_docstring_options(): assert m.test_overloaded3.__doc__ == "Overload docstr" # options.enable_function_signatures() - assert m.test_function3.__doc__ .startswith("test_function3(a: int, b: int) -> None") + assert m.test_function3.__doc__.startswith("test_function3(a: int, b: int) -> None") - assert m.test_function4.__doc__ .startswith("test_function4(a: int, b: int) -> None") - assert m.test_function4.__doc__ .endswith("A custom docstring\n") + assert m.test_function4.__doc__.startswith("test_function4(a: int, b: int) -> None") + assert m.test_function4.__doc__.endswith("A custom docstring\n") # options.disable_function_signatures() # options.disable_user_defined_docstrings() @@ -30,8 +31,8 @@ def test_docstring_options(): assert m.test_function6.__doc__ == "A custom docstring" # RAII destructor - assert m.test_function7.__doc__ .startswith("test_function7(a: int, b: int) -> None") - assert m.test_function7.__doc__ .endswith("A custom docstring\n") + assert m.test_function7.__doc__.startswith("test_function7(a: int, b: int) -> None") + assert m.test_function7.__doc__.endswith("A custom docstring\n") # Suppression of user-defined docstrings for non-function objects assert not m.DocstringTestFoo.__doc__ diff --git a/3rdparty/pybind11/tests/test_eigen.cpp b/3rdparty/pybind11/tests/test_eigen.cpp index aba088d7..2cc2243d 100644 --- a/3rdparty/pybind11/tests/test_eigen.cpp +++ b/3rdparty/pybind11/tests/test_eigen.cpp @@ -61,8 +61,9 @@ double get_elem(Eigen::Ref<const Eigen::MatrixXd> m) { return m(2, 1); }; // reference is referencing rows/columns correctly). template <typename MatrixArgType> 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; + 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; } @@ -87,8 +88,6 @@ TEST_SUBMODULE(eigen, m) { using SparseMatrixR = Eigen::SparseMatrix<float, Eigen::RowMajor>; using SparseMatrixC = Eigen::SparseMatrix<float>; - m.attr("have_eigen") = true; - // 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; }); @@ -257,7 +256,7 @@ TEST_SUBMODULE(eigen, m) { 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<Eigen::MatrixXf>(mat); }); + m.def("sparse_r", [mat]() -> SparseMatrixR { return Eigen::SparseView<Eigen::MatrixXf>(mat); }); //NOLINT(clang-analyzer-core.uninitialized.UndefReturn) m.def("sparse_c", [mat]() -> SparseMatrixC { return Eigen::SparseView<Eigen::MatrixXf>(mat); }); m.def("sparse_copy_r", [](const SparseMatrixR &m) -> SparseMatrixR { return m; }); m.def("sparse_copy_c", [](const SparseMatrixC &m) -> SparseMatrixC { return m; }); @@ -319,11 +318,11 @@ TEST_SUBMODULE(eigen, m) { // 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<const Eigen::VectorXd> v) { - py::module::import("numpy").attr("ones")(10); + py::module_::import("numpy").attr("ones")(10); return v(5); }); m.def("get_elem_indirect", [](std::vector<Eigen::Ref<const Eigen::VectorXd>> v) { - py::module::import("numpy").attr("ones")(10); + py::module_::import("numpy").attr("ones")(10); return v[0](5); }); } diff --git a/3rdparty/pybind11/tests/test_eigen.py b/3rdparty/pybind11/tests/test_eigen.py index 55d93517..a131dc15 100644 --- a/3rdparty/pybind11/tests/test_eigen.py +++ b/3rdparty/pybind11/tests/test_eigen.py @@ -1,17 +1,20 @@ +# -*- coding: utf-8 -*- import pytest from pybind11_tests import ConstructorStats -pytestmark = pytest.requires_eigen_and_numpy +np = pytest.importorskip("numpy") +m = pytest.importorskip("pybind11_tests.eigen") -with pytest.suppress(ImportError): - from pybind11_tests import eigen as m - import numpy as np - ref = np.array([[ 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]]) +ref = np.array( + [ + [0.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], + ] +) def assert_equal_ref(mat): @@ -41,28 +44,37 @@ def test_dense(): def test_partially_fixed(): - ref2 = np.array([[0., 1, 2, 3], [4, 5, 6, 7], [8, 9, 10, 11], [12, 13, 14, 15]]) + ref2 = np.array([[0.0, 1, 2, 3], [4, 5, 6, 7], [8, 9, 10, 11], [12, 13, 14, 15]]) np.testing.assert_array_equal(m.partial_copy_four_rm_r(ref2), ref2) np.testing.assert_array_equal(m.partial_copy_four_rm_c(ref2), ref2) np.testing.assert_array_equal(m.partial_copy_four_rm_r(ref2[:, 1]), ref2[:, [1]]) np.testing.assert_array_equal(m.partial_copy_four_rm_c(ref2[0, :]), ref2[[0], :]) - np.testing.assert_array_equal(m.partial_copy_four_rm_r(ref2[:, (0, 2)]), ref2[:, (0, 2)]) np.testing.assert_array_equal( - m.partial_copy_four_rm_c(ref2[(3, 1, 2), :]), ref2[(3, 1, 2), :]) + m.partial_copy_four_rm_r(ref2[:, (0, 2)]), ref2[:, (0, 2)] + ) + np.testing.assert_array_equal( + m.partial_copy_four_rm_c(ref2[(3, 1, 2), :]), ref2[(3, 1, 2), :] + ) np.testing.assert_array_equal(m.partial_copy_four_cm_r(ref2), ref2) np.testing.assert_array_equal(m.partial_copy_four_cm_c(ref2), ref2) np.testing.assert_array_equal(m.partial_copy_four_cm_r(ref2[:, 1]), ref2[:, [1]]) np.testing.assert_array_equal(m.partial_copy_four_cm_c(ref2[0, :]), ref2[[0], :]) - np.testing.assert_array_equal(m.partial_copy_four_cm_r(ref2[:, (0, 2)]), ref2[:, (0, 2)]) np.testing.assert_array_equal( - m.partial_copy_four_cm_c(ref2[(3, 1, 2), :]), ref2[(3, 1, 2), :]) + m.partial_copy_four_cm_r(ref2[:, (0, 2)]), ref2[:, (0, 2)] + ) + np.testing.assert_array_equal( + m.partial_copy_four_cm_c(ref2[(3, 1, 2), :]), ref2[(3, 1, 2), :] + ) # TypeError should be raise for a shape mismatch - functions = [m.partial_copy_four_rm_r, m.partial_copy_four_rm_c, - m.partial_copy_four_cm_r, m.partial_copy_four_cm_c] - matrix_with_wrong_shape = [[1, 2], - [3, 4]] + functions = [ + m.partial_copy_four_rm_r, + m.partial_copy_four_rm_c, + m.partial_copy_four_cm_r, + m.partial_copy_four_cm_c, + ] + matrix_with_wrong_shape = [[1, 2], [3, 4]] for f in functions: with pytest.raises(TypeError) as excinfo: f(matrix_with_wrong_shape) @@ -70,7 +82,7 @@ def test_partially_fixed(): def test_mutator_descriptors(): - zr = np.arange(30, dtype='float32').reshape(5, 6) # row-major + zr = np.arange(30, dtype="float32").reshape(5, 6) # row-major zc = zr.reshape(6, 5).transpose() # column-major m.fixed_mutator_r(zr) @@ -79,16 +91,21 @@ def test_mutator_descriptors(): m.fixed_mutator_a(zc) with pytest.raises(TypeError) as excinfo: m.fixed_mutator_r(zc) - assert ('(arg0: numpy.ndarray[float32[5, 6], flags.writeable, flags.c_contiguous]) -> None' - in str(excinfo.value)) + assert ( + "(arg0: numpy.ndarray[numpy.float32[5, 6]," + " flags.writeable, flags.c_contiguous]) -> None" in str(excinfo.value) + ) with pytest.raises(TypeError) as excinfo: m.fixed_mutator_c(zr) - assert ('(arg0: numpy.ndarray[float32[5, 6], flags.writeable, flags.f_contiguous]) -> None' - in str(excinfo.value)) + assert ( + "(arg0: numpy.ndarray[numpy.float32[5, 6]," + " flags.writeable, flags.f_contiguous]) -> None" in str(excinfo.value) + ) with pytest.raises(TypeError) as excinfo: - m.fixed_mutator_a(np.array([[1, 2], [3, 4]], dtype='float32')) - assert ('(arg0: numpy.ndarray[float32[5, 6], flags.writeable]) -> None' - in str(excinfo.value)) + m.fixed_mutator_a(np.array([[1, 2], [3, 4]], dtype="float32")) + assert "(arg0: numpy.ndarray[numpy.float32[5, 6], flags.writeable]) -> None" in str( + excinfo.value + ) zr.flags.writeable = False with pytest.raises(TypeError): m.fixed_mutator_r(zr) @@ -97,26 +114,26 @@ def test_mutator_descriptors(): def test_cpp_casting(): - assert m.cpp_copy(m.fixed_r()) == 22. - assert m.cpp_copy(m.fixed_c()) == 22. - z = np.array([[5., 6], [7, 8]]) - assert m.cpp_copy(z) == 7. - assert m.cpp_copy(m.get_cm_ref()) == 21. - assert m.cpp_copy(m.get_rm_ref()) == 21. - assert m.cpp_ref_c(m.get_cm_ref()) == 21. - assert m.cpp_ref_r(m.get_rm_ref()) == 21. + assert m.cpp_copy(m.fixed_r()) == 22.0 + assert m.cpp_copy(m.fixed_c()) == 22.0 + z = np.array([[5.0, 6], [7, 8]]) + assert m.cpp_copy(z) == 7.0 + assert m.cpp_copy(m.get_cm_ref()) == 21.0 + assert m.cpp_copy(m.get_rm_ref()) == 21.0 + assert m.cpp_ref_c(m.get_cm_ref()) == 21.0 + assert m.cpp_ref_r(m.get_rm_ref()) == 21.0 with pytest.raises(RuntimeError) as excinfo: # Can't reference m.fixed_c: it contains floats, m.cpp_ref_any wants doubles m.cpp_ref_any(m.fixed_c()) - assert 'Unable to cast Python instance' in str(excinfo.value) + assert "Unable to cast Python instance" in str(excinfo.value) with pytest.raises(RuntimeError) as excinfo: # Can't reference m.fixed_r: it contains floats, m.cpp_ref_any wants doubles m.cpp_ref_any(m.fixed_r()) - assert 'Unable to cast Python instance' in str(excinfo.value) - assert m.cpp_ref_any(m.ReturnTester.create()) == 1. + assert "Unable to cast Python instance" in str(excinfo.value) + assert m.cpp_ref_any(m.ReturnTester.create()) == 1.0 - assert m.cpp_ref_any(m.get_cm_ref()) == 21. - assert m.cpp_ref_any(m.get_cm_ref()) == 21. + assert m.cpp_ref_any(m.get_cm_ref()) == 21.0 + assert m.cpp_ref_any(m.get_cm_ref()) == 21.0 def test_pass_readonly_array(): @@ -141,14 +158,14 @@ def test_nonunit_stride_from_python(): counting_3d = np.arange(27.0, dtype=np.float32).reshape((3, 3, 3)) slices = [counting_3d[0, :, :], counting_3d[:, 0, :], counting_3d[:, :, 0]] - for slice_idx, ref_mat in enumerate(slices): + for ref_mat in slices: np.testing.assert_array_equal(m.double_mat_cm(ref_mat), 2.0 * ref_mat) np.testing.assert_array_equal(m.double_mat_rm(ref_mat), 2.0 * ref_mat) # Mutator: m.double_threer(second_row) m.double_threec(second_col) - np.testing.assert_array_equal(counting_mat, [[0., 2, 2], [6, 16, 10], [6, 14, 8]]) + np.testing.assert_array_equal(counting_mat, [[0.0, 2, 2], [6, 16, 10], [6, 14, 8]]) def test_negative_stride_from_python(msg): @@ -170,33 +187,43 @@ def test_negative_stride_from_python(msg): counting_3d = np.arange(27.0, dtype=np.float32).reshape((3, 3, 3)) counting_3d = counting_3d[::-1, ::-1, ::-1] slices = [counting_3d[0, :, :], counting_3d[:, 0, :], counting_3d[:, :, 0]] - for slice_idx, ref_mat in enumerate(slices): + for ref_mat in slices: np.testing.assert_array_equal(m.double_mat_cm(ref_mat), 2.0 * ref_mat) np.testing.assert_array_equal(m.double_mat_rm(ref_mat), 2.0 * ref_mat) # Mutator: with pytest.raises(TypeError) as excinfo: m.double_threer(second_row) - assert msg(excinfo.value) == """ + assert ( + msg(excinfo.value) + == """ double_threer(): incompatible function arguments. The following argument types are supported: - 1. (arg0: numpy.ndarray[float32[1, 3], flags.writeable]) -> None + 1. (arg0: numpy.ndarray[numpy.float32[1, 3], flags.writeable]) -> None - Invoked with: """ + repr(np.array([ 5., 4., 3.], dtype='float32')) # noqa: E501 line too long + Invoked with: """ # noqa: E501 line too long + + repr(np.array([5.0, 4.0, 3.0], dtype="float32")) + ) with pytest.raises(TypeError) as excinfo: m.double_threec(second_col) - assert msg(excinfo.value) == """ + assert ( + msg(excinfo.value) + == """ double_threec(): incompatible function arguments. The following argument types are supported: - 1. (arg0: numpy.ndarray[float32[3, 1], flags.writeable]) -> None + 1. (arg0: numpy.ndarray[numpy.float32[3, 1], flags.writeable]) -> None - Invoked with: """ + repr(np.array([ 7., 4., 1.], dtype='float32')) # noqa: E501 line too long + Invoked with: """ # noqa: E501 line too long + + repr(np.array([7.0, 4.0, 1.0], dtype="float32")) + ) 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) + ), "m.diagonal_n({})".format(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:]) @@ -206,8 +233,10 @@ def test_nonunit_stride_to_python(): def test_eigen_ref_to_python(): chols = [m.cholesky1, m.cholesky2, m.cholesky3, m.cholesky4] for i, chol in enumerate(chols, start=1): - mymat = chol(np.array([[1., 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) + 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) def assign_both(a1, a2, r, c, v): @@ -324,8 +353,12 @@ def test_eigen_return_references(): 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_corn1, master[0::master.shape[0] - 1, 0::master.shape[1] - 1]) - np.testing.assert_array_equal(a_corn2, master[0::master.shape[0] - 1, 0::master.shape[1] - 1]) + np.testing.assert_array_equal( + a_corn1, master[0 :: master.shape[0] - 1, 0 :: master.shape[1] - 1] + ) + np.testing.assert_array_equal( + a_corn2, master[0 :: master.shape[0] - 1, 0 :: master.shape[1] - 1] + ) np.testing.assert_array_equal(a_copy1, c1want) np.testing.assert_array_equal(a_copy2, c2want) @@ -354,16 +387,28 @@ def test_eigen_keepalive(): cstats = ConstructorStats.get(m.ReturnTester) assert cstats.alive() == 1 unsafe = [a.ref(), a.ref_const(), a.block(1, 2, 3, 4)] - copies = [a.copy_get(), a.copy_view(), a.copy_ref(), a.copy_ref_const(), - a.copy_block(4, 3, 2, 1)] + copies = [ + a.copy_get(), + a.copy_view(), + a.copy_ref(), + a.copy_ref_const(), + a.copy_block(4, 3, 2, 1), + ] del a assert cstats.alive() == 0 del unsafe del copies - for meth in [m.ReturnTester.get, m.ReturnTester.get_ptr, m.ReturnTester.view, - m.ReturnTester.view_ptr, m.ReturnTester.ref_safe, m.ReturnTester.ref_const_safe, - m.ReturnTester.corners, m.ReturnTester.corners_const]: + for meth in [ + m.ReturnTester.get, + m.ReturnTester.get_ptr, + m.ReturnTester.view, + m.ReturnTester.view_ptr, + m.ReturnTester.ref_safe, + m.ReturnTester.ref_const_safe, + m.ReturnTester.corners, + m.ReturnTester.corners_const, + ]: assert_keeps_alive(m.ReturnTester, meth) for meth in [m.ReturnTester.block_safe, m.ReturnTester.block_const]: @@ -373,18 +418,18 @@ def test_eigen_keepalive(): def test_eigen_ref_mutators(): """Tests Eigen's ability to mutate numpy values""" - orig = np.array([[1., 2, 3], [4, 5, 6], [7, 8, 9]]) + orig = np.array([[1.0, 2, 3], [4, 5, 6], [7, 8, 9]]) zr = np.array(orig) - zc = np.array(orig, order='F') + zc = np.array(orig, order="F") m.add_rm(zr, 1, 0, 100) - assert np.all(zr == np.array([[1., 2, 3], [104, 5, 6], [7, 8, 9]])) + assert np.all(zr == np.array([[1.0, 2, 3], [104, 5, 6], [7, 8, 9]])) m.add_cm(zc, 1, 0, 200) - assert np.all(zc == np.array([[1., 2, 3], [204, 5, 6], [7, 8, 9]])) + assert np.all(zc == np.array([[1.0, 2, 3], [204, 5, 6], [7, 8, 9]])) m.add_any(zr, 1, 0, 20) - assert np.all(zr == np.array([[1., 2, 3], [124, 5, 6], [7, 8, 9]])) + assert np.all(zr == np.array([[1.0, 2, 3], [124, 5, 6], [7, 8, 9]])) m.add_any(zc, 1, 0, 10) - assert np.all(zc == np.array([[1., 2, 3], [214, 5, 6], [7, 8, 9]])) + assert np.all(zc == np.array([[1.0, 2, 3], [214, 5, 6], [7, 8, 9]])) # Can't reference a col-major array with a row-major Ref, and vice versa: with pytest.raises(TypeError): @@ -405,8 +450,8 @@ def test_eigen_ref_mutators(): cornersr = zr[0::2, 0::2] cornersc = zc[0::2, 0::2] - assert np.all(cornersr == np.array([[1., 3], [7, 9]])) - assert np.all(cornersc == np.array([[1., 3], [7, 9]])) + assert np.all(cornersr == np.array([[1.0, 3], [7, 9]])) + assert np.all(cornersc == np.array([[1.0, 3], [7, 9]])) with pytest.raises(TypeError): m.add_rm(cornersr, 0, 1, 25) @@ -418,8 +463,8 @@ def test_eigen_ref_mutators(): m.add_cm(cornersc, 0, 1, 25) m.add_any(cornersr, 0, 1, 25) m.add_any(cornersc, 0, 1, 44) - assert np.all(zr == np.array([[1., 2, 28], [4, 5, 6], [7, 8, 9]])) - assert np.all(zc == np.array([[1., 2, 47], [4, 5, 6], [7, 8, 9]])) + assert np.all(zr == np.array([[1.0, 2, 28], [4, 5, 6], [7, 8, 9]])) + assert np.all(zc == np.array([[1.0, 2, 47], [4, 5, 6], [7, 8, 9]])) # You shouldn't be allowed to pass a non-writeable array to a mutating Eigen method: zro = zr[0:4, 0:4] @@ -457,7 +502,7 @@ def test_numpy_ref_mutators(): assert not zrro.flags.owndata and not zrro.flags.writeable zc[1, 2] = 99 - expect = np.array([[11., 12, 13], [21, 22, 99], [31, 32, 33]]) + expect = np.array([[11.0, 12, 13], [21, 22, 99], [31, 32, 33]]) # We should have just changed zc, of course, but also zcro and the original eigen matrix assert np.all(zc == expect) assert np.all(zcro == expect) @@ -505,18 +550,20 @@ def test_both_ref_mutators(): assert np.all(z == z3) assert np.all(z == z4) assert np.all(z == z5) - expect = np.array([[0., 22, 20], [31, 37, 33], [41, 42, 38]]) + expect = np.array([[0.0, 22, 20], [31, 37, 33], [41, 42, 38]]) assert np.all(z == expect) - y = np.array(range(100), dtype='float64').reshape(10, 10) + y = np.array(range(100), dtype="float64").reshape(10, 10) y2 = m.incr_matrix_any(y, 10) # np -> eigen -> np - y3 = m.incr_matrix_any(y2[0::2, 0::2], -33) # np -> eigen -> np slice -> np -> eigen -> np + y3 = m.incr_matrix_any( + y2[0::2, 0::2], -33 + ) # np -> eigen -> np slice -> np -> eigen -> np y4 = m.even_rows(y3) # numpy -> eigen slice -> (... y3) y5 = m.even_cols(y4) # numpy -> eigen slice -> (... y4) y6 = m.incr_matrix_any(y5, 1000) # numpy -> eigen -> (... y5) # Apply same mutations using just numpy: - yexpect = np.array(range(100), dtype='float64').reshape(10, 10) + yexpect = np.array(range(100), dtype="float64").reshape(10, 10) yexpect += 10 yexpect[0::2, 0::2] -= 33 yexpect[0::4, 0::4] += 1000 @@ -531,10 +578,14 @@ def test_both_ref_mutators(): def test_nocopy_wrapper(): # get_elem requires a column-contiguous matrix reference, but should be # callable with other types of matrix (via copying): - int_matrix_colmajor = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]], order='F') - dbl_matrix_colmajor = np.array(int_matrix_colmajor, dtype='double', order='F', copy=True) - int_matrix_rowmajor = np.array(int_matrix_colmajor, order='C', copy=True) - dbl_matrix_rowmajor = np.array(int_matrix_rowmajor, dtype='double', order='C', copy=True) + int_matrix_colmajor = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]], order="F") + dbl_matrix_colmajor = np.array( + int_matrix_colmajor, dtype="double", order="F", copy=True + ) + int_matrix_rowmajor = np.array(int_matrix_colmajor, order="C", copy=True) + dbl_matrix_rowmajor = np.array( + int_matrix_rowmajor, dtype="double", order="C", copy=True + ) # All should be callable via get_elem: assert m.get_elem(int_matrix_colmajor) == 8 @@ -545,32 +596,38 @@ def test_nocopy_wrapper(): # All but the second should fail with m.get_elem_nocopy: with pytest.raises(TypeError) as excinfo: m.get_elem_nocopy(int_matrix_colmajor) - assert ('get_elem_nocopy(): incompatible function arguments.' in str(excinfo.value) and - ', flags.f_contiguous' in str(excinfo.value)) + assert "get_elem_nocopy(): incompatible function arguments." in str( + excinfo.value + ) and ", flags.f_contiguous" in str(excinfo.value) assert m.get_elem_nocopy(dbl_matrix_colmajor) == 8 with pytest.raises(TypeError) as excinfo: m.get_elem_nocopy(int_matrix_rowmajor) - assert ('get_elem_nocopy(): incompatible function arguments.' in str(excinfo.value) and - ', flags.f_contiguous' in str(excinfo.value)) + assert "get_elem_nocopy(): incompatible function arguments." in str( + excinfo.value + ) and ", flags.f_contiguous" in str(excinfo.value) with pytest.raises(TypeError) as excinfo: m.get_elem_nocopy(dbl_matrix_rowmajor) - assert ('get_elem_nocopy(): incompatible function arguments.' in str(excinfo.value) and - ', flags.f_contiguous' in str(excinfo.value)) + assert "get_elem_nocopy(): incompatible function arguments." in str( + excinfo.value + ) and ", flags.f_contiguous" in str(excinfo.value) # For the row-major test, we take a long matrix in row-major, so only the third is allowed: with pytest.raises(TypeError) as excinfo: m.get_elem_rm_nocopy(int_matrix_colmajor) - assert ('get_elem_rm_nocopy(): incompatible function arguments.' in str(excinfo.value) and - ', flags.c_contiguous' in str(excinfo.value)) + assert "get_elem_rm_nocopy(): incompatible function arguments." in str( + excinfo.value + ) and ", flags.c_contiguous" in str(excinfo.value) with pytest.raises(TypeError) as excinfo: m.get_elem_rm_nocopy(dbl_matrix_colmajor) - assert ('get_elem_rm_nocopy(): incompatible function arguments.' in str(excinfo.value) and - ', flags.c_contiguous' in str(excinfo.value)) + assert "get_elem_rm_nocopy(): incompatible function arguments." in str( + excinfo.value + ) and ", flags.c_contiguous" in str(excinfo.value) assert m.get_elem_rm_nocopy(int_matrix_rowmajor) == 8 with pytest.raises(TypeError) as excinfo: m.get_elem_rm_nocopy(dbl_matrix_rowmajor) - assert ('get_elem_rm_nocopy(): incompatible function arguments.' in str(excinfo.value) and - ', flags.c_contiguous' in str(excinfo.value)) + assert "get_elem_rm_nocopy(): incompatible function arguments." in str( + excinfo.value + ) and ", flags.c_contiguous" in str(excinfo.value) def test_eigen_ref_life_support(): @@ -588,12 +645,9 @@ def test_eigen_ref_life_support(): def test_special_matrix_objects(): - assert np.all(m.incr_diag(7) == np.diag([1., 2, 3, 4, 5, 6, 7])) + assert np.all(m.incr_diag(7) == np.diag([1.0, 2, 3, 4, 5, 6, 7])) - asymm = np.array([[ 1., 2, 3, 4], - [ 5, 6, 7, 8], - [ 9, 10, 11, 12], - [13, 14, 15, 16]]) + asymm = np.array([[1.0, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12], [13, 14, 15, 16]]) symm_lower = np.array(asymm) symm_upper = np.array(asymm) for i in range(4): @@ -606,43 +660,55 @@ def test_special_matrix_objects(): def test_dense_signature(doc): - assert doc(m.double_col) == """ - double_col(arg0: numpy.ndarray[float32[m, 1]]) -> numpy.ndarray[float32[m, 1]] + assert ( + doc(m.double_col) + == """ + double_col(arg0: numpy.ndarray[numpy.float32[m, 1]]) -> numpy.ndarray[numpy.float32[m, 1]] """ - assert doc(m.double_row) == """ - double_row(arg0: numpy.ndarray[float32[1, n]]) -> numpy.ndarray[float32[1, n]] + ) + assert ( + doc(m.double_row) + == """ + double_row(arg0: numpy.ndarray[numpy.float32[1, n]]) -> numpy.ndarray[numpy.float32[1, n]] """ - assert doc(m.double_complex) == """ - double_complex(arg0: numpy.ndarray[complex64[m, 1]]) -> numpy.ndarray[complex64[m, 1]] + ) + assert doc(m.double_complex) == ( + """ + double_complex(arg0: numpy.ndarray[numpy.complex64[m, 1]])""" + """ -> numpy.ndarray[numpy.complex64[m, 1]] """ - assert doc(m.double_mat_rm) == """ - double_mat_rm(arg0: numpy.ndarray[float32[m, n]]) -> numpy.ndarray[float32[m, n]] + ) + assert doc(m.double_mat_rm) == ( + """ + double_mat_rm(arg0: numpy.ndarray[numpy.float32[m, n]])""" + """ -> numpy.ndarray[numpy.float32[m, n]] """ + ) def test_named_arguments(): a = np.array([[1.0, 2], [3, 4], [5, 6]]) b = np.ones((2, 1)) - assert np.all(m.matrix_multiply(a, b) == np.array([[3.], [7], [11]])) - assert np.all(m.matrix_multiply(A=a, B=b) == np.array([[3.], [7], [11]])) - assert np.all(m.matrix_multiply(B=b, A=a) == np.array([[3.], [7], [11]])) + assert np.all(m.matrix_multiply(a, b) == np.array([[3.0], [7], [11]])) + assert np.all(m.matrix_multiply(A=a, B=b) == np.array([[3.0], [7], [11]])) + assert np.all(m.matrix_multiply(B=b, A=a) == np.array([[3.0], [7], [11]])) with pytest.raises(ValueError) as excinfo: m.matrix_multiply(b, a) - assert str(excinfo.value) == 'Nonconformable matrices!' + assert str(excinfo.value) == "Nonconformable matrices!" with pytest.raises(ValueError) as excinfo: m.matrix_multiply(A=b, B=a) - assert str(excinfo.value) == 'Nonconformable matrices!' + assert str(excinfo.value) == "Nonconformable matrices!" with pytest.raises(ValueError) as excinfo: m.matrix_multiply(B=a, A=b) - assert str(excinfo.value) == 'Nonconformable matrices!' + assert str(excinfo.value) == "Nonconformable matrices!" -@pytest.requires_eigen_and_scipy def test_sparse(): + pytest.importorskip("scipy") assert_sparse_equal_ref(m.sparse_r()) assert_sparse_equal_ref(m.sparse_c()) assert_sparse_equal_ref(m.sparse_copy_r(m.sparse_r())) @@ -651,23 +717,33 @@ def test_sparse(): assert_sparse_equal_ref(m.sparse_copy_c(m.sparse_r())) -@pytest.requires_eigen_and_scipy def test_sparse_signature(doc): - assert doc(m.sparse_copy_r) == """ - sparse_copy_r(arg0: scipy.sparse.csr_matrix[float32]) -> scipy.sparse.csr_matrix[float32] + pytest.importorskip("scipy") + assert ( + 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[float32]) -> scipy.sparse.csc_matrix[float32] + ) + 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 + ) def test_issue738(): """Ignore strides on a length-1 dimension (even if they would be incompatible length > 1)""" - assert np.all(m.iss738_f1(np.array([[1., 2, 3]])) == np.array([[1., 102, 203]])) - assert np.all(m.iss738_f1(np.array([[1.], [2], [3]])) == np.array([[1.], [12], [23]])) - - assert np.all(m.iss738_f2(np.array([[1., 2, 3]])) == np.array([[1., 102, 203]])) - assert np.all(m.iss738_f2(np.array([[1.], [2], [3]])) == np.array([[1.], [12], [23]])) + assert np.all(m.iss738_f1(np.array([[1.0, 2, 3]])) == np.array([[1.0, 102, 203]])) + assert np.all( + m.iss738_f1(np.array([[1.0], [2], [3]])) == np.array([[1.0], [12], [23]]) + ) + + assert np.all(m.iss738_f2(np.array([[1.0, 2, 3]])) == np.array([[1.0, 102, 203]])) + assert np.all( + m.iss738_f2(np.array([[1.0], [2], [3]])) == np.array([[1.0], [12], [23]]) + ) def test_issue1105(): diff --git a/3rdparty/pybind11/tests/test_embed/CMakeLists.txt b/3rdparty/pybind11/tests/test_embed/CMakeLists.txt index 8b4f1f84..fabcb24e 100644 --- a/3rdparty/pybind11/tests/test_embed/CMakeLists.txt +++ b/3rdparty/pybind11/tests/test_embed/CMakeLists.txt @@ -1,41 +1,43 @@ -if(${PYTHON_MODULE_EXTENSION} MATCHES "pypy") - add_custom_target(cpptest) # Dummy target on PyPy. Embedding is not supported. +if("${PYTHON_MODULE_EXTENSION}" MATCHES "pypy" OR "${Python_INTERPRETER_ID}" STREQUAL "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 1.9.3) +find_package(Catch 2.13.2) + if(CATCH_FOUND) message(STATUS "Building interpreter tests using Catch v${CATCH_VERSION}") else() message(STATUS "Catch not detected. Interpreter tests will be skipped. Install Catch headers" - " manually or use `cmake -DDOWNLOAD_CATCH=1` to fetch them automatically.") + " manually or use `cmake -DDOWNLOAD_CATCH=ON` to fetch them automatically.") return() endif() -add_executable(test_embed - catch.cpp - test_interpreter.cpp -) -target_include_directories(test_embed PRIVATE ${CATCH_INCLUDE_DIR}) +find_package(Threads REQUIRED) + +add_executable(test_embed catch.cpp test_interpreter.cpp) pybind11_enable_warnings(test_embed) -if(NOT CMAKE_VERSION VERSION_LESS 3.0) - target_link_libraries(test_embed PRIVATE pybind11::embed) -else() - target_include_directories(test_embed PRIVATE ${PYBIND11_INCLUDE_DIR} ${PYTHON_INCLUDE_DIRS}) - target_compile_options(test_embed PRIVATE ${PYBIND11_CPP_STANDARD}) - target_link_libraries(test_embed PRIVATE ${PYTHON_LIBRARIES}) -endif() +target_link_libraries(test_embed PRIVATE pybind11::embed Catch2::Catch2 Threads::Threads) -find_package(Threads REQUIRED) -target_link_libraries(test_embed PUBLIC ${CMAKE_THREAD_LIBS_INIT}) +if(NOT CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_CURRENT_BINARY_DIR) + file(COPY test_interpreter.py DESTINATION "${CMAKE_CURRENT_BINARY_DIR}") +endif() -add_custom_target(cpptest COMMAND $<TARGET_FILE:test_embed> - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) +add_custom_target( + cpptest + COMMAND "$<TARGET_FILE:test_embed>" + WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}") pybind11_add_module(external_module THIN_LTO external_module.cpp) -set_target_properties(external_module PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) +set_target_properties(external_module PROPERTIES LIBRARY_OUTPUT_DIRECTORY + "${CMAKE_CURRENT_BINARY_DIR}") +foreach(config ${CMAKE_CONFIGURATION_TYPES}) + string(TOUPPER ${config} config) + set_target_properties(external_module PROPERTIES LIBRARY_OUTPUT_DIRECTORY_${config} + "${CMAKE_CURRENT_BINARY_DIR}") +endforeach() add_dependencies(cpptest external_module) add_dependencies(check cpptest) diff --git a/3rdparty/pybind11/tests/test_embed/test_interpreter.cpp b/3rdparty/pybind11/tests/test_embed/test_interpreter.cpp index 222bd565..944334ce 100644 --- a/3rdparty/pybind11/tests/test_embed/test_interpreter.cpp +++ b/3rdparty/pybind11/tests/test_embed/test_interpreter.cpp @@ -30,7 +30,7 @@ private: class PyWidget final : public Widget { using Widget::Widget; - int the_answer() const override { PYBIND11_OVERLOAD_PURE(int, Widget, the_answer); } + int the_answer() const override { PYBIND11_OVERRIDE_PURE(int, Widget, the_answer); } }; PYBIND11_EMBEDDED_MODULE(widget_module, m) { @@ -51,17 +51,17 @@ PYBIND11_EMBEDDED_MODULE(throw_error_already_set, ) { } 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 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); REQUIRE(locals["message"].cast<std::string>() == "Hello, World! - 5"); - auto py_widget = module.attr("DerivedWidget")("The question"); + auto py_widget = module_.attr("DerivedWidget")("The question"); auto message = py_widget.attr("the_message"); REQUIRE(message.cast<std::string>() == "The question"); @@ -70,10 +70,10 @@ TEST_CASE("Pass classes and data between modules defined in C++ and Python") { } TEST_CASE("Import error handling") { - REQUIRE_NOTHROW(py::module::import("widget_module")); - REQUIRE_THROWS_WITH(py::module::import("throw_exception"), + 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_error_already_set"), + REQUIRE_THROWS_WITH(py::module_::import("throw_error_already_set"), Catch::Contains("ImportError: KeyError")); } @@ -107,14 +107,14 @@ bool has_pybind11_internals_static() { TEST_CASE("Restart the interpreter") { // Verify pre-restart state. - REQUIRE(py::module::import("widget_module").attr("add")(1, 2).cast<int>() == 3); + REQUIRE(py::module_::import("widget_module").attr("add")(1, 2).cast<int>() == 3); REQUIRE(has_pybind11_internals_builtin()); REQUIRE(has_pybind11_internals_static()); - REQUIRE(py::module::import("external_module").attr("A")(123).attr("value").cast<int>() == 123); + REQUIRE(py::module_::import("external_module").attr("A")(123).attr("value").cast<int>() == 123); // local and foreign module internals should point to the same internals: REQUIRE(reinterpret_cast<uintptr_t>(*py::detail::get_internals_pp()) == - py::module::import("external_module").attr("internals_at")().cast<uintptr_t>()); + py::module_::import("external_module").attr("internals_at")().cast<uintptr_t>()); // Restart the interpreter. py::finalize_interpreter(); @@ -130,14 +130,14 @@ TEST_CASE("Restart the interpreter") { REQUIRE(has_pybind11_internals_builtin()); REQUIRE(has_pybind11_internals_static()); REQUIRE(reinterpret_cast<uintptr_t>(*py::detail::get_internals_pp()) == - py::module::import("external_module").attr("internals_at")().cast<uintptr_t>()); + py::module_::import("external_module").attr("internals_at")().cast<uintptr_t>()); // 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::module_::import("__main__").attr("internals_destroy_test") = py::capsule(&ran, [](void *ran) { py::detail::get_internals(); *static_cast<bool *>(ran) = true; }); REQUIRE_FALSE(has_pybind11_internals_builtin()); REQUIRE_FALSE(has_pybind11_internals_static()); @@ -149,20 +149,20 @@ TEST_CASE("Restart the interpreter") { REQUIRE_FALSE(has_pybind11_internals_static()); // C++ modules can be reloaded. - auto cpp_module = py::module::import("widget_module"); + auto cpp_module = py::module_::import("widget_module"); REQUIRE(cpp_module.attr("add")(1, 2).cast<int>() == 3); // C++ type information is reloaded and can be used in python modules. - auto py_module = py::module::import("test_interpreter"); + auto py_module = py::module_::import("test_interpreter"); auto py_widget = py_module.attr("DerivedWidget")("Hello after restart"); REQUIRE(py_widget.attr("the_message").cast<std::string>() == "Hello after restart"); } TEST_CASE("Subinterpreter") { // Add tags to the modules in the main interpreter and test the basics. - py::module::import("__main__").attr("main_tag") = "main interpreter"; + py::module_::import("__main__").attr("main_tag") = "main interpreter"; { - auto m = py::module::import("widget_module"); + auto m = py::module_::import("widget_module"); m.attr("extension_module_tag") = "added to module in main interpreter"; REQUIRE(m.attr("add")(1, 2).cast<int>() == 3); @@ -181,9 +181,9 @@ TEST_CASE("Subinterpreter") { REQUIRE(has_pybind11_internals_static()); // Modules tags should be gone. - REQUIRE_FALSE(py::hasattr(py::module::import("__main__"), "tag")); + REQUIRE_FALSE(py::hasattr(py::module_::import("__main__"), "tag")); { - auto m = py::module::import("widget_module"); + auto m = py::module_::import("widget_module"); REQUIRE_FALSE(py::hasattr(m, "extension_module_tag")); // Function bindings should still work. @@ -194,8 +194,8 @@ TEST_CASE("Subinterpreter") { Py_EndInterpreter(sub_tstate); PyThreadState_Swap(main_tstate); - REQUIRE(py::hasattr(py::module::import("__main__"), "main_tag")); - REQUIRE(py::hasattr(py::module::import("widget_module"), "extension_module_tag")); + REQUIRE(py::hasattr(py::module_::import("__main__"), "main_tag")); + REQUIRE(py::hasattr(py::module_::import("widget_module"), "extension_module_tag")); } TEST_CASE("Execution frame") { @@ -245,7 +245,7 @@ TEST_CASE("Reload module from file") { // Disable generation of cached bytecode (.pyc files) for this test, otherwise // Python might pick up an old version from the cache instead of the new versions // of the .py files generated below - auto sys = py::module::import("sys"); + auto sys = py::module_::import("sys"); bool dont_write_bytecode = sys.attr("dont_write_bytecode").cast<bool>(); sys.attr("dont_write_bytecode") = true; // Reset the value at scope exit @@ -267,8 +267,8 @@ TEST_CASE("Reload module from file") { }); // Import the module from file - auto module = py::module::import(module_name.c_str()); - int result = module.attr("test")().cast<int>(); + auto module_ = py::module_::import(module_name.c_str()); + int result = module_.attr("test")().cast<int>(); REQUIRE(result == 1); // Update the module .py file with a small change @@ -278,7 +278,7 @@ TEST_CASE("Reload module from file") { test_module.close(); // Reload the module - module.reload(); - result = module.attr("test")().cast<int>(); + module_.reload(); + result = module_.attr("test")().cast<int>(); REQUIRE(result == 2); } diff --git a/3rdparty/pybind11/tests/test_embed/test_interpreter.py b/3rdparty/pybind11/tests/test_embed/test_interpreter.py index 26a04792..6174ede4 100644 --- a/3rdparty/pybind11/tests/test_embed/test_interpreter.py +++ b/3rdparty/pybind11/tests/test_embed/test_interpreter.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- from widget_module import Widget diff --git a/3rdparty/pybind11/tests/test_enum.py b/3rdparty/pybind11/tests/test_enum.py index 7fe9b618..f6b24fc2 100644 --- a/3rdparty/pybind11/tests/test_enum.py +++ b/3rdparty/pybind11/tests/test_enum.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- import pytest from pybind11_tests import enums as m @@ -6,6 +7,9 @@ def test_unscoped_enum(): assert str(m.UnscopedEnum.EOne) == "UnscopedEnum.EOne" assert str(m.UnscopedEnum.ETwo) == "UnscopedEnum.ETwo" assert str(m.EOne) == "UnscopedEnum.EOne" + assert repr(m.UnscopedEnum.EOne) == "<UnscopedEnum.EOne: 1>" + assert repr(m.UnscopedEnum.ETwo) == "<UnscopedEnum.ETwo: 2>" + assert repr(m.EOne) == "<UnscopedEnum.EOne: 1>" # name property assert m.UnscopedEnum.EOne.name == "EOne" @@ -20,18 +24,24 @@ def test_unscoped_enum(): assert m.UnscopedEnum.EOne.name == "EOne" # __members__ property - assert m.UnscopedEnum.__members__ == \ - {"EOne": m.UnscopedEnum.EOne, "ETwo": m.UnscopedEnum.ETwo, "EThree": m.UnscopedEnum.EThree} + assert m.UnscopedEnum.__members__ == { + "EOne": m.UnscopedEnum.EOne, + "ETwo": m.UnscopedEnum.ETwo, + "EThree": m.UnscopedEnum.EThree, + } # __members__ readonly with pytest.raises(AttributeError): m.UnscopedEnum.__members__ = {} # __members__ returns a copy foo = m.UnscopedEnum.__members__ foo["bar"] = "baz" - assert m.UnscopedEnum.__members__ == \ - {"EOne": m.UnscopedEnum.EOne, "ETwo": m.UnscopedEnum.ETwo, "EThree": m.UnscopedEnum.EThree} + assert m.UnscopedEnum.__members__ == { + "EOne": m.UnscopedEnum.EOne, + "ETwo": m.UnscopedEnum.ETwo, + "EThree": m.UnscopedEnum.EThree, + } - for docstring_line in '''An unscoped enumeration + for docstring_line in """An unscoped enumeration Members: @@ -39,7 +49,9 @@ Members: ETwo : Docstring for ETwo - EThree : Docstring for EThree'''.split('\n'): + EThree : Docstring for EThree""".split( + "\n" + ): assert docstring_line in m.UnscopedEnum.__doc__ # Unscoped enums will accept ==/!= int comparisons @@ -49,10 +61,10 @@ Members: assert y != 3 assert 3 != y # Compare with None - assert (y != None) # noqa: E711 + assert y != None # noqa: E711 assert not (y == None) # noqa: E711 # Compare with an object - assert (y != object()) + assert y != object() assert not (y == object()) # Compare with string assert y != "2" @@ -115,10 +127,10 @@ def test_scoped_enum(): assert z != 3 assert 3 != z # Compare with None - assert (z != None) # noqa: E711 + assert z != None # noqa: E711 assert not (z == None) # noqa: E711 # Compare with an object - assert (z != object()) + assert z != object() assert not (z == object()) # Scoped enums will *NOT* accept >, <, >= and <= int comparisons (Will throw exceptions) with pytest.raises(TypeError): @@ -142,6 +154,8 @@ def test_scoped_enum(): def test_implicit_conversion(): assert str(m.ClassWithUnscopedEnum.EMode.EFirstMode) == "EMode.EFirstMode" assert str(m.ClassWithUnscopedEnum.EFirstMode) == "EMode.EFirstMode" + assert repr(m.ClassWithUnscopedEnum.EMode.EFirstMode) == "<EMode.EFirstMode: 1>" + assert repr(m.ClassWithUnscopedEnum.EFirstMode) == "<EMode.EFirstMode: 1>" f = m.ClassWithUnscopedEnum.test_function first = m.ClassWithUnscopedEnum.EFirstMode @@ -166,7 +180,7 @@ def test_implicit_conversion(): x[f(first)] = 3 x[f(second)] = 4 # Hashing test - assert str(x) == "{EMode.EFirstMode: 3, EMode.ESecondMode: 4}" + assert repr(x) == "{<EMode.EFirstMode: 1>: 3, <EMode.ESecondMode: 2>: 4}" def test_binary_operators(): @@ -204,3 +218,10 @@ def test_duplicate_enum_name(): with pytest.raises(ValueError) as excinfo: m.register_bad_enum() assert str(excinfo.value) == 'SimpleEnum: element "ONE" already exists!' + + +def test_docstring_signatures(): + for enum_type in [m.ScopedEnum, m.UnscopedEnum]: + for attr in enum_type.__dict__.values(): + # Issue #2623/PR #2637: Add argument names to enum_ methods + assert "arg0" not in (attr.__doc__ or "") diff --git a/3rdparty/pybind11/tests/test_eval.cpp b/3rdparty/pybind11/tests/test_eval.cpp index e0948219..5416c2ec 100644 --- a/3rdparty/pybind11/tests/test_eval.cpp +++ b/3rdparty/pybind11/tests/test_eval.cpp @@ -14,7 +14,7 @@ TEST_SUBMODULE(eval_, m) { // test_evals - auto global = py::dict(py::module::import("__main__").attr("__dict__")); + auto global = py::dict(py::module_::import("__main__").attr("__dict__")); m.def("test_eval_statements", [global]() { auto local = py::dict(); @@ -88,4 +88,12 @@ TEST_SUBMODULE(eval_, m) { } return false; }); + + // test_eval_empty_globals + m.def("eval_empty_globals", [](py::object global) { + if (global.is_none()) + global = py::dict(); + auto int_class = py::eval("isinstance(42, int)", global); + return global; + }); } diff --git a/3rdparty/pybind11/tests/test_eval.py b/3rdparty/pybind11/tests/test_eval.py index bda4ef6b..1bb05af0 100644 --- a/3rdparty/pybind11/tests/test_eval.py +++ b/3rdparty/pybind11/tests/test_eval.py @@ -1,4 +1,10 @@ +# -*- coding: utf-8 -*- import os + +import pytest + +import env # noqa: F401 + from pybind11_tests import eval_ as m @@ -10,8 +16,20 @@ def test_evals(capture): assert m.test_eval() assert m.test_eval_single_statement() + assert m.test_eval_failure() + + +@pytest.mark.xfail("env.PYPY and not env.PY2", raises=RuntimeError) +def test_eval_file(): filename = os.path.join(os.path.dirname(__file__), "test_eval_call.py") assert m.test_eval_file(filename) - assert m.test_eval_failure() assert m.test_eval_file_failure() + + +def test_eval_empty_globals(): + assert "__builtins__" in m.eval_empty_globals(None) + + g = {} + assert "__builtins__" in m.eval_empty_globals(g) + assert "__builtins__" in g diff --git a/3rdparty/pybind11/tests/test_eval_call.py b/3rdparty/pybind11/tests/test_eval_call.py index 53c7e721..373b67ba 100644 --- a/3rdparty/pybind11/tests/test_eval_call.py +++ b/3rdparty/pybind11/tests/test_eval_call.py @@ -1,4 +1,5 @@ +# -*- coding: utf-8 -*- # This file is called from 'test_eval.py' -if 'call_test2' in locals(): +if "call_test2" in locals(): call_test2(y) # noqa: F821 undefined name diff --git a/3rdparty/pybind11/tests/test_exceptions.cpp b/3rdparty/pybind11/tests/test_exceptions.cpp index 56cd9bc4..e27c16df 100644 --- a/3rdparty/pybind11/tests/test_exceptions.cpp +++ b/3rdparty/pybind11/tests/test_exceptions.cpp @@ -13,7 +13,7 @@ class MyException : public std::exception { public: explicit MyException(const char * m) : message{m} {} - virtual const char * what() const noexcept override {return message.c_str();} + const char * what() const noexcept override {return message.c_str();} private: std::string message = ""; }; @@ -22,7 +22,7 @@ private: class MyException2 : public std::exception { public: explicit MyException2(const char * m) : message{m} {} - virtual const char * what() const noexcept override {return message.c_str();} + const char * what() const noexcept override {return message.c_str();} private: std::string message = ""; }; @@ -32,6 +32,13 @@ class MyException3 { public: 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; + virtual ~MyException3() = default; + // Rule of 5 END. private: std::string message = ""; }; @@ -41,7 +48,7 @@ private: class MyException4 : public std::exception { public: explicit MyException4(const char * m) : message{m} {} - virtual const char * what() const noexcept override {return message.c_str();} + const char * what() const noexcept override {return message.c_str();} private: std::string message = ""; }; @@ -65,6 +72,25 @@ struct PythonCallInDestructor { py::dict d; }; + + +struct PythonAlreadySetInDestructor { + 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) { + ex.discard_as_unraisable(s); + } + } + + py::str s; +}; + + TEST_SUBMODULE(exceptions, m) { m.def("throw_std_exception", []() { throw std::runtime_error("This exception was intentionally thrown."); @@ -144,7 +170,7 @@ TEST_SUBMODULE(exceptions, m) { m.def("modulenotfound_exception_matches_base", []() { try { // On Python >= 3.6, this raises a ModuleNotFoundError, a subclass of ImportError - py::module::import("nonexistent"); + py::module_::import("nonexistent"); } catch (py::error_already_set &ex) { if (!ex.matches(PyExc_ImportError)) throw; @@ -183,6 +209,11 @@ TEST_SUBMODULE(exceptions, m) { return false; }); + m.def("python_alreadyset_in_destructor", [](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); } @@ -194,4 +225,7 @@ TEST_SUBMODULE(exceptions, m) { } }); + // Test repr that cannot be displayed + m.def("simple_bool_passthrough", [](bool x) {return x;}); + } diff --git a/3rdparty/pybind11/tests/test_exceptions.py b/3rdparty/pybind11/tests/test_exceptions.py index ac2b3603..95eac709 100644 --- a/3rdparty/pybind11/tests/test_exceptions.py +++ b/3rdparty/pybind11/tests/test_exceptions.py @@ -1,3 +1,6 @@ +# -*- coding: utf-8 -*- +import sys + import pytest from pybind11_tests import exceptions as m @@ -47,6 +50,33 @@ def test_python_call_in_catch(): assert d["good"] is True +def test_python_alreadyset_in_destructor(monkeypatch, capsys): + hooked = False + triggered = [False] # mutable, so Python 2.7 closure can modify it + + if hasattr(sys, "unraisablehook"): # Python 3.8+ + hooked = True + 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 + default_hook(unraisable_hook_args) + return + + # Use monkeypatch so pytest can apply and remove the patch as appropriate + monkeypatch.setattr(sys, "unraisablehook", hook) + + assert m.python_alreadyset_in_destructor("already_set demo") is True + if hooked: + assert triggered[0] 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 + + def test_exception_matches(): assert m.exception_matches() assert m.exception_matches_base() @@ -77,7 +107,9 @@ def test_custom(msg): # Can we fall-through to the default handler? with pytest.raises(RuntimeError) as excinfo: m.throws_logic_error() - assert msg(excinfo.value) == "this error should fall through to the standard handler" + assert ( + msg(excinfo.value) == "this error should fall through to the standard handler" + ) # OverFlow error translation. with pytest.raises(OverflowError) as excinfo: @@ -136,7 +168,13 @@ def test_nested_throws(capture): # C++ -> Python -> C++ -> Python with capture: m.try_catch( - m.MyException5, pycatch, m.MyException, m.try_catch, m.MyException, throw_myex5) + m.MyException5, + pycatch, + m.MyException, + m.try_catch, + m.MyException, + throw_myex5, + ) assert str(capture).startswith("MyException5: nested error 5") # C++ -> Python -> C++ @@ -148,3 +186,13 @@ def test_nested_throws(capture): with pytest.raises(m.MyException5) as excinfo: m.try_catch(m.MyException, pycatch, m.MyException, m.throws5) assert str(excinfo.value) == "this is a helper-defined translated exception" + + +# This can often happen if you wrap a pybind11 class in a Python wrapper +def test_invalid_repr(): + class MyRepr(object): + def __repr__(self): + raise AttributeError("Example error") + + with pytest.raises(TypeError): + m.simple_bool_passthrough(MyRepr()) diff --git a/3rdparty/pybind11/tests/test_factory_constructors.cpp b/3rdparty/pybind11/tests/test_factory_constructors.cpp index 5cfbfdc3..f42d1f29 100644 --- a/3rdparty/pybind11/tests/test_factory_constructors.cpp +++ b/3rdparty/pybind11/tests/test_factory_constructors.cpp @@ -11,6 +11,7 @@ #include "pybind11_tests.h" #include "constructor_stats.h" #include <cmath> +#include <new> // Classes for testing python construction via C++ factory function: // Not publicly constructible, copyable, or movable: @@ -57,13 +58,13 @@ class TestFactory4 : public TestFactory3 { public: TestFactory4() : TestFactory3() { print_default_created(this); } TestFactory4(int v) : TestFactory3(v) { print_created(this, v); } - virtual ~TestFactory4() { print_destroyed(this); } + ~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); } - virtual ~TestFactory5() { print_destroyed(this); } + ~TestFactory5() override { print_destroyed(this); } }; class TestFactory6 { @@ -87,8 +88,8 @@ public: PyTF6(PyTF6 &&f) : 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); } - virtual ~PyTF6() { print_destroyed(this); } - int get() override { PYBIND11_OVERLOAD(int, TestFactory6, get, /*no args*/); } + ~PyTF6() override { print_destroyed(this); } + int get() override { PYBIND11_OVERRIDE(int, TestFactory6, get, /*no args*/); } }; class TestFactory7 { @@ -108,8 +109,8 @@ public: PyTF7(int i) : TestFactory7(i) { alias = true; print_created(this, i); } PyTF7(PyTF7 &&f) : TestFactory7(std::move(f)) { print_move_created(this); } PyTF7(const PyTF7 &f) : TestFactory7(f) { print_copy_created(this); } - virtual ~PyTF7() { print_destroyed(this); } - int get() override { PYBIND11_OVERLOAD(int, TestFactory7, get, /*no args*/); } + ~PyTF7() override { print_destroyed(this); } + int get() override { PYBIND11_OVERRIDE(int, TestFactory7, get, /*no args*/); } }; @@ -141,7 +142,7 @@ public: TEST_SUBMODULE(factory_constructors, m) { // Define various trivial types to allow simpler overload resolution: - py::module m_tag = m.def_submodule("tag"); + py::module_ m_tag = m.def_submodule("tag"); #define MAKE_TAG_TYPE(Name) \ struct Name##_tag {}; \ py::class_<Name##_tag>(m_tag, #Name "_tag").def(py::init<>()); \ @@ -154,6 +155,8 @@ TEST_SUBMODULE(factory_constructors, m) { MAKE_TAG_TYPE(TF4); MAKE_TAG_TYPE(TF5); MAKE_TAG_TYPE(null_ptr); + MAKE_TAG_TYPE(null_unique_ptr); + MAKE_TAG_TYPE(null_shared_ptr); MAKE_TAG_TYPE(base); MAKE_TAG_TYPE(invalid_base); MAKE_TAG_TYPE(alias); @@ -194,6 +197,8 @@ TEST_SUBMODULE(factory_constructors, m) { // Returns nullptr: .def(py::init([](null_ptr_tag) { return (TestFactory3 *) nullptr; })) + .def(py::init([](null_unique_ptr_tag) { return std::unique_ptr<TestFactory3>(); })) + .def(py::init([](null_shared_ptr_tag) { return std::shared_ptr<TestFactory3>(); })) .def_readwrite("value", &TestFactory3::value) ; diff --git a/3rdparty/pybind11/tests/test_factory_constructors.py b/3rdparty/pybind11/tests/test_factory_constructors.py index 78a3910a..ffcce6fd 100644 --- a/3rdparty/pybind11/tests/test_factory_constructors.py +++ b/3rdparty/pybind11/tests/test_factory_constructors.py @@ -1,6 +1,9 @@ +# -*- coding: utf-8 -*- import pytest import re +import env # noqa: F401 + from pybind11_tests import factory_constructors as m from pybind11_tests.factory_constructors import tag from pybind11_tests import ConstructorStats @@ -9,7 +12,10 @@ from pybind11_tests import ConstructorStats def test_init_factory_basic(): """Tests py::init_factory() wrapper around various ways of returning the object""" - cstats = [ConstructorStats.get(c) for c in [m.TestFactory1, m.TestFactory2, m.TestFactory3]] + cstats = [ + ConstructorStats.get(c) + for c in [m.TestFactory1, m.TestFactory2, m.TestFactory3] + ] cstats[0].alive() # force gc n_inst = ConstructorStats.detail_reg_inst() @@ -38,9 +44,12 @@ def test_init_factory_basic(): z3 = m.TestFactory3("bye") assert z3.value == "bye" - with pytest.raises(TypeError) as excinfo: - m.TestFactory3(tag.null_ptr) - assert str(excinfo.value) == "pybind11::init(): factory function returned nullptr" + for null_ptr_kind in [tag.null_ptr, tag.null_unique_ptr, tag.null_shared_ptr]: + with pytest.raises(TypeError) as excinfo: + m.TestFactory3(null_ptr_kind) + assert ( + str(excinfo.value) == "pybind11::init(): factory function returned nullptr" + ) assert [i.alive() for i in cstats] == [3, 3, 3] assert ConstructorStats.detail_reg_inst() == n_inst + 9 @@ -55,7 +64,7 @@ def test_init_factory_basic(): assert [i.values() for i in cstats] == [ ["3", "hi!"], ["7", "hi again"], - ["42", "bye"] + ["42", "bye"], ] assert [i.default_constructions for i in cstats] == [1, 1, 1] @@ -63,7 +72,9 @@ def test_init_factory_basic(): def test_init_factory_signature(msg): with pytest.raises(TypeError) as excinfo: m.TestFactory1("invalid", "constructor", "arguments") - assert msg(excinfo.value) == """ + assert ( + msg(excinfo.value) + == """ __init__(): incompatible constructor arguments. The following argument types are supported: 1. m.factory_constructors.TestFactory1(arg0: m.factory_constructors.tag.unique_ptr_tag, arg1: int) 2. m.factory_constructors.TestFactory1(arg0: str) @@ -72,8 +83,11 @@ def test_init_factory_signature(msg): Invoked with: 'invalid', 'constructor', 'arguments' """ # noqa: E501 line too long + ) - assert msg(m.TestFactory1.__init__.__doc__) == """ + assert ( + msg(m.TestFactory1.__init__.__doc__) + == """ __init__(*args, **kwargs) Overloaded function. @@ -85,12 +99,16 @@ def test_init_factory_signature(msg): 4. __init__(self: m.factory_constructors.TestFactory1, arg0: handle, arg1: int, arg2: handle) -> None """ # noqa: E501 line too long + ) def test_init_factory_casting(): """Tests py::init_factory() wrapper with various upcasting and downcasting returns""" - cstats = [ConstructorStats.get(c) for c in [m.TestFactory3, m.TestFactory4, m.TestFactory5]] + cstats = [ + ConstructorStats.get(c) + for c in [m.TestFactory3, m.TestFactory4, m.TestFactory5] + ] cstats[0].alive() # force gc n_inst = ConstructorStats.detail_reg_inst() @@ -128,7 +146,7 @@ def test_init_factory_casting(): assert [i.values() for i in cstats] == [ ["4", "5", "6", "7", "8"], ["4", "5", "8"], - ["6", "7"] + ["6", "7"], ] @@ -198,7 +216,7 @@ def test_init_factory_alias(): assert [i.values() for i in cstats] == [ ["1", "8", "3", "4", "5", "6", "123", "10", "47"], - ["hi there", "3", "4", "6", "move", "123", "why hello!", "move", "47"] + ["hi there", "3", "4", "6", "move", "123", "why hello!", "move", "47"], ] @@ -262,9 +280,11 @@ def test_init_factory_dual(): assert not g1.has_alias() with pytest.raises(TypeError) as excinfo: PythFactory7(tag.shared_ptr, tag.invalid_base, 14) - assert (str(excinfo.value) == - "pybind11::init(): construction failed: returned holder-wrapped instance is not an " - "alias instance") + assert ( + str(excinfo.value) + == "pybind11::init(): construction failed: returned holder-wrapped instance is not an " + "alias instance" + ) assert [i.alive() for i in cstats] == [13, 7] assert ConstructorStats.detail_reg_inst() == n_inst + 13 @@ -278,7 +298,7 @@ def test_init_factory_dual(): assert [i.values() for i in cstats] == [ ["1", "2", "3", "4", "5", "6", "7", "8", "9", "100", "11", "12", "13", "14"], - ["2", "4", "6", "8", "9", "100", "12"] + ["2", "4", "6", "8", "9", "100", "12"], ] @@ -288,7 +308,7 @@ def test_no_placement_new(capture): with capture: a = m.NoPlacementNew(123) - found = re.search(r'^operator new called, returning (\d+)\n$', str(capture)) + found = re.search(r"^operator new called, returning (\d+)\n$", str(capture)) assert found assert a.i == 123 with capture: @@ -299,7 +319,7 @@ def test_no_placement_new(capture): with capture: b = m.NoPlacementNew() - found = re.search(r'^operator new called, returning (\d+)\n$', str(capture)) + found = re.search(r"^operator new called, returning (\d+)\n$", str(capture)) assert found assert b.i == 100 with capture: @@ -327,19 +347,21 @@ def create_and_destroy(*args): def strip_comments(s): - return re.sub(r'\s+#.*', '', s) + return re.sub(r"\s+#.*", "", s) -def test_reallocations(capture, msg): +def test_reallocation_a(capture, msg): """When the constructor is overloaded, previous overloads can require a preallocated value. This test makes sure that such preallocated values only happen when they might be necessary, - and that they are deallocated properly""" + and that they are deallocated properly.""" pytest.gc_collect() with capture: create_and_destroy(1) - assert msg(capture) == """ + assert ( + msg(capture) + == """ noisy new noisy placement new NoisyAlloc(int 1) @@ -347,9 +369,14 @@ def test_reallocations(capture, msg): ~NoisyAlloc() noisy delete """ + ) + + +def test_reallocation_b(capture, msg): with capture: create_and_destroy(1.5) - assert msg(capture) == strip_comments(""" + assert msg(capture) == strip_comments( + """ noisy new # allocation required to attempt first overload noisy delete # have to dealloc before considering factory init overload noisy new # pointer factory calling "new", part 1: allocation @@ -357,43 +384,59 @@ def test_reallocations(capture, msg): --- ~NoisyAlloc() # Destructor noisy delete # operator delete - """) + """ + ) + +def test_reallocation_c(capture, msg): with capture: create_and_destroy(2, 3) - assert msg(capture) == strip_comments(""" + assert msg(capture) == strip_comments( + """ noisy new # pointer factory calling "new", allocation NoisyAlloc(int 2) # constructor --- ~NoisyAlloc() # Destructor noisy delete # operator delete - """) + """ + ) + +def test_reallocation_d(capture, msg): with capture: create_and_destroy(2.5, 3) - assert msg(capture) == strip_comments(""" + assert msg(capture) == strip_comments( + """ NoisyAlloc(double 2.5) # construction (local func variable: operator_new not called) noisy new # return-by-value "new" part 1: allocation ~NoisyAlloc() # moved-away local func variable destruction --- ~NoisyAlloc() # Destructor noisy delete # operator delete - """) + """ + ) + +def test_reallocation_e(capture, msg): with capture: create_and_destroy(3.5, 4.5) - assert msg(capture) == strip_comments(""" + assert msg(capture) == strip_comments( + """ noisy new # preallocation needed before invoking placement-new overload noisy placement new # Placement new NoisyAlloc(double 3.5) # construction --- ~NoisyAlloc() # Destructor noisy delete # operator delete - """) + """ + ) + +def test_reallocation_f(capture, msg): with capture: create_and_destroy(4, 0.5) - assert msg(capture) == strip_comments(""" + assert msg(capture) == strip_comments( + """ noisy new # preallocation needed before invoking placement-new overload noisy delete # deallocation of preallocated storage noisy new # Factory pointer allocation @@ -401,11 +444,15 @@ def test_reallocations(capture, msg): --- ~NoisyAlloc() # Destructor noisy delete # operator delete - """) + """ + ) + +def test_reallocation_g(capture, msg): with capture: create_and_destroy(5, "hi") - assert msg(capture) == strip_comments(""" + assert msg(capture) == strip_comments( + """ noisy new # preallocation needed before invoking first placement new noisy delete # delete before considering new-style constructor noisy new # preallocation for second placement new @@ -414,13 +461,15 @@ def test_reallocations(capture, msg): --- ~NoisyAlloc() # Destructor noisy delete # operator delete - """) + """ + ) -@pytest.unsupported_on_py2 +@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.""" + class NotPybindDerived(object): pass @@ -444,16 +493,26 @@ def test_invalid_self(): a = m.TestFactory2(tag.pointer, 1) m.TestFactory6.__init__(a, tag.alias, 1) elif bad == 3: - m.TestFactory6.__init__(NotPybindDerived.__new__(NotPybindDerived), tag.base, 1) + m.TestFactory6.__init__( + NotPybindDerived.__new__(NotPybindDerived), tag.base, 1 + ) elif bad == 4: - m.TestFactory6.__init__(NotPybindDerived.__new__(NotPybindDerived), tag.alias, 1) + m.TestFactory6.__init__( + NotPybindDerived.__new__(NotPybindDerived), tag.alias, 1 + ) for arg in (1, 2): with pytest.raises(TypeError) as excinfo: BrokenTF1(arg) - assert str(excinfo.value) == "__init__(self, ...) called with invalid `self` argument" + assert ( + str(excinfo.value) + == "__init__(self, ...) called with invalid `self` argument" + ) for arg in (1, 2, 3, 4): with pytest.raises(TypeError) as excinfo: BrokenTF6(arg) - assert str(excinfo.value) == "__init__(self, ...) called with invalid `self` argument" + assert ( + str(excinfo.value) + == "__init__(self, ...) called with invalid `self` argument" + ) diff --git a/3rdparty/pybind11/tests/test_gil_scoped.cpp b/3rdparty/pybind11/tests/test_gil_scoped.cpp index 76c17fdc..b6a45a5f 100644 --- a/3rdparty/pybind11/tests/test_gil_scoped.cpp +++ b/3rdparty/pybind11/tests/test_gil_scoped.cpp @@ -13,17 +13,19 @@ class VirtClass { public: - virtual ~VirtClass() {} + virtual ~VirtClass() = default; + VirtClass() = default; + VirtClass(const VirtClass&) = delete; virtual void virtual_func() {} virtual void pure_virtual_func() = 0; }; class PyVirtClass : public VirtClass { void virtual_func() override { - PYBIND11_OVERLOAD(void, VirtClass, virtual_func,); + PYBIND11_OVERRIDE(void, VirtClass, virtual_func,); } void pure_virtual_func() override { - PYBIND11_OVERLOAD_PURE(void, VirtClass, pure_virtual_func,); + PYBIND11_OVERRIDE_PURE(void, VirtClass, pure_virtual_func,); } }; @@ -43,7 +45,7 @@ TEST_SUBMODULE(gil_scoped, m) { [](VirtClass &virt) { virt.pure_virtual_func(); }); m.def("test_cross_module_gil", []() { - auto cm = py::module::import("cross_module_gil_utils"); + auto cm = py::module_::import("cross_module_gil_utils"); auto gil_acquire = reinterpret_cast<void (*)()>( PyLong_AsVoidPtr(cm.attr("gil_acquire_funcaddr").ptr())); py::gil_scoped_release gil_release; diff --git a/3rdparty/pybind11/tests/test_gil_scoped.py b/3rdparty/pybind11/tests/test_gil_scoped.py index 1548337c..0a1d6274 100644 --- a/3rdparty/pybind11/tests/test_gil_scoped.py +++ b/3rdparty/pybind11/tests/test_gil_scoped.py @@ -1,5 +1,7 @@ +# -*- coding: utf-8 -*- import multiprocessing import threading + from pybind11_tests import gil_scoped as m @@ -19,6 +21,7 @@ def _run_in_process(target, *args, **kwargs): def _python_to_cpp_to_python(): """Calls different C++ functions that come back to Python.""" + class ExtendedVirtClass(m.VirtClass): def virtual_func(self): pass @@ -48,6 +51,7 @@ def _python_to_cpp_to_python_from_threads(num_threads, parallel=False): thread.join() +# TODO: FIXME, sometimes returns -11 (segfault) instead of 0 on macOS Python 3.9 def test_python_to_cpp_to_python_from_thread(): """Makes sure there is no GIL deadlock when running in a thread. @@ -56,6 +60,7 @@ def test_python_to_cpp_to_python_from_thread(): assert _run_in_process(_python_to_cpp_to_python_from_threads, 1) == 0 +# TODO: FIXME on macOS Python 3.9 def test_python_to_cpp_to_python_from_thread_multiple_parallel(): """Makes sure there is no GIL deadlock when running in a thread multiple times in parallel. @@ -64,14 +69,18 @@ def test_python_to_cpp_to_python_from_thread_multiple_parallel(): assert _run_in_process(_python_to_cpp_to_python_from_threads, 8, parallel=True) == 0 +# TODO: FIXME on macOS Python 3.9 def test_python_to_cpp_to_python_from_thread_multiple_sequential(): """Makes sure there is no GIL deadlock when running in a thread multiple times sequentially. It runs in a separate process to be able to stop and assert if it deadlocks. """ - assert _run_in_process(_python_to_cpp_to_python_from_threads, 8, parallel=False) == 0 + assert ( + _run_in_process(_python_to_cpp_to_python_from_threads, 8, parallel=False) == 0 + ) +# TODO: FIXME on macOS Python 3.9 def test_python_to_cpp_to_python_from_process(): """Makes sure there is no GIL deadlock when using processes. diff --git a/3rdparty/pybind11/tests/test_iostream.cpp b/3rdparty/pybind11/tests/test_iostream.cpp index e67f88af..e9161505 100644 --- a/3rdparty/pybind11/tests/test_iostream.cpp +++ b/3rdparty/pybind11/tests/test_iostream.cpp @@ -37,7 +37,7 @@ TEST_SUBMODULE(iostream, m) { }); m.def("captured_output", [](std::string msg) { - py::scoped_ostream_redirect redir(std::cout, py::module::import("sys").attr("stdout")); + py::scoped_ostream_redirect redir(std::cout, py::module_::import("sys").attr("stdout")); std::cout << msg << std::flush; }); @@ -46,7 +46,7 @@ TEST_SUBMODULE(iostream, m) { py::arg("msg"), py::arg("flush")=true); m.def("captured_err", [](std::string msg) { - py::scoped_ostream_redirect redir(std::cerr, py::module::import("sys").attr("stderr")); + py::scoped_ostream_redirect redir(std::cerr, py::module_::import("sys").attr("stderr")); std::cerr << msg << std::flush; }); @@ -65,8 +65,8 @@ TEST_SUBMODULE(iostream, m) { }); m.def("captured_dual", [](std::string msg, 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")); + 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; }); diff --git a/3rdparty/pybind11/tests/test_iostream.py b/3rdparty/pybind11/tests/test_iostream.py index 27095b27..506db42e 100644 --- a/3rdparty/pybind11/tests/test_iostream.py +++ b/3rdparty/pybind11/tests/test_iostream.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- from pybind11_tests import iostream as m import sys @@ -17,6 +18,7 @@ try: # Python 3.4 from contextlib import redirect_stdout except ImportError: + @contextmanager def redirect_stdout(target): original = sys.stdout @@ -24,10 +26,12 @@ except ImportError: yield sys.stdout = original + try: # Python 3.5 from contextlib import redirect_stderr except ImportError: + @contextmanager def redirect_stderr(target): original = sys.stderr @@ -41,16 +45,16 @@ def test_captured(capsys): m.captured_output(msg) stdout, stderr = capsys.readouterr() assert stdout == msg - assert stderr == '' + assert stderr == "" m.captured_output_default(msg) stdout, stderr = capsys.readouterr() assert stdout == msg - assert stderr == '' + assert stderr == "" m.captured_err(msg) stdout, stderr = capsys.readouterr() - assert stdout == '' + assert stdout == "" assert stderr == msg @@ -62,7 +66,7 @@ def test_captured_large_string(capsys): m.captured_output_default(msg) stdout, stderr = capsys.readouterr() assert stdout == msg - assert stderr == '' + assert stderr == "" def test_guard_capture(capsys): @@ -70,7 +74,7 @@ def test_guard_capture(capsys): m.guard_output(msg) stdout, stderr = capsys.readouterr() assert stdout == msg - assert stderr == '' + assert stderr == "" def test_series_captured(capture): @@ -87,7 +91,7 @@ def test_flush(capfd): with m.ostream_redirect(): m.noisy_function(msg, flush=False) stdout, stderr = capfd.readouterr() - assert stdout == '' + assert stdout == "" m.noisy_function(msg2, flush=True) stdout, stderr = capfd.readouterr() @@ -106,15 +110,15 @@ def test_not_captured(capfd): m.raw_output(msg) stdout, stderr = capfd.readouterr() assert stdout == msg - assert stderr == '' - assert stream.getvalue() == '' + assert stderr == "" + assert stream.getvalue() == "" stream = StringIO() with redirect_stdout(stream): m.captured_output(msg) stdout, stderr = capfd.readouterr() - assert stdout == '' - assert stderr == '' + assert stdout == "" + assert stderr == "" assert stream.getvalue() == msg @@ -124,16 +128,16 @@ def test_err(capfd): with redirect_stderr(stream): m.raw_err(msg) stdout, stderr = capfd.readouterr() - assert stdout == '' + assert stdout == "" assert stderr == msg - assert stream.getvalue() == '' + assert stream.getvalue() == "" stream = StringIO() with redirect_stderr(stream): m.captured_err(msg) stdout, stderr = capfd.readouterr() - assert stdout == '' - assert stderr == '' + assert stdout == "" + assert stderr == "" assert stream.getvalue() == msg @@ -145,8 +149,8 @@ def test_multi_captured(capfd): m.captured_output("c") m.raw_output("d") stdout, stderr = capfd.readouterr() - assert stdout == 'bd' - assert stream.getvalue() == 'ac' + assert stdout == "bd" + assert stream.getvalue() == "ac" def test_dual(capsys): @@ -163,14 +167,14 @@ def test_redirect(capfd): m.raw_output(msg) stdout, stderr = capfd.readouterr() assert stdout == msg - assert stream.getvalue() == '' + assert stream.getvalue() == "" stream = StringIO() with redirect_stdout(stream): with m.ostream_redirect(): m.raw_output(msg) stdout, stderr = capfd.readouterr() - assert stdout == '' + assert stdout == "" assert stream.getvalue() == msg stream = StringIO() @@ -178,7 +182,7 @@ def test_redirect(capfd): m.raw_output(msg) stdout, stderr = capfd.readouterr() assert stdout == msg - assert stream.getvalue() == '' + assert stream.getvalue() == "" def test_redirect_err(capfd): @@ -192,7 +196,7 @@ def test_redirect_err(capfd): m.raw_err(msg2) stdout, stderr = capfd.readouterr() assert stdout == msg - assert stderr == '' + assert stderr == "" assert stream.getvalue() == msg2 @@ -208,7 +212,7 @@ def test_redirect_both(capfd): m.raw_output(msg) m.raw_err(msg2) stdout, stderr = capfd.readouterr() - assert stdout == '' - assert stderr == '' + assert stdout == "" + assert stderr == "" assert stream.getvalue() == msg assert stream2.getvalue() == msg2 diff --git a/3rdparty/pybind11/tests/test_kwargs_and_defaults.cpp b/3rdparty/pybind11/tests/test_kwargs_and_defaults.cpp index 6563fb9a..627a7969 100644 --- a/3rdparty/pybind11/tests/test_kwargs_and_defaults.cpp +++ b/3rdparty/pybind11/tests/test_kwargs_and_defaults.cpp @@ -71,7 +71,7 @@ TEST_SUBMODULE(kwargs_and_defaults, m) { py::tuple t(a.size()); 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<ssize_t>(i))); + t[i] = (int) Py_REFCNT(PyTuple_GET_ITEM(a.ptr(), static_cast<py::ssize_t>(i))); return t; }); m.def("mixed_args_refcount", [](py::object o, py::args a) { @@ -80,7 +80,7 @@ TEST_SUBMODULE(kwargs_and_defaults, m) { t[0] = o.ref_count(); 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<ssize_t>(i))); + t[i + 1] = (int) Py_REFCNT(PyTuple_GET_ITEM(a.ptr(), static_cast<py::ssize_t>(i))); return t; }); @@ -94,9 +94,49 @@ TEST_SUBMODULE(kwargs_and_defaults, m) { // 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("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); + }); + + // 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); + + + // These should fail to compile: + // argument annotations are required when using 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); + // test_function_signatures (along with most of the above) struct KWClass { void foo(int, float) {} }; py::class_<KWClass>(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); }, + "a"_a = py::module_::import("decimal").attr("Decimal")); } diff --git a/3rdparty/pybind11/tests/test_kwargs_and_defaults.py b/3rdparty/pybind11/tests/test_kwargs_and_defaults.py index 27a05a02..12fe705b 100644 --- a/3rdparty/pybind11/tests/test_kwargs_and_defaults.py +++ b/3rdparty/pybind11/tests/test_kwargs_and_defaults.py @@ -1,4 +1,8 @@ +# -*- coding: utf-8 -*- import pytest + +import env # noqa: F401 + from pybind11_tests import kwargs_and_defaults as m @@ -11,11 +15,17 @@ def test_function_signatures(doc): assert doc(m.kw_func_udl) == "kw_func_udl(x: int, y: int = 300) -> str" assert doc(m.kw_func_udl_z) == "kw_func_udl_z(x: int, y: int = 0) -> str" assert doc(m.args_function) == "args_function(*args) -> tuple" - assert doc(m.args_kwargs_function) == "args_kwargs_function(*args, **kwargs) -> tuple" - assert doc(m.KWClass.foo0) == \ - "foo0(self: m.kwargs_and_defaults.KWClass, arg0: int, arg1: float) -> None" - assert doc(m.KWClass.foo1) == \ - "foo1(self: m.kwargs_and_defaults.KWClass, x: int, y: float) -> None" + assert ( + doc(m.args_kwargs_function) == "args_kwargs_function(*args, **kwargs) -> tuple" + ) + assert ( + doc(m.KWClass.foo0) + == "foo0(self: m.kwargs_and_defaults.KWClass, arg0: int, arg1: float) -> None" + ) + assert ( + doc(m.KWClass.foo1) + == "foo1(self: m.kwargs_and_defaults.KWClass, x: int, y: float) -> None" + ) def test_named_arguments(msg): @@ -36,7 +46,9 @@ def test_named_arguments(msg): # noinspection PyArgumentList m.kw_func2(x=5, y=10, z=12) assert excinfo.match( - r'(?s)^kw_func2\(\): incompatible.*Invoked with: kwargs: ((x=5|y=10|z=12)(, |$))' + '{3}$') + r"(?s)^kw_func2\(\): incompatible.*Invoked with: kwargs: ((x=5|y=10|z=12)(, |$))" + + "{3}$" + ) assert m.kw_func4() == "{13 17}" assert m.kw_func4(myList=[1, 2, 3]) == "{1 2 3}" @@ -46,11 +58,11 @@ def test_named_arguments(msg): def test_arg_and_kwargs(): - args = 'arg1_value', 'arg2_value', 3 + args = "arg1_value", "arg2_value", 3 assert m.args_function(*args) == args - args = 'a1', 'a2' - kwargs = dict(arg3='a3', arg4=4) + args = "a1", "a2" + kwargs = dict(arg3="a3", arg4=4) assert m.args_kwargs_function(*args, **kwargs) == (args, kwargs) @@ -64,49 +76,166 @@ def test_mixed_args_and_kwargs(msg): assert mpa(1, 2.5) == (1, 2.5, ()) with pytest.raises(TypeError) as excinfo: assert mpa(1) - assert msg(excinfo.value) == """ + assert ( + msg(excinfo.value) + == """ mixed_plus_args(): incompatible function arguments. The following argument types are supported: 1. (arg0: int, arg1: float, *args) -> tuple Invoked with: 1 """ # noqa: E501 line too long + ) with pytest.raises(TypeError) as excinfo: assert mpa() - assert msg(excinfo.value) == """ + assert ( + msg(excinfo.value) + == """ mixed_plus_args(): incompatible function arguments. The following argument types are supported: 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) == (-2, 3.5, {'e': 2.71828, 'pi': 3.14159}) + assert mpk(-2, 3.5, pi=3.14159, e=2.71828) == ( + -2, + 3.5, + {"e": 2.71828, "pi": 3.14159}, + ) assert mpak(7, 7.7, 7.77, 7.777, 7.7777, minusseven=-7) == ( - 7, 7.7, (7.77, 7.777, 7.7777), {'minusseven': -7}) + 7, + 7.7, + (7.77, 7.777, 7.7777), + {"minusseven": -7}, + ) assert mpakd() == (1, 3.14159, (), {}) assert mpakd(3) == (3, 3.14159, (), {}) assert mpakd(j=2.71828) == (1, 2.71828, (), {}) - assert mpakd(k=42) == (1, 3.14159, (), {'k': 42}) + assert mpakd(k=42) == (1, 3.14159, (), {"k": 42}) assert mpakd(1, 1, 2, 3, 5, 8, then=13, followedby=21) == ( - 1, 1, (2, 3, 5, 8), {'then': 13, 'followedby': 21}) + 1, + 1, + (2, 3, 5, 8), + {"then": 13, "followedby": 21}, + ) # Arguments specified both positionally and via kwargs should fail: with pytest.raises(TypeError) as excinfo: assert mpakd(1, i=1) - assert msg(excinfo.value) == """ + assert ( + msg(excinfo.value) + == """ mixed_plus_args_kwargs_defaults(): incompatible function arguments. The following argument types are supported: 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) - assert msg(excinfo.value) == """ + assert ( + msg(excinfo.value) + == """ mixed_plus_args_kwargs_defaults(): incompatible function arguments. The following argument types are supported: 1. (i: int = 1, j: float = 3.14159, *args, **kwargs) -> tuple Invoked with: 1, 2; kwargs: j=1 """ # noqa: E501 line too long + ) + + +def test_keyword_only_args(msg): + assert m.kw_only_all(i=1, j=2) == (1, 2) + assert m.kw_only_all(j=1, i=2) == (2, 1) + + with pytest.raises(TypeError) as excinfo: + assert m.kw_only_all(i=1) == (1,) + assert "incompatible function arguments" in str(excinfo.value) + + with pytest.raises(TypeError) as excinfo: + assert m.kw_only_all(1, 2) == (1, 2) + assert "incompatible function arguments" in str(excinfo.value) + + assert m.kw_only_some(1, k=3, j=2) == (1, 2, 3) + assert m.kw_only_with_defaults(z=8) == (3, 4, 5, 8) + assert m.kw_only_with_defaults(2, z=8) == (2, 4, 5, 8) + assert m.kw_only_with_defaults(2, j=7, k=8, z=9) == (2, 7, 8, 9) + assert m.kw_only_with_defaults(2, 7, z=9, k=8) == (2, 7, 8, 9) + + assert m.kw_only_mixed(1, j=2) == (1, 2) + assert m.kw_only_mixed(j=2, i=3) == (3, 2) + assert m.kw_only_mixed(i=2, j=3) == (2, 3) + + assert m.kw_only_plus_more(4, 5, k=6, extra=7) == (4, 5, 6, {"extra": 7}) + assert m.kw_only_plus_more(3, k=5, j=4, extra=6) == (3, 4, 5, {"extra": 6}) + assert m.kw_only_plus_more(2, k=3, extra=4) == (2, -1, 3, {"extra": 4}) + + with pytest.raises(TypeError) as excinfo: + assert m.kw_only_mixed(i=1) == (1,) + assert "incompatible function arguments" in str(excinfo.value) + + with pytest.raises(RuntimeError) as excinfo: + m.register_invalid_kw_only(m) + assert ( + msg(excinfo.value) + == """ + arg(): cannot specify an unnamed argument after an kw_only() annotation + """ + ) + + +def test_positional_only_args(msg): + assert m.pos_only_all(1, 2) == (1, 2) + assert m.pos_only_all(2, 1) == (2, 1) + + with pytest.raises(TypeError) as excinfo: + m.pos_only_all(i=1, j=2) + assert "incompatible function arguments" in str(excinfo.value) + assert m.pos_only_mix(1, 2) == (1, 2) + assert m.pos_only_mix(2, j=1) == (2, 1) + + with pytest.raises(TypeError) as excinfo: + m.pos_only_mix(i=1, j=2) + assert "incompatible function arguments" in str(excinfo.value) + + assert m.pos_kw_only_mix(1, 2, k=3) == (1, 2, 3) + assert m.pos_kw_only_mix(1, j=2, k=3) == (1, 2, 3) + + with pytest.raises(TypeError) as excinfo: + m.pos_kw_only_mix(i=1, j=2, k=3) + assert "incompatible function arguments" in str(excinfo.value) + + with pytest.raises(TypeError) as excinfo: + m.pos_kw_only_mix(1, 2, 3) + assert "incompatible function arguments" in str(excinfo.value) + + with pytest.raises(TypeError) as excinfo: + m.pos_only_def_mix() + assert "incompatible function arguments" in str(excinfo.value) + + assert m.pos_only_def_mix(1) == (1, 2, 3) + assert m.pos_only_def_mix(1, 4) == (1, 4, 3) + assert m.pos_only_def_mix(1, 4, 7) == (1, 4, 7) + assert m.pos_only_def_mix(1, 4, k=7) == (1, 4, 7) + + with pytest.raises(TypeError) as excinfo: + m.pos_only_def_mix(1, j=4) + assert "incompatible function arguments" in str(excinfo.value) + + +def test_signatures(): + assert "kw_only_all(*, i: int, j: int) -> tuple\n" == m.kw_only_all.__doc__ + assert "kw_only_mixed(i: int, *, j: int) -> tuple\n" == m.kw_only_mixed.__doc__ + assert "pos_only_all(i: int, j: int, /) -> tuple\n" == m.pos_only_all.__doc__ + assert "pos_only_mix(i: int, /, j: int) -> tuple\n" == m.pos_only_mix.__doc__ + assert ( + "pos_kw_only_mix(i: int, /, j: int, *, k: int) -> tuple\n" + == m.pos_kw_only_mix.__doc__ + ) + + +@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""" @@ -128,11 +257,18 @@ def test_args_refcount(): assert m.args_function(-1, myval) == (-1, myval) assert refcount(myval) == expected - assert m.mixed_plus_args_kwargs(5, 6.0, myval, a=myval) == (5, 6.0, (myval,), {"a": myval}) + assert m.mixed_plus_args_kwargs(5, 6.0, myval, a=myval) == ( + 5, + 6.0, + (myval,), + {"a": myval}, + ) assert refcount(myval) == expected - assert m.args_kwargs_function(7, 8, myval, a=1, b=myval) == \ - ((7, 8, myval), {"a": 1, "b": myval}) + assert m.args_kwargs_function(7, 8, myval, a=1, b=myval) == ( + (7, 8, myval), + {"a": 1, "b": myval}, + ) assert refcount(myval) == expected exp3 = refcount(myval, myval, myval) @@ -145,3 +281,5 @@ def test_args_refcount(): # tuple without having to inc_ref the individual elements, but here we can't, hence the extra # refs. assert m.mixed_args_refcount(myval, myval, myval) == (exp3 + 3, exp3 + 3, exp3 + 3) + + assert m.class_default_argument() == "<class 'decimal.Decimal'>" diff --git a/3rdparty/pybind11/tests/test_local_bindings.cpp b/3rdparty/pybind11/tests/test_local_bindings.cpp index 97c02dbe..c61e3888 100644 --- a/3rdparty/pybind11/tests/test_local_bindings.cpp +++ b/3rdparty/pybind11/tests/test_local_bindings.cpp @@ -41,7 +41,7 @@ TEST_SUBMODULE(local_bindings, m) { // 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"); + auto main = py::module_::import("pybind11_tests"); if (py::hasattr(main, "class_")) { bind_local<LocalExternal, 7>(m, "LocalExternal", py::module_local()); } diff --git a/3rdparty/pybind11/tests/test_local_bindings.py b/3rdparty/pybind11/tests/test_local_bindings.py index b380376e..d23c4675 100644 --- a/3rdparty/pybind11/tests/test_local_bindings.py +++ b/3rdparty/pybind11/tests/test_local_bindings.py @@ -1,5 +1,8 @@ +# -*- coding: utf-8 -*- import pytest +import env # noqa: F401 + from pybind11_tests import local_bindings as m @@ -33,8 +36,8 @@ def test_local_bindings(): assert i2.get() == 11 assert i2.get2() == 12 - assert not hasattr(i1, 'get2') - assert not hasattr(i2, 'get3') + assert not hasattr(i1, "get2") + assert not hasattr(i2, "get3") # Loading within the local module assert m.local_value(i1) == 5 @@ -52,7 +55,9 @@ def test_nonlocal_failure(): with pytest.raises(RuntimeError) as excinfo: cm.register_nonlocal() - assert str(excinfo.value) == 'generic_type: type "NonLocalType" is already registered!' + assert ( + str(excinfo.value) == 'generic_type: type "NonLocalType" is already registered!' + ) def test_duplicate_local(): @@ -60,9 +65,12 @@ def test_duplicate_local(): with pytest.raises(RuntimeError) as excinfo: m.register_local_external() import pybind11_tests + assert str(excinfo.value) == ( 'generic_type: type "LocalExternal" is already registered!' - if hasattr(pybind11_tests, 'class_') else 'test_class not enabled') + if hasattr(pybind11_tests, "class_") + else "test_class not enabled" + ) def test_stl_bind_local(): @@ -95,8 +103,8 @@ def test_stl_bind_local(): d1["b"] = v1[1] d2["c"] = v2[0] d2["d"] = v2[1] - assert {i: d1[i].get() for i in d1} == {'a': 0, 'b': 1} - assert {i: d2[i].get() for i in d2} == {'c': 2, 'd': 3} + assert {i: d1[i].get() for i in d1} == {"a": 0, "b": 1} + assert {i: d2[i].get() for i in d2} == {"c": 2, "d": 3} def test_stl_bind_global(): @@ -104,15 +112,21 @@ def test_stl_bind_global(): with pytest.raises(RuntimeError) as excinfo: cm.register_nonlocal_map() - assert str(excinfo.value) == 'generic_type: type "NonLocalMap" is already registered!' + assert ( + str(excinfo.value) == 'generic_type: type "NonLocalMap" is already registered!' + ) with pytest.raises(RuntimeError) as excinfo: cm.register_nonlocal_vec() - assert str(excinfo.value) == 'generic_type: type "NonLocalVec" is already registered!' + assert ( + str(excinfo.value) == 'generic_type: type "NonLocalVec" is already registered!' + ) with pytest.raises(RuntimeError) as excinfo: cm.register_nonlocal_map2() - assert str(excinfo.value) == 'generic_type: type "NonLocalMap2" is already registered!' + assert ( + str(excinfo.value) == 'generic_type: type "NonLocalMap2" is already registered!' + ) def test_mixed_local_global(): @@ -120,6 +134,7 @@ def test_mixed_local_global(): type can be registered even if the type is already registered globally. With the module, casting will go to the local type; outside the module casting goes to the global type.""" import pybind11_cross_module_tests as cm + m.register_mixed_global() m.register_mixed_local() @@ -142,16 +157,30 @@ def test_mixed_local_global(): a.append(cm.get_mixed_gl(11)) a.append(cm.get_mixed_lg(12)) - assert [x.get() for x in a] == \ - [101, 1002, 103, 1004, 105, 1006, 207, 2008, 109, 1010, 211, 2012] + assert [x.get() for x in a] == [ + 101, + 1002, + 103, + 1004, + 105, + 1006, + 207, + 2008, + 109, + 1010, + 211, + 2012, + ] def test_internal_locals_differ(): """Makes sure the internal local type map differs across the two modules""" import pybind11_cross_module_tests as cm + assert m.local_cpp_types_addr() != cm.local_cpp_types_addr() +@pytest.mark.xfail("env.PYPY and sys.pypy_version_info < (7, 3, 2)") def test_stl_caster_vs_stl_bind(msg): """One module uses a generic vector caster from `<pybind11/stl.h>` while the other exports `std::vector<int>` via `py:bind_vector` and `py::module_local`""" @@ -165,12 +194,15 @@ def test_stl_caster_vs_stl_bind(msg): assert m.load_vector_via_caster(v2) == 6 with pytest.raises(TypeError) as excinfo: cm.load_vector_via_binding(v2) == 6 - assert msg(excinfo.value) == """ + assert ( + msg(excinfo.value) + == """ load_vector_via_binding(): incompatible function arguments. The following argument types are supported: 1. (arg0: pybind11_cross_module_tests.VectorInt) -> int Invoked with: [1, 2, 3] """ # noqa: E501 line too long + ) def test_cross_module_calls(): diff --git a/3rdparty/pybind11/tests/test_methods_and_attributes.cpp b/3rdparty/pybind11/tests/test_methods_and_attributes.cpp index c7b82f13..6a2cfb6f 100644 --- a/3rdparty/pybind11/tests/test_methods_and_attributes.cpp +++ b/3rdparty/pybind11/tests/test_methods_and_attributes.cpp @@ -21,6 +21,7 @@ public: ExampleMandA() { print_default_created(this); } 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); } ~ExampleMandA() { print_destroyed(this); } @@ -43,6 +44,8 @@ public: 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 @@ -105,76 +108,6 @@ struct TestPropRVP { UserType TestPropRVP::sv1(1); UserType TestPropRVP::sv2(1); -// 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<ArgInspector1> { -public: - PYBIND11_TYPE_CASTER(ArgInspector1, _("ArgInspector1")); - - bool load(handle src, bool convert) { - value.arg = "loading ArgInspector1 argument " + - std::string(convert ? "WITH" : "WITHOUT") + " conversion allowed. " - "Argument value = " + (std::string) str(src); - return true; - } - - static handle cast(const ArgInspector1 &src, return_value_policy, handle) { - return str(src.arg).release(); - } -}; -template <> struct type_caster<ArgInspector2> { -public: - PYBIND11_TYPE_CASTER(ArgInspector2, _("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); - return true; - } - - static handle cast(const ArgInspector2 &src, return_value_policy, handle) { - return str(src.arg).release(); - } -}; -template <> struct type_caster<ArgAlwaysConverts> { -public: - PYBIND11_TYPE_CASTER(ArgAlwaysConverts, _("ArgAlwaysConverts")); - - bool load(handle, bool convert) { - return convert; - } - - static handle cast(const ArgAlwaysConverts &, return_value_policy, handle) { - return py::none().release(); - } -}; -}} - -// test_custom_caster_destruction -class DestructionTester { -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; } -}; -namespace pybind11 { namespace detail { -template <> struct type_caster<DestructionTester> { - PYBIND11_TYPE_CASTER(DestructionTester, _("DestructionTester")); - bool load(handle, bool) { return true; } - - static handle cast(const DestructionTester &, return_value_policy, handle) { - return py::bool_(true).release(); - } -}; -}} - // Test None-allowed py::arg argument policy class NoneTester { public: int answer = 42; }; int none1(const NoneTester &obj) { return obj.answer; } @@ -207,11 +140,20 @@ public: double sum() const { return rw_value + ro_value; } }; +// Test explicit lvalue ref-qualification +struct RefQualified { + int value = 0; + + void refQualified(int other) & { value += other; } + int constRefQualified(int other) const & { return value + other; } +}; + TEST_SUBMODULE(methods_and_attributes, m) { // test_methods_and_attributes py::class_<ExampleMandA> emna(m, "ExampleMandA"); emna.def(py::init<>()) .def(py::init<int>()) + .def(py::init<std::string&&>()) .def(py::init<const ExampleMandA&>()) .def("add1", &ExampleMandA::add1) .def("add2", &ExampleMandA::add2) @@ -223,6 +165,7 @@ TEST_SUBMODULE(methods_and_attributes, m) { .def("add8", &ExampleMandA::add8) .def("add9", &ExampleMandA::add9) .def("add10", &ExampleMandA::add10) + .def("consume_str", &ExampleMandA::consume_str) .def("self1", &ExampleMandA::self1) .def("self2", &ExampleMandA::self2) .def("self3", &ExampleMandA::self3) @@ -264,12 +207,12 @@ TEST_SUBMODULE(methods_and_attributes, m) { // 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::class_<ExampleMandA>>(py::module::import("pybind11_tests.methods_and_attributes").attr("ExampleMandA")); + auto emna = py::reinterpret_borrow<py::class_<ExampleMandA>>(py::module_::import("pybind11_tests.methods_and_attributes").attr("ExampleMandA")); emna.def ("overload_mixed1", static_cast<py::str (ExampleMandA::*)(int, int)>(&ExampleMandA::overloaded)) .def_static("overload_mixed1", static_cast<py::str ( *)(float )>(&ExampleMandA::overloaded)); }) .def_static("add_mixed_overloads2", []() { - auto emna = py::reinterpret_borrow<py::class_<ExampleMandA>>(py::module::import("pybind11_tests.methods_and_attributes").attr("ExampleMandA")); + auto emna = py::reinterpret_borrow<py::class_<ExampleMandA>>(py::module_::import("pybind11_tests.methods_and_attributes").attr("ExampleMandA")); emna.def_static("overload_mixed2", static_cast<py::str ( *)(float )>(&ExampleMandA::overloaded)) .def ("overload_mixed2", static_cast<py::str (ExampleMandA::*)(int, int)>(&ExampleMandA::overloaded)); }) @@ -341,11 +284,18 @@ TEST_SUBMODULE(methods_and_attributes, m) { py::class_<MetaclassOverride>(m, "MetaclassOverride", py::metaclass((PyObject *) &PyType_Type)) .def_property_readonly_static("readonly", [](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", [](int) { return 3; }); + 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() { print_destroyed(this); } }; py::class_<DynamicClass>(m, "DynamicClass", py::dynamic_attr()) @@ -356,33 +306,6 @@ TEST_SUBMODULE(methods_and_attributes, m) { .def(py::init()); #endif - // test_noconvert_args - // - // Test converting. The ArgAlwaysConverts is just there to make the first no-conversion pass - // fail so that our call always ends up happening via the second dispatch (the one that allows - // some conversion). - class ArgInspector { - public: - ArgInspector1 f(ArgInspector1 a, ArgAlwaysConverts) { return a; } - std::string g(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; } - }; - py::class_<ArgInspector>(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()); - // test_bad_arg_default // Issue/PR #648: bad arg default debugging output #if !defined(NDEBUG) @@ -391,11 +314,11 @@ TEST_SUBMODULE(methods_and_attributes, m) { m.attr("debug_enabled") = false; #endif m.def("bad_arg_def_named", []{ - auto m = py::module::import("pybind11_tests"); + auto m = py::module_::import("pybind11_tests"); m.def("should_fail", [](int, UnregisteredType) {}, py::arg(), py::arg("a") = UnregisteredType()); }); m.def("bad_arg_def_unnamed", []{ - auto m = py::module::import("pybind11_tests"); + auto m = py::module_::import("pybind11_tests"); m.def("should_fail", [](int, UnregisteredType) {}, py::arg(), py::arg() = UnregisteredType()); }); @@ -413,6 +336,9 @@ TEST_SUBMODULE(methods_and_attributes, m) { 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)); + // test_str_issue // Issue #283: __str__ called on uninitialized instance when constructor arguments invalid py::class_<StrIssue>(m, "StrIssue") @@ -446,15 +372,10 @@ TEST_SUBMODULE(methods_and_attributes, m) { using Adapted = decltype(py::method_adaptor<RegisteredDerived>(&RegisteredDerived::do_nothing)); static_assert(std::is_same<Adapted, void (RegisteredDerived::*)() const>::value, ""); - // 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<DestructionTester>, py::return_value_policy::reference); + // test_methods_and_attributes + py::class_<RefQualified>(m, "RefQualified") + .def(py::init<>()) + .def_readonly("value", &RefQualified::value) + .def("refQualified", &RefQualified::refQualified) + .def("constRefQualified", &RefQualified::constRefQualified); } diff --git a/3rdparty/pybind11/tests/test_methods_and_attributes.py b/3rdparty/pybind11/tests/test_methods_and_attributes.py index f1c862be..2aaf9331 100644 --- a/3rdparty/pybind11/tests/test_methods_and_attributes.py +++ b/3rdparty/pybind11/tests/test_methods_and_attributes.py @@ -1,4 +1,8 @@ +# -*- coding: utf-8 -*- import pytest + +import env # noqa: F401 + from pybind11_tests import methods_and_attributes as m from pybind11_tests import ConstructorStats @@ -36,17 +40,17 @@ def test_methods_and_attributes(): assert instance1.overloaded(0) == "(int)" assert instance1.overloaded(1, 1.0) == "(int, float)" assert instance1.overloaded(2.0, 2) == "(float, int)" - assert instance1.overloaded(3, 3) == "(int, int)" - assert instance1.overloaded(4., 4.) == "(float, float)" + assert instance1.overloaded(3, 3) == "(int, int)" + assert instance1.overloaded(4.0, 4.0) == "(float, float)" assert instance1.overloaded_const(-3) == "(int) const" assert instance1.overloaded_const(5, 5.0) == "(int, float) const" assert instance1.overloaded_const(6.0, 6) == "(float, int) const" - assert instance1.overloaded_const(7, 7) == "(int, int) const" - assert instance1.overloaded_const(8., 8.) == "(float, float) const" + assert instance1.overloaded_const(7, 7) == "(int, int) const" + assert instance1.overloaded_const(8.0, 8.0) == "(float, float) const" assert instance1.overloaded_float(1, 1) == "(float, float)" - assert instance1.overloaded_float(1, 1.) == "(float, float)" - assert instance1.overloaded_float(1., 1) == "(float, float)" - assert instance1.overloaded_float(1., 1.) == "(float, float)" + assert instance1.overloaded_float(1, 1.0) == "(float, float)" + assert instance1.overloaded_float(1.0, 1) == "(float, float)" + assert instance1.overloaded_float(1.0, 1.0) == "(float, float)" assert instance1.value == 320 instance1.value = 100 @@ -58,8 +62,8 @@ def test_methods_and_attributes(): assert cstats.alive() == 0 assert cstats.values() == ["32"] assert cstats.default_constructions == 1 - assert cstats.copy_constructions == 3 - assert cstats.move_constructions >= 1 + assert cstats.copy_constructions == 2 + assert cstats.move_constructions >= 2 assert cstats.copy_assignments == 0 assert cstats.move_assignments == 0 @@ -167,6 +171,19 @@ def test_static_properties(): assert m.TestPropertiesOverride().def_readonly == 99 assert m.TestPropertiesOverride.def_readonly_static == 99 + # Only static attributes can be deleted + del m.TestPropertiesOverride.def_readonly_static + assert ( + hasattr(m.TestPropertiesOverride, "def_readonly_static") + and m.TestPropertiesOverride.def_readonly_static + is m.TestProperties.def_readonly_static + ) + assert "def_readonly_static" not in m.TestPropertiesOverride.__dict__ + properties_override = m.TestPropertiesOverride() + with pytest.raises(AttributeError) as excinfo: + del properties_override.def_readonly + assert "can't delete attribute" in str(excinfo.value) + def test_static_cls(): """Static property getter and setters expect the type object as the their only argument""" @@ -189,7 +206,10 @@ def test_metaclass_override(): assert type(m.MetaclassOverride).__name__ == "type" assert m.MetaclassOverride.readonly == 1 - assert type(m.MetaclassOverride.__dict__["readonly"]).__name__ == "pybind11_static_property" + assert ( + type(m.MetaclassOverride.__dict__["readonly"]).__name__ + == "pybind11_static_property" + ) # Regular `type` replaces the property instead of calling `__set__()` m.MetaclassOverride.readonly = 2 @@ -202,22 +222,26 @@ def test_no_mixed_overloads(): 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 else - "error while attempting to bind static method ExampleMandA.overload_mixed1" - "(arg0: float) -> str") - ) + 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 + else "error while attempting to bind static method ExampleMandA.overload_mixed1" + "(arg0: float) -> str" + ) with pytest.raises(RuntimeError) as excinfo: m.ExampleMandA.add_mixed_overloads2() - 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 else - "error while attempting to bind instance method ExampleMandA.overload_mixed2" - "(self: pybind11_tests.methods_and_attributes.ExampleMandA, arg0: int, arg1: int)" - " -> str") - ) + 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 + else "error while attempting to bind instance method ExampleMandA.overload_mixed2" + "(self: pybind11_tests.methods_and_attributes.ExampleMandA, arg0: int, arg1: int)" + " -> str" + ) @pytest.mark.parametrize("access", ["ro", "rw", "static_ro", "static_rw"]) @@ -256,8 +280,8 @@ def test_property_rvalue_policy(): assert os.value == 1 -# https://bitbucket.org/pypy/pypy/issues/2447 -@pytest.unsupported_on_pypy +# https://foss.heptapod.net/pypy/pypy/-/issues/2447 +@pytest.mark.xfail("env.PYPY") def test_dynamic_attributes(): instance = m.DynamicClass() assert not hasattr(instance, "foo") @@ -298,8 +322,8 @@ def test_dynamic_attributes(): assert cstats.alive() == 0 -# https://bitbucket.org/pypy/pypy/issues/2447 -@pytest.unsupported_on_pypy +# https://foss.heptapod.net/pypy/pypy/-/issues/2447 +@pytest.mark.xfail("env.PYPY") def test_cyclic_gc(): # One object references itself instance = m.DynamicClass() @@ -321,69 +345,6 @@ def test_cyclic_gc(): assert cstats.alive() == 0 -def test_noconvert_args(msg): - a = m.ArgInspector() - assert msg(a.f("hi")) == """ - loading ArgInspector1 argument WITH conversion allowed. Argument value = hi - """ - assert msg(a.g("this is a", "this is b")) == """ - loading ArgInspector1 argument WITHOUT conversion allowed. Argument value = this is a - 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)) == """ - loading ArgInspector1 argument WITHOUT conversion allowed. Argument value = this is a - 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")) == """ - loading ArgInspector1 argument WITHOUT conversion allowed. Argument value = this is a - loading ArgInspector1 argument WITH conversion allowed. Argument value = this is b - 42 - loading ArgInspector2 argument WITH conversion allowed. Argument value = this is d - """ - assert (a.h("arg 1") == - "loading ArgInspector2 argument WITHOUT conversion allowed. Argument value = arg 1") - assert msg(m.arg_inspect_func("A1", "A2")) == """ - loading ArgInspector2 argument WITH conversion allowed. Argument value = A1 - loading ArgInspector1 argument WITHOUT conversion allowed. Argument value = A2 - """ - - assert m.floats_preferred(4) == 2.0 - assert m.floats_only(4.0) == 2.0 - with pytest.raises(TypeError) as excinfo: - m.floats_only(4) - assert msg(excinfo.value) == """ - floats_only(): incompatible function arguments. The following argument types are supported: - 1. (f: float) -> float - - Invoked with: 4 - """ - - assert m.ints_preferred(4) == 2 - assert m.ints_preferred(True) == 0 - with pytest.raises(TypeError) as excinfo: - m.ints_preferred(4.0) - assert msg(excinfo.value) == """ - ints_preferred(): incompatible function arguments. The following argument types are supported: - 1. (i: int) -> int - - Invoked with: 4.0 - """ # noqa: E501 line too long - - assert m.ints_only(4) == 2 - with pytest.raises(TypeError) as excinfo: - m.ints_only(4.0) - assert msg(excinfo.value) == """ - ints_only(): incompatible function arguments. The following argument types are supported: - 1. (i: int) -> int - - Invoked with: 4.0 - """ - - def test_bad_arg_default(msg): from pybind11_tests import debug_enabled @@ -392,8 +353,8 @@ def test_bad_arg_default(msg): 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 else - "arg(): could not convert default argument into a Python object (type not registered " + if debug_enabled + else "arg(): could not convert default argument into a Python object (type not registered " "yet?). Compile in debug mode for more information." ) @@ -402,8 +363,8 @@ 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 else - "arg(): could not convert default argument into a Python object (type not registered " + if debug_enabled + else "arg(): could not convert default argument into a Python object (type not registered " "yet?). Compile in debug mode for more information." ) @@ -440,12 +401,15 @@ def test_accepts_none(msg): # The first one still raises because you can't pass None as a lvalue reference arg: with pytest.raises(TypeError) as excinfo: assert m.ok_none1(None) == -1 - assert msg(excinfo.value) == """ + assert ( + msg(excinfo.value) + == """ ok_none1(): incompatible function arguments. The following argument types are supported: 1. (arg0: m.methods_and_attributes.NoneTester) -> int Invoked with: None """ + ) # The rest take the argument as pointer or holder, and accept None: assert m.ok_none2(None) == -1 @@ -453,6 +417,19 @@ def test_accepts_none(msg): assert m.ok_none4(None) == -1 assert m.ok_none5(None) == -1 + with pytest.raises(TypeError) as excinfo: + m.no_none_kwarg(None) + assert "incompatible function arguments" in str(excinfo.value) + with pytest.raises(TypeError) as excinfo: + m.no_none_kwarg(a=None) + assert "incompatible function arguments" in str(excinfo.value) + with pytest.raises(TypeError) as excinfo: + m.no_none_kwarg_kw_only(None) + assert "incompatible function arguments" in str(excinfo.value) + with pytest.raises(TypeError) as excinfo: + m.no_none_kwarg_kw_only(a=None) + assert "incompatible function arguments" in str(excinfo.value) + def test_str_issue(msg): """#283: __str__ called on uninitialized instance when constructor arguments invalid""" @@ -461,13 +438,16 @@ def test_str_issue(msg): with pytest.raises(TypeError) as excinfo: str(m.StrIssue("no", "such", "constructor")) - assert msg(excinfo.value) == """ + assert ( + msg(excinfo.value) + == """ __init__(): incompatible constructor arguments. The following argument types are supported: 1. m.methods_and_attributes.StrIssue(arg0: int) 2. m.methods_and_attributes.StrIssue() Invoked with: 'no', 'such', 'constructor' """ + ) def test_unregistered_base_implementations(): @@ -488,25 +468,40 @@ def test_unregistered_base_implementations(): assert a.ro_value_prop == 1.75 -def test_custom_caster_destruction(): - """Tests that returning a pointer to a type that gets converted with a custom type caster gets - destroyed when the function has py::return_value_policy::take_ownership policy applied.""" +def test_ref_qualified(): + """Tests that explicit lvalue ref-qualified methods can be called just like their + non ref-qualified counterparts.""" - cstats = m.destruction_tester_cstats() - # This one *doesn't* have take_ownership: the pointer should be used but not destroyed: - z = m.custom_caster_no_destroy() - assert cstats.alive() == 1 and cstats.default_constructions == 1 - assert z + r = m.RefQualified() + assert r.value == 0 + r.refQualified(17) + assert r.value == 17 + assert r.constRefQualified(23) == 40 - # take_ownership applied: this constructs a new object, casts it, then destroys it: - z = m.custom_caster_destroy() - assert z - assert cstats.default_constructions == 2 - # Same, but with a const pointer return (which should *not* inhibit destruction): - z = m.custom_caster_destroy_const() - assert z - assert cstats.default_constructions == 3 +def test_overload_ordering(): + "Check to see if the normal overload order (first defined) and prepend overload order works" + assert m.overload_order("string") == 1 + assert m.overload_order(0) == 4 - # Make sure we still only have the original object (from ..._no_destroy()) alive: - assert cstats.alive() == 1 + # 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 "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 "4. (arg0: int) -> int" in str(err.value) diff --git a/3rdparty/pybind11/tests/test_modules.cpp b/3rdparty/pybind11/tests/test_modules.cpp index c1475fa6..67387e80 100644 --- a/3rdparty/pybind11/tests/test_modules.cpp +++ b/3rdparty/pybind11/tests/test_modules.cpp @@ -13,6 +13,7 @@ TEST_SUBMODULE(modules, m) { // test_nested_modules + // This is intentionally "py::module" to verify it still can be used in place of "py::module_" py::module m_sub = m.def_submodule("subsubmodule"); m_sub.def("submodule_func", []() { return "submodule_func()"; }); @@ -50,6 +51,7 @@ TEST_SUBMODULE(modules, m) { .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_" m.attr("OD") = py::module::import("collections").attr("OrderedDict"); // test_duplicate_registration @@ -60,7 +62,8 @@ TEST_SUBMODULE(modules, m) { class Dupe3 { }; class DupeException { }; - auto dm = py::module("dummy"); + // 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 failures = py::list(); py::class_<Dupe1>(dm, "Dupe1"); diff --git a/3rdparty/pybind11/tests/test_modules.py b/3rdparty/pybind11/tests/test_modules.py index 2552838c..5630ccf9 100644 --- a/3rdparty/pybind11/tests/test_modules.py +++ b/3rdparty/pybind11/tests/test_modules.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- from pybind11_tests import modules as m from pybind11_tests.modules import subsubmodule as ms from pybind11_tests import ConstructorStats @@ -5,9 +6,13 @@ from pybind11_tests import ConstructorStats def test_nested_modules(): import pybind11_tests + assert pybind11_tests.__name__ == "pybind11_tests" assert pybind11_tests.modules.__name__ == "pybind11_tests.modules" - assert pybind11_tests.modules.subsubmodule.__name__ == "pybind11_tests.modules.subsubmodule" + assert ( + pybind11_tests.modules.subsubmodule.__name__ + == "pybind11_tests.modules.subsubmodule" + ) assert m.__name__ == "pybind11_tests.modules" assert ms.__name__ == "pybind11_tests.modules.subsubmodule" @@ -34,7 +39,7 @@ def test_reference_internal(): del b assert astats.alive() == 0 assert bstats.alive() == 0 - assert astats.values() == ['1', '2', '42', '43'] + assert astats.values() == ["1", "2", "42", "43"] assert bstats.values() == [] assert astats.default_constructions == 0 assert bstats.default_constructions == 1 @@ -53,7 +58,7 @@ def test_importing(): from collections import OrderedDict assert OD is OrderedDict - assert str(OD([(1, 'a'), (2, 'b')])) == "OrderedDict([(1, 'a'), (2, 'b')])" + assert str(OD([(1, "a"), (2, "b")])) == "OrderedDict([(1, 'a'), (2, 'b')])" def test_pydoc(): diff --git a/3rdparty/pybind11/tests/test_multiple_inheritance.cpp b/3rdparty/pybind11/tests/test_multiple_inheritance.cpp index ba1674fb..e6720080 100644 --- a/3rdparty/pybind11/tests/test_multiple_inheritance.cpp +++ b/3rdparty/pybind11/tests/test_multiple_inheritance.cpp @@ -193,14 +193,12 @@ TEST_SUBMODULE(multiple_inheritance, m) { .def_readwrite_static("static_value", &VanillaStaticMix2::static_value); -#if !defined(PYPY_VERSION) struct WithDict { }; struct VanillaDictMix1 : Vanilla, WithDict { }; struct VanillaDictMix2 : WithDict, Vanilla { }; py::class_<WithDict>(m, "WithDict", py::dynamic_attr()).def(py::init<>()); py::class_<VanillaDictMix1, Vanilla, WithDict>(m, "VanillaDictMix1").def(py::init<>()); py::class_<VanillaDictMix2, WithDict, Vanilla>(m, "VanillaDictMix2").def(py::init<>()); -#endif // test_diamond_inheritance // Issue #959: segfault when constructing diamond inheritance instance diff --git a/3rdparty/pybind11/tests/test_multiple_inheritance.py b/3rdparty/pybind11/tests/test_multiple_inheritance.py index 475dd3b3..e6a7f976 100644 --- a/3rdparty/pybind11/tests/test_multiple_inheritance.py +++ b/3rdparty/pybind11/tests/test_multiple_inheritance.py @@ -1,4 +1,8 @@ +# -*- coding: utf-8 -*- import pytest + +import env # noqa: F401 + from pybind11_tests import ConstructorStats from pybind11_tests import multiple_inheritance as m @@ -10,6 +14,8 @@ 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") def test_multiple_inheritance_mix1(): class Base1: def __init__(self, i): @@ -30,7 +36,6 @@ def test_multiple_inheritance_mix1(): def test_multiple_inheritance_mix2(): - class Base2: def __init__(self, i): self.i = i @@ -49,8 +54,9 @@ 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") def test_multiple_inheritance_python(): - class MI1(m.Base1, m.Base2): def __init__(self, i, j): m.Base1.__init__(self, i) @@ -156,7 +162,6 @@ def test_multiple_inheritance_python(): def test_multiple_inheritance_python_many_bases(): - class MIMany14(m.BaseN1, m.BaseN2, m.BaseN3, m.BaseN4): def __init__(self): m.BaseN1.__init__(self, 1) @@ -171,8 +176,16 @@ def test_multiple_inheritance_python_many_bases(): m.BaseN7.__init__(self, 7) m.BaseN8.__init__(self, 8) - class MIMany916(m.BaseN9, m.BaseN10, m.BaseN11, m.BaseN12, m.BaseN13, m.BaseN14, m.BaseN15, - m.BaseN16): + class MIMany916( + m.BaseN9, + m.BaseN10, + m.BaseN11, + m.BaseN12, + m.BaseN13, + m.BaseN14, + m.BaseN15, + m.BaseN16, + ): def __init__(self): m.BaseN9.__init__(self, 9) m.BaseN10.__init__(self, 10) @@ -218,7 +231,6 @@ def test_multiple_inheritance_python_many_bases(): def test_multiple_inheritance_virtbase(): - class MITypePy(m.Base12a): def __init__(self, i, j): m.Base12a.__init__(self, i, j) @@ -231,7 +243,7 @@ def test_multiple_inheritance_virtbase(): def test_mi_static_properties(): """Mixing bases with and without static properties should be possible - and the result should be independent of base definition order""" + and the result should be independent of base definition order""" for d in (m.VanillaStaticMix1(), m.VanillaStaticMix2()): assert d.vanilla() == "Vanilla" @@ -253,7 +265,7 @@ def test_mi_static_properties(): assert d.static_value == 0 -@pytest.unsupported_on_pypy +# Requires PyPy 6+ def test_mi_dynamic_attributes(): """Mixing bases with and without dynamic attribute support""" diff --git a/3rdparty/pybind11/tests/test_numpy_array.cpp b/3rdparty/pybind11/tests/test_numpy_array.cpp index 156a3bfa..a84de77f 100644 --- a/3rdparty/pybind11/tests/test_numpy_array.cpp +++ b/3rdparty/pybind11/tests/test_numpy_array.cpp @@ -22,7 +22,7 @@ struct DtypeCheck { template <typename T> DtypeCheck get_dtype_check(const char* name) { - py::module np = py::module::import("numpy"); + py::module_ np = py::module_::import("numpy"); DtypeCheck check{}; check.numpy = np.attr("dtype")(np.attr(name)); check.pybind11 = py::dtype::of<T>(); @@ -89,23 +89,23 @@ template<typename... Ix> arr data_t(const arr_t& a, Ix... index) { template<typename... Ix> arr& mutate_data(arr& a, Ix... index) { auto ptr = (uint8_t *) a.mutable_data(index...); - for (ssize_t i = 0; i < a.nbytes() - a.offset_at(index...); i++) + for (py::ssize_t i = 0; i < a.nbytes() - a.offset_at(index...); i++) ptr[i] = (uint8_t) (ptr[i] * 2); return a; } template<typename... Ix> arr_t& mutate_data_t(arr_t& a, Ix... index) { auto ptr = a.mutable_data(index...); - for (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<typename... Ix> ssize_t index_at(const arr& a, Ix... idx) { return a.index_at(idx...); } -template<typename... Ix> ssize_t index_at_t(const arr_t& a, Ix... idx) { return a.index_at(idx...); } -template<typename... Ix> ssize_t offset_at(const arr& a, Ix... idx) { return a.offset_at(idx...); } -template<typename... Ix> ssize_t offset_at_t(const arr_t& a, Ix... idx) { return a.offset_at(idx...); } -template<typename... Ix> ssize_t at_t(const arr_t& a, Ix... idx) { return a.at(idx...); } +template<typename... Ix> py::ssize_t index_at(const arr& a, Ix... idx) { return a.index_at(idx...); } +template<typename... Ix> py::ssize_t index_at_t(const arr_t& a, Ix... idx) { return a.index_at(idx...); } +template<typename... Ix> py::ssize_t offset_at(const arr& a, Ix... idx) { return a.offset_at(idx...); } +template<typename... Ix> py::ssize_t offset_at_t(const arr_t& a, Ix... idx) { return a.offset_at(idx...); } +template<typename... Ix> py::ssize_t at_t(const arr_t& a, Ix... idx) { return a.at(idx...); } template<typename... Ix> arr_t& mutate_at_t(arr_t& a, Ix... idx) { a.mutable_at(idx...)++; return a; } #define def_index_fn(name, type) \ @@ -133,7 +133,7 @@ template <typename T, typename T2> py::handle auxiliaries(T &&r, T2 &&r2) { static int data_i = 42; TEST_SUBMODULE(numpy_array, sm) { - try { py::module::import("numpy"); } + try { py::module_::import("numpy"); } catch (...) { return; } // test_dtypes @@ -159,9 +159,9 @@ TEST_SUBMODULE(numpy_array, sm) { // 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, ssize_t dim) { return a.shape(dim); }); + 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, ssize_t dim) { return a.strides(dim); }); + 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(); }); @@ -212,7 +212,7 @@ TEST_SUBMODULE(numpy_array, sm) { .def(py::init<>()) .def("numpy_view", [](py::object &obj) { py::print("ArrayClass::numpy_view()"); - ArrayClass &a = obj.cast<ArrayClass&>(); + auto &a = obj.cast<ArrayClass&>(); return py::array_t<int>({2}, {4}, a.data, obj); } ); @@ -281,33 +281,33 @@ TEST_SUBMODULE(numpy_array, sm) { // test_array_unchecked_fixed_dims sm.def("proxy_add2", [](py::array_t<double> a, double v) { auto r = a.mutable_unchecked<2>(); - for (ssize_t i = 0; i < r.shape(0); i++) - for (ssize_t j = 0; j < r.shape(1); j++) + 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<double, py::array::c_style> a({ 3, 3, 3 }); auto r = a.mutable_unchecked<3>(); - for (ssize_t i = 0; i < r.shape(0); i++) - for (ssize_t j = 0; j < r.shape(1); j++) - for (ssize_t k = 0; k < r.shape(2); k++) + 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<double, py::array::f_style> a({ 3, 3, 3 }); auto r = a.mutable_unchecked<3>(); - for (ssize_t k = 0; k < r.shape(2); k++) - for (ssize_t j = 0; j < r.shape(1); j++) - for (ssize_t i = 0; i < r.shape(0); i++) + 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<double> a) { auto r = a.unchecked<1>(); double sumsq = 0; - for (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; }); @@ -318,22 +318,34 @@ TEST_SUBMODULE(numpy_array, sm) { return auxiliaries(r, r2); }); + sm.def("proxy_auxiliaries1_const_ref", [](py::array_t<double> a) { + const auto &r = a.unchecked<1>(); + const auto &r2 = a.mutable_unchecked<1>(); + return r(0) == r2(0) && r[0] == r2[0]; + }); + + sm.def("proxy_auxiliaries2_const_ref", [](py::array_t<double> a) { + const auto &r = a.unchecked<2>(); + const auto &r2 = a.mutable_unchecked<2>(); + return r(0, 0) == r2(0, 0); + }); + // test_array_unchecked_dyn_dims // Same as the above, but without a compile-time dimensions specification: sm.def("proxy_add2_dyn", [](py::array_t<double> a, double v) { auto r = a.mutable_unchecked(); if (r.ndim() != 2) throw std::domain_error("error: ndim != 2"); - for (ssize_t i = 0; i < r.shape(0); i++) - for (ssize_t j = 0; j < r.shape(1); j++) + 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<double, py::array::c_style> a({ 3, 3, 3 }); auto r = a.mutable_unchecked(); if (r.ndim() != 3) throw std::domain_error("error: ndim != 3"); - for (ssize_t i = 0; i < r.shape(0); i++) - for (ssize_t j = 0; j < r.shape(1); j++) - for (ssize_t k = 0; k < r.shape(2); k++) + 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; }); @@ -362,7 +374,7 @@ TEST_SUBMODULE(numpy_array, sm) { // test_array_resize // reshape array to 2D without changing size sm.def("array_reshape2", [](py::array_t<double> a) { - const ssize_t dim_sz = (ssize_t)std::sqrt(a.size()); + 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}); @@ -382,9 +394,45 @@ TEST_SUBMODULE(numpy_array, sm) { return a; }); -#if PY_MAJOR_VERSION >= 3 - sm.def("index_using_ellipsis", [](py::array a) { - return a[py::make_tuple(0, py::ellipsis(), 0)]; - }); -#endif + sm.def("index_using_ellipsis", [](py::array a) { + return a[py::make_tuple(0, py::ellipsis(), 0)]; + }); + + // test_argument_conversions + sm.def("accept_double", + [](py::array_t<double, 0>) {}, + py::arg("a")); + sm.def("accept_double_forcecast", + [](py::array_t<double, py::array::forcecast>) {}, + py::arg("a")); + sm.def("accept_double_c_style", + [](py::array_t<double, py::array::c_style>) {}, + py::arg("a")); + sm.def("accept_double_c_style_forcecast", + [](py::array_t<double, py::array::forcecast | py::array::c_style>) {}, + py::arg("a")); + sm.def("accept_double_f_style", + [](py::array_t<double, py::array::f_style>) {}, + py::arg("a")); + sm.def("accept_double_f_style_forcecast", + [](py::array_t<double, py::array::forcecast | py::array::f_style>) {}, + py::arg("a")); + sm.def("accept_double_noconvert", + [](py::array_t<double, 0>) {}, + py::arg("a").noconvert()); + sm.def("accept_double_forcecast_noconvert", + [](py::array_t<double, py::array::forcecast>) {}, + py::arg("a").noconvert()); + sm.def("accept_double_c_style_noconvert", + [](py::array_t<double, py::array::c_style>) {}, + py::arg("a").noconvert()); + sm.def("accept_double_c_style_forcecast_noconvert", + [](py::array_t<double, py::array::forcecast | py::array::c_style>) {}, + py::arg("a").noconvert()); + sm.def("accept_double_f_style_noconvert", + [](py::array_t<double, py::array::f_style>) {}, + py::arg("a").noconvert()); + sm.def("accept_double_f_style_forcecast_noconvert", + [](py::array_t<double, py::array::forcecast | py::array::f_style>) {}, + py::arg("a").noconvert()); } diff --git a/3rdparty/pybind11/tests/test_numpy_array.py b/3rdparty/pybind11/tests/test_numpy_array.py index d0a6324d..02f3ecfc 100644 --- a/3rdparty/pybind11/tests/test_numpy_array.py +++ b/3rdparty/pybind11/tests/test_numpy_array.py @@ -1,10 +1,11 @@ +# -*- coding: utf-8 -*- import pytest -from pybind11_tests import numpy_array as m -pytestmark = pytest.requires_numpy +import env # noqa: F401 + +from pybind11_tests import numpy_array as m -with pytest.suppress(ImportError): - import numpy as np +np = pytest.importorskip("numpy") def test_dtypes(): @@ -18,33 +19,36 @@ def test_dtypes(): print(check) 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)) + print( + "NOTE: typenum mismatch for {}: {} != {}".format( + check, check.numpy.num, check.pybind11.num + ) + ) -@pytest.fixture(scope='function') +@pytest.fixture(scope="function") def arr(): - return np.array([[1, 2, 3], [4, 5, 6]], '=u2') + return np.array([[1, 2, 3], [4, 5, 6]], "=u2") def test_array_attributes(): - a = np.array(0, 'f8') + a = np.array(0, "f8") assert m.ndim(a) == 0 assert all(m.shape(a) == []) assert all(m.strides(a) == []) with pytest.raises(IndexError) as excinfo: m.shape(a, 0) - assert str(excinfo.value) == 'invalid axis: 0 (ndim = 0)' + assert str(excinfo.value) == "invalid axis: 0 (ndim = 0)" with pytest.raises(IndexError) as excinfo: m.strides(a, 0) - assert str(excinfo.value) == 'invalid axis: 0 (ndim = 0)' + assert str(excinfo.value) == "invalid axis: 0 (ndim = 0)" assert m.writeable(a) assert m.size(a) == 1 assert m.itemsize(a) == 8 assert m.nbytes(a) == 8 assert m.owndata(a) - a = np.array([[1, 2, 3], [4, 5, 6]], 'u2').view() + a = np.array([[1, 2, 3], [4, 5, 6]], "u2").view() a.flags.writeable = False assert m.ndim(a) == 2 assert all(m.shape(a) == [2, 3]) @@ -55,10 +59,10 @@ def test_array_attributes(): assert m.strides(a, 1) == 2 with pytest.raises(IndexError) as excinfo: m.shape(a, 2) - assert str(excinfo.value) == 'invalid axis: 2 (ndim = 2)' + assert str(excinfo.value) == "invalid axis: 2 (ndim = 2)" with pytest.raises(IndexError) as excinfo: m.strides(a, 2) - assert str(excinfo.value) == 'invalid axis: 2 (ndim = 2)' + assert str(excinfo.value) == "invalid axis: 2 (ndim = 2)" assert not m.writeable(a) assert m.size(a) == 6 assert m.itemsize(a) == 2 @@ -66,7 +70,9 @@ def test_array_attributes(): assert not m.owndata(a) -@pytest.mark.parametrize('args, ret', [([], 0), ([0], 0), ([1], 3), ([0, 1], 1), ([1, 2], 5)]) +@pytest.mark.parametrize( + "args, ret", [([], 0), ([0], 0), ([1], 3), ([0, 1], 1), ([1, 2], 5)] +) def test_index_offset(arr, args, ret): assert m.index_at(arr, *args) == ret assert m.index_at_t(arr, *args) == ret @@ -75,31 +81,46 @@ def test_index_offset(arr, args, ret): def test_dim_check_fail(arr): - for func in (m.index_at, m.index_at_t, m.offset_at, m.offset_at_t, m.data, m.data_t, - m.mutate_data, m.mutate_data_t): + for func in ( + m.index_at, + m.index_at_t, + m.offset_at, + m.offset_at_t, + m.data, + m.data_t, + m.mutate_data, + m.mutate_data_t, + ): with pytest.raises(IndexError) as excinfo: func(arr, 1, 2, 3) - assert str(excinfo.value) == 'too many indices for an array: 3 (ndim = 2)' - - -@pytest.mark.parametrize('args, ret', - [([], [1, 2, 3, 4, 5, 6]), - ([1], [4, 5, 6]), - ([0, 1], [2, 3, 4, 5, 6]), - ([1, 2], [6])]) + assert str(excinfo.value) == "too many indices for an array: 3 (ndim = 2)" + + +@pytest.mark.parametrize( + "args, ret", + [ + ([], [1, 2, 3, 4, 5, 6]), + ([1], [4, 5, 6]), + ([0, 1], [2, 3, 4, 5, 6]), + ([1, 2], [6]), + ], +) def test_data(arr, args, ret): from sys import byteorder + assert all(m.data_t(arr, *args) == ret) - assert all(m.data(arr, *args)[(0 if byteorder == 'little' else 1)::2] == ret) - assert all(m.data(arr, *args)[(1 if byteorder == 'little' else 0)::2] == 0) + assert all(m.data(arr, *args)[(0 if byteorder == "little" else 1) :: 2] == ret) + assert all(m.data(arr, *args)[(1 if byteorder == "little" else 0) :: 2] == 0) -@pytest.mark.parametrize('dim', [0, 1, 3]) +@pytest.mark.parametrize("dim", [0, 1, 3]) 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) == "index dimension mismatch: {} (ndim = 2)".format( + dim + ) def test_at(arr): @@ -112,10 +133,14 @@ def test_at(arr): def test_mutate_readonly(arr): arr.flags.writeable = False - for func, args in (m.mutate_data, ()), (m.mutate_data_t, ()), (m.mutate_at_t, (0, 0)): + for func, args in ( + (m.mutate_data, ()), + (m.mutate_data_t, ()), + (m.mutate_at_t, (0, 0)), + ): with pytest.raises(ValueError) as excinfo: func(arr, *args) - assert str(excinfo.value) == 'array is not writeable' + assert str(excinfo.value) == "array is not writeable" def test_mutate_data(arr): @@ -133,14 +158,22 @@ def test_mutate_data(arr): def test_bounds_check(arr): - for func in (m.index_at, m.index_at_t, m.data, m.data_t, - m.mutate_data, m.mutate_data_t, m.at_t, m.mutate_at_t): + for func in ( + m.index_at, + m.index_at_t, + m.data, + m.data_t, + m.mutate_data, + m.mutate_data_t, + m.at_t, + m.mutate_at_t, + ): with pytest.raises(IndexError) as excinfo: func(arr, 2, 0) - assert str(excinfo.value) == 'index 2 is out of bounds for axis 0 with size 2' + assert str(excinfo.value) == "index 2 is out of bounds for axis 0 with size 2" with pytest.raises(IndexError) as excinfo: func(arr, 0, 4) - assert str(excinfo.value) == 'index 4 is out of bounds for axis 1 with size 3' + assert str(excinfo.value) == "index 4 is out of bounds for axis 1 with size 3" def test_make_c_f_array(): @@ -162,10 +195,11 @@ 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 - assert a.__array_interface__['data'][0] == b.__array_interface__['data'][0] + assert a.__array_interface__["data"][0] == b.__array_interface__["data"][0] assert a.shape == b.shape assert a.strides == b.strides assert a.flags.c_contiguous == b.flags.c_contiguous @@ -188,12 +222,12 @@ def test_wrap(): a2 = m.wrap(a1) assert_references(a1, a2) - a1 = np.array([[1, 2], [3, 4]], dtype=np.float32, order='F') + a1 = np.array([[1, 2], [3, 4]], dtype=np.float32, order="F") assert a1.flags.owndata and a1.base is None a2 = m.wrap(a1) assert_references(a1, a2) - a1 = np.array([[1, 2], [3, 4]], dtype=np.float32, order='C') + a1 = np.array([[1, 2], [3, 4]], dtype=np.float32, order="C") a1.flags.writeable = False a2 = m.wrap(a1) assert_references(a1, a2) @@ -223,11 +257,14 @@ def test_numpy_view(capture): assert np.all(ac_view_1 == np.array([1, 2], dtype=np.int32)) del ac pytest.gc_collect() - assert capture == """ + assert ( + capture + == """ ArrayClass() ArrayClass::numpy_view() ArrayClass::numpy_view() """ + ) ac_view_1[0] = 4 ac_view_1[1] = 3 assert ac_view_2[0] == 4 @@ -237,12 +274,14 @@ def test_numpy_view(capture): del ac_view_2 pytest.gc_collect() pytest.gc_collect() - assert capture == """ + assert ( + capture + == """ ~ArrayClass() """ + ) -@pytest.unsupported_on_pypy def test_cast_numpy_int64_to_uint64(): m.function_taking_uint64(123) m.function_taking_uint64(np.uint64(123)) @@ -271,89 +310,94 @@ def test_constructors(): def test_overload_resolution(msg): # Exact overload matches: - assert m.overloaded(np.array([1], dtype='float64')) == 'double' - assert m.overloaded(np.array([1], dtype='float32')) == 'float' - assert m.overloaded(np.array([1], dtype='ushort')) == 'unsigned short' - assert m.overloaded(np.array([1], dtype='intc')) == 'int' - assert m.overloaded(np.array([1], dtype='longlong')) == 'long long' - assert m.overloaded(np.array([1], dtype='complex')) == 'double complex' - assert m.overloaded(np.array([1], dtype='csingle')) == 'float complex' + assert m.overloaded(np.array([1], dtype="float64")) == "double" + assert m.overloaded(np.array([1], dtype="float32")) == "float" + assert m.overloaded(np.array([1], dtype="ushort")) == "unsigned short" + assert m.overloaded(np.array([1], dtype="intc")) == "int" + assert m.overloaded(np.array([1], dtype="longlong")) == "long long" + assert m.overloaded(np.array([1], dtype="complex")) == "double complex" + assert m.overloaded(np.array([1], dtype="csingle")) == "float complex" # No exact match, should call first convertible version: - assert m.overloaded(np.array([1], dtype='uint8')) == 'double' + assert m.overloaded(np.array([1], dtype="uint8")) == "double" with pytest.raises(TypeError) as excinfo: m.overloaded("not an array") - assert msg(excinfo.value) == """ + assert ( + msg(excinfo.value) + == """ overloaded(): incompatible function arguments. The following argument types are supported: - 1. (arg0: numpy.ndarray[float64]) -> str - 2. (arg0: numpy.ndarray[float32]) -> str - 3. (arg0: numpy.ndarray[int32]) -> str - 4. (arg0: numpy.ndarray[uint16]) -> str - 5. (arg0: numpy.ndarray[int64]) -> str - 6. (arg0: numpy.ndarray[complex128]) -> str - 7. (arg0: numpy.ndarray[complex64]) -> str + 1. (arg0: numpy.ndarray[numpy.float64]) -> str + 2. (arg0: numpy.ndarray[numpy.float32]) -> str + 3. (arg0: numpy.ndarray[numpy.int32]) -> str + 4. (arg0: numpy.ndarray[numpy.uint16]) -> str + 5. (arg0: numpy.ndarray[numpy.int64]) -> str + 6. (arg0: numpy.ndarray[numpy.complex128]) -> str + 7. (arg0: numpy.ndarray[numpy.complex64]) -> str Invoked with: 'not an array' """ + ) - assert m.overloaded2(np.array([1], dtype='float64')) == 'double' - assert m.overloaded2(np.array([1], dtype='float32')) == 'float' - assert m.overloaded2(np.array([1], dtype='complex64')) == 'float complex' - assert m.overloaded2(np.array([1], dtype='complex128')) == 'double complex' - assert m.overloaded2(np.array([1], dtype='float32')) == 'float' + assert m.overloaded2(np.array([1], dtype="float64")) == "double" + assert m.overloaded2(np.array([1], dtype="float32")) == "float" + assert m.overloaded2(np.array([1], dtype="complex64")) == "float complex" + assert m.overloaded2(np.array([1], dtype="complex128")) == "double complex" + assert m.overloaded2(np.array([1], dtype="float32")) == "float" - assert m.overloaded3(np.array([1], dtype='float64')) == 'double' - assert m.overloaded3(np.array([1], dtype='intc')) == 'int' + assert m.overloaded3(np.array([1], dtype="float64")) == "double" + assert m.overloaded3(np.array([1], dtype="intc")) == "int" expected_exc = """ overloaded3(): incompatible function arguments. The following argument types are supported: - 1. (arg0: numpy.ndarray[int32]) -> str - 2. (arg0: numpy.ndarray[float64]) -> str + 1. (arg0: numpy.ndarray[numpy.int32]) -> str + 2. (arg0: numpy.ndarray[numpy.float64]) -> str Invoked with: """ with pytest.raises(TypeError) as excinfo: - m.overloaded3(np.array([1], dtype='uintc')) - assert msg(excinfo.value) == expected_exc + repr(np.array([1], dtype='uint32')) + m.overloaded3(np.array([1], dtype="uintc")) + assert msg(excinfo.value) == expected_exc + repr(np.array([1], dtype="uint32")) with pytest.raises(TypeError) as excinfo: - m.overloaded3(np.array([1], dtype='float32')) - assert msg(excinfo.value) == expected_exc + repr(np.array([1.], dtype='float32')) + m.overloaded3(np.array([1], dtype="float32")) + assert msg(excinfo.value) == expected_exc + repr(np.array([1.0], dtype="float32")) with pytest.raises(TypeError) as excinfo: - m.overloaded3(np.array([1], dtype='complex')) - assert msg(excinfo.value) == expected_exc + repr(np.array([1. + 0.j])) + m.overloaded3(np.array([1], dtype="complex")) + assert msg(excinfo.value) == expected_exc + repr(np.array([1.0 + 0.0j])) # Exact matches: - assert m.overloaded4(np.array([1], dtype='double')) == 'double' - assert m.overloaded4(np.array([1], dtype='longlong')) == 'long long' + assert m.overloaded4(np.array([1], dtype="double")) == "double" + assert m.overloaded4(np.array([1], dtype="longlong")) == "long long" # Non-exact matches requiring conversion. Since float to integer isn't a # save conversion, it should go to the double overload, but short can go to # either (and so should end up on the first-registered, the long long). - assert m.overloaded4(np.array([1], dtype='float32')) == 'double' - assert m.overloaded4(np.array([1], dtype='short')) == 'long long' + assert m.overloaded4(np.array([1], dtype="float32")) == "double" + assert m.overloaded4(np.array([1], dtype="short")) == "long long" - assert m.overloaded5(np.array([1], dtype='double')) == 'double' - assert m.overloaded5(np.array([1], dtype='uintc')) == 'unsigned int' - assert m.overloaded5(np.array([1], dtype='float32')) == 'unsigned int' + assert m.overloaded5(np.array([1], dtype="double")) == "double" + assert m.overloaded5(np.array([1], dtype="uintc")) == "unsigned int" + assert m.overloaded5(np.array([1], dtype="float32")) == "unsigned int" def test_greedy_string_overload(): """Tests fix for #685 - ndarray shouldn't go to std::string overload""" assert m.issue685("abc") == "string" - assert m.issue685(np.array([97, 98, 99], dtype='b')) == "array" + assert m.issue685(np.array([97, 98, 99], dtype="b")) == "array" assert m.issue685(123) == "other" def test_array_unchecked_fixed_dims(msg): - z1 = np.array([[1, 2], [3, 4]], dtype='float64') + z1 = np.array([[1, 2], [3, 4]], dtype="float64") m.proxy_add2(z1, 10) assert np.all(z1 == [[11, 12], [13, 14]]) with pytest.raises(ValueError) as excinfo: - m.proxy_add2(np.array([1., 2, 3]), 5.0) - assert msg(excinfo.value) == "array has incorrect number of dimensions: 1; expected 2" + m.proxy_add2(np.array([1.0, 2, 3]), 5.0) + assert ( + msg(excinfo.value) == "array has incorrect number of dimensions: 1; expected 2" + ) - expect_c = np.ndarray(shape=(3, 3, 3), buffer=np.array(range(3, 30)), dtype='int') + expect_c = np.ndarray(shape=(3, 3, 3), buffer=np.array(range(3, 30)), dtype="int") assert np.all(m.proxy_init3(3.0) == expect_c) expect_f = np.transpose(expect_c) assert np.all(m.proxy_init3F(3.0) == expect_f) @@ -364,13 +408,16 @@ def test_array_unchecked_fixed_dims(msg): assert m.proxy_auxiliaries2(z1) == [11, 11, True, 2, 8, 2, 2, 4, 32] assert m.proxy_auxiliaries2(z1) == m.array_auxiliaries2(z1) + assert m.proxy_auxiliaries1_const_ref(z1[0, :]) + assert m.proxy_auxiliaries2_const_ref(z1) + def test_array_unchecked_dyn_dims(msg): - z1 = np.array([[1, 2], [3, 4]], dtype='float64') + z1 = np.array([[1, 2], [3, 4]], dtype="float64") m.proxy_add2_dyn(z1, 10) assert np.all(z1 == [[11, 12], [13, 14]]) - expect_c = np.ndarray(shape=(3, 3, 3), buffer=np.array(range(3, 30)), dtype='int') + expect_c = np.ndarray(shape=(3, 3, 3), buffer=np.array(range(3, 30)), dtype="int") assert np.all(m.proxy_init3_dyn(3.0) == expect_c) assert m.proxy_auxiliaries2_dyn(z1) == [11, 11, True, 2, 8, 2, 2, 4, 32] @@ -380,15 +427,15 @@ def test_array_unchecked_dyn_dims(msg): def test_array_failure(): with pytest.raises(ValueError) as excinfo: m.array_fail_test() - assert str(excinfo.value) == 'cannot create a pybind11::array from a nullptr' + assert str(excinfo.value) == "cannot create a pybind11::array from a nullptr" with pytest.raises(ValueError) as excinfo: m.array_t_fail_test() - assert str(excinfo.value) == 'cannot create a pybind11::array_t from a nullptr' + assert str(excinfo.value) == "cannot create a pybind11::array_t from a nullptr" with pytest.raises(ValueError) as excinfo: m.array_fail_test_negative_size() - assert str(excinfo.value) == 'negative dimensions are not allowed' + assert str(excinfo.value) == "negative dimensions are not allowed" def test_initializer_list(): @@ -399,46 +446,93 @@ def test_initializer_list(): def test_array_resize(msg): - a = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9], dtype='float64') + a = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9], dtype="float64") m.array_reshape2(a) - assert(a.size == 9) - assert(np.all(a == [[1, 2, 3], [4, 5, 6], [7, 8, 9]])) + assert a.size == 9 + assert np.all(a == [[1, 2, 3], [4, 5, 6], [7, 8, 9]]) # total size change should succced with refcheck off m.array_resize3(a, 4, False) - assert(a.size == 64) + assert a.size == 64 # ... and fail with refcheck on try: m.array_resize3(a, 3, True) except ValueError as e: - assert(str(e).startswith("cannot resize an array")) + assert str(e).startswith("cannot resize an array") # transposed array doesn't own data b = a.transpose() try: m.array_resize3(b, 3, False) except ValueError as e: - assert(str(e).startswith("cannot resize this array: it does not own its data")) + assert str(e).startswith("cannot resize this array: it does not own its data") # ... but reshape should be fine m.array_reshape2(b) - assert(b.shape == (8, 8)) + assert b.shape == (8, 8) -@pytest.unsupported_on_pypy +@pytest.mark.xfail("env.PYPY") def test_array_create_and_resize(msg): a = m.create_and_resize(2) - assert(a.size == 4) - assert(np.all(a == 42.)) + assert a.size == 4 + assert np.all(a == 42.0) -@pytest.unsupported_on_py2 def test_index_using_ellipsis(): a = m.index_using_ellipsis(np.zeros((5, 6, 7))) assert a.shape == (6,) -@pytest.unsupported_on_pypy +@pytest.mark.parametrize("forcecast", [False, True]) +@pytest.mark.parametrize("contiguity", [None, "C", "F"]) +@pytest.mark.parametrize("noconvert", [False, True]) +@pytest.mark.filterwarnings( + "ignore:Casting complex values to real discards the imaginary part:numpy.ComplexWarning" +) +def test_argument_conversions(forcecast, contiguity, noconvert): + function_name = "accept_double" + if contiguity == "C": + function_name += "_c_style" + elif contiguity == "F": + function_name += "_f_style" + if forcecast: + function_name += "_forcecast" + if noconvert: + function_name += "_noconvert" + function = getattr(m, function_name) + + for dtype in [np.dtype("float32"), np.dtype("float64"), np.dtype("complex128")]: + for order in ["C", "F"]: + for shape in [(2, 2), (1, 3, 1, 1), (1, 1, 1), (0,)]: + if not noconvert: + # If noconvert is not passed, only complex128 needs to be truncated and + # "cannot be safely obtained". So without `forcecast`, the argument shouldn't + # be accepted. + should_raise = dtype.name == "complex128" and not forcecast + else: + # If noconvert is passed, only float64 and the matching order is accepted. + # If at most one dimension has a size greater than 1, the array is also + # trivially contiguous. + trivially_contiguous = sum(1 for d in shape if d > 1) <= 1 + should_raise = dtype.name != "float64" or ( + contiguity is not None + and contiguity != order + and not trivially_contiguous + ) + + array = np.zeros(shape, dtype=dtype, order=order) + if not should_raise: + function(array) + else: + with pytest.raises( + TypeError, match="incompatible function arguments" + ): + function(array) + + +@pytest.mark.xfail("env.PYPY") def test_dtype_refcount_leak(): from sys import getrefcount + dtype = np.dtype(np.float_) a = np.array([1], dtype=dtype) before = getrefcount(dtype) diff --git a/3rdparty/pybind11/tests/test_numpy_dtypes.cpp b/3rdparty/pybind11/tests/test_numpy_dtypes.cpp index 467e0253..b2e5e607 100644 --- a/3rdparty/pybind11/tests/test_numpy_dtypes.cpp +++ b/3rdparty/pybind11/tests/test_numpy_dtypes.cpp @@ -168,7 +168,7 @@ py::list print_recarray(py::array_t<S, 0> arr) { const auto req = arr.request(); const auto ptr = static_cast<S*>(req.ptr); auto l = py::list(); - for (ssize_t i = 0; i < req.size; i++) { + for (py::ssize_t i = 0; i < req.size; i++) { std::stringstream ss; ss << ptr[i]; l.append(py::str(ss.str())); @@ -180,8 +180,8 @@ py::array_t<int32_t, 0> test_array_ctors(int i) { using arr_t = py::array_t<int32_t, 0>; std::vector<int32_t> data { 1, 2, 3, 4, 5, 6 }; - std::vector<ssize_t> shape { 3, 2 }; - std::vector<ssize_t> strides { 8, 4 }; + std::vector<py::ssize_t> shape { 3, 2 }; + std::vector<py::ssize_t> strides { 8, 4 }; auto ptr = data.data(); auto vptr = (void *) ptr; @@ -255,11 +255,30 @@ struct A {}; struct B {}; TEST_SUBMODULE(numpy_dtypes, m) { - try { py::module::import("numpy"); } + try { py::module_::import("numpy"); } catch (...) { return; } // typeinfo may be registered before the dtype descriptor for scalar casts to work... - py::class_<SimpleStruct>(m, "SimpleStruct"); + py::class_<SimpleStruct>(m, "SimpleStruct") + // Explicit construct to ensure zero-valued initialization. + .def(py::init([]() { return SimpleStruct(); })) + .def_readwrite("bool_", &SimpleStruct::bool_) + .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) { + if (py::len(tup) != 4) { + throw py::cast_error("Invalid size"); + } + return SimpleStruct{ + tup[0].cast<bool>(), + tup[1].cast<uint32_t>(), + tup[2].cast<float>(), + tup[3].cast<long double>()}; + }); PYBIND11_NUMPY_DTYPE(SimpleStruct, bool_, uint_, float_, ldbl_); PYBIND11_NUMPY_DTYPE(SimpleStructReordered, bool_, uint_, float_, ldbl_); @@ -379,7 +398,7 @@ TEST_SUBMODULE(numpy_dtypes, m) { if (non_empty) { auto req = arr.request(); auto ptr = static_cast<StringStruct*>(req.ptr); - for (ssize_t i = 0; i < req.size * req.itemsize; i++) + for (py::ssize_t i = 0; i < req.size * req.itemsize; i++) static_cast<char*>(req.ptr)[i] = 0; ptr[1].a[0] = 'a'; ptr[1].b[0] = 'a'; ptr[2].a[0] = 'a'; ptr[2].b[0] = 'a'; @@ -462,10 +481,16 @@ TEST_SUBMODULE(numpy_dtypes, m) { m.def("buffer_to_dtype", [](py::buffer& buf) { return py::dtype(buf.request()); }); // test_scalar_conversion - m.def("f_simple", [](SimpleStruct s) { return s.uint_ * 10; }); + auto f_simple = [](SimpleStruct s) { return s.uint_ * 10; }; + m.def("f_simple", f_simple); m.def("f_packed", [](PackedStruct s) { return s.uint_ * 10; }); m.def("f_nested", [](NestedStruct s) { return s.a.uint_ * 10; }); + // test_vectorize + m.def("f_simple_vectorized", py::vectorize(f_simple)); + auto f_simple_pass_thru = [](SimpleStruct s) { return s; }; + 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_); }); diff --git a/3rdparty/pybind11/tests/test_numpy_dtypes.py b/3rdparty/pybind11/tests/test_numpy_dtypes.py index 2e638851..f56b776a 100644 --- a/3rdparty/pybind11/tests/test_numpy_dtypes.py +++ b/3rdparty/pybind11/tests/test_numpy_dtypes.py @@ -1,64 +1,80 @@ +# -*- coding: utf-8 -*- import re + import pytest -from pybind11_tests import numpy_dtypes as m -pytestmark = pytest.requires_numpy +import env # noqa: F401 -with pytest.suppress(ImportError): - import numpy as np +from pybind11_tests import numpy_dtypes as m +np = pytest.importorskip("numpy") -@pytest.fixture(scope='module') + +@pytest.fixture(scope="module") def simple_dtype(): - ld = np.dtype('longdouble') - return np.dtype({'names': ['bool_', 'uint_', 'float_', 'ldbl_'], - 'formats': ['?', 'u4', 'f4', 'f{}'.format(ld.itemsize)], - 'offsets': [0, 4, 8, (16 if ld.alignment > 4 else 12)]}) + ld = np.dtype("longdouble") + return np.dtype( + { + "names": ["bool_", "uint_", "float_", "ldbl_"], + "formats": ["?", "u4", "f4", "f{}".format(ld.itemsize)], + "offsets": [0, 4, 8, (16 if ld.alignment > 4 else 12)], + } + ) -@pytest.fixture(scope='module') +@pytest.fixture(scope="module") def packed_dtype(): - return np.dtype([('bool_', '?'), ('uint_', 'u4'), ('float_', 'f4'), ('ldbl_', 'g')]) + return np.dtype([("bool_", "?"), ("uint_", "u4"), ("float_", "f4"), ("ldbl_", "g")]) def dt_fmt(): from sys import byteorder - e = '<' if byteorder == 'little' else '>' - return ("{{'names':['bool_','uint_','float_','ldbl_']," - " 'formats':['?','" + e + "u4','" + e + "f4','" + e + "f{}']," - " 'offsets':[0,4,8,{}], 'itemsize':{}}}") + + e = "<" if byteorder == "little" else ">" + return ( + "{{'names':['bool_','uint_','float_','ldbl_']," + " 'formats':['?','" + e + "u4','" + e + "f4','" + e + "f{}']," + " 'offsets':[0,4,8,{}], 'itemsize':{}}}" + ) def simple_dtype_fmt(): - ld = np.dtype('longdouble') + ld = np.dtype("longdouble") simple_ld_off = 12 + 4 * (ld.alignment > 4) return dt_fmt().format(ld.itemsize, simple_ld_off, simple_ld_off + ld.itemsize) def packed_dtype_fmt(): from sys import byteorder + return "[('bool_', '?'), ('uint_', '{e}u4'), ('float_', '{e}f4'), ('ldbl_', '{e}f{}')]".format( - np.dtype('longdouble').itemsize, e='<' if byteorder == 'little' else '>') + np.dtype("longdouble").itemsize, e="<" if byteorder == "little" else ">" + ) def partial_ld_offset(): - return 12 + 4 * (np.dtype('uint64').alignment > 4) + 8 + 8 * ( - np.dtype('longdouble').alignment > 8) + return ( + 12 + + 4 * (np.dtype("uint64").alignment > 4) + + 8 + + 8 * (np.dtype("longdouble").alignment > 8) + ) def partial_dtype_fmt(): - ld = np.dtype('longdouble') + ld = np.dtype("longdouble") partial_ld_off = partial_ld_offset() return dt_fmt().format(ld.itemsize, partial_ld_off, partial_ld_off + ld.itemsize) def partial_nested_fmt(): - ld = np.dtype('longdouble') + 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_dtype_fmt(), partial_nested_off, partial_nested_size) + partial_dtype_fmt(), partial_nested_off, partial_nested_size + ) def assert_equal(actual, expected_data, expected_dtype): @@ -68,15 +84,19 @@ def assert_equal(actual, expected_data, expected_dtype): def test_format_descriptors(): with pytest.raises(RuntimeError) as excinfo: m.get_format_unbound() - assert re.match('^NumPy type info missing for .*UnboundStruct.*$', str(excinfo.value)) + assert re.match( + "^NumPy type info missing for .*UnboundStruct.*$", str(excinfo.value) + ) - ld = np.dtype('longdouble') - ldbl_fmt = ('4x' if ld.alignment > 4 else '') + ld.char + ld = np.dtype("longdouble") + 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') - partial_fmt = ("^T{?:bool_:3xI:uint_:f:float_:" + - str(4 * (dbl.alignment > 4) + dbl.itemsize + 8 * (ld.alignment > 8)) + - "xg:ldbl_:}") + dbl = np.dtype("double") + partial_fmt = ( + "^T{?:bool_:3xI:uint_:f:float_:" + + str(4 * (dbl.alignment > 4) + dbl.itemsize + 8 * (ld.alignment > 8)) + + "xg:ldbl_:}" + ) nested_extra = str(max(8, ld.alignment)) assert m.print_format_descriptors() == [ ss_fmt, @@ -86,14 +106,15 @@ def test_format_descriptors(): "^T{" + nested_extra + "x" + partial_fmt + ":a:" + nested_extra + "x}", "^T{3s:a:3s:b:}", "^T{(3)4s:a:(2)i:b:(3)B:c:1x(4, 2)f:d:}", - '^T{q:e1:B:e2:}', - '^T{Zf:cflt:Zd:cdbl:}' + "^T{q:e1:B:e2:}", + "^T{Zf:cflt:Zd:cdbl:}", ] def test_dtype(simple_dtype): from sys import byteorder - e = '<' if byteorder == 'little' else '>' + + e = "<" if byteorder == "little" else ">" assert m.print_dtypes() == [ simple_dtype_fmt(), @@ -102,30 +123,60 @@ def test_dtype(simple_dtype): partial_dtype_fmt(), partial_nested_fmt(), "[('a', 'S3'), ('b', 'S3')]", - ("{{'names':['a','b','c','d'], " + - "'formats':[('S4', (3,)),('" + e + "i4', (2,)),('u1', (3,)),('" + e + "f4', (4, 2))], " + - "'offsets':[0,12,20,24], 'itemsize':56}}").format(e=e), + ( + "{{'names':['a','b','c','d'], " + + "'formats':[('S4', (3,)),('" + + e + + "i4', (2,)),('u1', (3,)),('" + + e + + "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')]" + "[('cflt', '" + e + "c8'), ('cdbl', '" + e + "c16')]", ] - d1 = np.dtype({'names': ['a', 'b'], 'formats': ['int32', 'float64'], - 'offsets': [1, 10], 'itemsize': 20}) - d2 = np.dtype([('a', 'i4'), ('b', 'f4')]) - assert m.test_dtype_ctors() == [np.dtype('int32'), np.dtype('float64'), - np.dtype('bool'), d1, d1, np.dtype('uint32'), d2] + d1 = np.dtype( + { + "names": ["a", "b"], + "formats": ["int32", "float64"], + "offsets": [1, 10], + "itemsize": 20, + } + ) + d2 = np.dtype([("a", "i4"), ("b", "f4")]) + assert m.test_dtype_ctors() == [ + np.dtype("int32"), + np.dtype("float64"), + np.dtype("bool"), + d1, + d1, + np.dtype("uint32"), + d2, + ] - assert m.test_dtype_methods() == [np.dtype('int32'), simple_dtype, False, True, - np.dtype('int32').itemsize, simple_dtype.itemsize] + assert m.test_dtype_methods() == [ + np.dtype("int32"), + simple_dtype, + False, + True, + np.dtype("int32").itemsize, + simple_dtype.itemsize, + ] - assert m.trailing_padding_dtype() == m.buffer_to_dtype(np.zeros(1, m.trailing_padding_dtype())) + assert m.trailing_padding_dtype() == m.buffer_to_dtype( + np.zeros(1, m.trailing_padding_dtype()) + ) 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)] - for func, dtype in [(m.create_rec_simple, simple_dtype), (m.create_rec_packed, packed_dtype)]: + for func, dtype in [ + (m.create_rec_simple, simple_dtype), + (m.create_rec_packed, packed_dtype), + ]: arr = func(0) assert arr.dtype == dtype assert_equal(arr, [], simple_dtype) @@ -136,20 +187,24 @@ def test_recarray(simple_dtype, packed_dtype): assert_equal(arr, elements, simple_dtype) assert_equal(arr, elements, packed_dtype) + # Show what recarray's look like in NumPy. + assert type(arr[0]) == np.void + assert type(arr[0].item()) == tuple + if dtype == simple_dtype: assert m.print_rec_simple(arr) == [ "s:0,0,0,-0", "s:1,1,1.5,-2.5", - "s:0,2,3,-5" + "s:0,2,3,-5", ] else: assert m.print_rec_packed(arr) == [ "p:0,0,0,-0", "p:1,1,1.5,-2.5", - "p:0,2,3,-5" + "p:0,2,3,-5", ] - nested_dtype = np.dtype([('a', simple_dtype), ('b', packed_dtype)]) + nested_dtype = np.dtype([("a", simple_dtype), ("b", packed_dtype)]) arr = m.create_rec_nested(0) assert arr.dtype == nested_dtype @@ -157,33 +212,39 @@ def test_recarray(simple_dtype, packed_dtype): arr = m.create_rec_nested(3) assert arr.dtype == nested_dtype - assert_equal(arr, [((False, 0, 0.0, -0.0), (True, 1, 1.5, -2.5)), - ((True, 1, 1.5, -2.5), (False, 2, 3.0, -5.0)), - ((False, 2, 3.0, -5.0), (True, 3, 4.5, -7.5))], nested_dtype) + assert_equal( + arr, + [ + ((False, 0, 0.0, -0.0), (True, 1, 1.5, -2.5)), + ((True, 1, 1.5, -2.5), (False, 2, 3.0, -5.0)), + ((False, 2, 3.0, -5.0), (True, 3, 4.5, -7.5)), + ], + nested_dtype, + ) assert m.print_rec_nested(arr) == [ "n:a=s:0,0,0,-0;b=p:1,1,1.5,-2.5", "n:a=s:1,1,1.5,-2.5;b=p:0,2,3,-5", - "n:a=s:0,2,3,-5;b=p:1,3,4.5,-7.5" + "n:a=s:0,2,3,-5;b=p:1,3,4.5,-7.5", ] arr = m.create_rec_partial(3) assert str(arr.dtype) == partial_dtype_fmt() partial_dtype = arr.dtype - assert '' not in arr.dtype.fields + assert "" not in arr.dtype.fields assert partial_dtype.itemsize > simple_dtype.itemsize assert_equal(arr, elements, simple_dtype) assert_equal(arr, elements, packed_dtype) arr = m.create_rec_partial_nested(3) assert str(arr.dtype) == partial_nested_fmt() - assert '' not in arr.dtype.fields - assert '' not in arr.dtype.fields['a'][0].fields + assert "" not in arr.dtype.fields + assert "" not in arr.dtype.fields["a"][0].fields assert arr.dtype.itemsize > partial_dtype.itemsize - np.testing.assert_equal(arr['a'], m.create_rec_partial(3)) + np.testing.assert_equal(arr["a"], m.create_rec_partial(3)) def test_array_constructors(): - data = np.arange(1, 7, dtype='int32') + data = np.arange(1, 7, dtype="int32") for i in range(8): np.testing.assert_array_equal(m.test_array_ctors(10 + i), data.reshape((3, 2))) np.testing.assert_array_equal(m.test_array_ctors(20 + i), data.reshape((3, 2))) @@ -199,82 +260,92 @@ def test_string_array(): "a='',b=''", "a='a',b='a'", "a='ab',b='ab'", - "a='abc',b='abc'" + "a='abc',b='abc'", ] dtype = arr.dtype - assert arr['a'].tolist() == [b'', b'a', b'ab', b'abc'] - assert arr['b'].tolist() == [b'', b'a', b'ab', b'abc'] + assert arr["a"].tolist() == [b"", b"a", b"ab", b"abc"] + assert arr["b"].tolist() == [b"", b"a", b"ab", b"abc"] arr = m.create_string_array(False) assert dtype == arr.dtype def test_array_array(): from sys import byteorder - e = '<' if byteorder == 'little' else '>' + + e = "<" if byteorder == "little" else ">" arr = m.create_array_array(3) assert str(arr.dtype) == ( - "{{'names':['a','b','c','d'], " + - "'formats':[('S4', (3,)),('" + e + "i4', (2,)),('u1', (3,)),('{e}f4', (4, 2))], " + - "'offsets':[0,12,20,24], 'itemsize':56}}").format(e=e) + "{{'names':['a','b','c','d'], " + + "'formats':[('S4', (3,)),('" + + e + + "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}," + - "c={0,1,2},d={{0,1},{10,11},{20,21},{30,31}}", - "a={{W,X,Y,Z},{G,H,I,J},{Q,R,S,T}},b={1000,1001}," + - "c={10,11,12},d={{100,101},{110,111},{120,121},{130,131}}", - "a={{S,T,U,V},{C,D,E,F},{M,N,O,P}},b={2000,2001}," + - "c={20,21,22},d={{200,201},{210,211},{220,221},{230,231}}", + "a={{A,B,C,D},{K,L,M,N},{U,V,W,X}},b={0,1}," + + "c={0,1,2},d={{0,1},{10,11},{20,21},{30,31}}", + "a={{W,X,Y,Z},{G,H,I,J},{Q,R,S,T}},b={1000,1001}," + + "c={10,11,12},d={{100,101},{110,111},{120,121},{130,131}}", + "a={{S,T,U,V},{C,D,E,F},{M,N,O,P}},b={2000,2001}," + + "c={20,21,22},d={{200,201},{210,211},{220,221},{230,231}}", + ] + assert arr["a"].tolist() == [ + [b"ABCD", b"KLMN", b"UVWX"], + [b"WXYZ", b"GHIJ", b"QRST"], + [b"STUV", b"CDEF", b"MNOP"], ] - assert arr['a'].tolist() == [[b'ABCD', b'KLMN', b'UVWX'], - [b'WXYZ', b'GHIJ', b'QRST'], - [b'STUV', b'CDEF', b'MNOP']] - assert arr['b'].tolist() == [[0, 1], [1000, 1001], [2000, 2001]] + assert arr["b"].tolist() == [[0, 1], [1000, 1001], [2000, 2001]] assert m.create_array_array(0).dtype == arr.dtype def test_enum_array(): from sys import byteorder - e = '<' if byteorder == 'little' else '>' + + e = "<" if byteorder == "little" else ">" arr = m.create_enum_array(3) dtype = arr.dtype - assert dtype == np.dtype([('e1', e + 'i8'), ('e2', 'u1')]) - assert m.print_enum_array(arr) == [ - "e1=A,e2=X", - "e1=B,e2=Y", - "e1=A,e2=X" - ] - assert arr['e1'].tolist() == [-1, 1, -1] - assert arr['e2'].tolist() == [1, 2, 1] + assert dtype == np.dtype([("e1", e + "i8"), ("e2", "u1")]) + assert m.print_enum_array(arr) == ["e1=A,e2=X", "e1=B,e2=Y", "e1=A,e2=X"] + assert arr["e1"].tolist() == [-1, 1, -1] + assert arr["e2"].tolist() == [1, 2, 1] assert m.create_enum_array(0).dtype == dtype def test_complex_array(): from sys import byteorder - e = '<' if byteorder == 'little' else '>' + + e = "<" if byteorder == "little" else ">" arr = m.create_complex_array(3) dtype = arr.dtype - assert dtype == np.dtype([('cflt', e + 'c8'), ('cdbl', e + 'c16')]) + assert dtype == np.dtype([("cflt", e + "c8"), ("cdbl", e + "c16")]) assert m.print_complex_array(arr) == [ "c:(0,0.25),(0.5,0.75)", "c:(1,1.25),(1.5,1.75)", - "c:(2,2.25),(2.5,2.75)" + "c:(2,2.25),(2.5,2.75)", ] - assert arr['cflt'].tolist() == [0.0 + 0.25j, 1.0 + 1.25j, 2.0 + 2.25j] - assert arr['cdbl'].tolist() == [0.5 + 0.75j, 1.5 + 1.75j, 2.5 + 2.75j] + assert arr["cflt"].tolist() == [0.0 + 0.25j, 1.0 + 1.25j, 2.0 + 2.25j] + assert arr["cdbl"].tolist() == [0.5 + 0.75j, 1.5 + 1.75j, 2.5 + 2.75j] assert m.create_complex_array(0).dtype == dtype def test_signature(doc): - assert doc(m.create_rec_nested) == \ - "create_rec_nested(arg0: int) -> numpy.ndarray[NestedStruct]" + assert ( + doc(m.create_rec_nested) + == "create_rec_nested(arg0: int) -> numpy.ndarray[NestedStruct]" + ) def test_scalar_conversion(): n = 3 - arrays = [m.create_rec_simple(n), m.create_rec_packed(n), - m.create_rec_nested(n), m.create_enum_array(n)] + arrays = [ + m.create_rec_simple(n), + m.create_rec_packed(n), + m.create_rec_nested(n), + m.create_enum_array(n), + ] funcs = [m.f_simple, m.f_packed, m.f_nested] for i, func in enumerate(funcs): @@ -284,18 +355,68 @@ def test_scalar_conversion(): else: with pytest.raises(TypeError) as excinfo: func(arr[0]) - assert 'incompatible function arguments' in str(excinfo.value) + assert "incompatible function arguments" in str(excinfo.value) + + +def test_vectorize(): + n = 3 + array = m.create_rec_simple(n) + values = m.f_simple_vectorized(array) + np.testing.assert_array_equal(values, [0, 10, 20]) + array_2 = m.f_simple_pass_thru_vectorized(array) + np.testing.assert_array_equal(array, array_2) + + +def test_cls_and_dtype_conversion(simple_dtype): + s = m.SimpleStruct() + assert s.astuple() == (False, 0, 0.0, 0.0) + assert m.SimpleStruct.fromtuple(s.astuple()).astuple() == s.astuple() + + s.uint_ = 2 + assert m.f_simple(s) == 20 + + # Try as recarray of shape==(1,). + s_recarray = np.array([(False, 2, 0.0, 0.0)], dtype=simple_dtype) + # Show that this will work for vectorized case. + np.testing.assert_array_equal(m.f_simple_vectorized(s_recarray), [20]) + + # Show as a scalar that inherits from np.generic. + s_scalar = s_recarray[0] + assert isinstance(s_scalar, np.void) + assert m.f_simple(s_scalar) == 20 + + # Show that an *array* scalar (np.ndarray.shape == ()) does not convert. + # More specifically, conversion to SimpleStruct is not implicit. + s_recarray_scalar = s_recarray.reshape(()) + assert isinstance(s_recarray_scalar, np.ndarray) + assert s_recarray_scalar.dtype == simple_dtype + with pytest.raises(TypeError) as excinfo: + m.f_simple(s_recarray_scalar) + assert "incompatible function arguments" in str(excinfo.value) + # Explicitly convert to m.SimpleStruct. + assert m.f_simple(m.SimpleStruct.fromtuple(s_recarray_scalar.item())) == 20 + + # Show that an array of dtype=object does *not* convert. + s_array_object = np.array([s]) + assert s_array_object.dtype == object + with pytest.raises(TypeError) as excinfo: + m.f_simple_vectorized(s_array_object) + assert "incompatible function arguments" in str(excinfo.value) + # Explicitly convert to `np.array(..., dtype=simple_dtype)` + s_array = np.array([s.astuple()], dtype=simple_dtype) + np.testing.assert_array_equal(m.f_simple_vectorized(s_array), [20]) def test_register_dtype(): with pytest.raises(RuntimeError) as excinfo: m.register_dtype() - assert 'dtype is already registered' in str(excinfo.value) + assert "dtype is already registered" in str(excinfo.value) -@pytest.unsupported_on_pypy +@pytest.mark.xfail("env.PYPY") def test_str_leak(): from sys import getrefcount + fmt = "f4" pytest.gc_collect() start = getrefcount(fmt) diff --git a/3rdparty/pybind11/tests/test_numpy_vectorize.cpp b/3rdparty/pybind11/tests/test_numpy_vectorize.cpp index a875a74b..274b7558 100644 --- a/3rdparty/pybind11/tests/test_numpy_vectorize.cpp +++ b/3rdparty/pybind11/tests/test_numpy_vectorize.cpp @@ -17,7 +17,7 @@ double my_func(int x, float y, double z) { } TEST_SUBMODULE(numpy_vectorize, m) { - try { py::module::import("numpy"); } + try { py::module_::import("numpy"); } catch (...) { return; } // test_vectorize, test_docs, test_array_collapse @@ -37,7 +37,7 @@ TEST_SUBMODULE(numpy_vectorize, m) { )); // test_type_selection - // Numpy function which only accepts specific data types + // NumPy function which only accepts specific data types m.def("selective_func", [](py::array_t<int, py::array::c_style>) { return "Int branch taken."; }); m.def("selective_func", [](py::array_t<float, py::array::c_style>) { return "Float branch taken."; }); m.def("selective_func", [](py::array_t<std::complex<float>, py::array::c_style>) { return "Complex float branch taken."; }); @@ -50,7 +50,9 @@ TEST_SUBMODULE(numpy_vectorize, m) { NonPODClass(int v) : value{v} {} int value; }; - py::class_<NonPODClass>(m, "NonPODClass").def(py::init<int>()); + py::class_<NonPODClass>(m, "NonPODClass") + .def(py::init<int>()) + .def_readwrite("value", &NonPODClass::value); m.def("vec_passthrough", py::vectorize( [](double *a, double b, py::array_t<double> c, const int &d, int &e, NonPODClass f, const double g) { return *a + b + c.at(0) + d + e + f.value + g; @@ -81,9 +83,11 @@ TEST_SUBMODULE(numpy_vectorize, m) { py::array_t<float, py::array::forcecast> arg2, py::array_t<double, py::array::forcecast> arg3 ) { - ssize_t ndim; - std::vector<ssize_t> shape; + py::ssize_t ndim; + std::vector<py::ssize_t> shape; std::array<py::buffer_info, 3> 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; })); } diff --git a/3rdparty/pybind11/tests/test_numpy_vectorize.py b/3rdparty/pybind11/tests/test_numpy_vectorize.py index 0e9c8839..4e6b2d19 100644 --- a/3rdparty/pybind11/tests/test_numpy_vectorize.py +++ b/3rdparty/pybind11/tests/test_numpy_vectorize.py @@ -1,10 +1,8 @@ +# -*- coding: utf-8 -*- import pytest from pybind11_tests import numpy_vectorize as m -pytestmark = pytest.requires_numpy - -with pytest.suppress(ImportError): - import numpy as np +np = pytest.importorskip("numpy") def test_vectorize(capture): @@ -19,28 +17,40 @@ def test_vectorize(capture): assert capture == "my_func(x:int=1, y:float=2, z:float=3)" with capture: assert np.allclose(f(np.array([1, 3]), np.array([2, 4]), 3), [6, 36]) - assert capture == """ + assert ( + capture + == """ my_func(x:int=1, y:float=2, z:float=3) my_func(x:int=3, y:float=4, z:float=3) """ + ) with capture: - a = np.array([[1, 2], [3, 4]], order='F') - b = np.array([[10, 20], [30, 40]], order='F') + a = np.array([[1, 2], [3, 4]], order="F") + b = np.array([[10, 20], [30, 40]], order="F") c = 3 result = f(a, b, c) assert np.allclose(result, a * b * c) assert result.flags.f_contiguous # All inputs are F order and full or singletons, so we the result is in col-major order: - assert capture == """ + assert ( + capture + == """ my_func(x:int=1, y:float=10, z:float=3) my_func(x:int=3, y:float=30, z:float=3) my_func(x:int=2, y:float=20, z:float=3) my_func(x:int=4, y:float=40, z:float=3) """ + ) with capture: - a, b, c = np.array([[1, 3, 5], [7, 9, 11]]), np.array([[2, 4, 6], [8, 10, 12]]), 3 + a, b, c = ( + np.array([[1, 3, 5], [7, 9, 11]]), + np.array([[2, 4, 6], [8, 10, 12]]), + 3, + ) assert np.allclose(f(a, b, c), a * b * c) - assert capture == """ + assert ( + capture + == """ my_func(x:int=1, y:float=2, z:float=3) my_func(x:int=3, y:float=4, z:float=3) my_func(x:int=5, y:float=6, z:float=3) @@ -48,10 +58,13 @@ def test_vectorize(capture): my_func(x:int=9, y:float=10, z:float=3) my_func(x:int=11, y:float=12, z:float=3) """ + ) with capture: a, b, c = np.array([[1, 2, 3], [4, 5, 6]]), np.array([2, 3, 4]), 2 assert np.allclose(f(a, b, c), a * b * c) - assert capture == """ + assert ( + capture + == """ my_func(x:int=1, y:float=2, z:float=2) my_func(x:int=2, y:float=3, z:float=2) my_func(x:int=3, y:float=4, z:float=2) @@ -59,10 +72,13 @@ def test_vectorize(capture): my_func(x:int=5, y:float=3, z:float=2) my_func(x:int=6, y:float=4, z:float=2) """ + ) with capture: a, b, c = np.array([[1, 2, 3], [4, 5, 6]]), np.array([[2], [3]]), 2 assert np.allclose(f(a, b, c), a * b * c) - assert capture == """ + assert ( + capture + == """ my_func(x:int=1, y:float=2, z:float=2) my_func(x:int=2, y:float=2, z:float=2) my_func(x:int=3, y:float=2, z:float=2) @@ -70,10 +86,17 @@ def test_vectorize(capture): my_func(x:int=5, y:float=3, z:float=2) my_func(x:int=6, y:float=3, z:float=2) """ + ) with capture: - a, b, c = np.array([[1, 2, 3], [4, 5, 6]], order='F'), np.array([[2], [3]]), 2 + a, b, c = ( + np.array([[1, 2, 3], [4, 5, 6]], order="F"), + np.array([[2], [3]]), + 2, + ) assert np.allclose(f(a, b, c), a * b * c) - assert capture == """ + assert ( + capture + == """ my_func(x:int=1, y:float=2, z:float=2) my_func(x:int=2, y:float=2, z:float=2) my_func(x:int=3, y:float=2, z:float=2) @@ -81,36 +104,53 @@ def test_vectorize(capture): my_func(x:int=5, y:float=3, z:float=2) my_func(x:int=6, y:float=3, z:float=2) """ + ) with capture: a, b, c = np.array([[1, 2, 3], [4, 5, 6]])[::, ::2], np.array([[2], [3]]), 2 assert np.allclose(f(a, b, c), a * b * c) - assert capture == """ + assert ( + capture + == """ my_func(x:int=1, y:float=2, z:float=2) my_func(x:int=3, y:float=2, z:float=2) my_func(x:int=4, y:float=3, z:float=2) my_func(x:int=6, y:float=3, z:float=2) """ + ) with capture: - a, b, c = np.array([[1, 2, 3], [4, 5, 6]], order='F')[::, ::2], np.array([[2], [3]]), 2 + a, b, c = ( + np.array([[1, 2, 3], [4, 5, 6]], order="F")[::, ::2], + np.array([[2], [3]]), + 2, + ) assert np.allclose(f(a, b, c), a * b * c) - assert capture == """ + assert ( + capture + == """ my_func(x:int=1, y:float=2, z:float=2) my_func(x:int=3, y:float=2, z:float=2) my_func(x:int=4, y:float=3, z:float=2) my_func(x:int=6, y:float=3, z:float=2) """ + ) def test_type_selection(): assert m.selective_func(np.array([1], dtype=np.int32)) == "Int branch taken." assert m.selective_func(np.array([1.0], dtype=np.float32)) == "Float branch taken." - assert m.selective_func(np.array([1.0j], dtype=np.complex64)) == "Complex float branch taken." + assert ( + m.selective_func(np.array([1.0j], dtype=np.complex64)) + == "Complex float branch taken." + ) def test_docs(doc): - assert doc(m.vectorized_func) == """ - vectorized_func(arg0: numpy.ndarray[int32], arg1: numpy.ndarray[float32], arg2: numpy.ndarray[float64]) -> object + assert ( + doc(m.vectorized_func) + == """ + vectorized_func(arg0: numpy.ndarray[numpy.int32], arg1: numpy.ndarray[numpy.float32], arg2: numpy.ndarray[numpy.float64]) -> object """ # noqa: E501 line too long + ) def test_trivial_broadcasting(): @@ -118,16 +158,24 @@ def test_trivial_broadcasting(): assert vectorized_is_trivial(1, 2, 3) == trivial.c_trivial assert vectorized_is_trivial(np.array(1), np.array(2), 3) == trivial.c_trivial - assert vectorized_is_trivial(np.array([1, 3]), np.array([2, 4]), 3) == trivial.c_trivial + assert ( + vectorized_is_trivial(np.array([1, 3]), np.array([2, 4]), 3) + == trivial.c_trivial + ) assert trivial.c_trivial == vectorized_is_trivial( - np.array([[1, 3, 5], [7, 9, 11]]), np.array([[2, 4, 6], [8, 10, 12]]), 3) - assert vectorized_is_trivial( - np.array([[1, 2, 3], [4, 5, 6]]), np.array([2, 3, 4]), 2) == trivial.non_trivial - assert vectorized_is_trivial( - np.array([[1, 2, 3], [4, 5, 6]]), np.array([[2], [3]]), 2) == trivial.non_trivial - z1 = np.array([[1, 2, 3, 4], [5, 6, 7, 8]], dtype='int32') - z2 = np.array(z1, dtype='float32') - z3 = np.array(z1, dtype='float64') + np.array([[1, 3, 5], [7, 9, 11]]), np.array([[2, 4, 6], [8, 10, 12]]), 3 + ) + assert ( + vectorized_is_trivial(np.array([[1, 2, 3], [4, 5, 6]]), np.array([2, 3, 4]), 2) + == trivial.non_trivial + ) + assert ( + vectorized_is_trivial(np.array([[1, 2, 3], [4, 5, 6]]), np.array([[2], [3]]), 2) + == trivial.non_trivial + ) + z1 = np.array([[1, 2, 3, 4], [5, 6, 7, 8]], dtype="int32") + z2 = np.array(z1, dtype="float32") + z3 = np.array(z1, dtype="float64") assert vectorized_is_trivial(z1, z2, z3) == trivial.c_trivial assert vectorized_is_trivial(1, z2, z3) == trivial.c_trivial assert vectorized_is_trivial(z1, 1, z3) == trivial.c_trivial @@ -137,7 +185,7 @@ def test_trivial_broadcasting(): assert vectorized_is_trivial(1, 1, z3[::2, ::2]) == trivial.non_trivial assert vectorized_is_trivial(z1, 1, z3[1::4, 1::4]) == trivial.c_trivial - y1 = np.array(z1, order='F') + y1 = np.array(z1, order="F") y2 = np.array(y1) y3 = np.array(y1) assert vectorized_is_trivial(y1, y2, y3) == trivial.f_trivial @@ -158,30 +206,41 @@ def test_trivial_broadcasting(): def test_passthrough_arguments(doc): assert doc(m.vec_passthrough) == ( - "vec_passthrough(" + ", ".join([ - "arg0: float", - "arg1: numpy.ndarray[float64]", - "arg2: numpy.ndarray[float64]", - "arg3: numpy.ndarray[int32]", - "arg4: int", - "arg5: m.numpy_vectorize.NonPODClass", - "arg6: numpy.ndarray[float64]"]) + ") -> object") - - b = np.array([[10, 20, 30]], dtype='float64') + "vec_passthrough(" + + ", ".join( + [ + "arg0: float", + "arg1: numpy.ndarray[numpy.float64]", + "arg2: numpy.ndarray[numpy.float64]", + "arg3: numpy.ndarray[numpy.int32]", + "arg4: int", + "arg5: m.numpy_vectorize.NonPODClass", + "arg6: numpy.ndarray[numpy.float64]", + ] + ) + + ") -> object" + ) + + b = np.array([[10, 20, 30]], dtype="float64") c = np.array([100, 200]) # NOT a vectorized argument - d = np.array([[1000], [2000], [3000]], dtype='int') - g = np.array([[1000000, 2000000, 3000000]], dtype='int') # requires casting + d = np.array([[1000], [2000], [3000]], dtype="int") + g = np.array([[1000000, 2000000, 3000000]], dtype="int") # requires casting assert np.all( - m.vec_passthrough(1, b, c, d, 10000, m.NonPODClass(100000), g) == - np.array([[1111111, 2111121, 3111131], - [1112111, 2112121, 3112131], - [1113111, 2113121, 3113131]])) + m.vec_passthrough(1, b, c, d, 10000, m.NonPODClass(100000), g) + == np.array( + [ + [1111111, 2111121, 3111131], + [1112111, 2112121, 3112131], + [1113111, 2113121, 3113131], + ] + ) + ) def test_method_vectorization(): o = m.VectorizeTestClass(3) - x = np.array([1, 2], dtype='int') - y = np.array([[10], [20]], dtype='float32') + x = np.array([1, 2], dtype="int") + y = np.array([[10], [20]], dtype="float32") assert np.all(o.method(x, y) == [[14, 15], [24, 25]]) @@ -190,7 +249,18 @@ def test_array_collapse(): assert not isinstance(m.vectorized_func(np.array(1), 2, 3), np.ndarray) z = m.vectorized_func([1], 2, 3) assert isinstance(z, np.ndarray) - assert z.shape == (1, ) + assert z.shape == (1,) z = m.vectorized_func(1, [[[2]]], 3) assert isinstance(z, np.ndarray) assert z.shape == (1, 1, 1) + + +def test_vectorized_noreturn(): + x = m.NonPODClass(0) + assert x.value == 0 + m.add_to(x, [1, 2, 3, 4]) + assert x.value == 10 + m.add_to(x, 1) + assert x.value == 11 + m.add_to(x, [[1, 1], [2, 3]]) + assert x.value == 18 diff --git a/3rdparty/pybind11/tests/test_opaque_types.cpp b/3rdparty/pybind11/tests/test_opaque_types.cpp index 0d20d9a0..5a234316 100644 --- a/3rdparty/pybind11/tests/test_opaque_types.cpp +++ b/3rdparty/pybind11/tests/test_opaque_types.cpp @@ -60,8 +60,14 @@ TEST_SUBMODULE(opaque_types, m) { m.def("get_null_str_value", [](char *ptr) { return reinterpret_cast<std::intptr_t>(ptr); }); m.def("return_unique_ptr", []() -> std::unique_ptr<StringList> { - StringList *result = new StringList(); + auto *result = new StringList(); result->push_back("some value"); return std::unique_ptr<StringList>(result); }); + + // test unions + py::class_<IntFloat>(m, "IntFloat") + .def(py::init<>()) + .def_readwrite("i", &IntFloat::i) + .def_readwrite("f", &IntFloat::f); } diff --git a/3rdparty/pybind11/tests/test_opaque_types.py b/3rdparty/pybind11/tests/test_opaque_types.py index 6b3802fd..77379463 100644 --- a/3rdparty/pybind11/tests/test_opaque_types.py +++ b/3rdparty/pybind11/tests/test_opaque_types.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- import pytest from pybind11_tests import opaque_types as m from pybind11_tests import ConstructorStats, UserType @@ -31,12 +32,15 @@ def test_pointers(msg): with pytest.raises(TypeError) as excinfo: m.get_void_ptr_value([1, 2, 3]) # This should not work - assert msg(excinfo.value) == """ + assert ( + msg(excinfo.value) + == """ get_void_ptr_value(): incompatible function arguments. The following argument types are supported: 1. (arg0: capsule) -> int Invoked with: [1, 2, 3] """ # noqa: E501 line too long + ) assert m.return_null_str() is None assert m.get_null_str_value(m.return_null_str()) is not None @@ -44,3 +48,11 @@ def test_pointers(msg): ptr = m.return_unique_ptr() assert "StringList" in repr(ptr) assert m.print_opaque_list(ptr) == "Opaque list: [some value]" + + +def test_unions(): + int_float_union = m.IntFloat() + int_float_union.i = 42 + assert int_float_union.i == 42 + int_float_union.f = 3.0 + assert int_float_union.f == 3.0 diff --git a/3rdparty/pybind11/tests/test_operator_overloading.cpp b/3rdparty/pybind11/tests/test_operator_overloading.cpp index 7b111704..0a27bfd5 100644 --- a/3rdparty/pybind11/tests/test_operator_overloading.cpp +++ b/3rdparty/pybind11/tests/test_operator_overloading.cpp @@ -43,6 +43,13 @@ public: 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; + } private: float x, y; }; @@ -55,12 +62,22 @@ int operator+(const C2 &, const C2 &) { return 22; } int operator+(const C2 &, const C1 &) { return 21; } int operator+(const C1 &, const C2 &) { return 12; } +// 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<Vector2> { // Not a good hash function, but easy to test size_t operator()(const Vector2 &) { return 4; } }; +} // namespace std + +// Not a good abs function, but easy to test. +std::string abs(const Vector2&) { + return "abs(Vector2)"; } // MSVC warns about unknown pragmas, and warnings are errors. @@ -71,11 +88,11 @@ namespace std { // 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 (__APPLE__) && (__clang__) - #if (__clang_major__ >= 10) && (__clang_minor__ >= 0) && (__clang_patchlevel__ >= 1) + #if defined(__APPLE__) && defined(__clang__) + #if (__clang_major__ >= 10) #pragma GCC diagnostic ignored "-Wself-assign-overloaded" #endif - #elif (__clang__) + #elif defined(__clang__) #if (__clang_major__ >= 7) #pragma GCC diagnostic ignored "-Wself-assign-overloaded" #endif @@ -107,7 +124,13 @@ TEST_SUBMODULE(operators, m) { .def(float() / py::self) .def(-py::self) .def("__str__", &Vector2::toString) - .def(hash(py::self)) + .def("__repr__", &Vector2::toString) + .def(py::self == py::self) + .def(py::self != py::self) + .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); }) ; m.attr("Vector") = m.attr("Vector2"); @@ -164,6 +187,38 @@ TEST_SUBMODULE(operators, m) { .def(py::self *= int()) .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;} + }; + + struct Hashable : Comparable { + explicit Hashable(int value): Comparable{value}{}; + size_t hash() const { return static_cast<size_t>(value); } + }; + + struct Hashable2 : Hashable { + using Hashable::Hashable; + }; + + py::class_<Comparable>(m, "Comparable") + .def(py::init<int>()) + .def(py::self == py::self); + + py::class_<Hashable>(m, "Hashable") + .def(py::init<int>()) + .def(py::self == py::self) + .def("__hash__", &Hashable::hash); + + // define __hash__ before __eq__ + py::class_<Hashable2>(m, "Hashable2") + .def("__hash__", &Hashable::hash) + .def(py::init<int>()) + .def(py::self == py::self); } #ifndef _MSC_VER diff --git a/3rdparty/pybind11/tests/test_operator_overloading.py b/3rdparty/pybind11/tests/test_operator_overloading.py index bd36ac2a..5dbfb32c 100644 --- a/3rdparty/pybind11/tests/test_operator_overloading.py +++ b/3rdparty/pybind11/tests/test_operator_overloading.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- import pytest from pybind11_tests import operators as m from pybind11_tests import ConstructorStats @@ -6,6 +7,9 @@ from pybind11_tests import ConstructorStats def test_operator_overloading(): v1 = m.Vector2(1, 2) v2 = m.Vector(3, -1) + v3 = m.Vector2(1, 2) # Same value as v1, but different instance. + assert v1 is not v3 + assert str(v1) == "[1.000000, 2.000000]" assert str(v2) == "[3.000000, -1.000000]" @@ -24,6 +28,12 @@ def test_operator_overloading(): assert str(v1 * v2) == "[3.000000, -2.000000]" assert str(v2 / v1) == "[3.000000, -0.500000]" + assert v1 == v3 + assert v1 != v2 + assert hash(v1) == 4 + # TODO(eric.cousineau): Make this work. + # assert abs(v1) == "abs(Vector2)" + v1 += 2 * v2 assert str(v1) == "[7.000000, 0.000000]" v1 -= v2 @@ -37,22 +47,33 @@ def test_operator_overloading(): v2 /= v1 assert str(v2) == "[2.000000, 8.000000]" - assert hash(v1) == 4 - cstats = ConstructorStats.get(m.Vector2) - assert cstats.alive() == 2 + assert cstats.alive() == 3 del v1 - assert cstats.alive() == 1 + assert cstats.alive() == 2 del v2 + assert cstats.alive() == 1 + del v3 assert cstats.alive() == 0 - assert cstats.values() == ['[1.000000, 2.000000]', '[3.000000, -1.000000]', - '[-3.000000, 1.000000]', '[4.000000, 1.000000]', - '[-2.000000, 3.000000]', '[-7.000000, -6.000000]', - '[9.000000, 10.000000]', '[8.000000, 16.000000]', - '[0.125000, 0.250000]', '[7.000000, 6.000000]', - '[9.000000, 10.000000]', '[8.000000, 16.000000]', - '[8.000000, 4.000000]', '[3.000000, -2.000000]', - '[3.000000, -0.500000]', '[6.000000, -2.000000]'] + assert cstats.values() == [ + "[1.000000, 2.000000]", + "[3.000000, -1.000000]", + "[1.000000, 2.000000]", + "[-3.000000, 1.000000]", + "[4.000000, 1.000000]", + "[-2.000000, 3.000000]", + "[-7.000000, -6.000000]", + "[9.000000, 10.000000]", + "[8.000000, 16.000000]", + "[0.125000, 0.250000]", + "[7.000000, 6.000000]", + "[9.000000, 10.000000]", + "[8.000000, 16.000000]", + "[8.000000, 4.000000]", + "[3.000000, -2.000000]", + "[3.000000, -0.500000]", + "[6.000000, -2.000000]", + ] assert cstats.default_constructions == 0 assert cstats.copy_constructions == 0 assert cstats.move_constructions >= 10 @@ -106,3 +127,19 @@ def test_nested(): assert abase.value == 42 del abase, b pytest.gc_collect() + + +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' + + for hashable in (m.Hashable, m.Hashable2): + assert hashable(15) is not hashable(15) + assert hashable(15) == hashable(15) + + assert hash(hashable(15)) == 15 + assert hash(hashable(15)) == hash(hashable(15)) diff --git a/3rdparty/pybind11/tests/test_pickling.py b/3rdparty/pybind11/tests/test_pickling.py index 5ae05aaa..6b27a73a 100644 --- a/3rdparty/pybind11/tests/test_pickling.py +++ b/3rdparty/pybind11/tests/test_pickling.py @@ -1,4 +1,8 @@ +# -*- coding: utf-8 -*- import pytest + +import env # noqa: F401 + from pybind11_tests import pickling as m try: @@ -21,7 +25,7 @@ def test_roundtrip(cls_name): assert p2.extra2() == p.extra2() -@pytest.unsupported_on_pypy +@pytest.mark.xfail("env.PYPY") @pytest.mark.parametrize("cls_name", ["PickleableWithDict", "PickleableWithDictNew"]) def test_roundtrip_with_dict(cls_name): cls = getattr(m, cls_name) @@ -38,5 +42,6 @@ def test_roundtrip_with_dict(cls_name): def test_enum_pickle(): from pybind11_tests import enums as e + data = pickle.dumps(e.EOne, 2) assert e.EOne == pickle.loads(data) diff --git a/3rdparty/pybind11/tests/test_pytypes.cpp b/3rdparty/pybind11/tests/test_pytypes.cpp index 244e1db0..113cf5cb 100644 --- a/3rdparty/pybind11/tests/test_pytypes.cpp +++ b/3rdparty/pybind11/tests/test_pytypes.cpp @@ -11,6 +11,12 @@ TEST_SUBMODULE(pytypes, m) { + // test_int + m.def("get_int", []{return py::int_(0);}); + // test_iterator + m.def("get_iterator", []{return py::iterator();}); + // test_iterable + m.def("get_iterable", []{return py::iterable();}); // test_list m.def("get_list", []() { py::list list; @@ -26,6 +32,11 @@ TEST_SUBMODULE(pytypes, m) { 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)); + }); // test_set m.def("get_set", []() { @@ -69,6 +80,7 @@ TEST_SUBMODULE(pytypes, m) { 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_handle", [](py::handle h) { return py::str(h); }); m.def("str_format", []() { auto s1 = "{} + {} = {}"_s.format(1, 2, 3); @@ -96,7 +108,7 @@ TEST_SUBMODULE(pytypes, m) { }); m.def("return_capsule_with_name_and_destructor", []() { - auto capsule = py::capsule((void *) 1234, "pointer type description", [](PyObject *ptr) { + auto capsule = py::capsule((void *) 12345, "pointer type description", [](PyObject *ptr) { if (ptr) { auto name = PyCapsule_GetName(ptr); py::print("destructing capsule ({}, '{}')"_s.format( @@ -104,8 +116,19 @@ TEST_SUBMODULE(pytypes, m) { )); } }); - void *contents = capsule; - py::print("created capsule ({}, '{}')"_s.format((size_t) contents, capsule.name())); + + capsule.set_pointer((void *) 1234); + + // Using get_pointer<T>() + void* contents1 = static_cast<void*>(capsule); + void* contents2 = capsule.get_pointer(); + void* contents3 = capsule.get_pointer<void>(); + + auto result1 = reinterpret_cast<size_t>(contents1); + auto result2 = reinterpret_cast<size_t>(contents2); + auto result3 = reinterpret_cast<size_t>(contents3); + + py::print("created capsule ({}, '{}')"_s.format(result1 & result2 & result3, capsule.name())); return capsule; }); @@ -116,7 +139,7 @@ TEST_SUBMODULE(pytypes, m) { d["basic_attr"] = o.attr("basic_attr"); auto l = py::list(); - for (const auto &item : o.attr("begin_end")) { + for (auto item : o.attr("begin_end")) { l.append(item); } d["begin_end"] = l; @@ -186,6 +209,7 @@ TEST_SUBMODULE(pytypes, m) { // 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_(), @@ -199,6 +223,7 @@ TEST_SUBMODULE(pytypes, m) { 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"]), @@ -214,6 +239,7 @@ TEST_SUBMODULE(pytypes, m) { m.def("cast_functions", [](py::dict d) { // When converting between Python types, obj.cast<T>() should be the same as T(obj) return py::dict( + "bytes"_a=d["bytes"].cast<py::bytes>(), "str"_a=d["str"].cast<py::str>(), "bool"_a=d["bool"].cast<py::bool_>(), "int"_a=d["int"].cast<py::int_>(), @@ -226,6 +252,21 @@ TEST_SUBMODULE(pytypes, m) { ); }); + 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"); + }); + m.def("get_implicit_casting", []() { py::dict d; d["char*_i1"] = "abc"; @@ -272,7 +313,7 @@ TEST_SUBMODULE(pytypes, m) { py::print("no new line here", "end"_a=" -- "); py::print("next print"); - auto py_stderr = py::module::import("sys").attr("stderr"); + auto py_stderr = py::module_::import("sys").attr("stderr"); py::print("this goes to stderr", "file"_a=py_stderr); py::print("flush", "flush"_a=true); @@ -307,4 +348,66 @@ TEST_SUBMODULE(pytypes, m) { m.def("test_list_slicing", [](py::list a) { return a[py::slice(0, -1, 2)]; }); + + // See #2361 + m.def("issue2361_str_implicit_copy_none", []() { + py::str is_this_none = py::none(); + return is_this_none; + }); + m.def("issue2361_dict_implicit_copy_none", []() { + py::dict is_this_none = py::none(); + return is_this_none; + }); + + m.def("test_memoryview_object", [](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_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) }); + }); + + 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) }); + }); + + m.def("test_memoryview_from_buffer_empty_shape", []() { + 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 }, { }); + }); + + m.def("test_memoryview_from_buffer_nullptr", []() { + return py::memoryview::from_buffer( + static_cast<void*>(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<py::ssize_t>(strlen(buf))); + }); +#endif + + // test_builtin_functions + m.def("get_len", [](py::handle h) { return py::len(h); }); } diff --git a/3rdparty/pybind11/tests/test_pytypes.py b/3rdparty/pybind11/tests/test_pytypes.py index 0e8d6c33..9e5c302e 100644 --- a/3rdparty/pybind11/tests/test_pytypes.py +++ b/3rdparty/pybind11/tests/test_pytypes.py @@ -1,11 +1,26 @@ +# -*- coding: utf-8 -*- from __future__ import division import pytest import sys +import env # noqa: F401 + from pybind11_tests import pytypes as m from pybind11_tests import debug_enabled +def test_int(doc): + assert doc(m.get_int) == "get_int() -> int" + + +def test_iterator(doc): + assert doc(m.get_iterator) == "get_iterator() -> Iterator" + + +def test_iterable(doc): + assert doc(m.get_iterable) == "get_iterable() -> Iterable" + + def test_list(capture, doc): with capture: lst = m.get_list() @@ -13,18 +28,26 @@ def test_list(capture, doc): lst.append("value2") m.print_list(lst) - assert capture.unordered == """ + assert ( + capture.unordered + == """ Entry at position 0: value list item 0: inserted-0 list item 1: overwritten list item 2: inserted-2 list item 3: value2 """ + ) assert doc(m.get_list) == "get_list() -> list" assert doc(m.print_list) == "print_list(arg0: list) -> None" +def test_none(capture, doc): + assert doc(m.get_none) == "get_none() -> None" + assert doc(m.print_none) == "print_none(arg0: None) -> None" + + def test_set(capture, doc): s = m.get_set() assert s == {"key1", "key2", "key3"} @@ -32,12 +55,15 @@ def test_set(capture, doc): with capture: s.add("key4") m.print_set(s) - assert capture.unordered == """ + assert ( + capture.unordered + == """ key: key1 key: key2 key: key3 key: key4 """ + ) assert not m.set_contains(set([]), 42) assert m.set_contains({42}, 42) @@ -54,10 +80,13 @@ def test_dict(capture, doc): with capture: d["key2"] = "value2" m.print_dict(d) - assert capture.unordered == """ + assert ( + capture.unordered + == """ key: key, value=value key: key2, value=value2 """ + ) assert not m.dict_contains({}, 42) assert m.dict_contains({42: None}, 42) @@ -84,18 +113,30 @@ def test_str(doc): assert m.str_from_object(A()) == "this is a str" assert m.repr_from_object(A()) == "this is a repr" + assert m.str_from_handle(A()) == "this is a str" s1, s2 = m.str_format() assert s1 == "1 + 2 = 3" 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) + else: + # assert m.str_from_object(malformed_utf8) == "b'\\x80'" + assert m.str_from_handle(malformed_utf8) == "b'\\x80'" + def test_bytes(doc): assert m.bytes_from_string().decode() == "foo" assert m.bytes_from_str().decode() == "bar" assert doc(m.bytes_from_str) == "bytes_from_str() -> {}".format( - "bytes" if sys.version_info[0] == 3 else "str" + "str" if env.PY2 else "bytes" ) @@ -105,28 +146,37 @@ def test_capsule(capture): a = m.return_capsule_with_destructor() del a pytest.gc_collect() - assert capture.unordered == """ + assert ( + capture.unordered + == """ creating capsule destructing capsule """ + ) with capture: a = m.return_capsule_with_destructor_2() del a pytest.gc_collect() - assert capture.unordered == """ + assert ( + capture.unordered + == """ creating capsule destructing capsule: 1234 """ + ) with capture: a = m.return_capsule_with_name_and_destructor() del a pytest.gc_collect() - assert capture.unordered == """ + assert ( + capture.unordered + == """ created capsule (1234, 'pointer type description') destructing capsule (1234, 'pointer type description') """ + ) def test_accessors(): @@ -170,11 +220,17 @@ def test_accessors(): def test_constructors(): """C++ default and converting constructors are equivalent to type calls in Python""" - types = [str, bool, int, float, tuple, list, dict, set] + types = [bytes, 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. str: 42, bool: "Not empty", int: "42", @@ -183,10 +239,15 @@ def test_constructors(): list: range(3), dict: [("two", 2), ("one", 1), ("three", 3)], set: [4, 4, 5, 6, 6, 6], - memoryview: b'abc' + 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 @@ -202,21 +263,79 @@ def test_constructors(): assert noconv2[k] is expected[k] +def test_non_converting_constructors(): + non_converting_test_cases = [ + ("bytes", range(10)), + ("none", 42), + ("ellipsis", 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 + + +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" + 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" + + malformed_utf8 = b"\x80" + malformed_cvt = cvt(malformed_utf8) + assert type(malformed_cvt) == bytes # Probably surprising. + assert malformed_cvt == b"\x80" + + def test_implicit_casting(): """Tests implicit casting when assigning or appending to dicts and lists.""" z = m.get_implicit_casting() - assert z['d'] == { - 'char*_i1': 'abc', 'char*_i2': 'abc', 'char*_e': 'abc', 'char*_p': 'abc', - 'str_i1': 'str', 'str_i2': 'str1', 'str_e': 'str2', 'str_p': 'str3', - 'int_i1': 42, 'int_i2': 42, 'int_e': 43, 'int_p': 44 + assert z["d"] == { + "char*_i1": "abc", + "char*_i2": "abc", + "char*_e": "abc", + "char*_p": "abc", + "str_i1": "str", + "str_i2": "str1", + "str_e": "str2", + "str_p": "str3", + "int_i1": 42, + "int_i2": 42, + "int_e": 43, + "int_p": 44, } - assert z['l'] == [3, 6, 9, 12, 15] + assert z["l"] == [3, 6, 9, 12, 15] def test_print(capture): with capture: m.print_function() - assert capture == """ + assert ( + capture + == """ Hello, World! 1 2.0 three True -- multiple args *args-and-a-custom-separator @@ -224,14 +343,15 @@ def test_print(capture): flush py::print + str.format = this """ + ) assert capture.stderr == "this goes to stderr" 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)" + if debug_enabled + else "arguments to Python object (compile in debug mode for details)" ) @@ -253,11 +373,116 @@ def test_hash(): def test_number_protocol(): for a, b in [(1, 1), (3, 5)]: - li = [a == b, a != b, a < b, a <= b, a > b, a >= b, a + b, - a - b, a * b, a / b, a | b, a & b, a ^ b, a >> b, a << b] + li = [ + a == b, + a != b, + a < b, + a <= b, + a > b, + a >= b, + a + b, + a - b, + a * b, + a / b, + a | b, + a & b, + a ^ b, + a >> b, + a << b, + ] assert m.test_number_protocol(a, b) == li def test_list_slicing(): li = list(range(100)) assert li[::2] == m.test_list_slicing(li) + + +def test_issue2361(): + # See issue #2361 + 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) + + +@pytest.mark.parametrize( + "method, args, fmt, expected_view", + [ + (m.test_memoryview_object, (b"red",), "B", b"red"), + (m.test_memoryview_buffer_info, (b"green",), "B", b"green"), + (m.test_memoryview_from_buffer, (False,), "h", [3, 1, 4, 1, 5]), + (m.test_memoryview_from_buffer, (True,), "H", [2, 7, 1, 8]), + (m.test_memoryview_from_buffer_nativeformat, (), "@i", [4, 7, 5]), + ], +) +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) + + +@pytest.mark.xfail("env.PYPY", reason="getrefcount is not available") +@pytest.mark.parametrize( + "method", + [ + m.test_memoryview_object, + m.test_memoryview_buffer_info, + ], +) +def test_memoryview_refcount(method): + buf = b"\x0a\x0b\x0c\x0d" + ref_before = sys.getrefcount(buf) + view = method(buf) + ref_after = sys.getrefcount(buf) + assert ref_before < ref_after + assert list(view) == list(buf) + + +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 <memoryview, while CPython 2 has <memory + assert bytes(view).startswith(b"<memory") + else: + assert bytes(view) == b"" + + +def test_test_memoryview_from_buffer_invalid_strides(): + with pytest.raises(RuntimeError): + m.test_memoryview_from_buffer_invalid_strides() + + +def test_test_memoryview_from_buffer_nullptr(): + if env.PY2: + m.test_memoryview_from_buffer_nullptr() + else: + with pytest.raises(ValueError): + m.test_memoryview_from_buffer_nullptr() + + +@pytest.mark.skipif("env.PY2") +def test_memoryview_from_memory(): + view = m.test_memoryview_from_memory() + assert isinstance(view, memoryview) + assert view.format == "B" + assert bytes(view) == b"\xff\xe1\xab\x37" + + +def test_builtin_functions(): + assert m.get_len([i for i in range(42)]) == 42 + with pytest.raises(TypeError) as exc_info: + m.get_len(i for i in range(42)) + assert str(exc_info.value) in [ + "object of type 'generator' has no len()", + "'generator' has no length", + ] # PyPy diff --git a/3rdparty/pybind11/tests/test_sequences_and_iterators.cpp b/3rdparty/pybind11/tests/test_sequences_and_iterators.cpp index 87ccf99d..d318052a 100644 --- a/3rdparty/pybind11/tests/test_sequences_and_iterators.cpp +++ b/3rdparty/pybind11/tests/test_sequences_and_iterators.cpp @@ -13,6 +13,8 @@ #include <pybind11/operators.h> #include <pybind11/stl.h> +#include <algorithm> + template<typename T> class NonZeroIterator { const T* ptr_; @@ -81,7 +83,7 @@ TEST_SUBMODULE(sequences_and_iterators, m) { py::class_<Sliceable>(m,"Sliceable") .def(py::init<int>()) .def("__getitem__",[](const Sliceable &s, py::slice slice) { - ssize_t start, stop, step, slicelength; + 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<int>(start); @@ -198,7 +200,7 @@ TEST_SUBMODULE(sequences_and_iterators, m) { size_t start, stop, step, slicelength; if (!slice.compute(s.size(), &start, &stop, &step, &slicelength)) throw py::error_already_set(); - Sequence *seq = new Sequence(slicelength); + auto *seq = new Sequence(slicelength); for (size_t i = 0; i < slicelength; ++i) { (*seq)[i] = s[start]; start += step; } @@ -319,6 +321,9 @@ TEST_SUBMODULE(sequences_and_iterators, m) { return l; }); + // test_sequence_length: check that Python sequences can be converted to py::sequence. + m.def("sequence_length", [](py::sequence seq) { return seq.size(); }); + // Make sure that py::iterator works with std algorithms m.def("count_none", [](py::object o) { return std::count_if(o.begin(), o.end(), [](py::handle h) { return h.is_none(); }); diff --git a/3rdparty/pybind11/tests/test_sequences_and_iterators.py b/3rdparty/pybind11/tests/test_sequences_and_iterators.py index 6bd16064..c3b608c4 100644 --- a/3rdparty/pybind11/tests/test_sequences_and_iterators.py +++ b/3rdparty/pybind11/tests/test_sequences_and_iterators.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- import pytest from pybind11_tests import sequences_and_iterators as m from pybind11_tests import ConstructorStats @@ -9,7 +10,9 @@ def isclose(a, b, rel_tol=1e-05, abs_tol=0.0): 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)) + return all( + isclose(a, b, rel_tol=rel_tol, abs_tol=abs_tol) for a, b in zip(a_list, b_list) + ) def test_generalized_iterators(): @@ -50,7 +53,7 @@ def test_sequence(): cstats = ConstructorStats.get(m.Sequence) s = m.Sequence(5) - assert cstats.values() == ['of size', '5'] + assert cstats.values() == ["of size", "5"] assert "Sequence" in repr(s) assert len(s) == 5 @@ -61,16 +64,16 @@ def test_sequence(): assert isclose(s[0], 12.34) and isclose(s[3], 56.78) rev = reversed(s) - assert cstats.values() == ['of size', '5'] + assert cstats.values() == ["of size", "5"] rev2 = s[::-1] - assert cstats.values() == ['of size', '5'] + assert cstats.values() == ["of size", "5"] it = iter(m.Sequence(0)) for _ in range(3): # __next__ must continue to raise StopIteration with pytest.raises(StopIteration): next(it) - assert cstats.values() == ['of size', '0'] + assert cstats.values() == ["of size", "0"] expected = [0, 56.78, 0, 0, 12.34] assert allclose(rev, expected) @@ -78,7 +81,7 @@ def test_sequence(): assert rev == rev2 rev[0::2] = m.Sequence([2.0, 2.0, 2.0]) - assert cstats.values() == ['of size', '3', 'from std::vector'] + assert cstats.values() == ["of size", "3", "from std::vector"] assert allclose(rev, [2, 56.78, 2, 0, 2]) @@ -100,18 +103,38 @@ def test_sequence(): assert cstats.move_assignments == 0 +def test_sequence_length(): + """#2076: Exception raised by len(arg) should be propagated """ + + class BadLen(RuntimeError): + pass + + class SequenceLike: + def __getitem__(self, i): + return None + + def __len__(self): + raise BadLen() + + with pytest.raises(BadLen): + m.sequence_length(SequenceLike()) + + assert m.sequence_length([1, 2, 3]) == 3 + assert m.sequence_length("hello") == 5 + + def test_map_iterator(): - sm = m.StringMap({'hi': 'bye', 'black': 'white'}) - assert sm['hi'] == 'bye' + sm = m.StringMap({"hi": "bye", "black": "white"}) + assert sm["hi"] == "bye" assert len(sm) == 2 - assert sm['black'] == 'white' + assert sm["black"] == "white" with pytest.raises(KeyError): - assert sm['orange'] - sm['orange'] = 'banana' - assert sm['orange'] == 'banana' + assert sm["orange"] + sm["orange"] = "banana" + assert sm["orange"] == "banana" - expected = {'hi': 'bye', 'black': 'white', 'orange': 'banana'} + expected = {"hi": "bye", "black": "white", "orange": "banana"} for k in sm: assert sm[k] == expected[k] for k, v in sm.items(): @@ -159,7 +182,8 @@ def test_iterator_passthrough(): """#181: iterator passthrough did not compile""" from pybind11_tests.sequences_and_iterators import iterator_passthrough - assert list(iterator_passthrough(iter([3, 5, 7, 9, 11, 13, 15]))) == [3, 5, 7, 9, 11, 13, 15] + values = [3, 5, 7, 9, 11, 13, 15] + assert list(iterator_passthrough(iter(values))) == values def test_iterator_rvp(): diff --git a/3rdparty/pybind11/tests/test_smart_ptr.cpp b/3rdparty/pybind11/tests/test_smart_ptr.cpp index 87c9be8c..60c2e692 100644 --- a/3rdparty/pybind11/tests/test_smart_ptr.cpp +++ b/3rdparty/pybind11/tests/test_smart_ptr.cpp @@ -27,7 +27,8 @@ namespace pybind11 { namespace detail { struct holder_helper<ref<T>> { static const T *get(const ref<T> &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<T>); @@ -97,9 +98,9 @@ TEST_SUBMODULE(smart_ptr, m) { class MyObject1 : public Object { public: MyObject1(int value) : value(value) { print_created(this, toString()); } - std::string toString() const { return "MyObject1[" + std::to_string(value) + "]"; } + std::string toString() const override { return "MyObject1[" + std::to_string(value) + "]"; } protected: - virtual ~MyObject1() { print_destroyed(this); } + ~MyObject1() override { print_destroyed(this); } private: int value; }; @@ -207,7 +208,7 @@ TEST_SUBMODULE(smart_ptr, m) { class MyObject4b : public MyObject4a { public: MyObject4b(int i) : MyObject4a(i) { print_created(this); } - ~MyObject4b() { print_destroyed(this); } + ~MyObject4b() override { print_destroyed(this); } }; py::class_<MyObject4b, MyObject4a>(m, "MyObject4b") .def(py::init<int>()); @@ -291,7 +292,8 @@ TEST_SUBMODULE(smart_ptr, m) { ~C() { print_destroyed(this); } }; py::class_<C, custom_unique_ptr<C>>(m, "TypeWithMoveOnlyHolder") - .def_static("make", []() { return custom_unique_ptr<C>(new C); }); + .def_static("make", []() { return custom_unique_ptr<C>(new C); }) + .def_static("make_as_object", []() { return py::cast(custom_unique_ptr<C>(new C)); }); // test_holder_with_addressof_operator struct TypeForHolderWithAddressOf { @@ -337,7 +339,9 @@ TEST_SUBMODULE(smart_ptr, m) { // test_shared_ptr_gc // #187: issue involving std::shared_ptr<> return value policy & garbage collection struct ElementBase { - virtual ~ElementBase() { } /* Force creation of virtual table */ + virtual ~ElementBase() = default; /* Force creation of virtual table */ + ElementBase() = default; + ElementBase(const ElementBase&) = delete; }; py::class_<ElementBase, std::shared_ptr<ElementBase>>(m, "ElementBase"); diff --git a/3rdparty/pybind11/tests/test_smart_ptr.py b/3rdparty/pybind11/tests/test_smart_ptr.py index c6627043..c55bffba 100644 --- a/3rdparty/pybind11/tests/test_smart_ptr.py +++ b/3rdparty/pybind11/tests/test_smart_ptr.py @@ -1,11 +1,15 @@ +# -*- coding: utf-8 -*- import pytest -from pybind11_tests import smart_ptr as m -from pybind11_tests import ConstructorStats + +m = pytest.importorskip("pybind11_tests.smart_ptr") +from pybind11_tests import ConstructorStats # noqa: E402 def test_smart_ptr(capture): # Object1 - for i, o in enumerate([m.make_object_1(), m.make_object_2(), m.MyObject1(3)], start=1): + for i, o in enumerate( + [m.make_object_1(), m.make_object_2(), m.MyObject1(3)], start=1 + ): assert o.getRefCount() == 1 with capture: m.print_object_1(o) @@ -14,8 +18,9 @@ def test_smart_ptr(capture): m.print_object_4(o) assert capture == "MyObject1[{i}]\n".format(i=i) * 4 - for i, o in enumerate([m.make_myobject1_1(), m.make_myobject1_2(), m.MyObject1(6), 7], - start=4): + for i, o in enumerate( + [m.make_myobject1_1(), m.make_myobject1_2(), m.MyObject1(6), 7], start=4 + ): print(o) with capture: if not isinstance(o, int): @@ -27,11 +32,15 @@ def test_smart_ptr(capture): m.print_myobject1_2(o) m.print_myobject1_3(o) m.print_myobject1_4(o) - assert capture == "MyObject1[{i}]\n".format(i=i) * (4 if isinstance(o, int) else 8) + + times = 4 if isinstance(o, int) else 8 + assert capture == "MyObject1[{i}]\n".format(i=i) * 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 = ["MyObject1[{}]".format(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 @@ -40,7 +49,9 @@ def test_smart_ptr(capture): assert cstats.move_assignments == 0 # Object2 - for i, o in zip([8, 6, 7], [m.MyObject2(8), m.make_myobject2_1(), m.make_myobject2_2()]): + for i, o in zip( + [8, 6, 7], [m.MyObject2(8), m.make_myobject2_1(), m.make_myobject2_2()] + ): print(o) with capture: m.print_myobject2_1(o) @@ -53,7 +64,7 @@ def test_smart_ptr(capture): assert cstats.alive() == 1 o = None assert cstats.alive() == 0 - assert cstats.values() == ['MyObject2[8]', 'MyObject2[6]', 'MyObject2[7]'] + assert cstats.values() == ["MyObject2[8]", "MyObject2[6]", "MyObject2[7]"] assert cstats.default_constructions == 0 assert cstats.copy_constructions == 0 # assert cstats.move_constructions >= 0 # Doesn't invoke any @@ -61,7 +72,9 @@ def test_smart_ptr(capture): assert cstats.move_assignments == 0 # Object3 - for i, o in zip([9, 8, 9], [m.MyObject3(9), m.make_myobject3_1(), m.make_myobject3_2()]): + for i, o in zip( + [9, 8, 9], [m.MyObject3(9), m.make_myobject3_1(), m.make_myobject3_2()] + ): print(o) with capture: m.print_myobject3_1(o) @@ -74,7 +87,7 @@ def test_smart_ptr(capture): assert cstats.alive() == 1 o = None assert cstats.alive() == 0 - assert cstats.values() == ['MyObject3[9]', 'MyObject3[8]', 'MyObject3[9]'] + assert cstats.values() == ["MyObject3[9]", "MyObject3[8]", "MyObject3[9]"] assert cstats.default_constructions == 0 assert cstats.copy_constructions == 0 # assert cstats.move_constructions >= 0 # Doesn't invoke any @@ -94,7 +107,7 @@ def test_smart_ptr(capture): # ref<> cstats = m.cstats_ref() assert cstats.alive() == 0 - assert cstats.values() == ['from pointer'] * 10 + assert cstats.values() == ["from pointer"] * 10 assert cstats.default_constructions == 30 assert cstats.copy_constructions == 12 # assert cstats.move_constructions >= 0 # Doesn't invoke any @@ -184,7 +197,9 @@ def test_shared_ptr_from_this_and_references(): ref = s.ref # init_holder_helper(holder_ptr=false, owned=false, bad_wp=false) assert stats.alive() == 2 assert s.set_ref(ref) - assert s.set_holder(ref) # std::enable_shared_from_this can create a holder from a reference + assert s.set_holder( + ref + ) # std::enable_shared_from_this can create a holder from a reference bad_wp = s.bad_wp # init_holder_helper(holder_ptr=false, owned=false, bad_wp=true) assert stats.alive() == 2 @@ -198,12 +213,16 @@ def test_shared_ptr_from_this_and_references(): assert s.set_ref(copy) assert s.set_holder(copy) - holder_ref = s.holder_ref # init_holder_helper(holder_ptr=true, owned=false, bad_wp=false) + holder_ref = ( + s.holder_ref + ) # init_holder_helper(holder_ptr=true, owned=false, bad_wp=false) assert stats.alive() == 3 assert s.set_ref(holder_ref) assert s.set_holder(holder_ref) - holder_copy = s.holder_copy # init_holder_helper(holder_ptr=true, owned=true, bad_wp=false) + holder_copy = ( + s.holder_copy + ) # init_holder_helper(holder_ptr=true, owned=true, bad_wp=false) assert stats.alive() == 3 assert s.set_ref(holder_copy) assert s.set_holder(holder_copy) @@ -218,7 +237,10 @@ def test_shared_ptr_from_this_and_references(): def test_move_only_holder(): a = m.TypeWithMoveOnlyHolder.make() + b = m.TypeWithMoveOnlyHolder.make_as_object() stats = ConstructorStats.get(m.TypeWithMoveOnlyHolder) + assert stats.alive() == 2 + del b assert stats.alive() == 1 del a assert stats.alive() == 0 @@ -272,8 +294,10 @@ def test_smart_ptr_from_default(): instance = m.HeldByDefaultHolder() with pytest.raises(RuntimeError) as excinfo: m.HeldByDefaultHolder.load_shared_ptr(instance) - assert "Unable to load a custom holder type from a " \ - "default-holder instance" in str(excinfo.value) + assert ( + "Unable to load a custom holder type from a " + "default-holder instance" in str(excinfo.value) + ) def test_shared_ptr_gc(): diff --git a/3rdparty/pybind11/tests/test_stl.cpp b/3rdparty/pybind11/tests/test_stl.cpp index 207c9fb2..05901627 100644 --- a/3rdparty/pybind11/tests/test_stl.cpp +++ b/3rdparty/pybind11/tests/test_stl.cpp @@ -15,7 +15,7 @@ #include <string> // Test with `std::variant` in C++17 mode, or with `boost::variant` in C++11/14 -#if PYBIND11_HAS_VARIANT +#if defined(PYBIND11_HAS_VARIANT) using std::variant; #elif defined(PYBIND11_TEST_BOOST) && (!defined(_MSC_VER) || _MSC_VER >= 1910) # include <boost/variant.hpp> @@ -47,7 +47,18 @@ struct TplCtorClass { namespace std { template <> struct hash<TplCtorClass> { size_t operator()(const TplCtorClass &) const { return 0; } }; -} +} // namespace std + + +template <template <typename> class OptionalImpl, typename T> +struct OptionalHolder +{ + OptionalHolder() = default; + bool member_initialized() const { + return member && member->initialized; + } + OptionalImpl<T> member = T{}; +}; TEST_SUBMODULE(stl, m) { @@ -154,6 +165,23 @@ TEST_SUBMODULE(stl, m) { .def(py::init<>()) .def(py::init<int>()); + + struct MoveOutDetector + { + MoveOutDetector() = default; + MoveOutDetector(const MoveOutDetector&) = default; + MoveOutDetector(MoveOutDetector&& other) noexcept + : initialized(other.initialized) { + // steal underlying resource + other.initialized = false; + } + bool initialized = true; + }; + py::class_<MoveOutDetector>(m, "MoveOutDetector", "Class with move tracking") + .def(py::init<>()) + .def_readonly("initialized", &MoveOutDetector::initialized); + + #ifdef PYBIND11_HAS_OPTIONAL // test_optional m.attr("has_optional") = true; @@ -175,6 +203,12 @@ TEST_SUBMODULE(stl, m) { m.def("nodefer_none_optional", [](std::optional<int>) { return true; }); m.def("nodefer_none_optional", [](py::none) { return false; }); + + using opt_holder = OptionalHolder<std::optional, MoveOutDetector>; + py::class_<opt_holder>(m, "OptionalHolder", "Class with optional member") + .def(py::init<>()) + .def_readonly("member", &opt_holder::member) + .def("member_initialized", &opt_holder::member_initialized); #endif #ifdef PYBIND11_HAS_EXP_OPTIONAL @@ -195,6 +229,12 @@ TEST_SUBMODULE(stl, m) { m.def("test_no_assign_exp", [](const exp_opt_no_assign &x) { return x ? x->value : 42; }, py::arg_v("x", std::experimental::nullopt, "None")); + + using opt_exp_holder = OptionalHolder<std::experimental::optional, MoveOutDetector>; + py::class_<opt_exp_holder>(m, "OptionalExpHolder", "Class with optional member") + .def(py::init<>()) + .def_readonly("member", &opt_exp_holder::member) + .def("member_initialized", &opt_exp_holder::member_initialized); #endif #ifdef PYBIND11_HAS_VARIANT diff --git a/3rdparty/pybind11/tests/test_stl.py b/3rdparty/pybind11/tests/test_stl.py index 2335cb9f..33001754 100644 --- a/3rdparty/pybind11/tests/test_stl.py +++ b/3rdparty/pybind11/tests/test_stl.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- import pytest from pybind11_tests import stl as m @@ -87,7 +88,7 @@ def test_recursive_casting(): assert m.cast_rv_nested() == [[[{"b": "rvalue", "c": "rvalue"}], [{"a": "rvalue"}]]] assert m.cast_lv_nested() == { "a": [[["lvalue", "lvalue"]], [["lvalue", "lvalue"]]], - "b": [[["lvalue", "lvalue"], ["lvalue", "lvalue"]]] + "b": [[["lvalue", "lvalue"], ["lvalue", "lvalue"]]], } # Issue #853 test case: @@ -105,15 +106,15 @@ def test_move_out_container(): assert [x.value for x in moved_out_list] == [0, 1, 2] -@pytest.mark.skipif(not hasattr(m, "has_optional"), reason='no <optional>') +@pytest.mark.skipif(not hasattr(m, "has_optional"), reason="no <optional>") def test_optional(): assert m.double_or_zero(None) == 0 assert m.double_or_zero(42) == 84 - pytest.raises(TypeError, m.double_or_zero, 'foo') + pytest.raises(TypeError, m.double_or_zero, "foo") assert m.half_or_none(0) is None assert m.half_or_none(42) == 21 - pytest.raises(TypeError, m.half_or_none, 'foo') + pytest.raises(TypeError, m.half_or_none, "foo") assert m.test_nullopt() == 42 assert m.test_nullopt(None) == 42 @@ -127,16 +128,23 @@ def test_optional(): assert m.nodefer_none_optional(None) + holder = m.OptionalHolder() + mvalue = holder.member + assert mvalue.initialized + assert holder.member_initialized() -@pytest.mark.skipif(not hasattr(m, "has_exp_optional"), reason='no <experimental/optional>') + +@pytest.mark.skipif( + not hasattr(m, "has_exp_optional"), reason="no <experimental/optional>" +) def test_exp_optional(): assert m.double_or_zero_exp(None) == 0 assert m.double_or_zero_exp(42) == 84 - pytest.raises(TypeError, m.double_or_zero_exp, 'foo') + pytest.raises(TypeError, m.double_or_zero_exp, "foo") assert m.half_or_none_exp(0) is None assert m.half_or_none_exp(42) == 21 - pytest.raises(TypeError, m.half_or_none_exp, 'foo') + pytest.raises(TypeError, m.half_or_none_exp, "foo") assert m.test_nullopt_exp() == 42 assert m.test_nullopt_exp(None) == 42 @@ -148,8 +156,13 @@ def test_exp_optional(): assert m.test_no_assign_exp(m.NoAssign(43)) == 43 pytest.raises(TypeError, m.test_no_assign_exp, 43) + holder = m.OptionalExpHolder() + mvalue = holder.member + assert mvalue.initialized + assert holder.member_initialized() + -@pytest.mark.skipif(not hasattr(m, "load_variant"), reason='no <variant>') +@pytest.mark.skipif(not hasattr(m, "load_variant"), reason="no <variant>") def test_variant(doc): assert m.load_variant(1) == "int" assert m.load_variant("1") == "std::string" @@ -161,34 +174,44 @@ def test_variant(doc): assert m.cast_variant() == (5, "Hello") - assert doc(m.load_variant) == "load_variant(arg0: Union[int, str, float, None]) -> str" + assert ( + doc(m.load_variant) == "load_variant(arg0: Union[int, str, float, None]) -> str" + ) def test_vec_of_reference_wrapper(): """#171: Can't return reference wrappers (or STL structures containing them)""" - assert str(m.return_vec_of_reference_wrapper(UserType(4))) == \ - "[UserType(1), UserType(2), UserType(3), UserType(4)]" + assert ( + str(m.return_vec_of_reference_wrapper(UserType(4))) + == "[UserType(1), UserType(2), UserType(3), UserType(4)]" + ) def test_stl_pass_by_pointer(msg): """Passing nullptr or None to an STL container pointer is not expected to work""" with pytest.raises(TypeError) as excinfo: m.stl_pass_by_pointer() # default value is `nullptr` - assert msg(excinfo.value) == """ + assert ( + msg(excinfo.value) + == """ stl_pass_by_pointer(): incompatible function arguments. The following argument types are supported: 1. (v: List[int] = None) -> List[int] Invoked with: """ # noqa: E501 line too long + ) with pytest.raises(TypeError) as excinfo: m.stl_pass_by_pointer(None) - assert msg(excinfo.value) == """ + assert ( + msg(excinfo.value) + == """ stl_pass_by_pointer(): incompatible function arguments. The following argument types are supported: 1. (v: List[int] = None) -> List[int] Invoked with: None """ # noqa: E501 line too long + ) assert m.stl_pass_by_pointer([1, 2, 3]) == [1, 2, 3] @@ -198,10 +221,12 @@ def test_missing_header_message(): <pybind11/stl.h> should result in a helpful suggestion in the error message""" import pybind11_cross_module_tests as cm - expected_message = ("Did you forget to `#include <pybind11/stl.h>`? Or <pybind11/complex.h>,\n" - "<pybind11/functional.h>, <pybind11/chrono.h>, etc. Some automatic\n" - "conversions are optional and require extra headers to be included\n" - "when compiling your pybind11 module.") + expected_message = ( + "Did you forget to `#include <pybind11/stl.h>`? Or <pybind11/complex.h>,\n" + "<pybind11/functional.h>, <pybind11/chrono.h>, etc. Some automatic\n" + "conversions are optional and require extra headers to be included\n" + "when compiling your pybind11 module." + ) with pytest.raises(TypeError) as excinfo: cm.missing_header_arg([1.0, 2.0, 3.0]) @@ -215,9 +240,9 @@ def test_missing_header_message(): def test_function_with_string_and_vector_string_arg(): """Check if a string is NOT implicitly converted to a list, which was the behavior before fix of issue #1258""" - assert m.func_with_string_or_vector_string_arg_overload(('A', 'B', )) == 2 - assert m.func_with_string_or_vector_string_arg_overload(['A', 'B']) == 2 - assert m.func_with_string_or_vector_string_arg_overload('A') == 3 + assert m.func_with_string_or_vector_string_arg_overload(("A", "B")) == 2 + assert m.func_with_string_or_vector_string_arg_overload(["A", "B"]) == 2 + assert m.func_with_string_or_vector_string_arg_overload("A") == 3 def test_stl_ownership(): @@ -236,6 +261,6 @@ def test_array_cast_sequence(): def test_issue_1561(): """ check fix for issue #1561 """ bar = m.Issue1561Outer() - bar.list = [m.Issue1561Inner('bar')] + bar.list = [m.Issue1561Inner("bar")] bar.list - assert bar.list[0].data == 'bar' + assert bar.list[0].data == "bar" diff --git a/3rdparty/pybind11/tests/test_stl_binders.cpp b/3rdparty/pybind11/tests/test_stl_binders.cpp index 86888740..1c0df984 100644 --- a/3rdparty/pybind11/tests/test_stl_binders.cpp +++ b/3rdparty/pybind11/tests/test_stl_binders.cpp @@ -117,7 +117,7 @@ TEST_SUBMODULE(stl_binders, m) { }); // The rest depends on numpy: - try { py::module::import("numpy"); } + try { py::module_::import("numpy"); } catch (...) { return; } // test_vector_buffer_numpy diff --git a/3rdparty/pybind11/tests/test_stl_binders.py b/3rdparty/pybind11/tests/test_stl_binders.py index c7b7e853..84132a2b 100644 --- a/3rdparty/pybind11/tests/test_stl_binders.py +++ b/3rdparty/pybind11/tests/test_stl_binders.py @@ -1,9 +1,9 @@ +# -*- coding: utf-8 -*- import pytest -import sys -from pybind11_tests import stl_binders as m -with pytest.suppress(ImportError): - import numpy as np +import env # noqa: F401 + +from pybind11_tests import stl_binders as m def test_vector_int(): @@ -45,7 +45,7 @@ def test_vector_int(): # test error handling, and that the vector is unchanged with pytest.raises(RuntimeError): - v_int2.extend([8, 'a']) + v_int2.extend([8, "a"]) assert v_int2 == m.VectorInt([0, 99, 2, 3, 4, 5, 6, 7]) @@ -67,30 +67,34 @@ def test_vector_int(): v_int2.clear() assert len(v_int2) == 0 -# related to the PyPy's buffer protocol. -@pytest.unsupported_on_pypy + +# Older PyPy's failed here, related to the PyPy's buffer protocol. def test_vector_buffer(): b = bytearray([1, 2, 3, 4]) v = m.VectorUChar(b) assert v[1] == 2 v[2] = 5 mv = memoryview(v) # We expose the buffer interface - if sys.version_info.major > 2: + if not env.PY2: assert mv[2] == 5 mv[2] = 6 else: - assert mv[2] == '\x05' - mv[2] = '\x06' + assert mv[2] == "\x05" + mv[2] = "\x06" assert v[2] == 6 + if not env.PY2: + mv = memoryview(b) + v = m.VectorUChar(mv[::2]) + assert v[1] == 3 + with pytest.raises(RuntimeError) as excinfo: m.create_undeclstruct() # Undeclared struct contents, no buffer interface assert "NumPy type info missing for " in str(excinfo.value) -@pytest.unsupported_on_pypy -@pytest.requires_numpy def test_vector_buffer_numpy(): + np = pytest.importorskip("numpy") a = np.array([1, 2, 3, 4], dtype=np.int32) with pytest.raises(TypeError): m.VectorInt(a) @@ -110,13 +114,23 @@ def test_vector_buffer_numpy(): v = m.get_vectorstruct() assert v[0].x == 5 ma = np.asarray(v) - ma[1]['x'] = 99 + ma[1]["x"] = 99 assert v[1].x == 99 - v = m.VectorStruct(np.zeros(3, dtype=np.dtype([('w', 'bool'), ('x', 'I'), - ('y', 'float64'), ('z', 'bool')], align=True))) + v = m.VectorStruct( + np.zeros( + 3, + dtype=np.dtype( + [("w", "bool"), ("x", "I"), ("y", "float64"), ("z", "bool")], align=True + ), + ) + ) assert len(v) == 3 + b = np.array([1, 2, 3, 4], dtype=np.uint8) + v = m.VectorUChar(b[::2]) + assert v[1] == 3 + def test_vector_bool(): import pybind11_cross_module_tests as cm @@ -143,31 +157,31 @@ def test_vector_custom(): def test_map_string_double(): mm = m.MapStringDouble() - mm['a'] = 1 - mm['b'] = 2.5 + mm["a"] = 1 + mm["b"] = 2.5 - assert list(mm) == ['a', 'b'] - assert list(mm.items()) == [('a', 1), ('b', 2.5)] + assert list(mm) == ["a", "b"] + assert list(mm.items()) == [("a", 1), ("b", 2.5)] assert str(mm) == "MapStringDouble{a: 1, b: 2.5}" um = m.UnorderedMapStringDouble() - um['ua'] = 1.1 - um['ub'] = 2.6 + um["ua"] = 1.1 + um["ub"] = 2.6 - assert sorted(list(um)) == ['ua', 'ub'] - assert sorted(list(um.items())) == [('ua', 1.1), ('ub', 2.6)] + assert sorted(list(um)) == ["ua", "ub"] + assert sorted(list(um.items())) == [("ua", 1.1), ("ub", 2.6)] assert "UnorderedMapStringDouble" in str(um) def test_map_string_double_const(): mc = m.MapStringDoubleConst() - mc['a'] = 10 - mc['b'] = 20.5 + mc["a"] = 10 + mc["b"] = 20.5 assert str(mc) == "MapStringDoubleConst{a: 10, b: 20.5}" umc = m.UnorderedMapStringDoubleConst() - umc['a'] = 11 - umc['b'] = 21.5 + umc["a"] = 11 + umc["b"] = 21.5 str(umc) @@ -188,7 +202,7 @@ def test_noncopyable_containers(): i = 1 for j in dnc: - assert(j.value == i) + assert j.value == i i += 1 # std::map @@ -221,7 +235,8 @@ def test_noncopyable_containers(): for j in range(0, 5): assert nvnc[i][j].value == j + 1 - for k, v in nvnc.items(): + # Note: maps do not have .values() + for _, v in nvnc.items(): for i, j in enumerate(v, start=1): assert j.value == i @@ -232,7 +247,7 @@ def test_noncopyable_containers(): assert nmnc[i][j].value == 10 * j vsum = 0 - for k_o, v_o in nmnc.items(): + for _, v_o in nmnc.items(): for k_i, v_i in v_o.items(): assert v_i.value == 10 * k_i vsum += v_i.value @@ -246,7 +261,7 @@ def test_noncopyable_containers(): assert numnc[i][j].value == 10 * j vsum = 0 - for k_o, v_o in numnc.items(): + for _, v_o in numnc.items(): for k_i, v_i in v_o.items(): assert v_i.value == 10 * k_i vsum += v_i.value @@ -256,21 +271,21 @@ def test_noncopyable_containers(): def test_map_delitem(): mm = m.MapStringDouble() - mm['a'] = 1 - mm['b'] = 2.5 + mm["a"] = 1 + mm["b"] = 2.5 - assert list(mm) == ['a', 'b'] - assert list(mm.items()) == [('a', 1), ('b', 2.5)] - del mm['a'] - assert list(mm) == ['b'] - assert list(mm.items()) == [('b', 2.5)] + assert list(mm) == ["a", "b"] + assert list(mm.items()) == [("a", 1), ("b", 2.5)] + del mm["a"] + assert list(mm) == ["b"] + assert list(mm.items()) == [("b", 2.5)] um = m.UnorderedMapStringDouble() - um['ua'] = 1.1 - um['ub'] = 2.6 - - assert sorted(list(um)) == ['ua', 'ub'] - assert sorted(list(um.items())) == [('ua', 1.1), ('ub', 2.6)] - del um['ua'] - assert sorted(list(um)) == ['ub'] - assert sorted(list(um.items())) == [('ub', 2.6)] + um["ua"] = 1.1 + um["ub"] = 2.6 + + assert sorted(list(um)) == ["ua", "ub"] + assert sorted(list(um.items())) == [("ua", 1.1), ("ub", 2.6)] + del um["ua"] + assert sorted(list(um)) == ["ub"] + assert sorted(list(um.items())) == [("ub", 2.6)] diff --git a/3rdparty/pybind11/tests/test_tagbased_polymorphic.cpp b/3rdparty/pybind11/tests/test_tagbased_polymorphic.cpp index 272e460c..838a168d 100644 --- a/3rdparty/pybind11/tests/test_tagbased_polymorphic.cpp +++ b/3rdparty/pybind11/tests/test_tagbased_polymorphic.cpp @@ -12,6 +12,12 @@ struct Animal { + // Make this type also a "standard" polymorphic type, to confirm that + // specializing polymorphic_type_hook using enable_if_t still works + // (https://github.com/pybind/pybind11/pull/2016/). + virtual ~Animal() = default; + + // Enum for tag-based polymorphism. enum class Kind { Unknown = 0, Dog = 100, Labrador, Chihuahua, LastDog = 199, @@ -111,7 +117,7 @@ namespace pybind11 { static const void *get(const itype *src, const std::type_info*& type) { type = src ? Animal::type_of_kind(src->kind) : nullptr; return src; } }; -} +} // namespace pybind11 TEST_SUBMODULE(tagbased_polymorphic, m) { py::class_<Animal>(m, "Animal") diff --git a/3rdparty/pybind11/tests/test_tagbased_polymorphic.py b/3rdparty/pybind11/tests/test_tagbased_polymorphic.py index 2574d7de..64eb8a3c 100644 --- a/3rdparty/pybind11/tests/test_tagbased_polymorphic.py +++ b/3rdparty/pybind11/tests/test_tagbased_polymorphic.py @@ -1,19 +1,28 @@ +# -*- coding: utf-8 -*- from pybind11_tests import tagbased_polymorphic as m def test_downcast(): zoo = m.create_zoo() assert [type(animal) for animal in zoo] == [ - m.Labrador, m.Dog, m.Chihuahua, m.Cat, m.Panther + m.Labrador, + m.Dog, + m.Chihuahua, + m.Cat, + m.Panther, ] assert [animal.name for animal in zoo] == [ - "Fido", "Ginger", "Hertzl", "Tiger", "Leo" + "Fido", + "Ginger", + "Hertzl", + "Tiger", + "Leo", ] zoo[1].sound = "woooooo" assert [dog.bark() for dog in zoo[:3]] == [ "Labrador Fido goes WOOF!", "Dog Ginger goes woooooo", - "Chihuahua Hertzl goes iyiyiyiyiyi and runs in circles" + "Chihuahua Hertzl goes iyiyiyiyiyi and runs in circles", ] assert [cat.purr() for cat in zoo[3:]] == ["mrowr", "mrrrRRRRRR"] zoo[0].excitement -= 1000 diff --git a/3rdparty/pybind11/tests/test_union.py b/3rdparty/pybind11/tests/test_union.py index e1866e70..2a2c12fb 100644 --- a/3rdparty/pybind11/tests/test_union.py +++ b/3rdparty/pybind11/tests/test_union.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- from pybind11_tests import union_ as m diff --git a/3rdparty/pybind11/tests/test_virtual_functions.cpp b/3rdparty/pybind11/tests/test_virtual_functions.cpp index ccf018d9..685d64a7 100644 --- a/3rdparty/pybind11/tests/test_virtual_functions.cpp +++ b/3rdparty/pybind11/tests/test_virtual_functions.cpp @@ -47,7 +47,7 @@ public: int run(int value) override { /* Generate wrapping code that enables native function overloading */ - PYBIND11_OVERLOAD( + PYBIND11_OVERRIDE( int, /* Return type */ ExampleVirt, /* Parent class */ run, /* Name of function */ @@ -56,7 +56,7 @@ public: } bool run_bool() override { - PYBIND11_OVERLOAD_PURE( + PYBIND11_OVERRIDE_PURE( bool, /* Return type */ ExampleVirt, /* Parent class */ run_bool, /* Name of function */ @@ -66,7 +66,7 @@ public: } void pure_virtual() override { - PYBIND11_OVERLOAD_PURE( + PYBIND11_OVERRIDE_PURE( void, /* Return type */ ExampleVirt, /* Parent class */ pure_virtual, /* Name of function */ @@ -78,7 +78,7 @@ public: // We can return reference types for compatibility with C++ virtual interfaces that do so, but // note they have some significant limitations (see the documentation). const std::string &get_string1() override { - PYBIND11_OVERLOAD( + PYBIND11_OVERRIDE( const std::string &, /* Return type */ ExampleVirt, /* Parent class */ get_string1, /* Name of function */ @@ -87,7 +87,7 @@ public: } const std::string *get_string2() override { - PYBIND11_OVERLOAD( + PYBIND11_OVERRIDE( const std::string *, /* Return type */ ExampleVirt, /* Parent class */ get_string2, /* Name of function */ @@ -129,7 +129,9 @@ private: class NCVirt { public: - virtual ~NCVirt() { } + virtual ~NCVirt() = default; + NCVirt() = default; + NCVirt(const NCVirt&) = delete; virtual NonCopyable get_noncopyable(int a, int b) { return NonCopyable(a, b); } virtual Movable get_movable(int a, int b) = 0; @@ -137,13 +139,13 @@ public: std::string print_movable(int a, int b) { return get_movable(a, b).get_value(); } }; class NCVirtTrampoline : public NCVirt { -#if !defined(__INTEL_COMPILER) +#if !defined(__INTEL_COMPILER) && !defined(__CUDACC__) && !defined(__PGIC__) NonCopyable get_noncopyable(int a, int b) override { - PYBIND11_OVERLOAD(NonCopyable, NCVirt, get_noncopyable, a, b); + PYBIND11_OVERRIDE(NonCopyable, NCVirt, get_noncopyable, a, b); } #endif Movable get_movable(int a, int b) override { - PYBIND11_OVERLOAD_PURE(Movable, NCVirt, get_movable, a, b); + PYBIND11_OVERRIDE_PURE(Movable, NCVirt, get_movable, a, b); } }; @@ -151,11 +153,13 @@ struct Base { /* for some reason MSVC2015 can't compile this if the function is pure virtual */ virtual std::string dispatch() const { return {}; }; virtual ~Base() = default; + Base() = default; + Base(const Base&) = delete; }; struct DispatchIssue : Base { - virtual std::string dispatch() const { - PYBIND11_OVERLOAD_PURE(std::string, Base, dispatch, /* no arguments */); + std::string dispatch() const override { + PYBIND11_OVERRIDE_PURE(std::string, Base, dispatch, /* no arguments */); } }; @@ -183,7 +187,7 @@ static void test_gil_from_thread() { // Forward declaration (so that we can put the main tests here; the inherited virtual approaches are // rather long). -void initialize_inherited_virtuals(py::module &m); +void initialize_inherited_virtuals(py::module_ &m); TEST_SUBMODULE(virtual_functions, m) { // test_override @@ -201,7 +205,7 @@ TEST_SUBMODULE(virtual_functions, m) { .def(py::init<int, int>()); // test_move_support -#if !defined(__INTEL_COMPILER) +#if !defined(__INTEL_COMPILER) && !defined(__CUDACC__) && !defined(__PGIC__) py::class_<NCVirt, NCVirtTrampoline>(m, "NCVirt") .def(py::init<>()) .def("get_noncopyable", &NCVirt::get_noncopyable) @@ -221,19 +225,22 @@ TEST_SUBMODULE(virtual_functions, m) { // don't invoke Python dispatch classes by default when instantiating C++ classes // that were not extended on the Python side struct A { - virtual ~A() {} + A() = default; + A(const A&) = delete; + virtual ~A() = default; virtual void f() { py::print("A.f()"); } }; struct PyA : A { PyA() { py::print("PyA.PyA()"); } - ~PyA() { py::print("PyA.~PyA()"); } + PyA(const PyA&) = delete; + ~PyA() override { py::print("PyA.~PyA()"); } void f() override { py::print("PyA.f()"); // This convolution just gives a `void`, but tests that PYBIND11_TYPE() works to protect // a type containing a , - PYBIND11_OVERLOAD(PYBIND11_TYPE(typename std::enable_if<true, void>::type), A, f); + PYBIND11_OVERRIDE(PYBIND11_TYPE(typename std::enable_if<true, void>::type), A, f); } }; @@ -246,16 +253,19 @@ TEST_SUBMODULE(virtual_functions, m) { // test_alias_delay_initialization2 // ... unless we explicitly request it, as in this example: struct A2 { - virtual ~A2() {} + A2() = default; + A2(const A2&) = delete; + virtual ~A2() = default; virtual void f() { py::print("A2.f()"); } }; struct PyA2 : A2 { PyA2() { py::print("PyA2.PyA2()"); } - ~PyA2() { py::print("PyA2.~PyA2()"); } + PyA2(const PyA2&) = delete; + ~PyA2() override { py::print("PyA2.~PyA2()"); } void f() override { py::print("PyA2.f()"); - PYBIND11_OVERLOAD(void, A2, f); + PYBIND11_OVERRIDE(void, A2, f); } }; @@ -282,6 +292,8 @@ TEST_SUBMODULE(virtual_functions, m) { std::string v; A a; explicit OverrideTest(const std::string &v) : v{v} {} + OverrideTest() = default; + OverrideTest(const OverrideTest&) = delete; virtual std::string str_value() { return v; } virtual std::string &str_ref() { return v; } virtual A A_value() { return a; } @@ -292,19 +304,19 @@ TEST_SUBMODULE(virtual_functions, m) { class PyOverrideTest : public OverrideTest { public: using OverrideTest::OverrideTest; - std::string str_value() override { PYBIND11_OVERLOAD(std::string, OverrideTest, str_value); } + std::string str_value() override { PYBIND11_OVERRIDE(std::string, OverrideTest, str_value); } // Not allowed (uncommenting should hit a static_assert failure): we can't get a reference // to a python numeric value, since we only copy values in the numeric type caster: -// std::string &str_ref() override { PYBIND11_OVERLOAD(std::string &, OverrideTest, str_ref); } +// std::string &str_ref() override { PYBIND11_OVERRIDE(std::string &, OverrideTest, str_ref); } // But we can work around it like this: private: std::string _tmp; - std::string str_ref_helper() { PYBIND11_OVERLOAD(std::string, OverrideTest, str_ref); } + std::string str_ref_helper() { PYBIND11_OVERRIDE(std::string, OverrideTest, str_ref); } public: std::string &str_ref() override { return _tmp = str_ref_helper(); } - A A_value() override { PYBIND11_OVERLOAD(A, OverrideTest, A_value); } - A &A_ref() override { PYBIND11_OVERLOAD(A &, OverrideTest, A_ref); } + A A_value() override { PYBIND11_OVERRIDE(A, OverrideTest, A_value); } + A &A_ref() override { PYBIND11_OVERRIDE(A &, OverrideTest, A_ref); } }; py::class_<OverrideTest::A>(m, "OverrideTest_A") @@ -339,6 +351,8 @@ public: \ return say_something(1) + " " + std::to_string(unlucky_number()); \ } A_METHODS + A_Repeat() = default; + A_Repeat(const A_Repeat&) = delete; virtual ~A_Repeat() = default; }; class B_Repeat : public A_Repeat { @@ -364,7 +378,12 @@ D_METHODS }; // Base classes for templated inheritance trampolines. Identical to the repeat-everything version: -class A_Tpl { A_METHODS; virtual ~A_Tpl() = default; }; +class A_Tpl { + A_METHODS; + A_Tpl() = default; + A_Tpl(const A_Tpl&) = delete; + virtual ~A_Tpl() = default; +}; class B_Tpl : public A_Tpl { B_METHODS }; class C_Tpl : public B_Tpl { C_METHODS }; class D_Tpl : public C_Tpl { D_METHODS }; @@ -374,29 +393,29 @@ class D_Tpl : public C_Tpl { D_METHODS }; class PyA_Repeat : public A_Repeat { public: using A_Repeat::A_Repeat; - int unlucky_number() override { PYBIND11_OVERLOAD_PURE(int, A_Repeat, unlucky_number, ); } - std::string say_something(unsigned times) override { PYBIND11_OVERLOAD(std::string, A_Repeat, say_something, times); } + int unlucky_number() override { PYBIND11_OVERRIDE_PURE(int, A_Repeat, unlucky_number, ); } + std::string say_something(unsigned times) override { PYBIND11_OVERRIDE(std::string, A_Repeat, say_something, times); } }; class PyB_Repeat : public B_Repeat { public: using B_Repeat::B_Repeat; - int unlucky_number() override { PYBIND11_OVERLOAD(int, B_Repeat, unlucky_number, ); } - std::string say_something(unsigned times) override { PYBIND11_OVERLOAD(std::string, B_Repeat, say_something, times); } - double lucky_number() override { PYBIND11_OVERLOAD(double, B_Repeat, lucky_number, ); } + int unlucky_number() override { PYBIND11_OVERRIDE(int, B_Repeat, unlucky_number, ); } + std::string say_something(unsigned times) override { PYBIND11_OVERRIDE(std::string, B_Repeat, say_something, times); } + double lucky_number() override { PYBIND11_OVERRIDE(double, B_Repeat, lucky_number, ); } }; class PyC_Repeat : public C_Repeat { public: using C_Repeat::C_Repeat; - int unlucky_number() override { PYBIND11_OVERLOAD(int, C_Repeat, unlucky_number, ); } - std::string say_something(unsigned times) override { PYBIND11_OVERLOAD(std::string, C_Repeat, say_something, times); } - double lucky_number() override { PYBIND11_OVERLOAD(double, C_Repeat, lucky_number, ); } + int unlucky_number() override { PYBIND11_OVERRIDE(int, C_Repeat, unlucky_number, ); } + std::string say_something(unsigned times) override { PYBIND11_OVERRIDE(std::string, C_Repeat, say_something, times); } + double lucky_number() override { PYBIND11_OVERRIDE(double, C_Repeat, lucky_number, ); } }; class PyD_Repeat : public D_Repeat { public: using D_Repeat::D_Repeat; - int unlucky_number() override { PYBIND11_OVERLOAD(int, D_Repeat, unlucky_number, ); } - std::string say_something(unsigned times) override { PYBIND11_OVERLOAD(std::string, D_Repeat, say_something, times); } - double lucky_number() override { PYBIND11_OVERLOAD(double, D_Repeat, lucky_number, ); } + int unlucky_number() override { PYBIND11_OVERRIDE(int, D_Repeat, unlucky_number, ); } + std::string say_something(unsigned times) override { PYBIND11_OVERRIDE(std::string, D_Repeat, say_something, times); } + double lucky_number() override { PYBIND11_OVERRIDE(double, D_Repeat, lucky_number, ); } }; // Inheritance approach 2: templated trampoline classes. @@ -417,15 +436,15 @@ template <class Base = A_Tpl> class PyA_Tpl : public Base { public: using Base::Base; // Inherit constructors - int unlucky_number() override { PYBIND11_OVERLOAD_PURE(int, Base, unlucky_number, ); } - std::string say_something(unsigned times) override { PYBIND11_OVERLOAD(std::string, Base, say_something, times); } + int unlucky_number() override { PYBIND11_OVERRIDE_PURE(int, Base, unlucky_number, ); } + std::string say_something(unsigned times) override { PYBIND11_OVERRIDE(std::string, Base, say_something, times); } }; template <class Base = B_Tpl> class PyB_Tpl : public PyA_Tpl<Base> { public: using PyA_Tpl<Base>::PyA_Tpl; // Inherit constructors (via PyA_Tpl's inherited constructors) - int unlucky_number() override { PYBIND11_OVERLOAD(int, Base, unlucky_number, ); } - double lucky_number() override { PYBIND11_OVERLOAD(double, Base, lucky_number, ); } + int unlucky_number() override { PYBIND11_OVERRIDE(int, Base, unlucky_number, ); } + double lucky_number() override { PYBIND11_OVERRIDE(double, Base, lucky_number, ); } }; // Since C_Tpl and D_Tpl don't declare any new virtual methods, we don't actually need these (we can // use PyB_Tpl<C_Tpl> and PyB_Tpl<D_Tpl> for the trampoline classes instead): @@ -440,7 +459,7 @@ public: }; */ -void initialize_inherited_virtuals(py::module &m) { +void initialize_inherited_virtuals(py::module_ &m) { // test_inherited_virtuals // Method 1: repeat diff --git a/3rdparty/pybind11/tests/test_virtual_functions.py b/3rdparty/pybind11/tests/test_virtual_functions.py index 5ce9abd3..ae199301 100644 --- a/3rdparty/pybind11/tests/test_virtual_functions.py +++ b/3rdparty/pybind11/tests/test_virtual_functions.py @@ -1,7 +1,10 @@ +# -*- coding: utf-8 -*- import pytest -from pybind11_tests import virtual_functions as m -from pybind11_tests import ConstructorStats +import env # noqa: F401 + +m = pytest.importorskip("pybind11_tests.virtual_functions") +from pybind11_tests import ConstructorStats # noqa: E402 def test_override(capture, msg): @@ -11,18 +14,18 @@ def test_override(capture, msg): self.data = "Hello world" def run(self, value): - print('ExtendedExampleVirt::run(%i), calling parent..' % value) + print("ExtendedExampleVirt::run(%i), calling parent.." % value) return super(ExtendedExampleVirt, self).run(value + 1) def run_bool(self): - print('ExtendedExampleVirt::run_bool()') + print("ExtendedExampleVirt::run_bool()") return False def get_string1(self): return "override1" def pure_virtual(self): - print('ExtendedExampleVirt::pure_virtual(): %s' % self.data) + print("ExtendedExampleVirt::pure_virtual(): %s" % self.data) class ExtendedExampleVirt2(ExtendedExampleVirt): def __init__(self, state): @@ -34,21 +37,30 @@ def test_override(capture, msg): ex12 = m.ExampleVirt(10) with capture: assert m.runExampleVirt(ex12, 20) == 30 - assert capture == """ + assert ( + capture + == """ Original implementation of ExampleVirt::run(state=10, value=20, str1=default1, str2=default2) """ # noqa: E501 line too long + ) with pytest.raises(RuntimeError) as excinfo: m.runExampleVirtVirtual(ex12) - assert msg(excinfo.value) == 'Tried to call pure virtual function "ExampleVirt::pure_virtual"' + assert ( + msg(excinfo.value) + == 'Tried to call pure virtual function "ExampleVirt::pure_virtual"' + ) ex12p = ExtendedExampleVirt(10) with capture: assert m.runExampleVirt(ex12p, 20) == 32 - assert capture == """ + assert ( + capture + == """ ExtendedExampleVirt::run(20), calling parent.. Original implementation of ExampleVirt::run(state=11, value=21, str1=override1, str2=default2) """ # noqa: E501 line too long + ) with capture: assert m.runExampleVirtBool(ex12p) is False assert capture == "ExtendedExampleVirt::run_bool()" @@ -59,16 +71,19 @@ def test_override(capture, msg): ex12p2 = ExtendedExampleVirt2(15) with capture: assert m.runExampleVirt(ex12p2, 50) == 68 - assert capture == """ + assert ( + capture + == """ ExtendedExampleVirt::run(50), calling parent.. Original implementation of ExampleVirt::run(state=17, value=51, str1=override1, str2=override2) """ # noqa: E501 line too long + ) cstats = ConstructorStats.get(m.ExampleVirt) assert cstats.alive() == 3 del ex12, ex12p, ex12p2 assert cstats.alive() == 0 - assert cstats.values() == ['10', '11', '17'] + assert cstats.values() == ["10", "11", "17"] assert cstats.copy_constructions == 0 assert cstats.move_constructions >= 0 @@ -79,6 +94,7 @@ def test_alias_delay_initialization1(capture): If we just create and use an A instance directly, the trampoline initialization is bypassed and we only initialize an A() instead (for performance reasons). """ + class B(m.A): def __init__(self): super(B, self).__init__() @@ -100,12 +116,15 @@ def test_alias_delay_initialization1(capture): m.call_f(b) del b pytest.gc_collect() - assert capture == """ + assert ( + capture + == """ PyA.PyA() PyA.f() In python f() PyA.~PyA() """ + ) def test_alias_delay_initialization2(capture): @@ -115,6 +134,7 @@ def test_alias_delay_initialization2(capture): performance penalty, it also allows us to do more things with the trampoline class such as defining local variables and performing construction/destruction. """ + class B2(m.A2): def __init__(self): super(B2, self).__init__() @@ -132,7 +152,9 @@ def test_alias_delay_initialization2(capture): m.call_f(a3) del a3 pytest.gc_collect() - assert capture == """ + assert ( + capture + == """ PyA2.PyA2() PyA2.f() A2.f() @@ -142,6 +164,7 @@ def test_alias_delay_initialization2(capture): A2.f() PyA2.~PyA2() """ + ) # Python subclass version with capture: @@ -149,18 +172,23 @@ def test_alias_delay_initialization2(capture): m.call_f(b2) del b2 pytest.gc_collect() - assert capture == """ + assert ( + capture + == """ PyA2.PyA2() PyA2.f() In python B2.f() PyA2.~PyA2() """ + ) # PyPy: Reference count > 1 causes call with noncopyable instance # to fail in ncv1.print_nc() -@pytest.unsupported_on_pypy -@pytest.mark.skipif(not hasattr(m, "NCVirt"), reason="NCVirt test broken on ICPC") +@pytest.mark.xfail("env.PYPY") +@pytest.mark.skipif( + not hasattr(m, "NCVirt"), reason="NCVirt does not work on Intel/PGI/NVCC compilers" +) def test_move_support(): class NCVirtExt(m.NCVirt): def get_noncopyable(self, a, b): @@ -199,8 +227,8 @@ def test_move_support(): del ncv1, ncv2 assert nc_stats.alive() == 0 assert mv_stats.alive() == 0 - assert nc_stats.values() == ['4', '9', '9', '9'] - assert mv_stats.values() == ['4', '5', '7', '7'] + assert nc_stats.values() == ["4", "9", "9", "9"] + assert mv_stats.values() == ["4", "5", "7", "7"] assert nc_stats.copy_constructions == 0 assert mv_stats.copy_constructions == 1 assert nc_stats.move_constructions >= 0 @@ -209,6 +237,7 @@ def test_move_support(): def test_dispatch_issue(msg): """#159: virtual function dispatch has problems with similar-named functions""" + class PyClass1(m.DispatchIssue): def dispatch(self): return "Yay.." @@ -217,7 +246,10 @@ def test_dispatch_issue(msg): def dispatch(self): with pytest.raises(RuntimeError) as excinfo: super(PyClass2, self).dispatch() - assert msg(excinfo.value) == 'Tried to call pure virtual function "Base::dispatch"' + assert ( + msg(excinfo.value) + == 'Tried to call pure virtual function "Base::dispatch"' + ) p = PyClass1() return m.dispatch_issue_go(p) @@ -333,7 +365,7 @@ def test_inherited_virtuals(): class DT(m.D_Tpl): def say_something(self, times): - return "DT says:" + (' quack' * times) + return "DT says:" + (" quack" * times) def unlucky_number(self): return 1234 @@ -349,7 +381,7 @@ def test_inherited_virtuals(): class DT2(DT): def say_something(self, times): - return "DT2: " + ('QUACK' * times) + return "DT2: " + ("QUACK" * times) def unlucky_number(self): return -3 |