mirror of
https://github.com/PaddlePaddle/FastDeploy.git
synced 2025-12-24 13:28:13 +08:00
first commit
This commit is contained in:
557
third_party/pybind11/tests/CMakeLists.txt
vendored
Normal file
557
third_party/pybind11/tests/CMakeLists.txt
vendored
Normal file
@@ -0,0 +1,557 @@
|
||||
# CMakeLists.txt -- Build system for the pybind11 test suite
|
||||
#
|
||||
# Copyright (c) 2015 Wenzel Jakob <wenzel@inf.ethz.ch>
|
||||
#
|
||||
# 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 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.21)
|
||||
cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION})
|
||||
else()
|
||||
cmake_policy(VERSION 3.21)
|
||||
endif()
|
||||
|
||||
# Only needed for CMake < 3.5 support
|
||||
include(CMakeParseArguments)
|
||||
|
||||
# Filter out items; print an optional message if any items filtered. This ignores extensions.
|
||||
#
|
||||
# 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)
|
||||
# Make a list of the test without any extensions, for easier filtering.
|
||||
set(_TMP_ACTUAL_LIST "${${LISTNAME}};") # enforce ';' at the end to allow matching last item.
|
||||
string(REGEX REPLACE "\\.[^.;]*;" ";" LIST_WITHOUT_EXTENSIONS "${_TMP_ACTUAL_LIST}")
|
||||
foreach(filename IN LISTS ARG_UNPARSED_ARGUMENTS)
|
||||
string(REGEX REPLACE "\\.[^.]*$" "" filename_no_ext ${filename})
|
||||
# Search in the list without extensions.
|
||||
list(FIND LIST_WITHOUT_EXTENSIONS ${filename_no_ext} _FILE_FOUND)
|
||||
if(_FILE_FOUND GREATER -1)
|
||||
list(REMOVE_AT ${LISTNAME} ${_FILE_FOUND}) # And remove from the list with extensions.
|
||||
list(REMOVE_AT LIST_WITHOUT_EXTENSIONS ${_FILE_FOUND}
|
||||
)# And our search list, to ensure it is in sync.
|
||||
set(PYBIND11_FILTER_TESTS_FOUND ON)
|
||||
endif()
|
||||
endforeach()
|
||||
if(PYBIND11_FILTER_TESTS_FOUND AND ARG_MESSAGE)
|
||||
message(STATUS "${ARG_MESSAGE}")
|
||||
endif()
|
||||
endmacro()
|
||||
|
||||
macro(possibly_uninitialized)
|
||||
foreach(VARNAME ${ARGN})
|
||||
if(NOT DEFINED "${VARNAME}")
|
||||
set("${VARNAME}" "")
|
||||
endif()
|
||||
endforeach()
|
||||
endmacro()
|
||||
|
||||
# Function to add additional targets if any of the provided tests are found.
|
||||
# Needles; Specifies the test names to look for.
|
||||
# Additions; Specifies the additional test targets to add when any of the needles are found.
|
||||
macro(tests_extra_targets needles additions)
|
||||
# Add the index for this relation to the index extra targets map.
|
||||
list(LENGTH PYBIND11_TEST_EXTRA_TARGETS PYBIND11_TEST_EXTRA_TARGETS_LEN)
|
||||
list(APPEND PYBIND11_TEST_EXTRA_TARGETS ${PYBIND11_TEST_EXTRA_TARGETS_LEN})
|
||||
# Add the test names to look for, and the associated test target additions.
|
||||
set(PYBIND11_TEST_EXTRA_TARGETS_NEEDLES_${PYBIND11_TEST_EXTRA_TARGETS_LEN} ${needles})
|
||||
set(PYBIND11_TEST_EXTRA_TARGETS_ADDITION_${PYBIND11_TEST_EXTRA_TARGETS_LEN} ${additions})
|
||||
endmacro()
|
||||
|
||||
# New Python support
|
||||
if(DEFINED Python_EXECUTABLE)
|
||||
set(PYTHON_EXECUTABLE "${Python_EXECUTABLE}")
|
||||
set(PYTHON_VERSION "${Python_VERSION}")
|
||||
endif()
|
||||
|
||||
# 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 DEFINED 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")
|
||||
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, overrides ignore extension)
|
||||
# Any test that has no extension is both .py and .cpp, so 'foo' will add 'foo.cpp' and 'foo.py'.
|
||||
# Any test that has an extension is exclusively that and handled as such.
|
||||
set(PYBIND11_TEST_FILES
|
||||
test_async
|
||||
test_buffers
|
||||
test_builtin_casters
|
||||
test_call_policies
|
||||
test_callbacks
|
||||
test_chrono
|
||||
test_class
|
||||
test_const_name
|
||||
test_constants_and_functions
|
||||
test_copy_move
|
||||
test_custom_type_casters
|
||||
test_custom_type_setup
|
||||
test_docstring_options
|
||||
test_eigen
|
||||
test_enum
|
||||
test_eval
|
||||
test_exceptions
|
||||
test_factory_constructors
|
||||
test_gil_scoped
|
||||
test_iostream
|
||||
test_kwargs_and_defaults
|
||||
test_local_bindings
|
||||
test_methods_and_attributes
|
||||
test_modules
|
||||
test_multiple_inheritance
|
||||
test_numpy_array
|
||||
test_numpy_dtypes
|
||||
test_numpy_vectorize
|
||||
test_opaque_types
|
||||
test_operator_overloading
|
||||
test_pickling
|
||||
test_pytypes
|
||||
test_sequences_and_iterators
|
||||
test_smart_ptr
|
||||
test_stl
|
||||
test_stl_binders
|
||||
test_tagbased_polymorphic
|
||||
test_thread
|
||||
test_union
|
||||
test_virtual_functions)
|
||||
|
||||
# Invoking cmake with something like:
|
||||
# cmake -DPYBIND11_TEST_OVERRIDE="test_callbacks.cpp;test_pickling.cpp" ..
|
||||
# lets you override the tests that get compiled and run. You can restore to all tests with:
|
||||
# cmake -DPYBIND11_TEST_OVERRIDE= ..
|
||||
if(PYBIND11_TEST_OVERRIDE)
|
||||
# Instead of doing a direct override here, we iterate over the overrides without extension and
|
||||
# match them against entries from the PYBIND11_TEST_FILES, anything that not matches goes into the filter list.
|
||||
string(REGEX REPLACE "\\.[^.;]*;" ";" TEST_OVERRIDE_NO_EXT "${PYBIND11_TEST_OVERRIDE};")
|
||||
string(REGEX REPLACE "\\.[^.;]*;" ";" TEST_FILES_NO_EXT "${PYBIND11_TEST_FILES};")
|
||||
# This allows the override to be done with extensions, preserving backwards compatibility.
|
||||
foreach(test_name ${TEST_FILES_NO_EXT})
|
||||
if(NOT ${test_name} IN_LIST TEST_OVERRIDE_NO_EXT
|
||||
)# If not in the whitelist, add to be filtered out.
|
||||
list(APPEND PYBIND11_TEST_FILTER ${test_name})
|
||||
endif()
|
||||
endforeach()
|
||||
endif()
|
||||
|
||||
# You can also filter tests:
|
||||
if(PYBIND11_TEST_FILTER)
|
||||
pybind11_filter_tests(PYBIND11_TEST_FILES ${PYBIND11_TEST_FILTER})
|
||||
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()
|
||||
|
||||
# Now that the test filtering is complete, we need to split the list into the test for PYTEST
|
||||
# and the list for the cpp targets.
|
||||
set(PYBIND11_CPPTEST_FILES "")
|
||||
set(PYBIND11_PYTEST_FILES "")
|
||||
|
||||
foreach(test_name ${PYBIND11_TEST_FILES})
|
||||
if(test_name MATCHES "\\.py$") # Ends in .py, purely python test.
|
||||
list(APPEND PYBIND11_PYTEST_FILES ${test_name})
|
||||
elseif(test_name MATCHES "\\.cpp$") # Ends in .cpp, purely cpp test.
|
||||
list(APPEND PYBIND11_CPPTEST_FILES ${test_name})
|
||||
elseif(NOT test_name MATCHES "\\.") # No extension specified, assume both, add extension.
|
||||
list(APPEND PYBIND11_PYTEST_FILES ${test_name}.py)
|
||||
list(APPEND PYBIND11_CPPTEST_FILES ${test_name}.cpp)
|
||||
else()
|
||||
message(WARNING "Unhanded test extension in test: ${test_name}")
|
||||
endif()
|
||||
endforeach()
|
||||
set(PYBIND11_TEST_FILES ${PYBIND11_CPPTEST_FILES})
|
||||
list(SORT PYBIND11_PYTEST_FILES)
|
||||
|
||||
# Contains the set of test files that require pybind11_cross_module_tests to be
|
||||
# built; if none of these are built (i.e. because TEST_OVERRIDE is used and
|
||||
# doesn't include them) the second module doesn't get built.
|
||||
tests_extra_targets("test_exceptions.py;test_local_bindings.py;test_stl.py;test_stl_binders.py"
|
||||
"pybind11_cross_module_tests")
|
||||
|
||||
# And add additional targets for other tests.
|
||||
tests_extra_targets("test_gil_scoped.py" "cross_module_gil_utils")
|
||||
|
||||
set(PYBIND11_EIGEN_REPO
|
||||
"https://gitlab.com/libeigen/eigen.git"
|
||||
CACHE STRING "Eigen repository to use for tests")
|
||||
# Always use a hash for reconfigure speed and security reasons
|
||||
# Include the version number for pretty printing (keep in sync)
|
||||
set(PYBIND11_EIGEN_VERSION_AND_HASH
|
||||
"3.4.0;929bc0e191d0927b1735b9a1ddc0e8b77e3a25ec"
|
||||
CACHE STRING "Eigen version to use for tests, format: VERSION;HASH")
|
||||
|
||||
list(GET PYBIND11_EIGEN_VERSION_AND_HASH 0 PYBIND11_EIGEN_VERSION_STRING)
|
||||
list(GET PYBIND11_EIGEN_VERSION_AND_HASH 1 PYBIND11_EIGEN_VERSION_HASH)
|
||||
|
||||
# Check if Eigen is available; if not, remove from PYBIND11_TEST_FILES (but
|
||||
# keep it in PYBIND11_PYTEST_FILES, so that we get the "eigen is not installed"
|
||||
# skip message).
|
||||
list(FIND PYBIND11_TEST_FILES test_eigen.cpp PYBIND11_TEST_FILES_EIGEN_I)
|
||||
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(DOWNLOAD_EIGEN)
|
||||
if(CMAKE_VERSION VERSION_LESS 3.11)
|
||||
message(FATAL_ERROR "CMake 3.11+ required when using DOWNLOAD_EIGEN")
|
||||
endif()
|
||||
|
||||
include(FetchContent)
|
||||
FetchContent_Declare(
|
||||
eigen
|
||||
GIT_REPOSITORY "${PYBIND11_EIGEN_REPO}"
|
||||
GIT_TAG "${PYBIND11_EIGEN_VERSION_HASH}")
|
||||
|
||||
FetchContent_GetProperties(eigen)
|
||||
if(NOT eigen_POPULATED)
|
||||
message(
|
||||
STATUS
|
||||
"Downloading Eigen ${PYBIND11_EIGEN_VERSION_STRING} (${PYBIND11_EIGEN_VERSION_HASH}) from ${PYBIND11_EIGEN_REPO}"
|
||||
)
|
||||
FetchContent_Populate(eigen)
|
||||
endif()
|
||||
|
||||
set(EIGEN3_INCLUDE_DIR ${eigen_SOURCE_DIR})
|
||||
set(EIGEN3_FOUND TRUE)
|
||||
# When getting locally, the version is not visible from a superprojet,
|
||||
# so just force it.
|
||||
set(EIGEN3_VERSION "${PYBIND11_EIGEN_VERSION_STRING}")
|
||||
|
||||
else()
|
||||
find_package(Eigen3 3.2.7 QUIET CONFIG)
|
||||
|
||||
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(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.
|
||||
if(NOT EIGEN3_VERSION AND EIGEN3_VERSION_STRING)
|
||||
set(EIGEN3_VERSION ${EIGEN3_VERSION_STRING})
|
||||
endif()
|
||||
message(STATUS "Building tests with Eigen v${EIGEN3_VERSION}")
|
||||
else()
|
||||
list(REMOVE_AT PYBIND11_TEST_FILES ${PYBIND11_TEST_FILES_EIGEN_I})
|
||||
message(
|
||||
STATUS "Building tests WITHOUT Eigen, use -DDOWNLOAD_EIGEN=ON 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()
|
||||
|
||||
# Check if we need to add -lstdc++fs or -lc++fs or nothing
|
||||
if(DEFINED CMAKE_CXX_STANDARD AND CMAKE_CXX_STANDARD LESS 17)
|
||||
set(STD_FS_NO_LIB_NEEDED TRUE)
|
||||
elseif(MSVC)
|
||||
set(STD_FS_NO_LIB_NEEDED TRUE)
|
||||
else()
|
||||
file(
|
||||
WRITE ${CMAKE_CURRENT_BINARY_DIR}/main.cpp
|
||||
"#include <filesystem>\nint main(int argc, char ** argv) {\n std::filesystem::path p(argv[0]);\n return p.string().length();\n}"
|
||||
)
|
||||
try_compile(
|
||||
STD_FS_NO_LIB_NEEDED ${CMAKE_CURRENT_BINARY_DIR}
|
||||
SOURCES ${CMAKE_CURRENT_BINARY_DIR}/main.cpp
|
||||
COMPILE_DEFINITIONS -std=c++17)
|
||||
try_compile(
|
||||
STD_FS_NEEDS_STDCXXFS ${CMAKE_CURRENT_BINARY_DIR}
|
||||
SOURCES ${CMAKE_CURRENT_BINARY_DIR}/main.cpp
|
||||
COMPILE_DEFINITIONS -std=c++17
|
||||
LINK_LIBRARIES stdc++fs)
|
||||
try_compile(
|
||||
STD_FS_NEEDS_CXXFS ${CMAKE_CURRENT_BINARY_DIR}
|
||||
SOURCES ${CMAKE_CURRENT_BINARY_DIR}/main.cpp
|
||||
COMPILE_DEFINITIONS -std=c++17
|
||||
LINK_LIBRARIES c++fs)
|
||||
endif()
|
||||
|
||||
if(${STD_FS_NEEDS_STDCXXFS})
|
||||
set(STD_FS_LIB stdc++fs)
|
||||
elseif(${STD_FS_NEEDS_CXXFS})
|
||||
set(STD_FS_LIB c++fs)
|
||||
elseif(${STD_FS_NO_LIB_NEEDED})
|
||||
set(STD_FS_LIB "")
|
||||
else()
|
||||
message(WARNING "Unknown C++17 compiler - not passing -lstdc++fs")
|
||||
set(STD_FS_LIB "")
|
||||
endif()
|
||||
|
||||
# Compile with compiler warnings turned on
|
||||
function(pybind11_enable_warnings target_name)
|
||||
if(MSVC)
|
||||
target_compile_options(${target_name} PRIVATE /W4 /wd4189)
|
||||
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|Clang|IntelLLVM)")
|
||||
target_compile_options(${target_name} PRIVATE -Werror)
|
||||
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "Intel")
|
||||
if(CMAKE_CXX_STANDARD EQUAL 17) # See PR #3570
|
||||
target_compile_options(${target_name} PRIVATE -Wno-conversion)
|
||||
endif()
|
||||
target_compile_options(
|
||||
${target_name}
|
||||
PRIVATE
|
||||
-Werror-all
|
||||
# "Inlining inhibited by limit max-size", "Inlining inhibited by limit max-total-size"
|
||||
-diag-disable 11074,11076)
|
||||
endif()
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
set(test_targets pybind11_tests)
|
||||
|
||||
# Check if any tests need extra targets by iterating through the mappings registered.
|
||||
foreach(i ${PYBIND11_TEST_EXTRA_TARGETS})
|
||||
foreach(needle ${PYBIND11_TEST_EXTRA_TARGETS_NEEDLES_${i}})
|
||||
if(needle IN_LIST PYBIND11_PYTEST_FILES)
|
||||
# Add all the additional targets to the test list. List join in newer cmake.
|
||||
foreach(extra_target ${PYBIND11_TEST_EXTRA_TARGETS_ADDITION_${i}})
|
||||
list(APPEND test_targets ${extra_target})
|
||||
endforeach()
|
||||
break() # Breaks out of the needle search, continues with the next mapping.
|
||||
endif()
|
||||
endforeach()
|
||||
endforeach()
|
||||
|
||||
# Support CUDA testing by forcing the target file to compile with NVCC
|
||||
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")
|
||||
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)
|
||||
target_link_libraries(${target} PRIVATE Eigen3::Eigen)
|
||||
target_compile_definitions(${target} PRIVATE -DPYBIND11_TEST_EIGEN)
|
||||
endif()
|
||||
|
||||
if(Boost_FOUND)
|
||||
target_link_libraries(${target} PRIVATE Boost::headers)
|
||||
target_compile_definitions(${target} PRIVATE -DPYBIND11_TEST_BOOST)
|
||||
endif()
|
||||
|
||||
target_link_libraries(${target} PRIVATE ${STD_FS_LIB})
|
||||
|
||||
# Always write the output file directly into the 'tests' directory (even on MSVC)
|
||||
if(NOT CMAKE_LIBRARY_OUTPUT_DIRECTORY)
|
||||
set_target_properties(${target} PROPERTIES LIBRARY_OUTPUT_DIRECTORY
|
||||
"${CMAKE_CURRENT_BINARY_DIR}")
|
||||
|
||||
if(DEFINED CMAKE_CONFIGURATION_TYPES)
|
||||
foreach(config ${CMAKE_CONFIGURATION_TYPES})
|
||||
string(TOUPPER ${config} config)
|
||||
set_target_properties(${target} PROPERTIES LIBRARY_OUTPUT_DIRECTORY_${config}
|
||||
"${CMAKE_CURRENT_BINARY_DIR}")
|
||||
endforeach()
|
||||
endif()
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
# Provide nice organisation in IDEs
|
||||
if(NOT CMAKE_VERSION VERSION_LESS 3.8)
|
||||
source_group(
|
||||
TREE "${CMAKE_CURRENT_SOURCE_DIR}/../include"
|
||||
PREFIX "Header Files"
|
||||
FILES ${PYBIND11_HEADERS})
|
||||
endif()
|
||||
|
||||
# Make sure pytest is found or produce a warning
|
||||
pybind11_find_import(pytest VERSION 3.1)
|
||||
|
||||
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}\"")
|
||||
|
||||
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}")
|
||||
|
||||
set(PYBIND11_TEST_PREFIX_COMMAND
|
||||
""
|
||||
CACHE STRING "Put this before pytest, use for checkers and such")
|
||||
|
||||
# A single command to compile and run the tests
|
||||
add_custom_target(
|
||||
pytest
|
||||
COMMAND ${PYBIND11_TEST_PREFIX_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")
|
||||
endif()
|
||||
|
||||
# cmake-format: off
|
||||
add_custom_target(
|
||||
memcheck
|
||||
COMMAND
|
||||
PYTHONMALLOC=malloc
|
||||
valgrind
|
||||
--leak-check=full
|
||||
--show-leak-kinds=definite,indirect
|
||||
--errors-for-leak-kinds=definite,indirect
|
||||
--error-exitcode=1
|
||||
--read-var-info=yes
|
||||
--track-origins=yes
|
||||
--suppressions="${CMAKE_CURRENT_SOURCE_DIR}/valgrind-python.supp"
|
||||
--suppressions="${CMAKE_CURRENT_SOURCE_DIR}/valgrind-numpy-scipy.supp"
|
||||
--gen-suppressions=all
|
||||
${PYTHON_EXECUTABLE} -m pytest ${PYBIND11_ABS_PYTEST_FILES}
|
||||
DEPENDS ${test_targets}
|
||||
WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}"
|
||||
USES_TERMINAL)
|
||||
# cmake-format: on
|
||||
|
||||
# Add a check target to run all the tests, starting with pytest (we add dependencies to this below)
|
||||
add_custom_target(check DEPENDS pytest)
|
||||
|
||||
# The remaining tests only apply when being built as part of the pybind11 project, but not if the
|
||||
# tests are being built independently.
|
||||
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} ${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()
|
||||
200
third_party/pybind11/tests/conftest.py
vendored
Normal file
200
third_party/pybind11/tests/conftest.py
vendored
Normal file
@@ -0,0 +1,200 @@
|
||||
"""pytest configuration
|
||||
|
||||
Extends output capture as needed by pybind11: ignore constructors, optional unordered lines.
|
||||
Adds docstring and exceptions message sanitizers.
|
||||
"""
|
||||
|
||||
import contextlib
|
||||
import difflib
|
||||
import gc
|
||||
import re
|
||||
import textwrap
|
||||
|
||||
import pytest
|
||||
|
||||
# Early diagnostic for failed imports
|
||||
import pybind11_tests # noqa: F401
|
||||
|
||||
_long_marker = re.compile(r"([0-9])L")
|
||||
_hexadecimal = re.compile(r"0x[0-9a-fA-F]+")
|
||||
|
||||
# Avoid collecting Python3 only files
|
||||
collect_ignore = []
|
||||
|
||||
|
||||
def _strip_and_dedent(s):
|
||||
"""For triple-quote strings"""
|
||||
return textwrap.dedent(s.lstrip("\n").rstrip())
|
||||
|
||||
|
||||
def _split_and_sort(s):
|
||||
"""For output which does not require specific line order"""
|
||||
return sorted(_strip_and_dedent(s).splitlines())
|
||||
|
||||
|
||||
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)
|
||||
]
|
||||
|
||||
|
||||
class Output:
|
||||
"""Basic output post-processing and comparison"""
|
||||
|
||||
def __init__(self, string):
|
||||
self.string = string
|
||||
self.explanation = []
|
||||
|
||||
def __str__(self):
|
||||
return self.string
|
||||
|
||||
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("###")
|
||||
]
|
||||
b = _strip_and_dedent(other).splitlines()
|
||||
if a == b:
|
||||
return True
|
||||
else:
|
||||
self.explanation = _make_explanation(a, b)
|
||||
return False
|
||||
|
||||
|
||||
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)
|
||||
if a == b:
|
||||
return True
|
||||
else:
|
||||
self.explanation = _make_explanation(a, b)
|
||||
return False
|
||||
|
||||
|
||||
class Capture:
|
||||
def __init__(self, capfd):
|
||||
self.capfd = capfd
|
||||
self.out = ""
|
||||
self.err = ""
|
||||
|
||||
def __enter__(self):
|
||||
self.capfd.readouterr()
|
||||
return self
|
||||
|
||||
def __exit__(self, *args):
|
||||
self.out, self.err = self.capfd.readouterr()
|
||||
|
||||
def __eq__(self, other):
|
||||
a = Output(self.out)
|
||||
b = other
|
||||
if a == b:
|
||||
return True
|
||||
else:
|
||||
self.explanation = a.explanation
|
||||
return False
|
||||
|
||||
def __str__(self):
|
||||
return self.out
|
||||
|
||||
def __contains__(self, item):
|
||||
return item in self.out
|
||||
|
||||
@property
|
||||
def unordered(self):
|
||||
return Unordered(self.out)
|
||||
|
||||
@property
|
||||
def stderr(self):
|
||||
return Output(self.err)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def capture(capsys):
|
||||
"""Extended `capsys` with context manager and custom equality operators"""
|
||||
return Capture(capsys)
|
||||
|
||||
|
||||
class SanitizedString:
|
||||
def __init__(self, sanitizer):
|
||||
self.sanitizer = sanitizer
|
||||
self.string = ""
|
||||
self.explanation = []
|
||||
|
||||
def __call__(self, thing):
|
||||
self.string = self.sanitizer(thing)
|
||||
return self
|
||||
|
||||
def __eq__(self, other):
|
||||
a = self.string
|
||||
b = _strip_and_dedent(other)
|
||||
if a == b:
|
||||
return True
|
||||
else:
|
||||
self.explanation = _make_explanation(a.splitlines(),
|
||||
b.splitlines())
|
||||
return False
|
||||
|
||||
|
||||
def _sanitize_general(s):
|
||||
s = s.strip()
|
||||
s = s.replace("pybind11_tests.", "m.")
|
||||
s = _long_marker.sub(r"\1", s)
|
||||
return s
|
||||
|
||||
|
||||
def _sanitize_docstring(thing):
|
||||
s = thing.__doc__
|
||||
s = _sanitize_general(s)
|
||||
return s
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def doc():
|
||||
"""Sanitize docstrings and add custom failure explanation"""
|
||||
return SanitizedString(_sanitize_docstring)
|
||||
|
||||
|
||||
def _sanitize_message(thing):
|
||||
s = str(thing)
|
||||
s = _sanitize_general(s)
|
||||
s = _hexadecimal.sub("0", s)
|
||||
return s
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def msg():
|
||||
"""Sanitize messages and add custom failure explanation"""
|
||||
return SanitizedString(_sanitize_message)
|
||||
|
||||
|
||||
# noinspection PyUnusedLocal
|
||||
def pytest_assertrepr_compare(op, left, right):
|
||||
"""Hook to insert custom failure explanation"""
|
||||
if hasattr(left, "explanation"):
|
||||
return left.explanation
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
def suppress(exception):
|
||||
"""Suppress the desired exception"""
|
||||
try:
|
||||
yield
|
||||
except exception:
|
||||
pass
|
||||
|
||||
|
||||
def gc_collect():
|
||||
"""Run the garbage collector twice (needed when running
|
||||
reference counting tests with PyPy)"""
|
||||
gc.collect()
|
||||
gc.collect()
|
||||
|
||||
|
||||
def pytest_configure():
|
||||
pytest.suppress = suppress
|
||||
pytest.gc_collect = gc_collect
|
||||
322
third_party/pybind11/tests/constructor_stats.h
vendored
Normal file
322
third_party/pybind11/tests/constructor_stats.h
vendored
Normal file
@@ -0,0 +1,322 @@
|
||||
#pragma once
|
||||
/*
|
||||
tests/constructor_stats.h -- framework for printing and tracking object
|
||||
instance lifetimes in example/test code.
|
||||
|
||||
Copyright (c) 2016 Jason Rhinelander <jason@imaginary.ca>
|
||||
|
||||
All rights reserved. Use of this source code is governed by a
|
||||
BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
This header provides a few useful tools for writing examples or tests that want to check and/or
|
||||
display object instance lifetimes. It requires that you include this header and add the following
|
||||
function calls to constructors:
|
||||
|
||||
class MyClass {
|
||||
MyClass() { ...; print_default_created(this); }
|
||||
~MyClass() { ...; print_destroyed(this); }
|
||||
MyClass(const MyClass &c) { ...; print_copy_created(this); }
|
||||
MyClass(MyClass &&c) { ...; print_move_created(this); }
|
||||
MyClass(int a, int b) { ...; print_created(this, a, b); }
|
||||
MyClass &operator=(const MyClass &c) { ...; print_copy_assigned(this); }
|
||||
MyClass &operator=(MyClass &&c) { ...; print_move_assigned(this); }
|
||||
|
||||
...
|
||||
}
|
||||
|
||||
You can find various examples of these in several of the existing testing .cpp files. (Of course
|
||||
you don't need to add any of the above constructors/operators that you don't actually have, except
|
||||
for the destructor).
|
||||
|
||||
Each of these will print an appropriate message such as:
|
||||
|
||||
### MyClass @ 0x2801910 created via default constructor
|
||||
### MyClass @ 0x27fa780 created 100 200
|
||||
### MyClass @ 0x2801910 destroyed
|
||||
### MyClass @ 0x27fa780 destroyed
|
||||
|
||||
You can also include extra arguments (such as the 100, 200 in the output above, coming from the
|
||||
value constructor) for all of the above methods which will be included in the output.
|
||||
|
||||
For testing, each of these also keeps track the created instances and allows you to check how many
|
||||
of the various constructors have been invoked from the Python side via code such as:
|
||||
|
||||
from pybind11_tests import ConstructorStats
|
||||
cstats = ConstructorStats.get(MyClass)
|
||||
print(cstats.alive())
|
||||
print(cstats.default_constructions)
|
||||
|
||||
Note that `.alive()` should usually be the first thing you call as it invokes Python's garbage
|
||||
collector to actually destroy objects that aren't yet referenced.
|
||||
|
||||
For everything except copy and move constructors and destructors, any extra values given to the
|
||||
print_...() function is stored in a class-specific values list which you can retrieve and inspect
|
||||
from the ConstructorStats instance `.values()` method.
|
||||
|
||||
In some cases, when you need to track instances of a C++ class not registered with pybind11, you
|
||||
need to add a function returning the ConstructorStats for the C++ class; this can be done with:
|
||||
|
||||
m.def("get_special_cstats", &ConstructorStats::get<SpecialClass>,
|
||||
py::return_value_policy::reference)
|
||||
|
||||
Finally, you can suppress the output messages, but keep the constructor tracking (for
|
||||
inspection/testing in python) by using the functions with `print_` replaced with `track_` (e.g.
|
||||
`track_copy_created(this)`).
|
||||
|
||||
*/
|
||||
|
||||
#include "pybind11_tests.h"
|
||||
|
||||
#include <list>
|
||||
#include <sstream>
|
||||
#include <typeindex>
|
||||
#include <unordered_map>
|
||||
|
||||
class ConstructorStats {
|
||||
protected:
|
||||
std::unordered_map<void *, int> _instances; // Need a map rather than set because members can
|
||||
// shared address with parents
|
||||
std::list<std::string> _values; // Used to track values
|
||||
// (e.g. of value constructors)
|
||||
public:
|
||||
int default_constructions = 0;
|
||||
int copy_constructions = 0;
|
||||
int move_constructions = 0;
|
||||
int copy_assignments = 0;
|
||||
int move_assignments = 0;
|
||||
|
||||
void copy_created(void *inst) {
|
||||
created(inst);
|
||||
copy_constructions++;
|
||||
}
|
||||
|
||||
void move_created(void *inst) {
|
||||
created(inst);
|
||||
move_constructions++;
|
||||
}
|
||||
|
||||
void default_created(void *inst) {
|
||||
created(inst);
|
||||
default_constructions++;
|
||||
}
|
||||
|
||||
void created(void *inst) { ++_instances[inst]; }
|
||||
|
||||
void destroyed(void *inst) {
|
||||
if (--_instances[inst] < 0) {
|
||||
throw std::runtime_error("cstats.destroyed() called with unknown "
|
||||
"instance; potential double-destruction "
|
||||
"or a missing cstats.created()");
|
||||
}
|
||||
}
|
||||
|
||||
static void gc() {
|
||||
// Force garbage collection to ensure any pending destructors are invoked:
|
||||
#if defined(PYPY_VERSION)
|
||||
PyObject *globals = PyEval_GetGlobals();
|
||||
PyObject *result = PyRun_String("import gc\n"
|
||||
"for i in range(2):"
|
||||
" gc.collect()\n",
|
||||
Py_file_input,
|
||||
globals,
|
||||
globals);
|
||||
if (result == nullptr)
|
||||
throw py::error_already_set();
|
||||
Py_DECREF(result);
|
||||
#else
|
||||
py::module_::import("gc").attr("collect")();
|
||||
#endif
|
||||
}
|
||||
|
||||
int alive() {
|
||||
gc();
|
||||
int total = 0;
|
||||
for (const auto &p : _instances) {
|
||||
if (p.second > 0) {
|
||||
total += p.second;
|
||||
}
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
void value() {} // Recursion terminator
|
||||
// Takes one or more values, converts them to strings, then stores them.
|
||||
template <typename T, typename... Tmore>
|
||||
void value(const T &v, Tmore &&...args) {
|
||||
std::ostringstream oss;
|
||||
oss << v;
|
||||
_values.push_back(oss.str());
|
||||
value(std::forward<Tmore>(args)...);
|
||||
}
|
||||
|
||||
// Move out stored values
|
||||
py::list values() {
|
||||
py::list l;
|
||||
for (const auto &v : _values) {
|
||||
l.append(py::cast(v));
|
||||
}
|
||||
_values.clear();
|
||||
return l;
|
||||
}
|
||||
|
||||
// Gets constructor stats from a C++ type index
|
||||
static ConstructorStats &get(std::type_index type) {
|
||||
static std::unordered_map<std::type_index, ConstructorStats> all_cstats;
|
||||
return all_cstats[type];
|
||||
}
|
||||
|
||||
// Gets constructor stats from a C++ type
|
||||
template <typename T>
|
||||
static ConstructorStats &get() {
|
||||
#if defined(PYPY_VERSION)
|
||||
gc();
|
||||
#endif
|
||||
return get(typeid(T));
|
||||
}
|
||||
|
||||
// Gets constructor stats from a Python class
|
||||
static ConstructorStats &get(py::object class_) {
|
||||
auto &internals = py::detail::get_internals();
|
||||
const std::type_index *t1 = nullptr, *t2 = nullptr;
|
||||
try {
|
||||
auto *type_info
|
||||
= internals.registered_types_py.at((PyTypeObject *) class_.ptr()).at(0);
|
||||
for (auto &p : internals.registered_types_cpp) {
|
||||
if (p.second == type_info) {
|
||||
if (t1) {
|
||||
t2 = &p.first;
|
||||
break;
|
||||
}
|
||||
t1 = &p.first;
|
||||
}
|
||||
}
|
||||
} catch (const std::out_of_range &) {
|
||||
}
|
||||
if (!t1) {
|
||||
throw std::runtime_error("Unknown class passed to ConstructorStats::get()");
|
||||
}
|
||||
auto &cs1 = get(*t1);
|
||||
// If we have both a t1 and t2 match, one is probably the trampoline class; return
|
||||
// whichever has more constructions (typically one or the other will be 0)
|
||||
if (t2) {
|
||||
auto &cs2 = get(*t2);
|
||||
int cs1_total = cs1.default_constructions + cs1.copy_constructions
|
||||
+ cs1.move_constructions + (int) cs1._values.size();
|
||||
int cs2_total = cs2.default_constructions + cs2.copy_constructions
|
||||
+ cs2.move_constructions + (int) cs2._values.size();
|
||||
if (cs2_total > cs1_total) {
|
||||
return cs2;
|
||||
}
|
||||
}
|
||||
return cs1;
|
||||
}
|
||||
};
|
||||
|
||||
// To track construction/destruction, you need to call these methods from the various
|
||||
// constructors/operators. The ones that take extra values record the given values in the
|
||||
// constructor stats values for later inspection.
|
||||
template <class T>
|
||||
void track_copy_created(T *inst) {
|
||||
ConstructorStats::get<T>().copy_created(inst);
|
||||
}
|
||||
template <class T>
|
||||
void track_move_created(T *inst) {
|
||||
ConstructorStats::get<T>().move_created(inst);
|
||||
}
|
||||
template <class T, typename... Values>
|
||||
void track_copy_assigned(T *, Values &&...values) {
|
||||
auto &cst = ConstructorStats::get<T>();
|
||||
cst.copy_assignments++;
|
||||
cst.value(std::forward<Values>(values)...);
|
||||
}
|
||||
template <class T, typename... Values>
|
||||
void track_move_assigned(T *, Values &&...values) {
|
||||
auto &cst = ConstructorStats::get<T>();
|
||||
cst.move_assignments++;
|
||||
cst.value(std::forward<Values>(values)...);
|
||||
}
|
||||
template <class T, typename... Values>
|
||||
void track_default_created(T *inst, Values &&...values) {
|
||||
auto &cst = ConstructorStats::get<T>();
|
||||
cst.default_created(inst);
|
||||
cst.value(std::forward<Values>(values)...);
|
||||
}
|
||||
template <class T, typename... Values>
|
||||
void track_created(T *inst, Values &&...values) {
|
||||
auto &cst = ConstructorStats::get<T>();
|
||||
cst.created(inst);
|
||||
cst.value(std::forward<Values>(values)...);
|
||||
}
|
||||
template <class T, typename... Values>
|
||||
void track_destroyed(T *inst) {
|
||||
ConstructorStats::get<T>().destroyed(inst);
|
||||
}
|
||||
template <class T, typename... Values>
|
||||
void track_values(T *, Values &&...values) {
|
||||
ConstructorStats::get<T>().value(std::forward<Values>(values)...);
|
||||
}
|
||||
|
||||
/// Don't cast pointers to Python, print them as strings
|
||||
inline const char *format_ptrs(const char *p) { return p; }
|
||||
template <typename T>
|
||||
py::str format_ptrs(T *p) {
|
||||
return "{:#x}"_s.format(reinterpret_cast<std::uintptr_t>(p));
|
||||
}
|
||||
template <typename T>
|
||||
auto format_ptrs(T &&x) -> decltype(std::forward<T>(x)) {
|
||||
return std::forward<T>(x);
|
||||
}
|
||||
|
||||
template <class T, typename... Output>
|
||||
void print_constr_details(T *inst, const std::string &action, Output &&...output) {
|
||||
py::print("###",
|
||||
py::type_id<T>(),
|
||||
"@",
|
||||
format_ptrs(inst),
|
||||
action,
|
||||
format_ptrs(std::forward<Output>(output))...);
|
||||
}
|
||||
|
||||
// Verbose versions of the above:
|
||||
template <class T, typename... Values>
|
||||
void print_copy_created(T *inst,
|
||||
Values &&...values) { // NB: this prints, but doesn't store, given values
|
||||
print_constr_details(inst, "created via copy constructor", values...);
|
||||
track_copy_created(inst);
|
||||
}
|
||||
template <class T, typename... Values>
|
||||
void print_move_created(T *inst,
|
||||
Values &&...values) { // NB: this prints, but doesn't store, given values
|
||||
print_constr_details(inst, "created via move constructor", values...);
|
||||
track_move_created(inst);
|
||||
}
|
||||
template <class T, typename... Values>
|
||||
void print_copy_assigned(T *inst, Values &&...values) {
|
||||
print_constr_details(inst, "assigned via copy assignment", values...);
|
||||
track_copy_assigned(inst, values...);
|
||||
}
|
||||
template <class T, typename... Values>
|
||||
void print_move_assigned(T *inst, Values &&...values) {
|
||||
print_constr_details(inst, "assigned via move assignment", values...);
|
||||
track_move_assigned(inst, values...);
|
||||
}
|
||||
template <class T, typename... Values>
|
||||
void print_default_created(T *inst, Values &&...values) {
|
||||
print_constr_details(inst, "created via default constructor", values...);
|
||||
track_default_created(inst, values...);
|
||||
}
|
||||
template <class T, typename... Values>
|
||||
void print_created(T *inst, Values &&...values) {
|
||||
print_constr_details(inst, "created", values...);
|
||||
track_created(inst, values...);
|
||||
}
|
||||
template <class T, typename... Values>
|
||||
void print_destroyed(T *inst, Values &&...values) { // Prints but doesn't store given values
|
||||
print_constr_details(inst, "destroyed", values...);
|
||||
track_destroyed(inst);
|
||||
}
|
||||
template <class T, typename... Values>
|
||||
void print_values(T *inst, Values &&...values) {
|
||||
print_constr_details(inst, ":", values...);
|
||||
track_values(inst, values...);
|
||||
}
|
||||
45
third_party/pybind11/tests/cross_module_gil_utils.cpp
vendored
Normal file
45
third_party/pybind11/tests/cross_module_gil_utils.cpp
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
tests/cross_module_gil_utils.cpp -- tools for acquiring GIL from a different module
|
||||
|
||||
Copyright (c) 2019 Google LLC
|
||||
|
||||
All rights reserved. Use of this source code is governed by a
|
||||
BSD-style license that can be found in the LICENSE file.
|
||||
*/
|
||||
#include <pybind11/pybind11.h>
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
// This file mimics a DSO that makes pybind11 calls but does not define a
|
||||
// PYBIND11_MODULE. The purpose is to test that such a DSO can create a
|
||||
// py::gil_scoped_acquire when the running thread is in a GIL-released state.
|
||||
//
|
||||
// Note that we define a Python module here for convenience, but in general
|
||||
// this need not be the case. The typical scenario would be a DSO that implements
|
||||
// shared logic used internally by multiple pybind11 modules.
|
||||
|
||||
namespace {
|
||||
|
||||
namespace py = pybind11;
|
||||
void gil_acquire() { py::gil_scoped_acquire gil; }
|
||||
|
||||
constexpr char kModuleName[] = "cross_module_gil_utils";
|
||||
|
||||
struct PyModuleDef moduledef = {
|
||||
PyModuleDef_HEAD_INIT, kModuleName, nullptr, 0, nullptr, nullptr, nullptr, nullptr, nullptr};
|
||||
|
||||
} // namespace
|
||||
|
||||
extern "C" PYBIND11_EXPORT PyObject *PyInit_cross_module_gil_utils() {
|
||||
|
||||
PyObject *m = PyModule_Create(&moduledef);
|
||||
|
||||
if (m != nullptr) {
|
||||
static_assert(sizeof(&gil_acquire) == sizeof(void *),
|
||||
"Function pointer must have the same size as void*");
|
||||
PyModule_AddObject(
|
||||
m, "gil_acquire_funcaddr", PyLong_FromVoidPtr(reinterpret_cast<void *>(&gil_acquire)));
|
||||
}
|
||||
|
||||
return m;
|
||||
}
|
||||
28
third_party/pybind11/tests/env.py
vendored
Normal file
28
third_party/pybind11/tests/env.py
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
import platform
|
||||
import sys
|
||||
|
||||
import pytest
|
||||
|
||||
LINUX = sys.platform.startswith("linux")
|
||||
MACOS = sys.platform.startswith("darwin")
|
||||
WIN = sys.platform.startswith("win32") or sys.platform.startswith("cygwin")
|
||||
|
||||
CPYTHON = platform.python_implementation() == "CPython"
|
||||
PYPY = platform.python_implementation() == "PyPy"
|
||||
|
||||
|
||||
def deprecated_call():
|
||||
"""
|
||||
pytest.deprecated_call() seems broken in pytest<3.9.x; concretely, it
|
||||
doesn't work on CPython 3.8.0 with pytest==3.3.2 on Ubuntu 18.04 (#2922).
|
||||
|
||||
This is a narrowed reimplementation of the following PR :(
|
||||
https://github.com/pytest-dev/pytest/pull/4104
|
||||
"""
|
||||
# TODO: Remove this when testing requires pytest>=3.9.
|
||||
pieces = pytest.__version__.split(".")
|
||||
pytest_major_minor = (int(pieces[0]), int(pieces[1]))
|
||||
if pytest_major_minor < (3, 9):
|
||||
return pytest.warns((DeprecationWarning, PendingDeprecationWarning))
|
||||
else:
|
||||
return pytest.deprecated_call()
|
||||
0
third_party/pybind11/tests/extra_python_package/pytest.ini
vendored
Normal file
0
third_party/pybind11/tests/extra_python_package/pytest.ini
vendored
Normal file
257
third_party/pybind11/tests/extra_python_package/test_files.py
vendored
Normal file
257
third_party/pybind11/tests/extra_python_package/test_files.py
vendored
Normal file
@@ -0,0 +1,257 @@
|
||||
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/gil.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/type_caster_base.h",
|
||||
"include/pybind11/detail/typeid.h",
|
||||
}
|
||||
|
||||
stl_headers = {"include/pybind11/stl/filesystem.h", }
|
||||
|
||||
cmake_files = {
|
||||
"share/cmake/pybind11/FindPythonLibsNew.cmake",
|
||||
"share/cmake/pybind11/pybind11Common.cmake",
|
||||
"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",
|
||||
"commands.py",
|
||||
"py.typed",
|
||||
"setup_helpers.py",
|
||||
}
|
||||
|
||||
headers = main_headers | detail_headers | stl_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/include/pybind11/stl",
|
||||
"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,
|
||||
"-m",
|
||||
"build",
|
||||
"--sdist",
|
||||
"--outdir",
|
||||
str(tmpdir),
|
||||
])
|
||||
if hasattr(out, "decode"):
|
||||
out = out.decode()
|
||||
|
||||
(sdist, ) = tmpdir.visit("*.tar.gz")
|
||||
|
||||
with tarfile.open(str(sdist), "r:gz") as tar:
|
||||
start = tar.getnames()[0] + "/"
|
||||
version = start[9:-1]
|
||||
simpler = {n.split("/", 1)[-1] for n in tar.getnames()[1:]}
|
||||
|
||||
with contextlib.closing(
|
||||
tar.extractfile(tar.getmember(start + "setup.py"))) as f:
|
||||
setup_py = f.read()
|
||||
|
||||
with contextlib.closing(
|
||||
tar.extractfile(tar.getmember(start + "pyproject.toml"))) as f:
|
||||
pyproject_toml = f.read()
|
||||
|
||||
with contextlib.closing(
|
||||
tar.extractfile(
|
||||
tar.getmember(
|
||||
start +
|
||||
"pybind11/share/cmake/pybind11/pybind11Config.cmake"))
|
||||
) as f:
|
||||
contents = f.read().decode("utf8")
|
||||
assert 'set(pybind11_INCLUDE_DIR "${PACKAGE_PREFIX_DIR}/include")' in contents
|
||||
|
||||
files = {f"pybind11/{n}" for n in all_files}
|
||||
files |= sdist_files
|
||||
files |= {f"pybind11{n}" for n in local_sdist_files}
|
||||
files.add("pybind11.egg-info/entry_points.txt")
|
||||
files.add("pybind11.egg-info/requires.txt")
|
||||
assert simpler == files
|
||||
|
||||
with open(os.path.join(MAIN_DIR, "tools", "setup_main.py.in"), "rb") as f:
|
||||
contents = (string.Template(f.read().decode()).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,
|
||||
"-m",
|
||||
"build",
|
||||
"--sdist",
|
||||
"--outdir",
|
||||
str(tmpdir),
|
||||
])
|
||||
|
||||
if hasattr(out, "decode"):
|
||||
out = out.decode()
|
||||
|
||||
(sdist, ) = tmpdir.visit("*.tar.gz")
|
||||
|
||||
with tarfile.open(str(sdist), "r:gz") as tar:
|
||||
start = tar.getnames()[0] + "/"
|
||||
version = start[16:-1]
|
||||
simpler = {n.split("/", 1)[-1] for n in tar.getnames()[1:]}
|
||||
|
||||
with contextlib.closing(
|
||||
tar.extractfile(tar.getmember(start + "setup.py"))) as f:
|
||||
setup_py = f.read()
|
||||
|
||||
with contextlib.closing(
|
||||
tar.extractfile(tar.getmember(start + "pyproject.toml"))) as f:
|
||||
pyproject_toml = f.read()
|
||||
|
||||
files = {f"pybind11/{n}" for n in all_files}
|
||||
files |= sdist_files
|
||||
files |= {f"pybind11_global{n}" for n in local_sdist_files}
|
||||
assert simpler == files
|
||||
|
||||
with open(os.path.join(MAIN_DIR, "tools", "setup_global.py.in"),
|
||||
"rb") as f:
|
||||
contents = (string.Template(f.read().decode()).substitute(
|
||||
version=version, extra_cmd="").encode())
|
||||
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 = {f"pybind11/{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 = {n for n in names if "dist-info" not in n}
|
||||
trimmed |= {
|
||||
f"dist-info/{n.split('/', 1)[-1]}"
|
||||
for n in names if "dist-info" in n
|
||||
}
|
||||
assert files == trimmed
|
||||
|
||||
|
||||
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 = {f"data/data/{n}" for n in src_files}
|
||||
files |= {f"data/headers/{n[8:]}" for n in headers}
|
||||
files |= {
|
||||
"dist-info/LICENSE",
|
||||
"dist-info/METADATA",
|
||||
"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 = {n[len(beginning) + 1:] for n in names}
|
||||
|
||||
assert files == trimmed
|
||||
0
third_party/pybind11/tests/extra_setuptools/pytest.ini
vendored
Normal file
0
third_party/pybind11/tests/extra_setuptools/pytest.ini
vendored
Normal file
142
third_party/pybind11/tests/extra_setuptools/test_setuphelper.py
vendored
Normal file
142
third_party/pybind11/tests/extra_setuptools/test_setuphelper.py
vendored
Normal file
@@ -0,0 +1,142 @@
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
from textwrap import dedent
|
||||
|
||||
import pytest
|
||||
|
||||
DIR = os.path.abspath(os.path.dirname(__file__))
|
||||
MAIN_DIR = os.path.dirname(os.path.dirname(DIR))
|
||||
WIN = sys.platform.startswith("win32") or sys.platform.startswith("cygwin")
|
||||
|
||||
|
||||
@pytest.mark.parametrize("parallel", [False, True])
|
||||
@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(f"""\
|
||||
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,
|
||||
)
|
||||
"""),
|
||||
encoding="ascii", )
|
||||
|
||||
(tmpdir / "main.cpp").write_text(
|
||||
dedent("""\
|
||||
#include <pybind11/pybind11.h>
|
||||
|
||||
int f(int x) {
|
||||
return x * 3;
|
||||
}
|
||||
PYBIND11_MODULE(simple_setup, m) {
|
||||
m.def("f", &f);
|
||||
}
|
||||
"""),
|
||||
encoding="ascii", )
|
||||
|
||||
out = subprocess.check_output(
|
||||
[sys.executable, "setup.py", "build_ext", "--inplace"], )
|
||||
if not WIN:
|
||||
assert b"-g0" in out
|
||||
out = subprocess.check_output(
|
||||
[sys.executable, "setup.py", "build_ext", "--inplace", "--force"],
|
||||
env=dict(
|
||||
os.environ, CFLAGS="-g"), )
|
||||
if not WIN:
|
||||
assert b"-g0" not in out
|
||||
|
||||
# Debug helper printout, normally hidden
|
||||
print(out)
|
||||
for item in tmpdir.listdir():
|
||||
print(item.basename)
|
||||
|
||||
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("""\
|
||||
import simple_setup
|
||||
assert simple_setup.f(3) == 9
|
||||
"""),
|
||||
encoding="ascii", )
|
||||
|
||||
subprocess.check_call(
|
||||
[sys.executable, "test.py"], stdout=sys.stdout, stderr=sys.stderr)
|
||||
|
||||
|
||||
def test_intree_extensions(monkeypatch, tmpdir):
|
||||
monkeypatch.syspath_prepend(MAIN_DIR)
|
||||
|
||||
from pybind11.setup_helpers import intree_extensions
|
||||
|
||||
monkeypatch.chdir(tmpdir)
|
||||
root = tmpdir
|
||||
root.ensure_dir()
|
||||
subdir = root / "dir"
|
||||
subdir.ensure_dir()
|
||||
src = subdir / "ext.cpp"
|
||||
src.ensure()
|
||||
relpath = src.relto(tmpdir)
|
||||
(ext, ) = intree_extensions([relpath])
|
||||
assert ext.name == "ext"
|
||||
subdir.ensure("__init__.py")
|
||||
(ext, ) = intree_extensions([relpath])
|
||||
assert ext.name == "dir.ext"
|
||||
|
||||
|
||||
def test_intree_extensions_package_dir(monkeypatch, tmpdir):
|
||||
monkeypatch.syspath_prepend(MAIN_DIR)
|
||||
|
||||
from pybind11.setup_helpers import intree_extensions
|
||||
|
||||
monkeypatch.chdir(tmpdir)
|
||||
root = tmpdir / "src"
|
||||
root.ensure_dir()
|
||||
subdir = root / "dir"
|
||||
subdir.ensure_dir()
|
||||
src = subdir / "ext.cpp"
|
||||
src.ensure()
|
||||
(ext, ) = intree_extensions([src.relto(tmpdir)], package_dir={"": "src"})
|
||||
assert ext.name == "dir.ext"
|
||||
(ext, ) = intree_extensions(
|
||||
[src.relto(tmpdir)], package_dir={"foo": "src"})
|
||||
assert ext.name == "foo.dir.ext"
|
||||
subdir.ensure("__init__.py")
|
||||
(ext, ) = intree_extensions([src.relto(tmpdir)], package_dir={"": "src"})
|
||||
assert ext.name == "dir.ext"
|
||||
(ext, ) = intree_extensions(
|
||||
[src.relto(tmpdir)], package_dir={"foo": "src"})
|
||||
assert ext.name == "foo.dir.ext"
|
||||
92
third_party/pybind11/tests/local_bindings.h
vendored
Normal file
92
third_party/pybind11/tests/local_bindings.h
vendored
Normal file
@@ -0,0 +1,92 @@
|
||||
#pragma once
|
||||
#include "pybind11_tests.h"
|
||||
|
||||
#include <utility>
|
||||
|
||||
/// Simple class used to test py::local:
|
||||
template <int>
|
||||
class LocalBase {
|
||||
public:
|
||||
explicit LocalBase(int i) : i(i) {}
|
||||
int i = -1;
|
||||
};
|
||||
|
||||
/// Registered with py::module_local in both main and secondary modules:
|
||||
using LocalType = LocalBase<0>;
|
||||
/// Registered without py::module_local in both modules:
|
||||
using NonLocalType = LocalBase<1>;
|
||||
/// A second non-local type (for stl_bind tests):
|
||||
using NonLocal2 = LocalBase<2>;
|
||||
/// Tests within-module, different-compilation-unit local definition conflict:
|
||||
using LocalExternal = LocalBase<3>;
|
||||
/// Mixed: registered local first, then global
|
||||
using MixedLocalGlobal = LocalBase<4>;
|
||||
/// Mixed: global first, then local
|
||||
using MixedGlobalLocal = LocalBase<5>;
|
||||
|
||||
/// Registered with py::module_local only in the secondary module:
|
||||
using ExternalType1 = LocalBase<6>;
|
||||
using ExternalType2 = LocalBase<7>;
|
||||
|
||||
using LocalVec = std::vector<LocalType>;
|
||||
using LocalVec2 = std::vector<NonLocal2>;
|
||||
using LocalMap = std::unordered_map<std::string, LocalType>;
|
||||
using NonLocalVec = std::vector<NonLocalType>;
|
||||
using NonLocalVec2 = std::vector<NonLocal2>;
|
||||
using NonLocalMap = std::unordered_map<std::string, NonLocalType>;
|
||||
using NonLocalMap2 = std::unordered_map<std::string, uint8_t>;
|
||||
|
||||
// Exception that will be caught via the module local translator.
|
||||
class LocalException : public std::exception {
|
||||
public:
|
||||
explicit LocalException(const char *m) : message{m} {}
|
||||
const char *what() const noexcept override { return message.c_str(); }
|
||||
|
||||
private:
|
||||
std::string message = "";
|
||||
};
|
||||
|
||||
// Exception that will be registered with register_local_exception_translator
|
||||
class LocalSimpleException : public std::exception {
|
||||
public:
|
||||
explicit LocalSimpleException(const char *m) : message{m} {}
|
||||
const char *what() const noexcept override { return message.c_str(); }
|
||||
|
||||
private:
|
||||
std::string message = "";
|
||||
};
|
||||
|
||||
PYBIND11_MAKE_OPAQUE(LocalVec);
|
||||
PYBIND11_MAKE_OPAQUE(LocalVec2);
|
||||
PYBIND11_MAKE_OPAQUE(LocalMap);
|
||||
PYBIND11_MAKE_OPAQUE(NonLocalVec);
|
||||
// PYBIND11_MAKE_OPAQUE(NonLocalVec2); // same type as LocalVec2
|
||||
PYBIND11_MAKE_OPAQUE(NonLocalMap);
|
||||
PYBIND11_MAKE_OPAQUE(NonLocalMap2);
|
||||
|
||||
// Simple bindings (used with the above):
|
||||
template <typename T, int Adjust = 0, typename... Args>
|
||||
py::class_<T> bind_local(Args &&...args) {
|
||||
return py::class_<T>(std::forward<Args>(args)...).def(py::init<int>()).def("get", [](T &i) {
|
||||
return i.i + Adjust;
|
||||
});
|
||||
};
|
||||
|
||||
// Simulate a foreign library base class (to match the example in the docs):
|
||||
namespace pets {
|
||||
class Pet {
|
||||
public:
|
||||
explicit Pet(std::string name) : name_(std::move(name)) {}
|
||||
std::string name_;
|
||||
const std::string &name() const { return name_; }
|
||||
};
|
||||
} // namespace pets
|
||||
|
||||
struct MixGL {
|
||||
int i;
|
||||
explicit MixGL(int i) : i{i} {}
|
||||
};
|
||||
struct MixGL2 {
|
||||
int i;
|
||||
explicit MixGL2(int i) : i{i} {}
|
||||
};
|
||||
205
third_party/pybind11/tests/object.h
vendored
Normal file
205
third_party/pybind11/tests/object.h
vendored
Normal file
@@ -0,0 +1,205 @@
|
||||
#if !defined(__OBJECT_H)
|
||||
# define __OBJECT_H
|
||||
|
||||
# include "constructor_stats.h"
|
||||
|
||||
# include <atomic>
|
||||
|
||||
/// Reference counted object base class
|
||||
class Object {
|
||||
public:
|
||||
/// Default constructor
|
||||
Object() { print_default_created(this); }
|
||||
|
||||
/// Copy constructor
|
||||
Object(const Object &) : m_refCount(0) { print_copy_created(this); }
|
||||
|
||||
/// Return the current reference count
|
||||
int getRefCount() const { return m_refCount; };
|
||||
|
||||
/// Increase the object's reference count by one
|
||||
void incRef() const { ++m_refCount; }
|
||||
|
||||
/** \brief Decrease the reference count of
|
||||
* the object and possibly deallocate it.
|
||||
*
|
||||
* The object will automatically be deallocated once
|
||||
* the reference count reaches zero.
|
||||
*/
|
||||
void decRef(bool dealloc = true) const {
|
||||
--m_refCount;
|
||||
if (m_refCount == 0 && dealloc) {
|
||||
delete this;
|
||||
} else if (m_refCount < 0) {
|
||||
throw std::runtime_error("Internal error: reference count < 0!");
|
||||
}
|
||||
}
|
||||
|
||||
virtual std::string toString() const = 0;
|
||||
|
||||
protected:
|
||||
/** \brief Virtual protected deconstructor.
|
||||
* (Will only be called by \ref ref)
|
||||
*/
|
||||
virtual ~Object() { print_destroyed(this); }
|
||||
|
||||
private:
|
||||
mutable std::atomic<int> m_refCount{0};
|
||||
};
|
||||
|
||||
// Tag class used to track constructions of ref objects. When we track constructors, below, we
|
||||
// track and print out the actual class (e.g. ref<MyObject>), and *also* add a fake tracker for
|
||||
// ref_tag. This lets us check that the total number of ref<Anything> constructors/destructors is
|
||||
// correct without having to check each individual ref<Whatever> type individually.
|
||||
class ref_tag {};
|
||||
|
||||
/**
|
||||
* \brief Reference counting helper
|
||||
*
|
||||
* The \a ref refeference template is a simple wrapper to store a
|
||||
* pointer to an object. It takes care of increasing and decreasing
|
||||
* the reference count of the object. When the last reference goes
|
||||
* out of scope, the associated object will be deallocated.
|
||||
*
|
||||
* \ingroup libcore
|
||||
*/
|
||||
template <typename T>
|
||||
class ref {
|
||||
public:
|
||||
/// Create a nullptr reference
|
||||
ref() : m_ptr(nullptr) {
|
||||
print_default_created(this);
|
||||
track_default_created((ref_tag *) this);
|
||||
}
|
||||
|
||||
/// Construct a reference from a pointer
|
||||
explicit ref(T *ptr) : m_ptr(ptr) {
|
||||
if (m_ptr) {
|
||||
((Object *) m_ptr)->incRef();
|
||||
}
|
||||
|
||||
print_created(this, "from pointer", m_ptr);
|
||||
track_created((ref_tag *) this, "from pointer");
|
||||
}
|
||||
|
||||
/// Copy constructor
|
||||
ref(const ref &r) : m_ptr(r.m_ptr) {
|
||||
if (m_ptr) {
|
||||
((Object *) m_ptr)->incRef();
|
||||
}
|
||||
|
||||
print_copy_created(this, "with pointer", m_ptr);
|
||||
track_copy_created((ref_tag *) this);
|
||||
}
|
||||
|
||||
/// Move constructor
|
||||
ref(ref &&r) noexcept : m_ptr(r.m_ptr) {
|
||||
r.m_ptr = nullptr;
|
||||
|
||||
print_move_created(this, "with pointer", m_ptr);
|
||||
track_move_created((ref_tag *) this);
|
||||
}
|
||||
|
||||
/// Destroy this reference
|
||||
~ref() {
|
||||
if (m_ptr) {
|
||||
((Object *) m_ptr)->decRef();
|
||||
}
|
||||
|
||||
print_destroyed(this);
|
||||
track_destroyed((ref_tag *) this);
|
||||
}
|
||||
|
||||
/// Move another reference into the current one
|
||||
ref &operator=(ref &&r) noexcept {
|
||||
print_move_assigned(this, "pointer", r.m_ptr);
|
||||
track_move_assigned((ref_tag *) this);
|
||||
|
||||
if (*this == r) {
|
||||
return *this;
|
||||
}
|
||||
if (m_ptr) {
|
||||
((Object *) m_ptr)->decRef();
|
||||
}
|
||||
m_ptr = r.m_ptr;
|
||||
r.m_ptr = nullptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Overwrite this reference with another reference
|
||||
ref &operator=(const ref &r) {
|
||||
if (this == &r) {
|
||||
return *this;
|
||||
}
|
||||
print_copy_assigned(this, "pointer", r.m_ptr);
|
||||
track_copy_assigned((ref_tag *) this);
|
||||
|
||||
if (m_ptr == r.m_ptr) {
|
||||
return *this;
|
||||
}
|
||||
if (m_ptr) {
|
||||
((Object *) m_ptr)->decRef();
|
||||
}
|
||||
m_ptr = r.m_ptr;
|
||||
if (m_ptr) {
|
||||
((Object *) m_ptr)->incRef();
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Overwrite this reference with a pointer to another object
|
||||
ref &operator=(T *ptr) {
|
||||
print_values(this, "assigned pointer");
|
||||
track_values((ref_tag *) this, "assigned pointer");
|
||||
|
||||
if (m_ptr == ptr) {
|
||||
return *this;
|
||||
}
|
||||
if (m_ptr) {
|
||||
((Object *) m_ptr)->decRef();
|
||||
}
|
||||
m_ptr = ptr;
|
||||
if (m_ptr) {
|
||||
((Object *) m_ptr)->incRef();
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Compare this reference with another reference
|
||||
bool operator==(const ref &r) const { return m_ptr == r.m_ptr; }
|
||||
|
||||
/// Compare this reference with another reference
|
||||
bool operator!=(const ref &r) const { return m_ptr != r.m_ptr; }
|
||||
|
||||
/// Compare this reference with a pointer
|
||||
bool operator==(const T *ptr) const { return m_ptr == ptr; }
|
||||
|
||||
/// Compare this reference with a pointer
|
||||
bool operator!=(const T *ptr) const { return m_ptr != ptr; }
|
||||
|
||||
/// Access the object referenced by this reference
|
||||
T *operator->() { return m_ptr; }
|
||||
|
||||
/// Access the object referenced by this reference
|
||||
const T *operator->() const { return m_ptr; }
|
||||
|
||||
/// Return a C++ reference to the referenced object
|
||||
T &operator*() { return *m_ptr; }
|
||||
|
||||
/// Return a const C++ reference to the referenced object
|
||||
const T &operator*() const { return *m_ptr; }
|
||||
|
||||
/// Return a pointer to the referenced object
|
||||
explicit operator T *() { return m_ptr; }
|
||||
|
||||
/// Return a const pointer to the referenced object
|
||||
T *get_ptr() { return m_ptr; }
|
||||
|
||||
/// Return a pointer to the referenced object
|
||||
const T *get_ptr() const { return m_ptr; }
|
||||
|
||||
private:
|
||||
T *m_ptr;
|
||||
};
|
||||
|
||||
#endif /* __OBJECT_H */
|
||||
149
third_party/pybind11/tests/pybind11_cross_module_tests.cpp
vendored
Normal file
149
third_party/pybind11/tests/pybind11_cross_module_tests.cpp
vendored
Normal file
@@ -0,0 +1,149 @@
|
||||
/*
|
||||
tests/pybind11_cross_module_tests.cpp -- contains tests that require multiple modules
|
||||
|
||||
Copyright (c) 2017 Jason Rhinelander <jason@imaginary.ca>
|
||||
|
||||
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/stl_bind.h>
|
||||
|
||||
#include "local_bindings.h"
|
||||
#include "pybind11_tests.h"
|
||||
#include "test_exceptions.h"
|
||||
|
||||
#include <numeric>
|
||||
#include <utility>
|
||||
|
||||
PYBIND11_MODULE(pybind11_cross_module_tests, m) {
|
||||
m.doc() = "pybind11 cross-module test module";
|
||||
|
||||
// test_local_bindings.py tests:
|
||||
//
|
||||
// Definitions here are tested by importing both this module and the
|
||||
// relevant pybind11_tests submodule from a test_whatever.py
|
||||
|
||||
// test_load_external
|
||||
bind_local<ExternalType1>(m, "ExternalType1", py::module_local());
|
||||
bind_local<ExternalType2>(m, "ExternalType2", py::module_local());
|
||||
|
||||
// test_exceptions.py
|
||||
py::register_local_exception<LocalSimpleException>(m, "LocalSimpleException");
|
||||
m.def("raise_runtime_error", []() {
|
||||
PyErr_SetString(PyExc_RuntimeError, "My runtime error");
|
||||
throw py::error_already_set();
|
||||
});
|
||||
m.def("raise_value_error", []() {
|
||||
PyErr_SetString(PyExc_ValueError, "My value error");
|
||||
throw py::error_already_set();
|
||||
});
|
||||
m.def("throw_pybind_value_error", []() { throw py::value_error("pybind11 value error"); });
|
||||
m.def("throw_pybind_type_error", []() { throw py::type_error("pybind11 type error"); });
|
||||
m.def("throw_stop_iteration", []() { throw py::stop_iteration(); });
|
||||
m.def("throw_local_error", []() { throw LocalException("just local"); });
|
||||
m.def("throw_local_simple_error", []() { throw LocalSimpleException("external mod"); });
|
||||
py::register_exception_translator([](std::exception_ptr p) {
|
||||
try {
|
||||
if (p) {
|
||||
std::rethrow_exception(p);
|
||||
}
|
||||
} catch (const shared_exception &e) {
|
||||
PyErr_SetString(PyExc_KeyError, e.what());
|
||||
}
|
||||
});
|
||||
|
||||
// translate the local exception into a key error but only in this module
|
||||
py::register_local_exception_translator([](std::exception_ptr p) {
|
||||
try {
|
||||
if (p) {
|
||||
std::rethrow_exception(p);
|
||||
}
|
||||
} catch (const LocalException &e) {
|
||||
PyErr_SetString(PyExc_KeyError, e.what());
|
||||
}
|
||||
});
|
||||
|
||||
// test_local_bindings.py
|
||||
// Local to both:
|
||||
bind_local<LocalType, 1>(m, "LocalType", py::module_local()).def("get2", [](LocalType &t) {
|
||||
return t.i + 2;
|
||||
});
|
||||
|
||||
// Can only be called with our python type:
|
||||
m.def("local_value", [](LocalType &l) { return l.i; });
|
||||
|
||||
// test_nonlocal_failure
|
||||
// This registration will fail (global registration when LocalFail is already registered
|
||||
// globally in the main test module):
|
||||
m.def("register_nonlocal", [m]() { bind_local<NonLocalType, 0>(m, "NonLocalType"); });
|
||||
|
||||
// test_stl_bind_local
|
||||
// stl_bind.h binders defaults to py::module_local if the types are local or converting:
|
||||
py::bind_vector<LocalVec>(m, "LocalVec");
|
||||
py::bind_map<LocalMap>(m, "LocalMap");
|
||||
|
||||
// test_stl_bind_global
|
||||
// and global if the type (or one of the types, for the map) is global (so these will fail,
|
||||
// assuming pybind11_tests is already loaded):
|
||||
m.def("register_nonlocal_vec", [m]() { py::bind_vector<NonLocalVec>(m, "NonLocalVec"); });
|
||||
m.def("register_nonlocal_map", [m]() { py::bind_map<NonLocalMap>(m, "NonLocalMap"); });
|
||||
// The default can, however, be overridden to global using `py::module_local()` or
|
||||
// `py::module_local(false)`.
|
||||
// Explicitly made local:
|
||||
py::bind_vector<NonLocalVec2>(m, "NonLocalVec2", py::module_local());
|
||||
// Explicitly made global (and so will fail to bind):
|
||||
m.def("register_nonlocal_map2",
|
||||
[m]() { py::bind_map<NonLocalMap2>(m, "NonLocalMap2", py::module_local(false)); });
|
||||
|
||||
// test_mixed_local_global
|
||||
// We try this both with the global type registered first and vice versa (the order shouldn't
|
||||
// matter).
|
||||
m.def("register_mixed_global_local",
|
||||
[m]() { bind_local<MixedGlobalLocal, 200>(m, "MixedGlobalLocal", py::module_local()); });
|
||||
m.def("register_mixed_local_global", [m]() {
|
||||
bind_local<MixedLocalGlobal, 2000>(m, "MixedLocalGlobal", py::module_local(false));
|
||||
});
|
||||
m.def("get_mixed_gl", [](int i) { return MixedGlobalLocal(i); });
|
||||
m.def("get_mixed_lg", [](int i) { return MixedLocalGlobal(i); });
|
||||
|
||||
// test_internal_locals_differ
|
||||
m.def("local_cpp_types_addr",
|
||||
[]() { return (uintptr_t) &py::detail::get_local_internals().registered_types_cpp; });
|
||||
|
||||
// test_stl_caster_vs_stl_bind
|
||||
py::bind_vector<std::vector<int>>(m, "VectorInt");
|
||||
|
||||
m.def("load_vector_via_binding",
|
||||
[](std::vector<int> &v) { return std::accumulate(v.begin(), v.end(), 0); });
|
||||
|
||||
// test_cross_module_calls
|
||||
m.def("return_self", [](LocalVec *v) { return v; });
|
||||
m.def("return_copy", [](const LocalVec &v) { return LocalVec(v); });
|
||||
|
||||
class Dog : public pets::Pet {
|
||||
public:
|
||||
explicit Dog(std::string name) : Pet(std::move(name)) {}
|
||||
};
|
||||
py::class_<pets::Pet>(m, "Pet", py::module_local()).def("name", &pets::Pet::name);
|
||||
// Binding for local extending class:
|
||||
py::class_<Dog, pets::Pet>(m, "Dog").def(py::init<std::string>());
|
||||
m.def("pet_name", [](pets::Pet &p) { return p.name(); });
|
||||
|
||||
py::class_<MixGL>(m, "MixGL", py::module_local()).def(py::init<int>());
|
||||
m.def("get_gl_value", [](MixGL &o) { return o.i + 100; });
|
||||
|
||||
py::class_<MixGL2>(m, "MixGL2", py::module_local()).def(py::init<int>());
|
||||
|
||||
// test_vector_bool
|
||||
// We can't test both stl.h and stl_bind.h conversions of `std::vector<bool>` within
|
||||
// the same module (it would be an ODR violation). Therefore `bind_vector` of `bool`
|
||||
// is defined here and tested in `test_stl_binders.py`.
|
||||
py::bind_vector<std::vector<bool>>(m, "VectorBool");
|
||||
|
||||
// test_missing_header_message
|
||||
// The main module already includes stl.h, but we need to test the error message
|
||||
// which appears when this header is missing.
|
||||
m.def("missing_header_arg", [](const std::vector<float> &) {});
|
||||
m.def("missing_header_return", []() { return std::vector<float>(); });
|
||||
}
|
||||
92
third_party/pybind11/tests/pybind11_tests.cpp
vendored
Normal file
92
third_party/pybind11/tests/pybind11_tests.cpp
vendored
Normal file
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
tests/pybind11_tests.cpp -- pybind example plugin
|
||||
|
||||
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"
|
||||
|
||||
#include <functional>
|
||||
#include <list>
|
||||
|
||||
/*
|
||||
For testing purposes, we define a static global variable here in a function that each individual
|
||||
test .cpp calls with its initialization lambda. It's convenient here because we can just not
|
||||
compile some test files to disable/ignore some of the test code.
|
||||
|
||||
It is NOT recommended as a way to use pybind11 in practice, however: the initialization order will
|
||||
be essentially random, which is okay for our test scripts (there are no dependencies between the
|
||||
individual pybind11 test .cpp files), but most likely not what you want when using pybind11
|
||||
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;
|
||||
return inits;
|
||||
}
|
||||
|
||||
test_initializer::test_initializer(Initializer init) { initializers().emplace_back(init); }
|
||||
|
||||
test_initializer::test_initializer(const char *submodule_name, Initializer init) {
|
||||
initializers().emplace_back([=](py::module_ &parent) {
|
||||
auto m = parent.def_submodule(submodule_name);
|
||||
init(m);
|
||||
});
|
||||
}
|
||||
|
||||
void bind_ConstructorStats(py::module_ &m) {
|
||||
py::class_<ConstructorStats>(m, "ConstructorStats")
|
||||
.def("alive", &ConstructorStats::alive)
|
||||
.def("values", &ConstructorStats::values)
|
||||
.def_readwrite("default_constructions", &ConstructorStats::default_constructions)
|
||||
.def_readwrite("copy_assignments", &ConstructorStats::copy_assignments)
|
||||
.def_readwrite("move_assignments", &ConstructorStats::move_assignments)
|
||||
.def_readwrite("copy_constructions", &ConstructorStats::copy_constructions)
|
||||
.def_readwrite("move_constructions", &ConstructorStats::move_constructions)
|
||||
.def_static("get",
|
||||
(ConstructorStats & (*) (py::object)) & ConstructorStats::get,
|
||||
py::return_value_policy::reference_internal)
|
||||
|
||||
// Not exactly ConstructorStats, but related: expose the internal pybind number of
|
||||
// registered instances to allow instance cleanup checks (invokes a GC first)
|
||||
.def_static("detail_reg_inst", []() {
|
||||
ConstructorStats::gc();
|
||||
return py::detail::get_internals().registered_instances.size();
|
||||
});
|
||||
}
|
||||
|
||||
PYBIND11_MODULE(pybind11_tests, m) {
|
||||
m.doc() = "pybind11 test module";
|
||||
|
||||
bind_ConstructorStats(m);
|
||||
|
||||
#if defined(PYBIND11_DETAILED_ERROR_MESSAGES)
|
||||
m.attr("detailed_error_messages_enabled") = true;
|
||||
#else
|
||||
m.attr("detailed_error_messages_enabled") = false;
|
||||
#endif
|
||||
|
||||
py::class_<UserType>(m, "UserType", "A `py::class_` type for testing")
|
||||
.def(py::init<>())
|
||||
.def(py::init<int>())
|
||||
.def("get_value", &UserType::value, "Get value using a method")
|
||||
.def("set_value", &UserType::set, "Set value using a method")
|
||||
.def_property("value", &UserType::value, &UserType::set, "Get/set value using a property")
|
||||
.def("__repr__", [](const UserType &u) { return "UserType({})"_s.format(u.value()); });
|
||||
|
||||
py::class_<IncType, UserType>(m, "IncType")
|
||||
.def(py::init<>())
|
||||
.def(py::init<int>())
|
||||
.def("__repr__", [](const IncType &u) { return "IncType({})"_s.format(u.value()); });
|
||||
|
||||
for (const auto &initializer : initializers()) {
|
||||
initializer(m);
|
||||
}
|
||||
}
|
||||
85
third_party/pybind11/tests/pybind11_tests.h
vendored
Normal file
85
third_party/pybind11/tests/pybind11_tests.h
vendored
Normal file
@@ -0,0 +1,85 @@
|
||||
#pragma once
|
||||
|
||||
#include <pybind11/eval.h>
|
||||
#include <pybind11/pybind11.h>
|
||||
|
||||
namespace py = pybind11;
|
||||
using namespace pybind11::literals;
|
||||
|
||||
class test_initializer {
|
||||
using Initializer = void (*)(py::module_ &);
|
||||
|
||||
public:
|
||||
explicit test_initializer(Initializer init);
|
||||
test_initializer(const char *submodule_name, Initializer init);
|
||||
};
|
||||
|
||||
#define TEST_SUBMODULE(name, variable) \
|
||||
void test_submodule_##name(py::module_ &); \
|
||||
test_initializer name(#name, test_submodule_##name); \
|
||||
void test_submodule_##name(py::module_ &(variable))
|
||||
|
||||
/// Dummy type which is not exported anywhere -- something to trigger a conversion error
|
||||
struct UnregisteredType {};
|
||||
|
||||
/// A user-defined type which is exported and can be used by any test
|
||||
class UserType {
|
||||
public:
|
||||
UserType() = default;
|
||||
explicit UserType(int i) : i(i) {}
|
||||
|
||||
int value() const { return i; }
|
||||
void set(int set) { i = set; }
|
||||
|
||||
private:
|
||||
int i = -1;
|
||||
};
|
||||
|
||||
/// Like UserType, but increments `value` on copy for quick reference vs. copy tests
|
||||
class IncType : public UserType {
|
||||
public:
|
||||
using UserType::UserType;
|
||||
IncType() = default;
|
||||
IncType(const IncType &other) : IncType(other.value() + 1) {}
|
||||
IncType(IncType &&) = delete;
|
||||
IncType &operator=(const IncType &) = delete;
|
||||
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 {};
|
||||
PYBIND11_NAMESPACE_BEGIN(pybind11)
|
||||
PYBIND11_NAMESPACE_BEGIN(detail)
|
||||
template <>
|
||||
class type_caster<RValueCaster> {
|
||||
public:
|
||||
PYBIND11_TYPE_CASTER(RValueCaster, const_name("RValueCaster"));
|
||||
static handle cast(RValueCaster &&, return_value_policy, handle) {
|
||||
return py::str("rvalue").release();
|
||||
}
|
||||
static handle cast(const RValueCaster &, return_value_policy, handle) {
|
||||
return py::str("lvalue").release();
|
||||
}
|
||||
};
|
||||
PYBIND11_NAMESPACE_END(detail)
|
||||
PYBIND11_NAMESPACE_END(pybind11)
|
||||
|
||||
template <typename F>
|
||||
void ignoreOldStyleInitWarnings(F &&body) {
|
||||
py::exec(R"(
|
||||
message = "pybind11-bound class '.+' is using an old-style placement-new '(?:__init__|__setstate__)' which has been deprecated"
|
||||
|
||||
import warnings
|
||||
with warnings.catch_warnings():
|
||||
warnings.filterwarnings("ignore", message=message, category=FutureWarning)
|
||||
body()
|
||||
)",
|
||||
py::dict(py::arg("body") = py::cpp_function(body)));
|
||||
}
|
||||
22
third_party/pybind11/tests/pytest.ini
vendored
Normal file
22
third_party/pybind11/tests/pytest.ini
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
[pytest]
|
||||
minversion = 3.10
|
||||
norecursedirs = test_* extra_*
|
||||
xfail_strict = True
|
||||
addopts =
|
||||
# show summary of tests
|
||||
-ra
|
||||
# capture only Python print and C++ py::print, but not C output (low-level Python errors)
|
||||
--capture=sys
|
||||
# Show local info when a failure occurs
|
||||
--showlocals
|
||||
log_cli_level = info
|
||||
filterwarnings =
|
||||
# make warnings into errors but ignore certain third-party extension issues
|
||||
error
|
||||
# somehow, some DeprecationWarnings do not get turned into errors
|
||||
always::DeprecationWarning
|
||||
# importing scipy submodules on some version of Python
|
||||
ignore::ImportWarning
|
||||
# bogus numpy ABI warning (see numpy/#432)
|
||||
ignore:.*numpy.dtype size changed.*:RuntimeWarning
|
||||
ignore:.*numpy.ufunc size changed.*:RuntimeWarning
|
||||
9
third_party/pybind11/tests/requirements.txt
vendored
Normal file
9
third_party/pybind11/tests/requirements.txt
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
build==0.7.0
|
||||
numpy==1.21.5; platform_python_implementation=="PyPy" and sys_platform=="linux" and python_version=="3.7"
|
||||
numpy==1.19.3; platform_python_implementation!="PyPy" and python_version=="3.6"
|
||||
numpy==1.21.5; platform_python_implementation!="PyPy" and python_version>="3.7" and python_version<"3.10"
|
||||
numpy==1.22.2; platform_python_implementation!="PyPy" and python_version>="3.10" and python_version<"3.11"
|
||||
pytest==7.0.0
|
||||
pytest-timeout
|
||||
scipy==1.5.4; platform_python_implementation!="PyPy" and python_version<"3.10"
|
||||
scipy==1.8.0; platform_python_implementation!="PyPy" and python_version=="3.10"
|
||||
25
third_party/pybind11/tests/test_async.cpp
vendored
Normal file
25
third_party/pybind11/tests/test_async.cpp
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
tests/test_async.cpp -- __await__ support
|
||||
|
||||
Copyright (c) 2019 Google Inc.
|
||||
|
||||
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"
|
||||
|
||||
TEST_SUBMODULE(async_module, m) {
|
||||
struct DoesNotSupportAsync {};
|
||||
py::class_<DoesNotSupportAsync>(m, "DoesNotSupportAsync").def(py::init<>());
|
||||
struct SupportsAsync {};
|
||||
py::class_<SupportsAsync>(m, "SupportsAsync")
|
||||
.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 f = loop.attr("create_future")();
|
||||
f.attr("set_result")(5);
|
||||
return f.attr("__await__")();
|
||||
});
|
||||
}
|
||||
26
third_party/pybind11/tests/test_async.py
vendored
Normal file
26
third_party/pybind11/tests/test_async.py
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
import pytest
|
||||
|
||||
asyncio = pytest.importorskip("asyncio")
|
||||
m = pytest.importorskip("pybind11_tests.async_module")
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def event_loop():
|
||||
loop = asyncio.new_event_loop()
|
||||
yield loop
|
||||
loop.close()
|
||||
|
||||
|
||||
async def get_await_result(x):
|
||||
return await x
|
||||
|
||||
|
||||
def test_await(event_loop):
|
||||
assert 5 == event_loop.run_until_complete(
|
||||
get_await_result(m.SupportsAsync()))
|
||||
|
||||
|
||||
def test_await_missing(event_loop):
|
||||
with pytest.raises(TypeError):
|
||||
event_loop.run_until_complete(
|
||||
get_await_result(m.DoesNotSupportAsync()))
|
||||
224
third_party/pybind11/tests/test_buffers.cpp
vendored
Normal file
224
third_party/pybind11/tests/test_buffers.cpp
vendored
Normal file
@@ -0,0 +1,224 @@
|
||||
/*
|
||||
tests/test_buffers.cpp -- supporting Pythons' buffer protocol
|
||||
|
||||
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/stl.h>
|
||||
|
||||
#include "constructor_stats.h"
|
||||
#include "pybind11_tests.h"
|
||||
|
||||
TEST_SUBMODULE(buffers, m) {
|
||||
// test_from_python / test_to_python:
|
||||
class Matrix {
|
||||
public:
|
||||
Matrix(py::ssize_t rows, py::ssize_t cols) : m_rows(rows), m_cols(cols) {
|
||||
print_created(this, std::to_string(m_rows) + "x" + std::to_string(m_cols) + " matrix");
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-prefer-member-initializer)
|
||||
m_data = new float[(size_t) (rows * cols)];
|
||||
memset(m_data, 0, sizeof(float) * (size_t) (rows * cols));
|
||||
}
|
||||
|
||||
Matrix(const Matrix &s) : m_rows(s.m_rows), m_cols(s.m_cols) {
|
||||
print_copy_created(this,
|
||||
std::to_string(m_rows) + "x" + std::to_string(m_cols) + " matrix");
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-prefer-member-initializer)
|
||||
m_data = new float[(size_t) (m_rows * m_cols)];
|
||||
memcpy(m_data, s.m_data, sizeof(float) * (size_t) (m_rows * m_cols));
|
||||
}
|
||||
|
||||
Matrix(Matrix &&s) noexcept : m_rows(s.m_rows), m_cols(s.m_cols), m_data(s.m_data) {
|
||||
print_move_created(this);
|
||||
s.m_rows = 0;
|
||||
s.m_cols = 0;
|
||||
s.m_data = nullptr;
|
||||
}
|
||||
|
||||
~Matrix() {
|
||||
print_destroyed(this,
|
||||
std::to_string(m_rows) + "x" + std::to_string(m_cols) + " matrix");
|
||||
delete[] m_data;
|
||||
}
|
||||
|
||||
Matrix &operator=(const Matrix &s) {
|
||||
if (this == &s) {
|
||||
return *this;
|
||||
}
|
||||
print_copy_assigned(this,
|
||||
std::to_string(m_rows) + "x" + std::to_string(m_cols) + " matrix");
|
||||
delete[] m_data;
|
||||
m_rows = s.m_rows;
|
||||
m_cols = s.m_cols;
|
||||
m_data = new float[(size_t) (m_rows * m_cols)];
|
||||
memcpy(m_data, s.m_data, sizeof(float) * (size_t) (m_rows * m_cols));
|
||||
return *this;
|
||||
}
|
||||
|
||||
Matrix &operator=(Matrix &&s) noexcept {
|
||||
print_move_assigned(this,
|
||||
std::to_string(m_rows) + "x" + std::to_string(m_cols) + " matrix");
|
||||
if (&s != this) {
|
||||
delete[] m_data;
|
||||
m_rows = s.m_rows;
|
||||
m_cols = s.m_cols;
|
||||
m_data = s.m_data;
|
||||
s.m_rows = 0;
|
||||
s.m_cols = 0;
|
||||
s.m_data = nullptr;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
float operator()(py::ssize_t i, py::ssize_t j) const {
|
||||
return m_data[(size_t) (i * m_cols + j)];
|
||||
}
|
||||
|
||||
float &operator()(py::ssize_t i, py::ssize_t j) {
|
||||
return m_data[(size_t) (i * m_cols + j)];
|
||||
}
|
||||
|
||||
float *data() { return m_data; }
|
||||
|
||||
py::ssize_t rows() const { return m_rows; }
|
||||
py::ssize_t cols() const { return m_cols; }
|
||||
|
||||
private:
|
||||
py::ssize_t m_rows;
|
||||
py::ssize_t m_cols;
|
||||
float *m_data;
|
||||
};
|
||||
py::class_<Matrix>(m, "Matrix", py::buffer_protocol())
|
||||
.def(py::init<py::ssize_t, py::ssize_t>())
|
||||
/// Construct from a buffer
|
||||
.def(py::init([](const py::buffer &b) {
|
||||
py::buffer_info info = b.request();
|
||||
if (info.format != py::format_descriptor<float>::format() || info.ndim != 2) {
|
||||
throw std::runtime_error("Incompatible buffer format!");
|
||||
}
|
||||
|
||||
auto *v = new Matrix(info.shape[0], info.shape[1]);
|
||||
memcpy(v->data(), info.ptr, sizeof(float) * (size_t) (v->rows() * v->cols()));
|
||||
return v;
|
||||
}))
|
||||
|
||||
.def("rows", &Matrix::rows)
|
||||
.def("cols", &Matrix::cols)
|
||||
|
||||
/// Bare bones interface
|
||||
.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<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;
|
||||
})
|
||||
/// Provide buffer access
|
||||
.def_buffer([](Matrix &m) -> py::buffer_info {
|
||||
return py::buffer_info(
|
||||
m.data(), /* Pointer to buffer */
|
||||
{m.rows(), m.cols()}, /* Buffer dimensions */
|
||||
{sizeof(float) * size_t(m.cols()), /* Strides (in bytes) for each index */
|
||||
sizeof(float)});
|
||||
});
|
||||
|
||||
// test_inherited_protocol
|
||||
class SquareMatrix : public Matrix {
|
||||
public:
|
||||
explicit 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<py::ssize_t>());
|
||||
|
||||
// test_pointer_to_member_fn
|
||||
// Tests that passing a pointer to member to the base class works in
|
||||
// the derived class.
|
||||
struct Buffer {
|
||||
int32_t value = 0;
|
||||
|
||||
py::buffer_info get_buffer_info() {
|
||||
return py::buffer_info(
|
||||
&value, sizeof(value), py::format_descriptor<int32_t>::format(), 1);
|
||||
}
|
||||
};
|
||||
py::class_<Buffer>(m, "Buffer", py::buffer_protocol())
|
||||
.def(py::init<>())
|
||||
.def_readwrite("value", &Buffer::value)
|
||||
.def_buffer(&Buffer::get_buffer_info);
|
||||
|
||||
class ConstBuffer {
|
||||
std::unique_ptr<int32_t> value;
|
||||
|
||||
public:
|
||||
int32_t get_value() const { return *value; }
|
||||
void set_value(int32_t v) { *value = v; }
|
||||
|
||||
py::buffer_info get_buffer_info() const {
|
||||
return py::buffer_info(
|
||||
value.get(), sizeof(*value), py::format_descriptor<int32_t>::format(), 1);
|
||||
}
|
||||
|
||||
ConstBuffer() : value(new int32_t{0}) {}
|
||||
};
|
||||
py::class_<ConstBuffer>(m, "ConstBuffer", py::buffer_protocol())
|
||||
.def(py::init<>())
|
||||
.def_property("value", &ConstBuffer::get_value, &ConstBuffer::set_value)
|
||||
.def_buffer(&ConstBuffer::get_buffer_info);
|
||||
|
||||
struct DerivedBuffer : public Buffer {};
|
||||
py::class_<DerivedBuffer>(m, "DerivedBuffer", py::buffer_protocol())
|
||||
.def(py::init<>())
|
||||
.def_readwrite("value", (int32_t DerivedBuffer::*) &DerivedBuffer::value)
|
||||
.def_buffer(&DerivedBuffer::get_buffer_info);
|
||||
|
||||
struct BufferReadOnly {
|
||||
const uint8_t value = 0;
|
||||
explicit BufferReadOnly(uint8_t value) : value(value) {}
|
||||
|
||||
py::buffer_info get_buffer_info() { return py::buffer_info(&value, 1); }
|
||||
};
|
||||
py::class_<BufferReadOnly>(m, "BufferReadOnly", py::buffer_protocol())
|
||||
.def(py::init<uint8_t>())
|
||||
.def_buffer(&BufferReadOnly::get_buffer_info);
|
||||
|
||||
struct BufferReadOnlySelect {
|
||||
uint8_t value = 0;
|
||||
bool readonly = false;
|
||||
|
||||
py::buffer_info get_buffer_info() { return py::buffer_info(&value, 1, readonly); }
|
||||
};
|
||||
py::class_<BufferReadOnlySelect>(m, "BufferReadOnlySelect", py::buffer_protocol())
|
||||
.def(py::init<>())
|
||||
.def_readwrite("value", &BufferReadOnlySelect::value)
|
||||
.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", [](const py::buffer &buffer) { return buffer.request(); });
|
||||
}
|
||||
162
third_party/pybind11/tests/test_buffers.py
vendored
Normal file
162
third_party/pybind11/tests/test_buffers.py
vendored
Normal file
@@ -0,0 +1,162 @@
|
||||
import ctypes
|
||||
import io
|
||||
import struct
|
||||
|
||||
import pytest
|
||||
|
||||
import env
|
||||
from pybind11_tests import ConstructorStats
|
||||
from pybind11_tests import buffers as m
|
||||
|
||||
np = pytest.importorskip("numpy")
|
||||
|
||||
|
||||
def test_from_python():
|
||||
with pytest.raises(RuntimeError) as excinfo:
|
||||
m.Matrix(np.array([1, 2, 3])) # trying to assign a 1D array
|
||||
assert str(excinfo.value) == "Incompatible buffer format!"
|
||||
|
||||
m3 = np.array([[1, 2, 3], [4, 5, 6]]).astype(np.float32)
|
||||
m4 = m.Matrix(m3)
|
||||
|
||||
for i in range(m4.rows()):
|
||||
for j in range(m4.cols()):
|
||||
assert m3[i, j] == m4[i, j]
|
||||
|
||||
cstats = ConstructorStats.get(m.Matrix)
|
||||
assert cstats.alive() == 1
|
||||
del m3, m4
|
||||
assert cstats.alive() == 0
|
||||
assert cstats.values() == ["2x3 matrix"]
|
||||
assert cstats.copy_constructions == 0
|
||||
# assert cstats.move_constructions >= 0 # Don't invoke any
|
||||
assert cstats.copy_assignments == 0
|
||||
assert cstats.move_assignments == 0
|
||||
|
||||
|
||||
# https://foss.heptapod.net/pypy/pypy/-/issues/2444
|
||||
# TODO: fix on recent PyPy
|
||||
@pytest.mark.xfail(
|
||||
env.PYPY, reason="PyPy 7.3.7 doesn't clear this anymore", strict=False)
|
||||
def test_to_python():
|
||||
mat = m.Matrix(5, 4)
|
||||
assert memoryview(mat).shape == (5, 4)
|
||||
|
||||
assert mat[2, 3] == 0
|
||||
mat[2, 3] = 4.0
|
||||
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, )
|
||||
|
||||
mat2 = np.array(mat, copy=False)
|
||||
assert mat2.shape == (5, 4)
|
||||
assert abs(mat2).sum() == 11
|
||||
assert mat2[2, 3] == 4 and mat2[3, 2] == 7
|
||||
mat2[2, 3] = 5
|
||||
assert mat2[2, 3] == 5
|
||||
|
||||
cstats = ConstructorStats.get(m.Matrix)
|
||||
assert cstats.alive() == 1
|
||||
del mat
|
||||
pytest.gc_collect()
|
||||
assert cstats.alive() == 1
|
||||
del mat2 # holds a mat reference
|
||||
pytest.gc_collect()
|
||||
assert cstats.alive() == 0
|
||||
assert cstats.values() == ["5x4 matrix"]
|
||||
assert cstats.copy_constructions == 0
|
||||
# assert cstats.move_constructions >= 0 # Don't invoke any
|
||||
assert cstats.copy_assignments == 0
|
||||
assert cstats.move_assignments == 0
|
||||
|
||||
|
||||
def test_inherited_protocol():
|
||||
"""SquareMatrix is derived from Matrix and inherits the buffer protocol"""
|
||||
|
||||
matrix = m.SquareMatrix(5)
|
||||
assert memoryview(matrix).shape == (5, 5)
|
||||
assert np.asarray(matrix).shape == (5, 5)
|
||||
|
||||
|
||||
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]
|
||||
assert value == 0x12345678
|
||||
|
||||
|
||||
def test_readonly_buffer():
|
||||
buf = m.BufferReadOnly(0x64)
|
||||
view = memoryview(buf)
|
||||
assert view[0] == 0x64
|
||||
assert view.readonly
|
||||
with pytest.raises(TypeError):
|
||||
view[0] = 0
|
||||
|
||||
|
||||
def test_selective_readonly_buffer():
|
||||
buf = m.BufferReadOnlySelect()
|
||||
|
||||
memoryview(buf)[0] = 0x64
|
||||
assert buf.value == 0x64
|
||||
|
||||
io.BytesIO(b"A").readinto(buf)
|
||||
assert buf.value == ord(b"A")
|
||||
|
||||
buf.readonly = True
|
||||
with pytest.raises(TypeError):
|
||||
memoryview(buf)[0] = 0
|
||||
with pytest.raises(TypeError):
|
||||
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
|
||||
|
||||
|
||||
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
|
||||
382
third_party/pybind11/tests/test_builtin_casters.cpp
vendored
Normal file
382
third_party/pybind11/tests/test_builtin_casters.cpp
vendored
Normal file
@@ -0,0 +1,382 @@
|
||||
/*
|
||||
tests/test_builtin_casters.cpp -- Casters available without any additional headers
|
||||
|
||||
Copyright (c) 2017 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/complex.h>
|
||||
|
||||
#include "pybind11_tests.h"
|
||||
|
||||
struct ConstRefCasted {
|
||||
int tag;
|
||||
};
|
||||
|
||||
PYBIND11_NAMESPACE_BEGIN(pybind11)
|
||||
PYBIND11_NAMESPACE_BEGIN(detail)
|
||||
template <>
|
||||
class type_caster<ConstRefCasted> {
|
||||
public:
|
||||
static constexpr auto name = const_name<ConstRefCasted>();
|
||||
|
||||
// Input is unimportant, a new value will always be constructed based on the
|
||||
// cast operator.
|
||||
bool load(handle, bool) { return true; }
|
||||
|
||||
explicit operator ConstRefCasted &&() {
|
||||
value = {1};
|
||||
// NOLINTNEXTLINE(performance-move-const-arg)
|
||||
return std::move(value);
|
||||
}
|
||||
explicit operator ConstRefCasted &() {
|
||||
value = {2};
|
||||
return value;
|
||||
}
|
||||
explicit operator ConstRefCasted *() {
|
||||
value = {3};
|
||||
return &value;
|
||||
}
|
||||
|
||||
explicit operator const ConstRefCasted &() {
|
||||
value = {4};
|
||||
return value;
|
||||
}
|
||||
explicit operator const ConstRefCasted *() {
|
||||
value = {5};
|
||||
return &value;
|
||||
}
|
||||
|
||||
// custom cast_op to explicitly propagate types to the conversion operators.
|
||||
template <typename T_>
|
||||
using cast_op_type =
|
||||
/// const
|
||||
conditional_t<
|
||||
std::is_same<remove_reference_t<T_>, const ConstRefCasted *>::value,
|
||||
const ConstRefCasted *,
|
||||
conditional_t<
|
||||
std::is_same<T_, const ConstRefCasted &>::value,
|
||||
const ConstRefCasted &,
|
||||
/// non-const
|
||||
conditional_t<std::is_same<remove_reference_t<T_>, ConstRefCasted *>::value,
|
||||
ConstRefCasted *,
|
||||
conditional_t<std::is_same<T_, ConstRefCasted &>::value,
|
||||
ConstRefCasted &,
|
||||
/* else */ ConstRefCasted &&>>>>;
|
||||
|
||||
private:
|
||||
ConstRefCasted value = {0};
|
||||
};
|
||||
PYBIND11_NAMESPACE_END(detail)
|
||||
PYBIND11_NAMESPACE_END(pybind11)
|
||||
|
||||
TEST_SUBMODULE(builtin_casters, m) {
|
||||
// test_simple_string
|
||||
m.def("string_roundtrip", [](const char *s) { return s; });
|
||||
|
||||
// test_unicode_conversion
|
||||
// Some test characters in utf16 and utf32 encodings. The last one (the 𝐀) contains a null
|
||||
// byte
|
||||
char32_t a32 = 0x61 /*a*/, z32 = 0x7a /*z*/, ib32 = 0x203d /*‽*/, cake32 = 0x1f382 /*🎂*/,
|
||||
mathbfA32 = 0x1d400 /*𝐀*/;
|
||||
char16_t b16 = 0x62 /*b*/, z16 = 0x7a, ib16 = 0x203d, cake16_1 = 0xd83c, cake16_2 = 0xdf82,
|
||||
mathbfA16_1 = 0xd835, mathbfA16_2 = 0xdc00;
|
||||
std::wstring wstr;
|
||||
wstr.push_back(0x61); // a
|
||||
wstr.push_back(0x2e18); // ⸘
|
||||
if (PYBIND11_SILENCE_MSVC_C4127(sizeof(wchar_t) == 2)) {
|
||||
wstr.push_back(mathbfA16_1);
|
||||
wstr.push_back(mathbfA16_2);
|
||||
} // 𝐀, utf16
|
||||
else {
|
||||
wstr.push_back((wchar_t) mathbfA32);
|
||||
} // 𝐀, utf32
|
||||
wstr.push_back(0x7a); // z
|
||||
|
||||
m.def("good_utf8_string", []() {
|
||||
return std::string((const char *) u8"Say utf8\u203d \U0001f382 \U0001d400");
|
||||
}); // Say utf8‽ 🎂 𝐀
|
||||
m.def("good_utf16_string", [=]() {
|
||||
return std::u16string({b16, ib16, cake16_1, cake16_2, mathbfA16_1, mathbfA16_2, z16});
|
||||
}); // b‽🎂𝐀z
|
||||
m.def("good_utf32_string", [=]() {
|
||||
return std::u32string({a32, mathbfA32, cake32, ib32, z32});
|
||||
}); // a𝐀🎂‽z
|
||||
m.def("good_wchar_string", [=]() { return wstr; }); // a‽𝐀z
|
||||
m.def("bad_utf8_string", []() {
|
||||
return std::string("abc\xd0"
|
||||
"def");
|
||||
});
|
||||
m.def("bad_utf16_string", [=]() { return std::u16string({b16, char16_t(0xd800), z16}); });
|
||||
// Under Python 2.7, invalid unicode UTF-32 characters didn't appear to trigger
|
||||
// UnicodeDecodeError
|
||||
m.def("bad_utf32_string", [=]() { return std::u32string({a32, char32_t(0xd800), z32}); });
|
||||
if (PYBIND11_SILENCE_MSVC_C4127(sizeof(wchar_t) == 2)) {
|
||||
m.def("bad_wchar_string", [=]() {
|
||||
return std::wstring({wchar_t(0x61), wchar_t(0xd800)});
|
||||
});
|
||||
}
|
||||
m.def("u8_Z", []() -> char { return 'Z'; });
|
||||
m.def("u8_eacute", []() -> char { return '\xe9'; });
|
||||
m.def("u16_ibang", [=]() -> char16_t { return ib16; });
|
||||
m.def("u32_mathbfA", [=]() -> char32_t { return mathbfA32; });
|
||||
m.def("wchar_heart", []() -> wchar_t { return 0x2665; });
|
||||
|
||||
// test_single_char_arguments
|
||||
m.attr("wchar_size") = py::cast(sizeof(wchar_t));
|
||||
m.def("ord_char", [](char c) -> int { return static_cast<unsigned char>(c); });
|
||||
m.def("ord_char_lv", [](char &c) -> int { return static_cast<unsigned char>(c); });
|
||||
m.def("ord_char16", [](char16_t c) -> uint16_t { return c; });
|
||||
m.def("ord_char16_lv", [](char16_t &c) -> uint16_t { return c; });
|
||||
m.def("ord_char32", [](char32_t c) -> uint32_t { return c; });
|
||||
m.def("ord_wchar", [](wchar_t c) -> int { return c; });
|
||||
|
||||
// test_bytes_to_string
|
||||
m.def("strlen", [](char *s) { return strlen(s); });
|
||||
m.def("string_length", [](const std::string &s) { return s.length(); });
|
||||
|
||||
#ifdef PYBIND11_HAS_U8STRING
|
||||
m.attr("has_u8string") = true;
|
||||
m.def("good_utf8_u8string", []() {
|
||||
return std::u8string(u8"Say utf8\u203d \U0001f382 \U0001d400");
|
||||
}); // Say utf8‽ 🎂 𝐀
|
||||
m.def("bad_utf8_u8string", []() {
|
||||
return std::u8string((const char8_t *) "abc\xd0"
|
||||
"def");
|
||||
});
|
||||
|
||||
m.def("u8_char8_Z", []() -> char8_t { return u8'Z'; });
|
||||
|
||||
// test_single_char_arguments
|
||||
m.def("ord_char8", [](char8_t c) -> int { return static_cast<unsigned char>(c); });
|
||||
m.def("ord_char8_lv", [](char8_t &c) -> int { return static_cast<unsigned char>(c); });
|
||||
#endif
|
||||
|
||||
// test_string_view
|
||||
#ifdef PYBIND11_HAS_STRING_VIEW
|
||||
m.attr("has_string_view") = true;
|
||||
m.def("string_view_print", [](std::string_view s) { py::print(s, s.size()); });
|
||||
m.def("string_view16_print", [](std::u16string_view s) { py::print(s, s.size()); });
|
||||
m.def("string_view32_print", [](std::u32string_view s) { py::print(s, s.size()); });
|
||||
m.def("string_view_chars", [](std::string_view s) {
|
||||
py::list l;
|
||||
for (auto c : s) {
|
||||
l.append((std::uint8_t) c);
|
||||
}
|
||||
return l;
|
||||
});
|
||||
m.def("string_view16_chars", [](std::u16string_view s) {
|
||||
py::list l;
|
||||
for (auto c : s) {
|
||||
l.append((int) c);
|
||||
}
|
||||
return l;
|
||||
});
|
||||
m.def("string_view32_chars", [](std::u32string_view s) {
|
||||
py::list l;
|
||||
for (auto c : s) {
|
||||
l.append((int) c);
|
||||
}
|
||||
return l;
|
||||
});
|
||||
m.def("string_view_return",
|
||||
[]() { return std::string_view((const char *) u8"utf8 secret \U0001f382"); });
|
||||
m.def("string_view16_return",
|
||||
[]() { return std::u16string_view(u"utf16 secret \U0001f382"); });
|
||||
m.def("string_view32_return",
|
||||
[]() { return std::u32string_view(U"utf32 secret \U0001f382"); });
|
||||
|
||||
// The inner lambdas here are to also test implicit conversion
|
||||
using namespace std::literals;
|
||||
m.def("string_view_bytes",
|
||||
[]() { return [](py::bytes b) { return b; }("abc \x80\x80 def"sv); });
|
||||
m.def("string_view_str",
|
||||
[]() { return [](py::str s) { return s; }("abc \342\200\275 def"sv); });
|
||||
m.def("string_view_from_bytes",
|
||||
[](const py::bytes &b) { return [](std::string_view s) { return s; }(b); });
|
||||
m.def("string_view_memoryview", []() {
|
||||
static constexpr auto val = "Have some \360\237\216\202"sv;
|
||||
return py::memoryview::from_memory(val);
|
||||
});
|
||||
|
||||
# ifdef PYBIND11_HAS_U8STRING
|
||||
m.def("string_view8_print", [](std::u8string_view s) { py::print(s, s.size()); });
|
||||
m.def("string_view8_chars", [](std::u8string_view s) {
|
||||
py::list l;
|
||||
for (auto c : s)
|
||||
l.append((std::uint8_t) c);
|
||||
return l;
|
||||
});
|
||||
m.def("string_view8_return", []() { return std::u8string_view(u8"utf8 secret \U0001f382"); });
|
||||
m.def("string_view8_str", []() { return py::str{std::u8string_view{u8"abc ‽ def"}}; });
|
||||
# endif
|
||||
|
||||
struct TypeWithBothOperatorStringAndStringView {
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
operator std::string() const { return "success"; }
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
operator std::string_view() const { return "failure"; }
|
||||
};
|
||||
m.def("bytes_from_type_with_both_operator_string_and_string_view",
|
||||
[]() { return py::bytes(TypeWithBothOperatorStringAndStringView()); });
|
||||
m.def("str_from_type_with_both_operator_string_and_string_view",
|
||||
[]() { return py::str(TypeWithBothOperatorStringAndStringView()); });
|
||||
#endif
|
||||
|
||||
// test_integer_casting
|
||||
m.def("i32_str", [](std::int32_t v) { return std::to_string(v); });
|
||||
m.def("u32_str", [](std::uint32_t v) { return std::to_string(v); });
|
||||
m.def("i64_str", [](std::int64_t v) { return std::to_string(v); });
|
||||
m.def("u64_str", [](std::uint64_t v) { return std::to_string(v); });
|
||||
|
||||
// test_int_convert
|
||||
m.def("int_passthrough", [](int arg) { return arg; });
|
||||
m.def(
|
||||
"int_passthrough_noconvert", [](int arg) { return arg; }, py::arg{}.noconvert());
|
||||
|
||||
// test_tuple
|
||||
m.def(
|
||||
"pair_passthrough",
|
||||
[](const std::pair<bool, std::string> &input) {
|
||||
return std::make_pair(input.second, input.first);
|
||||
},
|
||||
"Return a pair in reversed order");
|
||||
m.def(
|
||||
"tuple_passthrough",
|
||||
[](std::tuple<bool, std::string, int> input) {
|
||||
return std::make_tuple(std::get<2>(input), std::get<1>(input), std::get<0>(input));
|
||||
},
|
||||
"Return a triple in reversed order");
|
||||
m.def("empty_tuple", []() { return std::tuple<>(); });
|
||||
static std::pair<RValueCaster, RValueCaster> lvpair;
|
||||
static std::tuple<RValueCaster, RValueCaster, RValueCaster> lvtuple;
|
||||
static std::pair<RValueCaster, std::tuple<RValueCaster, std::pair<RValueCaster, RValueCaster>>>
|
||||
lvnested;
|
||||
m.def("rvalue_pair", []() { return std::make_pair(RValueCaster{}, RValueCaster{}); });
|
||||
m.def("lvalue_pair", []() -> const decltype(lvpair) & { return lvpair; });
|
||||
m.def("rvalue_tuple",
|
||||
[]() { return std::make_tuple(RValueCaster{}, RValueCaster{}, RValueCaster{}); });
|
||||
m.def("lvalue_tuple", []() -> const decltype(lvtuple) & { return lvtuple; });
|
||||
m.def("rvalue_nested", []() {
|
||||
return std::make_pair(
|
||||
RValueCaster{},
|
||||
std::make_tuple(RValueCaster{}, std::make_pair(RValueCaster{}, RValueCaster{})));
|
||||
});
|
||||
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; }, py::return_value_policy::reference);
|
||||
|
||||
// test_builtins_cast_return_none
|
||||
m.def("return_none_string", []() -> std::string * { return nullptr; });
|
||||
m.def("return_none_char", []() -> const char * { return nullptr; });
|
||||
m.def("return_none_bool", []() -> bool * { return nullptr; });
|
||||
m.def("return_none_int", []() -> int * { return nullptr; });
|
||||
m.def("return_none_float", []() -> float * { return nullptr; });
|
||||
m.def("return_none_pair", []() -> std::pair<int, int> * { return nullptr; });
|
||||
|
||||
// test_none_deferred
|
||||
m.def("defer_none_cstring", [](char *) { return false; });
|
||||
m.def("defer_none_cstring", [](const py::none &) { return true; });
|
||||
m.def("defer_none_custom", [](UserType *) { return false; });
|
||||
m.def("defer_none_custom", [](const py::none &) { return true; });
|
||||
m.def("nodefer_none_void", [](void *) { return true; });
|
||||
m.def("nodefer_none_void", [](const py::none &) { return false; });
|
||||
|
||||
// test_void_caster
|
||||
m.def("load_nullptr_t", [](std::nullptr_t) {}); // not useful, but it should still compile
|
||||
m.def("cast_nullptr_t", []() { return std::nullptr_t{}; });
|
||||
|
||||
// [workaround(intel)] ICC 20/21 breaks with py::arg().stuff, using py::arg{}.stuff works.
|
||||
|
||||
// test_bool_caster
|
||||
m.def("bool_passthrough", [](bool arg) { return arg; });
|
||||
m.def(
|
||||
"bool_passthrough_noconvert", [](bool arg) { return arg; }, py::arg{}.noconvert());
|
||||
|
||||
// TODO: This should be disabled and fixed in future Intel compilers
|
||||
#if !defined(__INTEL_COMPILER)
|
||||
// Test "bool_passthrough_noconvert" again, but using () instead of {} to construct py::arg
|
||||
// When compiled with the Intel compiler, this results in segmentation faults when importing
|
||||
// the module. Tested with icc (ICC) 2021.1 Beta 20200827, this should be tested again when
|
||||
// a newer version of icc is available.
|
||||
m.def(
|
||||
"bool_passthrough_noconvert2", [](bool arg) { return arg; }, py::arg().noconvert());
|
||||
#endif
|
||||
|
||||
// test_reference_wrapper
|
||||
m.def("refwrap_builtin", [](std::reference_wrapper<int> p) { return 10 * p.get(); });
|
||||
m.def("refwrap_usertype", [](std::reference_wrapper<UserType> p) { return p.get().value(); });
|
||||
m.def("refwrap_usertype_const",
|
||||
[](std::reference_wrapper<const UserType> p) { return p.get().value(); });
|
||||
|
||||
m.def("refwrap_lvalue", []() -> std::reference_wrapper<UserType> {
|
||||
static UserType x(1);
|
||||
return std::ref(x);
|
||||
});
|
||||
m.def("refwrap_lvalue_const", []() -> std::reference_wrapper<const UserType> {
|
||||
static UserType x(1);
|
||||
return std::cref(x);
|
||||
});
|
||||
|
||||
// Not currently supported (std::pair caster has return-by-value cast operator);
|
||||
// triggers static_assert failure.
|
||||
// m.def("refwrap_pair", [](std::reference_wrapper<std::pair<int, int>>) { });
|
||||
|
||||
m.def(
|
||||
"refwrap_list",
|
||||
[](bool copy) {
|
||||
static IncType x1(1), x2(2);
|
||||
py::list l;
|
||||
for (const auto &f : {std::ref(x1), std::ref(x2)}) {
|
||||
l.append(py::cast(
|
||||
f, copy ? py::return_value_policy::copy : py::return_value_policy::reference));
|
||||
}
|
||||
return l;
|
||||
},
|
||||
"copy"_a);
|
||||
|
||||
m.def("refwrap_iiw", [](const IncType &w) { return w.value(); });
|
||||
m.def("refwrap_call_iiw", [](IncType &w, const py::function &f) {
|
||||
py::list l;
|
||||
l.append(f(std::ref(w)));
|
||||
l.append(f(std::cref(w)));
|
||||
IncType x(w.value());
|
||||
l.append(f(std::ref(x)));
|
||||
IncType y(w.value());
|
||||
auto r3 = std::ref(y);
|
||||
l.append(f(r3));
|
||||
return l;
|
||||
});
|
||||
|
||||
// test_complex
|
||||
m.def("complex_cast", [](float x) { return "{}"_s.format(x); });
|
||||
m.def("complex_cast",
|
||||
[](std::complex<float> x) { return "({}, {})"_s.format(x.real(), x.imag()); });
|
||||
|
||||
// test int vs. long (Python 2)
|
||||
m.def("int_cast", []() { return (int) 42; });
|
||||
m.def("long_cast", []() { return (long) 42; });
|
||||
m.def("longlong_cast", []() { return ULLONG_MAX; });
|
||||
|
||||
/// test void* cast operator
|
||||
m.def("test_void_caster", []() -> bool {
|
||||
void *v = (void *) 0xabcd;
|
||||
py::object o = py::cast(v);
|
||||
return py::cast<void *>(o) == v;
|
||||
});
|
||||
|
||||
// Tests const/non-const propagation in cast_op.
|
||||
m.def("takes", [](ConstRefCasted x) { return x.tag; });
|
||||
m.def("takes_move", [](ConstRefCasted &&x) { return x.tag; });
|
||||
m.def("takes_ptr", [](ConstRefCasted *x) { return x->tag; });
|
||||
m.def("takes_ref", [](ConstRefCasted &x) { return x.tag; });
|
||||
m.def("takes_ref_wrap", [](std::reference_wrapper<ConstRefCasted> x) { return x.get().tag; });
|
||||
m.def("takes_const_ptr", [](const ConstRefCasted *x) { return x->tag; });
|
||||
m.def("takes_const_ref", [](const ConstRefCasted &x) { return x.tag; });
|
||||
m.def("takes_const_ref_wrap",
|
||||
[](std::reference_wrapper<const ConstRefCasted> x) { return x.get().tag; });
|
||||
}
|
||||
512
third_party/pybind11/tests/test_builtin_casters.py
vendored
Normal file
512
third_party/pybind11/tests/test_builtin_casters.py
vendored
Normal file
@@ -0,0 +1,512 @@
|
||||
import sys
|
||||
|
||||
import pytest
|
||||
|
||||
import env
|
||||
from pybind11_tests import IncType, UserType
|
||||
from pybind11_tests import builtin_casters as m
|
||||
|
||||
|
||||
def test_simple_string():
|
||||
assert m.string_roundtrip("const char *") == "const char *"
|
||||
|
||||
|
||||
def test_unicode_conversion():
|
||||
"""Tests unicode conversion and error reporting."""
|
||||
assert m.good_utf8_string() == "Say utf8‽ 🎂 𝐀"
|
||||
assert m.good_utf16_string() == "b‽🎂𝐀z"
|
||||
assert m.good_utf32_string() == "a𝐀🎂‽z"
|
||||
assert m.good_wchar_string() == "a⸘𝐀z"
|
||||
if hasattr(m, "has_u8string"):
|
||||
assert m.good_utf8_u8string() == "Say utf8‽ 🎂 𝐀"
|
||||
|
||||
with pytest.raises(UnicodeDecodeError):
|
||||
m.bad_utf8_string()
|
||||
|
||||
with pytest.raises(UnicodeDecodeError):
|
||||
m.bad_utf16_string()
|
||||
|
||||
# These are provided only if they actually fail (they don't when 32-bit)
|
||||
if hasattr(m, "bad_utf32_string"):
|
||||
with pytest.raises(UnicodeDecodeError):
|
||||
m.bad_utf32_string()
|
||||
if hasattr(m, "bad_wchar_string"):
|
||||
with pytest.raises(UnicodeDecodeError):
|
||||
m.bad_wchar_string()
|
||||
if hasattr(m, "has_u8string"):
|
||||
with pytest.raises(UnicodeDecodeError):
|
||||
m.bad_utf8_u8string()
|
||||
|
||||
assert m.u8_Z() == "Z"
|
||||
assert m.u8_eacute() == "é"
|
||||
assert m.u16_ibang() == "‽"
|
||||
assert m.u32_mathbfA() == "𝐀"
|
||||
assert m.wchar_heart() == "♥"
|
||||
if hasattr(m, "has_u8string"):
|
||||
assert m.u8_char8_Z() == "Z"
|
||||
|
||||
|
||||
def test_single_char_arguments():
|
||||
"""Tests failures for passing invalid inputs to char-accepting functions"""
|
||||
|
||||
def toobig_message(r):
|
||||
return f"Character code point not in range({r:#x})"
|
||||
|
||||
toolong_message = "Expected a character, but multi-character string found"
|
||||
|
||||
assert m.ord_char("a") == 0x61 # simple ASCII
|
||||
assert m.ord_char_lv("b") == 0x62
|
||||
assert (m.ord_char("é") == 0xE9
|
||||
) # requires 2 bytes in utf-8, but can be stuffed in a char
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
assert m.ord_char(
|
||||
"Ā") == 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("ab")
|
||||
assert str(excinfo.value) == toolong_message
|
||||
|
||||
assert m.ord_char16("a") == 0x61
|
||||
assert m.ord_char16("é") == 0xE9
|
||||
assert m.ord_char16_lv("ê") == 0xEA
|
||||
assert m.ord_char16("Ā") == 0x100
|
||||
assert m.ord_char16("‽") == 0x203D
|
||||
assert m.ord_char16("♥") == 0x2665
|
||||
assert m.ord_char16_lv("♡") == 0x2661
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
assert m.ord_char16("🎂") == 0x1F382 # requires surrogate pair
|
||||
assert str(excinfo.value) == toobig_message(0x10000)
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
assert m.ord_char16("aa")
|
||||
assert str(excinfo.value) == toolong_message
|
||||
|
||||
assert m.ord_char32("a") == 0x61
|
||||
assert m.ord_char32("é") == 0xE9
|
||||
assert m.ord_char32("Ā") == 0x100
|
||||
assert m.ord_char32("‽") == 0x203D
|
||||
assert m.ord_char32("♥") == 0x2665
|
||||
assert m.ord_char32("🎂") == 0x1F382
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
assert m.ord_char32("aa")
|
||||
assert str(excinfo.value) == toolong_message
|
||||
|
||||
assert m.ord_wchar("a") == 0x61
|
||||
assert m.ord_wchar("é") == 0xE9
|
||||
assert m.ord_wchar("Ā") == 0x100
|
||||
assert m.ord_wchar("‽") == 0x203D
|
||||
assert m.ord_wchar("♥") == 0x2665
|
||||
if m.wchar_size == 2:
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
assert m.ord_wchar("🎂") == 0x1F382 # requires surrogate pair
|
||||
assert str(excinfo.value) == toobig_message(0x10000)
|
||||
else:
|
||||
assert m.ord_wchar("🎂") == 0x1F382
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
assert m.ord_wchar("aa")
|
||||
assert str(excinfo.value) == toolong_message
|
||||
|
||||
if hasattr(m, "has_u8string"):
|
||||
assert m.ord_char8("a") == 0x61 # simple ASCII
|
||||
assert m.ord_char8_lv("b") == 0x62
|
||||
assert (m.ord_char8("é") == 0xE9
|
||||
) # requires 2 bytes in utf-8, but can be stuffed in a char
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
assert m.ord_char8(
|
||||
"Ā") == 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("ab")
|
||||
assert str(excinfo.value) == toolong_message
|
||||
|
||||
|
||||
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
|
||||
|
||||
assert m.strlen(b"hi") == 2
|
||||
assert m.string_length(b"world") == 5
|
||||
assert m.string_length("a\x00b".encode()) == 3
|
||||
assert m.strlen("a\x00b".encode()) == 1 # C-string limitation
|
||||
|
||||
# passing in a utf8 encoded string should work
|
||||
assert m.string_length("💩".encode()) == 4
|
||||
|
||||
|
||||
def test_bytearray_to_string():
|
||||
"""Tests the ability to pass bytearray to C++ string-accepting functions"""
|
||||
assert m.string_length(bytearray(b"Hi")) == 2
|
||||
assert m.strlen(bytearray(b"bytearray")) == 9
|
||||
assert m.string_length(bytearray()) == 0
|
||||
assert m.string_length(bytearray("🦜", "utf-8", "strict")) == 4
|
||||
assert m.string_length(bytearray(b"\x80")) == 1
|
||||
|
||||
|
||||
@pytest.mark.skipif(
|
||||
not hasattr(m, "has_string_view"), reason="no <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]
|
||||
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_view_return() == "utf8 secret 🎂"
|
||||
assert m.string_view16_return() == "utf16 secret 🎂"
|
||||
assert m.string_view32_return() == "utf32 secret 🎂"
|
||||
if hasattr(m, "has_u8string"):
|
||||
assert m.string_view8_return() == "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 == """
|
||||
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 == """
|
||||
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 == """
|
||||
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 == """
|
||||
Hi, ascii 9
|
||||
Hi, utf8 🎂 13
|
||||
""")
|
||||
|
||||
assert m.string_view_bytes() == b"abc \x80\x80 def"
|
||||
assert m.string_view_str() == "abc ‽ def"
|
||||
assert m.string_view_from_bytes("abc ‽ def".encode()) == "abc ‽ def"
|
||||
if hasattr(m, "has_u8string"):
|
||||
assert m.string_view8_str() == "abc ‽ def"
|
||||
assert m.string_view_memoryview() == "Have some 🎂".encode()
|
||||
|
||||
assert m.bytes_from_type_with_both_operator_string_and_string_view(
|
||||
) == b"success"
|
||||
assert m.str_from_type_with_both_operator_string_and_string_view(
|
||||
) == "success"
|
||||
|
||||
|
||||
def test_integer_casting():
|
||||
"""Issue #929 - out-of-range integer values shouldn't be accepted"""
|
||||
assert m.i32_str(-1) == "-1"
|
||||
assert m.i64_str(-1) == "-1"
|
||||
assert m.i32_str(2000000000) == "2000000000"
|
||||
assert m.u32_str(2000000000) == "2000000000"
|
||||
assert m.i64_str(-999999999999) == "-999999999999"
|
||||
assert m.u64_str(999999999999) == "999999999999"
|
||||
|
||||
with pytest.raises(TypeError) as excinfo:
|
||||
m.u32_str(-1)
|
||||
assert "incompatible function arguments" in str(excinfo.value)
|
||||
with pytest.raises(TypeError) as excinfo:
|
||||
m.u64_str(-1)
|
||||
assert "incompatible function arguments" in str(excinfo.value)
|
||||
with pytest.raises(TypeError) as excinfo:
|
||||
m.i32_str(-3000000000)
|
||||
assert "incompatible function arguments" in str(excinfo.value)
|
||||
with pytest.raises(TypeError) as excinfo:
|
||||
m.i32_str(3000000000)
|
||||
assert "incompatible function arguments" in str(excinfo.value)
|
||||
|
||||
|
||||
def test_int_convert():
|
||||
class Int:
|
||||
def __int__(self):
|
||||
return 42
|
||||
|
||||
class NotInt:
|
||||
pass
|
||||
|
||||
class Float:
|
||||
def __float__(self):
|
||||
return 41.99999
|
||||
|
||||
class Index:
|
||||
def __index__(self):
|
||||
return 42
|
||||
|
||||
class IntAndIndex:
|
||||
def __int__(self):
|
||||
return 42
|
||||
|
||||
def __index__(self):
|
||||
return 0
|
||||
|
||||
class RaisingTypeErrorOnIndex:
|
||||
def __index__(self):
|
||||
raise TypeError
|
||||
|
||||
def __int__(self):
|
||||
return 42
|
||||
|
||||
class RaisingValueErrorOnIndex:
|
||||
def __index__(self):
|
||||
raise ValueError
|
||||
|
||||
def __int__(self):
|
||||
return 42
|
||||
|
||||
convert, noconvert = m.int_passthrough, m.int_passthrough_noconvert
|
||||
|
||||
def requires_conversion(v):
|
||||
pytest.raises(TypeError, noconvert, v)
|
||||
|
||||
def cant_convert(v):
|
||||
pytest.raises(TypeError, convert, v)
|
||||
|
||||
assert convert(7) == 7
|
||||
assert noconvert(7) == 7
|
||||
cant_convert(3.14159)
|
||||
# TODO: Avoid DeprecationWarning in `PyLong_AsLong` (and similar)
|
||||
# TODO: PyPy 3.8 does not behave like CPython 3.8 here yet (7.3.7)
|
||||
if (3, 8) <= sys.version_info < (3, 10) and env.CPYTHON:
|
||||
with env.deprecated_call():
|
||||
assert convert(Int()) == 42
|
||||
else:
|
||||
assert convert(Int()) == 42
|
||||
requires_conversion(Int())
|
||||
cant_convert(NotInt())
|
||||
cant_convert(Float())
|
||||
|
||||
# Before Python 3.8, `PyLong_AsLong` does not pick up on `obj.__index__`,
|
||||
# but pybind11 "backports" this behavior.
|
||||
assert convert(Index()) == 42
|
||||
assert noconvert(Index()) == 42
|
||||
assert convert(IntAndIndex()) == 0 # Fishy; `int(DoubleThought)` == 42
|
||||
assert noconvert(IntAndIndex()) == 0
|
||||
assert convert(RaisingTypeErrorOnIndex()) == 42
|
||||
requires_conversion(RaisingTypeErrorOnIndex())
|
||||
assert convert(RaisingValueErrorOnIndex()) == 42
|
||||
requires_conversion(RaisingValueErrorOnIndex())
|
||||
|
||||
|
||||
def test_numpy_int_convert():
|
||||
np = pytest.importorskip("numpy")
|
||||
|
||||
convert, noconvert = m.int_passthrough, m.int_passthrough_noconvert
|
||||
|
||||
def require_implicit(v):
|
||||
pytest.raises(TypeError, noconvert, v)
|
||||
|
||||
# `np.intc` is an alias that corresponds to a C++ `int`
|
||||
assert convert(np.intc(42)) == 42
|
||||
assert noconvert(np.intc(42)) == 42
|
||||
|
||||
# The implicit conversion from np.float32 is undesirable but currently accepted.
|
||||
# TODO: Avoid DeprecationWarning in `PyLong_AsLong` (and similar)
|
||||
# TODO: PyPy 3.8 does not behave like CPython 3.8 here yet (7.3.7)
|
||||
# https://github.com/pybind/pybind11/issues/3408
|
||||
if (3, 8) <= sys.version_info < (3, 10) and env.CPYTHON:
|
||||
with env.deprecated_call():
|
||||
assert convert(np.float32(3.14159)) == 3
|
||||
else:
|
||||
assert convert(np.float32(3.14159)) == 3
|
||||
require_implicit(np.float32(3.14159))
|
||||
|
||||
|
||||
def test_tuple(doc):
|
||||
"""std::pair <-> tuple & std::tuple <-> tuple"""
|
||||
assert m.pair_passthrough((True, "test")) == ("test", True)
|
||||
assert m.tuple_passthrough((True, "test", 5)) == (5, "test", True)
|
||||
# Any sequence can be cast to a std::pair or std::tuple
|
||||
assert m.pair_passthrough([True, "test"]) == ("test", True)
|
||||
assert m.tuple_passthrough([True, "test", 5]) == (5, "test", True)
|
||||
assert m.empty_tuple() == ()
|
||||
|
||||
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) == """
|
||||
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")
|
||||
assert m.rvalue_tuple() == ("rvalue", "rvalue", "rvalue")
|
||||
assert m.lvalue_tuple() == ("lvalue", "lvalue", "lvalue")
|
||||
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"""
|
||||
assert m.return_none_string() is None
|
||||
assert m.return_none_char() is 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():
|
||||
"""None passed as various argument types should defer to other overloads"""
|
||||
assert not m.defer_none_cstring("abc")
|
||||
assert m.defer_none_cstring(None)
|
||||
assert not m.defer_none_custom(UserType())
|
||||
assert m.defer_none_custom(None)
|
||||
assert m.nodefer_none_void(None)
|
||||
|
||||
|
||||
def test_void_caster():
|
||||
assert m.load_nullptr_t(None) is None
|
||||
assert m.cast_nullptr_t() is None
|
||||
|
||||
|
||||
def test_reference_wrapper():
|
||||
"""std::reference_wrapper for builtin and user types"""
|
||||
assert m.refwrap_builtin(42) == 420
|
||||
assert m.refwrap_usertype(UserType(42)) == 42
|
||||
assert m.refwrap_usertype_const(UserType(42)) == 42
|
||||
|
||||
with pytest.raises(TypeError) as excinfo:
|
||||
m.refwrap_builtin(None)
|
||||
assert "incompatible function arguments" in str(excinfo.value)
|
||||
|
||||
with pytest.raises(TypeError) as excinfo:
|
||||
m.refwrap_usertype(None)
|
||||
assert "incompatible function arguments" in str(excinfo.value)
|
||||
|
||||
assert m.refwrap_lvalue().value == 1
|
||||
assert m.refwrap_lvalue_const().value == 1
|
||||
|
||||
a1 = m.refwrap_list(copy=True)
|
||||
a2 = m.refwrap_list(copy=True)
|
||||
assert [x.value for x in a1] == [2, 3]
|
||||
assert [x.value for x in a2] == [2, 3]
|
||||
assert not a1[0] is a2[0] and not a1[1] is a2[1]
|
||||
|
||||
b1 = m.refwrap_list(copy=False)
|
||||
b2 = m.refwrap_list(copy=False)
|
||||
assert [x.value for x in b1] == [1, 2]
|
||||
assert [x.value for x in b2] == [1, 2]
|
||||
assert b1[0] is b2[0] and b1[1] is b2[1]
|
||||
|
||||
assert m.refwrap_iiw(IncType(5)) == 5
|
||||
assert m.refwrap_call_iiw(IncType(10), m.refwrap_iiw) == [10, 10, 10, 10]
|
||||
|
||||
|
||||
def test_complex_cast():
|
||||
"""std::complex casts"""
|
||||
assert m.complex_cast(1) == "1.0"
|
||||
assert m.complex_cast(2j) == "(0.0, 2.0)"
|
||||
|
||||
|
||||
def test_bool_caster():
|
||||
"""Test bool caster implicit conversions."""
|
||||
convert, noconvert = m.bool_passthrough, m.bool_passthrough_noconvert
|
||||
|
||||
def require_implicit(v):
|
||||
pytest.raises(TypeError, noconvert, v)
|
||||
|
||||
def cant_convert(v):
|
||||
pytest.raises(TypeError, convert, v)
|
||||
|
||||
# straight up bool
|
||||
assert convert(True) is True
|
||||
assert convert(False) is False
|
||||
assert noconvert(True) is True
|
||||
assert noconvert(False) is False
|
||||
|
||||
# None requires implicit conversion
|
||||
require_implicit(None)
|
||||
assert convert(None) is False
|
||||
|
||||
class A:
|
||||
def __init__(self, x):
|
||||
self.x = x
|
||||
|
||||
def __nonzero__(self):
|
||||
return self.x
|
||||
|
||||
def __bool__(self):
|
||||
return self.x
|
||||
|
||||
class B:
|
||||
pass
|
||||
|
||||
# Arbitrary objects are not accepted
|
||||
cant_convert(object())
|
||||
cant_convert(B())
|
||||
|
||||
# Objects with __nonzero__ / __bool__ defined can be converted
|
||||
require_implicit(A(True))
|
||||
assert convert(A(True)) is True
|
||||
assert convert(A(False)) is False
|
||||
|
||||
|
||||
def test_numpy_bool():
|
||||
np = pytest.importorskip("numpy")
|
||||
|
||||
convert, noconvert = m.bool_passthrough, m.bool_passthrough_noconvert
|
||||
|
||||
def cant_convert(v):
|
||||
pytest.raises(TypeError, convert, v)
|
||||
|
||||
# np.bool_ is not considered implicit
|
||||
assert convert(np.bool_(True)) is True
|
||||
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"))
|
||||
|
||||
|
||||
def test_int_long():
|
||||
assert isinstance(m.int_cast(), int)
|
||||
assert isinstance(m.long_cast(), int)
|
||||
assert isinstance(m.longlong_cast(), int)
|
||||
|
||||
|
||||
def test_void_caster_2():
|
||||
assert m.test_void_caster()
|
||||
|
||||
|
||||
def test_const_ref_caster():
|
||||
"""Verifies that const-ref is propagated through type_caster cast_op.
|
||||
The returned ConstRefCasted type is a minimal type that is constructed to
|
||||
reference the casting mode used.
|
||||
"""
|
||||
x = False
|
||||
assert m.takes(x) == 1
|
||||
assert m.takes_move(x) == 1
|
||||
|
||||
assert m.takes_ptr(x) == 3
|
||||
assert m.takes_ref(x) == 2
|
||||
assert m.takes_ref_wrap(x) == 2
|
||||
|
||||
assert m.takes_const_ptr(x) == 5
|
||||
assert m.takes_const_ref(x) == 4
|
||||
assert m.takes_const_ref_wrap(x) == 4
|
||||
115
third_party/pybind11/tests/test_call_policies.cpp
vendored
Normal file
115
third_party/pybind11/tests/test_call_policies.cpp
vendored
Normal file
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
tests/test_call_policies.cpp -- keep_alive and call_guard
|
||||
|
||||
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"
|
||||
|
||||
struct CustomGuard {
|
||||
static bool enabled;
|
||||
|
||||
CustomGuard() { enabled = true; }
|
||||
~CustomGuard() { enabled = false; }
|
||||
|
||||
static const char *report_status() { return enabled ? "guarded" : "unguarded"; }
|
||||
};
|
||||
bool CustomGuard::enabled = false;
|
||||
|
||||
struct DependentGuard {
|
||||
static bool enabled;
|
||||
|
||||
DependentGuard() { enabled = CustomGuard::enabled; }
|
||||
~DependentGuard() { enabled = false; }
|
||||
|
||||
static const char *report_status() { return enabled ? "guarded" : "unguarded"; }
|
||||
};
|
||||
bool DependentGuard::enabled = false;
|
||||
|
||||
TEST_SUBMODULE(call_policies, m) {
|
||||
// Parent/Child are used in:
|
||||
// test_keep_alive_argument, test_keep_alive_return_value, test_alive_gc_derived,
|
||||
// test_alive_gc_multi_derived, test_return_none, test_keep_alive_constructor
|
||||
class Child {
|
||||
public:
|
||||
Child() { py::print("Allocating child."); }
|
||||
Child(const Child &) = default;
|
||||
Child(Child &&) = default;
|
||||
~Child() { py::print("Releasing child."); }
|
||||
};
|
||||
py::class_<Child>(m, "Child").def(py::init<>());
|
||||
|
||||
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(); }
|
||||
Child *returnNullChild() { return nullptr; }
|
||||
static Child *staticFunction(Parent *) { return new Child(); }
|
||||
};
|
||||
py::class_<Parent>(m, "Parent")
|
||||
.def(py::init<>())
|
||||
.def(py::init([](Child *) { return new Parent(); }), py::keep_alive<1, 2>())
|
||||
.def("addChild", &Parent::addChild)
|
||||
.def("addChildKeepAlive", &Parent::addChild, py::keep_alive<1, 2>())
|
||||
.def("returnChild", &Parent::returnChild)
|
||||
.def("returnChildKeepAlive", &Parent::returnChild, py::keep_alive<1, 0>())
|
||||
.def("returnNullChildKeepAliveChild", &Parent::returnNullChild, py::keep_alive<1, 0>())
|
||||
.def("returnNullChildKeepAliveParent", &Parent::returnNullChild, py::keep_alive<0, 1>())
|
||||
.def_static("staticFunction", &Parent::staticFunction, py::keep_alive<1, 0>());
|
||||
|
||||
m.def(
|
||||
"free_function", [](Parent *, Child *) {}, py::keep_alive<1, 2>());
|
||||
m.def(
|
||||
"invalid_arg_index", [] {}, py::keep_alive<0, 1>());
|
||||
|
||||
#if !defined(PYPY_VERSION)
|
||||
// test_alive_gc
|
||||
class ParentGC : public Parent {
|
||||
public:
|
||||
using Parent::Parent;
|
||||
};
|
||||
py::class_<ParentGC, Parent>(m, "ParentGC", py::dynamic_attr()).def(py::init<>());
|
||||
#endif
|
||||
|
||||
// test_call_guard
|
||||
m.def("unguarded_call", &CustomGuard::report_status);
|
||||
m.def("guarded_call", &CustomGuard::report_status, py::call_guard<CustomGuard>());
|
||||
|
||||
m.def(
|
||||
"multiple_guards_correct_order",
|
||||
[]() {
|
||||
return CustomGuard::report_status() + std::string(" & ")
|
||||
+ DependentGuard::report_status();
|
||||
},
|
||||
py::call_guard<CustomGuard, DependentGuard>());
|
||||
|
||||
m.def(
|
||||
"multiple_guards_wrong_order",
|
||||
[]() {
|
||||
return DependentGuard::report_status() + std::string(" & ")
|
||||
+ CustomGuard::report_status();
|
||||
},
|
||||
py::call_guard<DependentGuard, CustomGuard>());
|
||||
|
||||
#if defined(WITH_THREAD) && !defined(PYPY_VERSION)
|
||||
// `py::call_guard<py::gil_scoped_release>()` should work in PyPy as well,
|
||||
// but it's unclear how to test it without `PyGILState_GetThisThreadState`.
|
||||
auto report_gil_status = []() {
|
||||
auto is_gil_held = false;
|
||||
if (auto *tstate = py::detail::get_thread_state_unchecked()) {
|
||||
is_gil_held = (tstate == PyGILState_GetThisThreadState());
|
||||
}
|
||||
|
||||
return is_gil_held ? "GIL held" : "GIL released";
|
||||
};
|
||||
|
||||
m.def("with_gil", report_gil_status);
|
||||
m.def("without_gil", report_gil_status, py::call_guard<py::gil_scoped_release>());
|
||||
#endif
|
||||
}
|
||||
218
third_party/pybind11/tests/test_call_policies.py
vendored
Normal file
218
third_party/pybind11/tests/test_call_policies.py
vendored
Normal file
@@ -0,0 +1,218 @@
|
||||
import pytest
|
||||
|
||||
import env # noqa: F401
|
||||
from pybind11_tests import ConstructorStats
|
||||
from pybind11_tests import call_policies as m
|
||||
|
||||
|
||||
@pytest.mark.xfail(
|
||||
"env.PYPY", reason="sometimes comes out 1 off on PyPy", strict=False)
|
||||
def test_keep_alive_argument(capture):
|
||||
n_inst = ConstructorStats.detail_reg_inst()
|
||||
with capture:
|
||||
p = m.Parent()
|
||||
assert capture == "Allocating parent."
|
||||
with capture:
|
||||
p.addChild(m.Child())
|
||||
assert ConstructorStats.detail_reg_inst() == n_inst + 1
|
||||
assert (capture == """
|
||||
Allocating child.
|
||||
Releasing child.
|
||||
""")
|
||||
with capture:
|
||||
del p
|
||||
assert ConstructorStats.detail_reg_inst() == n_inst
|
||||
assert capture == "Releasing parent."
|
||||
|
||||
with capture:
|
||||
p = m.Parent()
|
||||
assert capture == "Allocating parent."
|
||||
with capture:
|
||||
p.addChildKeepAlive(m.Child())
|
||||
assert ConstructorStats.detail_reg_inst() == n_inst + 2
|
||||
assert capture == "Allocating child."
|
||||
with capture:
|
||||
del p
|
||||
assert ConstructorStats.detail_reg_inst() == n_inst
|
||||
assert (capture == """
|
||||
Releasing parent.
|
||||
Releasing child.
|
||||
""")
|
||||
|
||||
p = m.Parent()
|
||||
c = m.Child()
|
||||
assert ConstructorStats.detail_reg_inst() == n_inst + 2
|
||||
m.free_function(p, c)
|
||||
del c
|
||||
assert ConstructorStats.detail_reg_inst() == n_inst + 2
|
||||
del p
|
||||
assert ConstructorStats.detail_reg_inst() == n_inst
|
||||
|
||||
with pytest.raises(RuntimeError) as excinfo:
|
||||
m.invalid_arg_index()
|
||||
assert str(excinfo.value) == "Could not activate keep_alive!"
|
||||
|
||||
|
||||
def test_keep_alive_return_value(capture):
|
||||
n_inst = ConstructorStats.detail_reg_inst()
|
||||
with capture:
|
||||
p = m.Parent()
|
||||
assert capture == "Allocating parent."
|
||||
with capture:
|
||||
p.returnChild()
|
||||
assert ConstructorStats.detail_reg_inst() == n_inst + 1
|
||||
assert (capture == """
|
||||
Allocating child.
|
||||
Releasing child.
|
||||
""")
|
||||
with capture:
|
||||
del p
|
||||
assert ConstructorStats.detail_reg_inst() == n_inst
|
||||
assert capture == "Releasing parent."
|
||||
|
||||
with capture:
|
||||
p = m.Parent()
|
||||
assert capture == "Allocating parent."
|
||||
with capture:
|
||||
p.returnChildKeepAlive()
|
||||
assert ConstructorStats.detail_reg_inst() == n_inst + 2
|
||||
assert capture == "Allocating child."
|
||||
with capture:
|
||||
del p
|
||||
assert ConstructorStats.detail_reg_inst() == n_inst
|
||||
assert (capture == """
|
||||
Releasing parent.
|
||||
Releasing child.
|
||||
""")
|
||||
|
||||
p = m.Parent()
|
||||
assert ConstructorStats.detail_reg_inst() == n_inst + 1
|
||||
with capture:
|
||||
m.Parent.staticFunction(p)
|
||||
assert ConstructorStats.detail_reg_inst() == n_inst + 2
|
||||
assert capture == "Allocating child."
|
||||
with capture:
|
||||
del p
|
||||
assert ConstructorStats.detail_reg_inst() == n_inst
|
||||
assert (capture == """
|
||||
Releasing parent.
|
||||
Releasing child.
|
||||
""")
|
||||
|
||||
|
||||
# https://foss.heptapod.net/pypy/pypy/-/issues/2447
|
||||
@pytest.mark.xfail("env.PYPY", reason="_PyObject_GetDictPtr is unimplemented")
|
||||
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
|
||||
with capture:
|
||||
del p, lst
|
||||
assert ConstructorStats.detail_reg_inst() == n_inst
|
||||
assert (capture == """
|
||||
Releasing parent.
|
||||
Releasing child.
|
||||
""")
|
||||
|
||||
|
||||
def test_alive_gc_derived(capture):
|
||||
class Derived(m.Parent):
|
||||
pass
|
||||
|
||||
n_inst = ConstructorStats.detail_reg_inst()
|
||||
p = Derived()
|
||||
p.addChildKeepAlive(m.Child())
|
||||
assert ConstructorStats.detail_reg_inst() == n_inst + 2
|
||||
lst = [p]
|
||||
lst.append(lst) # creates a circular reference
|
||||
with capture:
|
||||
del p, lst
|
||||
assert ConstructorStats.detail_reg_inst() == n_inst
|
||||
assert (capture == """
|
||||
Releasing parent.
|
||||
Releasing child.
|
||||
""")
|
||||
|
||||
|
||||
def test_alive_gc_multi_derived(capture):
|
||||
class Derived(m.Parent, m.Child):
|
||||
def __init__(self):
|
||||
m.Parent.__init__(self)
|
||||
m.Child.__init__(self)
|
||||
|
||||
n_inst = ConstructorStats.detail_reg_inst()
|
||||
p = Derived()
|
||||
p.addChildKeepAlive(m.Child())
|
||||
# +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
|
||||
with capture:
|
||||
del p, lst
|
||||
assert ConstructorStats.detail_reg_inst() == n_inst
|
||||
assert (capture == """
|
||||
Releasing parent.
|
||||
Releasing child.
|
||||
Releasing child.
|
||||
""")
|
||||
|
||||
|
||||
def test_return_none(capture):
|
||||
n_inst = ConstructorStats.detail_reg_inst()
|
||||
with capture:
|
||||
p = m.Parent()
|
||||
assert capture == "Allocating parent."
|
||||
with capture:
|
||||
p.returnNullChildKeepAliveChild()
|
||||
assert ConstructorStats.detail_reg_inst() == n_inst + 1
|
||||
assert capture == ""
|
||||
with capture:
|
||||
del p
|
||||
assert ConstructorStats.detail_reg_inst() == n_inst
|
||||
assert capture == "Releasing parent."
|
||||
|
||||
with capture:
|
||||
p = m.Parent()
|
||||
assert capture == "Allocating parent."
|
||||
with capture:
|
||||
p.returnNullChildKeepAliveParent()
|
||||
assert ConstructorStats.detail_reg_inst() == n_inst + 1
|
||||
assert capture == ""
|
||||
with capture:
|
||||
del p
|
||||
assert ConstructorStats.detail_reg_inst() == n_inst
|
||||
assert capture == "Releasing parent."
|
||||
|
||||
|
||||
def test_keep_alive_constructor(capture):
|
||||
n_inst = ConstructorStats.detail_reg_inst()
|
||||
|
||||
with capture:
|
||||
p = m.Parent(m.Child())
|
||||
assert ConstructorStats.detail_reg_inst() == n_inst + 2
|
||||
assert (capture == """
|
||||
Allocating child.
|
||||
Allocating parent.
|
||||
""")
|
||||
with capture:
|
||||
del p
|
||||
assert ConstructorStats.detail_reg_inst() == n_inst
|
||||
assert (capture == """
|
||||
Releasing parent.
|
||||
Releasing child.
|
||||
""")
|
||||
|
||||
|
||||
def test_call_guard():
|
||||
assert m.unguarded_call() == "unguarded"
|
||||
assert m.guarded_call() == "guarded"
|
||||
|
||||
assert m.multiple_guards_correct_order() == "guarded & guarded"
|
||||
assert m.multiple_guards_wrong_order() == "unguarded & guarded"
|
||||
|
||||
if hasattr(m, "with_gil"):
|
||||
assert m.with_gil() == "GIL held"
|
||||
assert m.without_gil() == "GIL released"
|
||||
243
third_party/pybind11/tests/test_callbacks.cpp
vendored
Normal file
243
third_party/pybind11/tests/test_callbacks.cpp
vendored
Normal file
@@ -0,0 +1,243 @@
|
||||
/*
|
||||
tests/test_callbacks.cpp -- callbacks
|
||||
|
||||
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/functional.h>
|
||||
|
||||
#include "constructor_stats.h"
|
||||
#include "pybind11_tests.h"
|
||||
|
||||
#include <thread>
|
||||
|
||||
int dummy_function(int i) { return i + 1; }
|
||||
|
||||
TEST_SUBMODULE(callbacks, m) {
|
||||
// test_callbacks, test_function_signatures
|
||||
m.def("test_callback1", [](const py::object &func) { return func(); });
|
||||
m.def("test_callback2", [](const py::object &func) { return func("Hello", 'x', true, 5); });
|
||||
m.def("test_callback3", [](const std::function<int(int)> &func) {
|
||||
return "func(43) = " + std::to_string(func(43));
|
||||
});
|
||||
m.def("test_callback4",
|
||||
[]() -> std::function<int(int)> { return [](int i) { return i + 1; }; });
|
||||
m.def("test_callback5",
|
||||
[]() { return py::cpp_function([](int i) { return i + 1; }, py::arg("number")); });
|
||||
|
||||
// test_keyword_args_and_generalized_unpacking
|
||||
m.def("test_tuple_unpacking", [](const py::function &f) {
|
||||
auto t1 = py::make_tuple(2, 3);
|
||||
auto t2 = py::make_tuple(5, 6);
|
||||
return f("positional", 1, *t1, 4, *t2);
|
||||
});
|
||||
|
||||
m.def("test_dict_unpacking", [](const py::function &f) {
|
||||
auto d1 = py::dict("key"_a = "value", "a"_a = 1);
|
||||
auto d2 = py::dict();
|
||||
auto d3 = py::dict("b"_a = 2);
|
||||
return f("positional", 1, **d1, **d2, **d3);
|
||||
});
|
||||
|
||||
m.def("test_keyword_args", [](const py::function &f) { return f("x"_a = 10, "y"_a = 20); });
|
||||
|
||||
m.def("test_unpacking_and_keywords1", [](const py::function &f) {
|
||||
auto args = py::make_tuple(2);
|
||||
auto kwargs = py::dict("d"_a = 4);
|
||||
return f(1, *args, "c"_a = 3, **kwargs);
|
||||
});
|
||||
|
||||
m.def("test_unpacking_and_keywords2", [](const py::function &f) {
|
||||
auto kwargs1 = py::dict("a"_a = 1);
|
||||
auto kwargs2 = py::dict("c"_a = 3, "d"_a = 4);
|
||||
return f("positional",
|
||||
*py::make_tuple(1),
|
||||
2,
|
||||
*py::make_tuple(3, 4),
|
||||
5,
|
||||
"key"_a = "value",
|
||||
**kwargs1,
|
||||
"b"_a = 2,
|
||||
**kwargs2,
|
||||
"e"_a = 5);
|
||||
});
|
||||
|
||||
m.def("test_unpacking_error1", [](const py::function &f) {
|
||||
auto kwargs = py::dict("x"_a = 3);
|
||||
return f("x"_a = 1, "y"_a = 2, **kwargs); // duplicate ** after keyword
|
||||
});
|
||||
|
||||
m.def("test_unpacking_error2", [](const py::function &f) {
|
||||
auto kwargs = py::dict("x"_a = 3);
|
||||
return f(**kwargs, "x"_a = 1); // duplicate keyword after **
|
||||
});
|
||||
|
||||
m.def("test_arg_conversion_error1",
|
||||
[](const py::function &f) { f(234, UnregisteredType(), "kw"_a = 567); });
|
||||
|
||||
m.def("test_arg_conversion_error2", [](const py::function &f) {
|
||||
f(234, "expected_name"_a = UnregisteredType(), "kw"_a = 567);
|
||||
});
|
||||
|
||||
// test_lambda_closure_cleanup
|
||||
struct Payload {
|
||||
Payload() { print_default_created(this); }
|
||||
~Payload() { print_destroyed(this); }
|
||||
Payload(const Payload &) { print_copy_created(this); }
|
||||
Payload(Payload &&) noexcept { print_move_created(this); }
|
||||
};
|
||||
// Export the payload constructor statistics for testing purposes:
|
||||
m.def("payload_cstats", &ConstructorStats::get<Payload>);
|
||||
m.def("test_lambda_closure_cleanup", []() -> std::function<void()> {
|
||||
Payload p;
|
||||
|
||||
// In this situation, `Func` in the implementation of
|
||||
// `cpp_function::initialize` is NOT trivially destructible.
|
||||
return [p]() {
|
||||
/* p should be cleaned up when the returned function is garbage collected */
|
||||
(void) p;
|
||||
};
|
||||
});
|
||||
|
||||
class CppCallable {
|
||||
public:
|
||||
CppCallable() { track_default_created(this); }
|
||||
~CppCallable() { track_destroyed(this); }
|
||||
CppCallable(const CppCallable &) { track_copy_created(this); }
|
||||
CppCallable(CppCallable &&) noexcept { track_move_created(this); }
|
||||
void operator()() {}
|
||||
};
|
||||
|
||||
m.def("test_cpp_callable_cleanup", []() {
|
||||
// Related issue: https://github.com/pybind/pybind11/issues/3228
|
||||
// Related PR: https://github.com/pybind/pybind11/pull/3229
|
||||
py::list alive_counts;
|
||||
ConstructorStats &stat = ConstructorStats::get<CppCallable>();
|
||||
alive_counts.append(stat.alive());
|
||||
{
|
||||
CppCallable cpp_callable;
|
||||
alive_counts.append(stat.alive());
|
||||
{
|
||||
// In this situation, `Func` in the implementation of
|
||||
// `cpp_function::initialize` IS trivially destructible,
|
||||
// only `capture` is not.
|
||||
py::cpp_function py_func(cpp_callable);
|
||||
py::detail::silence_unused_warnings(py_func);
|
||||
alive_counts.append(stat.alive());
|
||||
}
|
||||
alive_counts.append(stat.alive());
|
||||
{
|
||||
py::cpp_function py_func(std::move(cpp_callable));
|
||||
py::detail::silence_unused_warnings(py_func);
|
||||
alive_counts.append(stat.alive());
|
||||
}
|
||||
alive_counts.append(stat.alive());
|
||||
}
|
||||
alive_counts.append(stat.alive());
|
||||
return alive_counts;
|
||||
});
|
||||
|
||||
// test_cpp_function_roundtrip
|
||||
/* Test if passing a function pointer from C++ -> Python -> C++ yields the original pointer */
|
||||
m.def("dummy_function", &dummy_function);
|
||||
m.def("dummy_function_overloaded", [](int i, int j) { return i + j; });
|
||||
m.def("dummy_function_overloaded", &dummy_function);
|
||||
m.def("dummy_function2", [](int i, int j) { return i + j; });
|
||||
m.def(
|
||||
"roundtrip",
|
||||
[](std::function<int(int)> f, bool expect_none = false) {
|
||||
if (expect_none && f) {
|
||||
throw std::runtime_error("Expected None to be converted to empty std::function");
|
||||
}
|
||||
return f;
|
||||
},
|
||||
py::arg("f"),
|
||||
py::arg("expect_none") = false);
|
||||
m.def("test_dummy_function", [](const std::function<int(int)> &f) -> std::string {
|
||||
using fn_type = int (*)(int);
|
||||
const auto *result = f.target<fn_type>();
|
||||
if (!result) {
|
||||
auto r = f(1);
|
||||
return "can't convert to function pointer: eval(1) = " + std::to_string(r);
|
||||
}
|
||||
if (*result == dummy_function) {
|
||||
auto r = (*result)(1);
|
||||
return "matches dummy_function: eval(1) = " + std::to_string(r);
|
||||
}
|
||||
return "argument does NOT match dummy_function. This should never happen!";
|
||||
});
|
||||
|
||||
class AbstractBase {
|
||||
public:
|
||||
// [workaround(intel)] = default does not work here
|
||||
// Defaulting this destructor results in linking errors with the Intel compiler
|
||||
// (in Debug builds only, tested with icpc (ICC) 2021.1 Beta 20200827)
|
||||
virtual ~AbstractBase() {} // NOLINT(modernize-use-equals-default)
|
||||
virtual unsigned int func() = 0;
|
||||
};
|
||||
m.def("func_accepting_func_accepting_base",
|
||||
[](const std::function<double(AbstractBase &)> &) {});
|
||||
|
||||
struct MovableObject {
|
||||
bool valid = true;
|
||||
|
||||
MovableObject() = default;
|
||||
MovableObject(const MovableObject &) = default;
|
||||
MovableObject &operator=(const MovableObject &) = default;
|
||||
MovableObject(MovableObject &&o) noexcept : valid(o.valid) { o.valid = false; }
|
||||
MovableObject &operator=(MovableObject &&o) noexcept {
|
||||
valid = o.valid;
|
||||
o.valid = false;
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
py::class_<MovableObject>(m, "MovableObject");
|
||||
|
||||
// test_movable_object
|
||||
m.def("callback_with_movable", [](const std::function<void(MovableObject &)> &f) {
|
||||
auto x = MovableObject();
|
||||
f(x); // lvalue reference shouldn't move out object
|
||||
return x.valid; // must still return `true`
|
||||
});
|
||||
|
||||
// test_bound_method_callback
|
||||
struct CppBoundMethodTest {};
|
||||
py::class_<CppBoundMethodTest>(m, "CppBoundMethodTest")
|
||||
.def(py::init<>())
|
||||
.def("triple", [](CppBoundMethodTest &, int val) { return 3 * val; });
|
||||
|
||||
// This checks that builtin functions can be passed as callbacks
|
||||
// rather than throwing RuntimeError due to trying to extract as capsule
|
||||
m.def("test_sum_builtin",
|
||||
[](const std::function<double(py::iterable)> &sum_builtin, const py::iterable &i) {
|
||||
return sum_builtin(i);
|
||||
});
|
||||
|
||||
// test async Python callbacks
|
||||
using callback_f = std::function<void(int)>;
|
||||
m.def("test_async_callback", [](const callback_f &f, const py::list &work) {
|
||||
// make detached thread that calls `f` with piece of work after a little delay
|
||||
auto start_f = [f](int j) {
|
||||
auto invoke_f = [f, j] {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
||||
f(j);
|
||||
};
|
||||
auto t = std::thread(std::move(invoke_f));
|
||||
t.detach();
|
||||
};
|
||||
|
||||
// spawn worker threads
|
||||
for (auto i : work) {
|
||||
start_f(py::cast<int>(i));
|
||||
}
|
||||
});
|
||||
|
||||
m.def("callback_num_times", [](const py::function &f, std::size_t num) {
|
||||
for (std::size_t i = 0; i < num; i++) {
|
||||
f();
|
||||
}
|
||||
});
|
||||
}
|
||||
200
third_party/pybind11/tests/test_callbacks.py
vendored
Normal file
200
third_party/pybind11/tests/test_callbacks.py
vendored
Normal file
@@ -0,0 +1,200 @@
|
||||
import time
|
||||
from threading import Thread
|
||||
|
||||
import pytest
|
||||
|
||||
import env # noqa: F401
|
||||
from pybind11_tests import callbacks as m
|
||||
|
||||
|
||||
def test_callbacks():
|
||||
from functools import partial
|
||||
|
||||
def func1():
|
||||
return "func1"
|
||||
|
||||
def func2(a, b, c, d):
|
||||
return "func2", a, b, c, d
|
||||
|
||||
def func3(a):
|
||||
return f"func3({a})"
|
||||
|
||||
assert m.test_callback1(func1) == "func1"
|
||||
assert m.test_callback2(func2) == ("func2", "Hello", "x", True, 5)
|
||||
assert m.test_callback1(partial(func2, 1, 2, 3, 4)) == ("func2", 1, 2, 3, 4
|
||||
)
|
||||
assert m.test_callback1(partial(func3, "partial")) == "func3(partial)"
|
||||
assert m.test_callback3(lambda i: i + 1) == "func(43) = 44"
|
||||
|
||||
f = m.test_callback4()
|
||||
assert f(43) == 44
|
||||
f = m.test_callback5()
|
||||
assert f(number=43) == 44
|
||||
|
||||
|
||||
def test_bound_method_callback():
|
||||
# Bound Python method:
|
||||
class MyClass:
|
||||
def double(self, val):
|
||||
return 2 * val
|
||||
|
||||
z = MyClass()
|
||||
assert m.test_callback3(z.double) == "func(43) = 86"
|
||||
|
||||
z = m.CppBoundMethodTest()
|
||||
assert m.test_callback3(z.triple) == "func(43) = 129"
|
||||
|
||||
|
||||
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_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
|
||||
}, )
|
||||
|
||||
with pytest.raises(TypeError) as excinfo:
|
||||
m.test_unpacking_error1(f)
|
||||
assert "Got multiple values for keyword argument" in str(excinfo.value)
|
||||
|
||||
with pytest.raises(TypeError) as excinfo:
|
||||
m.test_unpacking_error2(f)
|
||||
assert "Got multiple values for keyword argument" in str(excinfo.value)
|
||||
|
||||
with pytest.raises(RuntimeError) as excinfo:
|
||||
m.test_arg_conversion_error1(f)
|
||||
assert "Unable to convert call argument" in str(excinfo.value)
|
||||
|
||||
with pytest.raises(RuntimeError) as excinfo:
|
||||
m.test_arg_conversion_error2(f)
|
||||
assert "Unable to convert call argument" in str(excinfo.value)
|
||||
|
||||
|
||||
def test_lambda_closure_cleanup():
|
||||
m.test_lambda_closure_cleanup()
|
||||
cstats = m.payload_cstats()
|
||||
assert cstats.alive() == 0
|
||||
assert cstats.copy_constructions == 1
|
||||
assert cstats.move_constructions >= 1
|
||||
|
||||
|
||||
def test_cpp_callable_cleanup():
|
||||
alive_counts = m.test_cpp_callable_cleanup()
|
||||
assert alive_counts == [0, 1, 2, 1, 2, 1, 0]
|
||||
|
||||
|
||||
def test_cpp_function_roundtrip():
|
||||
"""Test if passing a function pointer from C++ -> Python -> C++ yields the original pointer"""
|
||||
|
||||
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_overloaded) ==
|
||||
"matches dummy_function: eval(1) = 2")
|
||||
assert m.roundtrip(None, expect_none=True) is None
|
||||
assert (m.test_dummy_function(lambda x: x + 2) ==
|
||||
"can't convert to function pointer: eval(1) = 3")
|
||||
|
||||
with pytest.raises(TypeError) as excinfo:
|
||||
m.test_dummy_function(m.dummy_function2)
|
||||
assert "incompatible function arguments" in str(excinfo.value)
|
||||
|
||||
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"))
|
||||
|
||||
|
||||
def test_function_signatures(doc):
|
||||
assert doc(m.test_callback3
|
||||
) == "test_callback3(arg0: Callable[[int], int]) -> str"
|
||||
assert doc(m.test_callback4) == "test_callback4() -> Callable[[int], int]"
|
||||
|
||||
|
||||
def test_movable_object():
|
||||
assert m.callback_with_movable(lambda _: None) is True
|
||||
|
||||
|
||||
@pytest.mark.skipif(
|
||||
"env.PYPY",
|
||||
reason="PyPy segfaults on here. See discussion on #1413.", )
|
||||
def test_python_builtins():
|
||||
"""Test if python builtins like sum() can be used as callbacks"""
|
||||
assert m.test_sum_builtin(sum, [1, 2, 3]) == 6
|
||||
assert m.test_sum_builtin(sum, []) == 0
|
||||
|
||||
|
||||
def test_async_callbacks():
|
||||
# serves as state for async callback
|
||||
class Item:
|
||||
def __init__(self, value):
|
||||
self.value = value
|
||||
|
||||
res = []
|
||||
|
||||
# generate stateful lambda that will store result in `res`
|
||||
def gen_f():
|
||||
s = Item(3)
|
||||
return lambda j: res.append(s.value + j)
|
||||
|
||||
# do some work async
|
||||
work = [1, 2, 3, 4]
|
||||
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)
|
||||
|
||||
|
||||
def test_async_async_callbacks():
|
||||
t = Thread(target=test_async_callbacks)
|
||||
t.start()
|
||||
t.join()
|
||||
|
||||
|
||||
def test_callback_num_times():
|
||||
# Super-simple micro-benchmarking related to PR #2919.
|
||||
# Example runtimes (Intel Xeon 2.2GHz, fully optimized):
|
||||
# num_millions 1, repeats 2: 0.1 secs
|
||||
# num_millions 20, repeats 10: 11.5 secs
|
||||
one_million = 1000000
|
||||
num_millions = 1 # Try 20 for actual micro-benchmarking.
|
||||
repeats = 2 # Try 10.
|
||||
rates = []
|
||||
for rep in range(repeats):
|
||||
t0 = time.time()
|
||||
m.callback_num_times(lambda: None, num_millions * one_million)
|
||||
td = time.time() - t0
|
||||
rate = num_millions / td if td else 0
|
||||
rates.append(rate)
|
||||
if not rep:
|
||||
print()
|
||||
print(
|
||||
f"callback_num_times: {num_millions:d} million / {td:.3f} seconds = {rate:.3f} million / second"
|
||||
)
|
||||
if len(rates) > 1:
|
||||
print("Min Mean Max")
|
||||
print(
|
||||
f"{min(rates):6.3f} {sum(rates) / len(rates):6.3f} {max(rates):6.3f}"
|
||||
)
|
||||
81
third_party/pybind11/tests/test_chrono.cpp
vendored
Normal file
81
third_party/pybind11/tests/test_chrono.cpp
vendored
Normal file
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
tests/test_chrono.cpp -- test conversions to/from std::chrono types
|
||||
|
||||
Copyright (c) 2016 Trent Houliston <trent@houliston.me> and
|
||||
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/chrono.h>
|
||||
|
||||
#include "pybind11_tests.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;
|
||||
using steady_time = std::chrono::steady_clock::time_point;
|
||||
|
||||
using timespan = std::chrono::duration<int64_t, std::nano>;
|
||||
using timestamp = std::chrono::time_point<std::chrono::system_clock, timespan>;
|
||||
|
||||
// test_chrono_system_clock
|
||||
// Return the current time off the wall clock
|
||||
m.def("test_chrono1", []() { return std::chrono::system_clock::now(); });
|
||||
|
||||
// test_chrono_system_clock_roundtrip
|
||||
// Round trip the passed in system clock time
|
||||
m.def("test_chrono2", [](system_time t) { return t; });
|
||||
|
||||
// test_chrono_duration_roundtrip
|
||||
// Round trip the passed in duration
|
||||
m.def("test_chrono3", [](std::chrono::system_clock::duration d) { return d; });
|
||||
|
||||
// test_chrono_duration_subtraction_equivalence
|
||||
// Difference between two passed in time_points
|
||||
m.def("test_chrono4", [](system_time a, system_time b) { return a - b; });
|
||||
|
||||
// test_chrono_steady_clock
|
||||
// Return the current time off the steady_clock
|
||||
m.def("test_chrono5", []() { return std::chrono::steady_clock::now(); });
|
||||
|
||||
// test_chrono_steady_clock_roundtrip
|
||||
// Round trip a steady clock timepoint
|
||||
m.def("test_chrono6", [](steady_time t) { return t; });
|
||||
|
||||
// test_floating_point_duration
|
||||
// Roundtrip a duration in microseconds from a float argument
|
||||
m.def("test_chrono7", [](std::chrono::microseconds t) { return t; });
|
||||
// Float durations (issue #719)
|
||||
m.def("test_chrono_float_diff",
|
||||
[](std::chrono::duration<float> a, std::chrono::duration<float> b) { return a - b; });
|
||||
|
||||
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);
|
||||
}
|
||||
209
third_party/pybind11/tests/test_chrono.py
vendored
Normal file
209
third_party/pybind11/tests/test_chrono.py
vendored
Normal file
@@ -0,0 +1,209 @@
|
||||
import datetime
|
||||
|
||||
import pytest
|
||||
|
||||
import env # noqa: F401
|
||||
from pybind11_tests import chrono as m
|
||||
|
||||
|
||||
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()
|
||||
|
||||
# The returned value should be a datetime
|
||||
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 difference
|
||||
assert diff.days == 0
|
||||
|
||||
# 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():
|
||||
date1 = datetime.datetime.today()
|
||||
|
||||
# Roundtrip the time
|
||||
date2 = m.test_chrono2(date1)
|
||||
|
||||
# The returned value should be a datetime
|
||||
assert isinstance(date2, datetime.datetime)
|
||||
|
||||
# They should be identical (no information lost on roundtrip)
|
||||
diff = abs(date1 - date2)
|
||||
assert diff == datetime.timedelta(0)
|
||||
|
||||
|
||||
def test_chrono_system_clock_roundtrip_date():
|
||||
date1 = datetime.date.today()
|
||||
|
||||
# Roundtrip the time
|
||||
datetime2 = m.test_chrono2(date1)
|
||||
date2 = datetime2.date()
|
||||
time2 = datetime2.time()
|
||||
|
||||
# The returned value should be a datetime
|
||||
assert isinstance(datetime2, datetime.datetime)
|
||||
assert isinstance(date2, datetime.date)
|
||||
assert isinstance(time2, datetime.time)
|
||||
|
||||
# They should be identical (no information lost on roundtrip)
|
||||
diff = abs(date1 - date2)
|
||||
assert diff.days == 0
|
||||
assert diff.seconds == 0
|
||||
assert diff.microseconds == 0
|
||||
|
||||
# Year, Month & Day should be the same after the round trip
|
||||
assert date1 == date2
|
||||
|
||||
# There should be no time information
|
||||
assert time2.hour == 0
|
||||
assert time2.minute == 0
|
||||
assert time2.second == 0
|
||||
assert time2.microsecond == 0
|
||||
|
||||
|
||||
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", f"/usr/share/zoneinfo/{tz}")
|
||||
|
||||
# Roundtrip the time
|
||||
datetime2 = m.test_chrono2(time1)
|
||||
date2 = datetime2.date()
|
||||
time2 = datetime2.time()
|
||||
|
||||
# The returned value should be a datetime
|
||||
assert isinstance(datetime2, datetime.datetime)
|
||||
assert isinstance(date2, datetime.date)
|
||||
assert isinstance(time2, datetime.time)
|
||||
|
||||
# Hour, Minute, Second & Microsecond should be the same after the round trip
|
||||
assert time1 == time2
|
||||
|
||||
# There should be no date information (i.e. date = python base date)
|
||||
assert date2.year == 1970
|
||||
assert date2.month == 1
|
||||
assert date2.day == 1
|
||||
|
||||
|
||||
def test_chrono_duration_roundtrip():
|
||||
|
||||
# Get the difference between two times (a timedelta)
|
||||
date1 = datetime.datetime.today()
|
||||
date2 = datetime.datetime.today()
|
||||
diff = date2 - date1
|
||||
|
||||
# Make sure this is a timedelta
|
||||
assert isinstance(diff, datetime.timedelta)
|
||||
|
||||
cpp_diff = m.test_chrono3(diff)
|
||||
|
||||
assert cpp_diff == diff
|
||||
|
||||
# Negative timedelta roundtrip
|
||||
diff = datetime.timedelta(microseconds=-1)
|
||||
cpp_diff = m.test_chrono3(diff)
|
||||
|
||||
assert cpp_diff == diff
|
||||
|
||||
|
||||
def test_chrono_duration_subtraction_equivalence():
|
||||
|
||||
date1 = datetime.datetime.today()
|
||||
date2 = datetime.datetime.today()
|
||||
|
||||
diff = date2 - date1
|
||||
cpp_diff = m.test_chrono4(date2, date1)
|
||||
|
||||
assert cpp_diff == diff
|
||||
|
||||
|
||||
def test_chrono_duration_subtraction_equivalence_date():
|
||||
|
||||
date1 = datetime.date.today()
|
||||
date2 = datetime.date.today()
|
||||
|
||||
diff = date2 - date1
|
||||
cpp_diff = m.test_chrono4(date2, date1)
|
||||
|
||||
assert cpp_diff == diff
|
||||
|
||||
|
||||
def test_chrono_steady_clock():
|
||||
time1 = m.test_chrono5()
|
||||
assert isinstance(time1, datetime.timedelta)
|
||||
|
||||
|
||||
def test_chrono_steady_clock_roundtrip():
|
||||
time1 = datetime.timedelta(days=10, seconds=10, microseconds=100)
|
||||
time2 = m.test_chrono6(time1)
|
||||
|
||||
assert isinstance(time2, datetime.timedelta)
|
||||
|
||||
# They should be identical (no information lost on roundtrip)
|
||||
assert time1 == time2
|
||||
|
||||
|
||||
def test_floating_point_duration():
|
||||
# Test using a floating point number in seconds
|
||||
time = m.test_chrono7(35.525123)
|
||||
|
||||
assert isinstance(time, datetime.timedelta)
|
||||
|
||||
assert time.seconds == 35
|
||||
assert 525122 <= time.microseconds <= 525123
|
||||
|
||||
diff = m.test_chrono_float_diff(43.789012, 1.123456)
|
||||
assert diff.seconds == 42
|
||||
assert 665556 <= diff.microseconds <= 665557
|
||||
|
||||
|
||||
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)
|
||||
|
||||
|
||||
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
|
||||
619
third_party/pybind11/tests/test_class.cpp
vendored
Normal file
619
third_party/pybind11/tests/test_class.cpp
vendored
Normal file
@@ -0,0 +1,619 @@
|
||||
/*
|
||||
tests/test_class.cpp -- test py::class_ definitions and basic functionality
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
#if defined(__INTEL_COMPILER) && __cplusplus >= 201703L
|
||||
// Intel compiler requires a separate header file to support aligned new operators
|
||||
// and does not set the __cpp_aligned_new feature macro.
|
||||
// This header needs to be included before pybind11.
|
||||
# include <aligned_new>
|
||||
#endif
|
||||
|
||||
#include <pybind11/stl.h>
|
||||
|
||||
#include "constructor_stats.h"
|
||||
#include "local_bindings.h"
|
||||
#include "pybind11_tests.h"
|
||||
|
||||
#include <utility>
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
# pragma warning(disable : 4324)
|
||||
// warning C4324: structure was padded due to alignment specifier
|
||||
#endif
|
||||
|
||||
// test_brace_initialization
|
||||
struct NoBraceInitialization {
|
||||
explicit NoBraceInitialization(std::vector<int> v) : vec{std::move(v)} {}
|
||||
template <typename T>
|
||||
NoBraceInitialization(std::initializer_list<T> l) : vec(l) {}
|
||||
|
||||
std::vector<int> vec;
|
||||
};
|
||||
|
||||
TEST_SUBMODULE(class_, m) {
|
||||
// test_instance
|
||||
struct NoConstructor {
|
||||
NoConstructor() = default;
|
||||
NoConstructor(const NoConstructor &) = default;
|
||||
NoConstructor(NoConstructor &&) = default;
|
||||
static NoConstructor *new_instance() {
|
||||
auto *ptr = new NoConstructor();
|
||||
print_created(ptr, "via new_instance");
|
||||
return ptr;
|
||||
}
|
||||
~NoConstructor() { print_destroyed(this); }
|
||||
};
|
||||
struct NoConstructorNew {
|
||||
NoConstructorNew() = default;
|
||||
NoConstructorNew(const NoConstructorNew &) = default;
|
||||
NoConstructorNew(NoConstructorNew &&) = default;
|
||||
static NoConstructorNew *new_instance() {
|
||||
auto *ptr = new NoConstructorNew();
|
||||
print_created(ptr, "via new_instance");
|
||||
return ptr;
|
||||
}
|
||||
~NoConstructorNew() { print_destroyed(this); }
|
||||
};
|
||||
|
||||
py::class_<NoConstructor>(m, "NoConstructor")
|
||||
.def_static("new_instance", &NoConstructor::new_instance, "Return an instance");
|
||||
|
||||
py::class_<NoConstructorNew>(m, "NoConstructorNew")
|
||||
.def(py::init([](const NoConstructorNew &self) { return self; })) // Need a NOOP __init__
|
||||
.def_static("__new__",
|
||||
[](const py::object &) { return NoConstructorNew::new_instance(); });
|
||||
|
||||
// test_inheritance
|
||||
class Pet {
|
||||
public:
|
||||
Pet(const std::string &name, const std::string &species)
|
||||
: m_name(name), m_species(species) {}
|
||||
std::string name() const { return m_name; }
|
||||
std::string species() const { return m_species; }
|
||||
|
||||
private:
|
||||
std::string m_name;
|
||||
std::string m_species;
|
||||
};
|
||||
|
||||
class Dog : public Pet {
|
||||
public:
|
||||
explicit Dog(const std::string &name) : Pet(name, "dog") {}
|
||||
std::string bark() const { return "Woof!"; }
|
||||
};
|
||||
|
||||
class Rabbit : public Pet {
|
||||
public:
|
||||
explicit Rabbit(const std::string &name) : Pet(name, "parrot") {}
|
||||
};
|
||||
|
||||
class Hamster : public Pet {
|
||||
public:
|
||||
explicit Hamster(const std::string &name) : Pet(name, "rodent") {}
|
||||
};
|
||||
|
||||
class Chimera : public Pet {
|
||||
Chimera() : Pet("Kimmy", "chimera") {}
|
||||
};
|
||||
|
||||
py::class_<Pet> pet_class(m, "Pet");
|
||||
pet_class.def(py::init<std::string, std::string>())
|
||||
.def("name", &Pet::name)
|
||||
.def("species", &Pet::species);
|
||||
|
||||
/* One way of declaring a subclass relationship: reference parent's class_ object */
|
||||
py::class_<Dog>(m, "Dog", pet_class).def(py::init<std::string>());
|
||||
|
||||
/* Another way of declaring a subclass relationship: reference parent's C++ type */
|
||||
py::class_<Rabbit, Pet>(m, "Rabbit").def(py::init<std::string>());
|
||||
|
||||
/* And another: list parent in class template arguments */
|
||||
py::class_<Hamster, Pet>(m, "Hamster").def(py::init<std::string>());
|
||||
|
||||
/* Constructors are not inherited by default */
|
||||
py::class_<Chimera, Pet>(m, "Chimera");
|
||||
|
||||
m.def("pet_name_species",
|
||||
[](const Pet &pet) { return pet.name() + " is a " + pet.species(); });
|
||||
m.def("dog_bark", [](const Dog &dog) { return dog.bark(); });
|
||||
|
||||
// test_automatic_upcasting
|
||||
struct BaseClass {
|
||||
BaseClass() = default;
|
||||
BaseClass(const BaseClass &) = default;
|
||||
BaseClass(BaseClass &&) = default;
|
||||
virtual ~BaseClass() = default;
|
||||
};
|
||||
struct DerivedClass1 : BaseClass {};
|
||||
struct DerivedClass2 : BaseClass {};
|
||||
|
||||
py::class_<BaseClass>(m, "BaseClass").def(py::init<>());
|
||||
py::class_<DerivedClass1>(m, "DerivedClass1").def(py::init<>());
|
||||
py::class_<DerivedClass2>(m, "DerivedClass2").def(py::init<>());
|
||||
|
||||
m.def("return_class_1", []() -> BaseClass * { return new DerivedClass1(); });
|
||||
m.def("return_class_2", []() -> BaseClass * { return new DerivedClass2(); });
|
||||
m.def("return_class_n", [](int n) -> BaseClass * {
|
||||
if (n == 1) {
|
||||
return new DerivedClass1();
|
||||
}
|
||||
if (n == 2) {
|
||||
return new DerivedClass2();
|
||||
}
|
||||
return new BaseClass();
|
||||
});
|
||||
m.def("return_none", []() -> BaseClass * { return nullptr; });
|
||||
|
||||
// test_isinstance
|
||||
m.def("check_instances", [](const py::list &l) {
|
||||
return py::make_tuple(py::isinstance<py::tuple>(l[0]),
|
||||
py::isinstance<py::dict>(l[1]),
|
||||
py::isinstance<Pet>(l[2]),
|
||||
py::isinstance<Pet>(l[3]),
|
||||
py::isinstance<Dog>(l[4]),
|
||||
py::isinstance<Rabbit>(l[5]),
|
||||
py::isinstance<UnregisteredType>(l[6]));
|
||||
});
|
||||
|
||||
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>();
|
||||
}
|
||||
return py::type::of<Invalid>();
|
||||
});
|
||||
|
||||
m.def("get_type_of", [](py::object ob) { return py::type::of(std::move(ob)); });
|
||||
|
||||
m.def("get_type_classic", [](py::handle h) { return h.get_type(); });
|
||||
|
||||
m.def("as_type", [](const py::object &ob) { return py::type(ob); });
|
||||
|
||||
// test_mismatched_holder
|
||||
struct MismatchBase1 {};
|
||||
struct MismatchDerived1 : MismatchBase1 {};
|
||||
|
||||
struct MismatchBase2 {};
|
||||
struct MismatchDerived2 : MismatchBase2 {};
|
||||
|
||||
m.def("mismatched_holder_1", []() {
|
||||
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__");
|
||||
py::class_<MismatchBase2>(mod, "MismatchBase2");
|
||||
py::class_<MismatchDerived2, std::shared_ptr<MismatchDerived2>, MismatchBase2>(
|
||||
mod, "MismatchDerived2");
|
||||
});
|
||||
|
||||
// test_override_static
|
||||
// #511: problem with inheritance + overwritten def_static
|
||||
struct MyBase {
|
||||
static std::unique_ptr<MyBase> make() { return std::unique_ptr<MyBase>(new MyBase()); }
|
||||
};
|
||||
|
||||
struct MyDerived : MyBase {
|
||||
static std::unique_ptr<MyDerived> make() {
|
||||
return std::unique_ptr<MyDerived>(new MyDerived());
|
||||
}
|
||||
};
|
||||
|
||||
py::class_<MyBase>(m, "MyBase").def_static("make", &MyBase::make);
|
||||
|
||||
py::class_<MyDerived, MyBase>(m, "MyDerived")
|
||||
.def_static("make", &MyDerived::make)
|
||||
.def_static("make2", &MyDerived::make);
|
||||
|
||||
// test_implicit_conversion_life_support
|
||||
struct ConvertibleFromUserType {
|
||||
int i;
|
||||
|
||||
explicit ConvertibleFromUserType(UserType u) : i(u.value()) {}
|
||||
};
|
||||
|
||||
py::class_<ConvertibleFromUserType>(m, "AcceptsUserType").def(py::init<UserType>());
|
||||
py::implicitly_convertible<UserType, ConvertibleFromUserType>();
|
||||
|
||||
m.def("implicitly_convert_argument", [](const ConvertibleFromUserType &r) { return r.i; });
|
||||
m.def("implicitly_convert_variable", [](const py::object &o) {
|
||||
// `o` is `UserType` and `r` is a reference to a temporary created by implicit
|
||||
// conversion. This is valid when called inside a bound function because the temp
|
||||
// object is attached to the same life support system as the arguments.
|
||||
const auto &r = o.cast<const ConvertibleFromUserType &>();
|
||||
return r.i;
|
||||
});
|
||||
m.add_object("implicitly_convert_variable_fail", [&] {
|
||||
auto f = [](PyObject *, PyObject *args) -> PyObject * {
|
||||
auto o = py::reinterpret_borrow<py::tuple>(args)[0];
|
||||
try { // It should fail here because there is no life support.
|
||||
o.cast<const ConvertibleFromUserType &>();
|
||||
} catch (const py::cast_error &e) {
|
||||
return py::str(e.what()).release().ptr();
|
||||
}
|
||||
return py::str().release().ptr();
|
||||
};
|
||||
|
||||
auto *def = new PyMethodDef{"f", f, METH_VARARGS, nullptr};
|
||||
py::capsule def_capsule(def,
|
||||
[](void *ptr) { delete reinterpret_cast<PyMethodDef *>(ptr); });
|
||||
return py::reinterpret_steal<py::object>(
|
||||
PyCFunction_NewEx(def, def_capsule.ptr(), m.ptr()));
|
||||
}());
|
||||
|
||||
// test_operator_new_delete
|
||||
struct HasOpNewDel {
|
||||
std::uint64_t i;
|
||||
static void *operator new(size_t s) {
|
||||
py::print("A new", s);
|
||||
return ::operator new(s);
|
||||
}
|
||||
static void *operator new(size_t s, void *ptr) {
|
||||
py::print("A placement-new", s);
|
||||
return ptr;
|
||||
}
|
||||
static void operator delete(void *p) {
|
||||
py::print("A delete");
|
||||
return ::operator delete(p);
|
||||
}
|
||||
};
|
||||
struct HasOpNewDelSize {
|
||||
std::uint32_t i;
|
||||
static void *operator new(size_t s) {
|
||||
py::print("B new", s);
|
||||
return ::operator new(s);
|
||||
}
|
||||
static void *operator new(size_t s, void *ptr) {
|
||||
py::print("B placement-new", s);
|
||||
return ptr;
|
||||
}
|
||||
static void operator delete(void *p, size_t s) {
|
||||
py::print("B delete", s);
|
||||
return ::operator delete(p);
|
||||
}
|
||||
};
|
||||
struct AliasedHasOpNewDelSize {
|
||||
std::uint64_t i;
|
||||
static void *operator new(size_t s) {
|
||||
py::print("C new", s);
|
||||
return ::operator new(s);
|
||||
}
|
||||
static void *operator new(size_t s, void *ptr) {
|
||||
py::print("C placement-new", s);
|
||||
return ptr;
|
||||
}
|
||||
static void operator delete(void *p, size_t s) {
|
||||
py::print("C delete", s);
|
||||
return ::operator delete(p);
|
||||
}
|
||||
virtual ~AliasedHasOpNewDelSize() = default;
|
||||
AliasedHasOpNewDelSize() = default;
|
||||
AliasedHasOpNewDelSize(const AliasedHasOpNewDelSize &) = delete;
|
||||
};
|
||||
struct PyAliasedHasOpNewDelSize : AliasedHasOpNewDelSize {
|
||||
PyAliasedHasOpNewDelSize() = default;
|
||||
explicit PyAliasedHasOpNewDelSize(int) {}
|
||||
std::uint64_t j;
|
||||
};
|
||||
struct HasOpNewDelBoth {
|
||||
std::uint32_t i[8];
|
||||
static void *operator new(size_t s) {
|
||||
py::print("D new", s);
|
||||
return ::operator new(s);
|
||||
}
|
||||
static void *operator new(size_t s, void *ptr) {
|
||||
py::print("D placement-new", s);
|
||||
return ptr;
|
||||
}
|
||||
static void operator delete(void *p) {
|
||||
py::print("D delete");
|
||||
return ::operator delete(p);
|
||||
}
|
||||
static void operator delete(void *p, size_t s) {
|
||||
py::print("D wrong delete", s);
|
||||
return ::operator delete(p);
|
||||
}
|
||||
};
|
||||
py::class_<HasOpNewDel>(m, "HasOpNewDel").def(py::init<>());
|
||||
py::class_<HasOpNewDelSize>(m, "HasOpNewDelSize").def(py::init<>());
|
||||
py::class_<HasOpNewDelBoth>(m, "HasOpNewDelBoth").def(py::init<>());
|
||||
py::class_<AliasedHasOpNewDelSize, PyAliasedHasOpNewDelSize> aliased(m,
|
||||
"AliasedHasOpNewDelSize");
|
||||
aliased.def(py::init<>());
|
||||
aliased.attr("size_noalias") = py::int_(sizeof(AliasedHasOpNewDelSize));
|
||||
aliased.attr("size_alias") = py::int_(sizeof(PyAliasedHasOpNewDelSize));
|
||||
|
||||
// This test is actually part of test_local_bindings (test_duplicate_local), but we need a
|
||||
// definition in a different compilation unit within the same module:
|
||||
bind_local<LocalExternal, 17>(m, "LocalExternal", py::module_local());
|
||||
|
||||
// test_bind_protected_functions
|
||||
class ProtectedA {
|
||||
protected:
|
||||
int foo() const { return value; }
|
||||
|
||||
private:
|
||||
int value = 42;
|
||||
};
|
||||
|
||||
class PublicistA : public ProtectedA {
|
||||
public:
|
||||
using ProtectedA::foo;
|
||||
};
|
||||
|
||||
py::class_<ProtectedA>(m, "ProtectedA").def(py::init<>()).def("foo", &PublicistA::foo);
|
||||
|
||||
class ProtectedB {
|
||||
public:
|
||||
virtual ~ProtectedB() = default;
|
||||
ProtectedB() = default;
|
||||
ProtectedB(const ProtectedB &) = delete;
|
||||
|
||||
protected:
|
||||
virtual int foo() const { return value; }
|
||||
|
||||
private:
|
||||
int value = 42;
|
||||
};
|
||||
|
||||
class TrampolineB : public ProtectedB {
|
||||
public:
|
||||
int foo() const override { PYBIND11_OVERRIDE(int, ProtectedB, foo, ); }
|
||||
};
|
||||
|
||||
class PublicistB : public ProtectedB {
|
||||
public:
|
||||
// [workaround(intel)] = default does not work here
|
||||
// Removing or defaulting this destructor results in linking errors with the Intel compiler
|
||||
// (in Debug builds only, tested with icpc (ICC) 2021.1 Beta 20200827)
|
||||
~PublicistB() override{}; // NOLINT(modernize-use-equals-default)
|
||||
using ProtectedB::foo;
|
||||
};
|
||||
|
||||
py::class_<ProtectedB, TrampolineB>(m, "ProtectedB")
|
||||
.def(py::init<>())
|
||||
.def("foo", &PublicistB::foo);
|
||||
|
||||
// test_brace_initialization
|
||||
struct BraceInitialization {
|
||||
int field1;
|
||||
std::string field2;
|
||||
};
|
||||
|
||||
py::class_<BraceInitialization>(m, "BraceInitialization")
|
||||
.def(py::init<int, const std::string &>())
|
||||
.def_readwrite("field1", &BraceInitialization::field1)
|
||||
.def_readwrite("field2", &BraceInitialization::field2);
|
||||
// We *don't* want to construct using braces when the given constructor argument maps to a
|
||||
// constructor, because brace initialization could go to the wrong place (in particular when
|
||||
// there is also an `initializer_list<T>`-accept constructor):
|
||||
py::class_<NoBraceInitialization>(m, "NoBraceInitialization")
|
||||
.def(py::init<std::vector<int>>())
|
||||
.def_readonly("vec", &NoBraceInitialization::vec);
|
||||
|
||||
// test_reentrant_implicit_conversion_failure
|
||||
// #1035: issue with runaway reentrant implicit conversion
|
||||
struct BogusImplicitConversion {
|
||||
BogusImplicitConversion(const BogusImplicitConversion &) = default;
|
||||
};
|
||||
|
||||
py::class_<BogusImplicitConversion>(m, "BogusImplicitConversion")
|
||||
.def(py::init<const BogusImplicitConversion &>());
|
||||
|
||||
py::implicitly_convertible<int, BogusImplicitConversion>();
|
||||
|
||||
// test_qualname
|
||||
// #1166: nested class docstring doesn't show nested name
|
||||
// Also related: tests that __qualname__ is set properly
|
||||
struct NestBase {};
|
||||
struct Nested {};
|
||||
py::class_<NestBase> base(m, "NestBase");
|
||||
base.def(py::init<>());
|
||||
py::class_<Nested>(base, "Nested")
|
||||
.def(py::init<>())
|
||||
.def("fn", [](Nested &, int, NestBase &, Nested &) {})
|
||||
.def(
|
||||
"fa", [](Nested &, int, NestBase &, Nested &) {}, "a"_a, "b"_a, "c"_a);
|
||||
base.def("g", [](NestBase &, Nested &) {});
|
||||
base.def("h", []() { return NestBase(); });
|
||||
|
||||
// test_error_after_conversion
|
||||
// The second-pass path through dispatcher() previously didn't
|
||||
// remember which overload was used, and would crash trying to
|
||||
// generate a useful error message
|
||||
|
||||
struct NotRegistered {};
|
||||
struct StringWrapper {
|
||||
std::string str;
|
||||
};
|
||||
m.def("test_error_after_conversions", [](int) {});
|
||||
m.def("test_error_after_conversions",
|
||||
[](const StringWrapper &) -> NotRegistered { return {}; });
|
||||
py::class_<StringWrapper>(m, "StringWrapper").def(py::init<std::string>());
|
||||
py::implicitly_convertible<std::string, StringWrapper>();
|
||||
|
||||
#if defined(PYBIND11_CPP17)
|
||||
struct alignas(1024) Aligned {
|
||||
std::uintptr_t ptr() const { return (uintptr_t) this; }
|
||||
};
|
||||
py::class_<Aligned>(m, "Aligned").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; }));
|
||||
|
||||
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", [](const py::module_ &m) {
|
||||
py::class_<Duplicate>(m, "Duplicate");
|
||||
py::class_<OtherDuplicate>(m, "Duplicate");
|
||||
});
|
||||
m.def("register_duplicate_class_type", [](const py::module_ &m) {
|
||||
py::class_<OtherDuplicate>(m, "OtherDuplicate");
|
||||
py::class_<OtherDuplicate>(m, "YetAnotherDuplicate");
|
||||
});
|
||||
m.def("register_duplicate_nested_class_name", [](const py::object >) {
|
||||
py::class_<DuplicateNested>(gt, "DuplicateNested");
|
||||
py::class_<OtherDuplicateNested>(gt, "DuplicateNested");
|
||||
});
|
||||
m.def("register_duplicate_nested_class_type", [](const py::object >) {
|
||||
py::class_<OtherDuplicateNested>(gt, "OtherDuplicateNested");
|
||||
py::class_<OtherDuplicateNested>(gt, "YetAnotherDuplicateNested");
|
||||
});
|
||||
}
|
||||
|
||||
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:
|
||||
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);
|
||||
#define CHECK_ALIAS(N) \
|
||||
static_assert( \
|
||||
DoesntBreak##N::has_alias \
|
||||
&& std::is_same<typename DoesntBreak##N::type_alias, BreaksTramp<(N)>>::value, \
|
||||
"DoesntBreak" #N " has wrong type_alias!")
|
||||
#define CHECK_NOALIAS(N) \
|
||||
static_assert(!DoesntBreak##N::has_alias \
|
||||
&& std::is_void<typename DoesntBreak##N::type_alias>::value, \
|
||||
"DoesntBreak" #N " has type alias, but shouldn't!")
|
||||
CHECK_ALIAS(1);
|
||||
CHECK_ALIAS(2);
|
||||
CHECK_NOALIAS(3);
|
||||
CHECK_ALIAS(4);
|
||||
CHECK_NOALIAS(5);
|
||||
CHECK_ALIAS(6);
|
||||
CHECK_ALIAS(7);
|
||||
CHECK_NOALIAS(8);
|
||||
#define CHECK_HOLDER(N, TYPE) \
|
||||
static_assert(std::is_same<typename DoesntBreak##N::holder_type, \
|
||||
std::TYPE##_ptr<BreaksBase<(N)>>>::value, \
|
||||
"DoesntBreak" #N " has wrong holder_type!")
|
||||
CHECK_HOLDER(1, unique);
|
||||
CHECK_HOLDER(2, unique);
|
||||
CHECK_HOLDER(3, unique);
|
||||
CHECK_HOLDER(4, unique);
|
||||
CHECK_HOLDER(5, unique);
|
||||
CHECK_HOLDER(6, shared);
|
||||
CHECK_HOLDER(7, shared);
|
||||
CHECK_HOLDER(8, shared);
|
||||
|
||||
// There's no nice way to test that these fail because they fail to compile; leave them here,
|
||||
// though, so that they can be manually tested by uncommenting them (and seeing that compilation
|
||||
// failures occurs).
|
||||
|
||||
// We have to actually look into the type: the typedef alone isn't enough to instantiate the type:
|
||||
#define CHECK_BROKEN(N) \
|
||||
static_assert(std::is_same<typename Breaks##N::type, BreaksBase<-(N)>>::value, \
|
||||
"Breaks1 has wrong type!");
|
||||
|
||||
#ifdef PYBIND11_NEVER_DEFINED_EVER
|
||||
// Two holder classes:
|
||||
typedef py::
|
||||
class_<BreaksBase<-1>, std::unique_ptr<BreaksBase<-1>>, std::unique_ptr<BreaksBase<-1>>>
|
||||
Breaks1;
|
||||
CHECK_BROKEN(1);
|
||||
// Two aliases:
|
||||
typedef py::class_<BreaksBase<-2>, BreaksTramp<-2>, BreaksTramp<-2>> Breaks2;
|
||||
CHECK_BROKEN(2);
|
||||
// Holder + 2 aliases
|
||||
typedef py::
|
||||
class_<BreaksBase<-3>, std::unique_ptr<BreaksBase<-3>>, BreaksTramp<-3>, BreaksTramp<-3>>
|
||||
Breaks3;
|
||||
CHECK_BROKEN(3);
|
||||
// Alias + 2 holders
|
||||
typedef py::class_<BreaksBase<-4>,
|
||||
std::unique_ptr<BreaksBase<-4>>,
|
||||
BreaksTramp<-4>,
|
||||
std::shared_ptr<BreaksBase<-4>>>
|
||||
Breaks4;
|
||||
CHECK_BROKEN(4);
|
||||
// Invalid option (not a subclass or holder)
|
||||
typedef py::class_<BreaksBase<-5>, BreaksTramp<-4>> Breaks5;
|
||||
CHECK_BROKEN(5);
|
||||
// Invalid option: multiple inheritance not supported:
|
||||
template <>
|
||||
struct BreaksBase<-8> : BreaksBase<-6>, BreaksBase<-7> {};
|
||||
typedef py::class_<BreaksBase<-8>, BreaksBase<-6>, BreaksBase<-7>> Breaks8;
|
||||
CHECK_BROKEN(8);
|
||||
#endif
|
||||
435
third_party/pybind11/tests/test_class.py
vendored
Normal file
435
third_party/pybind11/tests/test_class.py
vendored
Normal file
@@ -0,0 +1,435 @@
|
||||
import pytest
|
||||
|
||||
import env # noqa: F401
|
||||
from pybind11_tests import ConstructorStats, UserType
|
||||
from pybind11_tests import class_ as m
|
||||
|
||||
|
||||
def test_repr():
|
||||
assert "pybind11_type" in repr(type(UserType))
|
||||
assert "UserType" in repr(UserType)
|
||||
|
||||
|
||||
def test_instance(msg):
|
||||
with pytest.raises(TypeError) as excinfo:
|
||||
m.NoConstructor()
|
||||
assert msg(
|
||||
excinfo.value) == "m.class_.NoConstructor: No constructor defined!"
|
||||
|
||||
instance = m.NoConstructor.new_instance()
|
||||
|
||||
cstats = ConstructorStats.get(m.NoConstructor)
|
||||
assert cstats.alive() == 1
|
||||
del instance
|
||||
assert cstats.alive() == 0
|
||||
|
||||
|
||||
def test_instance_new(msg):
|
||||
instance = m.NoConstructorNew() # .__new__(m.NoConstructor.__class__)
|
||||
cstats = ConstructorStats.get(m.NoConstructorNew)
|
||||
assert cstats.alive() == 1
|
||||
del instance
|
||||
assert cstats.alive() == 0
|
||||
|
||||
|
||||
def test_type():
|
||||
assert m.check_type(1) == m.DerivedClass1
|
||||
with pytest.raises(RuntimeError) as execinfo:
|
||||
m.check_type(0)
|
||||
|
||||
assert "pybind11::detail::get_type_info: unable to find type info" in str(
|
||||
execinfo.value)
|
||||
assert "Invalid" in str(execinfo.value)
|
||||
|
||||
# Currently not supported
|
||||
# See https://github.com/pybind/pybind11/issues/2486
|
||||
# assert m.check_type(2) == int
|
||||
|
||||
|
||||
def test_type_of_py():
|
||||
assert m.get_type_of(1) == int
|
||||
assert m.get_type_of(m.DerivedClass1()) == m.DerivedClass1
|
||||
assert m.get_type_of(int) == type
|
||||
|
||||
|
||||
def test_type_of_classic():
|
||||
assert m.get_type_classic(1) == int
|
||||
assert m.get_type_classic(m.DerivedClass1()) == m.DerivedClass1
|
||||
assert m.get_type_classic(int) == type
|
||||
|
||||
|
||||
def test_type_of_py_nodelete():
|
||||
# If the above test deleted the class, this will segfault
|
||||
assert m.get_type_of(m.DerivedClass1()) == m.DerivedClass1
|
||||
|
||||
|
||||
def test_as_type_py():
|
||||
assert m.as_type(int) == int
|
||||
|
||||
with pytest.raises(TypeError):
|
||||
assert m.as_type(1) == int
|
||||
|
||||
with pytest.raises(TypeError):
|
||||
assert m.as_type(m.DerivedClass1()) == m.DerivedClass1
|
||||
|
||||
|
||||
def test_docstrings(doc):
|
||||
assert doc(UserType) == "A `py::class_` type for testing"
|
||||
assert UserType.__name__ == "UserType"
|
||||
assert UserType.__module__ == "pybind11_tests"
|
||||
assert UserType.get_value.__name__ == "get_value"
|
||||
assert UserType.get_value.__module__ == "pybind11_tests"
|
||||
|
||||
assert (doc(UserType.get_value) == """
|
||||
get_value(self: m.UserType) -> int
|
||||
|
||||
Get value using a method
|
||||
""")
|
||||
assert doc(UserType.value) == "Get/set value using a property"
|
||||
|
||||
assert (doc(m.NoConstructor.new_instance) == """
|
||||
new_instance() -> m.class_.NoConstructor
|
||||
|
||||
Return an instance
|
||||
""")
|
||||
|
||||
|
||||
def test_qualname(doc):
|
||||
"""Tests that a properly qualified name is set in __qualname__ and that
|
||||
generated docstrings properly use it and the module name"""
|
||||
assert m.NestBase.__qualname__ == "NestBase"
|
||||
assert m.NestBase.Nested.__qualname__ == "NestBase.Nested"
|
||||
|
||||
assert (doc(m.NestBase.__init__) == """
|
||||
__init__(self: m.class_.NestBase) -> None
|
||||
""")
|
||||
assert (doc(m.NestBase.g) == """
|
||||
g(self: m.class_.NestBase, arg0: m.class_.NestBase.Nested) -> None
|
||||
""")
|
||||
assert (doc(m.NestBase.Nested.__init__) == """
|
||||
__init__(self: m.class_.NestBase.Nested) -> None
|
||||
""")
|
||||
assert (doc(m.NestBase.Nested.fn) == """
|
||||
fn(self: m.class_.NestBase.Nested, arg0: int, arg1: m.class_.NestBase, arg2: m.class_.NestBase.Nested) -> None
|
||||
""")
|
||||
assert (doc(m.NestBase.Nested.fa) == """
|
||||
fa(self: m.class_.NestBase.Nested, a: int, b: m.class_.NestBase, c: m.class_.NestBase.Nested) -> None
|
||||
""")
|
||||
assert m.NestBase.__module__ == "pybind11_tests.class_"
|
||||
assert m.NestBase.Nested.__module__ == "pybind11_tests.class_"
|
||||
|
||||
|
||||
def test_inheritance(msg):
|
||||
roger = m.Rabbit("Rabbit")
|
||||
assert roger.name() + " is a " + roger.species() == "Rabbit is a parrot"
|
||||
assert m.pet_name_species(roger) == "Rabbit is a parrot"
|
||||
|
||||
polly = m.Pet("Polly", "parrot")
|
||||
assert polly.name() + " is a " + polly.species() == "Polly is a parrot"
|
||||
assert m.pet_name_species(polly) == "Polly is a parrot"
|
||||
|
||||
molly = m.Dog("Molly")
|
||||
assert molly.name() + " is a " + molly.species() == "Molly is a dog"
|
||||
assert m.pet_name_species(molly) == "Molly is a dog"
|
||||
|
||||
fred = m.Hamster("Fred")
|
||||
assert fred.name() + " is a " + fred.species() == "Fred is a rodent"
|
||||
|
||||
assert m.dog_bark(molly) == "Woof!"
|
||||
|
||||
with pytest.raises(TypeError) as excinfo:
|
||||
m.dog_bark(polly)
|
||||
assert (msg(excinfo.value) == """
|
||||
dog_bark(): incompatible function arguments. The following argument types are supported:
|
||||
1. (arg0: m.class_.Dog) -> str
|
||||
|
||||
Invoked with: <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"
|
||||
assert type(m.return_none()).__name__ == "NoneType"
|
||||
# Repeat these a few times in a random order to ensure no invalid caching is applied
|
||||
assert type(m.return_class_n(1)).__name__ == "DerivedClass1"
|
||||
assert type(m.return_class_n(2)).__name__ == "DerivedClass2"
|
||||
assert type(m.return_class_n(0)).__name__ == "BaseClass"
|
||||
assert type(m.return_class_n(2)).__name__ == "DerivedClass2"
|
||||
assert type(m.return_class_n(2)).__name__ == "DerivedClass2"
|
||||
assert type(m.return_class_n(0)).__name__ == "BaseClass"
|
||||
assert type(m.return_class_n(1)).__name__ == "DerivedClass1"
|
||||
|
||||
|
||||
def test_isinstance():
|
||||
objects = [tuple(), dict(), m.Pet("Polly", "parrot")] + [m.Dog("Molly")
|
||||
] * 4
|
||||
expected = (True, True, True, True, True, False, False)
|
||||
assert m.check_instances(objects) == expected
|
||||
|
||||
|
||||
def test_mismatched_holder():
|
||||
import re
|
||||
|
||||
with pytest.raises(RuntimeError) as excinfo:
|
||||
m.mismatched_holder_1()
|
||||
assert re.match(
|
||||
'generic_type: type ".*MismatchDerived1" does not have a non-default '
|
||||
'holder type while its base ".*MismatchBase1" does',
|
||||
str(excinfo.value), )
|
||||
|
||||
with pytest.raises(RuntimeError) as excinfo:
|
||||
m.mismatched_holder_2()
|
||||
assert re.match(
|
||||
'generic_type: type ".*MismatchDerived2" has a non-default holder type '
|
||||
'while its base ".*MismatchBase2" does not',
|
||||
str(excinfo.value), )
|
||||
|
||||
|
||||
def test_override_static():
|
||||
"""#511: problem with inheritance + overwritten def_static"""
|
||||
b = m.MyBase.make()
|
||||
d1 = m.MyDerived.make2()
|
||||
d2 = m.MyDerived.make()
|
||||
|
||||
assert isinstance(b, m.MyBase)
|
||||
assert isinstance(d1, m.MyDerived)
|
||||
assert isinstance(d2, m.MyDerived)
|
||||
|
||||
|
||||
def test_implicit_conversion_life_support():
|
||||
"""Ensure the lifetime of temporary objects created for implicit conversions"""
|
||||
assert m.implicitly_convert_argument(UserType(5)) == 5
|
||||
assert m.implicitly_convert_variable(UserType(5)) == 5
|
||||
|
||||
assert "outside a bound function" in m.implicitly_convert_variable_fail(
|
||||
UserType(5))
|
||||
|
||||
|
||||
def test_operator_new_delete(capture):
|
||||
"""Tests that class-specific operator new/delete functions are invoked"""
|
||||
|
||||
class SubAliased(m.AliasedHasOpNewDelSize):
|
||||
pass
|
||||
|
||||
with capture:
|
||||
a = m.HasOpNewDel()
|
||||
b = m.HasOpNewDelSize()
|
||||
d = m.HasOpNewDelBoth()
|
||||
assert (capture == """
|
||||
A new 8
|
||||
B new 4
|
||||
D new 32
|
||||
""")
|
||||
sz_alias = str(m.AliasedHasOpNewDelSize.size_alias)
|
||||
sz_noalias = str(m.AliasedHasOpNewDelSize.size_noalias)
|
||||
with capture:
|
||||
c = m.AliasedHasOpNewDelSize()
|
||||
c2 = SubAliased()
|
||||
assert capture == (
|
||||
"C new " + sz_noalias + "\n" + "C new " + sz_alias + "\n")
|
||||
|
||||
with capture:
|
||||
del a
|
||||
pytest.gc_collect()
|
||||
del b
|
||||
pytest.gc_collect()
|
||||
del d
|
||||
pytest.gc_collect()
|
||||
assert (capture == """
|
||||
A delete
|
||||
B delete 4
|
||||
D delete
|
||||
""")
|
||||
|
||||
with capture:
|
||||
del c
|
||||
pytest.gc_collect()
|
||||
del c2
|
||||
pytest.gc_collect()
|
||||
assert capture == (
|
||||
"C delete " + sz_noalias + "\n" + "C delete " + sz_alias + "\n")
|
||||
|
||||
|
||||
def test_bind_protected_functions():
|
||||
"""Expose protected member functions to Python using a helper class"""
|
||||
a = m.ProtectedA()
|
||||
assert a.foo() == 42
|
||||
|
||||
b = m.ProtectedB()
|
||||
assert b.foo() == 42
|
||||
|
||||
class C(m.ProtectedB):
|
||||
def __init__(self):
|
||||
m.ProtectedB.__init__(self)
|
||||
|
||||
def foo(self):
|
||||
return 0
|
||||
|
||||
c = C()
|
||||
assert c.foo() == 0
|
||||
|
||||
|
||||
def test_brace_initialization():
|
||||
"""Tests that simple POD classes can be constructed using C++11 brace initialization"""
|
||||
a = m.BraceInitialization(123, "test")
|
||||
assert a.field1 == 123
|
||||
assert a.field2 == "test"
|
||||
|
||||
# Tests that a non-simple class doesn't get brace initialization (if the
|
||||
# class defines an initializer_list constructor, in particular, it would
|
||||
# win over the expected constructor).
|
||||
b = m.NoBraceInitialization([123, 456])
|
||||
assert b.vec == [123, 456]
|
||||
|
||||
|
||||
@pytest.mark.xfail("env.PYPY")
|
||||
def test_class_refcount():
|
||||
"""Instances must correctly increase/decrease the reference count of their types (#1029)"""
|
||||
from sys import getrefcount
|
||||
|
||||
class PyDog(m.Dog):
|
||||
pass
|
||||
|
||||
for cls in m.Dog, PyDog:
|
||||
refcount_1 = getrefcount(cls)
|
||||
molly = [cls("Molly") for _ in range(10)]
|
||||
refcount_2 = getrefcount(cls)
|
||||
|
||||
del molly
|
||||
pytest.gc_collect()
|
||||
refcount_3 = getrefcount(cls)
|
||||
|
||||
assert refcount_1 == refcount_3
|
||||
assert refcount_2 > refcount_1
|
||||
|
||||
|
||||
def test_reentrant_implicit_conversion_failure(msg):
|
||||
# ensure that there is no runaway reentrant implicit conversion (#1035)
|
||||
with pytest.raises(TypeError) as excinfo:
|
||||
m.BogusImplicitConversion(0)
|
||||
assert (msg(excinfo.value) == """
|
||||
__init__(): incompatible constructor arguments. The following argument types are supported:
|
||||
1. m.class_.BogusImplicitConversion(arg0: m.class_.BogusImplicitConversion)
|
||||
|
||||
Invoked with: 0
|
||||
""")
|
||||
|
||||
|
||||
def test_error_after_conversions():
|
||||
with pytest.raises(TypeError) as exc_info:
|
||||
m.test_error_after_conversions("hello")
|
||||
assert str(exc_info.value).startswith(
|
||||
"Unable to convert function return value to a Python type!")
|
||||
|
||||
|
||||
def test_aligned():
|
||||
if hasattr(m, "Aligned"):
|
||||
p = m.Aligned().ptr()
|
||||
assert p % 1024 == 0
|
||||
|
||||
|
||||
# https://foss.heptapod.net/pypy/pypy/-/issues/2742
|
||||
@pytest.mark.xfail("env.PYPY")
|
||||
def test_final():
|
||||
with pytest.raises(TypeError) as exc_info:
|
||||
|
||||
class PyFinalChild(m.IsFinal):
|
||||
pass
|
||||
|
||||
assert str(exc_info.value).endswith("is not an acceptable base type")
|
||||
|
||||
|
||||
# https://foss.heptapod.net/pypy/pypy/-/issues/2742
|
||||
@pytest.mark.xfail("env.PYPY")
|
||||
def test_non_final_final():
|
||||
with pytest.raises(TypeError) as exc_info:
|
||||
|
||||
class PyNonFinalFinalChild(m.IsNonFinalFinal):
|
||||
pass
|
||||
|
||||
assert str(exc_info.value).endswith("is not an acceptable base type")
|
||||
|
||||
|
||||
# https://github.com/pybind/pybind11/issues/1878
|
||||
def test_exception_rvalue_abort():
|
||||
with pytest.raises(RuntimeError):
|
||||
m.PyPrintDestructor().throw_something()
|
||||
|
||||
|
||||
# https://github.com/pybind/pybind11/issues/1568
|
||||
def test_multiple_instances_with_same_pointer(capture):
|
||||
n = 100
|
||||
instances = [m.SamePointer() for _ in range(n)]
|
||||
for i in range(n):
|
||||
# We need to reuse the same allocated memory for with a different type,
|
||||
# to ensure the bug in `deregister_instance_impl` is detected. Otherwise
|
||||
# `Py_TYPE(self) == Py_TYPE(it->second)` will still succeed, even though
|
||||
# the `instance` is already deleted.
|
||||
instances[i] = m.Empty()
|
||||
# No assert: if this does not trigger the error
|
||||
# pybind11_fail("pybind11_object_dealloc(): Tried to deallocate unregistered instance!");
|
||||
# and just completes without crashing, we're good.
|
||||
|
||||
|
||||
# https://github.com/pybind/pybind11/issues/1624
|
||||
def test_base_and_derived_nested_scope():
|
||||
assert issubclass(m.DerivedWithNested, m.BaseWithNested)
|
||||
assert m.BaseWithNested.Nested != m.DerivedWithNested.Nested
|
||||
assert m.BaseWithNested.Nested.get_name() == "BaseWithNested::Nested"
|
||||
assert m.DerivedWithNested.Nested.get_name() == "DerivedWithNested::Nested"
|
||||
|
||||
|
||||
def test_register_duplicate_class():
|
||||
import types
|
||||
|
||||
module_scope = types.ModuleType("module_scope")
|
||||
with pytest.raises(RuntimeError) as exc_info:
|
||||
m.register_duplicate_class_name(module_scope)
|
||||
expected = ('generic_type: cannot initialize type "Duplicate": '
|
||||
"an object with that name is already defined")
|
||||
assert str(exc_info.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
|
||||
84
third_party/pybind11/tests/test_cmake_build/CMakeLists.txt
vendored
Normal file
84
third_party/pybind11/tests/test_cmake_build/CMakeLists.txt
vendored
Normal file
@@ -0,0 +1,84 @@
|
||||
# Built-in in CMake 3.5+
|
||||
include(CMakeParseArguments)
|
||||
|
||||
add_custom_target(test_cmake_build)
|
||||
|
||||
function(pybind11_add_build_test name)
|
||||
cmake_parse_arguments(ARG "INSTALL" "" "" ${ARGN})
|
||||
|
||||
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_SOURCE_DIR=${pybind11_SOURCE_DIR}")
|
||||
else()
|
||||
list(APPEND build_options "-DCMAKE_PREFIX_PATH=${pybind11_BINARY_DIR}/mock_install")
|
||||
endif()
|
||||
|
||||
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_${name}
|
||||
--build-options
|
||||
${build_options})
|
||||
if(ARG_INSTALL)
|
||||
add_dependencies(test_build_${name} mock_install)
|
||||
endif()
|
||||
add_dependencies(test_cmake_build test_build_${name})
|
||||
endfunction()
|
||||
|
||||
possibly_uninitialized(PYTHON_MODULE_EXTENSION Python_INTERPRETER_ID)
|
||||
|
||||
pybind11_add_build_test(subdirectory_function)
|
||||
pybind11_add_build_test(subdirectory_target)
|
||||
if("${PYTHON_MODULE_EXTENSION}" MATCHES "pypy" OR "${Python_INTERPRETER_ID}" STREQUAL "PyPy")
|
||||
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=${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" OR "${Python_INTERPRETER_ID}" STREQUAL "PyPy"
|
||||
))
|
||||
pybind11_add_build_test(installed_embed INSTALL)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
add_dependencies(check test_cmake_build)
|
||||
|
||||
add_subdirectory(subdirectory_target EXCLUDE_FROM_ALL)
|
||||
add_subdirectory(subdirectory_embed EXCLUDE_FROM_ALL)
|
||||
23
third_party/pybind11/tests/test_cmake_build/embed.cpp
vendored
Normal file
23
third_party/pybind11/tests/test_cmake_build/embed.cpp
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
#include <pybind11/embed.h>
|
||||
namespace py = pybind11;
|
||||
|
||||
PYBIND11_EMBEDDED_MODULE(test_cmake_build, m) {
|
||||
m.def("add", [](int i, int j) { return i + j; });
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
if (argc != 2) {
|
||||
throw std::runtime_error("Expected test.py file as the first argument");
|
||||
}
|
||||
auto *test_py_file = argv[1];
|
||||
|
||||
py::scoped_interpreter guard{};
|
||||
|
||||
auto m = py::module_::import("test_cmake_build");
|
||||
if (m.attr("add")(1, 2).cast<int>() != 3) {
|
||||
throw std::runtime_error("embed.cpp failed");
|
||||
}
|
||||
|
||||
py::module_::import("sys").attr("argv") = py::make_tuple("test.py", "embed.cpp");
|
||||
py::eval_file(test_py_file, py::globals());
|
||||
}
|
||||
28
third_party/pybind11/tests/test_cmake_build/installed_embed/CMakeLists.txt
vendored
Normal file
28
third_party/pybind11/tests/test_cmake_build/installed_embed/CMakeLists.txt
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
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)
|
||||
|
||||
find_package(pybind11 CONFIG REQUIRED)
|
||||
message(STATUS "Found pybind11 v${pybind11_VERSION}: ${pybind11_INCLUDE_DIRS}")
|
||||
|
||||
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_installed_embed PROPERTIES NO_SYSTEM_FROM_IMPORTED ON)
|
||||
|
||||
add_custom_target(
|
||||
check_installed_embed
|
||||
$<TARGET_FILE:test_installed_embed> ${PROJECT_SOURCE_DIR}/../test.py
|
||||
DEPENDS test_installed_embed)
|
||||
39
third_party/pybind11/tests/test_cmake_build/installed_function/CMakeLists.txt
vendored
Normal file
39
third_party/pybind11/tests/test_cmake_build/installed_function/CMakeLists.txt
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
cmake_minimum_required(VERSION 3.4)
|
||||
project(test_installed_module CXX)
|
||||
|
||||
# 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_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)
|
||||
|
||||
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_installed_function
|
||||
${CMAKE_COMMAND}
|
||||
-E
|
||||
env
|
||||
PYTHONPATH=$<TARGET_FILE_DIR:test_installed_function>
|
||||
${_Python_EXECUTABLE}
|
||||
${PROJECT_SOURCE_DIR}/../test.py
|
||||
${PROJECT_NAME}
|
||||
DEPENDS test_installed_function)
|
||||
46
third_party/pybind11/tests/test_cmake_build/installed_target/CMakeLists.txt
vendored
Normal file
46
third_party/pybind11/tests/test_cmake_build/installed_target/CMakeLists.txt
vendored
Normal file
@@ -0,0 +1,46 @@
|
||||
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_target CXX)
|
||||
|
||||
find_package(pybind11 CONFIG REQUIRED)
|
||||
message(STATUS "Found pybind11 v${pybind11_VERSION}: ${pybind11_INCLUDE_DIRS}")
|
||||
|
||||
add_library(test_installed_target MODULE ../main.cpp)
|
||||
|
||||
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
|
||||
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_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_installed_target
|
||||
${CMAKE_COMMAND}
|
||||
-E
|
||||
env
|
||||
PYTHONPATH=$<TARGET_FILE_DIR:test_installed_target>
|
||||
${_Python_EXECUTABLE}
|
||||
${PROJECT_SOURCE_DIR}/../test.py
|
||||
${PROJECT_NAME}
|
||||
DEPENDS test_installed_target)
|
||||
6
third_party/pybind11/tests/test_cmake_build/main.cpp
vendored
Normal file
6
third_party/pybind11/tests/test_cmake_build/main.cpp
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
#include <pybind11/pybind11.h>
|
||||
namespace py = pybind11;
|
||||
|
||||
PYBIND11_MODULE(test_cmake_build, m) {
|
||||
m.def("add", [](int i, int j) { return i + j; });
|
||||
}
|
||||
41
third_party/pybind11/tests/test_cmake_build/subdirectory_embed/CMakeLists.txt
vendored
Normal file
41
third_party/pybind11/tests/test_cmake_build/subdirectory_embed/CMakeLists.txt
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
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_EXPORT_NAME test_export)
|
||||
|
||||
add_subdirectory("${pybind11_SOURCE_DIR}" pybind11)
|
||||
|
||||
# Test basic target functionality
|
||||
add_executable(test_subdirectory_embed ../embed.cpp)
|
||||
target_link_libraries(test_subdirectory_embed PRIVATE pybind11::embed)
|
||||
set_target_properties(test_subdirectory_embed PROPERTIES OUTPUT_NAME test_cmake_build)
|
||||
|
||||
add_custom_target(
|
||||
check_subdirectory_embed
|
||||
$<TARGET_FILE:test_subdirectory_embed> "${PROJECT_SOURCE_DIR}/../test.py"
|
||||
DEPENDS test_subdirectory_embed)
|
||||
|
||||
# Test custom export group -- PYBIND11_EXPORT_NAME
|
||||
add_library(test_embed_lib ../embed.cpp)
|
||||
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)
|
||||
35
third_party/pybind11/tests/test_cmake_build/subdirectory_function/CMakeLists.txt
vendored
Normal file
35
third_party/pybind11/tests/test_cmake_build/subdirectory_function/CMakeLists.txt
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
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_function CXX)
|
||||
|
||||
add_subdirectory("${pybind11_SOURCE_DIR}" pybind11)
|
||||
pybind11_add_module(test_subdirectory_function ../main.cpp)
|
||||
set_target_properties(test_subdirectory_function PROPERTIES OUTPUT_NAME test_cmake_build)
|
||||
|
||||
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}
|
||||
DEPENDS test_subdirectory_function)
|
||||
41
third_party/pybind11/tests/test_cmake_build/subdirectory_target/CMakeLists.txt
vendored
Normal file
41
third_party/pybind11/tests/test_cmake_build/subdirectory_target/CMakeLists.txt
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
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_SOURCE_DIR}" pybind11)
|
||||
|
||||
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)
|
||||
|
||||
# Make sure result is, for example, test_installed_target.so, not libtest_installed_target.dylib
|
||||
pybind11_extension(test_subdirectory_target)
|
||||
|
||||
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_target
|
||||
${CMAKE_COMMAND}
|
||||
-E
|
||||
env
|
||||
PYTHONPATH=$<TARGET_FILE_DIR:test_subdirectory_target>
|
||||
${_Python_EXECUTABLE}
|
||||
${PROJECT_SOURCE_DIR}/../test.py
|
||||
${PROJECT_NAME}
|
||||
DEPENDS test_subdirectory_target)
|
||||
8
third_party/pybind11/tests/test_cmake_build/test.py
vendored
Normal file
8
third_party/pybind11/tests/test_cmake_build/test.py
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
import sys
|
||||
|
||||
import test_cmake_build
|
||||
|
||||
assert isinstance(__file__, str) # Test this is properly set
|
||||
|
||||
assert test_cmake_build.add(1, 2) == 3
|
||||
print(f"{sys.argv[1]} imports, runs, and adds: 1 + 2 = 3")
|
||||
55
third_party/pybind11/tests/test_const_name.cpp
vendored
Normal file
55
third_party/pybind11/tests/test_const_name.cpp
vendored
Normal file
@@ -0,0 +1,55 @@
|
||||
// Copyright (c) 2021 The Pybind Development Team.
|
||||
// All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
#include "pybind11_tests.h"
|
||||
|
||||
// IUT = Implementation Under Test
|
||||
#define CONST_NAME_TESTS(TEST_FUNC, IUT) \
|
||||
std::string TEST_FUNC(int selector) { \
|
||||
switch (selector) { \
|
||||
case 0: \
|
||||
return IUT("").text; \
|
||||
case 1: \
|
||||
return IUT("A").text; \
|
||||
case 2: \
|
||||
return IUT("Bd").text; \
|
||||
case 3: \
|
||||
return IUT("Cef").text; \
|
||||
case 4: \
|
||||
return IUT<int>().text; /*NOLINT(bugprone-macro-parentheses)*/ \
|
||||
case 5: \
|
||||
return IUT<std::string>().text; /*NOLINT(bugprone-macro-parentheses)*/ \
|
||||
case 6: \
|
||||
return IUT<true>("T1", "T2").text; /*NOLINT(bugprone-macro-parentheses)*/ \
|
||||
case 7: \
|
||||
return IUT<false>("U1", "U2").text; /*NOLINT(bugprone-macro-parentheses)*/ \
|
||||
case 8: \
|
||||
/*NOLINTNEXTLINE(bugprone-macro-parentheses)*/ \
|
||||
return IUT<true>(IUT("D1"), IUT("D2")).text; \
|
||||
case 9: \
|
||||
/*NOLINTNEXTLINE(bugprone-macro-parentheses)*/ \
|
||||
return IUT<false>(IUT("E1"), IUT("E2")).text; \
|
||||
case 10: \
|
||||
return IUT("KeepAtEnd").text; \
|
||||
default: \
|
||||
break; \
|
||||
} \
|
||||
throw std::runtime_error("Invalid selector value."); \
|
||||
}
|
||||
|
||||
CONST_NAME_TESTS(const_name_tests, py::detail::const_name)
|
||||
|
||||
#ifdef PYBIND11_DETAIL_UNDERSCORE_BACKWARD_COMPATIBILITY
|
||||
CONST_NAME_TESTS(underscore_tests, py::detail::_)
|
||||
#endif
|
||||
|
||||
TEST_SUBMODULE(const_name, m) {
|
||||
m.def("const_name_tests", const_name_tests);
|
||||
|
||||
#if defined(PYBIND11_DETAIL_UNDERSCORE_BACKWARD_COMPATIBILITY)
|
||||
m.def("underscore_tests", underscore_tests);
|
||||
#else
|
||||
m.attr("underscore_tests") = "PYBIND11_DETAIL_UNDERSCORE_BACKWARD_COMPATIBILITY not defined.";
|
||||
#endif
|
||||
}
|
||||
25
third_party/pybind11/tests/test_const_name.py
vendored
Normal file
25
third_party/pybind11/tests/test_const_name.py
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
import pytest
|
||||
|
||||
from pybind11_tests import const_name as m
|
||||
|
||||
|
||||
@pytest.mark.parametrize("func", (m.const_name_tests, m.underscore_tests))
|
||||
@pytest.mark.parametrize(
|
||||
"selector, expected",
|
||||
enumerate((
|
||||
"",
|
||||
"A",
|
||||
"Bd",
|
||||
"Cef",
|
||||
"%",
|
||||
"%",
|
||||
"T1",
|
||||
"U2",
|
||||
"D1",
|
||||
"E2",
|
||||
"KeepAtEnd", )), )
|
||||
def test_const_name(func, selector, expected):
|
||||
if isinstance(func, str):
|
||||
pytest.skip(func)
|
||||
text = func(selector)
|
||||
assert text == expected
|
||||
159
third_party/pybind11/tests/test_constants_and_functions.cpp
vendored
Normal file
159
third_party/pybind11/tests/test_constants_and_functions.cpp
vendored
Normal file
@@ -0,0 +1,159 @@
|
||||
/*
|
||||
tests/test_constants_and_functions.cpp -- global constants and functions, enumerations, raw
|
||||
byte strings
|
||||
|
||||
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"
|
||||
|
||||
enum MyEnum { EFirstEntry = 1, ESecondEntry };
|
||||
|
||||
std::string test_function1() { return "test_function()"; }
|
||||
|
||||
std::string test_function2(MyEnum k) { return "test_function(enum=" + std::to_string(k) + ")"; }
|
||||
|
||||
std::string test_function3(int i) { return "test_function(" + std::to_string(i) + ")"; }
|
||||
|
||||
py::str test_function4() { return "test_function()"; }
|
||||
py::str test_function4(char *) { return "test_function(char *)"; }
|
||||
py::str test_function4(int, float) { return "test_function(int, float)"; }
|
||||
py::str test_function4(float, int) { return "test_function(float, int)"; }
|
||||
|
||||
py::bytes return_bytes() {
|
||||
const char *data = "\x01\x00\x02\x00";
|
||||
return std::string(data, 4);
|
||||
}
|
||||
|
||||
std::string print_bytes(const py::bytes &bytes) {
|
||||
std::string ret = "bytes[";
|
||||
const auto value = static_cast<std::string>(bytes);
|
||||
for (size_t i = 0; i < value.length(); ++i) {
|
||||
ret += std::to_string(static_cast<int>(value[i])) + " ";
|
||||
}
|
||||
ret.back() = ']';
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Test that we properly handle C++17 exception specifiers (which are part of the function
|
||||
// signature in C++17). These should all still work before C++17, but don't affect the function
|
||||
// signature.
|
||||
namespace test_exc_sp {
|
||||
// [workaround(intel)] Unable to use noexcept instead of noexcept(true)
|
||||
// Make the f1 test basically the same as the f2 test in C++17 mode for the Intel compiler as
|
||||
// it fails to compile with a plain noexcept (tested with icc (ICC) 2021.1 Beta 20200827).
|
||||
#if defined(__INTEL_COMPILER) && defined(PYBIND11_CPP17)
|
||||
int f1(int x) noexcept(true) { return x + 1; }
|
||||
#else
|
||||
int f1(int x) noexcept { return x + 1; }
|
||||
#endif
|
||||
int f2(int x) noexcept(true) { return x + 2; }
|
||||
int f3(int x) noexcept(false) { return x + 3; }
|
||||
#if defined(__GNUG__) && !defined(__INTEL_COMPILER)
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wdeprecated"
|
||||
#endif
|
||||
// NOLINTNEXTLINE(modernize-use-noexcept)
|
||||
int f4(int x) throw() { return x + 4; } // Deprecated equivalent to noexcept(true)
|
||||
#if defined(__GNUG__) && !defined(__INTEL_COMPILER)
|
||||
# pragma GCC diagnostic pop
|
||||
#endif
|
||||
struct C {
|
||||
int m1(int x) noexcept { return x - 1; }
|
||||
int m2(int x) const noexcept { return x - 2; }
|
||||
int m3(int x) noexcept(true) { return x - 3; }
|
||||
int m4(int x) const noexcept(true) { return x - 4; }
|
||||
int m5(int x) noexcept(false) { return x - 5; }
|
||||
int m6(int x) const noexcept(false) { return x - 6; }
|
||||
#if defined(__GNUG__) && !defined(__INTEL_COMPILER)
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wdeprecated"
|
||||
#endif
|
||||
// NOLINTNEXTLINE(modernize-use-noexcept)
|
||||
int m7(int x) throw() { return x - 7; }
|
||||
// NOLINTNEXTLINE(modernize-use-noexcept)
|
||||
int m8(int x) const throw() { return x - 8; }
|
||||
#if defined(__GNUG__) && !defined(__INTEL_COMPILER)
|
||||
# pragma GCC diagnostic pop
|
||||
#endif
|
||||
};
|
||||
} // namespace test_exc_sp
|
||||
|
||||
TEST_SUBMODULE(constants_and_functions, m) {
|
||||
// test_constants
|
||||
m.attr("some_constant") = py::int_(14);
|
||||
|
||||
// test_function_overloading
|
||||
m.def("test_function", &test_function1);
|
||||
m.def("test_function", &test_function2);
|
||||
m.def("test_function", &test_function3);
|
||||
|
||||
#if defined(PYBIND11_OVERLOAD_CAST)
|
||||
m.def("test_function", py::overload_cast<>(&test_function4));
|
||||
m.def("test_function", py::overload_cast<char *>(&test_function4));
|
||||
m.def("test_function", py::overload_cast<int, float>(&test_function4));
|
||||
m.def("test_function", py::overload_cast<float, int>(&test_function4));
|
||||
#else
|
||||
m.def("test_function", static_cast<py::str (*)()>(&test_function4));
|
||||
m.def("test_function", static_cast<py::str (*)(char *)>(&test_function4));
|
||||
m.def("test_function", static_cast<py::str (*)(int, float)>(&test_function4));
|
||||
m.def("test_function", static_cast<py::str (*)(float, int)>(&test_function4));
|
||||
#endif
|
||||
|
||||
py::enum_<MyEnum>(m, "MyEnum")
|
||||
.value("EFirstEntry", EFirstEntry)
|
||||
.value("ESecondEntry", ESecondEntry)
|
||||
.export_values();
|
||||
|
||||
// test_bytes
|
||||
m.def("return_bytes", &return_bytes);
|
||||
m.def("print_bytes", &print_bytes);
|
||||
|
||||
// test_exception_specifiers
|
||||
using namespace test_exc_sp;
|
||||
py::class_<C>(m, "C")
|
||||
.def(py::init<>())
|
||||
.def("m1", &C::m1)
|
||||
.def("m2", &C::m2)
|
||||
.def("m3", &C::m3)
|
||||
.def("m4", &C::m4)
|
||||
.def("m5", &C::m5)
|
||||
.def("m6", &C::m6)
|
||||
.def("m7", &C::m7)
|
||||
.def("m8", &C::m8);
|
||||
m.def("f1", f1);
|
||||
m.def("f2", f2);
|
||||
#if defined(__INTEL_COMPILER)
|
||||
# pragma warning push
|
||||
# pragma warning disable 878 // incompatible exception specifications
|
||||
#endif
|
||||
m.def("f3", f3);
|
||||
#if defined(__INTEL_COMPILER)
|
||||
# pragma warning pop
|
||||
#endif
|
||||
m.def("f4", f4);
|
||||
|
||||
// test_function_record_leaks
|
||||
m.def("register_large_capture_with_invalid_arguments", [](py::module_ m) {
|
||||
// This should always be enough to trigger the alternative branch
|
||||
// where `sizeof(capture) > sizeof(rec->data)`
|
||||
uint64_t capture[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
|
||||
#if defined(__GNUC__) && __GNUC__ == 4 // CentOS7
|
||||
py::detail::silence_unused_warnings(capture);
|
||||
#endif
|
||||
m.def(
|
||||
"should_raise", [capture](int) { return capture[9] + 33; }, py::kw_only(), py::arg());
|
||||
});
|
||||
m.def("register_with_raising_repr", [](py::module_ m, const py::object &default_value) {
|
||||
m.def(
|
||||
"should_raise",
|
||||
[](int, int, const py::object &) { return 42; },
|
||||
"some docstring",
|
||||
py::arg_v("x", 42),
|
||||
py::arg_v("y", 42, "<the answer>"),
|
||||
py::arg_v("z", default_value));
|
||||
});
|
||||
}
|
||||
52
third_party/pybind11/tests/test_constants_and_functions.py
vendored
Normal file
52
third_party/pybind11/tests/test_constants_and_functions.py
vendored
Normal file
@@ -0,0 +1,52 @@
|
||||
import pytest
|
||||
|
||||
m = pytest.importorskip("pybind11_tests.constants_and_functions")
|
||||
|
||||
|
||||
def test_constants():
|
||||
assert m.some_constant == 14
|
||||
|
||||
|
||||
def test_function_overloading():
|
||||
assert m.test_function() == "test_function()"
|
||||
assert m.test_function(7) == "test_function(7)"
|
||||
assert m.test_function(m.MyEnum.EFirstEntry) == "test_function(enum=1)"
|
||||
assert m.test_function(m.MyEnum.ESecondEntry) == "test_function(enum=2)"
|
||||
|
||||
assert m.test_function() == "test_function()"
|
||||
assert m.test_function("abcd") == "test_function(char *)"
|
||||
assert m.test_function(1, 1.0) == "test_function(int, float)"
|
||||
assert m.test_function(1, 1.0) == "test_function(int, float)"
|
||||
assert m.test_function(2.0, 2) == "test_function(float, int)"
|
||||
|
||||
|
||||
def test_bytes():
|
||||
assert m.print_bytes(m.return_bytes()) == "bytes[1 0 2 0]"
|
||||
|
||||
|
||||
def test_exception_specifiers():
|
||||
c = m.C()
|
||||
assert c.m1(2) == 1
|
||||
assert c.m2(3) == 1
|
||||
assert c.m3(5) == 2
|
||||
assert c.m4(7) == 3
|
||||
assert c.m5(10) == 5
|
||||
assert c.m6(14) == 8
|
||||
assert c.m7(20) == 13
|
||||
assert c.m8(29) == 21
|
||||
|
||||
assert m.f1(33) == 34
|
||||
assert m.f2(53) == 55
|
||||
assert m.f3(86) == 89
|
||||
assert m.f4(140) == 144
|
||||
|
||||
|
||||
def test_function_record_leaks():
|
||||
class RaisingRepr:
|
||||
def __repr__(self):
|
||||
raise RuntimeError("Surprise!")
|
||||
|
||||
with pytest.raises(RuntimeError):
|
||||
m.register_large_capture_with_invalid_arguments(m)
|
||||
with pytest.raises(RuntimeError):
|
||||
m.register_with_raising_repr(m, RaisingRepr())
|
||||
295
third_party/pybind11/tests/test_copy_move.cpp
vendored
Normal file
295
third_party/pybind11/tests/test_copy_move.cpp
vendored
Normal file
@@ -0,0 +1,295 @@
|
||||
/*
|
||||
tests/test_copy_move_policies.cpp -- 'copy' and 'move' return value policies
|
||||
and related tests
|
||||
|
||||
Copyright (c) 2016 Ben North <ben@redfrontdoor.org>
|
||||
|
||||
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/stl.h>
|
||||
|
||||
#include "constructor_stats.h"
|
||||
#include "pybind11_tests.h"
|
||||
|
||||
template <typename derived>
|
||||
struct empty {
|
||||
static const derived &get_one() { return instance_; }
|
||||
static derived instance_;
|
||||
};
|
||||
|
||||
struct lacking_copy_ctor : public empty<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() = default;
|
||||
lacking_move_ctor(const lacking_move_ctor &other) = delete;
|
||||
lacking_move_ctor(lacking_move_ctor &&other) = delete;
|
||||
};
|
||||
|
||||
template <>
|
||||
lacking_move_ctor empty<lacking_move_ctor>::instance_ = {};
|
||||
|
||||
/* Custom type caster move/copy test classes */
|
||||
class MoveOnlyInt {
|
||||
public:
|
||||
MoveOnlyInt() { print_default_created(this); }
|
||||
explicit MoveOnlyInt(int v) : value{v} { print_created(this, value); }
|
||||
MoveOnlyInt(MoveOnlyInt &&m) noexcept {
|
||||
print_move_created(this, m.value);
|
||||
std::swap(value, m.value);
|
||||
}
|
||||
MoveOnlyInt &operator=(MoveOnlyInt &&m) noexcept {
|
||||
print_move_assigned(this, m.value);
|
||||
std::swap(value, m.value);
|
||||
return *this;
|
||||
}
|
||||
MoveOnlyInt(const MoveOnlyInt &) = delete;
|
||||
MoveOnlyInt &operator=(const MoveOnlyInt &) = delete;
|
||||
~MoveOnlyInt() { print_destroyed(this); }
|
||||
|
||||
int value;
|
||||
};
|
||||
class MoveOrCopyInt {
|
||||
public:
|
||||
MoveOrCopyInt() { print_default_created(this); }
|
||||
explicit MoveOrCopyInt(int v) : value{v} { print_created(this, value); }
|
||||
MoveOrCopyInt(MoveOrCopyInt &&m) noexcept {
|
||||
print_move_created(this, m.value);
|
||||
std::swap(value, m.value);
|
||||
}
|
||||
MoveOrCopyInt &operator=(MoveOrCopyInt &&m) noexcept {
|
||||
print_move_assigned(this, m.value);
|
||||
std::swap(value, m.value);
|
||||
return *this;
|
||||
}
|
||||
MoveOrCopyInt(const MoveOrCopyInt &c) {
|
||||
print_copy_created(this, c.value);
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-prefer-member-initializer)
|
||||
value = c.value;
|
||||
}
|
||||
MoveOrCopyInt &operator=(const MoveOrCopyInt &c) {
|
||||
print_copy_assigned(this, c.value);
|
||||
value = c.value;
|
||||
return *this;
|
||||
}
|
||||
~MoveOrCopyInt() { print_destroyed(this); }
|
||||
|
||||
int value;
|
||||
};
|
||||
class CopyOnlyInt {
|
||||
public:
|
||||
CopyOnlyInt() { print_default_created(this); }
|
||||
explicit CopyOnlyInt(int v) : value{v} { print_created(this, value); }
|
||||
CopyOnlyInt(const CopyOnlyInt &c) {
|
||||
print_copy_created(this, c.value);
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-prefer-member-initializer)
|
||||
value = c.value;
|
||||
}
|
||||
CopyOnlyInt &operator=(const CopyOnlyInt &c) {
|
||||
print_copy_assigned(this, c.value);
|
||||
value = c.value;
|
||||
return *this;
|
||||
}
|
||||
~CopyOnlyInt() { print_destroyed(this); }
|
||||
|
||||
int value;
|
||||
};
|
||||
PYBIND11_NAMESPACE_BEGIN(pybind11)
|
||||
PYBIND11_NAMESPACE_BEGIN(detail)
|
||||
template <>
|
||||
struct type_caster<MoveOnlyInt> {
|
||||
PYBIND11_TYPE_CASTER(MoveOnlyInt, const_name("MoveOnlyInt"));
|
||||
bool load(handle src, bool) {
|
||||
value = MoveOnlyInt(src.cast<int>());
|
||||
return true;
|
||||
}
|
||||
static handle cast(const MoveOnlyInt &m, return_value_policy r, handle p) {
|
||||
return pybind11::cast(m.value, r, p);
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct type_caster<MoveOrCopyInt> {
|
||||
PYBIND11_TYPE_CASTER(MoveOrCopyInt, const_name("MoveOrCopyInt"));
|
||||
bool load(handle src, bool) {
|
||||
value = MoveOrCopyInt(src.cast<int>());
|
||||
return true;
|
||||
}
|
||||
static handle cast(const MoveOrCopyInt &m, return_value_policy r, handle p) {
|
||||
return pybind11::cast(m.value, r, p);
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct type_caster<CopyOnlyInt> {
|
||||
protected:
|
||||
CopyOnlyInt value;
|
||||
|
||||
public:
|
||||
static constexpr auto name = const_name("CopyOnlyInt");
|
||||
bool load(handle src, bool) {
|
||||
value = CopyOnlyInt(src.cast<int>());
|
||||
return true;
|
||||
}
|
||||
static handle cast(const CopyOnlyInt &m, return_value_policy r, handle p) {
|
||||
return pybind11::cast(m.value, r, p);
|
||||
}
|
||||
static handle cast(const CopyOnlyInt *src, return_value_policy policy, handle parent) {
|
||||
if (!src) {
|
||||
return none().release();
|
||||
}
|
||||
return cast(*src, policy, parent);
|
||||
}
|
||||
explicit operator CopyOnlyInt *() { return &value; }
|
||||
explicit operator CopyOnlyInt &() { return value; }
|
||||
template <typename T>
|
||||
using cast_op_type = pybind11::detail::cast_op_type<T>;
|
||||
};
|
||||
PYBIND11_NAMESPACE_END(detail)
|
||||
PYBIND11_NAMESPACE_END(pybind11)
|
||||
|
||||
TEST_SUBMODULE(copy_move_policies, m) {
|
||||
// test_lacking_copy_ctor
|
||||
py::class_<lacking_copy_ctor>(m, "lacking_copy_ctor")
|
||||
.def_static("get_one", &lacking_copy_ctor::get_one, py::return_value_policy::copy);
|
||||
// test_lacking_move_ctor
|
||||
py::class_<lacking_move_ctor>(m, "lacking_move_ctor")
|
||||
.def_static("get_one", &lacking_move_ctor::get_one, py::return_value_policy::move);
|
||||
|
||||
// test_move_and_copy_casts
|
||||
// NOLINTNEXTLINE(performance-unnecessary-value-param)
|
||||
m.def("move_and_copy_casts", [](const py::object &o) {
|
||||
int r = 0;
|
||||
r += py::cast<MoveOrCopyInt>(o).value; /* moves */
|
||||
r += py::cast<MoveOnlyInt>(o).value; /* moves */
|
||||
r += py::cast<CopyOnlyInt>(o).value; /* 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;
|
||||
});
|
||||
|
||||
// test_move_and_copy_loads
|
||||
m.def("move_only", [](MoveOnlyInt m) { return m.value; });
|
||||
// Changing this breaks the existing test: needs careful review.
|
||||
// NOLINTNEXTLINE(performance-unnecessary-value-param)
|
||||
m.def("move_or_copy", [](MoveOrCopyInt m) { return m.value; });
|
||||
// Changing this breaks the existing test: needs careful review.
|
||||
// NOLINTNEXTLINE(performance-unnecessary-value-param)
|
||||
m.def("copy_only", [](CopyOnlyInt m) { return m.value; });
|
||||
m.def("move_pair",
|
||||
[](std::pair<MoveOnlyInt, MoveOrCopyInt> p) { return p.first.value + p.second.value; });
|
||||
m.def("move_tuple", [](std::tuple<MoveOnlyInt, MoveOrCopyInt, MoveOnlyInt> t) {
|
||||
return std::get<0>(t).value + std::get<1>(t).value + std::get<2>(t).value;
|
||||
});
|
||||
m.def("copy_tuple", [](std::tuple<CopyOnlyInt, CopyOnlyInt> t) {
|
||||
return std::get<0>(t).value + std::get<1>(t).value;
|
||||
});
|
||||
m.def("move_copy_nested",
|
||||
[](std::pair<MoveOnlyInt,
|
||||
std::pair<std::tuple<MoveOrCopyInt, CopyOnlyInt, std::tuple<MoveOnlyInt>>,
|
||||
MoveOrCopyInt>> x) {
|
||||
return x.first.value + std::get<0>(x.second.first).value
|
||||
+ std::get<1>(x.second.first).value
|
||||
+ std::get<0>(std::get<2>(x.second.first)).value + x.second.second.value;
|
||||
});
|
||||
m.def("move_and_copy_cstats", []() {
|
||||
ConstructorStats::gc();
|
||||
// Reset counts to 0 so that previous tests don't affect later ones:
|
||||
auto &mc = ConstructorStats::get<MoveOrCopyInt>();
|
||||
mc.move_assignments = mc.move_constructions = mc.copy_assignments = mc.copy_constructions
|
||||
= 0;
|
||||
auto &mo = ConstructorStats::get<MoveOnlyInt>();
|
||||
mo.move_assignments = mo.move_constructions = mo.copy_assignments = mo.copy_constructions
|
||||
= 0;
|
||||
auto &co = ConstructorStats::get<CopyOnlyInt>();
|
||||
co.move_assignments = co.move_constructions = co.copy_assignments = co.copy_constructions
|
||||
= 0;
|
||||
py::dict d;
|
||||
d["MoveOrCopyInt"] = py::cast(mc, py::return_value_policy::reference);
|
||||
d["MoveOnlyInt"] = py::cast(mo, py::return_value_policy::reference);
|
||||
d["CopyOnlyInt"] = py::cast(co, py::return_value_policy::reference);
|
||||
return d;
|
||||
});
|
||||
#ifdef PYBIND11_HAS_OPTIONAL
|
||||
// test_move_and_copy_load_optional
|
||||
m.attr("has_optional") = true;
|
||||
m.def("move_optional", [](std::optional<MoveOnlyInt> o) { return o->value; });
|
||||
m.def("move_or_copy_optional", [](std::optional<MoveOrCopyInt> o) { return o->value; });
|
||||
m.def("copy_optional", [](std::optional<CopyOnlyInt> o) { return o->value; });
|
||||
m.def("move_optional_tuple",
|
||||
[](std::optional<std::tuple<MoveOrCopyInt, MoveOnlyInt, CopyOnlyInt>> x) {
|
||||
return std::get<0>(*x).value + std::get<1>(*x).value + std::get<2>(*x).value;
|
||||
});
|
||||
#else
|
||||
m.attr("has_optional") = false;
|
||||
#endif
|
||||
|
||||
// #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:
|
||||
void *operator new(size_t bytes) {
|
||||
void *ptr = std::malloc(bytes);
|
||||
if (ptr) {
|
||||
return ptr;
|
||||
}
|
||||
throw std::bad_alloc{};
|
||||
}
|
||||
};
|
||||
py::class_<PrivateOpNew>(m, "PrivateOpNew").def_readonly("value", &PrivateOpNew::value);
|
||||
m.def("private_op_new_value", []() { return PrivateOpNew(); });
|
||||
m.def(
|
||||
"private_op_new_reference",
|
||||
[]() -> const PrivateOpNew & {
|
||||
static PrivateOpNew x{};
|
||||
return x;
|
||||
},
|
||||
py::return_value_policy::reference);
|
||||
|
||||
// test_move_fallback
|
||||
// #389: rvp::move should fall-through to copy on non-movable objects
|
||||
struct MoveIssue1 {
|
||||
int v;
|
||||
explicit MoveIssue1(int v) : v{v} {}
|
||||
MoveIssue1(const MoveIssue1 &c) = default;
|
||||
MoveIssue1(MoveIssue1 &&) = delete;
|
||||
};
|
||||
py::class_<MoveIssue1>(m, "MoveIssue1")
|
||||
.def(py::init<int>())
|
||||
.def_readwrite("value", &MoveIssue1::v);
|
||||
|
||||
struct MoveIssue2 {
|
||||
int v;
|
||||
explicit MoveIssue2(int v) : v{v} {}
|
||||
MoveIssue2(MoveIssue2 &&) = default;
|
||||
};
|
||||
py::class_<MoveIssue2>(m, "MoveIssue2")
|
||||
.def(py::init<int>())
|
||||
.def_readwrite("value", &MoveIssue2::v);
|
||||
|
||||
// #2742: Don't expect ownership of raw pointer to `new`ed object to be transferred with
|
||||
// `py::return_value_policy::move`
|
||||
m.def(
|
||||
"get_moveissue1",
|
||||
[](int i) { return std::unique_ptr<MoveIssue1>(new MoveIssue1(i)); },
|
||||
py::return_value_policy::move);
|
||||
m.def(
|
||||
"get_moveissue2", [](int i) { return MoveIssue2(i); }, py::return_value_policy::move);
|
||||
|
||||
// Make sure that cast from pytype rvalue to other pytype works
|
||||
m.def("get_pytype_rvalue_castissue", [](double i) { return py::float_(i).cast<py::int_>(); });
|
||||
}
|
||||
130
third_party/pybind11/tests/test_copy_move.py
vendored
Normal file
130
third_party/pybind11/tests/test_copy_move.py
vendored
Normal file
@@ -0,0 +1,130 @@
|
||||
import pytest
|
||||
|
||||
from pybind11_tests import copy_move_policies as m
|
||||
|
||||
|
||||
def test_lacking_copy_ctor():
|
||||
with pytest.raises(RuntimeError) as excinfo:
|
||||
m.lacking_copy_ctor.get_one()
|
||||
assert "is non-copyable!" in str(excinfo.value)
|
||||
|
||||
|
||||
def test_lacking_move_ctor():
|
||||
with pytest.raises(RuntimeError) as excinfo:
|
||||
m.lacking_move_ctor.get_one()
|
||||
assert "is neither movable nor copyable!" in str(excinfo.value)
|
||||
|
||||
|
||||
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"], )
|
||||
|
||||
# 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
|
||||
# loading into an argument.
|
||||
assert m.move_and_copy_casts(3) == 18
|
||||
assert c_m.copy_assignments + c_m.copy_constructions == 0
|
||||
assert c_m.move_assignments == 2
|
||||
assert c_m.move_constructions >= 2
|
||||
assert c_mc.alive() == 0
|
||||
assert c_mc.copy_assignments + c_mc.copy_constructions == 0
|
||||
assert c_mc.move_assignments == 2
|
||||
assert c_mc.move_constructions >= 2
|
||||
assert c_c.alive() == 0
|
||||
assert c_c.copy_assignments == 2
|
||||
assert c_c.copy_constructions >= 2
|
||||
assert c_m.alive() + c_mc.alive() + c_c.alive() == 0
|
||||
|
||||
|
||||
def test_move_and_copy_loads():
|
||||
"""Call some functions that load arguments 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"], )
|
||||
|
||||
assert m.move_only(10) == 10 # 1 move, c_m
|
||||
assert m.move_or_copy(11) == 11 # 1 move, c_mc
|
||||
assert m.copy_only(12) == 12 # 1 copy, c_c
|
||||
assert m.move_pair((13, 14)) == 27 # 1 c_m move, 1 c_mc move
|
||||
assert m.move_tuple((15, 16, 17)) == 48 # 2 c_m moves, 1 c_mc move
|
||||
assert m.copy_tuple((18, 19)) == 37 # 2 c_c copies
|
||||
# Direct constructions: 2 c_m moves, 2 c_mc moves, 1 c_c copy
|
||||
# Extra moves/copies when moving pairs/tuples: 3 c_m, 3 c_mc, 2 c_c
|
||||
assert m.move_copy_nested((1, ((2, 3, (4, )), 5))) == 15
|
||||
|
||||
assert c_m.copy_assignments + c_m.copy_constructions == 0
|
||||
assert c_m.move_assignments == 6
|
||||
assert c_m.move_constructions == 9
|
||||
assert c_mc.copy_assignments + c_mc.copy_constructions == 0
|
||||
assert c_mc.move_assignments == 5
|
||||
assert c_mc.move_constructions == 8
|
||||
assert c_c.copy_assignments == 4
|
||||
assert c_c.copy_constructions == 6
|
||||
assert c_m.alive() + c_mc.alive() + c_c.alive() == 0
|
||||
|
||||
|
||||
@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"], )
|
||||
|
||||
# The extra move/copy constructions below come from the std::optional move (which has to move
|
||||
# its arguments):
|
||||
assert m.move_optional(10) == 10 # c_m: 1 move assign, 2 move construct
|
||||
assert m.move_or_copy_optional(
|
||||
11) == 11 # c_mc: 1 move assign, 2 move construct
|
||||
assert m.copy_optional(12) == 12 # c_c: 1 copy assign, 2 copy construct
|
||||
# 1 move assign + move construct moves each of c_m, c_mc, 1 c_c copy
|
||||
# +1 move/copy construct each from moving the tuple
|
||||
# +1 move/copy construct each from moving the optional (which moves the tuple again)
|
||||
assert m.move_optional_tuple((3, 4, 5)) == 12
|
||||
|
||||
assert c_m.copy_assignments + c_m.copy_constructions == 0
|
||||
assert c_m.move_assignments == 2
|
||||
assert c_m.move_constructions == 5
|
||||
assert c_mc.copy_assignments + c_mc.copy_constructions == 0
|
||||
assert c_mc.move_assignments == 2
|
||||
assert c_mc.move_constructions == 5
|
||||
assert c_c.copy_assignments == 2
|
||||
assert c_c.copy_constructions == 5
|
||||
assert c_m.alive() + c_mc.alive() + c_c.alive() == 0
|
||||
|
||||
|
||||
def test_private_op_new():
|
||||
"""An object with a private `operator new` cannot be returned by value"""
|
||||
|
||||
with pytest.raises(RuntimeError) as excinfo:
|
||||
m.private_op_new_value()
|
||||
assert "is neither movable nor copyable" in str(excinfo.value)
|
||||
|
||||
assert m.private_op_new_reference().value == 1
|
||||
|
||||
|
||||
def test_move_fallback():
|
||||
"""#389: rvp::move should fall-through to copy on non-movable objects"""
|
||||
|
||||
m1 = m.get_moveissue1(1)
|
||||
assert m1.value == 1
|
||||
m2 = m.get_moveissue2(2)
|
||||
assert m2.value == 2
|
||||
|
||||
|
||||
def test_pytype_rvalue_cast():
|
||||
"""Make sure that cast from pytype rvalue to other pytype works"""
|
||||
|
||||
value = m.get_pytype_rvalue_castissue(1.0)
|
||||
assert value == 1
|
||||
209
third_party/pybind11/tests/test_custom_type_casters.cpp
vendored
Normal file
209
third_party/pybind11/tests/test_custom_type_casters.cpp
vendored
Normal file
@@ -0,0 +1,209 @@
|
||||
/*
|
||||
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 "constructor_stats.h"
|
||||
#include "pybind11_tests.h"
|
||||
|
||||
// py::arg/py::arg_v testing: these arguments just record their argument when invoked
|
||||
class ArgInspector1 {
|
||||
public:
|
||||
std::string arg = "(default arg inspector 1)";
|
||||
};
|
||||
class ArgInspector2 {
|
||||
public:
|
||||
std::string arg = "(default arg inspector 2)";
|
||||
};
|
||||
class ArgAlwaysConverts {};
|
||||
|
||||
namespace pybind11 {
|
||||
namespace detail {
|
||||
template <>
|
||||
struct type_caster<ArgInspector1> {
|
||||
public:
|
||||
// Classic
|
||||
#ifdef PYBIND11_DETAIL_UNDERSCORE_BACKWARD_COMPATIBILITY
|
||||
PYBIND11_TYPE_CASTER(ArgInspector1, _("ArgInspector1"));
|
||||
#else
|
||||
PYBIND11_TYPE_CASTER(ArgInspector1, const_name("ArgInspector1"));
|
||||
#endif
|
||||
|
||||
bool load(handle src, bool convert) {
|
||||
value.arg = "loading ArgInspector1 argument " + std::string(convert ? "WITH" : "WITHOUT")
|
||||
+ " conversion allowed. "
|
||||
"Argument value = "
|
||||
+ (std::string) str(src);
|
||||
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, const_name("ArgInspector2"));
|
||||
|
||||
bool load(handle src, bool convert) {
|
||||
value.arg = "loading ArgInspector2 argument " + std::string(convert ? "WITH" : "WITHOUT")
|
||||
+ " conversion allowed. "
|
||||
"Argument value = "
|
||||
+ (std::string) str(src);
|
||||
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, const_name("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 &&) noexcept { print_move_created(this); }
|
||||
DestructionTester &operator=(const DestructionTester &) {
|
||||
print_copy_assigned(this);
|
||||
return *this;
|
||||
}
|
||||
DestructionTester &operator=(DestructionTester &&) noexcept {
|
||||
print_move_assigned(this);
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
namespace pybind11 {
|
||||
namespace detail {
|
||||
template <>
|
||||
struct type_caster<DestructionTester> {
|
||||
PYBIND11_TYPE_CASTER(DestructionTester, const_name("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
|
||||
|
||||
// Define type caster outside of `pybind11::detail` and then alias it.
|
||||
namespace other_lib {
|
||||
struct MyType {};
|
||||
// Corrupt `py` shorthand alias for surrounding context.
|
||||
namespace py {}
|
||||
// Corrupt unqualified relative `pybind11` namespace.
|
||||
namespace pybind11 {}
|
||||
// Correct alias.
|
||||
namespace py_ = ::pybind11;
|
||||
// Define caster. This is effectively no-op, we only ensure it compiles and we
|
||||
// don't have any symbol collision when using macro mixin.
|
||||
struct my_caster {
|
||||
PYBIND11_TYPE_CASTER(MyType, py_::detail::const_name("MyType"));
|
||||
bool load(py_::handle, bool) { return true; }
|
||||
|
||||
static py_::handle cast(const MyType &, py_::return_value_policy, py_::handle) {
|
||||
return py_::bool_(true).release();
|
||||
}
|
||||
};
|
||||
} // namespace other_lib
|
||||
// Effectively "alias" it into correct namespace (via inheritance).
|
||||
namespace pybind11 {
|
||||
namespace detail {
|
||||
template <>
|
||||
struct type_caster<other_lib::MyType> : public other_lib::my_caster {};
|
||||
} // 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(const ArgInspector1 &a,
|
||||
const ArgInspector1 &b,
|
||||
int c,
|
||||
ArgInspector2 *d,
|
||||
ArgAlwaysConverts) {
|
||||
return a.arg + "\n" + b.arg + "\n" + std::to_string(c) + "\n" + d->arg;
|
||||
}
|
||||
static ArgInspector2 h(ArgInspector2 a, ArgAlwaysConverts) { return a; }
|
||||
};
|
||||
// [workaround(intel)] ICC 20/21 breaks with py::arg().stuff, using py::arg{}.stuff works.
|
||||
py::class_<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",
|
||||
[](const ArgInspector2 &a, const ArgInspector1 &b, ArgAlwaysConverts) {
|
||||
return a.arg + "\n" + b.arg;
|
||||
},
|
||||
py::arg{}.noconvert(false),
|
||||
py::arg_v(nullptr, ArgInspector1()).noconvert(true),
|
||||
py::arg() = ArgAlwaysConverts());
|
||||
|
||||
m.def(
|
||||
"floats_preferred", [](double f) { return 0.5 * f; }, "f"_a);
|
||||
m.def(
|
||||
"floats_only", [](double f) { return 0.5 * f; }, "f"_a.noconvert());
|
||||
m.def(
|
||||
"ints_preferred", [](int i) { return i / 2; }, "i"_a);
|
||||
m.def(
|
||||
"ints_only", [](int i) { return i / 2; }, "i"_a.noconvert());
|
||||
|
||||
// test_custom_caster_destruction
|
||||
// Test that `take_ownership` works on types with a custom type caster when given a pointer
|
||||
|
||||
// default policy: don't take ownership:
|
||||
m.def("custom_caster_no_destroy", []() {
|
||||
static auto *dt = new DestructionTester();
|
||||
return dt;
|
||||
});
|
||||
|
||||
m.def(
|
||||
"custom_caster_destroy",
|
||||
[]() { return new DestructionTester(); },
|
||||
py::return_value_policy::take_ownership); // Takes ownership: destroy when finished
|
||||
m.def(
|
||||
"custom_caster_destroy_const",
|
||||
[]() -> const DestructionTester * { return new DestructionTester(); },
|
||||
py::return_value_policy::take_ownership); // Likewise (const doesn't inhibit destruction)
|
||||
m.def("destruction_tester_cstats",
|
||||
&ConstructorStats::get<DestructionTester>,
|
||||
py::return_value_policy::reference);
|
||||
|
||||
m.def("other_lib_type", [](other_lib::MyType x) { return x; });
|
||||
}
|
||||
96
third_party/pybind11/tests/test_custom_type_casters.py
vendored
Normal file
96
third_party/pybind11/tests/test_custom_type_casters.py
vendored
Normal file
@@ -0,0 +1,96 @@
|
||||
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)
|
||||
""")
|
||||
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)
|
||||
""")
|
||||
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
|
||||
""")
|
||||
|
||||
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
|
||||
|
||||
|
||||
def test_custom_caster_other_lib():
|
||||
assert m.other_lib_type(True)
|
||||
41
third_party/pybind11/tests/test_custom_type_setup.cpp
vendored
Normal file
41
third_party/pybind11/tests/test_custom_type_setup.cpp
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
tests/test_custom_type_setup.cpp -- Tests `pybind11::custom_type_setup`
|
||||
|
||||
Copyright (c) Google LLC
|
||||
|
||||
All rights reserved. Use of this source code is governed by a
|
||||
BSD-style license that can be found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include <pybind11/pybind11.h>
|
||||
|
||||
#include "pybind11_tests.h"
|
||||
|
||||
namespace py = pybind11;
|
||||
|
||||
namespace {
|
||||
|
||||
struct OwnsPythonObjects {
|
||||
py::object value = py::none();
|
||||
};
|
||||
} // namespace
|
||||
|
||||
TEST_SUBMODULE(custom_type_setup, m) {
|
||||
py::class_<OwnsPythonObjects> cls(
|
||||
m, "OwnsPythonObjects", py::custom_type_setup([](PyHeapTypeObject *heap_type) {
|
||||
auto *type = &heap_type->ht_type;
|
||||
type->tp_flags |= Py_TPFLAGS_HAVE_GC;
|
||||
type->tp_traverse = [](PyObject *self_base, visitproc visit, void *arg) {
|
||||
auto &self = py::cast<OwnsPythonObjects &>(py::handle(self_base));
|
||||
Py_VISIT(self.value.ptr());
|
||||
return 0;
|
||||
};
|
||||
type->tp_clear = [](PyObject *self_base) {
|
||||
auto &self = py::cast<OwnsPythonObjects &>(py::handle(self_base));
|
||||
self.value = py::none();
|
||||
return 0;
|
||||
};
|
||||
}));
|
||||
cls.def(py::init<>());
|
||||
cls.def_readwrite("value", &OwnsPythonObjects::value);
|
||||
}
|
||||
48
third_party/pybind11/tests/test_custom_type_setup.py
vendored
Normal file
48
third_party/pybind11/tests/test_custom_type_setup.py
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
import gc
|
||||
import weakref
|
||||
|
||||
import pytest
|
||||
|
||||
import env # noqa: F401
|
||||
from pybind11_tests import custom_type_setup as m
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def gc_tester():
|
||||
"""Tests that an object is garbage collected.
|
||||
|
||||
Assumes that any unreferenced objects are fully collected after calling
|
||||
`gc.collect()`. That is true on CPython, but does not appear to reliably
|
||||
hold on PyPy.
|
||||
"""
|
||||
|
||||
weak_refs = []
|
||||
|
||||
def add_ref(obj):
|
||||
# PyPy does not support `gc.is_tracked`.
|
||||
if hasattr(gc, "is_tracked"):
|
||||
assert gc.is_tracked(obj)
|
||||
weak_refs.append(weakref.ref(obj))
|
||||
|
||||
yield add_ref
|
||||
|
||||
gc.collect()
|
||||
for ref in weak_refs:
|
||||
assert ref() is None
|
||||
|
||||
|
||||
# PyPy does not seem to reliably garbage collect.
|
||||
@pytest.mark.skipif("env.PYPY")
|
||||
def test_self_cycle(gc_tester):
|
||||
obj = m.OwnsPythonObjects()
|
||||
obj.value = obj
|
||||
gc_tester(obj)
|
||||
|
||||
|
||||
# PyPy does not seem to reliably garbage collect.
|
||||
@pytest.mark.skipif("env.PYPY")
|
||||
def test_indirect_cycle(gc_tester):
|
||||
obj = m.OwnsPythonObjects()
|
||||
obj_list = [obj]
|
||||
obj.value = obj_list
|
||||
gc_tester(obj)
|
||||
88
third_party/pybind11/tests/test_docstring_options.cpp
vendored
Normal file
88
third_party/pybind11/tests/test_docstring_options.cpp
vendored
Normal file
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
tests/test_docstring_options.cpp -- generation of docstrings and signatures
|
||||
|
||||
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"
|
||||
|
||||
TEST_SUBMODULE(docstring_options, m) {
|
||||
// test_docstring_options
|
||||
{
|
||||
py::options options;
|
||||
options.disable_function_signatures();
|
||||
|
||||
m.def(
|
||||
"test_function1", [](int, int) {}, py::arg("a"), py::arg("b"));
|
||||
m.def(
|
||||
"test_function2", [](int, int) {}, py::arg("a"), py::arg("b"), "A custom docstring");
|
||||
|
||||
m.def(
|
||||
"test_overloaded1", [](int) {}, py::arg("i"), "Overload docstring");
|
||||
m.def(
|
||||
"test_overloaded1", [](double) {}, py::arg("d"));
|
||||
|
||||
m.def(
|
||||
"test_overloaded2", [](int) {}, py::arg("i"), "overload docstring 1");
|
||||
m.def(
|
||||
"test_overloaded2", [](double) {}, py::arg("d"), "overload docstring 2");
|
||||
|
||||
m.def(
|
||||
"test_overloaded3", [](int) {}, py::arg("i"));
|
||||
m.def(
|
||||
"test_overloaded3", [](double) {}, py::arg("d"), "Overload docstr");
|
||||
|
||||
options.enable_function_signatures();
|
||||
|
||||
m.def(
|
||||
"test_function3", [](int, int) {}, py::arg("a"), py::arg("b"));
|
||||
m.def(
|
||||
"test_function4", [](int, int) {}, py::arg("a"), py::arg("b"), "A custom docstring");
|
||||
|
||||
options.disable_function_signatures().disable_user_defined_docstrings();
|
||||
|
||||
m.def(
|
||||
"test_function5", [](int, int) {}, py::arg("a"), py::arg("b"), "A custom docstring");
|
||||
|
||||
{
|
||||
py::options nested_options;
|
||||
nested_options.enable_user_defined_docstrings();
|
||||
m.def(
|
||||
"test_function6",
|
||||
[](int, int) {},
|
||||
py::arg("a"),
|
||||
py::arg("b"),
|
||||
"A custom docstring");
|
||||
}
|
||||
}
|
||||
|
||||
m.def(
|
||||
"test_function7", [](int, int) {}, py::arg("a"), py::arg("b"), "A custom docstring");
|
||||
|
||||
{
|
||||
py::options options;
|
||||
options.disable_user_defined_docstrings();
|
||||
options.disable_function_signatures();
|
||||
|
||||
m.def("test_function8", []() {});
|
||||
}
|
||||
|
||||
{
|
||||
py::options options;
|
||||
options.disable_user_defined_docstrings();
|
||||
|
||||
struct DocstringTestFoo {
|
||||
int value;
|
||||
void setValue(int v) { value = v; }
|
||||
int getValue() const { return value; }
|
||||
};
|
||||
py::class_<DocstringTestFoo>(m, "DocstringTestFoo", "This is a class docstring")
|
||||
.def_property("value_prop",
|
||||
&DocstringTestFoo::getValue,
|
||||
&DocstringTestFoo::setValue,
|
||||
"This is a property docstring");
|
||||
}
|
||||
}
|
||||
44
third_party/pybind11/tests/test_docstring_options.py
vendored
Normal file
44
third_party/pybind11/tests/test_docstring_options.py
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
from pybind11_tests import docstring_options as m
|
||||
|
||||
|
||||
def test_docstring_options():
|
||||
# options.disable_function_signatures()
|
||||
assert not m.test_function1.__doc__
|
||||
|
||||
assert m.test_function2.__doc__ == "A custom docstring"
|
||||
|
||||
# docstring specified on just the first overload definition:
|
||||
assert m.test_overloaded1.__doc__ == "Overload docstring"
|
||||
|
||||
# docstring on both overloads:
|
||||
assert m.test_overloaded2.__doc__ == "overload docstring 1\noverload docstring 2"
|
||||
|
||||
# docstring on only second overload:
|
||||
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_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()
|
||||
assert not m.test_function5.__doc__
|
||||
|
||||
# nested options.enable_user_defined_docstrings()
|
||||
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")
|
||||
|
||||
# when all options are disabled, no docstring (instead of an empty one) should be generated
|
||||
assert m.test_function8.__doc__ is None
|
||||
|
||||
# Suppression of user-defined docstrings for non-function objects
|
||||
assert not m.DocstringTestFoo.__doc__
|
||||
assert not m.DocstringTestFoo.value_prop.__doc__
|
||||
398
third_party/pybind11/tests/test_eigen.cpp
vendored
Normal file
398
third_party/pybind11/tests/test_eigen.cpp
vendored
Normal file
@@ -0,0 +1,398 @@
|
||||
/*
|
||||
tests/eigen.cpp -- automatic conversion of Eigen types
|
||||
|
||||
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/eigen.h>
|
||||
#include <pybind11/stl.h>
|
||||
|
||||
#include "constructor_stats.h"
|
||||
#include "pybind11_tests.h"
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
# pragma warning(disable : 4996) // C4996: std::unary_negation is deprecated
|
||||
#endif
|
||||
|
||||
#include <Eigen/Cholesky>
|
||||
|
||||
using MatrixXdR = Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>;
|
||||
|
||||
// Sets/resets a testing reference matrix to have values of 10*r + c, where r and c are the
|
||||
// (1-based) row/column number.
|
||||
template <typename M>
|
||||
void reset_ref(M &x) {
|
||||
for (int i = 0; i < x.rows(); i++) {
|
||||
for (int j = 0; j < x.cols(); j++) {
|
||||
x(i, j) = 11 + 10 * i + j;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Returns a static, column-major matrix
|
||||
Eigen::MatrixXd &get_cm() {
|
||||
static Eigen::MatrixXd *x;
|
||||
if (!x) {
|
||||
x = new Eigen::MatrixXd(3, 3);
|
||||
reset_ref(*x);
|
||||
}
|
||||
return *x;
|
||||
}
|
||||
// Likewise, but row-major
|
||||
MatrixXdR &get_rm() {
|
||||
static MatrixXdR *x;
|
||||
if (!x) {
|
||||
x = new MatrixXdR(3, 3);
|
||||
reset_ref(*x);
|
||||
}
|
||||
return *x;
|
||||
}
|
||||
// Resets the values of the static matrices returned by get_cm()/get_rm()
|
||||
void reset_refs() {
|
||||
reset_ref(get_cm());
|
||||
reset_ref(get_rm());
|
||||
}
|
||||
|
||||
// Returns element 2,1 from a matrix (used to test copy/nocopy)
|
||||
double get_elem(const Eigen::Ref<const Eigen::MatrixXd> &m) { return m(2, 1); };
|
||||
|
||||
// Returns a matrix with 10*r + 100*c added to each matrix element (to help test that the matrix
|
||||
// reference is referencing rows/columns correctly).
|
||||
template <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; // NOLINT(clang-analyzer-core.uninitialized.Assign)
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct CustomOperatorNew {
|
||||
CustomOperatorNew() = default;
|
||||
|
||||
Eigen::Matrix4d a = Eigen::Matrix4d::Zero();
|
||||
Eigen::Matrix4d b = Eigen::Matrix4d::Identity();
|
||||
|
||||
EIGEN_MAKE_ALIGNED_OPERATOR_NEW;
|
||||
};
|
||||
|
||||
TEST_SUBMODULE(eigen, m) {
|
||||
using FixedMatrixR = Eigen::Matrix<float, 5, 6, Eigen::RowMajor>;
|
||||
using FixedMatrixC = Eigen::Matrix<float, 5, 6>;
|
||||
using DenseMatrixR = Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>;
|
||||
using DenseMatrixC = Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic>;
|
||||
using FourRowMatrixC = Eigen::Matrix<float, 4, Eigen::Dynamic>;
|
||||
using FourColMatrixC = Eigen::Matrix<float, Eigen::Dynamic, 4>;
|
||||
using FourRowMatrixR = Eigen::Matrix<float, 4, Eigen::Dynamic>;
|
||||
using FourColMatrixR = Eigen::Matrix<float, Eigen::Dynamic, 4>;
|
||||
using SparseMatrixR = Eigen::SparseMatrix<float, Eigen::RowMajor>;
|
||||
using SparseMatrixC = Eigen::SparseMatrix<float>;
|
||||
|
||||
// various tests
|
||||
m.def("double_col", [](const Eigen::VectorXf &x) -> Eigen::VectorXf { return 2.0f * x; });
|
||||
m.def("double_row",
|
||||
[](const Eigen::RowVectorXf &x) -> Eigen::RowVectorXf { return 2.0f * x; });
|
||||
m.def("double_complex",
|
||||
[](const Eigen::VectorXcf &x) -> Eigen::VectorXcf { return 2.0f * x; });
|
||||
m.def("double_threec", [](py::EigenDRef<Eigen::Vector3f> x) { x *= 2; });
|
||||
m.def("double_threer", [](py::EigenDRef<Eigen::RowVector3f> x) { x *= 2; });
|
||||
m.def("double_mat_cm", [](const Eigen::MatrixXf &x) -> Eigen::MatrixXf { return 2.0f * x; });
|
||||
m.def("double_mat_rm", [](const DenseMatrixR &x) -> DenseMatrixR { return 2.0f * x; });
|
||||
|
||||
// test_eigen_ref_to_python
|
||||
// Different ways of passing via Eigen::Ref; the first and second are the Eigen-recommended
|
||||
m.def("cholesky1",
|
||||
[](const Eigen::Ref<MatrixXdR> &x) -> Eigen::MatrixXd { return x.llt().matrixL(); });
|
||||
m.def("cholesky2", [](const Eigen::Ref<const MatrixXdR> &x) -> Eigen::MatrixXd {
|
||||
return x.llt().matrixL();
|
||||
});
|
||||
m.def("cholesky3",
|
||||
[](const Eigen::Ref<MatrixXdR> &x) -> Eigen::MatrixXd { return x.llt().matrixL(); });
|
||||
m.def("cholesky4", [](const Eigen::Ref<const MatrixXdR> &x) -> Eigen::MatrixXd {
|
||||
return x.llt().matrixL();
|
||||
});
|
||||
|
||||
// test_eigen_ref_mutators
|
||||
// Mutators: these add some value to the given element using Eigen, but Eigen should be mapping
|
||||
// into the numpy array data and so the result should show up there. There are three versions:
|
||||
// one that works on a contiguous-row matrix (numpy's default), one for a contiguous-column
|
||||
// matrix, and one for any matrix.
|
||||
auto add_rm = [](Eigen::Ref<MatrixXdR> x, int r, int c, double v) { x(r, c) += v; };
|
||||
auto add_cm = [](Eigen::Ref<Eigen::MatrixXd> x, int r, int c, double v) { x(r, c) += v; };
|
||||
|
||||
// Mutators (Eigen maps into numpy variables):
|
||||
m.def("add_rm", add_rm); // Only takes row-contiguous
|
||||
m.def("add_cm", add_cm); // Only takes column-contiguous
|
||||
// Overloaded versions that will accept either row or column contiguous:
|
||||
m.def("add1", add_rm);
|
||||
m.def("add1", add_cm);
|
||||
m.def("add2", add_cm);
|
||||
m.def("add2", add_rm);
|
||||
// This one accepts a matrix of any stride:
|
||||
m.def("add_any",
|
||||
[](py::EigenDRef<Eigen::MatrixXd> x, int r, int c, double v) { x(r, c) += v; });
|
||||
|
||||
// Return mutable references (numpy maps into eigen variables)
|
||||
m.def("get_cm_ref", []() { return Eigen::Ref<Eigen::MatrixXd>(get_cm()); });
|
||||
m.def("get_rm_ref", []() { return Eigen::Ref<MatrixXdR>(get_rm()); });
|
||||
// The same references, but non-mutable (numpy maps into eigen variables, but is !writeable)
|
||||
m.def("get_cm_const_ref", []() { return Eigen::Ref<const Eigen::MatrixXd>(get_cm()); });
|
||||
m.def("get_rm_const_ref", []() { return Eigen::Ref<const MatrixXdR>(get_rm()); });
|
||||
|
||||
m.def("reset_refs", reset_refs); // Restores get_{cm,rm}_ref to original values
|
||||
|
||||
// Increments and returns ref to (same) matrix
|
||||
m.def(
|
||||
"incr_matrix",
|
||||
[](Eigen::Ref<Eigen::MatrixXd> m, double v) {
|
||||
m += Eigen::MatrixXd::Constant(m.rows(), m.cols(), v);
|
||||
return m;
|
||||
},
|
||||
py::return_value_policy::reference);
|
||||
|
||||
// Same, but accepts a matrix of any strides
|
||||
m.def(
|
||||
"incr_matrix_any",
|
||||
[](py::EigenDRef<Eigen::MatrixXd> m, double v) {
|
||||
m += Eigen::MatrixXd::Constant(m.rows(), m.cols(), v);
|
||||
return m;
|
||||
},
|
||||
py::return_value_policy::reference);
|
||||
|
||||
// Returns an eigen slice of even rows
|
||||
m.def(
|
||||
"even_rows",
|
||||
[](py::EigenDRef<Eigen::MatrixXd> m) {
|
||||
return py::EigenDMap<Eigen::MatrixXd>(
|
||||
m.data(),
|
||||
(m.rows() + 1) / 2,
|
||||
m.cols(),
|
||||
py::EigenDStride(m.outerStride(), 2 * m.innerStride()));
|
||||
},
|
||||
py::return_value_policy::reference);
|
||||
|
||||
// Returns an eigen slice of even columns
|
||||
m.def(
|
||||
"even_cols",
|
||||
[](py::EigenDRef<Eigen::MatrixXd> m) {
|
||||
return py::EigenDMap<Eigen::MatrixXd>(
|
||||
m.data(),
|
||||
m.rows(),
|
||||
(m.cols() + 1) / 2,
|
||||
py::EigenDStride(2 * m.outerStride(), m.innerStride()));
|
||||
},
|
||||
py::return_value_policy::reference);
|
||||
|
||||
// Returns diagonals: a vector-like object with an inner stride != 1
|
||||
m.def("diagonal", [](const Eigen::Ref<const Eigen::MatrixXd> &x) { return x.diagonal(); });
|
||||
m.def("diagonal_1",
|
||||
[](const Eigen::Ref<const Eigen::MatrixXd> &x) { return x.diagonal<1>(); });
|
||||
m.def("diagonal_n",
|
||||
[](const Eigen::Ref<const Eigen::MatrixXd> &x, int index) { return x.diagonal(index); });
|
||||
|
||||
// Return a block of a matrix (gives non-standard strides)
|
||||
m.def("block",
|
||||
[](const Eigen::Ref<const Eigen::MatrixXd> &x,
|
||||
int start_row,
|
||||
int start_col,
|
||||
int block_rows,
|
||||
int block_cols) { return x.block(start_row, start_col, block_rows, block_cols); });
|
||||
|
||||
// test_eigen_return_references, test_eigen_keepalive
|
||||
// return value referencing/copying tests:
|
||||
class ReturnTester {
|
||||
Eigen::MatrixXd mat = create();
|
||||
|
||||
public:
|
||||
ReturnTester() { print_created(this); }
|
||||
~ReturnTester() { print_destroyed(this); }
|
||||
static Eigen::MatrixXd create() { return Eigen::MatrixXd::Ones(10, 10); }
|
||||
// NOLINTNEXTLINE(readability-const-return-type)
|
||||
static const Eigen::MatrixXd createConst() { return Eigen::MatrixXd::Ones(10, 10); }
|
||||
Eigen::MatrixXd &get() { return mat; }
|
||||
Eigen::MatrixXd *getPtr() { return &mat; }
|
||||
const Eigen::MatrixXd &view() { return mat; }
|
||||
const Eigen::MatrixXd *viewPtr() { return &mat; }
|
||||
Eigen::Ref<Eigen::MatrixXd> ref() { return mat; }
|
||||
Eigen::Ref<const Eigen::MatrixXd> refConst() { return mat; }
|
||||
Eigen::Block<Eigen::MatrixXd> block(int r, int c, int nrow, int ncol) {
|
||||
return mat.block(r, c, nrow, ncol);
|
||||
}
|
||||
Eigen::Block<const Eigen::MatrixXd> blockConst(int r, int c, int nrow, int ncol) const {
|
||||
return mat.block(r, c, nrow, ncol);
|
||||
}
|
||||
py::EigenDMap<Eigen::Matrix2d> corners() {
|
||||
return py::EigenDMap<Eigen::Matrix2d>(
|
||||
mat.data(),
|
||||
py::EigenDStride(mat.outerStride() * (mat.outerSize() - 1),
|
||||
mat.innerStride() * (mat.innerSize() - 1)));
|
||||
}
|
||||
py::EigenDMap<const Eigen::Matrix2d> cornersConst() const {
|
||||
return py::EigenDMap<const Eigen::Matrix2d>(
|
||||
mat.data(),
|
||||
py::EigenDStride(mat.outerStride() * (mat.outerSize() - 1),
|
||||
mat.innerStride() * (mat.innerSize() - 1)));
|
||||
}
|
||||
};
|
||||
using rvp = py::return_value_policy;
|
||||
py::class_<ReturnTester>(m, "ReturnTester")
|
||||
.def(py::init<>())
|
||||
.def_static("create", &ReturnTester::create)
|
||||
.def_static("create_const", &ReturnTester::createConst)
|
||||
.def("get", &ReturnTester::get, rvp::reference_internal)
|
||||
.def("get_ptr", &ReturnTester::getPtr, rvp::reference_internal)
|
||||
.def("view", &ReturnTester::view, rvp::reference_internal)
|
||||
.def("view_ptr", &ReturnTester::view, rvp::reference_internal)
|
||||
.def("copy_get", &ReturnTester::get) // Default rvp: copy
|
||||
.def("copy_view", &ReturnTester::view) // "
|
||||
.def("ref", &ReturnTester::ref) // Default for Ref is to reference
|
||||
.def("ref_const", &ReturnTester::refConst) // Likewise, but const
|
||||
.def("ref_safe", &ReturnTester::ref, rvp::reference_internal)
|
||||
.def("ref_const_safe", &ReturnTester::refConst, rvp::reference_internal)
|
||||
.def("copy_ref", &ReturnTester::ref, rvp::copy)
|
||||
.def("copy_ref_const", &ReturnTester::refConst, rvp::copy)
|
||||
.def("block", &ReturnTester::block)
|
||||
.def("block_safe", &ReturnTester::block, rvp::reference_internal)
|
||||
.def("block_const", &ReturnTester::blockConst, rvp::reference_internal)
|
||||
.def("copy_block", &ReturnTester::block, rvp::copy)
|
||||
.def("corners", &ReturnTester::corners, rvp::reference_internal)
|
||||
.def("corners_const", &ReturnTester::cornersConst, rvp::reference_internal);
|
||||
|
||||
// test_special_matrix_objects
|
||||
// Returns a DiagonalMatrix with diagonal (1,2,3,...)
|
||||
m.def("incr_diag", [](int k) {
|
||||
Eigen::DiagonalMatrix<int, Eigen::Dynamic> m(k);
|
||||
for (int i = 0; i < k; i++) {
|
||||
m.diagonal()[i] = i + 1;
|
||||
}
|
||||
return m;
|
||||
});
|
||||
|
||||
// Returns a SelfAdjointView referencing the lower triangle of m
|
||||
m.def("symmetric_lower",
|
||||
[](const Eigen::MatrixXi &m) { return m.selfadjointView<Eigen::Lower>(); });
|
||||
// Returns a SelfAdjointView referencing the lower triangle of m
|
||||
m.def("symmetric_upper",
|
||||
[](const Eigen::MatrixXi &m) { return m.selfadjointView<Eigen::Upper>(); });
|
||||
|
||||
// Test matrix for various functions below.
|
||||
Eigen::MatrixXf mat(5, 6);
|
||||
mat << 0, 3, 0, 0, 0, 11, 22, 0, 0, 0, 17, 11, 7, 5, 0, 1, 0, 11, 0, 0, 0, 0, 0, 11, 0, 0, 14,
|
||||
0, 8, 11;
|
||||
|
||||
// test_fixed, and various other tests
|
||||
m.def("fixed_r", [mat]() -> FixedMatrixR { return FixedMatrixR(mat); });
|
||||
// Our Eigen does a hack which respects constness through the numpy writeable flag.
|
||||
// Therefore, the const return actually affects this type despite being an rvalue.
|
||||
// NOLINTNEXTLINE(readability-const-return-type)
|
||||
m.def("fixed_r_const", [mat]() -> const FixedMatrixR { return FixedMatrixR(mat); });
|
||||
m.def("fixed_c", [mat]() -> FixedMatrixC { return FixedMatrixC(mat); });
|
||||
m.def("fixed_copy_r", [](const FixedMatrixR &m) -> FixedMatrixR { return m; });
|
||||
m.def("fixed_copy_c", [](const FixedMatrixC &m) -> FixedMatrixC { return m; });
|
||||
// test_mutator_descriptors
|
||||
m.def("fixed_mutator_r", [](const Eigen::Ref<FixedMatrixR> &) {});
|
||||
m.def("fixed_mutator_c", [](const Eigen::Ref<FixedMatrixC> &) {});
|
||||
m.def("fixed_mutator_a", [](const py::EigenDRef<FixedMatrixC> &) {});
|
||||
// test_dense
|
||||
m.def("dense_r", [mat]() -> DenseMatrixR { return DenseMatrixR(mat); });
|
||||
m.def("dense_c", [mat]() -> DenseMatrixC { return DenseMatrixC(mat); });
|
||||
m.def("dense_copy_r", [](const DenseMatrixR &m) -> DenseMatrixR { return m; });
|
||||
m.def("dense_copy_c", [](const DenseMatrixC &m) -> DenseMatrixC { return m; });
|
||||
// test_sparse, test_sparse_signature
|
||||
m.def("sparse_r", [mat]() -> SparseMatrixR {
|
||||
// NOLINTNEXTLINE(clang-analyzer-core.uninitialized.UndefReturn)
|
||||
return Eigen::SparseView<Eigen::MatrixXf>(mat);
|
||||
});
|
||||
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; });
|
||||
// test_partially_fixed
|
||||
m.def("partial_copy_four_rm_r", [](const FourRowMatrixR &m) -> FourRowMatrixR { return m; });
|
||||
m.def("partial_copy_four_rm_c", [](const FourColMatrixR &m) -> FourColMatrixR { return m; });
|
||||
m.def("partial_copy_four_cm_r", [](const FourRowMatrixC &m) -> FourRowMatrixC { return m; });
|
||||
m.def("partial_copy_four_cm_c", [](const FourColMatrixC &m) -> FourColMatrixC { return m; });
|
||||
|
||||
// test_cpp_casting
|
||||
// Test that we can cast a numpy object to a Eigen::MatrixXd explicitly
|
||||
m.def("cpp_copy", [](py::handle m) { return m.cast<Eigen::MatrixXd>()(1, 0); });
|
||||
m.def("cpp_ref_c", [](py::handle m) { return m.cast<Eigen::Ref<Eigen::MatrixXd>>()(1, 0); });
|
||||
m.def("cpp_ref_r", [](py::handle m) { return m.cast<Eigen::Ref<MatrixXdR>>()(1, 0); });
|
||||
m.def("cpp_ref_any",
|
||||
[](py::handle m) { return m.cast<py::EigenDRef<Eigen::MatrixXd>>()(1, 0); });
|
||||
|
||||
// [workaround(intel)] ICC 20/21 breaks with py::arg().stuff, using py::arg{}.stuff works.
|
||||
|
||||
// test_nocopy_wrapper
|
||||
// Test that we can prevent copying into an argument that would normally copy: First a version
|
||||
// that would allow copying (if types or strides don't match) for comparison:
|
||||
m.def("get_elem", &get_elem);
|
||||
// Now this alternative that calls the tells pybind to fail rather than copy:
|
||||
m.def(
|
||||
"get_elem_nocopy",
|
||||
[](const Eigen::Ref<const Eigen::MatrixXd> &m) -> double { return get_elem(m); },
|
||||
py::arg{}.noconvert());
|
||||
// Also test a row-major-only no-copy const ref:
|
||||
m.def(
|
||||
"get_elem_rm_nocopy",
|
||||
[](Eigen::Ref<const Eigen::Matrix<long, -1, -1, Eigen::RowMajor>> &m) -> long {
|
||||
return m(2, 1);
|
||||
},
|
||||
py::arg{}.noconvert());
|
||||
|
||||
// test_issue738
|
||||
// Issue #738: 1xN or Nx1 2D matrices were neither accepted nor properly copied with an
|
||||
// incompatible stride value on the length-1 dimension--but that should be allowed (without
|
||||
// requiring a copy!) because the stride value can be safely ignored on a size-1 dimension.
|
||||
m.def("iss738_f1",
|
||||
&adjust_matrix<const Eigen::Ref<const Eigen::MatrixXd> &>,
|
||||
py::arg{}.noconvert());
|
||||
m.def("iss738_f2",
|
||||
&adjust_matrix<const Eigen::Ref<const Eigen::Matrix<double, -1, -1, Eigen::RowMajor>> &>,
|
||||
py::arg{}.noconvert());
|
||||
|
||||
// test_issue1105
|
||||
// Issue #1105: when converting from a numpy two-dimensional (Nx1) or (1xN) value into a dense
|
||||
// eigen Vector or RowVector, the argument would fail to load because the numpy copy would
|
||||
// fail: numpy won't broadcast a Nx1 into a 1-dimensional vector.
|
||||
m.def("iss1105_col", [](const Eigen::VectorXd &) { return true; });
|
||||
m.def("iss1105_row", [](const Eigen::RowVectorXd &) { return true; });
|
||||
|
||||
// test_named_arguments
|
||||
// Make sure named arguments are working properly:
|
||||
m.def(
|
||||
"matrix_multiply",
|
||||
[](const py::EigenDRef<const Eigen::MatrixXd> &A,
|
||||
const py::EigenDRef<const Eigen::MatrixXd> &B) -> Eigen::MatrixXd {
|
||||
if (A.cols() != B.rows()) {
|
||||
throw std::domain_error("Nonconformable matrices!");
|
||||
}
|
||||
return A * B;
|
||||
},
|
||||
py::arg("A"),
|
||||
py::arg("B"));
|
||||
|
||||
// test_custom_operator_new
|
||||
py::class_<CustomOperatorNew>(m, "CustomOperatorNew")
|
||||
.def(py::init<>())
|
||||
.def_readonly("a", &CustomOperatorNew::a)
|
||||
.def_readonly("b", &CustomOperatorNew::b);
|
||||
|
||||
// test_eigen_ref_life_support
|
||||
// In case of a failure (the caster's temp array does not live long enough), creating
|
||||
// a new array (np.ones(10)) increases the chances that the temp array will be garbage
|
||||
// collected and/or that its memory will be overridden with different values.
|
||||
m.def("get_elem_direct", [](const Eigen::Ref<const Eigen::VectorXd> &v) {
|
||||
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);
|
||||
return v[0](5);
|
||||
});
|
||||
}
|
||||
741
third_party/pybind11/tests/test_eigen.py
vendored
Normal file
741
third_party/pybind11/tests/test_eigen.py
vendored
Normal file
@@ -0,0 +1,741 @@
|
||||
import pytest
|
||||
|
||||
from pybind11_tests import ConstructorStats
|
||||
|
||||
np = pytest.importorskip("numpy")
|
||||
m = pytest.importorskip("pybind11_tests.eigen")
|
||||
|
||||
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):
|
||||
np.testing.assert_array_equal(mat, ref)
|
||||
|
||||
|
||||
def assert_sparse_equal_ref(sparse_mat):
|
||||
assert_equal_ref(sparse_mat.toarray())
|
||||
|
||||
|
||||
def test_fixed():
|
||||
assert_equal_ref(m.fixed_c())
|
||||
assert_equal_ref(m.fixed_r())
|
||||
assert_equal_ref(m.fixed_copy_r(m.fixed_r()))
|
||||
assert_equal_ref(m.fixed_copy_c(m.fixed_c()))
|
||||
assert_equal_ref(m.fixed_copy_r(m.fixed_c()))
|
||||
assert_equal_ref(m.fixed_copy_c(m.fixed_r()))
|
||||
|
||||
|
||||
def test_dense():
|
||||
assert_equal_ref(m.dense_r())
|
||||
assert_equal_ref(m.dense_c())
|
||||
assert_equal_ref(m.dense_copy_r(m.dense_r()))
|
||||
assert_equal_ref(m.dense_copy_c(m.dense_c()))
|
||||
assert_equal_ref(m.dense_copy_r(m.dense_c()))
|
||||
assert_equal_ref(m.dense_copy_c(m.dense_r()))
|
||||
|
||||
|
||||
def test_partially_fixed():
|
||||
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), :])
|
||||
|
||||
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), :])
|
||||
|
||||
# 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]]
|
||||
for f in functions:
|
||||
with pytest.raises(TypeError) as excinfo:
|
||||
f(matrix_with_wrong_shape)
|
||||
assert "incompatible function arguments" in str(excinfo.value)
|
||||
|
||||
|
||||
def test_mutator_descriptors():
|
||||
zr = np.arange(30, dtype="float32").reshape(5, 6) # row-major
|
||||
zc = zr.reshape(6, 5).transpose() # column-major
|
||||
|
||||
m.fixed_mutator_r(zr)
|
||||
m.fixed_mutator_c(zc)
|
||||
m.fixed_mutator_a(zr)
|
||||
m.fixed_mutator_a(zc)
|
||||
with pytest.raises(TypeError) as excinfo:
|
||||
m.fixed_mutator_r(zc)
|
||||
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[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[numpy.float32[5, 6], flags.writeable]) -> None" in str(
|
||||
excinfo.value)
|
||||
zr.flags.writeable = False
|
||||
with pytest.raises(TypeError):
|
||||
m.fixed_mutator_r(zr)
|
||||
with pytest.raises(TypeError):
|
||||
m.fixed_mutator_a(zr)
|
||||
|
||||
|
||||
def test_cpp_casting():
|
||||
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)
|
||||
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.0
|
||||
|
||||
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():
|
||||
z = np.full((5, 6), 42.0)
|
||||
z.flags.writeable = False
|
||||
np.testing.assert_array_equal(z, m.fixed_copy_r(z))
|
||||
np.testing.assert_array_equal(m.fixed_r_const(), m.fixed_r())
|
||||
assert not m.fixed_r_const().flags.writeable
|
||||
np.testing.assert_array_equal(
|
||||
m.fixed_copy_r(m.fixed_r_const()), m.fixed_r_const())
|
||||
|
||||
|
||||
def test_nonunit_stride_from_python():
|
||||
counting_mat = np.arange(9.0, dtype=np.float32).reshape((3, 3))
|
||||
second_row = counting_mat[1, :]
|
||||
second_col = counting_mat[:, 1]
|
||||
np.testing.assert_array_equal(m.double_row(second_row), 2.0 * second_row)
|
||||
np.testing.assert_array_equal(m.double_col(second_row), 2.0 * second_row)
|
||||
np.testing.assert_array_equal(
|
||||
m.double_complex(second_row), 2.0 * second_row)
|
||||
np.testing.assert_array_equal(m.double_row(second_col), 2.0 * second_col)
|
||||
np.testing.assert_array_equal(m.double_col(second_col), 2.0 * second_col)
|
||||
np.testing.assert_array_equal(
|
||||
m.double_complex(second_col), 2.0 * second_col)
|
||||
|
||||
counting_3d = np.arange(27.0, dtype=np.float32).reshape((3, 3, 3))
|
||||
slices = [counting_3d[0, :, :], counting_3d[:, 0, :], counting_3d[:, :, 0]]
|
||||
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.0, 2, 2], [6, 16, 10], [6, 14, 8]])
|
||||
|
||||
|
||||
def test_negative_stride_from_python(msg):
|
||||
"""Eigen doesn't support (as of yet) negative strides. When a function takes an Eigen matrix by
|
||||
copy or const reference, we can pass a numpy array that has negative strides. Otherwise, an
|
||||
exception will be thrown as Eigen will not be able to map the numpy array."""
|
||||
|
||||
counting_mat = np.arange(9.0, dtype=np.float32).reshape((3, 3))
|
||||
counting_mat = counting_mat[::-1, ::-1]
|
||||
second_row = counting_mat[1, :]
|
||||
second_col = counting_mat[:, 1]
|
||||
np.testing.assert_array_equal(m.double_row(second_row), 2.0 * second_row)
|
||||
np.testing.assert_array_equal(m.double_col(second_row), 2.0 * second_row)
|
||||
np.testing.assert_array_equal(
|
||||
m.double_complex(second_row), 2.0 * second_row)
|
||||
np.testing.assert_array_equal(m.double_row(second_col), 2.0 * second_col)
|
||||
np.testing.assert_array_equal(m.double_col(second_col), 2.0 * second_col)
|
||||
np.testing.assert_array_equal(
|
||||
m.double_complex(second_col), 2.0 * second_col)
|
||||
|
||||
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 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) == """
|
||||
double_threer(): incompatible function arguments. The following argument types are supported:
|
||||
1. (arg0: numpy.ndarray[numpy.float32[1, 3], flags.writeable]) -> None
|
||||
|
||||
Invoked with: """ + 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) == """
|
||||
double_threec(): incompatible function arguments. The following argument types are supported:
|
||||
1. (arg0: numpy.ndarray[numpy.float32[3, 1], flags.writeable]) -> None
|
||||
|
||||
Invoked with: """ + 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)), f"m.diagonal_n({i})"
|
||||
|
||||
assert np.all(m.block(ref, 2, 1, 3, 3) == ref[2:5, 1:4])
|
||||
assert np.all(m.block(ref, 1, 4, 4, 2) == ref[1:, 4:])
|
||||
assert np.all(m.block(ref, 1, 4, 3, 2) == ref[1:4, 4:])
|
||||
|
||||
|
||||
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.0, 2, 4], [2, 13, 23], [4, 23, 77]]))
|
||||
assert np.all(mymat == np.array(
|
||||
[[1, 0, 0], [2, 3, 0], [4, 5, 6]])), f"cholesky{i}"
|
||||
|
||||
|
||||
def assign_both(a1, a2, r, c, v):
|
||||
a1[r, c] = v
|
||||
a2[r, c] = v
|
||||
|
||||
|
||||
def array_copy_but_one(a, r, c, v):
|
||||
z = np.array(a, copy=True)
|
||||
z[r, c] = v
|
||||
return z
|
||||
|
||||
|
||||
def test_eigen_return_references():
|
||||
"""Tests various ways of returning references and non-referencing copies"""
|
||||
|
||||
master = np.ones((10, 10))
|
||||
a = m.ReturnTester()
|
||||
a_get1 = a.get()
|
||||
assert not a_get1.flags.owndata and a_get1.flags.writeable
|
||||
assign_both(a_get1, master, 3, 3, 5)
|
||||
a_get2 = a.get_ptr()
|
||||
assert not a_get2.flags.owndata and a_get2.flags.writeable
|
||||
assign_both(a_get1, master, 2, 3, 6)
|
||||
|
||||
a_view1 = a.view()
|
||||
assert not a_view1.flags.owndata and not a_view1.flags.writeable
|
||||
with pytest.raises(ValueError):
|
||||
a_view1[2, 3] = 4
|
||||
a_view2 = a.view_ptr()
|
||||
assert not a_view2.flags.owndata and not a_view2.flags.writeable
|
||||
with pytest.raises(ValueError):
|
||||
a_view2[2, 3] = 4
|
||||
|
||||
a_copy1 = a.copy_get()
|
||||
assert a_copy1.flags.owndata and a_copy1.flags.writeable
|
||||
np.testing.assert_array_equal(a_copy1, master)
|
||||
a_copy1[7, 7] = -44 # Shouldn't affect anything else
|
||||
c1want = array_copy_but_one(master, 7, 7, -44)
|
||||
a_copy2 = a.copy_view()
|
||||
assert a_copy2.flags.owndata and a_copy2.flags.writeable
|
||||
np.testing.assert_array_equal(a_copy2, master)
|
||||
a_copy2[4, 4] = -22 # Shouldn't affect anything else
|
||||
c2want = array_copy_but_one(master, 4, 4, -22)
|
||||
|
||||
a_ref1 = a.ref()
|
||||
assert not a_ref1.flags.owndata and a_ref1.flags.writeable
|
||||
assign_both(a_ref1, master, 1, 1, 15)
|
||||
a_ref2 = a.ref_const()
|
||||
assert not a_ref2.flags.owndata and not a_ref2.flags.writeable
|
||||
with pytest.raises(ValueError):
|
||||
a_ref2[5, 5] = 33
|
||||
a_ref3 = a.ref_safe()
|
||||
assert not a_ref3.flags.owndata and a_ref3.flags.writeable
|
||||
assign_both(a_ref3, master, 0, 7, 99)
|
||||
a_ref4 = a.ref_const_safe()
|
||||
assert not a_ref4.flags.owndata and not a_ref4.flags.writeable
|
||||
with pytest.raises(ValueError):
|
||||
a_ref4[7, 0] = 987654321
|
||||
|
||||
a_copy3 = a.copy_ref()
|
||||
assert a_copy3.flags.owndata and a_copy3.flags.writeable
|
||||
np.testing.assert_array_equal(a_copy3, master)
|
||||
a_copy3[8, 1] = 11
|
||||
c3want = array_copy_but_one(master, 8, 1, 11)
|
||||
a_copy4 = a.copy_ref_const()
|
||||
assert a_copy4.flags.owndata and a_copy4.flags.writeable
|
||||
np.testing.assert_array_equal(a_copy4, master)
|
||||
a_copy4[8, 4] = 88
|
||||
c4want = array_copy_but_one(master, 8, 4, 88)
|
||||
|
||||
a_block1 = a.block(3, 3, 2, 2)
|
||||
assert not a_block1.flags.owndata and a_block1.flags.writeable
|
||||
a_block1[0, 0] = 55
|
||||
master[3, 3] = 55
|
||||
a_block2 = a.block_safe(2, 2, 3, 2)
|
||||
assert not a_block2.flags.owndata and a_block2.flags.writeable
|
||||
a_block2[2, 1] = -123
|
||||
master[4, 3] = -123
|
||||
a_block3 = a.block_const(6, 7, 4, 3)
|
||||
assert not a_block3.flags.owndata and not a_block3.flags.writeable
|
||||
with pytest.raises(ValueError):
|
||||
a_block3[2, 2] = -44444
|
||||
|
||||
a_copy5 = a.copy_block(2, 2, 2, 3)
|
||||
assert a_copy5.flags.owndata and a_copy5.flags.writeable
|
||||
np.testing.assert_array_equal(a_copy5, master[2:4, 2:5])
|
||||
a_copy5[1, 1] = 777
|
||||
c5want = array_copy_but_one(master[2:4, 2:5], 1, 1, 777)
|
||||
|
||||
a_corn1 = a.corners()
|
||||
assert not a_corn1.flags.owndata and a_corn1.flags.writeable
|
||||
a_corn1 *= 50
|
||||
a_corn1[1, 1] = 999
|
||||
master[0, 0] = 50
|
||||
master[0, 9] = 50
|
||||
master[9, 0] = 50
|
||||
master[9, 9] = 999
|
||||
a_corn2 = a.corners_const()
|
||||
assert not a_corn2.flags.owndata and not a_corn2.flags.writeable
|
||||
with pytest.raises(ValueError):
|
||||
a_corn2[1, 0] = 51
|
||||
|
||||
# All of the changes made all the way along should be visible everywhere
|
||||
# now (except for the copies, of course)
|
||||
np.testing.assert_array_equal(a_get1, master)
|
||||
np.testing.assert_array_equal(a_get2, master)
|
||||
np.testing.assert_array_equal(a_view1, master)
|
||||
np.testing.assert_array_equal(a_view2, master)
|
||||
np.testing.assert_array_equal(a_ref1, master)
|
||||
np.testing.assert_array_equal(a_ref2, master)
|
||||
np.testing.assert_array_equal(a_ref3, master)
|
||||
np.testing.assert_array_equal(a_ref4, master)
|
||||
np.testing.assert_array_equal(a_block1, master[3:5, 3:5])
|
||||
np.testing.assert_array_equal(a_block2, master[2:5, 2:4])
|
||||
np.testing.assert_array_equal(a_block3, master[6:10, 7:10])
|
||||
np.testing.assert_array_equal(
|
||||
a_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)
|
||||
np.testing.assert_array_equal(a_copy3, c3want)
|
||||
np.testing.assert_array_equal(a_copy4, c4want)
|
||||
np.testing.assert_array_equal(a_copy5, c5want)
|
||||
|
||||
|
||||
def assert_keeps_alive(cl, method, *args):
|
||||
cstats = ConstructorStats.get(cl)
|
||||
start_with = cstats.alive()
|
||||
a = cl()
|
||||
assert cstats.alive() == start_with + 1
|
||||
z = method(a, *args)
|
||||
assert cstats.alive() == start_with + 1
|
||||
del a
|
||||
# Here's the keep alive in action:
|
||||
assert cstats.alive() == start_with + 1
|
||||
del z
|
||||
# Keep alive should have expired:
|
||||
assert cstats.alive() == start_with
|
||||
|
||||
|
||||
def test_eigen_keepalive():
|
||||
a = m.ReturnTester()
|
||||
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),
|
||||
]
|
||||
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,
|
||||
]:
|
||||
assert_keeps_alive(m.ReturnTester, meth)
|
||||
|
||||
for meth in [m.ReturnTester.block_safe, m.ReturnTester.block_const]:
|
||||
assert_keeps_alive(m.ReturnTester, meth, 4, 3, 2, 1)
|
||||
|
||||
|
||||
def test_eigen_ref_mutators():
|
||||
"""Tests Eigen's ability to mutate numpy values"""
|
||||
|
||||
orig = np.array([[1.0, 2, 3], [4, 5, 6], [7, 8, 9]])
|
||||
zr = np.array(orig)
|
||||
zc = np.array(orig, order="F")
|
||||
m.add_rm(zr, 1, 0, 100)
|
||||
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.0, 2, 3], [204, 5, 6], [7, 8, 9]]))
|
||||
|
||||
m.add_any(zr, 1, 0, 20)
|
||||
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.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):
|
||||
m.add_rm(zc, 1, 0, 1)
|
||||
with pytest.raises(TypeError):
|
||||
m.add_cm(zr, 1, 0, 1)
|
||||
|
||||
# Overloads:
|
||||
m.add1(zr, 1, 0, -100)
|
||||
m.add2(zr, 1, 0, -20)
|
||||
assert np.all(zr == orig)
|
||||
m.add1(zc, 1, 0, -200)
|
||||
m.add2(zc, 1, 0, -10)
|
||||
assert np.all(zc == orig)
|
||||
|
||||
# a non-contiguous slice (this won't work on either the row- or
|
||||
# column-contiguous refs, but should work for the any)
|
||||
cornersr = zr[0::2, 0::2]
|
||||
cornersc = zc[0::2, 0::2]
|
||||
|
||||
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)
|
||||
with pytest.raises(TypeError):
|
||||
m.add_cm(cornersr, 0, 1, 25)
|
||||
with pytest.raises(TypeError):
|
||||
m.add_rm(cornersc, 0, 1, 25)
|
||||
with pytest.raises(TypeError):
|
||||
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.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]
|
||||
zro.flags.writeable = False
|
||||
with pytest.raises(TypeError):
|
||||
m.add_rm(zro, 0, 0, 0)
|
||||
with pytest.raises(TypeError):
|
||||
m.add_any(zro, 0, 0, 0)
|
||||
with pytest.raises(TypeError):
|
||||
m.add1(zro, 0, 0, 0)
|
||||
with pytest.raises(TypeError):
|
||||
m.add2(zro, 0, 0, 0)
|
||||
|
||||
# integer array shouldn't be passable to a double-matrix-accepting mutating func:
|
||||
zi = np.array([[1, 2], [3, 4]])
|
||||
with pytest.raises(TypeError):
|
||||
m.add_rm(zi)
|
||||
|
||||
|
||||
def test_numpy_ref_mutators():
|
||||
"""Tests numpy mutating Eigen matrices (for returned Eigen::Ref<...>s)"""
|
||||
|
||||
m.reset_refs() # In case another test already changed it
|
||||
|
||||
zc = m.get_cm_ref()
|
||||
zcro = m.get_cm_const_ref()
|
||||
zr = m.get_rm_ref()
|
||||
zrro = m.get_rm_const_ref()
|
||||
|
||||
assert [zc[1, 2], zcro[1, 2], zr[1, 2], zrro[1, 2]] == [23] * 4
|
||||
|
||||
assert not zc.flags.owndata and zc.flags.writeable
|
||||
assert not zr.flags.owndata and zr.flags.writeable
|
||||
assert not zcro.flags.owndata and not zcro.flags.writeable
|
||||
assert not zrro.flags.owndata and not zrro.flags.writeable
|
||||
|
||||
zc[1, 2] = 99
|
||||
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)
|
||||
assert np.all(m.get_cm_ref() == expect)
|
||||
|
||||
zr[1, 2] = 99
|
||||
assert np.all(zr == expect)
|
||||
assert np.all(zrro == expect)
|
||||
assert np.all(m.get_rm_ref() == expect)
|
||||
|
||||
# Make sure the readonly ones are numpy-readonly:
|
||||
with pytest.raises(ValueError):
|
||||
zcro[1, 2] = 6
|
||||
with pytest.raises(ValueError):
|
||||
zrro[1, 2] = 6
|
||||
|
||||
# We should be able to explicitly copy like this (and since we're copying,
|
||||
# the const should drop away)
|
||||
y1 = np.array(m.get_cm_const_ref())
|
||||
|
||||
assert y1.flags.owndata and y1.flags.writeable
|
||||
# We should get copies of the eigen data, which was modified above:
|
||||
assert y1[1, 2] == 99
|
||||
y1[1, 2] += 12
|
||||
assert y1[1, 2] == 111
|
||||
assert zc[1, 2] == 99 # Make sure we aren't referencing the original
|
||||
|
||||
|
||||
def test_both_ref_mutators():
|
||||
"""Tests a complex chain of nested eigen/numpy references"""
|
||||
|
||||
m.reset_refs() # In case another test already changed it
|
||||
|
||||
z = m.get_cm_ref() # numpy -> eigen
|
||||
z[0, 2] -= 3
|
||||
z2 = m.incr_matrix(z, 1) # numpy -> eigen -> numpy -> eigen
|
||||
z2[1, 1] += 6
|
||||
z3 = m.incr_matrix(z, 2) # (numpy -> eigen)^3
|
||||
z3[2, 2] += -5
|
||||
z4 = m.incr_matrix(z, 3) # (numpy -> eigen)^4
|
||||
z4[1, 1] -= 1
|
||||
z5 = m.incr_matrix(z, 4) # (numpy -> eigen)^5
|
||||
z5[0, 0] = 0
|
||||
assert np.all(z == z2)
|
||||
assert np.all(z == z3)
|
||||
assert np.all(z == z4)
|
||||
assert np.all(z == z5)
|
||||
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)
|
||||
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
|
||||
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 += 10
|
||||
yexpect[0::2, 0::2] -= 33
|
||||
yexpect[0::4, 0::4] += 1000
|
||||
assert np.all(y6 == yexpect[0::4, 0::4])
|
||||
assert np.all(y5 == yexpect[0::4, 0::4])
|
||||
assert np.all(y4 == yexpect[0::4, 0::2])
|
||||
assert np.all(y3 == yexpect[0::2, 0::2])
|
||||
assert np.all(y2 == yexpect)
|
||||
assert np.all(y == yexpect)
|
||||
|
||||
|
||||
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)
|
||||
|
||||
# All should be callable via get_elem:
|
||||
assert m.get_elem(int_matrix_colmajor) == 8
|
||||
assert m.get_elem(dbl_matrix_colmajor) == 8
|
||||
assert m.get_elem(int_matrix_rowmajor) == 8
|
||||
assert m.get_elem(dbl_matrix_rowmajor) == 8
|
||||
|
||||
# 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 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)
|
||||
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)
|
||||
|
||||
# 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)
|
||||
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 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)
|
||||
|
||||
|
||||
def test_eigen_ref_life_support():
|
||||
"""Ensure the lifetime of temporary arrays created by the `Ref` caster
|
||||
|
||||
The `Ref` caster sometimes creates a copy which needs to stay alive. This needs to
|
||||
happen both for directs casts (just the array) or indirectly (e.g. list of arrays).
|
||||
"""
|
||||
|
||||
a = np.full(shape=10, fill_value=8, dtype=np.int8)
|
||||
assert m.get_elem_direct(a) == 8
|
||||
|
||||
list_of_a = [a]
|
||||
assert m.get_elem_indirect(list_of_a) == 8
|
||||
|
||||
|
||||
def test_special_matrix_objects():
|
||||
assert np.all(m.incr_diag(7) == np.diag([1.0, 2, 3, 4, 5, 6, 7]))
|
||||
|
||||
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):
|
||||
for j in range(i + 1, 4):
|
||||
symm_lower[i, j] = symm_lower[j, i]
|
||||
symm_upper[j, i] = symm_upper[i, j]
|
||||
|
||||
assert np.all(m.symmetric_lower(asymm) == symm_lower)
|
||||
assert np.all(m.symmetric_upper(asymm) == symm_upper)
|
||||
|
||||
|
||||
def test_dense_signature(doc):
|
||||
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[numpy.float32[1, n]]) -> numpy.ndarray[numpy.float32[1, n]]
|
||||
""")
|
||||
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[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.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!"
|
||||
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
m.matrix_multiply(A=b, B=a)
|
||||
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!"
|
||||
|
||||
|
||||
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()))
|
||||
assert_sparse_equal_ref(m.sparse_copy_c(m.sparse_c()))
|
||||
assert_sparse_equal_ref(m.sparse_copy_r(m.sparse_c()))
|
||||
assert_sparse_equal_ref(m.sparse_copy_c(m.sparse_r()))
|
||||
|
||||
|
||||
def test_sparse_signature(doc):
|
||||
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]
|
||||
""")
|
||||
assert (doc(m.sparse_copy_c) == """
|
||||
sparse_copy_c(arg0: scipy.sparse.csc_matrix[numpy.float32]) -> scipy.sparse.csc_matrix[numpy.float32]
|
||||
""")
|
||||
|
||||
|
||||
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.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():
|
||||
"""Issue 1105: 1xN or Nx1 input arrays weren't accepted for eigen
|
||||
compile-time row vectors or column vector"""
|
||||
assert m.iss1105_row(np.ones((1, 7)))
|
||||
assert m.iss1105_col(np.ones((7, 1)))
|
||||
|
||||
# These should still fail (incompatible dimensions):
|
||||
with pytest.raises(TypeError) as excinfo:
|
||||
m.iss1105_row(np.ones((7, 1)))
|
||||
assert "incompatible function arguments" in str(excinfo.value)
|
||||
with pytest.raises(TypeError) as excinfo:
|
||||
m.iss1105_col(np.ones((1, 7)))
|
||||
assert "incompatible function arguments" in str(excinfo.value)
|
||||
|
||||
|
||||
def test_custom_operator_new():
|
||||
"""Using Eigen types as member variables requires a class-specific
|
||||
operator new with proper alignment"""
|
||||
|
||||
o = m.CustomOperatorNew()
|
||||
np.testing.assert_allclose(o.a, 0.0)
|
||||
np.testing.assert_allclose(o.b.diagonal(), 1.0)
|
||||
47
third_party/pybind11/tests/test_embed/CMakeLists.txt
vendored
Normal file
47
third_party/pybind11/tests/test_embed/CMakeLists.txt
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
possibly_uninitialized(PYTHON_MODULE_EXTENSION Python_INTERPRETER_ID)
|
||||
|
||||
if("${PYTHON_MODULE_EXTENSION}" MATCHES "pypy" OR "${Python_INTERPRETER_ID}" STREQUAL "PyPy")
|
||||
message(STATUS "Skipping embed test on PyPy")
|
||||
add_custom_target(cpptest) # Dummy target on PyPy. Embedding is not supported.
|
||||
set(_suppress_unused_variable_warning "${DOWNLOAD_CATCH}")
|
||||
return()
|
||||
endif()
|
||||
|
||||
find_package(Catch 2.13.5)
|
||||
|
||||
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=ON` to fetch them automatically.")
|
||||
return()
|
||||
endif()
|
||||
|
||||
find_package(Threads REQUIRED)
|
||||
|
||||
add_executable(test_embed catch.cpp test_interpreter.cpp)
|
||||
pybind11_enable_warnings(test_embed)
|
||||
|
||||
target_link_libraries(test_embed PRIVATE pybind11::embed Catch2::Catch2 Threads::Threads)
|
||||
|
||||
if(NOT CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_CURRENT_BINARY_DIR)
|
||||
file(COPY test_interpreter.py test_trampoline.py DESTINATION "${CMAKE_CURRENT_BINARY_DIR}")
|
||||
endif()
|
||||
|
||||
add_custom_target(
|
||||
cpptest
|
||||
COMMAND "$<TARGET_FILE:test_embed>"
|
||||
DEPENDS 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_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)
|
||||
27
third_party/pybind11/tests/test_embed/catch.cpp
vendored
Normal file
27
third_party/pybind11/tests/test_embed/catch.cpp
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
// The Catch implementation is compiled here. This is a standalone
|
||||
// translation unit to avoid recompiling it for every test change.
|
||||
|
||||
#include <pybind11/embed.h>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
// Silence MSVC C++17 deprecation warning from Catch regarding std::uncaught_exceptions (up to
|
||||
// catch 2.0.1; this should be fixed in the next catch release after 2.0.1).
|
||||
# pragma warning(disable : 4996)
|
||||
#endif
|
||||
|
||||
// Catch uses _ internally, which breaks gettext style defines
|
||||
#ifdef _
|
||||
# undef _
|
||||
#endif
|
||||
|
||||
#define CATCH_CONFIG_RUNNER
|
||||
#include <catch.hpp>
|
||||
|
||||
namespace py = pybind11;
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
py::scoped_interpreter guard{};
|
||||
auto result = Catch::Session().run(argc, argv);
|
||||
|
||||
return result < 0xff ? result : 0xff;
|
||||
}
|
||||
20
third_party/pybind11/tests/test_embed/external_module.cpp
vendored
Normal file
20
third_party/pybind11/tests/test_embed/external_module.cpp
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
#include <pybind11/pybind11.h>
|
||||
|
||||
namespace py = pybind11;
|
||||
|
||||
/* Simple test module/test class to check that the referenced internals data of external pybind11
|
||||
* modules aren't preserved over a finalize/initialize.
|
||||
*/
|
||||
|
||||
PYBIND11_MODULE(external_module, m) {
|
||||
class A {
|
||||
public:
|
||||
explicit A(int value) : v{value} {};
|
||||
int v;
|
||||
};
|
||||
|
||||
py::class_<A>(m, "A").def(py::init<int>()).def_readwrite("value", &A::v);
|
||||
|
||||
m.def("internals_at",
|
||||
[]() { return reinterpret_cast<uintptr_t>(&py::detail::get_internals()); });
|
||||
}
|
||||
395
third_party/pybind11/tests/test_embed/test_interpreter.cpp
vendored
Normal file
395
third_party/pybind11/tests/test_embed/test_interpreter.cpp
vendored
Normal file
@@ -0,0 +1,395 @@
|
||||
#include <pybind11/embed.h>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
// Silence MSVC C++17 deprecation warning from Catch regarding std::uncaught_exceptions (up to
|
||||
// catch 2.0.1; this should be fixed in the next catch release after 2.0.1).
|
||||
# pragma warning(disable : 4996)
|
||||
#endif
|
||||
|
||||
#include <catch.hpp>
|
||||
#include <cstdlib>
|
||||
#include <fstream>
|
||||
#include <functional>
|
||||
#include <thread>
|
||||
#include <utility>
|
||||
|
||||
namespace py = pybind11;
|
||||
using namespace py::literals;
|
||||
|
||||
class Widget {
|
||||
public:
|
||||
explicit Widget(std::string message) : message(std::move(message)) {}
|
||||
virtual ~Widget() = default;
|
||||
|
||||
std::string the_message() const { return message; }
|
||||
virtual int the_answer() const = 0;
|
||||
virtual std::string argv0() const = 0;
|
||||
|
||||
private:
|
||||
std::string message;
|
||||
};
|
||||
|
||||
class PyWidget final : public Widget {
|
||||
using Widget::Widget;
|
||||
|
||||
int the_answer() const override { PYBIND11_OVERRIDE_PURE(int, Widget, the_answer); }
|
||||
std::string argv0() const override { PYBIND11_OVERRIDE_PURE(std::string, Widget, argv0); }
|
||||
};
|
||||
|
||||
class test_override_cache_helper {
|
||||
|
||||
public:
|
||||
virtual int func() { return 0; }
|
||||
|
||||
test_override_cache_helper() = default;
|
||||
virtual ~test_override_cache_helper() = default;
|
||||
// Non-copyable
|
||||
test_override_cache_helper &operator=(test_override_cache_helper const &Right) = delete;
|
||||
test_override_cache_helper(test_override_cache_helper const &Copy) = delete;
|
||||
};
|
||||
|
||||
class test_override_cache_helper_trampoline : public test_override_cache_helper {
|
||||
int func() override { PYBIND11_OVERRIDE(int, test_override_cache_helper, func); }
|
||||
};
|
||||
|
||||
PYBIND11_EMBEDDED_MODULE(widget_module, m) {
|
||||
py::class_<Widget, PyWidget>(m, "Widget")
|
||||
.def(py::init<std::string>())
|
||||
.def_property_readonly("the_message", &Widget::the_message);
|
||||
|
||||
m.def("add", [](int i, int j) { return i + j; });
|
||||
}
|
||||
|
||||
PYBIND11_EMBEDDED_MODULE(trampoline_module, m) {
|
||||
py::class_<test_override_cache_helper,
|
||||
test_override_cache_helper_trampoline,
|
||||
std::shared_ptr<test_override_cache_helper>>(m, "test_override_cache_helper")
|
||||
.def(py::init_alias<>())
|
||||
.def("func", &test_override_cache_helper::func);
|
||||
}
|
||||
|
||||
PYBIND11_EMBEDDED_MODULE(throw_exception, ) { throw std::runtime_error("C++ Error"); }
|
||||
|
||||
PYBIND11_EMBEDDED_MODULE(throw_error_already_set, ) {
|
||||
auto d = py::dict();
|
||||
d["missing"].cast<py::object>();
|
||||
}
|
||||
|
||||
TEST_CASE("Pass classes and data between modules defined in C++ and Python") {
|
||||
auto module_ = py::module_::import("test_interpreter");
|
||||
REQUIRE(py::hasattr(module_, "DerivedWidget"));
|
||||
|
||||
auto locals = py::dict("hello"_a = "Hello, World!", "x"_a = 5, **module_.attr("__dict__"));
|
||||
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 message = py_widget.attr("the_message");
|
||||
REQUIRE(message.cast<std::string>() == "The question");
|
||||
|
||||
const auto &cpp_widget = py_widget.cast<const Widget &>();
|
||||
REQUIRE(cpp_widget.the_answer() == 42);
|
||||
}
|
||||
|
||||
TEST_CASE("Override cache") {
|
||||
auto module_ = py::module_::import("test_trampoline");
|
||||
REQUIRE(py::hasattr(module_, "func"));
|
||||
REQUIRE(py::hasattr(module_, "func2"));
|
||||
|
||||
auto locals = py::dict(**module_.attr("__dict__"));
|
||||
|
||||
int i = 0;
|
||||
for (; i < 1500; ++i) {
|
||||
std::shared_ptr<test_override_cache_helper> p_obj;
|
||||
std::shared_ptr<test_override_cache_helper> p_obj2;
|
||||
|
||||
py::object loc_inst = locals["func"]();
|
||||
p_obj = py::cast<std::shared_ptr<test_override_cache_helper>>(loc_inst);
|
||||
|
||||
int ret = p_obj->func();
|
||||
|
||||
REQUIRE(ret == 42);
|
||||
|
||||
loc_inst = locals["func2"]();
|
||||
|
||||
p_obj2 = py::cast<std::shared_ptr<test_override_cache_helper>>(loc_inst);
|
||||
|
||||
p_obj2->func();
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Import error handling") {
|
||||
REQUIRE_NOTHROW(py::module_::import("widget_module"));
|
||||
REQUIRE_THROWS_WITH(py::module_::import("throw_exception"), "ImportError: C++ Error");
|
||||
REQUIRE_THROWS_WITH(py::module_::import("throw_error_already_set"),
|
||||
Catch::Contains("ImportError: initialization failed"));
|
||||
|
||||
auto locals = py::dict("is_keyerror"_a = false, "message"_a = "not set");
|
||||
py::exec(R"(
|
||||
try:
|
||||
import throw_error_already_set
|
||||
except ImportError as e:
|
||||
is_keyerror = type(e.__cause__) == KeyError
|
||||
message = str(e.__cause__)
|
||||
)",
|
||||
py::globals(),
|
||||
locals);
|
||||
REQUIRE(locals["is_keyerror"].cast<bool>() == true);
|
||||
REQUIRE(locals["message"].cast<std::string>() == "'missing'");
|
||||
}
|
||||
|
||||
TEST_CASE("There can be only one interpreter") {
|
||||
static_assert(std::is_move_constructible<py::scoped_interpreter>::value, "");
|
||||
static_assert(!std::is_move_assignable<py::scoped_interpreter>::value, "");
|
||||
static_assert(!std::is_copy_constructible<py::scoped_interpreter>::value, "");
|
||||
static_assert(!std::is_copy_assignable<py::scoped_interpreter>::value, "");
|
||||
|
||||
REQUIRE_THROWS_WITH(py::initialize_interpreter(), "The interpreter is already running");
|
||||
REQUIRE_THROWS_WITH(py::scoped_interpreter(), "The interpreter is already running");
|
||||
|
||||
py::finalize_interpreter();
|
||||
REQUIRE_NOTHROW(py::scoped_interpreter());
|
||||
{
|
||||
auto pyi1 = py::scoped_interpreter();
|
||||
auto pyi2 = std::move(pyi1);
|
||||
}
|
||||
py::initialize_interpreter();
|
||||
}
|
||||
|
||||
bool has_pybind11_internals_builtin() {
|
||||
auto builtins = py::handle(PyEval_GetBuiltins());
|
||||
return builtins.contains(PYBIND11_INTERNALS_ID);
|
||||
};
|
||||
|
||||
bool has_pybind11_internals_static() {
|
||||
auto **&ipp = py::detail::get_internals_pp();
|
||||
return (ipp != nullptr) && (*ipp != nullptr);
|
||||
}
|
||||
|
||||
TEST_CASE("Restart the interpreter") {
|
||||
// Verify pre-restart state.
|
||||
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);
|
||||
|
||||
// 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>());
|
||||
|
||||
// Restart the interpreter.
|
||||
py::finalize_interpreter();
|
||||
REQUIRE(Py_IsInitialized() == 0);
|
||||
|
||||
py::initialize_interpreter();
|
||||
REQUIRE(Py_IsInitialized() == 1);
|
||||
|
||||
// Internals are deleted after a restart.
|
||||
REQUIRE_FALSE(has_pybind11_internals_builtin());
|
||||
REQUIRE_FALSE(has_pybind11_internals_static());
|
||||
pybind11::detail::get_internals();
|
||||
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>());
|
||||
|
||||
// Make sure that an interpreter with no get_internals() created until finalize still gets the
|
||||
// internals destroyed
|
||||
py::finalize_interpreter();
|
||||
py::initialize_interpreter();
|
||||
bool ran = false;
|
||||
py::module_::import("__main__").attr("internals_destroy_test")
|
||||
= py::capsule(&ran, [](void *ran) {
|
||||
py::detail::get_internals();
|
||||
*static_cast<bool *>(ran) = true;
|
||||
});
|
||||
REQUIRE_FALSE(has_pybind11_internals_builtin());
|
||||
REQUIRE_FALSE(has_pybind11_internals_static());
|
||||
REQUIRE_FALSE(ran);
|
||||
py::finalize_interpreter();
|
||||
REQUIRE(ran);
|
||||
py::initialize_interpreter();
|
||||
REQUIRE_FALSE(has_pybind11_internals_builtin());
|
||||
REQUIRE_FALSE(has_pybind11_internals_static());
|
||||
|
||||
// C++ modules can be reloaded.
|
||||
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_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";
|
||||
{
|
||||
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);
|
||||
}
|
||||
REQUIRE(has_pybind11_internals_builtin());
|
||||
REQUIRE(has_pybind11_internals_static());
|
||||
|
||||
/// Create and switch to a subinterpreter.
|
||||
auto *main_tstate = PyThreadState_Get();
|
||||
auto *sub_tstate = Py_NewInterpreter();
|
||||
|
||||
// Subinterpreters get their own copy of builtins. detail::get_internals() still
|
||||
// works by returning from the static variable, i.e. all interpreters share a single
|
||||
// global pybind11::internals;
|
||||
REQUIRE_FALSE(has_pybind11_internals_builtin());
|
||||
REQUIRE(has_pybind11_internals_static());
|
||||
|
||||
// Modules tags should be gone.
|
||||
REQUIRE_FALSE(py::hasattr(py::module_::import("__main__"), "tag"));
|
||||
{
|
||||
auto m = py::module_::import("widget_module");
|
||||
REQUIRE_FALSE(py::hasattr(m, "extension_module_tag"));
|
||||
|
||||
// Function bindings should still work.
|
||||
REQUIRE(m.attr("add")(1, 2).cast<int>() == 3);
|
||||
}
|
||||
|
||||
// Restore main interpreter.
|
||||
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"));
|
||||
}
|
||||
|
||||
TEST_CASE("Execution frame") {
|
||||
// When the interpreter is embedded, there is no execution frame, but `py::exec`
|
||||
// should still function by using reasonable globals: `__main__.__dict__`.
|
||||
py::exec("var = dict(number=42)");
|
||||
REQUIRE(py::globals()["var"]["number"].cast<int>() == 42);
|
||||
}
|
||||
|
||||
TEST_CASE("Threads") {
|
||||
// Restart interpreter to ensure threads are not initialized
|
||||
py::finalize_interpreter();
|
||||
py::initialize_interpreter();
|
||||
REQUIRE_FALSE(has_pybind11_internals_static());
|
||||
|
||||
constexpr auto num_threads = 10;
|
||||
auto locals = py::dict("count"_a = 0);
|
||||
|
||||
{
|
||||
py::gil_scoped_release gil_release{};
|
||||
REQUIRE(has_pybind11_internals_static());
|
||||
|
||||
auto threads = std::vector<std::thread>();
|
||||
for (auto i = 0; i < num_threads; ++i) {
|
||||
threads.emplace_back([&]() {
|
||||
py::gil_scoped_acquire gil{};
|
||||
locals["count"] = locals["count"].cast<int>() + 1;
|
||||
});
|
||||
}
|
||||
|
||||
for (auto &thread : threads) {
|
||||
thread.join();
|
||||
}
|
||||
}
|
||||
|
||||
REQUIRE(locals["count"].cast<int>() == num_threads);
|
||||
}
|
||||
|
||||
// Scope exit utility https://stackoverflow.com/a/36644501/7255855
|
||||
struct scope_exit {
|
||||
std::function<void()> f_;
|
||||
explicit scope_exit(std::function<void()> f) noexcept : f_(std::move(f)) {}
|
||||
~scope_exit() {
|
||||
if (f_) {
|
||||
f_();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
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");
|
||||
bool dont_write_bytecode = sys.attr("dont_write_bytecode").cast<bool>();
|
||||
sys.attr("dont_write_bytecode") = true;
|
||||
// Reset the value at scope exit
|
||||
scope_exit reset_dont_write_bytecode(
|
||||
[&]() { sys.attr("dont_write_bytecode") = dont_write_bytecode; });
|
||||
|
||||
std::string module_name = "test_module_reload";
|
||||
std::string module_file = module_name + ".py";
|
||||
|
||||
// Create the module .py file
|
||||
std::ofstream test_module(module_file);
|
||||
test_module << "def test():\n";
|
||||
test_module << " return 1\n";
|
||||
test_module.close();
|
||||
// Delete the file at scope exit
|
||||
scope_exit delete_module_file([&]() { std::remove(module_file.c_str()); });
|
||||
|
||||
// Import the module from file
|
||||
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
|
||||
test_module.open(module_file);
|
||||
test_module << "def test():\n";
|
||||
test_module << " return 2\n";
|
||||
test_module.close();
|
||||
|
||||
// Reload the module
|
||||
module_.reload();
|
||||
result = module_.attr("test")().cast<int>();
|
||||
REQUIRE(result == 2);
|
||||
}
|
||||
|
||||
TEST_CASE("sys.argv gets initialized properly") {
|
||||
py::finalize_interpreter();
|
||||
{
|
||||
py::scoped_interpreter default_scope;
|
||||
auto module = py::module::import("test_interpreter");
|
||||
auto py_widget = module.attr("DerivedWidget")("The question");
|
||||
const auto &cpp_widget = py_widget.cast<const Widget &>();
|
||||
REQUIRE(cpp_widget.argv0().empty());
|
||||
}
|
||||
|
||||
{
|
||||
char *argv[] = {strdup("a.out")};
|
||||
py::scoped_interpreter argv_scope(true, 1, argv);
|
||||
std::free(argv[0]);
|
||||
auto module = py::module::import("test_interpreter");
|
||||
auto py_widget = module.attr("DerivedWidget")("The question");
|
||||
const auto &cpp_widget = py_widget.cast<const Widget &>();
|
||||
REQUIRE(cpp_widget.argv0() == "a.out");
|
||||
}
|
||||
py::initialize_interpreter();
|
||||
}
|
||||
|
||||
TEST_CASE("make_iterator can be called before then after finalizing an interpreter") {
|
||||
// Reproduction of issue #2101 (https://github.com/pybind/pybind11/issues/2101)
|
||||
py::finalize_interpreter();
|
||||
|
||||
std::vector<int> container;
|
||||
{
|
||||
pybind11::scoped_interpreter g;
|
||||
auto iter = pybind11::make_iterator(container.begin(), container.end());
|
||||
}
|
||||
|
||||
REQUIRE_NOTHROW([&]() {
|
||||
pybind11::scoped_interpreter g;
|
||||
auto iter = pybind11::make_iterator(container.begin(), container.end());
|
||||
}());
|
||||
|
||||
py::initialize_interpreter();
|
||||
}
|
||||
14
third_party/pybind11/tests/test_embed/test_interpreter.py
vendored
Normal file
14
third_party/pybind11/tests/test_embed/test_interpreter.py
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
import sys
|
||||
|
||||
from widget_module import Widget
|
||||
|
||||
|
||||
class DerivedWidget(Widget):
|
||||
def __init__(self, message):
|
||||
super().__init__(message)
|
||||
|
||||
def the_answer(self):
|
||||
return 42
|
||||
|
||||
def argv0(self):
|
||||
return sys.argv[0]
|
||||
16
third_party/pybind11/tests/test_embed/test_trampoline.py
vendored
Normal file
16
third_party/pybind11/tests/test_embed/test_trampoline.py
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
import trampoline_module
|
||||
|
||||
|
||||
def func():
|
||||
class Test(trampoline_module.test_override_cache_helper):
|
||||
def func(self):
|
||||
return 42
|
||||
|
||||
return Test()
|
||||
|
||||
|
||||
def func2():
|
||||
class Test(trampoline_module.test_override_cache_helper):
|
||||
pass
|
||||
|
||||
return Test()
|
||||
133
third_party/pybind11/tests/test_enum.cpp
vendored
Normal file
133
third_party/pybind11/tests/test_enum.cpp
vendored
Normal file
@@ -0,0 +1,133 @@
|
||||
/*
|
||||
tests/test_enums.cpp -- enumerations
|
||||
|
||||
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"
|
||||
|
||||
TEST_SUBMODULE(enums, m) {
|
||||
// test_unscoped_enum
|
||||
enum UnscopedEnum { EOne = 1, ETwo, EThree };
|
||||
py::enum_<UnscopedEnum>(m, "UnscopedEnum", py::arithmetic(), "An unscoped enumeration")
|
||||
.value("EOne", EOne, "Docstring for EOne")
|
||||
.value("ETwo", ETwo, "Docstring for ETwo")
|
||||
.value("EThree", EThree, "Docstring for EThree")
|
||||
.export_values();
|
||||
|
||||
// test_scoped_enum
|
||||
enum class ScopedEnum { Two = 2, Three };
|
||||
py::enum_<ScopedEnum>(m, "ScopedEnum", py::arithmetic())
|
||||
.value("Two", ScopedEnum::Two)
|
||||
.value("Three", ScopedEnum::Three);
|
||||
|
||||
m.def("test_scoped_enum", [](ScopedEnum z) {
|
||||
return "ScopedEnum::" + std::string(z == ScopedEnum::Two ? "Two" : "Three");
|
||||
});
|
||||
|
||||
// test_binary_operators
|
||||
enum Flags { Read = 4, Write = 2, Execute = 1 };
|
||||
py::enum_<Flags>(m, "Flags", py::arithmetic())
|
||||
.value("Read", Flags::Read)
|
||||
.value("Write", Flags::Write)
|
||||
.value("Execute", Flags::Execute)
|
||||
.export_values();
|
||||
|
||||
// test_implicit_conversion
|
||||
class ClassWithUnscopedEnum {
|
||||
public:
|
||||
enum EMode { EFirstMode = 1, ESecondMode };
|
||||
|
||||
static EMode test_function(EMode mode) { return mode; }
|
||||
};
|
||||
py::class_<ClassWithUnscopedEnum> exenum_class(m, "ClassWithUnscopedEnum");
|
||||
exenum_class.def_static("test_function", &ClassWithUnscopedEnum::test_function);
|
||||
py::enum_<ClassWithUnscopedEnum::EMode>(exenum_class, "EMode")
|
||||
.value("EFirstMode", ClassWithUnscopedEnum::EFirstMode)
|
||||
.value("ESecondMode", ClassWithUnscopedEnum::ESecondMode)
|
||||
.export_values();
|
||||
|
||||
// test_enum_to_int
|
||||
m.def("test_enum_to_int", [](int) {});
|
||||
m.def("test_enum_to_uint", [](uint32_t) {});
|
||||
m.def("test_enum_to_long_long", [](long long) {});
|
||||
|
||||
// test_duplicate_enum_name
|
||||
enum SimpleEnum { ONE, TWO, THREE };
|
||||
|
||||
m.def("register_bad_enum", [m]() {
|
||||
py::enum_<SimpleEnum>(m, "SimpleEnum")
|
||||
.value("ONE", SimpleEnum::ONE) // NOTE: all value function calls are called with the
|
||||
// same first parameter value
|
||||
.value("ONE", SimpleEnum::TWO)
|
||||
.value("ONE", SimpleEnum::THREE)
|
||||
.export_values();
|
||||
});
|
||||
|
||||
// test_enum_scalar
|
||||
enum UnscopedUCharEnum : unsigned char {};
|
||||
enum class ScopedShortEnum : short {};
|
||||
enum class ScopedLongEnum : long {};
|
||||
enum UnscopedUInt64Enum : std::uint64_t {};
|
||||
static_assert(
|
||||
py::detail::all_of<
|
||||
std::is_same<py::enum_<UnscopedUCharEnum>::Scalar, unsigned char>,
|
||||
std::is_same<py::enum_<ScopedShortEnum>::Scalar, short>,
|
||||
std::is_same<py::enum_<ScopedLongEnum>::Scalar, long>,
|
||||
std::is_same<py::enum_<UnscopedUInt64Enum>::Scalar, std::uint64_t>>::value,
|
||||
"Error during the deduction of enum's scalar type with normal integer underlying");
|
||||
|
||||
// test_enum_scalar_with_char_underlying
|
||||
enum class ScopedCharEnum : char { Zero, Positive };
|
||||
enum class ScopedWCharEnum : wchar_t { Zero, Positive };
|
||||
enum class ScopedChar32Enum : char32_t { Zero, Positive };
|
||||
enum class ScopedChar16Enum : char16_t { Zero, Positive };
|
||||
|
||||
// test the scalar of char type enums according to chapter 'Character types'
|
||||
// from https://en.cppreference.com/w/cpp/language/types
|
||||
static_assert(
|
||||
py::detail::any_of<
|
||||
std::is_same<py::enum_<ScopedCharEnum>::Scalar, signed char>, // e.g. gcc on x86
|
||||
std::is_same<py::enum_<ScopedCharEnum>::Scalar, unsigned char> // e.g. arm linux
|
||||
>::value,
|
||||
"char should be cast to either signed char or unsigned char");
|
||||
static_assert(sizeof(py::enum_<ScopedWCharEnum>::Scalar) == 2
|
||||
|| sizeof(py::enum_<ScopedWCharEnum>::Scalar) == 4,
|
||||
"wchar_t should be either 16 bits (Windows) or 32 (everywhere else)");
|
||||
static_assert(
|
||||
py::detail::all_of<
|
||||
std::is_same<py::enum_<ScopedChar32Enum>::Scalar, std::uint_least32_t>,
|
||||
std::is_same<py::enum_<ScopedChar16Enum>::Scalar, std::uint_least16_t>>::value,
|
||||
"char32_t, char16_t (and char8_t)'s size, signedness, and alignment is determined");
|
||||
#if defined(PYBIND11_HAS_U8STRING)
|
||||
enum class ScopedChar8Enum : char8_t { Zero, Positive };
|
||||
static_assert(std::is_same<py::enum_<ScopedChar8Enum>::Scalar, unsigned char>::value);
|
||||
#endif
|
||||
|
||||
// test_char_underlying_enum
|
||||
py::enum_<ScopedCharEnum>(m, "ScopedCharEnum")
|
||||
.value("Zero", ScopedCharEnum::Zero)
|
||||
.value("Positive", ScopedCharEnum::Positive);
|
||||
py::enum_<ScopedWCharEnum>(m, "ScopedWCharEnum")
|
||||
.value("Zero", ScopedWCharEnum::Zero)
|
||||
.value("Positive", ScopedWCharEnum::Positive);
|
||||
py::enum_<ScopedChar32Enum>(m, "ScopedChar32Enum")
|
||||
.value("Zero", ScopedChar32Enum::Zero)
|
||||
.value("Positive", ScopedChar32Enum::Positive);
|
||||
py::enum_<ScopedChar16Enum>(m, "ScopedChar16Enum")
|
||||
.value("Zero", ScopedChar16Enum::Zero)
|
||||
.value("Positive", ScopedChar16Enum::Positive);
|
||||
|
||||
// test_bool_underlying_enum
|
||||
enum class ScopedBoolEnum : bool { FALSE, TRUE };
|
||||
|
||||
// bool is unsigned (std::is_signed returns false) and 1-byte long, so represented with u8
|
||||
static_assert(std::is_same<py::enum_<ScopedBoolEnum>::Scalar, std::uint8_t>::value, "");
|
||||
|
||||
py::enum_<ScopedBoolEnum>(m, "ScopedBoolEnum")
|
||||
.value("FALSE", ScopedBoolEnum::FALSE)
|
||||
.value("TRUE", ScopedBoolEnum::TRUE);
|
||||
}
|
||||
263
third_party/pybind11/tests/test_enum.py
vendored
Normal file
263
third_party/pybind11/tests/test_enum.py
vendored
Normal file
@@ -0,0 +1,263 @@
|
||||
import pytest
|
||||
|
||||
from pybind11_tests import enums as m
|
||||
|
||||
|
||||
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"
|
||||
assert m.UnscopedEnum.EOne.value == 1
|
||||
assert m.UnscopedEnum.ETwo.name == "ETwo"
|
||||
assert m.UnscopedEnum.ETwo.value == 2
|
||||
assert m.EOne is m.UnscopedEnum.EOne
|
||||
# name, value readonly
|
||||
with pytest.raises(AttributeError):
|
||||
m.UnscopedEnum.EOne.name = ""
|
||||
with pytest.raises(AttributeError):
|
||||
m.UnscopedEnum.EOne.value = 10
|
||||
# name, value returns a copy
|
||||
# TODO: Neither the name nor value tests actually check against aliasing.
|
||||
# Use a mutable type that has reference semantics.
|
||||
nonaliased_name = m.UnscopedEnum.EOne.name
|
||||
nonaliased_name = "bar" # noqa: F841
|
||||
assert m.UnscopedEnum.EOne.name == "EOne"
|
||||
nonaliased_value = m.UnscopedEnum.EOne.value
|
||||
nonaliased_value = 10 # noqa: F841
|
||||
assert m.UnscopedEnum.EOne.value == 1
|
||||
|
||||
# __members__ property
|
||||
assert m.UnscopedEnum.__members__ == {
|
||||
"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
|
||||
nonaliased_members = m.UnscopedEnum.__members__
|
||||
nonaliased_members["bar"] = "baz"
|
||||
assert m.UnscopedEnum.__members__ == {
|
||||
"EOne": m.UnscopedEnum.EOne,
|
||||
"ETwo": m.UnscopedEnum.ETwo,
|
||||
"EThree": m.UnscopedEnum.EThree,
|
||||
}
|
||||
|
||||
for docstring_line in """An unscoped enumeration
|
||||
|
||||
Members:
|
||||
|
||||
EOne : Docstring for EOne
|
||||
|
||||
ETwo : Docstring for ETwo
|
||||
|
||||
EThree : Docstring for EThree""".split("\n"):
|
||||
assert docstring_line in m.UnscopedEnum.__doc__
|
||||
|
||||
# Unscoped enums will accept ==/!= int comparisons
|
||||
y = m.UnscopedEnum.ETwo
|
||||
assert y == 2
|
||||
assert 2 == y
|
||||
assert y != 3
|
||||
assert 3 != y
|
||||
# Compare with None
|
||||
assert y != None # noqa: E711
|
||||
assert not (y == None) # noqa: E711
|
||||
# Compare with an object
|
||||
assert y != object()
|
||||
assert not (y == object())
|
||||
# Compare with string
|
||||
assert y != "2"
|
||||
assert "2" != y
|
||||
assert not ("2" == y)
|
||||
assert not (y == "2")
|
||||
|
||||
with pytest.raises(TypeError):
|
||||
y < object() # noqa: B015
|
||||
|
||||
with pytest.raises(TypeError):
|
||||
y <= object() # noqa: B015
|
||||
|
||||
with pytest.raises(TypeError):
|
||||
y > object() # noqa: B015
|
||||
|
||||
with pytest.raises(TypeError):
|
||||
y >= object() # noqa: B015
|
||||
|
||||
with pytest.raises(TypeError):
|
||||
y | object()
|
||||
|
||||
with pytest.raises(TypeError):
|
||||
y & object()
|
||||
|
||||
with pytest.raises(TypeError):
|
||||
y ^ object()
|
||||
|
||||
assert int(m.UnscopedEnum.ETwo) == 2
|
||||
assert str(m.UnscopedEnum(2)) == "UnscopedEnum.ETwo"
|
||||
|
||||
# order
|
||||
assert m.UnscopedEnum.EOne < m.UnscopedEnum.ETwo
|
||||
assert m.UnscopedEnum.EOne < 2
|
||||
assert m.UnscopedEnum.ETwo > m.UnscopedEnum.EOne
|
||||
assert m.UnscopedEnum.ETwo > 1
|
||||
assert m.UnscopedEnum.ETwo <= 2
|
||||
assert m.UnscopedEnum.ETwo >= 2
|
||||
assert m.UnscopedEnum.EOne <= m.UnscopedEnum.ETwo
|
||||
assert m.UnscopedEnum.EOne <= 2
|
||||
assert m.UnscopedEnum.ETwo >= m.UnscopedEnum.EOne
|
||||
assert m.UnscopedEnum.ETwo >= 1
|
||||
assert not (m.UnscopedEnum.ETwo < m.UnscopedEnum.EOne)
|
||||
assert not (2 < m.UnscopedEnum.EOne)
|
||||
|
||||
# arithmetic
|
||||
assert m.UnscopedEnum.EOne & m.UnscopedEnum.EThree == m.UnscopedEnum.EOne
|
||||
assert m.UnscopedEnum.EOne | m.UnscopedEnum.ETwo == m.UnscopedEnum.EThree
|
||||
assert m.UnscopedEnum.EOne ^ m.UnscopedEnum.EThree == m.UnscopedEnum.ETwo
|
||||
|
||||
|
||||
def test_scoped_enum():
|
||||
assert m.test_scoped_enum(m.ScopedEnum.Three) == "ScopedEnum::Three"
|
||||
z = m.ScopedEnum.Two
|
||||
assert m.test_scoped_enum(z) == "ScopedEnum::Two"
|
||||
|
||||
# Scoped enums will *NOT* accept ==/!= int comparisons (Will always return False)
|
||||
assert not z == 3
|
||||
assert not 3 == z
|
||||
assert z != 3
|
||||
assert 3 != z
|
||||
# Compare with None
|
||||
assert z != None # noqa: E711
|
||||
assert not (z == None) # noqa: E711
|
||||
# Compare with an object
|
||||
assert z != object()
|
||||
assert not (z == object())
|
||||
# Scoped enums will *NOT* accept >, <, >= and <= int comparisons (Will throw exceptions)
|
||||
with pytest.raises(TypeError):
|
||||
z > 3 # noqa: B015
|
||||
with pytest.raises(TypeError):
|
||||
z < 3 # noqa: B015
|
||||
with pytest.raises(TypeError):
|
||||
z >= 3 # noqa: B015
|
||||
with pytest.raises(TypeError):
|
||||
z <= 3 # noqa: B015
|
||||
|
||||
# order
|
||||
assert m.ScopedEnum.Two < m.ScopedEnum.Three
|
||||
assert m.ScopedEnum.Three > m.ScopedEnum.Two
|
||||
assert m.ScopedEnum.Two <= m.ScopedEnum.Three
|
||||
assert m.ScopedEnum.Two <= m.ScopedEnum.Two
|
||||
assert m.ScopedEnum.Two >= m.ScopedEnum.Two
|
||||
assert m.ScopedEnum.Three >= m.ScopedEnum.Two
|
||||
|
||||
|
||||
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
|
||||
second = m.ClassWithUnscopedEnum.ESecondMode
|
||||
|
||||
assert f(first) == 1
|
||||
|
||||
assert f(first) == f(first)
|
||||
assert not f(first) != f(first)
|
||||
|
||||
assert f(first) != f(second)
|
||||
assert not f(first) == f(second)
|
||||
|
||||
assert f(first) == int(f(first))
|
||||
assert not f(first) != int(f(first))
|
||||
|
||||
assert f(first) != int(f(second))
|
||||
assert not f(first) == int(f(second))
|
||||
|
||||
# noinspection PyDictCreation
|
||||
x = {f(first): 1, f(second): 2}
|
||||
x[f(first)] = 3
|
||||
x[f(second)] = 4
|
||||
# Hashing test
|
||||
assert repr(x) == "{<EMode.EFirstMode: 1>: 3, <EMode.ESecondMode: 2>: 4}"
|
||||
|
||||
|
||||
def test_binary_operators():
|
||||
assert int(m.Flags.Read) == 4
|
||||
assert int(m.Flags.Write) == 2
|
||||
assert int(m.Flags.Execute) == 1
|
||||
assert int(m.Flags.Read | m.Flags.Write | m.Flags.Execute) == 7
|
||||
assert int(m.Flags.Read | m.Flags.Write) == 6
|
||||
assert int(m.Flags.Read | m.Flags.Execute) == 5
|
||||
assert int(m.Flags.Write | m.Flags.Execute) == 3
|
||||
assert int(m.Flags.Write | 1) == 3
|
||||
assert ~m.Flags.Write == -3
|
||||
|
||||
state = m.Flags.Read | m.Flags.Write
|
||||
assert (state & m.Flags.Read) != 0
|
||||
assert (state & m.Flags.Write) != 0
|
||||
assert (state & m.Flags.Execute) == 0
|
||||
assert (state & 1) == 0
|
||||
|
||||
state2 = ~state
|
||||
assert state2 == -7
|
||||
assert int(state ^ state2) == -1
|
||||
|
||||
|
||||
def test_enum_to_int():
|
||||
m.test_enum_to_int(m.Flags.Read)
|
||||
m.test_enum_to_int(m.ClassWithUnscopedEnum.EMode.EFirstMode)
|
||||
m.test_enum_to_int(m.ScopedCharEnum.Positive)
|
||||
m.test_enum_to_int(m.ScopedBoolEnum.TRUE)
|
||||
m.test_enum_to_uint(m.Flags.Read)
|
||||
m.test_enum_to_uint(m.ClassWithUnscopedEnum.EMode.EFirstMode)
|
||||
m.test_enum_to_uint(m.ScopedCharEnum.Positive)
|
||||
m.test_enum_to_uint(m.ScopedBoolEnum.TRUE)
|
||||
m.test_enum_to_long_long(m.Flags.Read)
|
||||
m.test_enum_to_long_long(m.ClassWithUnscopedEnum.EMode.EFirstMode)
|
||||
m.test_enum_to_long_long(m.ScopedCharEnum.Positive)
|
||||
m.test_enum_to_long_long(m.ScopedBoolEnum.TRUE)
|
||||
|
||||
|
||||
def test_duplicate_enum_name():
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
m.register_bad_enum()
|
||||
assert str(excinfo.value) == 'SimpleEnum: element "ONE" already exists!'
|
||||
|
||||
|
||||
def test_char_underlying_enum(): # Issue #1331/PR #1334:
|
||||
assert type(m.ScopedCharEnum.Positive.__int__()) is int
|
||||
assert int(m.ScopedChar16Enum.Zero) == 0
|
||||
assert hash(m.ScopedChar32Enum.Positive) == 1
|
||||
assert type(m.ScopedCharEnum.Positive.__getstate__()) is int
|
||||
assert m.ScopedWCharEnum(1) == m.ScopedWCharEnum.Positive
|
||||
with pytest.raises(TypeError):
|
||||
# Even if the underlying type is char, only an int can be used to construct the enum:
|
||||
m.ScopedCharEnum("0")
|
||||
|
||||
|
||||
def test_bool_underlying_enum():
|
||||
assert type(m.ScopedBoolEnum.TRUE.__int__()) is int
|
||||
assert int(m.ScopedBoolEnum.FALSE) == 0
|
||||
assert hash(m.ScopedBoolEnum.TRUE) == 1
|
||||
assert type(m.ScopedBoolEnum.TRUE.__getstate__()) is int
|
||||
assert m.ScopedBoolEnum(1) == m.ScopedBoolEnum.TRUE
|
||||
# Enum could construct with a bool
|
||||
# (bool is a strict subclass of int, and False will be converted to 0)
|
||||
assert m.ScopedBoolEnum(False) == m.ScopedBoolEnum.FALSE
|
||||
|
||||
|
||||
def test_docstring_signatures():
|
||||
for enum_type in [m.ScopedEnum, m.UnscopedEnum]:
|
||||
for attr in enum_type.__dict__.values():
|
||||
# Issue #2623/PR #2637: Add argument names to enum_ methods
|
||||
assert "arg0" not in (attr.__doc__ or "")
|
||||
118
third_party/pybind11/tests/test_eval.cpp
vendored
Normal file
118
third_party/pybind11/tests/test_eval.cpp
vendored
Normal file
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
tests/test_eval.cpp -- Usage of eval() and eval_file()
|
||||
|
||||
Copyright (c) 2016 Klemens D. Morgenstern
|
||||
|
||||
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/eval.h>
|
||||
|
||||
#include "pybind11_tests.h"
|
||||
|
||||
#include <utility>
|
||||
|
||||
TEST_SUBMODULE(eval_, m) {
|
||||
// test_evals
|
||||
|
||||
auto global = py::dict(py::module_::import("__main__").attr("__dict__"));
|
||||
|
||||
m.def("test_eval_statements", [global]() {
|
||||
auto local = py::dict();
|
||||
local["call_test"] = py::cpp_function([&]() -> int { return 42; });
|
||||
|
||||
// Regular string literal
|
||||
py::exec("message = 'Hello World!'\n"
|
||||
"x = call_test()",
|
||||
global,
|
||||
local);
|
||||
|
||||
// Multi-line raw string literal
|
||||
py::exec(R"(
|
||||
if x == 42:
|
||||
print(message)
|
||||
else:
|
||||
raise RuntimeError
|
||||
)",
|
||||
global,
|
||||
local);
|
||||
auto x = local["x"].cast<int>();
|
||||
|
||||
return x == 42;
|
||||
});
|
||||
|
||||
m.def("test_eval", [global]() {
|
||||
auto local = py::dict();
|
||||
local["x"] = py::int_(42);
|
||||
auto x = py::eval("x", global, local);
|
||||
return x.cast<int>() == 42;
|
||||
});
|
||||
|
||||
m.def("test_eval_single_statement", []() {
|
||||
auto local = py::dict();
|
||||
local["call_test"] = py::cpp_function([&]() -> int { return 42; });
|
||||
|
||||
auto result = py::eval<py::eval_single_statement>("x = call_test()", py::dict(), local);
|
||||
auto x = local["x"].cast<int>();
|
||||
return result.is_none() && x == 42;
|
||||
});
|
||||
|
||||
m.def("test_eval_file", [global](py::str filename) {
|
||||
auto local = py::dict();
|
||||
local["y"] = py::int_(43);
|
||||
|
||||
int val_out = 0;
|
||||
local["call_test2"] = py::cpp_function([&](int value) { val_out = value; });
|
||||
|
||||
auto result = py::eval_file(std::move(filename), global, local);
|
||||
return val_out == 43 && result.is_none();
|
||||
});
|
||||
|
||||
m.def("test_eval_failure", []() {
|
||||
try {
|
||||
py::eval("nonsense code ...");
|
||||
} catch (py::error_already_set &) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
m.def("test_eval_file_failure", []() {
|
||||
try {
|
||||
py::eval_file("non-existing file");
|
||||
} catch (std::exception &) {
|
||||
return true;
|
||||
}
|
||||
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;
|
||||
});
|
||||
|
||||
// test_eval_closure
|
||||
m.def("test_eval_closure", []() {
|
||||
py::dict global;
|
||||
global["closure_value"] = 42;
|
||||
py::dict local;
|
||||
local["closure_value"] = 0;
|
||||
py::exec(R"(
|
||||
local_value = closure_value
|
||||
|
||||
def func_global():
|
||||
return closure_value
|
||||
|
||||
def func_local():
|
||||
return local_value
|
||||
)",
|
||||
global,
|
||||
local);
|
||||
return std::make_pair(global, local);
|
||||
});
|
||||
}
|
||||
50
third_party/pybind11/tests/test_eval.py
vendored
Normal file
50
third_party/pybind11/tests/test_eval.py
vendored
Normal file
@@ -0,0 +1,50 @@
|
||||
import os
|
||||
|
||||
import pytest
|
||||
|
||||
import env # noqa: F401
|
||||
from pybind11_tests import eval_ as m
|
||||
|
||||
|
||||
def test_evals(capture):
|
||||
with capture:
|
||||
assert m.test_eval_statements()
|
||||
assert capture == "Hello World!"
|
||||
|
||||
assert m.test_eval()
|
||||
assert m.test_eval_single_statement()
|
||||
|
||||
assert m.test_eval_failure()
|
||||
|
||||
|
||||
@pytest.mark.xfail("env.PYPY", raises=RuntimeError)
|
||||
def test_eval_file():
|
||||
filename = os.path.join(os.path.dirname(__file__), "test_eval_call.py")
|
||||
assert m.test_eval_file(filename)
|
||||
|
||||
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
|
||||
|
||||
|
||||
def test_eval_closure():
|
||||
global_, local = m.test_eval_closure()
|
||||
|
||||
assert global_["closure_value"] == 42
|
||||
assert local["closure_value"] == 0
|
||||
|
||||
assert "local_value" not in global_
|
||||
assert local["local_value"] == 0
|
||||
|
||||
assert "func_global" not in global_
|
||||
assert local["func_global"]() == 42
|
||||
|
||||
assert "func_local" not in global_
|
||||
with pytest.raises(NameError):
|
||||
local["func_local"]()
|
||||
4
third_party/pybind11/tests/test_eval_call.py
vendored
Normal file
4
third_party/pybind11/tests/test_eval_call.py
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
# This file is called from 'test_eval.py'
|
||||
|
||||
if "call_test2" in locals():
|
||||
call_test2(y) # noqa: F821 undefined name
|
||||
302
third_party/pybind11/tests/test_exceptions.cpp
vendored
Normal file
302
third_party/pybind11/tests/test_exceptions.cpp
vendored
Normal file
@@ -0,0 +1,302 @@
|
||||
/*
|
||||
tests/test_custom-exceptions.cpp -- exception translation
|
||||
|
||||
Copyright (c) 2016 Pim Schellart <P.Schellart@princeton.edu>
|
||||
|
||||
All rights reserved. Use of this source code is governed by a
|
||||
BSD-style license that can be found in the LICENSE file.
|
||||
*/
|
||||
#include "test_exceptions.h"
|
||||
|
||||
#include "local_bindings.h"
|
||||
#include "pybind11_tests.h"
|
||||
|
||||
#include <exception>
|
||||
#include <stdexcept>
|
||||
#include <utility>
|
||||
|
||||
// A type that should be raised as an exception in Python
|
||||
class MyException : public std::exception {
|
||||
public:
|
||||
explicit MyException(const char *m) : message{m} {}
|
||||
const char *what() const noexcept override { return message.c_str(); }
|
||||
|
||||
private:
|
||||
std::string message = "";
|
||||
};
|
||||
|
||||
// A type that should be translated to a standard Python exception
|
||||
class MyException2 : public std::exception {
|
||||
public:
|
||||
explicit MyException2(const char *m) : message{m} {}
|
||||
const char *what() const noexcept override { return message.c_str(); }
|
||||
|
||||
private:
|
||||
std::string message = "";
|
||||
};
|
||||
|
||||
// A type that is not derived from std::exception (and is thus unknown)
|
||||
class MyException3 {
|
||||
public:
|
||||
explicit MyException3(const char *m) : message{m} {}
|
||||
virtual const char *what() const noexcept { return message.c_str(); }
|
||||
// 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 = "";
|
||||
};
|
||||
|
||||
// A type that should be translated to MyException
|
||||
// and delegated to its exception translator
|
||||
class MyException4 : public std::exception {
|
||||
public:
|
||||
explicit MyException4(const char *m) : message{m} {}
|
||||
const char *what() const noexcept override { return message.c_str(); }
|
||||
|
||||
private:
|
||||
std::string message = "";
|
||||
};
|
||||
|
||||
// Like the above, but declared via the helper function
|
||||
class MyException5 : public std::logic_error {
|
||||
public:
|
||||
explicit MyException5(const std::string &what) : std::logic_error(what) {}
|
||||
};
|
||||
|
||||
// Inherits from MyException5
|
||||
class MyException5_1 : public MyException5 {
|
||||
using MyException5::MyException5;
|
||||
};
|
||||
|
||||
// Exception that will be caught via the module local translator.
|
||||
class MyException6 : public std::exception {
|
||||
public:
|
||||
explicit MyException6(const char *m) : message{m} {}
|
||||
const char *what() const noexcept override { return message.c_str(); }
|
||||
|
||||
private:
|
||||
std::string message = "";
|
||||
};
|
||||
|
||||
struct PythonCallInDestructor {
|
||||
explicit PythonCallInDestructor(const py::dict &d) : d(d) {}
|
||||
~PythonCallInDestructor() { d["good"] = true; }
|
||||
|
||||
py::dict d;
|
||||
};
|
||||
|
||||
struct PythonAlreadySetInDestructor {
|
||||
explicit PythonAlreadySetInDestructor(const py::str &s) : s(s) {}
|
||||
~PythonAlreadySetInDestructor() {
|
||||
py::dict foo;
|
||||
try {
|
||||
// Assign to a py::object to force read access of nonexistent dict entry
|
||||
py::object o = foo["bar"];
|
||||
} catch (py::error_already_set &ex) {
|
||||
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."); });
|
||||
|
||||
// make a new custom exception and use it as a translation target
|
||||
static py::exception<MyException> ex(m, "MyException");
|
||||
py::register_exception_translator([](std::exception_ptr p) {
|
||||
try {
|
||||
if (p) {
|
||||
std::rethrow_exception(p);
|
||||
}
|
||||
} catch (const MyException &e) {
|
||||
// Set MyException as the active python error
|
||||
ex(e.what());
|
||||
}
|
||||
});
|
||||
|
||||
// register new translator for MyException2
|
||||
// no need to store anything here because this type will
|
||||
// never by visible from Python
|
||||
py::register_exception_translator([](std::exception_ptr p) {
|
||||
try {
|
||||
if (p) {
|
||||
std::rethrow_exception(p);
|
||||
}
|
||||
} catch (const MyException2 &e) {
|
||||
// Translate this exception to a standard RuntimeError
|
||||
PyErr_SetString(PyExc_RuntimeError, e.what());
|
||||
}
|
||||
});
|
||||
|
||||
// register new translator for MyException4
|
||||
// which will catch it and delegate to the previously registered
|
||||
// translator for MyException by throwing a new exception
|
||||
py::register_exception_translator([](std::exception_ptr p) {
|
||||
try {
|
||||
if (p) {
|
||||
std::rethrow_exception(p);
|
||||
}
|
||||
} catch (const MyException4 &e) {
|
||||
throw MyException(e.what());
|
||||
}
|
||||
});
|
||||
|
||||
// A simple exception translation:
|
||||
auto ex5 = py::register_exception<MyException5>(m, "MyException5");
|
||||
// A slightly more complicated one that declares MyException5_1 as a subclass of MyException5
|
||||
py::register_exception<MyException5_1>(m, "MyException5_1", ex5.ptr());
|
||||
|
||||
// py::register_local_exception<LocalSimpleException>(m, "LocalSimpleException")
|
||||
|
||||
py::register_local_exception_translator([](std::exception_ptr p) {
|
||||
try {
|
||||
if (p) {
|
||||
std::rethrow_exception(p);
|
||||
}
|
||||
} catch (const MyException6 &e) {
|
||||
PyErr_SetString(PyExc_RuntimeError, e.what());
|
||||
}
|
||||
});
|
||||
|
||||
m.def("throws1", []() { throw MyException("this error should go to a custom type"); });
|
||||
m.def("throws2",
|
||||
[]() { throw MyException2("this error should go to a standard Python exception"); });
|
||||
m.def("throws3", []() { throw MyException3("this error cannot be translated"); });
|
||||
m.def("throws4", []() { throw MyException4("this error is rethrown"); });
|
||||
m.def("throws5",
|
||||
[]() { throw MyException5("this is a helper-defined translated exception"); });
|
||||
m.def("throws5_1", []() { throw MyException5_1("MyException5 subclass"); });
|
||||
m.def("throws6", []() { throw MyException6("MyException6 only handled in this module"); });
|
||||
m.def("throws_logic_error", []() {
|
||||
throw std::logic_error("this error should fall through to the standard handler");
|
||||
});
|
||||
m.def("throws_overflow_error", []() { throw std::overflow_error(""); });
|
||||
m.def("throws_local_error", []() { throw LocalException("never caught"); });
|
||||
m.def("throws_local_simple_error", []() { throw LocalSimpleException("this mod"); });
|
||||
m.def("exception_matches", []() {
|
||||
py::dict foo;
|
||||
try {
|
||||
// Assign to a py::object to force read access of nonexistent dict entry
|
||||
py::object o = foo["bar"];
|
||||
} catch (py::error_already_set &ex) {
|
||||
if (!ex.matches(PyExc_KeyError)) {
|
||||
throw;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
m.def("exception_matches_base", []() {
|
||||
py::dict foo;
|
||||
try {
|
||||
// Assign to a py::object to force read access of nonexistent dict entry
|
||||
py::object o = foo["bar"];
|
||||
} catch (py::error_already_set &ex) {
|
||||
if (!ex.matches(PyExc_Exception)) {
|
||||
throw;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
m.def("modulenotfound_exception_matches_base", []() {
|
||||
try {
|
||||
// On Python >= 3.6, this raises a ModuleNotFoundError, a subclass of ImportError
|
||||
py::module_::import("nonexistent");
|
||||
} catch (py::error_already_set &ex) {
|
||||
if (!ex.matches(PyExc_ImportError)) {
|
||||
throw;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
m.def("throw_already_set", [](bool err) {
|
||||
if (err) {
|
||||
PyErr_SetString(PyExc_ValueError, "foo");
|
||||
}
|
||||
try {
|
||||
throw py::error_already_set();
|
||||
} catch (const std::runtime_error &e) {
|
||||
if ((err && e.what() != std::string("ValueError: foo"))
|
||||
|| (!err && e.what() != std::string("Unknown internal error occurred"))) {
|
||||
PyErr_Clear();
|
||||
throw std::runtime_error("error message mismatch");
|
||||
}
|
||||
}
|
||||
PyErr_Clear();
|
||||
if (err) {
|
||||
PyErr_SetString(PyExc_ValueError, "foo");
|
||||
}
|
||||
throw py::error_already_set();
|
||||
});
|
||||
|
||||
m.def("python_call_in_destructor", [](const py::dict &d) {
|
||||
bool retval = false;
|
||||
try {
|
||||
PythonCallInDestructor set_dict_in_destructor(d);
|
||||
PyErr_SetString(PyExc_ValueError, "foo");
|
||||
throw py::error_already_set();
|
||||
} catch (const py::error_already_set &) {
|
||||
retval = true;
|
||||
}
|
||||
return retval;
|
||||
});
|
||||
|
||||
m.def("python_alreadyset_in_destructor", [](const py::str &s) {
|
||||
PythonAlreadySetInDestructor alreadyset_in_destructor(s);
|
||||
return true;
|
||||
});
|
||||
|
||||
// test_nested_throws
|
||||
m.def("try_catch",
|
||||
[m](const py::object &exc_type, const py::function &f, const py::args &args) {
|
||||
try {
|
||||
f(*args);
|
||||
} catch (py::error_already_set &ex) {
|
||||
if (ex.matches(exc_type)) {
|
||||
py::print(ex.what());
|
||||
} else {
|
||||
throw;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Test repr that cannot be displayed
|
||||
m.def("simple_bool_passthrough", [](bool x) { return x; });
|
||||
|
||||
m.def("throw_should_be_translated_to_key_error", []() { throw shared_exception(); });
|
||||
|
||||
m.def("raise_from", []() {
|
||||
PyErr_SetString(PyExc_ValueError, "inner");
|
||||
py::raise_from(PyExc_ValueError, "outer");
|
||||
throw py::error_already_set();
|
||||
});
|
||||
|
||||
m.def("raise_from_already_set", []() {
|
||||
try {
|
||||
PyErr_SetString(PyExc_ValueError, "inner");
|
||||
throw py::error_already_set();
|
||||
} catch (py::error_already_set &e) {
|
||||
py::raise_from(e, PyExc_ValueError, "outer");
|
||||
throw py::error_already_set();
|
||||
}
|
||||
});
|
||||
|
||||
m.def("throw_nested_exception", []() {
|
||||
try {
|
||||
throw std::runtime_error("Inner Exception");
|
||||
} catch (const std::runtime_error &) {
|
||||
std::throw_with_nested(std::runtime_error("Outer Exception"));
|
||||
}
|
||||
});
|
||||
}
|
||||
13
third_party/pybind11/tests/test_exceptions.h
vendored
Normal file
13
third_party/pybind11/tests/test_exceptions.h
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
#include "pybind11_tests.h"
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
// shared exceptions for cross_module_tests
|
||||
|
||||
class PYBIND11_EXPORT_EXCEPTION shared_exception : public pybind11::builtin_exception {
|
||||
public:
|
||||
using builtin_exception::builtin_exception;
|
||||
explicit shared_exception() : shared_exception("") {}
|
||||
void set_error() const override { PyErr_SetString(PyExc_RuntimeError, what()); }
|
||||
};
|
||||
275
third_party/pybind11/tests/test_exceptions.py
vendored
Normal file
275
third_party/pybind11/tests/test_exceptions.py
vendored
Normal file
@@ -0,0 +1,275 @@
|
||||
import sys
|
||||
|
||||
import pytest
|
||||
|
||||
import env
|
||||
import pybind11_cross_module_tests as cm
|
||||
from pybind11_tests import exceptions as m
|
||||
|
||||
|
||||
def test_std_exception(msg):
|
||||
with pytest.raises(RuntimeError) as excinfo:
|
||||
m.throw_std_exception()
|
||||
assert msg(excinfo.value) == "This exception was intentionally thrown."
|
||||
|
||||
|
||||
def test_error_already_set(msg):
|
||||
with pytest.raises(RuntimeError) as excinfo:
|
||||
m.throw_already_set(False)
|
||||
assert msg(excinfo.value) == "Unknown internal error occurred"
|
||||
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
m.throw_already_set(True)
|
||||
assert msg(excinfo.value) == "foo"
|
||||
|
||||
|
||||
def test_raise_from(msg):
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
m.raise_from()
|
||||
assert msg(excinfo.value) == "outer"
|
||||
assert msg(excinfo.value.__cause__) == "inner"
|
||||
|
||||
|
||||
def test_raise_from_already_set(msg):
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
m.raise_from_already_set()
|
||||
assert msg(excinfo.value) == "outer"
|
||||
assert msg(excinfo.value.__cause__) == "inner"
|
||||
|
||||
|
||||
def test_cross_module_exceptions(msg):
|
||||
with pytest.raises(RuntimeError) as excinfo:
|
||||
cm.raise_runtime_error()
|
||||
assert str(excinfo.value) == "My runtime error"
|
||||
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
cm.raise_value_error()
|
||||
assert str(excinfo.value) == "My value error"
|
||||
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
cm.throw_pybind_value_error()
|
||||
assert str(excinfo.value) == "pybind11 value error"
|
||||
|
||||
with pytest.raises(TypeError) as excinfo:
|
||||
cm.throw_pybind_type_error()
|
||||
assert str(excinfo.value) == "pybind11 type error"
|
||||
|
||||
with pytest.raises(StopIteration) as excinfo:
|
||||
cm.throw_stop_iteration()
|
||||
|
||||
with pytest.raises(cm.LocalSimpleException) as excinfo:
|
||||
cm.throw_local_simple_error()
|
||||
assert msg(excinfo.value) == "external mod"
|
||||
|
||||
with pytest.raises(KeyError) as excinfo:
|
||||
cm.throw_local_error()
|
||||
# KeyError is a repr of the key, so it has an extra set of quotes
|
||||
assert str(excinfo.value) == "'just local'"
|
||||
|
||||
|
||||
# TODO: FIXME
|
||||
@pytest.mark.xfail(
|
||||
"env.PYPY and env.MACOS",
|
||||
raises=RuntimeError,
|
||||
reason="Expected failure with PyPy and libc++ (Issue #2847 & PR #2999)", )
|
||||
def test_cross_module_exception_translator():
|
||||
with pytest.raises(KeyError):
|
||||
# translator registered in cross_module_tests
|
||||
m.throw_should_be_translated_to_key_error()
|
||||
|
||||
|
||||
def test_python_call_in_catch():
|
||||
d = {}
|
||||
assert m.python_call_in_destructor(d) is True
|
||||
assert d["good"] is True
|
||||
|
||||
|
||||
def ignore_pytest_unraisable_warning(f):
|
||||
unraisable = "PytestUnraisableExceptionWarning"
|
||||
if hasattr(pytest, unraisable): # Python >= 3.8 and pytest >= 6
|
||||
dec = pytest.mark.filterwarnings(f"ignore::pytest.{unraisable}")
|
||||
return dec(f)
|
||||
else:
|
||||
return f
|
||||
|
||||
|
||||
# TODO: find out why this fails on PyPy, https://foss.heptapod.net/pypy/pypy/-/issues/3583
|
||||
@pytest.mark.xfail(
|
||||
env.PYPY, reason="Failure on PyPy 3.8 (7.3.7)", strict=False)
|
||||
@ignore_pytest_unraisable_warning
|
||||
def test_python_alreadyset_in_destructor(monkeypatch, capsys):
|
||||
hooked = False
|
||||
triggered = False
|
||||
|
||||
if hasattr(sys, "unraisablehook"): # Python 3.8+
|
||||
hooked = True
|
||||
# Don't take `sys.unraisablehook`, as that's overwritten by pytest
|
||||
default_hook = sys.__unraisablehook__
|
||||
|
||||
def hook(unraisable_hook_args):
|
||||
exc_type, exc_value, exc_tb, err_msg, obj = unraisable_hook_args
|
||||
if obj == "already_set demo":
|
||||
nonlocal triggered
|
||||
triggered = 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 is True
|
||||
|
||||
_, captured_stderr = capsys.readouterr()
|
||||
assert captured_stderr.startswith(
|
||||
"Exception ignored in: 'already_set demo'")
|
||||
assert captured_stderr.rstrip().endswith("KeyError: 'bar'")
|
||||
|
||||
|
||||
def test_exception_matches():
|
||||
assert m.exception_matches()
|
||||
assert m.exception_matches_base()
|
||||
assert m.modulenotfound_exception_matches_base()
|
||||
|
||||
|
||||
def test_custom(msg):
|
||||
# Can we catch a MyException?
|
||||
with pytest.raises(m.MyException) as excinfo:
|
||||
m.throws1()
|
||||
assert msg(excinfo.value) == "this error should go to a custom type"
|
||||
|
||||
# Can we translate to standard Python exceptions?
|
||||
with pytest.raises(RuntimeError) as excinfo:
|
||||
m.throws2()
|
||||
assert msg(
|
||||
excinfo.value) == "this error should go to a standard Python exception"
|
||||
|
||||
# Can we handle unknown exceptions?
|
||||
with pytest.raises(RuntimeError) as excinfo:
|
||||
m.throws3()
|
||||
assert msg(excinfo.value) == "Caught an unknown exception!"
|
||||
|
||||
# Can we delegate to another handler by rethrowing?
|
||||
with pytest.raises(m.MyException) as excinfo:
|
||||
m.throws4()
|
||||
assert msg(excinfo.value) == "this error is rethrown"
|
||||
|
||||
# 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")
|
||||
|
||||
# OverFlow error translation.
|
||||
with pytest.raises(OverflowError) as excinfo:
|
||||
m.throws_overflow_error()
|
||||
|
||||
# Can we handle a helper-declared exception?
|
||||
with pytest.raises(m.MyException5) as excinfo:
|
||||
m.throws5()
|
||||
assert msg(
|
||||
excinfo.value) == "this is a helper-defined translated exception"
|
||||
|
||||
# Exception subclassing:
|
||||
with pytest.raises(m.MyException5) as excinfo:
|
||||
m.throws5_1()
|
||||
assert msg(excinfo.value) == "MyException5 subclass"
|
||||
assert isinstance(excinfo.value, m.MyException5_1)
|
||||
|
||||
with pytest.raises(m.MyException5_1) as excinfo:
|
||||
m.throws5_1()
|
||||
assert msg(excinfo.value) == "MyException5 subclass"
|
||||
|
||||
with pytest.raises(m.MyException5) as excinfo:
|
||||
try:
|
||||
m.throws5()
|
||||
except m.MyException5_1:
|
||||
raise RuntimeError("Exception error: caught child from parent")
|
||||
assert msg(
|
||||
excinfo.value) == "this is a helper-defined translated exception"
|
||||
|
||||
|
||||
def test_nested_throws(capture):
|
||||
"""Tests nested (e.g. C++ -> Python -> C++) exception handling"""
|
||||
|
||||
def throw_myex():
|
||||
raise m.MyException("nested error")
|
||||
|
||||
def throw_myex5():
|
||||
raise m.MyException5("nested error 5")
|
||||
|
||||
# In the comments below, the exception is caught in the first step, thrown in the last step
|
||||
|
||||
# C++ -> Python
|
||||
with capture:
|
||||
m.try_catch(m.MyException5, throw_myex5)
|
||||
assert str(capture).startswith("MyException5: nested error 5")
|
||||
|
||||
# Python -> C++ -> Python
|
||||
with pytest.raises(m.MyException) as excinfo:
|
||||
m.try_catch(m.MyException5, throw_myex)
|
||||
assert str(excinfo.value) == "nested error"
|
||||
|
||||
def pycatch(exctype, f, *args):
|
||||
try:
|
||||
f(*args)
|
||||
except m.MyException as e:
|
||||
print(e)
|
||||
|
||||
# C++ -> Python -> C++ -> Python
|
||||
with capture:
|
||||
m.try_catch(
|
||||
m.MyException5,
|
||||
pycatch,
|
||||
m.MyException,
|
||||
m.try_catch,
|
||||
m.MyException,
|
||||
throw_myex5, )
|
||||
assert str(capture).startswith("MyException5: nested error 5")
|
||||
|
||||
# C++ -> Python -> C++
|
||||
with capture:
|
||||
m.try_catch(m.MyException, pycatch, m.MyException5, m.throws4)
|
||||
assert capture == "this error is rethrown"
|
||||
|
||||
# Python -> C++ -> Python -> C++
|
||||
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"
|
||||
|
||||
|
||||
def test_throw_nested_exception():
|
||||
with pytest.raises(RuntimeError) as excinfo:
|
||||
m.throw_nested_exception()
|
||||
assert str(excinfo.value) == "Outer Exception"
|
||||
assert str(excinfo.value.__cause__) == "Inner Exception"
|
||||
|
||||
|
||||
# This can often happen if you wrap a pybind11 class in a Python wrapper
|
||||
def test_invalid_repr():
|
||||
class MyRepr:
|
||||
def __repr__(self):
|
||||
raise AttributeError("Example error")
|
||||
|
||||
with pytest.raises(TypeError):
|
||||
m.simple_bool_passthrough(MyRepr())
|
||||
|
||||
|
||||
def test_local_translator(msg):
|
||||
"""Tests that a local translator works and that the local translator from
|
||||
the cross module is not applied"""
|
||||
with pytest.raises(RuntimeError) as excinfo:
|
||||
m.throws6()
|
||||
assert msg(excinfo.value) == "MyException6 only handled in this module"
|
||||
|
||||
with pytest.raises(RuntimeError) as excinfo:
|
||||
m.throws_local_error()
|
||||
assert not isinstance(excinfo.value, KeyError)
|
||||
assert msg(excinfo.value) == "never caught"
|
||||
|
||||
with pytest.raises(Exception) as excinfo:
|
||||
m.throws_local_simple_error()
|
||||
assert not isinstance(excinfo.value, cm.LocalSimpleException)
|
||||
assert msg(excinfo.value) == "this mod"
|
||||
430
third_party/pybind11/tests/test_factory_constructors.cpp
vendored
Normal file
430
third_party/pybind11/tests/test_factory_constructors.cpp
vendored
Normal file
@@ -0,0 +1,430 @@
|
||||
/*
|
||||
tests/test_factory_constructors.cpp -- tests construction from a factory function
|
||||
via py::init_factory()
|
||||
|
||||
Copyright (c) 2017 Jason Rhinelander <jason@imaginary.ca>
|
||||
|
||||
All rights reserved. Use of this source code is governed by a
|
||||
BSD-style license that can be found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "constructor_stats.h"
|
||||
#include "pybind11_tests.h"
|
||||
|
||||
#include <cmath>
|
||||
#include <new>
|
||||
#include <utility>
|
||||
|
||||
// Classes for testing python construction via C++ factory function:
|
||||
// Not publicly constructible, copyable, or movable:
|
||||
class TestFactory1 {
|
||||
friend class TestFactoryHelper;
|
||||
TestFactory1() : value("(empty)") { print_default_created(this); }
|
||||
explicit TestFactory1(int v) : value(std::to_string(v)) { print_created(this, value); }
|
||||
explicit TestFactory1(std::string v) : value(std::move(v)) { print_created(this, value); }
|
||||
|
||||
public:
|
||||
std::string value;
|
||||
TestFactory1(TestFactory1 &&) = delete;
|
||||
TestFactory1(const TestFactory1 &) = delete;
|
||||
TestFactory1 &operator=(TestFactory1 &&) = delete;
|
||||
TestFactory1 &operator=(const TestFactory1 &) = delete;
|
||||
~TestFactory1() { print_destroyed(this); }
|
||||
};
|
||||
// Non-public construction, but moveable:
|
||||
class TestFactory2 {
|
||||
friend class TestFactoryHelper;
|
||||
TestFactory2() : value("(empty2)") { print_default_created(this); }
|
||||
explicit TestFactory2(int v) : value(std::to_string(v)) { print_created(this, value); }
|
||||
explicit TestFactory2(std::string v) : value(std::move(v)) { print_created(this, value); }
|
||||
|
||||
public:
|
||||
TestFactory2(TestFactory2 &&m) noexcept : value{std::move(m.value)} {
|
||||
print_move_created(this);
|
||||
}
|
||||
TestFactory2 &operator=(TestFactory2 &&m) noexcept {
|
||||
value = std::move(m.value);
|
||||
print_move_assigned(this);
|
||||
return *this;
|
||||
}
|
||||
std::string value;
|
||||
~TestFactory2() { print_destroyed(this); }
|
||||
};
|
||||
// Mixed direct/factory construction:
|
||||
class TestFactory3 {
|
||||
protected:
|
||||
friend class TestFactoryHelper;
|
||||
TestFactory3() : value("(empty3)") { print_default_created(this); }
|
||||
explicit TestFactory3(int v) : value(std::to_string(v)) { print_created(this, value); }
|
||||
|
||||
public:
|
||||
explicit TestFactory3(std::string v) : value(std::move(v)) { print_created(this, value); }
|
||||
TestFactory3(TestFactory3 &&m) noexcept : value{std::move(m.value)} {
|
||||
print_move_created(this);
|
||||
}
|
||||
TestFactory3 &operator=(TestFactory3 &&m) noexcept {
|
||||
value = std::move(m.value);
|
||||
print_move_assigned(this);
|
||||
return *this;
|
||||
}
|
||||
std::string value;
|
||||
virtual ~TestFactory3() { print_destroyed(this); }
|
||||
};
|
||||
// Inheritance test
|
||||
class TestFactory4 : public TestFactory3 {
|
||||
public:
|
||||
TestFactory4() : TestFactory3() { print_default_created(this); }
|
||||
explicit TestFactory4(int v) : TestFactory3(v) { print_created(this, v); }
|
||||
~TestFactory4() override { print_destroyed(this); }
|
||||
};
|
||||
// Another class for an invalid downcast test
|
||||
class TestFactory5 : public TestFactory3 {
|
||||
public:
|
||||
explicit TestFactory5(int i) : TestFactory3(i) { print_created(this, i); }
|
||||
~TestFactory5() override { print_destroyed(this); }
|
||||
};
|
||||
|
||||
class TestFactory6 {
|
||||
protected:
|
||||
int value;
|
||||
bool alias = false;
|
||||
|
||||
public:
|
||||
explicit TestFactory6(int i) : value{i} { print_created(this, i); }
|
||||
TestFactory6(TestFactory6 &&f) noexcept {
|
||||
print_move_created(this);
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-prefer-member-initializer)
|
||||
value = f.value;
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-prefer-member-initializer)
|
||||
alias = f.alias;
|
||||
}
|
||||
TestFactory6(const TestFactory6 &f) {
|
||||
print_copy_created(this);
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-prefer-member-initializer)
|
||||
value = f.value;
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-prefer-member-initializer)
|
||||
alias = f.alias;
|
||||
}
|
||||
virtual ~TestFactory6() { print_destroyed(this); }
|
||||
virtual int get() { return value; }
|
||||
bool has_alias() const { return alias; }
|
||||
};
|
||||
class PyTF6 : public TestFactory6 {
|
||||
public:
|
||||
// Special constructor that allows the factory to construct a PyTF6 from a TestFactory6 only
|
||||
// when an alias is needed:
|
||||
explicit PyTF6(TestFactory6 &&base) : TestFactory6(std::move(base)) {
|
||||
alias = true;
|
||||
print_created(this, "move", value);
|
||||
}
|
||||
explicit PyTF6(int i) : TestFactory6(i) {
|
||||
alias = true;
|
||||
print_created(this, i);
|
||||
}
|
||||
PyTF6(PyTF6 &&f) noexcept : TestFactory6(std::move(f)) { print_move_created(this); }
|
||||
PyTF6(const PyTF6 &f) : TestFactory6(f) { print_copy_created(this); }
|
||||
explicit PyTF6(std::string s) : TestFactory6((int) s.size()) {
|
||||
alias = true;
|
||||
print_created(this, s);
|
||||
}
|
||||
~PyTF6() override { print_destroyed(this); }
|
||||
int get() override { PYBIND11_OVERRIDE(int, TestFactory6, get, /*no args*/); }
|
||||
};
|
||||
|
||||
class TestFactory7 {
|
||||
protected:
|
||||
int value;
|
||||
bool alias = false;
|
||||
|
||||
public:
|
||||
explicit TestFactory7(int i) : value{i} { print_created(this, i); }
|
||||
TestFactory7(TestFactory7 &&f) noexcept {
|
||||
print_move_created(this);
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-prefer-member-initializer)
|
||||
value = f.value;
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-prefer-member-initializer)
|
||||
alias = f.alias;
|
||||
}
|
||||
TestFactory7(const TestFactory7 &f) {
|
||||
print_copy_created(this);
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-prefer-member-initializer)
|
||||
value = f.value;
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-prefer-member-initializer)
|
||||
alias = f.alias;
|
||||
}
|
||||
virtual ~TestFactory7() { print_destroyed(this); }
|
||||
virtual int get() { return value; }
|
||||
bool has_alias() const { return alias; }
|
||||
};
|
||||
class PyTF7 : public TestFactory7 {
|
||||
public:
|
||||
explicit PyTF7(int i) : TestFactory7(i) {
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-prefer-member-initializer)
|
||||
alias = true;
|
||||
print_created(this, i);
|
||||
}
|
||||
PyTF7(PyTF7 &&f) noexcept : TestFactory7(std::move(f)) { print_move_created(this); }
|
||||
PyTF7(const PyTF7 &f) : TestFactory7(f) { print_copy_created(this); }
|
||||
~PyTF7() override { print_destroyed(this); }
|
||||
int get() override { PYBIND11_OVERRIDE(int, TestFactory7, get, /*no args*/); }
|
||||
};
|
||||
|
||||
class TestFactoryHelper {
|
||||
public:
|
||||
// Non-movable, non-copyable type:
|
||||
// Return via pointer:
|
||||
static TestFactory1 *construct1() { return new TestFactory1(); }
|
||||
// Holder:
|
||||
static std::unique_ptr<TestFactory1> construct1(int a) {
|
||||
return std::unique_ptr<TestFactory1>(new TestFactory1(a));
|
||||
}
|
||||
// pointer again
|
||||
static TestFactory1 *construct1_string(std::string a) {
|
||||
return new TestFactory1(std::move(a));
|
||||
}
|
||||
|
||||
// Moveable type:
|
||||
// pointer:
|
||||
static TestFactory2 *construct2() { return new TestFactory2(); }
|
||||
// holder:
|
||||
static std::unique_ptr<TestFactory2> construct2(int a) {
|
||||
return std::unique_ptr<TestFactory2>(new TestFactory2(a));
|
||||
}
|
||||
// by value moving:
|
||||
static TestFactory2 construct2(std::string a) { return TestFactory2(std::move(a)); }
|
||||
|
||||
// shared_ptr holder type:
|
||||
// pointer:
|
||||
static TestFactory3 *construct3() { return new TestFactory3(); }
|
||||
// holder:
|
||||
static std::shared_ptr<TestFactory3> construct3(int a) {
|
||||
return std::shared_ptr<TestFactory3>(new TestFactory3(a));
|
||||
}
|
||||
};
|
||||
|
||||
TEST_SUBMODULE(factory_constructors, m) {
|
||||
|
||||
// Define various trivial types to allow simpler overload resolution:
|
||||
py::module_ m_tag = m.def_submodule("tag");
|
||||
#define MAKE_TAG_TYPE(Name) \
|
||||
struct Name##_tag {}; \
|
||||
py::class_<Name##_tag>(m_tag, #Name "_tag").def(py::init<>()); \
|
||||
m_tag.attr(#Name) = py::cast(Name##_tag{})
|
||||
MAKE_TAG_TYPE(pointer);
|
||||
MAKE_TAG_TYPE(unique_ptr);
|
||||
MAKE_TAG_TYPE(move);
|
||||
MAKE_TAG_TYPE(shared_ptr);
|
||||
MAKE_TAG_TYPE(derived);
|
||||
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);
|
||||
MAKE_TAG_TYPE(unaliasable);
|
||||
MAKE_TAG_TYPE(mixed);
|
||||
|
||||
// test_init_factory_basic, test_bad_type
|
||||
py::class_<TestFactory1>(m, "TestFactory1")
|
||||
.def(py::init([](unique_ptr_tag, int v) { return TestFactoryHelper::construct1(v); }))
|
||||
.def(py::init(&TestFactoryHelper::construct1_string)) // raw function pointer
|
||||
.def(py::init([](pointer_tag) { return TestFactoryHelper::construct1(); }))
|
||||
.def(py::init(
|
||||
[](py::handle, int v, py::handle) { return TestFactoryHelper::construct1(v); }))
|
||||
.def_readwrite("value", &TestFactory1::value);
|
||||
py::class_<TestFactory2>(m, "TestFactory2")
|
||||
.def(py::init([](pointer_tag, int v) { return TestFactoryHelper::construct2(v); }))
|
||||
.def(py::init([](unique_ptr_tag, std::string v) {
|
||||
return TestFactoryHelper::construct2(std::move(v));
|
||||
}))
|
||||
.def(py::init([](move_tag) { return TestFactoryHelper::construct2(); }))
|
||||
.def_readwrite("value", &TestFactory2::value);
|
||||
|
||||
// Stateful & reused:
|
||||
int c = 1;
|
||||
auto c4a = [c](pointer_tag, TF4_tag, int a) {
|
||||
(void) c;
|
||||
return new TestFactory4(a);
|
||||
};
|
||||
|
||||
// test_init_factory_basic, test_init_factory_casting
|
||||
py::class_<TestFactory3, std::shared_ptr<TestFactory3>> pyTestFactory3(m, "TestFactory3");
|
||||
pyTestFactory3
|
||||
.def(py::init([](pointer_tag, int v) { return TestFactoryHelper::construct3(v); }))
|
||||
.def(py::init([](shared_ptr_tag) { return TestFactoryHelper::construct3(); }));
|
||||
ignoreOldStyleInitWarnings([&pyTestFactory3]() {
|
||||
pyTestFactory3.def("__init__", [](TestFactory3 &self, std::string v) {
|
||||
new (&self) TestFactory3(std::move(v));
|
||||
}); // placement-new ctor
|
||||
});
|
||||
pyTestFactory3
|
||||
// factories returning a derived type:
|
||||
.def(py::init(c4a)) // derived ptr
|
||||
.def(py::init([](pointer_tag, TF5_tag, int a) { return new TestFactory5(a); }))
|
||||
// derived shared ptr:
|
||||
.def(py::init(
|
||||
[](shared_ptr_tag, TF4_tag, int a) { return std::make_shared<TestFactory4>(a); }))
|
||||
.def(py::init(
|
||||
[](shared_ptr_tag, TF5_tag, int a) { return std::make_shared<TestFactory5>(a); }))
|
||||
|
||||
// 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);
|
||||
|
||||
// test_init_factory_casting
|
||||
py::class_<TestFactory4, TestFactory3, std::shared_ptr<TestFactory4>>(m, "TestFactory4")
|
||||
.def(py::init(c4a)) // pointer
|
||||
;
|
||||
|
||||
// Doesn't need to be registered, but registering makes getting ConstructorStats easier:
|
||||
py::class_<TestFactory5, TestFactory3, std::shared_ptr<TestFactory5>>(m, "TestFactory5");
|
||||
|
||||
// test_init_factory_alias
|
||||
// Alias testing
|
||||
py::class_<TestFactory6, PyTF6>(m, "TestFactory6")
|
||||
.def(py::init([](base_tag, int i) { return TestFactory6(i); }))
|
||||
.def(py::init([](alias_tag, int i) { return PyTF6(i); }))
|
||||
.def(py::init([](alias_tag, std::string s) { return PyTF6(std::move(s)); }))
|
||||
.def(py::init([](alias_tag, pointer_tag, int i) { return new PyTF6(i); }))
|
||||
.def(py::init([](base_tag, pointer_tag, int i) { return new TestFactory6(i); }))
|
||||
.def(py::init(
|
||||
[](base_tag, alias_tag, pointer_tag, int i) { return (TestFactory6 *) new PyTF6(i); }))
|
||||
|
||||
.def("get", &TestFactory6::get)
|
||||
.def("has_alias", &TestFactory6::has_alias)
|
||||
|
||||
.def_static(
|
||||
"get_cstats", &ConstructorStats::get<TestFactory6>, py::return_value_policy::reference)
|
||||
.def_static(
|
||||
"get_alias_cstats", &ConstructorStats::get<PyTF6>, py::return_value_policy::reference);
|
||||
|
||||
// test_init_factory_dual
|
||||
// Separate alias constructor testing
|
||||
py::class_<TestFactory7, PyTF7, std::shared_ptr<TestFactory7>>(m, "TestFactory7")
|
||||
.def(py::init([](int i) { return TestFactory7(i); }, [](int i) { return PyTF7(i); }))
|
||||
.def(py::init([](pointer_tag, int i) { return new TestFactory7(i); },
|
||||
[](pointer_tag, int i) { return new PyTF7(i); }))
|
||||
.def(py::init([](mixed_tag, int i) { return new TestFactory7(i); },
|
||||
[](mixed_tag, int i) { return PyTF7(i); }))
|
||||
.def(py::init([](mixed_tag, const std::string &s) { return TestFactory7((int) s.size()); },
|
||||
[](mixed_tag, const std::string &s) { return new PyTF7((int) s.size()); }))
|
||||
.def(py::init([](base_tag, pointer_tag, int i) { return new TestFactory7(i); },
|
||||
[](base_tag, pointer_tag, int i) { return (TestFactory7 *) new PyTF7(i); }))
|
||||
.def(py::init([](alias_tag, pointer_tag, int i) { return new PyTF7(i); },
|
||||
[](alias_tag, pointer_tag, int i) { return new PyTF7(10 * i); }))
|
||||
.def(py::init(
|
||||
[](shared_ptr_tag, base_tag, int i) { return std::make_shared<TestFactory7>(i); },
|
||||
[](shared_ptr_tag, base_tag, int i) {
|
||||
auto *p = new PyTF7(i);
|
||||
return std::shared_ptr<TestFactory7>(p);
|
||||
}))
|
||||
.def(py::init([](shared_ptr_tag,
|
||||
invalid_base_tag,
|
||||
int i) { return std::make_shared<TestFactory7>(i); },
|
||||
[](shared_ptr_tag, invalid_base_tag, int i) {
|
||||
return std::make_shared<TestFactory7>(i);
|
||||
})) // <-- invalid alias factory
|
||||
|
||||
.def("get", &TestFactory7::get)
|
||||
.def("has_alias", &TestFactory7::has_alias)
|
||||
|
||||
.def_static(
|
||||
"get_cstats", &ConstructorStats::get<TestFactory7>, py::return_value_policy::reference)
|
||||
.def_static(
|
||||
"get_alias_cstats", &ConstructorStats::get<PyTF7>, py::return_value_policy::reference);
|
||||
|
||||
// test_placement_new_alternative
|
||||
// Class with a custom new operator but *without* a placement new operator (issue #948)
|
||||
class NoPlacementNew {
|
||||
public:
|
||||
explicit NoPlacementNew(int i) : i(i) {}
|
||||
static void *operator new(std::size_t s) {
|
||||
auto *p = ::operator new(s);
|
||||
py::print("operator new called, returning", reinterpret_cast<uintptr_t>(p));
|
||||
return p;
|
||||
}
|
||||
static void operator delete(void *p) {
|
||||
py::print("operator delete called on", reinterpret_cast<uintptr_t>(p));
|
||||
::operator delete(p);
|
||||
}
|
||||
int i;
|
||||
};
|
||||
// As of 2.2, `py::init<args>` no longer requires placement new
|
||||
py::class_<NoPlacementNew>(m, "NoPlacementNew")
|
||||
.def(py::init<int>())
|
||||
.def(py::init([]() { return new NoPlacementNew(100); }))
|
||||
.def_readwrite("i", &NoPlacementNew::i);
|
||||
|
||||
// test_reallocations
|
||||
// Class that has verbose operator_new/operator_delete calls
|
||||
struct NoisyAlloc {
|
||||
NoisyAlloc(const NoisyAlloc &) = default;
|
||||
explicit NoisyAlloc(int i) { py::print(py::str("NoisyAlloc(int {})").format(i)); }
|
||||
explicit NoisyAlloc(double d) { py::print(py::str("NoisyAlloc(double {})").format(d)); }
|
||||
~NoisyAlloc() { py::print("~NoisyAlloc()"); }
|
||||
|
||||
static void *operator new(size_t s) {
|
||||
py::print("noisy new");
|
||||
return ::operator new(s);
|
||||
}
|
||||
static void *operator new(size_t, void *p) {
|
||||
py::print("noisy placement new");
|
||||
return p;
|
||||
}
|
||||
static void operator delete(void *p, size_t) {
|
||||
py::print("noisy delete");
|
||||
::operator delete(p);
|
||||
}
|
||||
static void operator delete(void *, void *) { py::print("noisy placement delete"); }
|
||||
};
|
||||
|
||||
py::class_<NoisyAlloc> pyNoisyAlloc(m, "NoisyAlloc");
|
||||
// Since these overloads have the same number of arguments, the dispatcher will try each of
|
||||
// them until the arguments convert. Thus we can get a pre-allocation here when passing a
|
||||
// single non-integer:
|
||||
ignoreOldStyleInitWarnings([&pyNoisyAlloc]() {
|
||||
pyNoisyAlloc.def("__init__", [](NoisyAlloc *a, int i) {
|
||||
new (a) NoisyAlloc(i);
|
||||
}); // Regular constructor, runs first, requires preallocation
|
||||
});
|
||||
|
||||
pyNoisyAlloc.def(py::init([](double d) { return new NoisyAlloc(d); }));
|
||||
|
||||
// The two-argument version: first the factory pointer overload.
|
||||
pyNoisyAlloc.def(py::init([](int i, int) { return new NoisyAlloc(i); }));
|
||||
// Return-by-value:
|
||||
pyNoisyAlloc.def(py::init([](double d, int) { return NoisyAlloc(d); }));
|
||||
// Old-style placement new init; requires preallocation
|
||||
ignoreOldStyleInitWarnings([&pyNoisyAlloc]() {
|
||||
pyNoisyAlloc.def("__init__",
|
||||
[](NoisyAlloc &a, double d, double) { new (&a) NoisyAlloc(d); });
|
||||
});
|
||||
// Requires deallocation of previous overload preallocated value:
|
||||
pyNoisyAlloc.def(py::init([](int i, double) { return new NoisyAlloc(i); }));
|
||||
// Regular again: requires yet another preallocation
|
||||
ignoreOldStyleInitWarnings([&pyNoisyAlloc]() {
|
||||
pyNoisyAlloc.def(
|
||||
"__init__", [](NoisyAlloc &a, int i, const std::string &) { new (&a) NoisyAlloc(i); });
|
||||
});
|
||||
|
||||
// static_assert testing (the following def's should all fail with appropriate compilation
|
||||
// errors):
|
||||
#if 0
|
||||
struct BadF1Base {};
|
||||
struct BadF1 : BadF1Base {};
|
||||
struct PyBadF1 : BadF1 {};
|
||||
py::class_<BadF1, PyBadF1, std::shared_ptr<BadF1>> bf1(m, "BadF1");
|
||||
// wrapped factory function must return a compatible pointer, holder, or value
|
||||
bf1.def(py::init([]() { return 3; }));
|
||||
// incompatible factory function pointer return type
|
||||
bf1.def(py::init([]() { static int three = 3; return &three; }));
|
||||
// incompatible factory function std::shared_ptr<T> return type: cannot convert shared_ptr<T> to holder
|
||||
// (non-polymorphic base)
|
||||
bf1.def(py::init([]() { return std::shared_ptr<BadF1Base>(new BadF1()); }));
|
||||
#endif
|
||||
}
|
||||
504
third_party/pybind11/tests/test_factory_constructors.py
vendored
Normal file
504
third_party/pybind11/tests/test_factory_constructors.py
vendored
Normal file
@@ -0,0 +1,504 @@
|
||||
import re
|
||||
|
||||
import pytest
|
||||
|
||||
from pybind11_tests import ConstructorStats
|
||||
from pybind11_tests import factory_constructors as m
|
||||
from pybind11_tests.factory_constructors import tag
|
||||
|
||||
|
||||
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[0].alive() # force gc
|
||||
n_inst = ConstructorStats.detail_reg_inst()
|
||||
|
||||
x1 = m.TestFactory1(tag.unique_ptr, 3)
|
||||
assert x1.value == "3"
|
||||
y1 = m.TestFactory1(tag.pointer)
|
||||
assert y1.value == "(empty)"
|
||||
z1 = m.TestFactory1("hi!")
|
||||
assert z1.value == "hi!"
|
||||
|
||||
assert ConstructorStats.detail_reg_inst() == n_inst + 3
|
||||
|
||||
x2 = m.TestFactory2(tag.move)
|
||||
assert x2.value == "(empty2)"
|
||||
y2 = m.TestFactory2(tag.pointer, 7)
|
||||
assert y2.value == "7"
|
||||
z2 = m.TestFactory2(tag.unique_ptr, "hi again")
|
||||
assert z2.value == "hi again"
|
||||
|
||||
assert ConstructorStats.detail_reg_inst() == n_inst + 6
|
||||
|
||||
x3 = m.TestFactory3(tag.shared_ptr)
|
||||
assert x3.value == "(empty3)"
|
||||
y3 = m.TestFactory3(tag.pointer, 42)
|
||||
assert y3.value == "42"
|
||||
z3 = m.TestFactory3("bye")
|
||||
assert z3.value == "bye"
|
||||
|
||||
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
|
||||
|
||||
del x1, y2, y3, z3
|
||||
assert [i.alive() for i in cstats] == [2, 2, 1]
|
||||
assert ConstructorStats.detail_reg_inst() == n_inst + 5
|
||||
del x2, x3, y1, z1, z2
|
||||
assert [i.alive() for i in cstats] == [0, 0, 0]
|
||||
assert ConstructorStats.detail_reg_inst() == n_inst
|
||||
|
||||
assert [i.values() for i in cstats] == [
|
||||
["3", "hi!"],
|
||||
["7", "hi again"],
|
||||
["42", "bye"],
|
||||
]
|
||||
assert [i.default_constructions for i in cstats] == [1, 1, 1]
|
||||
|
||||
|
||||
def test_init_factory_signature(msg):
|
||||
with pytest.raises(TypeError) as excinfo:
|
||||
m.TestFactory1("invalid", "constructor", "arguments")
|
||||
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)
|
||||
3. m.factory_constructors.TestFactory1(arg0: m.factory_constructors.tag.pointer_tag)
|
||||
4. m.factory_constructors.TestFactory1(arg0: handle, arg1: int, arg2: handle)
|
||||
|
||||
Invoked with: 'invalid', 'constructor', 'arguments'
|
||||
""")
|
||||
|
||||
assert (msg(m.TestFactory1.__init__.__doc__) == """
|
||||
__init__(*args, **kwargs)
|
||||
Overloaded function.
|
||||
|
||||
1. __init__(self: m.factory_constructors.TestFactory1, arg0: m.factory_constructors.tag.unique_ptr_tag, arg1: int) -> None
|
||||
|
||||
2. __init__(self: m.factory_constructors.TestFactory1, arg0: str) -> None
|
||||
|
||||
3. __init__(self: m.factory_constructors.TestFactory1, arg0: m.factory_constructors.tag.pointer_tag) -> None
|
||||
|
||||
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[0].alive() # force gc
|
||||
n_inst = ConstructorStats.detail_reg_inst()
|
||||
|
||||
# Construction from derived references:
|
||||
a = m.TestFactory3(tag.pointer, tag.TF4, 4)
|
||||
assert a.value == "4"
|
||||
b = m.TestFactory3(tag.shared_ptr, tag.TF4, 5)
|
||||
assert b.value == "5"
|
||||
c = m.TestFactory3(tag.pointer, tag.TF5, 6)
|
||||
assert c.value == "6"
|
||||
d = m.TestFactory3(tag.shared_ptr, tag.TF5, 7)
|
||||
assert d.value == "7"
|
||||
|
||||
assert ConstructorStats.detail_reg_inst() == n_inst + 4
|
||||
|
||||
# Shared a lambda with TF3:
|
||||
e = m.TestFactory4(tag.pointer, tag.TF4, 8)
|
||||
assert e.value == "8"
|
||||
|
||||
assert ConstructorStats.detail_reg_inst() == n_inst + 5
|
||||
assert [i.alive() for i in cstats] == [5, 3, 2]
|
||||
|
||||
del a
|
||||
assert [i.alive() for i in cstats] == [4, 2, 2]
|
||||
assert ConstructorStats.detail_reg_inst() == n_inst + 4
|
||||
|
||||
del b, c, e
|
||||
assert [i.alive() for i in cstats] == [1, 0, 1]
|
||||
assert ConstructorStats.detail_reg_inst() == n_inst + 1
|
||||
|
||||
del d
|
||||
assert [i.alive() for i in cstats] == [0, 0, 0]
|
||||
assert ConstructorStats.detail_reg_inst() == n_inst
|
||||
|
||||
assert [i.values() for i in cstats] == [
|
||||
["4", "5", "6", "7", "8"],
|
||||
["4", "5", "8"],
|
||||
["6", "7"],
|
||||
]
|
||||
|
||||
|
||||
def test_init_factory_alias():
|
||||
"""Tests py::init_factory() wrapper with value conversions and alias types"""
|
||||
|
||||
cstats = [m.TestFactory6.get_cstats(), m.TestFactory6.get_alias_cstats()]
|
||||
cstats[0].alive() # force gc
|
||||
n_inst = ConstructorStats.detail_reg_inst()
|
||||
|
||||
a = m.TestFactory6(tag.base, 1)
|
||||
assert a.get() == 1
|
||||
assert not a.has_alias()
|
||||
b = m.TestFactory6(tag.alias, "hi there")
|
||||
assert b.get() == 8
|
||||
assert b.has_alias()
|
||||
c = m.TestFactory6(tag.alias, 3)
|
||||
assert c.get() == 3
|
||||
assert c.has_alias()
|
||||
d = m.TestFactory6(tag.alias, tag.pointer, 4)
|
||||
assert d.get() == 4
|
||||
assert d.has_alias()
|
||||
e = m.TestFactory6(tag.base, tag.pointer, 5)
|
||||
assert e.get() == 5
|
||||
assert not e.has_alias()
|
||||
f = m.TestFactory6(tag.base, tag.alias, tag.pointer, 6)
|
||||
assert f.get() == 6
|
||||
assert f.has_alias()
|
||||
|
||||
assert ConstructorStats.detail_reg_inst() == n_inst + 6
|
||||
assert [i.alive() for i in cstats] == [6, 4]
|
||||
|
||||
del a, b, e
|
||||
assert [i.alive() for i in cstats] == [3, 3]
|
||||
assert ConstructorStats.detail_reg_inst() == n_inst + 3
|
||||
del f, c, d
|
||||
assert [i.alive() for i in cstats] == [0, 0]
|
||||
assert ConstructorStats.detail_reg_inst() == n_inst
|
||||
|
||||
class MyTest(m.TestFactory6):
|
||||
def __init__(self, *args):
|
||||
m.TestFactory6.__init__(self, *args)
|
||||
|
||||
def get(self):
|
||||
return -5 + m.TestFactory6.get(self)
|
||||
|
||||
# Return Class by value, moved into new alias:
|
||||
z = MyTest(tag.base, 123)
|
||||
assert z.get() == 118
|
||||
assert z.has_alias()
|
||||
|
||||
# Return alias by value, moved into new alias:
|
||||
y = MyTest(tag.alias, "why hello!")
|
||||
assert y.get() == 5
|
||||
assert y.has_alias()
|
||||
|
||||
# Return Class by pointer, moved into new alias then original destroyed:
|
||||
x = MyTest(tag.base, tag.pointer, 47)
|
||||
assert x.get() == 42
|
||||
assert x.has_alias()
|
||||
|
||||
assert ConstructorStats.detail_reg_inst() == n_inst + 3
|
||||
assert [i.alive() for i in cstats] == [3, 3]
|
||||
del x, y, z
|
||||
assert [i.alive() for i in cstats] == [0, 0]
|
||||
assert ConstructorStats.detail_reg_inst() == n_inst
|
||||
|
||||
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"
|
||||
],
|
||||
]
|
||||
|
||||
|
||||
def test_init_factory_dual():
|
||||
"""Tests init factory functions with dual main/alias factory functions"""
|
||||
from pybind11_tests.factory_constructors import TestFactory7
|
||||
|
||||
cstats = [TestFactory7.get_cstats(), TestFactory7.get_alias_cstats()]
|
||||
cstats[0].alive() # force gc
|
||||
n_inst = ConstructorStats.detail_reg_inst()
|
||||
|
||||
class PythFactory7(TestFactory7):
|
||||
def get(self):
|
||||
return 100 + TestFactory7.get(self)
|
||||
|
||||
a1 = TestFactory7(1)
|
||||
a2 = PythFactory7(2)
|
||||
assert a1.get() == 1
|
||||
assert a2.get() == 102
|
||||
assert not a1.has_alias()
|
||||
assert a2.has_alias()
|
||||
|
||||
b1 = TestFactory7(tag.pointer, 3)
|
||||
b2 = PythFactory7(tag.pointer, 4)
|
||||
assert b1.get() == 3
|
||||
assert b2.get() == 104
|
||||
assert not b1.has_alias()
|
||||
assert b2.has_alias()
|
||||
|
||||
c1 = TestFactory7(tag.mixed, 5)
|
||||
c2 = PythFactory7(tag.mixed, 6)
|
||||
assert c1.get() == 5
|
||||
assert c2.get() == 106
|
||||
assert not c1.has_alias()
|
||||
assert c2.has_alias()
|
||||
|
||||
d1 = TestFactory7(tag.base, tag.pointer, 7)
|
||||
d2 = PythFactory7(tag.base, tag.pointer, 8)
|
||||
assert d1.get() == 7
|
||||
assert d2.get() == 108
|
||||
assert not d1.has_alias()
|
||||
assert d2.has_alias()
|
||||
|
||||
# Both return an alias; the second multiplies the value by 10:
|
||||
e1 = TestFactory7(tag.alias, tag.pointer, 9)
|
||||
e2 = PythFactory7(tag.alias, tag.pointer, 10)
|
||||
assert e1.get() == 9
|
||||
assert e2.get() == 200
|
||||
assert e1.has_alias()
|
||||
assert e2.has_alias()
|
||||
|
||||
f1 = TestFactory7(tag.shared_ptr, tag.base, 11)
|
||||
f2 = PythFactory7(tag.shared_ptr, tag.base, 12)
|
||||
assert f1.get() == 11
|
||||
assert f2.get() == 112
|
||||
assert not f1.has_alias()
|
||||
assert f2.has_alias()
|
||||
|
||||
g1 = TestFactory7(tag.shared_ptr, tag.invalid_base, 13)
|
||||
assert g1.get() == 13
|
||||
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 [i.alive() for i in cstats] == [13, 7]
|
||||
assert ConstructorStats.detail_reg_inst() == n_inst + 13
|
||||
|
||||
del a1, a2, b1, d1, e1, e2
|
||||
assert [i.alive() for i in cstats] == [7, 4]
|
||||
assert ConstructorStats.detail_reg_inst() == n_inst + 7
|
||||
del b2, c1, c2, d2, f1, f2, g1
|
||||
assert [i.alive() for i in cstats] == [0, 0]
|
||||
assert ConstructorStats.detail_reg_inst() == n_inst
|
||||
|
||||
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"],
|
||||
]
|
||||
|
||||
|
||||
def test_no_placement_new(capture):
|
||||
"""Prior to 2.2, `py::init<...>` relied on the type supporting placement
|
||||
new; this tests a class without placement new support."""
|
||||
with capture:
|
||||
a = m.NoPlacementNew(123)
|
||||
|
||||
found = re.search(r"^operator new called, returning (\d+)\n$",
|
||||
str(capture))
|
||||
assert found
|
||||
assert a.i == 123
|
||||
with capture:
|
||||
del a
|
||||
pytest.gc_collect()
|
||||
assert capture == "operator delete called on " + found.group(1)
|
||||
|
||||
with capture:
|
||||
b = m.NoPlacementNew()
|
||||
|
||||
found = re.search(r"^operator new called, returning (\d+)\n$",
|
||||
str(capture))
|
||||
assert found
|
||||
assert b.i == 100
|
||||
with capture:
|
||||
del b
|
||||
pytest.gc_collect()
|
||||
assert capture == "operator delete called on " + found.group(1)
|
||||
|
||||
|
||||
def test_multiple_inheritance():
|
||||
class MITest(m.TestFactory1, m.TestFactory2):
|
||||
def __init__(self):
|
||||
m.TestFactory1.__init__(self, tag.unique_ptr, 33)
|
||||
m.TestFactory2.__init__(self, tag.move)
|
||||
|
||||
a = MITest()
|
||||
assert m.TestFactory1.value.fget(a) == "33"
|
||||
assert m.TestFactory2.value.fget(a) == "(empty2)"
|
||||
|
||||
|
||||
def create_and_destroy(*args):
|
||||
a = m.NoisyAlloc(*args)
|
||||
print("---")
|
||||
del a
|
||||
pytest.gc_collect()
|
||||
|
||||
|
||||
def strip_comments(s):
|
||||
return re.sub(r"\s+#.*", "", s)
|
||||
|
||||
|
||||
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."""
|
||||
|
||||
pytest.gc_collect()
|
||||
|
||||
with capture:
|
||||
create_and_destroy(1)
|
||||
assert (msg(capture) == """
|
||||
noisy new
|
||||
noisy placement new
|
||||
NoisyAlloc(int 1)
|
||||
---
|
||||
~NoisyAlloc()
|
||||
noisy delete
|
||||
""")
|
||||
|
||||
|
||||
def test_reallocation_b(capture, msg):
|
||||
with capture:
|
||||
create_and_destroy(1.5)
|
||||
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
|
||||
NoisyAlloc(double 1.5) # ... part two, invoking constructor
|
||||
---
|
||||
~NoisyAlloc() # Destructor
|
||||
noisy delete # operator delete
|
||||
""")
|
||||
|
||||
|
||||
def test_reallocation_c(capture, msg):
|
||||
with capture:
|
||||
create_and_destroy(2, 3)
|
||||
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("""
|
||||
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("""
|
||||
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("""
|
||||
noisy new # preallocation needed before invoking placement-new overload
|
||||
noisy delete # deallocation of preallocated storage
|
||||
noisy new # Factory pointer allocation
|
||||
NoisyAlloc(int 4) # factory pointer construction
|
||||
---
|
||||
~NoisyAlloc() # Destructor
|
||||
noisy delete # operator delete
|
||||
""")
|
||||
|
||||
|
||||
def test_reallocation_g(capture, msg):
|
||||
with capture:
|
||||
create_and_destroy(5, "hi")
|
||||
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
|
||||
noisy placement new # Placement new in the second placement new overload
|
||||
NoisyAlloc(int 5) # construction
|
||||
---
|
||||
~NoisyAlloc() # Destructor
|
||||
noisy delete # operator delete
|
||||
""")
|
||||
|
||||
|
||||
def test_invalid_self():
|
||||
"""Tests invocation of the pybind-registered base class with an invalid `self` argument."""
|
||||
|
||||
class NotPybindDerived:
|
||||
pass
|
||||
|
||||
# Attempts to initialize with an invalid type passed as `self`:
|
||||
class BrokenTF1(m.TestFactory1):
|
||||
def __init__(self, bad):
|
||||
if bad == 1:
|
||||
a = m.TestFactory2(tag.pointer, 1)
|
||||
m.TestFactory1.__init__(a, tag.pointer)
|
||||
elif bad == 2:
|
||||
a = NotPybindDerived()
|
||||
m.TestFactory1.__init__(a, tag.pointer)
|
||||
|
||||
# Same as above, but for a class with an alias:
|
||||
class BrokenTF6(m.TestFactory6):
|
||||
def __init__(self, bad):
|
||||
if bad == 0:
|
||||
m.TestFactory6.__init__()
|
||||
elif bad == 1:
|
||||
a = m.TestFactory2(tag.pointer, 1)
|
||||
m.TestFactory6.__init__(a, tag.base, 1)
|
||||
elif bad == 2:
|
||||
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)
|
||||
elif bad == 4:
|
||||
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 or missing `self` argument"
|
||||
)
|
||||
|
||||
for arg in (0, 1, 2, 3, 4):
|
||||
with pytest.raises(TypeError) as excinfo:
|
||||
BrokenTF6(arg)
|
||||
assert (
|
||||
str(excinfo.value) ==
|
||||
"__init__(self, ...) called with invalid or missing `self` argument"
|
||||
)
|
||||
47
third_party/pybind11/tests/test_gil_scoped.cpp
vendored
Normal file
47
third_party/pybind11/tests/test_gil_scoped.cpp
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
tests/test_gil_scoped.cpp -- acquire and release gil
|
||||
|
||||
Copyright (c) 2017 Borja Zarco (Google LLC) <bzarco@google.com>
|
||||
|
||||
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/functional.h>
|
||||
|
||||
#include "pybind11_tests.h"
|
||||
|
||||
class VirtClass {
|
||||
public:
|
||||
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_OVERRIDE(void, VirtClass, virtual_func, ); }
|
||||
void pure_virtual_func() override {
|
||||
PYBIND11_OVERRIDE_PURE(void, VirtClass, pure_virtual_func, );
|
||||
}
|
||||
};
|
||||
|
||||
TEST_SUBMODULE(gil_scoped, m) {
|
||||
py::class_<VirtClass, PyVirtClass>(m, "VirtClass")
|
||||
.def(py::init<>())
|
||||
.def("virtual_func", &VirtClass::virtual_func)
|
||||
.def("pure_virtual_func", &VirtClass::pure_virtual_func);
|
||||
|
||||
m.def("test_callback_py_obj", [](py::object &func) { func(); });
|
||||
m.def("test_callback_std_func", [](const std::function<void()> &func) { func(); });
|
||||
m.def("test_callback_virtual_func", [](VirtClass &virt) { virt.virtual_func(); });
|
||||
m.def("test_callback_pure_virtual_func", [](VirtClass &virt) { virt.pure_virtual_func(); });
|
||||
m.def("test_cross_module_gil", []() {
|
||||
auto cm = py::module_::import("cross_module_gil_utils");
|
||||
auto gil_acquire = reinterpret_cast<void (*)()>(
|
||||
PyLong_AsVoidPtr(cm.attr("gil_acquire_funcaddr").ptr()));
|
||||
py::gil_scoped_release gil_release;
|
||||
gil_acquire();
|
||||
});
|
||||
}
|
||||
93
third_party/pybind11/tests/test_gil_scoped.py
vendored
Normal file
93
third_party/pybind11/tests/test_gil_scoped.py
vendored
Normal file
@@ -0,0 +1,93 @@
|
||||
import multiprocessing
|
||||
import threading
|
||||
|
||||
from pybind11_tests import gil_scoped as m
|
||||
|
||||
|
||||
def _run_in_process(target, *args, **kwargs):
|
||||
"""Runs target in process and returns its exitcode after 10s (None if still alive)."""
|
||||
process = multiprocessing.Process(target=target, args=args, kwargs=kwargs)
|
||||
process.daemon = True
|
||||
try:
|
||||
process.start()
|
||||
# Do not need to wait much, 10s should be more than enough.
|
||||
process.join(timeout=10)
|
||||
return process.exitcode
|
||||
finally:
|
||||
if process.is_alive():
|
||||
process.terminate()
|
||||
|
||||
|
||||
def _python_to_cpp_to_python():
|
||||
"""Calls different C++ functions that come back to Python."""
|
||||
|
||||
class ExtendedVirtClass(m.VirtClass):
|
||||
def virtual_func(self):
|
||||
pass
|
||||
|
||||
def pure_virtual_func(self):
|
||||
pass
|
||||
|
||||
extended = ExtendedVirtClass()
|
||||
m.test_callback_py_obj(lambda: None)
|
||||
m.test_callback_std_func(lambda: None)
|
||||
m.test_callback_virtual_func(extended)
|
||||
m.test_callback_pure_virtual_func(extended)
|
||||
|
||||
|
||||
def _python_to_cpp_to_python_from_threads(num_threads, parallel=False):
|
||||
"""Calls different C++ functions that come back to Python, from Python threads."""
|
||||
threads = []
|
||||
for _ in range(num_threads):
|
||||
thread = threading.Thread(target=_python_to_cpp_to_python)
|
||||
thread.daemon = True
|
||||
thread.start()
|
||||
if parallel:
|
||||
threads.append(thread)
|
||||
else:
|
||||
thread.join()
|
||||
for thread in threads:
|
||||
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.
|
||||
|
||||
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, 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.
|
||||
|
||||
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=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)
|
||||
|
||||
|
||||
# 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.
|
||||
|
||||
This test is for completion, but it was never an issue.
|
||||
"""
|
||||
assert _run_in_process(_python_to_cpp_to_python) == 0
|
||||
|
||||
|
||||
def test_cross_module_gil():
|
||||
"""Makes sure that the GIL can be acquired by another module from a GIL-released state."""
|
||||
m.test_cross_module_gil() # Should not raise a SIGSEGV
|
||||
126
third_party/pybind11/tests/test_iostream.cpp
vendored
Normal file
126
third_party/pybind11/tests/test_iostream.cpp
vendored
Normal file
@@ -0,0 +1,126 @@
|
||||
/*
|
||||
tests/test_iostream.cpp -- Usage of scoped_output_redirect
|
||||
|
||||
Copyright (c) 2017 Henry F. Schreiner
|
||||
|
||||
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/iostream.h>
|
||||
|
||||
#include "pybind11_tests.h"
|
||||
|
||||
#include <atomic>
|
||||
#include <iostream>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
|
||||
void noisy_function(const std::string &msg, bool flush) {
|
||||
|
||||
std::cout << msg;
|
||||
if (flush) {
|
||||
std::cout << std::flush;
|
||||
}
|
||||
}
|
||||
|
||||
void noisy_funct_dual(const std::string &msg, const std::string &emsg) {
|
||||
std::cout << msg;
|
||||
std::cerr << emsg;
|
||||
}
|
||||
|
||||
// object to manage C++ thread
|
||||
// simply repeatedly write to std::cerr until stopped
|
||||
// redirect is called at some point to test the safety of scoped_estream_redirect
|
||||
struct TestThread {
|
||||
TestThread() : stop_{false} {
|
||||
auto thread_f = [this] {
|
||||
static std::mutex cout_mutex;
|
||||
while (!stop_) {
|
||||
{
|
||||
// #HelpAppreciated: Work on iostream.h thread safety.
|
||||
// Without this lock, the clang ThreadSanitizer (tsan) reliably reports a
|
||||
// data race, and this test is predictably flakey on Windows.
|
||||
// For more background see the discussion under
|
||||
// https://github.com/pybind/pybind11/pull/2982 and
|
||||
// https://github.com/pybind/pybind11/pull/2995.
|
||||
const std::lock_guard<std::mutex> lock(cout_mutex);
|
||||
std::cout << "x" << std::flush;
|
||||
}
|
||||
std::this_thread::sleep_for(std::chrono::microseconds(50));
|
||||
}
|
||||
};
|
||||
t_ = new std::thread(std::move(thread_f));
|
||||
}
|
||||
|
||||
~TestThread() { delete t_; }
|
||||
|
||||
void stop() { stop_ = true; }
|
||||
|
||||
void join() const {
|
||||
py::gil_scoped_release gil_lock;
|
||||
t_->join();
|
||||
}
|
||||
|
||||
void sleep() {
|
||||
py::gil_scoped_release gil_lock;
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
||||
}
|
||||
|
||||
std::thread *t_{nullptr};
|
||||
std::atomic<bool> stop_;
|
||||
};
|
||||
|
||||
TEST_SUBMODULE(iostream, m) {
|
||||
|
||||
add_ostream_redirect(m);
|
||||
|
||||
// test_evals
|
||||
|
||||
m.def("captured_output_default", [](const std::string &msg) {
|
||||
py::scoped_ostream_redirect redir;
|
||||
std::cout << msg << std::flush;
|
||||
});
|
||||
|
||||
m.def("captured_output", [](const std::string &msg) {
|
||||
py::scoped_ostream_redirect redir(std::cout, py::module_::import("sys").attr("stdout"));
|
||||
std::cout << msg << std::flush;
|
||||
});
|
||||
|
||||
m.def("guard_output",
|
||||
&noisy_function,
|
||||
py::call_guard<py::scoped_ostream_redirect>(),
|
||||
py::arg("msg"),
|
||||
py::arg("flush") = true);
|
||||
|
||||
m.def("captured_err", [](const std::string &msg) {
|
||||
py::scoped_ostream_redirect redir(std::cerr, py::module_::import("sys").attr("stderr"));
|
||||
std::cerr << msg << std::flush;
|
||||
});
|
||||
|
||||
m.def("noisy_function", &noisy_function, py::arg("msg"), py::arg("flush") = true);
|
||||
|
||||
m.def("dual_guard",
|
||||
&noisy_funct_dual,
|
||||
py::call_guard<py::scoped_ostream_redirect, py::scoped_estream_redirect>(),
|
||||
py::arg("msg"),
|
||||
py::arg("emsg"));
|
||||
|
||||
m.def("raw_output", [](const std::string &msg) { std::cout << msg << std::flush; });
|
||||
|
||||
m.def("raw_err", [](const std::string &msg) { std::cerr << msg << std::flush; });
|
||||
|
||||
m.def("captured_dual", [](const std::string &msg, const std::string &emsg) {
|
||||
py::scoped_ostream_redirect redirout(std::cout, py::module_::import("sys").attr("stdout"));
|
||||
py::scoped_ostream_redirect redirerr(std::cerr, py::module_::import("sys").attr("stderr"));
|
||||
std::cout << msg << std::flush;
|
||||
std::cerr << emsg << std::flush;
|
||||
});
|
||||
|
||||
py::class_<TestThread>(m, "TestThread")
|
||||
.def(py::init<>())
|
||||
.def("stop", &TestThread::stop)
|
||||
.def("join", &TestThread::join)
|
||||
.def("sleep", &TestThread::sleep);
|
||||
}
|
||||
295
third_party/pybind11/tests/test_iostream.py
vendored
Normal file
295
third_party/pybind11/tests/test_iostream.py
vendored
Normal file
@@ -0,0 +1,295 @@
|
||||
from contextlib import redirect_stderr, redirect_stdout
|
||||
from io import StringIO
|
||||
|
||||
from pybind11_tests import iostream as m
|
||||
|
||||
|
||||
def test_captured(capsys):
|
||||
msg = "I've been redirected to Python, I hope!"
|
||||
m.captured_output(msg)
|
||||
stdout, stderr = capsys.readouterr()
|
||||
assert stdout == msg
|
||||
assert stderr == ""
|
||||
|
||||
m.captured_output_default(msg)
|
||||
stdout, stderr = capsys.readouterr()
|
||||
assert stdout == msg
|
||||
assert stderr == ""
|
||||
|
||||
m.captured_err(msg)
|
||||
stdout, stderr = capsys.readouterr()
|
||||
assert stdout == ""
|
||||
assert stderr == msg
|
||||
|
||||
|
||||
def test_captured_large_string(capsys):
|
||||
# Make this bigger than the buffer used on the C++ side: 1024 chars
|
||||
msg = "I've been redirected to Python, I hope!"
|
||||
msg = msg * (1024 // len(msg) + 1)
|
||||
|
||||
m.captured_output_default(msg)
|
||||
stdout, stderr = capsys.readouterr()
|
||||
assert stdout == msg
|
||||
assert stderr == ""
|
||||
|
||||
|
||||
def test_captured_utf8_2byte_offset0(capsys):
|
||||
msg = "\u07FF"
|
||||
msg = "" + msg * (1024 // len(msg) + 1)
|
||||
|
||||
m.captured_output_default(msg)
|
||||
stdout, stderr = capsys.readouterr()
|
||||
assert stdout == msg
|
||||
assert stderr == ""
|
||||
|
||||
|
||||
def test_captured_utf8_2byte_offset1(capsys):
|
||||
msg = "\u07FF"
|
||||
msg = "1" + msg * (1024 // len(msg) + 1)
|
||||
|
||||
m.captured_output_default(msg)
|
||||
stdout, stderr = capsys.readouterr()
|
||||
assert stdout == msg
|
||||
assert stderr == ""
|
||||
|
||||
|
||||
def test_captured_utf8_3byte_offset0(capsys):
|
||||
msg = "\uFFFF"
|
||||
msg = "" + msg * (1024 // len(msg) + 1)
|
||||
|
||||
m.captured_output_default(msg)
|
||||
stdout, stderr = capsys.readouterr()
|
||||
assert stdout == msg
|
||||
assert stderr == ""
|
||||
|
||||
|
||||
def test_captured_utf8_3byte_offset1(capsys):
|
||||
msg = "\uFFFF"
|
||||
msg = "1" + msg * (1024 // len(msg) + 1)
|
||||
|
||||
m.captured_output_default(msg)
|
||||
stdout, stderr = capsys.readouterr()
|
||||
assert stdout == msg
|
||||
assert stderr == ""
|
||||
|
||||
|
||||
def test_captured_utf8_3byte_offset2(capsys):
|
||||
msg = "\uFFFF"
|
||||
msg = "12" + msg * (1024 // len(msg) + 1)
|
||||
|
||||
m.captured_output_default(msg)
|
||||
stdout, stderr = capsys.readouterr()
|
||||
assert stdout == msg
|
||||
assert stderr == ""
|
||||
|
||||
|
||||
def test_captured_utf8_4byte_offset0(capsys):
|
||||
msg = "\U0010FFFF"
|
||||
msg = "" + msg * (1024 // len(msg) + 1)
|
||||
|
||||
m.captured_output_default(msg)
|
||||
stdout, stderr = capsys.readouterr()
|
||||
assert stdout == msg
|
||||
assert stderr == ""
|
||||
|
||||
|
||||
def test_captured_utf8_4byte_offset1(capsys):
|
||||
msg = "\U0010FFFF"
|
||||
msg = "1" + msg * (1024 // len(msg) + 1)
|
||||
|
||||
m.captured_output_default(msg)
|
||||
stdout, stderr = capsys.readouterr()
|
||||
assert stdout == msg
|
||||
assert stderr == ""
|
||||
|
||||
|
||||
def test_captured_utf8_4byte_offset2(capsys):
|
||||
msg = "\U0010FFFF"
|
||||
msg = "12" + msg * (1024 // len(msg) + 1)
|
||||
|
||||
m.captured_output_default(msg)
|
||||
stdout, stderr = capsys.readouterr()
|
||||
assert stdout == msg
|
||||
assert stderr == ""
|
||||
|
||||
|
||||
def test_captured_utf8_4byte_offset3(capsys):
|
||||
msg = "\U0010FFFF"
|
||||
msg = "123" + msg * (1024 // len(msg) + 1)
|
||||
|
||||
m.captured_output_default(msg)
|
||||
stdout, stderr = capsys.readouterr()
|
||||
assert stdout == msg
|
||||
assert stderr == ""
|
||||
|
||||
|
||||
def test_guard_capture(capsys):
|
||||
msg = "I've been redirected to Python, I hope!"
|
||||
m.guard_output(msg)
|
||||
stdout, stderr = capsys.readouterr()
|
||||
assert stdout == msg
|
||||
assert stderr == ""
|
||||
|
||||
|
||||
def test_series_captured(capture):
|
||||
with capture:
|
||||
m.captured_output("a")
|
||||
m.captured_output("b")
|
||||
assert capture == "ab"
|
||||
|
||||
|
||||
def test_flush(capfd):
|
||||
msg = "(not flushed)"
|
||||
msg2 = "(flushed)"
|
||||
|
||||
with m.ostream_redirect():
|
||||
m.noisy_function(msg, flush=False)
|
||||
stdout, stderr = capfd.readouterr()
|
||||
assert stdout == ""
|
||||
|
||||
m.noisy_function(msg2, flush=True)
|
||||
stdout, stderr = capfd.readouterr()
|
||||
assert stdout == msg + msg2
|
||||
|
||||
m.noisy_function(msg, flush=False)
|
||||
|
||||
stdout, stderr = capfd.readouterr()
|
||||
assert stdout == msg
|
||||
|
||||
|
||||
def test_not_captured(capfd):
|
||||
msg = "Something that should not show up in log"
|
||||
stream = StringIO()
|
||||
with redirect_stdout(stream):
|
||||
m.raw_output(msg)
|
||||
stdout, stderr = capfd.readouterr()
|
||||
assert stdout == msg
|
||||
assert stderr == ""
|
||||
assert stream.getvalue() == ""
|
||||
|
||||
stream = StringIO()
|
||||
with redirect_stdout(stream):
|
||||
m.captured_output(msg)
|
||||
stdout, stderr = capfd.readouterr()
|
||||
assert stdout == ""
|
||||
assert stderr == ""
|
||||
assert stream.getvalue() == msg
|
||||
|
||||
|
||||
def test_err(capfd):
|
||||
msg = "Something that should not show up in log"
|
||||
stream = StringIO()
|
||||
with redirect_stderr(stream):
|
||||
m.raw_err(msg)
|
||||
stdout, stderr = capfd.readouterr()
|
||||
assert stdout == ""
|
||||
assert stderr == msg
|
||||
assert stream.getvalue() == ""
|
||||
|
||||
stream = StringIO()
|
||||
with redirect_stderr(stream):
|
||||
m.captured_err(msg)
|
||||
stdout, stderr = capfd.readouterr()
|
||||
assert stdout == ""
|
||||
assert stderr == ""
|
||||
assert stream.getvalue() == msg
|
||||
|
||||
|
||||
def test_multi_captured(capfd):
|
||||
stream = StringIO()
|
||||
with redirect_stdout(stream):
|
||||
m.captured_output("a")
|
||||
m.raw_output("b")
|
||||
m.captured_output("c")
|
||||
m.raw_output("d")
|
||||
stdout, stderr = capfd.readouterr()
|
||||
assert stdout == "bd"
|
||||
assert stream.getvalue() == "ac"
|
||||
|
||||
|
||||
def test_dual(capsys):
|
||||
m.captured_dual("a", "b")
|
||||
stdout, stderr = capsys.readouterr()
|
||||
assert stdout == "a"
|
||||
assert stderr == "b"
|
||||
|
||||
|
||||
def test_redirect(capfd):
|
||||
msg = "Should not be in log!"
|
||||
stream = StringIO()
|
||||
with redirect_stdout(stream):
|
||||
m.raw_output(msg)
|
||||
stdout, stderr = capfd.readouterr()
|
||||
assert stdout == msg
|
||||
assert stream.getvalue() == ""
|
||||
|
||||
stream = StringIO()
|
||||
with redirect_stdout(stream):
|
||||
with m.ostream_redirect():
|
||||
m.raw_output(msg)
|
||||
stdout, stderr = capfd.readouterr()
|
||||
assert stdout == ""
|
||||
assert stream.getvalue() == msg
|
||||
|
||||
stream = StringIO()
|
||||
with redirect_stdout(stream):
|
||||
m.raw_output(msg)
|
||||
stdout, stderr = capfd.readouterr()
|
||||
assert stdout == msg
|
||||
assert stream.getvalue() == ""
|
||||
|
||||
|
||||
def test_redirect_err(capfd):
|
||||
msg = "StdOut"
|
||||
msg2 = "StdErr"
|
||||
|
||||
stream = StringIO()
|
||||
with redirect_stderr(stream):
|
||||
with m.ostream_redirect(stdout=False):
|
||||
m.raw_output(msg)
|
||||
m.raw_err(msg2)
|
||||
stdout, stderr = capfd.readouterr()
|
||||
assert stdout == msg
|
||||
assert stderr == ""
|
||||
assert stream.getvalue() == msg2
|
||||
|
||||
|
||||
def test_redirect_both(capfd):
|
||||
msg = "StdOut"
|
||||
msg2 = "StdErr"
|
||||
|
||||
stream = StringIO()
|
||||
stream2 = StringIO()
|
||||
with redirect_stdout(stream):
|
||||
with redirect_stderr(stream2):
|
||||
with m.ostream_redirect():
|
||||
m.raw_output(msg)
|
||||
m.raw_err(msg2)
|
||||
stdout, stderr = capfd.readouterr()
|
||||
assert stdout == ""
|
||||
assert stderr == ""
|
||||
assert stream.getvalue() == msg
|
||||
assert stream2.getvalue() == msg2
|
||||
|
||||
|
||||
def test_threading():
|
||||
with m.ostream_redirect(stdout=True, stderr=False):
|
||||
# start some threads
|
||||
threads = []
|
||||
|
||||
# start some threads
|
||||
for _j in range(20):
|
||||
threads.append(m.TestThread())
|
||||
|
||||
# give the threads some time to fail
|
||||
threads[0].sleep()
|
||||
|
||||
# stop all the threads
|
||||
for t in threads:
|
||||
t.stop()
|
||||
|
||||
for t in threads:
|
||||
t.join()
|
||||
|
||||
# if a thread segfaults, we don't get here
|
||||
assert True
|
||||
273
third_party/pybind11/tests/test_kwargs_and_defaults.cpp
vendored
Normal file
273
third_party/pybind11/tests/test_kwargs_and_defaults.cpp
vendored
Normal file
@@ -0,0 +1,273 @@
|
||||
/*
|
||||
tests/test_kwargs_and_defaults.cpp -- keyword arguments and default values
|
||||
|
||||
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/stl.h>
|
||||
|
||||
#include "constructor_stats.h"
|
||||
#include "pybind11_tests.h"
|
||||
|
||||
#include <utility>
|
||||
|
||||
TEST_SUBMODULE(kwargs_and_defaults, m) {
|
||||
auto kw_func
|
||||
= [](int x, int y) { return "x=" + std::to_string(x) + ", y=" + std::to_string(y); };
|
||||
|
||||
// test_named_arguments
|
||||
m.def("kw_func0", kw_func);
|
||||
m.def("kw_func1", kw_func, py::arg("x"), py::arg("y"));
|
||||
m.def("kw_func2", kw_func, py::arg("x") = 100, py::arg("y") = 200);
|
||||
m.def(
|
||||
"kw_func3", [](const char *) {}, py::arg("data") = std::string("Hello world!"));
|
||||
|
||||
/* A fancier default argument */
|
||||
std::vector<int> list{{13, 17}};
|
||||
m.def(
|
||||
"kw_func4",
|
||||
[](const std::vector<int> &entries) {
|
||||
std::string ret = "{";
|
||||
for (int i : entries) {
|
||||
ret += std::to_string(i) + " ";
|
||||
}
|
||||
ret.back() = '}';
|
||||
return ret;
|
||||
},
|
||||
py::arg("myList") = list);
|
||||
|
||||
m.def("kw_func_udl", kw_func, "x"_a, "y"_a = 300);
|
||||
m.def("kw_func_udl_z", kw_func, "x"_a, "y"_a = 0);
|
||||
|
||||
// test_args_and_kwargs
|
||||
m.def("args_function", [](py::args args) -> py::tuple { return std::move(args); });
|
||||
m.def("args_kwargs_function", [](const py::args &args, const py::kwargs &kwargs) {
|
||||
return py::make_tuple(args, kwargs);
|
||||
});
|
||||
|
||||
// test_mixed_args_and_kwargs
|
||||
m.def("mixed_plus_args",
|
||||
[](int i, double j, const py::args &args) { return py::make_tuple(i, j, args); });
|
||||
m.def("mixed_plus_kwargs",
|
||||
[](int i, double j, const py::kwargs &kwargs) { return py::make_tuple(i, j, kwargs); });
|
||||
auto mixed_plus_both = [](int i, double j, const py::args &args, const py::kwargs &kwargs) {
|
||||
return py::make_tuple(i, j, args, kwargs);
|
||||
};
|
||||
m.def("mixed_plus_args_kwargs", mixed_plus_both);
|
||||
|
||||
m.def("mixed_plus_args_kwargs_defaults",
|
||||
mixed_plus_both,
|
||||
py::arg("i") = 1,
|
||||
py::arg("j") = 3.14159);
|
||||
|
||||
m.def(
|
||||
"args_kwonly",
|
||||
[](int i, double j, const py::args &args, int z) { return py::make_tuple(i, j, args, z); },
|
||||
"i"_a,
|
||||
"j"_a,
|
||||
"z"_a);
|
||||
m.def(
|
||||
"args_kwonly_kwargs",
|
||||
[](int i, double j, const py::args &args, int z, const py::kwargs &kwargs) {
|
||||
return py::make_tuple(i, j, args, z, kwargs);
|
||||
},
|
||||
"i"_a,
|
||||
"j"_a,
|
||||
py::kw_only{},
|
||||
"z"_a);
|
||||
m.def(
|
||||
"args_kwonly_kwargs_defaults",
|
||||
[](int i, double j, const py::args &args, int z, const py::kwargs &kwargs) {
|
||||
return py::make_tuple(i, j, args, z, kwargs);
|
||||
},
|
||||
"i"_a = 1,
|
||||
"j"_a = 3.14159,
|
||||
"z"_a = 42);
|
||||
m.def(
|
||||
"args_kwonly_full_monty",
|
||||
[](int h, int i, double j, const py::args &args, int z, const py::kwargs &kwargs) {
|
||||
return py::make_tuple(h, i, j, args, z, kwargs);
|
||||
},
|
||||
py::arg() = 1,
|
||||
py::arg() = 2,
|
||||
py::pos_only{},
|
||||
"j"_a = 3.14159,
|
||||
"z"_a = 42);
|
||||
|
||||
// test_args_refcount
|
||||
// PyPy needs a garbage collection to get the reference count values to match CPython's behaviour
|
||||
#ifdef PYPY_VERSION
|
||||
# define GC_IF_NEEDED ConstructorStats::gc()
|
||||
#else
|
||||
# define GC_IF_NEEDED
|
||||
#endif
|
||||
m.def("arg_refcount_h", [](py::handle h) {
|
||||
GC_IF_NEEDED;
|
||||
return h.ref_count();
|
||||
});
|
||||
m.def("arg_refcount_h", [](py::handle h, py::handle, py::handle) {
|
||||
GC_IF_NEEDED;
|
||||
return h.ref_count();
|
||||
});
|
||||
m.def("arg_refcount_o", [](const py::object &o) {
|
||||
GC_IF_NEEDED;
|
||||
return o.ref_count();
|
||||
});
|
||||
m.def("args_refcount", [](py::args a) {
|
||||
GC_IF_NEEDED;
|
||||
py::tuple t(a.size());
|
||||
for (size_t i = 0; i < a.size(); i++) {
|
||||
// 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<py::ssize_t>(i)));
|
||||
}
|
||||
return t;
|
||||
});
|
||||
m.def("mixed_args_refcount", [](const py::object &o, py::args a) {
|
||||
GC_IF_NEEDED;
|
||||
py::tuple t(a.size() + 1);
|
||||
t[0] = o.ref_count();
|
||||
for (size_t i = 0; i < a.size(); i++) {
|
||||
// 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<py::ssize_t>(i)));
|
||||
}
|
||||
return t;
|
||||
});
|
||||
|
||||
// pybind11 won't allow these to be bound: args and kwargs, if present, must be at the end.
|
||||
// Uncomment these to test that the static_assert is indeed working:
|
||||
// m.def("bad_args1", [](py::args, int) {});
|
||||
// m.def("bad_args2", [](py::kwargs, int) {});
|
||||
// m.def("bad_args3", [](py::kwargs, py::args) {});
|
||||
// m.def("bad_args4", [](py::args, int, py::kwargs) {});
|
||||
// m.def("bad_args5", [](py::args, py::kwargs, int) {});
|
||||
// m.def("bad_args6", [](py::args, py::args) {});
|
||||
// m.def("bad_args7", [](py::kwargs, py::kwargs) {});
|
||||
|
||||
// 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, const py::kwargs &kwargs) {
|
||||
return py::make_tuple(i, j, k, kwargs);
|
||||
},
|
||||
py::arg() /* positional */,
|
||||
py::arg("j") = -1 /* both */,
|
||||
py::kw_only(),
|
||||
py::arg("k") /* kw-only */);
|
||||
|
||||
m.def("register_invalid_kw_only", [](py::module_ m) {
|
||||
m.def(
|
||||
"bad_kw_only",
|
||||
[](int i, int j) { return py::make_tuple(i, j); },
|
||||
py::kw_only(),
|
||||
py::arg() /* invalid unnamed argument */,
|
||||
"j"_a);
|
||||
});
|
||||
|
||||
// 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:
|
||||
#ifdef PYBIND11_NEVER_DEFINED_EVER
|
||||
// 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);
|
||||
#endif
|
||||
|
||||
// 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(std::move(a)); },
|
||||
"a"_a = py::module_::import("decimal").attr("Decimal"));
|
||||
|
||||
// Initial implementation of kw_only was broken when used on a method/constructor before any
|
||||
// other arguments
|
||||
// https://github.com/pybind/pybind11/pull/3402#issuecomment-963341987
|
||||
|
||||
struct first_arg_kw_only {};
|
||||
py::class_<first_arg_kw_only>(m, "first_arg_kw_only")
|
||||
.def(py::init([](int) { return first_arg_kw_only(); }),
|
||||
py::kw_only(), // This being before any args was broken
|
||||
py::arg("i") = 0)
|
||||
.def(
|
||||
"method",
|
||||
[](first_arg_kw_only &, int, int) {},
|
||||
py::kw_only(), // and likewise here
|
||||
py::arg("i") = 1,
|
||||
py::arg("j") = 2)
|
||||
// Closely related: pos_only marker didn't show up properly when it was before any other
|
||||
// arguments (although that is fairly useless in practice).
|
||||
.def(
|
||||
"pos_only",
|
||||
[](first_arg_kw_only &, int, int) {},
|
||||
py::pos_only{},
|
||||
py::arg("i"),
|
||||
py::arg("j"));
|
||||
}
|
||||
404
third_party/pybind11/tests/test_kwargs_and_defaults.py
vendored
Normal file
404
third_party/pybind11/tests/test_kwargs_and_defaults.py
vendored
Normal file
@@ -0,0 +1,404 @@
|
||||
import pytest
|
||||
|
||||
from pybind11_tests import kwargs_and_defaults as m
|
||||
|
||||
|
||||
def test_function_signatures(doc):
|
||||
assert doc(m.kw_func0) == "kw_func0(arg0: int, arg1: int) -> str"
|
||||
assert doc(m.kw_func1) == "kw_func1(x: int, y: int) -> str"
|
||||
assert doc(m.kw_func2) == "kw_func2(x: int = 100, y: int = 200) -> str"
|
||||
assert doc(m.kw_func3) == "kw_func3(data: str = 'Hello world!') -> None"
|
||||
assert doc(m.kw_func4) == "kw_func4(myList: List[int] = [13, 17]) -> str"
|
||||
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")
|
||||
|
||||
|
||||
def test_named_arguments(msg):
|
||||
assert m.kw_func0(5, 10) == "x=5, y=10"
|
||||
|
||||
assert m.kw_func1(5, 10) == "x=5, y=10"
|
||||
assert m.kw_func1(5, y=10) == "x=5, y=10"
|
||||
assert m.kw_func1(y=10, x=5) == "x=5, y=10"
|
||||
|
||||
assert m.kw_func2() == "x=100, y=200"
|
||||
assert m.kw_func2(5) == "x=5, y=200"
|
||||
assert m.kw_func2(x=5) == "x=5, y=200"
|
||||
assert m.kw_func2(y=10) == "x=100, y=10"
|
||||
assert m.kw_func2(5, 10) == "x=5, y=10"
|
||||
assert m.kw_func2(x=5, y=10) == "x=5, y=10"
|
||||
|
||||
with pytest.raises(TypeError) as excinfo:
|
||||
# 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}$")
|
||||
|
||||
assert m.kw_func4() == "{13 17}"
|
||||
assert m.kw_func4(myList=[1, 2, 3]) == "{1 2 3}"
|
||||
|
||||
assert m.kw_func_udl(x=5, y=10) == "x=5, y=10"
|
||||
assert m.kw_func_udl_z(x=5) == "x=5, y=0"
|
||||
|
||||
|
||||
def test_arg_and_kwargs():
|
||||
args = "arg1_value", "arg2_value", 3
|
||||
assert m.args_function(*args) == args
|
||||
|
||||
args = "a1", "a2"
|
||||
kwargs = dict(arg3="a3", arg4=4)
|
||||
assert m.args_kwargs_function(*args, **kwargs) == (args, kwargs)
|
||||
|
||||
|
||||
def test_mixed_args_and_kwargs(msg):
|
||||
mpa = m.mixed_plus_args
|
||||
mpk = m.mixed_plus_kwargs
|
||||
mpak = m.mixed_plus_args_kwargs
|
||||
mpakd = m.mixed_plus_args_kwargs_defaults
|
||||
|
||||
assert mpa(1, 2.5, 4, 99.5, None) == (1, 2.5, (4, 99.5, None))
|
||||
assert mpa(1, 2.5) == (1, 2.5, ())
|
||||
with pytest.raises(TypeError) as excinfo:
|
||||
assert mpa(1)
|
||||
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
|
||||
""")
|
||||
with pytest.raises(TypeError) as excinfo:
|
||||
assert mpa()
|
||||
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:
|
||||
""")
|
||||
|
||||
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
|
||||
}, )
|
||||
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(
|
||||
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) == """
|
||||
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
|
||||
""")
|
||||
with pytest.raises(TypeError) as excinfo:
|
||||
assert mpakd(1, 2, j=1)
|
||||
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
|
||||
""")
|
||||
|
||||
# Arguments after a py::args are automatically keyword-only (pybind 2.9+)
|
||||
assert m.args_kwonly(2, 2.5, z=22) == (2, 2.5, (), 22)
|
||||
assert m.args_kwonly(
|
||||
2, 2.5, "a", "b", "c", z=22) == (2, 2.5, ("a", "b", "c"), 22)
|
||||
assert m.args_kwonly(z=22, i=4, j=16) == (4, 16, (), 22)
|
||||
|
||||
with pytest.raises(TypeError) as excinfo:
|
||||
assert m.args_kwonly(2, 2.5, 22) # missing z= keyword
|
||||
assert (msg(excinfo.value) == """
|
||||
args_kwonly(): incompatible function arguments. The following argument types are supported:
|
||||
1. (i: int, j: float, *args, z: int) -> tuple
|
||||
|
||||
Invoked with: 2, 2.5, 22
|
||||
""")
|
||||
|
||||
assert m.args_kwonly_kwargs(
|
||||
i=1, k=4, j=10, z=-1, y=9) == (
|
||||
1,
|
||||
10,
|
||||
(),
|
||||
-1,
|
||||
{
|
||||
"k": 4,
|
||||
"y": 9
|
||||
}, )
|
||||
assert m.args_kwonly_kwargs(
|
||||
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, z=11, y=12) == (
|
||||
1,
|
||||
2,
|
||||
(3, 4, 5, 6, 7, 8, 9, 10),
|
||||
11,
|
||||
{
|
||||
"y": 12
|
||||
}, )
|
||||
assert (
|
||||
m.args_kwonly_kwargs.__doc__ ==
|
||||
"args_kwonly_kwargs(i: int, j: float, *args, z: int, **kwargs) -> tuple\n"
|
||||
)
|
||||
|
||||
assert (
|
||||
m.args_kwonly_kwargs_defaults.__doc__ ==
|
||||
"args_kwonly_kwargs_defaults(i: int = 1, j: float = 3.14159, *args, z: int = 42, **kwargs) -> tuple\n" # noqa: E501 line too long
|
||||
)
|
||||
assert m.args_kwonly_kwargs_defaults() == (1, 3.14159, (), 42, {})
|
||||
assert m.args_kwonly_kwargs_defaults(2) == (2, 3.14159, (), 42, {})
|
||||
assert m.args_kwonly_kwargs_defaults(z=-99) == (1, 3.14159, (), -99, {})
|
||||
assert m.args_kwonly_kwargs_defaults(5, 6, 7, 8) == (5, 6, (7, 8), 42, {})
|
||||
assert m.args_kwonly_kwargs_defaults(
|
||||
5, 6, 7, m=8) == (5, 6, (7, ), 42, {
|
||||
"m": 8
|
||||
})
|
||||
assert m.args_kwonly_kwargs_defaults(
|
||||
5, 6, 7, m=8, z=9) == (5, 6, (7, ), 9, {
|
||||
"m": 8
|
||||
})
|
||||
|
||||
|
||||
def test_keyword_only_args(msg):
|
||||
assert m.kw_only_all(i=1, j=2) == (1, 2)
|
||||
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 a kw_only() annotation or args() argument
|
||||
""")
|
||||
|
||||
# https://github.com/pybind/pybind11/pull/3402#issuecomment-963341987
|
||||
x = m.first_arg_kw_only(i=1)
|
||||
x.method()
|
||||
x.method(i=1, j=2)
|
||||
assert (
|
||||
m.first_arg_kw_only.__init__.__doc__ ==
|
||||
"__init__(self: pybind11_tests.kwargs_and_defaults.first_arg_kw_only, *, i: int = 0) -> None\n" # noqa: E501 line too long
|
||||
)
|
||||
assert (
|
||||
m.first_arg_kw_only.method.__doc__ ==
|
||||
"method(self: pybind11_tests.kwargs_and_defaults.first_arg_kw_only, *, i: int = 1, j: int = 2) -> None\n" # noqa: E501 line too long
|
||||
)
|
||||
|
||||
|
||||
def test_positional_only_args(msg):
|
||||
assert m.pos_only_all(1, 2) == (1, 2)
|
||||
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)
|
||||
|
||||
# Mix it with args and kwargs:
|
||||
assert (
|
||||
m.args_kwonly_full_monty.__doc__ ==
|
||||
"args_kwonly_full_monty(arg0: int = 1, arg1: int = 2, /, j: float = 3.14159, *args, z: int = 42, **kwargs) -> tuple\n" # noqa: E501 line too long
|
||||
)
|
||||
assert m.args_kwonly_full_monty() == (1, 2, 3.14159, (), 42, {})
|
||||
assert m.args_kwonly_full_monty(8) == (8, 2, 3.14159, (), 42, {})
|
||||
assert m.args_kwonly_full_monty(8, 9) == (8, 9, 3.14159, (), 42, {})
|
||||
assert m.args_kwonly_full_monty(8, 9, 10) == (8, 9, 10.0, (), 42, {})
|
||||
assert m.args_kwonly_full_monty(
|
||||
3, 4, 5, 6, 7, m=8, z=9) == (
|
||||
3,
|
||||
4,
|
||||
5.0,
|
||||
(
|
||||
6,
|
||||
7, ),
|
||||
9,
|
||||
{
|
||||
"m": 8
|
||||
}, )
|
||||
assert m.args_kwonly_full_monty(
|
||||
3, 4, 5, 6, 7, m=8, z=9) == (
|
||||
3,
|
||||
4,
|
||||
5.0,
|
||||
(
|
||||
6,
|
||||
7, ),
|
||||
9,
|
||||
{
|
||||
"m": 8
|
||||
}, )
|
||||
assert m.args_kwonly_full_monty(
|
||||
5, j=7, m=8, z=9) == (5, 2, 7.0, (), 9, {
|
||||
"m": 8
|
||||
})
|
||||
assert m.args_kwonly_full_monty(
|
||||
i=5, j=7, m=8, z=9) == (
|
||||
1,
|
||||
2,
|
||||
7.0,
|
||||
(),
|
||||
9,
|
||||
{
|
||||
"i": 5,
|
||||
"m": 8
|
||||
}, )
|
||||
|
||||
# pos_only at the beginning of the argument list was "broken" in how it was displayed (though
|
||||
# this is fairly useless in practice). Related to:
|
||||
# https://github.com/pybind/pybind11/pull/3402#issuecomment-963341987
|
||||
assert (
|
||||
m.first_arg_kw_only.pos_only.__doc__ ==
|
||||
"pos_only(self: pybind11_tests.kwargs_and_defaults.first_arg_kw_only, /, i: int, j: int) -> None\n" # noqa: E501 line too long
|
||||
)
|
||||
|
||||
|
||||
def test_signatures():
|
||||
assert "kw_only_all(*, i: int, j: int) -> tuple\n" == m.kw_only_all.__doc__
|
||||
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__)
|
||||
|
||||
|
||||
def test_args_refcount():
|
||||
"""Issue/PR #1216 - py::args elements get double-inc_ref()ed when combined with regular
|
||||
arguments"""
|
||||
refcount = m.arg_refcount_h
|
||||
|
||||
myval = 54321
|
||||
expected = refcount(myval)
|
||||
assert m.arg_refcount_h(myval) == expected
|
||||
assert m.arg_refcount_o(myval) == expected + 1
|
||||
assert m.arg_refcount_h(myval) == expected
|
||||
assert refcount(myval) == expected
|
||||
|
||||
assert m.mixed_plus_args(1, 2.0, "a", myval) == (1, 2.0, ("a", myval))
|
||||
assert refcount(myval) == expected
|
||||
|
||||
assert m.mixed_plus_kwargs(
|
||||
3, 4.0, a=1, b=myval) == (3, 4.0, {
|
||||
"a": 1,
|
||||
"b": myval
|
||||
})
|
||||
assert refcount(myval) == expected
|
||||
|
||||
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 refcount(myval) == expected
|
||||
|
||||
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)
|
||||
assert m.args_refcount(myval, myval, myval) == (exp3, exp3, exp3)
|
||||
assert refcount(myval) == expected
|
||||
|
||||
# This function takes the first arg as a `py::object` and the rest as a `py::args`. Unlike the
|
||||
# previous case, when we have both positional and `py::args` we need to construct a new tuple
|
||||
# for the `py::args`; in the previous case, we could simply inc_ref and pass on Python's input
|
||||
# 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'>"
|
||||
106
third_party/pybind11/tests/test_local_bindings.cpp
vendored
Normal file
106
third_party/pybind11/tests/test_local_bindings.cpp
vendored
Normal file
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
tests/test_local_bindings.cpp -- tests the py::module_local class feature which makes a class
|
||||
binding local to the module in which it is defined.
|
||||
|
||||
Copyright (c) 2017 Jason Rhinelander <jason@imaginary.ca>
|
||||
|
||||
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/stl.h>
|
||||
#include <pybind11/stl_bind.h>
|
||||
|
||||
#include "local_bindings.h"
|
||||
#include "pybind11_tests.h"
|
||||
|
||||
#include <numeric>
|
||||
#include <utility>
|
||||
|
||||
TEST_SUBMODULE(local_bindings, m) {
|
||||
// test_load_external
|
||||
m.def("load_external1", [](ExternalType1 &e) { return e.i; });
|
||||
m.def("load_external2", [](ExternalType2 &e) { return e.i; });
|
||||
|
||||
// test_local_bindings
|
||||
// Register a class with py::module_local:
|
||||
bind_local<LocalType, -1>(m, "LocalType", py::module_local()).def("get3", [](LocalType &t) {
|
||||
return t.i + 3;
|
||||
});
|
||||
|
||||
m.def("local_value", [](LocalType &l) { return l.i; });
|
||||
|
||||
// test_nonlocal_failure
|
||||
// The main pybind11 test module is loaded first, so this registration will succeed (the second
|
||||
// one, in pybind11_cross_module_tests.cpp, is designed to fail):
|
||||
bind_local<NonLocalType, 0>(m, "NonLocalType")
|
||||
.def(py::init<int>())
|
||||
.def("get", [](LocalType &i) { return i.i; });
|
||||
|
||||
// test_duplicate_local
|
||||
// py::module_local declarations should be visible across compilation units that get linked
|
||||
// together; this tries to register a duplicate local. It depends on a definition in
|
||||
// test_class.cpp and should raise a runtime error from the duplicate definition attempt. If
|
||||
// test_class isn't available it *also* throws a runtime error (with "test_class not enabled"
|
||||
// as value).
|
||||
m.def("register_local_external", [m]() {
|
||||
auto main = py::module_::import("pybind11_tests");
|
||||
if (py::hasattr(main, "class_")) {
|
||||
bind_local<LocalExternal, 7>(m, "LocalExternal", py::module_local());
|
||||
} else {
|
||||
throw std::runtime_error("test_class not enabled");
|
||||
}
|
||||
});
|
||||
|
||||
// test_stl_bind_local
|
||||
// stl_bind.h binders defaults to py::module_local if the types are local or converting:
|
||||
py::bind_vector<LocalVec>(m, "LocalVec");
|
||||
py::bind_map<LocalMap>(m, "LocalMap");
|
||||
// and global if the type (or one of the types, for the map) is global:
|
||||
py::bind_vector<NonLocalVec>(m, "NonLocalVec");
|
||||
py::bind_map<NonLocalMap>(m, "NonLocalMap");
|
||||
|
||||
// test_stl_bind_global
|
||||
// They can, however, be overridden to global using `py::module_local(false)`:
|
||||
bind_local<NonLocal2, 10>(m, "NonLocal2");
|
||||
py::bind_vector<LocalVec2>(m, "LocalVec2", py::module_local());
|
||||
py::bind_map<NonLocalMap2>(m, "NonLocalMap2", py::module_local(false));
|
||||
|
||||
// test_mixed_local_global
|
||||
// We try this both with the global type registered first and vice versa (the order shouldn't
|
||||
// matter).
|
||||
m.def("register_mixed_global", [m]() {
|
||||
bind_local<MixedGlobalLocal, 100>(m, "MixedGlobalLocal", py::module_local(false));
|
||||
});
|
||||
m.def("register_mixed_local", [m]() {
|
||||
bind_local<MixedLocalGlobal, 1000>(m, "MixedLocalGlobal", py::module_local());
|
||||
});
|
||||
m.def("get_mixed_gl", [](int i) { return MixedGlobalLocal(i); });
|
||||
m.def("get_mixed_lg", [](int i) { return MixedLocalGlobal(i); });
|
||||
|
||||
// test_internal_locals_differ
|
||||
m.def("local_cpp_types_addr",
|
||||
[]() { return (uintptr_t) &py::detail::get_local_internals().registered_types_cpp; });
|
||||
|
||||
// test_stl_caster_vs_stl_bind
|
||||
m.def("load_vector_via_caster",
|
||||
[](std::vector<int> v) { return std::accumulate(v.begin(), v.end(), 0); });
|
||||
|
||||
// test_cross_module_calls
|
||||
m.def("return_self", [](LocalVec *v) { return v; });
|
||||
m.def("return_copy", [](const LocalVec &v) { return LocalVec(v); });
|
||||
|
||||
class Cat : public pets::Pet {
|
||||
public:
|
||||
explicit Cat(std::string name) : Pet(std::move(name)) {}
|
||||
};
|
||||
py::class_<pets::Pet>(m, "Pet", py::module_local()).def("get_name", &pets::Pet::name);
|
||||
// Binding for local extending class:
|
||||
py::class_<Cat, pets::Pet>(m, "Cat").def(py::init<std::string>());
|
||||
m.def("pet_name", [](pets::Pet &p) { return p.name(); });
|
||||
|
||||
py::class_<MixGL>(m, "MixGL").def(py::init<int>());
|
||||
m.def("get_gl_value", [](MixGL &o) { return o.i + 10; });
|
||||
|
||||
py::class_<MixGL2>(m, "MixGL2").def(py::init<int>());
|
||||
}
|
||||
247
third_party/pybind11/tests/test_local_bindings.py
vendored
Normal file
247
third_party/pybind11/tests/test_local_bindings.py
vendored
Normal file
@@ -0,0 +1,247 @@
|
||||
import pytest
|
||||
|
||||
import env # noqa: F401
|
||||
from pybind11_tests import local_bindings as m
|
||||
|
||||
|
||||
def test_load_external():
|
||||
"""Load a `py::module_local` type that's only registered in an external module"""
|
||||
import pybind11_cross_module_tests as cm
|
||||
|
||||
assert m.load_external1(cm.ExternalType1(11)) == 11
|
||||
assert m.load_external2(cm.ExternalType2(22)) == 22
|
||||
|
||||
with pytest.raises(TypeError) as excinfo:
|
||||
assert m.load_external2(cm.ExternalType1(21)) == 21
|
||||
assert "incompatible function arguments" in str(excinfo.value)
|
||||
|
||||
with pytest.raises(TypeError) as excinfo:
|
||||
assert m.load_external1(cm.ExternalType2(12)) == 12
|
||||
assert "incompatible function arguments" in str(excinfo.value)
|
||||
|
||||
|
||||
def test_local_bindings():
|
||||
"""Tests that duplicate `py::module_local` class bindings work across modules"""
|
||||
|
||||
# Make sure we can load the second module with the conflicting (but local) definition:
|
||||
import pybind11_cross_module_tests as cm
|
||||
|
||||
i1 = m.LocalType(5)
|
||||
assert i1.get() == 4
|
||||
assert i1.get3() == 8
|
||||
|
||||
i2 = cm.LocalType(10)
|
||||
assert i2.get() == 11
|
||||
assert i2.get2() == 12
|
||||
|
||||
assert not hasattr(i1, "get2")
|
||||
assert not hasattr(i2, "get3")
|
||||
|
||||
# Loading within the local module
|
||||
assert m.local_value(i1) == 5
|
||||
assert cm.local_value(i2) == 10
|
||||
|
||||
# Cross-module loading works as well (on failure, the type loader looks for
|
||||
# external module-local converters):
|
||||
assert m.local_value(i2) == 10
|
||||
assert cm.local_value(i1) == 5
|
||||
|
||||
|
||||
def test_nonlocal_failure():
|
||||
"""Tests that attempting to register a non-local type in multiple modules fails"""
|
||||
import pybind11_cross_module_tests as cm
|
||||
|
||||
with pytest.raises(RuntimeError) as excinfo:
|
||||
cm.register_nonlocal()
|
||||
assert (str(excinfo.value) ==
|
||||
'generic_type: type "NonLocalType" is already registered!')
|
||||
|
||||
|
||||
def test_duplicate_local():
|
||||
"""Tests expected failure when registering a class twice with py::local in the same module"""
|
||||
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")
|
||||
|
||||
|
||||
def test_stl_bind_local():
|
||||
import pybind11_cross_module_tests as cm
|
||||
|
||||
v1, v2 = m.LocalVec(), cm.LocalVec()
|
||||
v1.append(m.LocalType(1))
|
||||
v1.append(m.LocalType(2))
|
||||
v2.append(cm.LocalType(1))
|
||||
v2.append(cm.LocalType(2))
|
||||
|
||||
# Cross module value loading:
|
||||
v1.append(cm.LocalType(3))
|
||||
v2.append(m.LocalType(3))
|
||||
|
||||
assert [i.get() for i in v1] == [0, 1, 2]
|
||||
assert [i.get() for i in v2] == [2, 3, 4]
|
||||
|
||||
v3, v4 = m.NonLocalVec(), cm.NonLocalVec2()
|
||||
v3.append(m.NonLocalType(1))
|
||||
v3.append(m.NonLocalType(2))
|
||||
v4.append(m.NonLocal2(3))
|
||||
v4.append(m.NonLocal2(4))
|
||||
|
||||
assert [i.get() for i in v3] == [1, 2]
|
||||
assert [i.get() for i in v4] == [13, 14]
|
||||
|
||||
d1, d2 = m.LocalMap(), cm.LocalMap()
|
||||
d1["a"] = v1[0]
|
||||
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}
|
||||
|
||||
|
||||
def test_stl_bind_global():
|
||||
import pybind11_cross_module_tests as cm
|
||||
|
||||
with pytest.raises(RuntimeError) as excinfo:
|
||||
cm.register_nonlocal_map()
|
||||
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!')
|
||||
|
||||
with pytest.raises(RuntimeError) as excinfo:
|
||||
cm.register_nonlocal_map2()
|
||||
assert (str(excinfo.value) ==
|
||||
'generic_type: type "NonLocalMap2" is already registered!')
|
||||
|
||||
|
||||
def test_mixed_local_global():
|
||||
"""Local types take precedence over globally registered types: a module with a `module_local`
|
||||
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()
|
||||
|
||||
a = []
|
||||
a.append(m.MixedGlobalLocal(1))
|
||||
a.append(m.MixedLocalGlobal(2))
|
||||
a.append(m.get_mixed_gl(3))
|
||||
a.append(m.get_mixed_lg(4))
|
||||
|
||||
assert [x.get() for x in a] == [101, 1002, 103, 1004]
|
||||
|
||||
cm.register_mixed_global_local()
|
||||
cm.register_mixed_local_global()
|
||||
a.append(m.MixedGlobalLocal(5))
|
||||
a.append(m.MixedLocalGlobal(6))
|
||||
a.append(cm.MixedGlobalLocal(7))
|
||||
a.append(cm.MixedLocalGlobal(8))
|
||||
a.append(m.get_mixed_gl(9))
|
||||
a.append(m.get_mixed_lg(10))
|
||||
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,
|
||||
]
|
||||
|
||||
|
||||
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`"""
|
||||
import pybind11_cross_module_tests as cm
|
||||
|
||||
v1 = cm.VectorInt([1, 2, 3])
|
||||
assert m.load_vector_via_caster(v1) == 6
|
||||
assert cm.load_vector_via_binding(v1) == 6
|
||||
|
||||
v2 = [1, 2, 3]
|
||||
assert m.load_vector_via_caster(v2) == 6
|
||||
with pytest.raises(TypeError) as excinfo:
|
||||
cm.load_vector_via_binding(v2)
|
||||
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]
|
||||
""")
|
||||
|
||||
|
||||
def test_cross_module_calls():
|
||||
import pybind11_cross_module_tests as cm
|
||||
|
||||
v1 = m.LocalVec()
|
||||
v1.append(m.LocalType(1))
|
||||
v2 = cm.LocalVec()
|
||||
v2.append(cm.LocalType(2))
|
||||
|
||||
# Returning the self pointer should get picked up as returning an existing
|
||||
# instance (even when that instance is of a foreign, non-local type).
|
||||
assert m.return_self(v1) is v1
|
||||
assert cm.return_self(v2) is v2
|
||||
assert m.return_self(v2) is v2
|
||||
assert cm.return_self(v1) is v1
|
||||
|
||||
assert m.LocalVec is not cm.LocalVec
|
||||
# Returning a copy, on the other hand, always goes to the local type,
|
||||
# regardless of where the source type came from.
|
||||
assert type(m.return_copy(v1)) is m.LocalVec
|
||||
assert type(m.return_copy(v2)) is m.LocalVec
|
||||
assert type(cm.return_copy(v1)) is cm.LocalVec
|
||||
assert type(cm.return_copy(v2)) is cm.LocalVec
|
||||
|
||||
# Test the example given in the documentation (which also tests inheritance casting):
|
||||
mycat = m.Cat("Fluffy")
|
||||
mydog = cm.Dog("Rover")
|
||||
assert mycat.get_name() == "Fluffy"
|
||||
assert mydog.name() == "Rover"
|
||||
assert m.Cat.__base__.__name__ == "Pet"
|
||||
assert cm.Dog.__base__.__name__ == "Pet"
|
||||
assert m.Cat.__base__ is not cm.Dog.__base__
|
||||
assert m.pet_name(mycat) == "Fluffy"
|
||||
assert m.pet_name(mydog) == "Rover"
|
||||
assert cm.pet_name(mycat) == "Fluffy"
|
||||
assert cm.pet_name(mydog) == "Rover"
|
||||
|
||||
assert m.MixGL is not cm.MixGL
|
||||
a = m.MixGL(1)
|
||||
b = cm.MixGL(2)
|
||||
assert m.get_gl_value(a) == 11
|
||||
assert m.get_gl_value(b) == 12
|
||||
assert cm.get_gl_value(a) == 101
|
||||
assert cm.get_gl_value(b) == 102
|
||||
|
||||
c, d = m.MixGL2(3), cm.MixGL2(4)
|
||||
with pytest.raises(TypeError) as excinfo:
|
||||
m.get_gl_value(c)
|
||||
assert "incompatible function arguments" in str(excinfo.value)
|
||||
with pytest.raises(TypeError) as excinfo:
|
||||
m.get_gl_value(d)
|
||||
assert "incompatible function arguments" in str(excinfo.value)
|
||||
459
third_party/pybind11/tests/test_methods_and_attributes.cpp
vendored
Normal file
459
third_party/pybind11/tests/test_methods_and_attributes.cpp
vendored
Normal file
@@ -0,0 +1,459 @@
|
||||
/*
|
||||
tests/test_methods_and_attributes.cpp -- constructors, deconstructors, attribute access,
|
||||
__str__, argument and return value conventions
|
||||
|
||||
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 "constructor_stats.h"
|
||||
#include "pybind11_tests.h"
|
||||
|
||||
#if !defined(PYBIND11_OVERLOAD_CAST)
|
||||
template <typename... Args>
|
||||
using overload_cast_ = pybind11::detail::overload_cast_impl<Args...>;
|
||||
#endif
|
||||
|
||||
class ExampleMandA {
|
||||
public:
|
||||
ExampleMandA() { print_default_created(this); }
|
||||
explicit ExampleMandA(int value) : value(value) { print_created(this, value); }
|
||||
ExampleMandA(const ExampleMandA &e) : value(e.value) { print_copy_created(this); }
|
||||
explicit ExampleMandA(std::string &&) {}
|
||||
ExampleMandA(ExampleMandA &&e) noexcept : value(e.value) { print_move_created(this); }
|
||||
~ExampleMandA() { print_destroyed(this); }
|
||||
|
||||
std::string toString() const { return "ExampleMandA[value=" + std::to_string(value) + "]"; }
|
||||
|
||||
void operator=(const ExampleMandA &e) {
|
||||
print_copy_assigned(this);
|
||||
value = e.value;
|
||||
}
|
||||
void operator=(ExampleMandA &&e) noexcept {
|
||||
print_move_assigned(this);
|
||||
value = e.value;
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(performance-unnecessary-value-param)
|
||||
void add1(ExampleMandA other) { value += other.value; } // passing by value
|
||||
void add2(ExampleMandA &other) { value += other.value; } // passing by reference
|
||||
void add3(const ExampleMandA &other) { value += other.value; } // passing by const reference
|
||||
void add4(ExampleMandA *other) { value += other->value; } // passing by pointer
|
||||
void add5(const ExampleMandA *other) { value += other->value; } // passing by const pointer
|
||||
|
||||
void add6(int other) { value += other; } // passing by value
|
||||
void add7(int &other) { value += other; } // passing by reference
|
||||
void add8(const int &other) { value += other; } // passing by const reference
|
||||
// NOLINTNEXTLINE(readability-non-const-parameter) Deliberately non-const for testing
|
||||
void add9(int *other) { value += *other; } // passing by pointer
|
||||
void add10(const int *other) { value += *other; } // passing by const pointer
|
||||
|
||||
void consume_str(std::string &&) {}
|
||||
|
||||
ExampleMandA self1() { return *this; } // return by value
|
||||
ExampleMandA &self2() { return *this; } // return by reference
|
||||
const ExampleMandA &self3() const { return *this; } // return by const reference
|
||||
ExampleMandA *self4() { return this; } // return by pointer
|
||||
const ExampleMandA *self5() const { return this; } // return by const pointer
|
||||
|
||||
int internal1() const { return value; } // return by value
|
||||
int &internal2() { return value; } // return by reference
|
||||
const int &internal3() const { return value; } // return by const reference
|
||||
int *internal4() { return &value; } // return by pointer
|
||||
const int *internal5() { return &value; } // return by const pointer
|
||||
|
||||
py::str overloaded() { return "()"; }
|
||||
py::str overloaded(int) { return "(int)"; }
|
||||
py::str overloaded(int, float) { return "(int, float)"; }
|
||||
py::str overloaded(float, int) { return "(float, int)"; }
|
||||
py::str overloaded(int, int) { return "(int, int)"; }
|
||||
py::str overloaded(float, float) { return "(float, float)"; }
|
||||
py::str overloaded(int) const { return "(int) const"; }
|
||||
py::str overloaded(int, float) const { return "(int, float) const"; }
|
||||
py::str overloaded(float, int) const { return "(float, int) const"; }
|
||||
py::str overloaded(int, int) const { return "(int, int) const"; }
|
||||
py::str overloaded(float, float) const { return "(float, float) const"; }
|
||||
|
||||
static py::str overloaded(float) { return "static float"; }
|
||||
|
||||
int value = 0;
|
||||
};
|
||||
|
||||
struct TestProperties {
|
||||
int value = 1;
|
||||
static int static_value;
|
||||
|
||||
int get() const { return value; }
|
||||
void set(int v) { value = v; }
|
||||
|
||||
static int static_get() { return static_value; }
|
||||
static void static_set(int v) { static_value = v; }
|
||||
};
|
||||
int TestProperties::static_value = 1;
|
||||
|
||||
struct TestPropertiesOverride : TestProperties {
|
||||
int value = 99;
|
||||
static int static_value;
|
||||
};
|
||||
int TestPropertiesOverride::static_value = 99;
|
||||
|
||||
struct TestPropRVP {
|
||||
UserType v1{1};
|
||||
UserType v2{1};
|
||||
static UserType sv1;
|
||||
static UserType sv2;
|
||||
|
||||
const UserType &get1() const { return v1; }
|
||||
const UserType &get2() const { return v2; }
|
||||
UserType get_rvalue() const { return v2; }
|
||||
void set1(int v) { v1.set(v); }
|
||||
void set2(int v) { v2.set(v); }
|
||||
};
|
||||
UserType TestPropRVP::sv1(1);
|
||||
UserType TestPropRVP::sv2(1);
|
||||
|
||||
// Test None-allowed py::arg argument policy
|
||||
class NoneTester {
|
||||
public:
|
||||
int answer = 42;
|
||||
};
|
||||
int none1(const NoneTester &obj) { return obj.answer; }
|
||||
int none2(NoneTester *obj) { return obj ? obj->answer : -1; }
|
||||
int none3(std::shared_ptr<NoneTester> &obj) { return obj ? obj->answer : -1; }
|
||||
int none4(std::shared_ptr<NoneTester> *obj) { return obj && *obj ? (*obj)->answer : -1; }
|
||||
int none5(const std::shared_ptr<NoneTester> &obj) { return obj ? obj->answer : -1; }
|
||||
|
||||
// Issue #2778: implicit casting from None to object (not pointer)
|
||||
class NoneCastTester {
|
||||
public:
|
||||
int answer = -1;
|
||||
NoneCastTester() = default;
|
||||
explicit NoneCastTester(int v) : answer(v) {}
|
||||
};
|
||||
|
||||
struct StrIssue {
|
||||
int val = -1;
|
||||
|
||||
StrIssue() = default;
|
||||
explicit StrIssue(int i) : val{i} {}
|
||||
};
|
||||
|
||||
// Issues #854, #910: incompatible function args when member function/pointer is in unregistered
|
||||
// base class
|
||||
class UnregisteredBase {
|
||||
public:
|
||||
void do_nothing() const {}
|
||||
void increase_value() {
|
||||
rw_value++;
|
||||
ro_value += 0.25;
|
||||
}
|
||||
void set_int(int v) { rw_value = v; }
|
||||
int get_int() const { return rw_value; }
|
||||
double get_double() const { return ro_value; }
|
||||
int rw_value = 42;
|
||||
double ro_value = 1.25;
|
||||
};
|
||||
class RegisteredDerived : public UnregisteredBase {
|
||||
public:
|
||||
using UnregisteredBase::UnregisteredBase;
|
||||
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 rvalue ref param
|
||||
struct RValueRefParam {
|
||||
std::size_t func1(std::string &&s) { return s.size(); }
|
||||
std::size_t func2(std::string &&s) const { return s.size(); }
|
||||
std::size_t func3(std::string &&s) & { return s.size(); }
|
||||
std::size_t func4(std::string &&s) const & { return s.size(); }
|
||||
};
|
||||
|
||||
TEST_SUBMODULE(methods_and_attributes, m) {
|
||||
// test_methods_and_attributes
|
||||
py::class_<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)
|
||||
.def("add3", &ExampleMandA::add3)
|
||||
.def("add4", &ExampleMandA::add4)
|
||||
.def("add5", &ExampleMandA::add5)
|
||||
.def("add6", &ExampleMandA::add6)
|
||||
.def("add7", &ExampleMandA::add7)
|
||||
.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)
|
||||
.def("self4", &ExampleMandA::self4)
|
||||
.def("self5", &ExampleMandA::self5)
|
||||
.def("internal1", &ExampleMandA::internal1)
|
||||
.def("internal2", &ExampleMandA::internal2)
|
||||
.def("internal3", &ExampleMandA::internal3)
|
||||
.def("internal4", &ExampleMandA::internal4)
|
||||
.def("internal5", &ExampleMandA::internal5)
|
||||
#if defined(PYBIND11_OVERLOAD_CAST)
|
||||
.def("overloaded", py::overload_cast<>(&ExampleMandA::overloaded))
|
||||
.def("overloaded", py::overload_cast<int>(&ExampleMandA::overloaded))
|
||||
.def("overloaded", py::overload_cast<int, float>(&ExampleMandA::overloaded))
|
||||
.def("overloaded", py::overload_cast<float, int>(&ExampleMandA::overloaded))
|
||||
.def("overloaded", py::overload_cast<int, int>(&ExampleMandA::overloaded))
|
||||
.def("overloaded", py::overload_cast<float, float>(&ExampleMandA::overloaded))
|
||||
.def("overloaded_float", py::overload_cast<float, float>(&ExampleMandA::overloaded))
|
||||
.def("overloaded_const", py::overload_cast<int>(&ExampleMandA::overloaded, py::const_))
|
||||
.def("overloaded_const",
|
||||
py::overload_cast<int, float>(&ExampleMandA::overloaded, py::const_))
|
||||
.def("overloaded_const",
|
||||
py::overload_cast<float, int>(&ExampleMandA::overloaded, py::const_))
|
||||
.def("overloaded_const",
|
||||
py::overload_cast<int, int>(&ExampleMandA::overloaded, py::const_))
|
||||
.def("overloaded_const",
|
||||
py::overload_cast<float, float>(&ExampleMandA::overloaded, py::const_))
|
||||
#else
|
||||
// Use both the traditional static_cast method and the C++11 compatible overload_cast_
|
||||
.def("overloaded", overload_cast_<>()(&ExampleMandA::overloaded))
|
||||
.def("overloaded", overload_cast_<int>()(&ExampleMandA::overloaded))
|
||||
.def("overloaded", overload_cast_<int, float>()(&ExampleMandA::overloaded))
|
||||
.def("overloaded", static_cast<py::str (ExampleMandA::*)(float, int)>(&ExampleMandA::overloaded))
|
||||
.def("overloaded", static_cast<py::str (ExampleMandA::*)(int, int)>(&ExampleMandA::overloaded))
|
||||
.def("overloaded", static_cast<py::str (ExampleMandA::*)(float, float)>(&ExampleMandA::overloaded))
|
||||
.def("overloaded_float", overload_cast_<float, float>()(&ExampleMandA::overloaded))
|
||||
.def("overloaded_const", overload_cast_<int >()(&ExampleMandA::overloaded, py::const_))
|
||||
.def("overloaded_const", overload_cast_<int, float>()(&ExampleMandA::overloaded, py::const_))
|
||||
.def("overloaded_const", static_cast<py::str (ExampleMandA::*)(float, int) const>(&ExampleMandA::overloaded))
|
||||
.def("overloaded_const", static_cast<py::str (ExampleMandA::*)(int, int) const>(&ExampleMandA::overloaded))
|
||||
.def("overloaded_const", static_cast<py::str (ExampleMandA::*)(float, float) const>(&ExampleMandA::overloaded))
|
||||
#endif
|
||||
// test_no_mixed_overloads
|
||||
// Raise error if trying to mix static/non-static overloads on the same name:
|
||||
.def_static("add_mixed_overloads1",
|
||||
[]() {
|
||||
auto emna = py::reinterpret_borrow<py::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"));
|
||||
emna.def_static("overload_mixed2",
|
||||
static_cast<py::str (*)(float)>(&ExampleMandA::overloaded))
|
||||
.def("overload_mixed2",
|
||||
static_cast<py::str (ExampleMandA::*)(int, int)>(
|
||||
&ExampleMandA::overloaded));
|
||||
})
|
||||
.def("__str__", &ExampleMandA::toString)
|
||||
.def_readwrite("value", &ExampleMandA::value);
|
||||
|
||||
// test_copy_method
|
||||
// Issue #443: can't call copied methods in Python 3
|
||||
emna.attr("add2b") = emna.attr("add2");
|
||||
|
||||
// test_properties, test_static_properties, test_static_cls
|
||||
py::class_<TestProperties>(m, "TestProperties")
|
||||
.def(py::init<>())
|
||||
.def_readonly("def_readonly", &TestProperties::value)
|
||||
.def_readwrite("def_readwrite", &TestProperties::value)
|
||||
.def_property("def_writeonly", nullptr, [](TestProperties &s, int v) { s.value = v; })
|
||||
.def_property("def_property_writeonly", nullptr, &TestProperties::set)
|
||||
.def_property_readonly("def_property_readonly", &TestProperties::get)
|
||||
.def_property("def_property", &TestProperties::get, &TestProperties::set)
|
||||
.def_property("def_property_impossible", nullptr, nullptr)
|
||||
.def_readonly_static("def_readonly_static", &TestProperties::static_value)
|
||||
.def_readwrite_static("def_readwrite_static", &TestProperties::static_value)
|
||||
.def_property_static("def_writeonly_static",
|
||||
nullptr,
|
||||
[](const py::object &, int v) { TestProperties::static_value = v; })
|
||||
.def_property_readonly_static(
|
||||
"def_property_readonly_static",
|
||||
[](const py::object &) { return TestProperties::static_get(); })
|
||||
.def_property_static(
|
||||
"def_property_writeonly_static",
|
||||
nullptr,
|
||||
[](const py::object &, int v) { return TestProperties::static_set(v); })
|
||||
.def_property_static(
|
||||
"def_property_static",
|
||||
[](const py::object &) { return TestProperties::static_get(); },
|
||||
[](const py::object &, int v) { TestProperties::static_set(v); })
|
||||
.def_property_static(
|
||||
"static_cls",
|
||||
[](py::object cls) { return cls; },
|
||||
[](const py::object &cls, const py::function &f) { f(cls); });
|
||||
|
||||
py::class_<TestPropertiesOverride, TestProperties>(m, "TestPropertiesOverride")
|
||||
.def(py::init<>())
|
||||
.def_readonly("def_readonly", &TestPropertiesOverride::value)
|
||||
.def_readonly_static("def_readonly_static", &TestPropertiesOverride::static_value);
|
||||
|
||||
auto static_get1 = [](const py::object &) -> const UserType & { return TestPropRVP::sv1; };
|
||||
auto static_get2 = [](const py::object &) -> const UserType & { return TestPropRVP::sv2; };
|
||||
auto static_set1 = [](const py::object &, int v) { TestPropRVP::sv1.set(v); };
|
||||
auto static_set2 = [](const py::object &, int v) { TestPropRVP::sv2.set(v); };
|
||||
auto rvp_copy = py::return_value_policy::copy;
|
||||
|
||||
// test_property_return_value_policies
|
||||
py::class_<TestPropRVP>(m, "TestPropRVP")
|
||||
.def(py::init<>())
|
||||
.def_property_readonly("ro_ref", &TestPropRVP::get1)
|
||||
.def_property_readonly("ro_copy", &TestPropRVP::get2, rvp_copy)
|
||||
.def_property_readonly("ro_func", py::cpp_function(&TestPropRVP::get2, rvp_copy))
|
||||
.def_property("rw_ref", &TestPropRVP::get1, &TestPropRVP::set1)
|
||||
.def_property("rw_copy", &TestPropRVP::get2, &TestPropRVP::set2, rvp_copy)
|
||||
.def_property(
|
||||
"rw_func", py::cpp_function(&TestPropRVP::get2, rvp_copy), &TestPropRVP::set2)
|
||||
.def_property_readonly_static("static_ro_ref", static_get1)
|
||||
.def_property_readonly_static("static_ro_copy", static_get2, rvp_copy)
|
||||
.def_property_readonly_static("static_ro_func", py::cpp_function(static_get2, rvp_copy))
|
||||
.def_property_static("static_rw_ref", static_get1, static_set1)
|
||||
.def_property_static("static_rw_copy", static_get2, static_set2, rvp_copy)
|
||||
.def_property_static(
|
||||
"static_rw_func", py::cpp_function(static_get2, rvp_copy), static_set2)
|
||||
// test_property_rvalue_policy
|
||||
.def_property_readonly("rvalue", &TestPropRVP::get_rvalue)
|
||||
.def_property_readonly_static("static_rvalue",
|
||||
[](const py::object &) { return UserType(1); });
|
||||
|
||||
// test_metaclass_override
|
||||
struct MetaclassOverride {};
|
||||
py::class_<MetaclassOverride>(m, "MetaclassOverride", py::metaclass((PyObject *) &PyType_Type))
|
||||
.def_property_readonly_static("readonly", [](const py::object &) { return 1; });
|
||||
|
||||
// test_overload_ordering
|
||||
m.def("overload_order", [](const std::string &) { return 1; });
|
||||
m.def("overload_order", [](const std::string &) { return 2; });
|
||||
m.def("overload_order", [](int) { return 3; });
|
||||
m.def(
|
||||
"overload_order", [](int) { return 4; }, py::prepend{});
|
||||
|
||||
#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()).def(py::init());
|
||||
|
||||
class CppDerivedDynamicClass : public DynamicClass {};
|
||||
py::class_<CppDerivedDynamicClass, DynamicClass>(m, "CppDerivedDynamicClass").def(py::init());
|
||||
#endif
|
||||
|
||||
// test_bad_arg_default
|
||||
// Issue/PR #648: bad arg default debugging output
|
||||
#if defined(PYBIND11_DETAILED_ERROR_MESSAGES)
|
||||
m.attr("detailed_error_messages_enabled") = true;
|
||||
#else
|
||||
m.attr("detailed_error_messages_enabled") = false;
|
||||
#endif
|
||||
m.def("bad_arg_def_named", [] {
|
||||
auto m = py::module_::import("pybind11_tests");
|
||||
m.def(
|
||||
"should_fail",
|
||||
[](int, UnregisteredType) {},
|
||||
py::arg(),
|
||||
py::arg("a") = UnregisteredType());
|
||||
});
|
||||
m.def("bad_arg_def_unnamed", [] {
|
||||
auto m = py::module_::import("pybind11_tests");
|
||||
m.def(
|
||||
"should_fail",
|
||||
[](int, UnregisteredType) {},
|
||||
py::arg(),
|
||||
py::arg() = UnregisteredType());
|
||||
});
|
||||
|
||||
// [workaround(intel)] ICC 20/21 breaks with py::arg().stuff, using py::arg{}.stuff works.
|
||||
|
||||
// test_accepts_none
|
||||
py::class_<NoneTester, std::shared_ptr<NoneTester>>(m, "NoneTester").def(py::init<>());
|
||||
m.def("no_none1", &none1, py::arg{}.none(false));
|
||||
m.def("no_none2", &none2, py::arg{}.none(false));
|
||||
m.def("no_none3", &none3, py::arg{}.none(false));
|
||||
m.def("no_none4", &none4, py::arg{}.none(false));
|
||||
m.def("no_none5", &none5, py::arg{}.none(false));
|
||||
m.def("ok_none1", &none1);
|
||||
m.def("ok_none2", &none2, py::arg{}.none(true));
|
||||
m.def("ok_none3", &none3);
|
||||
m.def("ok_none4", &none4, py::arg{}.none(true));
|
||||
m.def("ok_none5", &none5);
|
||||
|
||||
m.def("no_none_kwarg", &none2, "a"_a.none(false));
|
||||
m.def("no_none_kwarg_kw_only", &none2, py::kw_only(), "a"_a.none(false));
|
||||
|
||||
// test_casts_none
|
||||
// Issue #2778: implicit casting from None to object (not pointer)
|
||||
py::class_<NoneCastTester>(m, "NoneCastTester")
|
||||
.def(py::init<>())
|
||||
.def(py::init<int>())
|
||||
.def(py::init([](py::none const &) { return NoneCastTester{}; }));
|
||||
py::implicitly_convertible<py::none, NoneCastTester>();
|
||||
m.def("ok_obj_or_none", [](NoneCastTester const &foo) { return foo.answer; });
|
||||
|
||||
// test_str_issue
|
||||
// Issue #283: __str__ called on uninitialized instance when constructor arguments invalid
|
||||
py::class_<StrIssue>(m, "StrIssue")
|
||||
.def(py::init<int>())
|
||||
.def(py::init<>())
|
||||
.def("__str__",
|
||||
[](const StrIssue &si) { return "StrIssue[" + std::to_string(si.val) + "]"; });
|
||||
|
||||
// test_unregistered_base_implementations
|
||||
//
|
||||
// Issues #854/910: incompatible function args when member function/pointer is in unregistered
|
||||
// base class The methods and member pointers below actually resolve to members/pointers in
|
||||
// UnregisteredBase; before this test/fix they would be registered via lambda with a first
|
||||
// argument of an unregistered type, and thus uncallable.
|
||||
py::class_<RegisteredDerived>(m, "RegisteredDerived")
|
||||
.def(py::init<>())
|
||||
.def("do_nothing", &RegisteredDerived::do_nothing)
|
||||
.def("increase_value", &RegisteredDerived::increase_value)
|
||||
.def_readwrite("rw_value", &RegisteredDerived::rw_value)
|
||||
.def_readonly("ro_value", &RegisteredDerived::ro_value)
|
||||
// Uncommenting the next line should trigger a static_assert:
|
||||
// .def_readwrite("fails", &UserType::value)
|
||||
// Uncommenting the next line should trigger a static_assert:
|
||||
// .def_readonly("fails", &UserType::value)
|
||||
.def_property("rw_value_prop", &RegisteredDerived::get_int, &RegisteredDerived::set_int)
|
||||
.def_property_readonly("ro_value_prop", &RegisteredDerived::get_double)
|
||||
// This one is in the registered class:
|
||||
.def("sum", &RegisteredDerived::sum);
|
||||
|
||||
using Adapted
|
||||
= decltype(py::method_adaptor<RegisteredDerived>(&RegisteredDerived::do_nothing));
|
||||
static_assert(std::is_same<Adapted, void (RegisteredDerived::*)() const>::value, "");
|
||||
|
||||
// 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);
|
||||
|
||||
py::class_<RValueRefParam>(m, "RValueRefParam")
|
||||
.def(py::init<>())
|
||||
.def("func1", &RValueRefParam::func1)
|
||||
.def("func2", &RValueRefParam::func2)
|
||||
.def("func3", &RValueRefParam::func3)
|
||||
.def("func4", &RValueRefParam::func4);
|
||||
}
|
||||
504
third_party/pybind11/tests/test_methods_and_attributes.py
vendored
Normal file
504
third_party/pybind11/tests/test_methods_and_attributes.py
vendored
Normal file
@@ -0,0 +1,504 @@
|
||||
import pytest
|
||||
|
||||
import env # noqa: F401
|
||||
from pybind11_tests import ConstructorStats
|
||||
from pybind11_tests import methods_and_attributes as m
|
||||
|
||||
|
||||
def test_methods_and_attributes():
|
||||
instance1 = m.ExampleMandA()
|
||||
instance2 = m.ExampleMandA(32)
|
||||
|
||||
instance1.add1(instance2)
|
||||
instance1.add2(instance2)
|
||||
instance1.add3(instance2)
|
||||
instance1.add4(instance2)
|
||||
instance1.add5(instance2)
|
||||
instance1.add6(32)
|
||||
instance1.add7(32)
|
||||
instance1.add8(32)
|
||||
instance1.add9(32)
|
||||
instance1.add10(32)
|
||||
|
||||
assert str(instance1) == "ExampleMandA[value=320]"
|
||||
assert str(instance2) == "ExampleMandA[value=32]"
|
||||
assert str(instance1.self1()) == "ExampleMandA[value=320]"
|
||||
assert str(instance1.self2()) == "ExampleMandA[value=320]"
|
||||
assert str(instance1.self3()) == "ExampleMandA[value=320]"
|
||||
assert str(instance1.self4()) == "ExampleMandA[value=320]"
|
||||
assert str(instance1.self5()) == "ExampleMandA[value=320]"
|
||||
|
||||
assert instance1.internal1() == 320
|
||||
assert instance1.internal2() == 320
|
||||
assert instance1.internal3() == 320
|
||||
assert instance1.internal4() == 320
|
||||
assert instance1.internal5() == 320
|
||||
|
||||
assert instance1.overloaded() == "()"
|
||||
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.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.0, 8.0) == "(float, float) const"
|
||||
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
|
||||
assert str(instance1) == "ExampleMandA[value=100]"
|
||||
|
||||
cstats = ConstructorStats.get(m.ExampleMandA)
|
||||
assert cstats.alive() == 2
|
||||
del instance1, instance2
|
||||
assert cstats.alive() == 0
|
||||
assert cstats.values() == ["32"]
|
||||
assert cstats.default_constructions == 1
|
||||
assert cstats.copy_constructions == 2
|
||||
assert cstats.move_constructions >= 2
|
||||
assert cstats.copy_assignments == 0
|
||||
assert cstats.move_assignments == 0
|
||||
|
||||
|
||||
def test_copy_method():
|
||||
"""Issue #443: calling copied methods fails in Python 3"""
|
||||
|
||||
m.ExampleMandA.add2c = m.ExampleMandA.add2
|
||||
m.ExampleMandA.add2d = m.ExampleMandA.add2b
|
||||
a = m.ExampleMandA(123)
|
||||
assert a.value == 123
|
||||
a.add2(m.ExampleMandA(-100))
|
||||
assert a.value == 23
|
||||
a.add2b(m.ExampleMandA(20))
|
||||
assert a.value == 43
|
||||
a.add2c(m.ExampleMandA(6))
|
||||
assert a.value == 49
|
||||
a.add2d(m.ExampleMandA(-7))
|
||||
assert a.value == 42
|
||||
|
||||
|
||||
def test_properties():
|
||||
instance = m.TestProperties()
|
||||
|
||||
assert instance.def_readonly == 1
|
||||
with pytest.raises(AttributeError):
|
||||
instance.def_readonly = 2
|
||||
|
||||
instance.def_readwrite = 2
|
||||
assert instance.def_readwrite == 2
|
||||
|
||||
assert instance.def_property_readonly == 2
|
||||
with pytest.raises(AttributeError):
|
||||
instance.def_property_readonly = 3
|
||||
|
||||
instance.def_property = 3
|
||||
assert instance.def_property == 3
|
||||
|
||||
with pytest.raises(AttributeError) as excinfo:
|
||||
dummy = instance.def_property_writeonly # unused var
|
||||
assert "unreadable attribute" in str(excinfo.value)
|
||||
|
||||
instance.def_property_writeonly = 4
|
||||
assert instance.def_property_readonly == 4
|
||||
|
||||
with pytest.raises(AttributeError) as excinfo:
|
||||
dummy = instance.def_property_impossible # noqa: F841 unused var
|
||||
assert "unreadable attribute" in str(excinfo.value)
|
||||
|
||||
with pytest.raises(AttributeError) as excinfo:
|
||||
instance.def_property_impossible = 5
|
||||
assert "can't set attribute" in str(excinfo.value)
|
||||
|
||||
|
||||
def test_static_properties():
|
||||
assert m.TestProperties.def_readonly_static == 1
|
||||
with pytest.raises(AttributeError) as excinfo:
|
||||
m.TestProperties.def_readonly_static = 2
|
||||
assert "can't set attribute" in str(excinfo.value)
|
||||
|
||||
m.TestProperties.def_readwrite_static = 2
|
||||
assert m.TestProperties.def_readwrite_static == 2
|
||||
|
||||
with pytest.raises(AttributeError) as excinfo:
|
||||
dummy = m.TestProperties.def_writeonly_static # unused var
|
||||
assert "unreadable attribute" in str(excinfo.value)
|
||||
|
||||
m.TestProperties.def_writeonly_static = 3
|
||||
assert m.TestProperties.def_readonly_static == 3
|
||||
|
||||
assert m.TestProperties.def_property_readonly_static == 3
|
||||
with pytest.raises(AttributeError) as excinfo:
|
||||
m.TestProperties.def_property_readonly_static = 99
|
||||
assert "can't set attribute" in str(excinfo.value)
|
||||
|
||||
m.TestProperties.def_property_static = 4
|
||||
assert m.TestProperties.def_property_static == 4
|
||||
|
||||
with pytest.raises(AttributeError) as excinfo:
|
||||
dummy = m.TestProperties.def_property_writeonly_static
|
||||
assert "unreadable attribute" in str(excinfo.value)
|
||||
|
||||
m.TestProperties.def_property_writeonly_static = 5
|
||||
assert m.TestProperties.def_property_static == 5
|
||||
|
||||
# Static property read and write via instance
|
||||
instance = m.TestProperties()
|
||||
|
||||
m.TestProperties.def_readwrite_static = 0
|
||||
assert m.TestProperties.def_readwrite_static == 0
|
||||
assert instance.def_readwrite_static == 0
|
||||
|
||||
instance.def_readwrite_static = 2
|
||||
assert m.TestProperties.def_readwrite_static == 2
|
||||
assert instance.def_readwrite_static == 2
|
||||
|
||||
with pytest.raises(AttributeError) as excinfo:
|
||||
dummy = instance.def_property_writeonly_static # noqa: F841 unused var
|
||||
assert "unreadable attribute" in str(excinfo.value)
|
||||
|
||||
instance.def_property_writeonly_static = 4
|
||||
assert instance.def_property_static == 4
|
||||
|
||||
# It should be possible to override properties in derived classes
|
||||
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"""
|
||||
|
||||
instance = m.TestProperties()
|
||||
assert m.TestProperties.static_cls is m.TestProperties
|
||||
assert instance.static_cls is m.TestProperties
|
||||
|
||||
def check_self(self):
|
||||
assert self is m.TestProperties
|
||||
|
||||
m.TestProperties.static_cls = check_self
|
||||
instance.static_cls = check_self
|
||||
|
||||
|
||||
def test_metaclass_override():
|
||||
"""Overriding pybind11's default metaclass changes the behavior of `static_property`"""
|
||||
|
||||
assert type(m.ExampleMandA).__name__ == "pybind11_type"
|
||||
assert type(m.MetaclassOverride).__name__ == "type"
|
||||
|
||||
assert m.MetaclassOverride.readonly == 1
|
||||
assert (type(m.MetaclassOverride.__dict__["readonly"]).__name__ ==
|
||||
"pybind11_static_property")
|
||||
|
||||
# Regular `type` replaces the property instead of calling `__set__()`
|
||||
m.MetaclassOverride.readonly = 2
|
||||
assert m.MetaclassOverride.readonly == 2
|
||||
assert isinstance(m.MetaclassOverride.__dict__["readonly"], int)
|
||||
|
||||
|
||||
def test_no_mixed_overloads():
|
||||
from pybind11_tests import detailed_error_messages_enabled
|
||||
|
||||
with pytest.raises(RuntimeError) as excinfo:
|
||||
m.ExampleMandA.add_mixed_overloads1()
|
||||
assert str(
|
||||
excinfo.value
|
||||
) == "overloading a method with both static and instance methods is not supported; " + (
|
||||
"#define PYBIND11_DETAILED_ERROR_MESSAGES or compile in debug mode for more details"
|
||||
if not detailed_error_messages_enabled else
|
||||
"error while attempting to bind static method ExampleMandA.overload_mixed1"
|
||||
"(arg0: float) -> str")
|
||||
|
||||
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; " + (
|
||||
"#define PYBIND11_DETAILED_ERROR_MESSAGES or compile in debug mode for more details"
|
||||
if not detailed_error_messages_enabled else
|
||||
"error while attempting to bind instance method ExampleMandA.overload_mixed2"
|
||||
"(self: pybind11_tests.methods_and_attributes.ExampleMandA, arg0: int, arg1: int)"
|
||||
" -> str")
|
||||
|
||||
|
||||
@pytest.mark.parametrize("access", ["ro", "rw", "static_ro", "static_rw"])
|
||||
def test_property_return_value_policies(access):
|
||||
if not access.startswith("static"):
|
||||
obj = m.TestPropRVP()
|
||||
else:
|
||||
obj = m.TestPropRVP
|
||||
|
||||
ref = getattr(obj, access + "_ref")
|
||||
assert ref.value == 1
|
||||
ref.value = 2
|
||||
assert getattr(obj, access + "_ref").value == 2
|
||||
ref.value = 1 # restore original value for static properties
|
||||
|
||||
copy = getattr(obj, access + "_copy")
|
||||
assert copy.value == 1
|
||||
copy.value = 2
|
||||
assert getattr(obj, access + "_copy").value == 1
|
||||
|
||||
copy = getattr(obj, access + "_func")
|
||||
assert copy.value == 1
|
||||
copy.value = 2
|
||||
assert getattr(obj, access + "_func").value == 1
|
||||
|
||||
|
||||
def test_property_rvalue_policy():
|
||||
"""When returning an rvalue, the return value policy is automatically changed from
|
||||
`reference(_internal)` to `move`. The following would not work otherwise."""
|
||||
|
||||
instance = m.TestPropRVP()
|
||||
o = instance.rvalue
|
||||
assert o.value == 1
|
||||
|
||||
os = m.TestPropRVP.static_rvalue
|
||||
assert os.value == 1
|
||||
|
||||
|
||||
# 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")
|
||||
assert "foo" not in dir(instance)
|
||||
|
||||
# Dynamically add attribute
|
||||
instance.foo = 42
|
||||
assert hasattr(instance, "foo")
|
||||
assert instance.foo == 42
|
||||
assert "foo" in dir(instance)
|
||||
|
||||
# __dict__ should be accessible and replaceable
|
||||
assert "foo" in instance.__dict__
|
||||
instance.__dict__ = {"bar": True}
|
||||
assert not hasattr(instance, "foo")
|
||||
assert hasattr(instance, "bar")
|
||||
|
||||
with pytest.raises(TypeError) as excinfo:
|
||||
instance.__dict__ = []
|
||||
assert str(
|
||||
excinfo.value) == "__dict__ must be set to a dictionary, not a 'list'"
|
||||
|
||||
cstats = ConstructorStats.get(m.DynamicClass)
|
||||
assert cstats.alive() == 1
|
||||
del instance
|
||||
assert cstats.alive() == 0
|
||||
|
||||
# Derived classes should work as well
|
||||
class PythonDerivedDynamicClass(m.DynamicClass):
|
||||
pass
|
||||
|
||||
for cls in m.CppDerivedDynamicClass, PythonDerivedDynamicClass:
|
||||
derived = cls()
|
||||
derived.foobar = 100
|
||||
assert derived.foobar == 100
|
||||
|
||||
assert cstats.alive() == 1
|
||||
del derived
|
||||
assert cstats.alive() == 0
|
||||
|
||||
|
||||
# https://foss.heptapod.net/pypy/pypy/-/issues/2447
|
||||
@pytest.mark.xfail("env.PYPY")
|
||||
def test_cyclic_gc():
|
||||
# One object references itself
|
||||
instance = m.DynamicClass()
|
||||
instance.circular_reference = instance
|
||||
|
||||
cstats = ConstructorStats.get(m.DynamicClass)
|
||||
assert cstats.alive() == 1
|
||||
del instance
|
||||
assert cstats.alive() == 0
|
||||
|
||||
# Two object reference each other
|
||||
i1 = m.DynamicClass()
|
||||
i2 = m.DynamicClass()
|
||||
i1.cycle = i2
|
||||
i2.cycle = i1
|
||||
|
||||
assert cstats.alive() == 2
|
||||
del i1, i2
|
||||
assert cstats.alive() == 0
|
||||
|
||||
|
||||
def test_bad_arg_default(msg):
|
||||
from pybind11_tests import detailed_error_messages_enabled
|
||||
|
||||
with pytest.raises(RuntimeError) as excinfo:
|
||||
m.bad_arg_def_named()
|
||||
assert msg(excinfo.value) == (
|
||||
"arg(): could not convert default argument 'a: UnregisteredType' in function "
|
||||
"'should_fail' into a Python object (type not registered yet?)"
|
||||
if detailed_error_messages_enabled else
|
||||
"arg(): could not convert default argument into a Python object (type not registered "
|
||||
"yet?). #define PYBIND11_DETAILED_ERROR_MESSAGES or compile in debug mode for more information."
|
||||
)
|
||||
|
||||
with pytest.raises(RuntimeError) as excinfo:
|
||||
m.bad_arg_def_unnamed()
|
||||
assert msg(excinfo.value) == (
|
||||
"arg(): could not convert default argument 'UnregisteredType' in function "
|
||||
"'should_fail' into a Python object (type not registered yet?)"
|
||||
if detailed_error_messages_enabled else
|
||||
"arg(): could not convert default argument into a Python object (type not registered "
|
||||
"yet?). #define PYBIND11_DETAILED_ERROR_MESSAGES or compile in debug mode for more information."
|
||||
)
|
||||
|
||||
|
||||
def test_accepts_none(msg):
|
||||
a = m.NoneTester()
|
||||
assert m.no_none1(a) == 42
|
||||
assert m.no_none2(a) == 42
|
||||
assert m.no_none3(a) == 42
|
||||
assert m.no_none4(a) == 42
|
||||
assert m.no_none5(a) == 42
|
||||
assert m.ok_none1(a) == 42
|
||||
assert m.ok_none2(a) == 42
|
||||
assert m.ok_none3(a) == 42
|
||||
assert m.ok_none4(a) == 42
|
||||
assert m.ok_none5(a) == 42
|
||||
|
||||
with pytest.raises(TypeError) as excinfo:
|
||||
m.no_none1(None)
|
||||
assert "incompatible function arguments" in str(excinfo.value)
|
||||
with pytest.raises(TypeError) as excinfo:
|
||||
m.no_none2(None)
|
||||
assert "incompatible function arguments" in str(excinfo.value)
|
||||
with pytest.raises(TypeError) as excinfo:
|
||||
m.no_none3(None)
|
||||
assert "incompatible function arguments" in str(excinfo.value)
|
||||
with pytest.raises(TypeError) as excinfo:
|
||||
m.no_none4(None)
|
||||
assert "incompatible function arguments" in str(excinfo.value)
|
||||
with pytest.raises(TypeError) as excinfo:
|
||||
m.no_none5(None)
|
||||
assert "incompatible function arguments" in str(excinfo.value)
|
||||
|
||||
# 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) == """
|
||||
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
|
||||
assert m.ok_none3(None) == -1
|
||||
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_casts_none():
|
||||
"""#2778: implicit casting from None to object (not pointer)"""
|
||||
a = m.NoneCastTester()
|
||||
assert m.ok_obj_or_none(a) == -1
|
||||
a = m.NoneCastTester(4)
|
||||
assert m.ok_obj_or_none(a) == 4
|
||||
a = m.NoneCastTester(None)
|
||||
assert m.ok_obj_or_none(a) == -1
|
||||
assert m.ok_obj_or_none(None) == -1
|
||||
|
||||
|
||||
def test_str_issue(msg):
|
||||
"""#283: __str__ called on uninitialized instance when constructor arguments invalid"""
|
||||
|
||||
assert str(m.StrIssue(3)) == "StrIssue[3]"
|
||||
|
||||
with pytest.raises(TypeError) as excinfo:
|
||||
str(m.StrIssue("no", "such", "constructor"))
|
||||
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():
|
||||
a = m.RegisteredDerived()
|
||||
a.do_nothing()
|
||||
assert a.rw_value == 42
|
||||
assert a.ro_value == 1.25
|
||||
a.rw_value += 5
|
||||
assert a.sum() == 48.25
|
||||
a.increase_value()
|
||||
assert a.rw_value == 48
|
||||
assert a.ro_value == 1.5
|
||||
assert a.sum() == 49.5
|
||||
assert a.rw_value_prop == 48
|
||||
a.rw_value_prop += 1
|
||||
assert a.rw_value_prop == 49
|
||||
a.increase_value()
|
||||
assert a.ro_value_prop == 1.75
|
||||
|
||||
|
||||
def test_ref_qualified():
|
||||
"""Tests that explicit lvalue ref-qualified methods can be called just like their
|
||||
non ref-qualified counterparts."""
|
||||
|
||||
r = m.RefQualified()
|
||||
assert r.value == 0
|
||||
r.refQualified(17)
|
||||
assert r.value == 17
|
||||
assert r.constRefQualified(23) == 40
|
||||
|
||||
|
||||
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
|
||||
|
||||
assert "1. overload_order(arg0: int) -> int" in m.overload_order.__doc__
|
||||
assert "2. overload_order(arg0: str) -> int" in m.overload_order.__doc__
|
||||
assert "3. overload_order(arg0: str) -> int" in m.overload_order.__doc__
|
||||
assert "4. overload_order(arg0: int) -> int" in m.overload_order.__doc__
|
||||
|
||||
with pytest.raises(TypeError) as err:
|
||||
m.overload_order(1.1)
|
||||
|
||||
assert "1. (arg0: int) -> int" in str(err.value)
|
||||
assert "2. (arg0: str) -> int" in str(err.value)
|
||||
assert "3. (arg0: str) -> int" in str(err.value)
|
||||
assert "4. (arg0: int) -> int" in str(err.value)
|
||||
|
||||
|
||||
def test_rvalue_ref_param():
|
||||
r = m.RValueRefParam()
|
||||
assert r.func1("123") == 3
|
||||
assert r.func2("1234") == 4
|
||||
assert r.func3("12345") == 5
|
||||
assert r.func4("123456") == 6
|
||||
123
third_party/pybind11/tests/test_modules.cpp
vendored
Normal file
123
third_party/pybind11/tests/test_modules.cpp
vendored
Normal file
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
tests/test_modules.cpp -- nested modules, importing modules, and
|
||||
internal references
|
||||
|
||||
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 "constructor_stats.h"
|
||||
#include "pybind11_tests.h"
|
||||
|
||||
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()"; });
|
||||
|
||||
// test_reference_internal
|
||||
class A {
|
||||
public:
|
||||
explicit A(int v) : v(v) { print_created(this, v); }
|
||||
~A() { print_destroyed(this); }
|
||||
A(const A &) { print_copy_created(this); }
|
||||
A &operator=(const A ©) {
|
||||
print_copy_assigned(this);
|
||||
v = copy.v;
|
||||
return *this;
|
||||
}
|
||||
std::string toString() const { return "A[" + std::to_string(v) + "]"; }
|
||||
|
||||
private:
|
||||
int v;
|
||||
};
|
||||
py::class_<A>(m_sub, "A").def(py::init<int>()).def("__repr__", &A::toString);
|
||||
|
||||
class B {
|
||||
public:
|
||||
B() { print_default_created(this); }
|
||||
~B() { print_destroyed(this); }
|
||||
B(const B &) { print_copy_created(this); }
|
||||
B &operator=(const B ©) {
|
||||
print_copy_assigned(this);
|
||||
a1 = copy.a1;
|
||||
a2 = copy.a2;
|
||||
return *this;
|
||||
}
|
||||
A &get_a1() { return a1; }
|
||||
A &get_a2() { return a2; }
|
||||
|
||||
A a1{1};
|
||||
A a2{2};
|
||||
};
|
||||
py::class_<B>(m_sub, "B")
|
||||
.def(py::init<>())
|
||||
.def("get_a1",
|
||||
&B::get_a1,
|
||||
"Return the internal A 1",
|
||||
py::return_value_policy::reference_internal)
|
||||
.def("get_a2",
|
||||
&B::get_a2,
|
||||
"Return the internal A 2",
|
||||
py::return_value_policy::reference_internal)
|
||||
.def_readwrite("a1", &B::a1) // def_readonly uses an internal
|
||||
// reference return policy by default
|
||||
.def_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
|
||||
// Registering two things with the same name
|
||||
m.def("duplicate_registration", []() {
|
||||
class Dupe1 {};
|
||||
class Dupe2 {};
|
||||
class Dupe3 {};
|
||||
class DupeException {};
|
||||
|
||||
// Go ahead and leak, until we have a non-leaking py::module_ constructor
|
||||
auto dm
|
||||
= py::module_::create_extension_module("dummy", nullptr, new py::module_::module_def);
|
||||
auto failures = py::list();
|
||||
|
||||
py::class_<Dupe1>(dm, "Dupe1");
|
||||
py::class_<Dupe2>(dm, "Dupe2");
|
||||
dm.def("dupe1_factory", []() { return Dupe1(); });
|
||||
py::exception<DupeException>(dm, "DupeException");
|
||||
|
||||
try {
|
||||
py::class_<Dupe1>(dm, "Dupe1");
|
||||
failures.append("Dupe1 class");
|
||||
} catch (std::runtime_error &) {
|
||||
}
|
||||
try {
|
||||
dm.def("Dupe1", []() { return Dupe1(); });
|
||||
failures.append("Dupe1 function");
|
||||
} catch (std::runtime_error &) {
|
||||
}
|
||||
try {
|
||||
py::class_<Dupe3>(dm, "dupe1_factory");
|
||||
failures.append("dupe1_factory");
|
||||
} catch (std::runtime_error &) {
|
||||
}
|
||||
try {
|
||||
py::exception<Dupe3>(dm, "Dupe2");
|
||||
failures.append("Dupe2");
|
||||
} catch (std::runtime_error &) {
|
||||
}
|
||||
try {
|
||||
dm.def("DupeException", []() { return 30; });
|
||||
failures.append("DupeException1");
|
||||
} catch (std::runtime_error &) {
|
||||
}
|
||||
try {
|
||||
py::class_<DupeException>(dm, "DupeException");
|
||||
failures.append("DupeException2");
|
||||
} catch (std::runtime_error &) {
|
||||
}
|
||||
|
||||
return failures;
|
||||
});
|
||||
}
|
||||
89
third_party/pybind11/tests/test_modules.py
vendored
Normal file
89
third_party/pybind11/tests/test_modules.py
vendored
Normal file
@@ -0,0 +1,89 @@
|
||||
from pybind11_tests import ConstructorStats
|
||||
from pybind11_tests import modules as m
|
||||
from pybind11_tests.modules import subsubmodule as ms
|
||||
|
||||
|
||||
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 m.__name__ == "pybind11_tests.modules"
|
||||
assert ms.__name__ == "pybind11_tests.modules.subsubmodule"
|
||||
|
||||
assert ms.submodule_func() == "submodule_func()"
|
||||
|
||||
|
||||
def test_reference_internal():
|
||||
b = ms.B()
|
||||
assert str(b.get_a1()) == "A[1]"
|
||||
assert str(b.a1) == "A[1]"
|
||||
assert str(b.get_a2()) == "A[2]"
|
||||
assert str(b.a2) == "A[2]"
|
||||
|
||||
b.a1 = ms.A(42)
|
||||
b.a2 = ms.A(43)
|
||||
assert str(b.get_a1()) == "A[42]"
|
||||
assert str(b.a1) == "A[42]"
|
||||
assert str(b.get_a2()) == "A[43]"
|
||||
assert str(b.a2) == "A[43]"
|
||||
|
||||
astats, bstats = ConstructorStats.get(ms.A), ConstructorStats.get(ms.B)
|
||||
assert astats.alive() == 2
|
||||
assert bstats.alive() == 1
|
||||
del b
|
||||
assert astats.alive() == 0
|
||||
assert bstats.alive() == 0
|
||||
assert astats.values() == ["1", "2", "42", "43"]
|
||||
assert bstats.values() == []
|
||||
assert astats.default_constructions == 0
|
||||
assert bstats.default_constructions == 1
|
||||
assert astats.copy_constructions == 0
|
||||
assert bstats.copy_constructions == 0
|
||||
# assert astats.move_constructions >= 0 # Don't invoke any
|
||||
# assert bstats.move_constructions >= 0 # Don't invoke any
|
||||
assert astats.copy_assignments == 2
|
||||
assert bstats.copy_assignments == 0
|
||||
assert astats.move_assignments == 0
|
||||
assert bstats.move_assignments == 0
|
||||
|
||||
|
||||
def test_importing():
|
||||
from collections import OrderedDict
|
||||
|
||||
from pybind11_tests.modules import OD
|
||||
|
||||
assert OD is OrderedDict
|
||||
assert str(OD([(1, "a"), (2, "b")])) == "OrderedDict([(1, 'a'), (2, 'b')])"
|
||||
|
||||
|
||||
def test_pydoc():
|
||||
"""Pydoc needs to be able to provide help() for everything inside a pybind11 module"""
|
||||
import pydoc
|
||||
|
||||
import pybind11_tests
|
||||
|
||||
assert pybind11_tests.__name__ == "pybind11_tests"
|
||||
assert pybind11_tests.__doc__ == "pybind11 test module"
|
||||
assert pydoc.text.docmodule(pybind11_tests)
|
||||
|
||||
|
||||
def test_duplicate_registration():
|
||||
"""Registering two things with the same name"""
|
||||
|
||||
assert m.duplicate_registration() == []
|
||||
|
||||
|
||||
def test_builtin_key_type():
|
||||
"""Test that all the keys in the builtin modules have type str.
|
||||
|
||||
Previous versions of pybind11 would add a unicode key in python 2.
|
||||
"""
|
||||
if hasattr(__builtins__, "keys"):
|
||||
keys = __builtins__.keys()
|
||||
else: # this is to make pypy happy since builtins is different there.
|
||||
keys = __builtins__.__dict__.keys()
|
||||
|
||||
assert {type(k) for k in keys} == {str}
|
||||
341
third_party/pybind11/tests/test_multiple_inheritance.cpp
vendored
Normal file
341
third_party/pybind11/tests/test_multiple_inheritance.cpp
vendored
Normal file
@@ -0,0 +1,341 @@
|
||||
/*
|
||||
tests/test_multiple_inheritance.cpp -- multiple inheritance,
|
||||
implicit MI casts
|
||||
|
||||
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 "constructor_stats.h"
|
||||
#include "pybind11_tests.h"
|
||||
|
||||
namespace {
|
||||
|
||||
// Many bases for testing that multiple inheritance from many classes (i.e. requiring extra
|
||||
// space for holder constructed flags) works.
|
||||
template <int N>
|
||||
struct BaseN {
|
||||
explicit BaseN(int i) : i(i) {}
|
||||
int i;
|
||||
};
|
||||
|
||||
// test_mi_static_properties
|
||||
struct Vanilla {
|
||||
std::string vanilla() { return "Vanilla"; };
|
||||
};
|
||||
struct WithStatic1 {
|
||||
static std::string static_func1() { return "WithStatic1"; };
|
||||
static int static_value1;
|
||||
};
|
||||
struct WithStatic2 {
|
||||
static std::string static_func2() { return "WithStatic2"; };
|
||||
static int static_value2;
|
||||
};
|
||||
struct VanillaStaticMix1 : Vanilla, WithStatic1, WithStatic2 {
|
||||
static std::string static_func() { return "VanillaStaticMix1"; }
|
||||
static int static_value;
|
||||
};
|
||||
struct VanillaStaticMix2 : WithStatic1, Vanilla, WithStatic2 {
|
||||
static std::string static_func() { return "VanillaStaticMix2"; }
|
||||
static int static_value;
|
||||
};
|
||||
int WithStatic1::static_value1 = 1;
|
||||
int WithStatic2::static_value2 = 2;
|
||||
int VanillaStaticMix1::static_value = 12;
|
||||
int VanillaStaticMix2::static_value = 12;
|
||||
|
||||
// test_multiple_inheritance_virtbase
|
||||
struct Base1a {
|
||||
explicit Base1a(int i) : i(i) {}
|
||||
int foo() const { return i; }
|
||||
int i;
|
||||
};
|
||||
struct Base2a {
|
||||
explicit Base2a(int i) : i(i) {}
|
||||
int bar() const { return i; }
|
||||
int i;
|
||||
};
|
||||
struct Base12a : Base1a, Base2a {
|
||||
Base12a(int i, int j) : Base1a(i), Base2a(j) {}
|
||||
};
|
||||
|
||||
// test_mi_unaligned_base
|
||||
// test_mi_base_return
|
||||
struct I801B1 {
|
||||
int a = 1;
|
||||
I801B1() = default;
|
||||
I801B1(const I801B1 &) = default;
|
||||
virtual ~I801B1() = default;
|
||||
};
|
||||
struct I801B2 {
|
||||
int b = 2;
|
||||
I801B2() = default;
|
||||
I801B2(const I801B2 &) = default;
|
||||
virtual ~I801B2() = default;
|
||||
};
|
||||
struct I801C : I801B1, I801B2 {};
|
||||
struct I801D : I801C {}; // Indirect MI
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST_SUBMODULE(multiple_inheritance, m) {
|
||||
// Please do not interleave `struct` and `class` definitions with bindings code,
|
||||
// but implement `struct`s and `class`es in the anonymous namespace above.
|
||||
// This helps keeping the smart_holder branch in sync with master.
|
||||
|
||||
// test_multiple_inheritance_mix1
|
||||
// test_multiple_inheritance_mix2
|
||||
struct Base1 {
|
||||
explicit Base1(int i) : i(i) {}
|
||||
int foo() const { return i; }
|
||||
int i;
|
||||
};
|
||||
py::class_<Base1> b1(m, "Base1");
|
||||
b1.def(py::init<int>()).def("foo", &Base1::foo);
|
||||
|
||||
struct Base2 {
|
||||
explicit Base2(int i) : i(i) {}
|
||||
int bar() const { return i; }
|
||||
int i;
|
||||
};
|
||||
py::class_<Base2> b2(m, "Base2");
|
||||
b2.def(py::init<int>()).def("bar", &Base2::bar);
|
||||
|
||||
// test_multiple_inheritance_cpp
|
||||
struct Base12 : Base1, Base2 {
|
||||
Base12(int i, int j) : Base1(i), Base2(j) {}
|
||||
};
|
||||
struct MIType : Base12 {
|
||||
MIType(int i, int j) : Base12(i, j) {}
|
||||
};
|
||||
py::class_<Base12, Base1, Base2>(m, "Base12");
|
||||
py::class_<MIType, Base12>(m, "MIType").def(py::init<int, int>());
|
||||
|
||||
// test_multiple_inheritance_python_many_bases
|
||||
#define PYBIND11_BASEN(N) \
|
||||
py::class_<BaseN<(N)>>(m, "BaseN" #N).def(py::init<int>()).def("f" #N, [](BaseN<N> &b) { \
|
||||
return b.i + (N); \
|
||||
})
|
||||
PYBIND11_BASEN(1);
|
||||
PYBIND11_BASEN(2);
|
||||
PYBIND11_BASEN(3);
|
||||
PYBIND11_BASEN(4);
|
||||
PYBIND11_BASEN(5);
|
||||
PYBIND11_BASEN(6);
|
||||
PYBIND11_BASEN(7);
|
||||
PYBIND11_BASEN(8);
|
||||
PYBIND11_BASEN(9);
|
||||
PYBIND11_BASEN(10);
|
||||
PYBIND11_BASEN(11);
|
||||
PYBIND11_BASEN(12);
|
||||
PYBIND11_BASEN(13);
|
||||
PYBIND11_BASEN(14);
|
||||
PYBIND11_BASEN(15);
|
||||
PYBIND11_BASEN(16);
|
||||
PYBIND11_BASEN(17);
|
||||
|
||||
// Uncommenting this should result in a compile time failure (MI can only be specified via
|
||||
// template parameters because pybind has to know the types involved; see discussion in #742
|
||||
// for details).
|
||||
// struct Base12v2 : Base1, Base2 {
|
||||
// Base12v2(int i, int j) : Base1(i), Base2(j) { }
|
||||
// };
|
||||
// py::class_<Base12v2>(m, "Base12v2", b1, b2)
|
||||
// .def(py::init<int, int>());
|
||||
|
||||
// test_multiple_inheritance_virtbase
|
||||
// Test the case where not all base classes are specified, and where pybind11 requires the
|
||||
// py::multiple_inheritance flag to perform proper casting between types.
|
||||
py::class_<Base1a, std::shared_ptr<Base1a>>(m, "Base1a")
|
||||
.def(py::init<int>())
|
||||
.def("foo", &Base1a::foo);
|
||||
|
||||
py::class_<Base2a, std::shared_ptr<Base2a>>(m, "Base2a")
|
||||
.def(py::init<int>())
|
||||
.def("bar", &Base2a::bar);
|
||||
|
||||
py::class_<Base12a, /* Base1 missing */ Base2a, std::shared_ptr<Base12a>>(
|
||||
m, "Base12a", py::multiple_inheritance())
|
||||
.def(py::init<int, int>());
|
||||
|
||||
m.def("bar_base2a", [](Base2a *b) { return b->bar(); });
|
||||
m.def("bar_base2a_sharedptr", [](const std::shared_ptr<Base2a> &b) { return b->bar(); });
|
||||
|
||||
// test_mi_unaligned_base
|
||||
// test_mi_base_return
|
||||
// Issue #801: invalid casting to derived type with MI bases
|
||||
// Unregistered classes:
|
||||
struct I801B3 {
|
||||
int c = 3;
|
||||
virtual ~I801B3() = default;
|
||||
};
|
||||
struct I801E : I801B3, I801D {};
|
||||
|
||||
py::class_<I801B1, std::shared_ptr<I801B1>>(m, "I801B1")
|
||||
.def(py::init<>())
|
||||
.def_readonly("a", &I801B1::a);
|
||||
py::class_<I801B2, std::shared_ptr<I801B2>>(m, "I801B2")
|
||||
.def(py::init<>())
|
||||
.def_readonly("b", &I801B2::b);
|
||||
py::class_<I801C, I801B1, I801B2, std::shared_ptr<I801C>>(m, "I801C").def(py::init<>());
|
||||
py::class_<I801D, I801C, std::shared_ptr<I801D>>(m, "I801D").def(py::init<>());
|
||||
|
||||
// Two separate issues here: first, we want to recognize a pointer to a base type as being a
|
||||
// known instance even when the pointer value is unequal (i.e. due to a non-first
|
||||
// multiple-inheritance base class):
|
||||
m.def("i801b1_c", [](I801C *c) { return static_cast<I801B1 *>(c); });
|
||||
m.def("i801b2_c", [](I801C *c) { return static_cast<I801B2 *>(c); });
|
||||
m.def("i801b1_d", [](I801D *d) { return static_cast<I801B1 *>(d); });
|
||||
m.def("i801b2_d", [](I801D *d) { return static_cast<I801B2 *>(d); });
|
||||
|
||||
// Second, when returned a base class pointer to a derived instance, we cannot assume that the
|
||||
// pointer is `reinterpret_cast`able to the derived pointer because, like above, the base class
|
||||
// pointer could be offset.
|
||||
m.def("i801c_b1", []() -> I801B1 * { return new I801C(); });
|
||||
m.def("i801c_b2", []() -> I801B2 * { return new I801C(); });
|
||||
m.def("i801d_b1", []() -> I801B1 * { return new I801D(); });
|
||||
m.def("i801d_b2", []() -> I801B2 * { return new I801D(); });
|
||||
|
||||
// Return a base class pointer to a pybind-registered type when the actual derived type
|
||||
// isn't pybind-registered (and uses multiple-inheritance to offset the pybind base)
|
||||
m.def("i801e_c", []() -> I801C * { return new I801E(); });
|
||||
m.def("i801e_b2", []() -> I801B2 * { return new I801E(); });
|
||||
|
||||
// test_mi_static_properties
|
||||
py::class_<Vanilla>(m, "Vanilla").def(py::init<>()).def("vanilla", &Vanilla::vanilla);
|
||||
|
||||
py::class_<WithStatic1>(m, "WithStatic1")
|
||||
.def(py::init<>())
|
||||
.def_static("static_func1", &WithStatic1::static_func1)
|
||||
.def_readwrite_static("static_value1", &WithStatic1::static_value1);
|
||||
|
||||
py::class_<WithStatic2>(m, "WithStatic2")
|
||||
.def(py::init<>())
|
||||
.def_static("static_func2", &WithStatic2::static_func2)
|
||||
.def_readwrite_static("static_value2", &WithStatic2::static_value2);
|
||||
|
||||
py::class_<VanillaStaticMix1, Vanilla, WithStatic1, WithStatic2>(m, "VanillaStaticMix1")
|
||||
.def(py::init<>())
|
||||
.def_static("static_func", &VanillaStaticMix1::static_func)
|
||||
.def_readwrite_static("static_value", &VanillaStaticMix1::static_value);
|
||||
|
||||
py::class_<VanillaStaticMix2, WithStatic1, Vanilla, WithStatic2>(m, "VanillaStaticMix2")
|
||||
.def(py::init<>())
|
||||
.def_static("static_func", &VanillaStaticMix2::static_func)
|
||||
.def_readwrite_static("static_value", &VanillaStaticMix2::static_value);
|
||||
|
||||
struct WithDict {};
|
||||
struct VanillaDictMix1 : Vanilla, WithDict {};
|
||||
struct VanillaDictMix2 : WithDict, Vanilla {};
|
||||
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<>());
|
||||
|
||||
// test_diamond_inheritance
|
||||
// Issue #959: segfault when constructing diamond inheritance instance
|
||||
// All of these have int members so that there will be various unequal pointers involved.
|
||||
struct B {
|
||||
int b;
|
||||
B() = default;
|
||||
B(const B &) = default;
|
||||
virtual ~B() = default;
|
||||
};
|
||||
struct C0 : public virtual B {
|
||||
int c0;
|
||||
};
|
||||
struct C1 : public virtual B {
|
||||
int c1;
|
||||
};
|
||||
struct D : public C0, public C1 {
|
||||
int d;
|
||||
};
|
||||
py::class_<B>(m, "B").def("b", [](B *self) { return self; });
|
||||
py::class_<C0, B>(m, "C0").def("c0", [](C0 *self) { return self; });
|
||||
py::class_<C1, B>(m, "C1").def("c1", [](C1 *self) { return self; });
|
||||
py::class_<D, C0, C1>(m, "D").def(py::init<>());
|
||||
|
||||
// test_pr3635_diamond_*
|
||||
// - functions are get_{base}_{var}, return {var}
|
||||
struct MVB {
|
||||
MVB() = default;
|
||||
MVB(const MVB &) = default;
|
||||
virtual ~MVB() = default;
|
||||
|
||||
int b = 1;
|
||||
int get_b_b() const { return b; }
|
||||
};
|
||||
struct MVC : virtual MVB {
|
||||
int c = 2;
|
||||
int get_c_b() const { return b; }
|
||||
int get_c_c() const { return c; }
|
||||
};
|
||||
struct MVD0 : virtual MVC {
|
||||
int d0 = 3;
|
||||
int get_d0_b() const { return b; }
|
||||
int get_d0_c() const { return c; }
|
||||
int get_d0_d0() const { return d0; }
|
||||
};
|
||||
struct MVD1 : virtual MVC {
|
||||
int d1 = 4;
|
||||
int get_d1_b() const { return b; }
|
||||
int get_d1_c() const { return c; }
|
||||
int get_d1_d1() const { return d1; }
|
||||
};
|
||||
struct MVE : virtual MVD0, virtual MVD1 {
|
||||
int e = 5;
|
||||
int get_e_b() const { return b; }
|
||||
int get_e_c() const { return c; }
|
||||
int get_e_d0() const { return d0; }
|
||||
int get_e_d1() const { return d1; }
|
||||
int get_e_e() const { return e; }
|
||||
};
|
||||
struct MVF : virtual MVE {
|
||||
int f = 6;
|
||||
int get_f_b() const { return b; }
|
||||
int get_f_c() const { return c; }
|
||||
int get_f_d0() const { return d0; }
|
||||
int get_f_d1() const { return d1; }
|
||||
int get_f_e() const { return e; }
|
||||
int get_f_f() const { return f; }
|
||||
};
|
||||
py::class_<MVB>(m, "MVB")
|
||||
.def(py::init<>())
|
||||
.def("get_b_b", &MVB::get_b_b)
|
||||
.def_readwrite("b", &MVB::b);
|
||||
py::class_<MVC, MVB>(m, "MVC")
|
||||
.def(py::init<>())
|
||||
.def("get_c_b", &MVC::get_c_b)
|
||||
.def("get_c_c", &MVC::get_c_c)
|
||||
.def_readwrite("c", &MVC::c);
|
||||
py::class_<MVD0, MVC>(m, "MVD0")
|
||||
.def(py::init<>())
|
||||
.def("get_d0_b", &MVD0::get_d0_b)
|
||||
.def("get_d0_c", &MVD0::get_d0_c)
|
||||
.def("get_d0_d0", &MVD0::get_d0_d0)
|
||||
.def_readwrite("d0", &MVD0::d0);
|
||||
py::class_<MVD1, MVC>(m, "MVD1")
|
||||
.def(py::init<>())
|
||||
.def("get_d1_b", &MVD1::get_d1_b)
|
||||
.def("get_d1_c", &MVD1::get_d1_c)
|
||||
.def("get_d1_d1", &MVD1::get_d1_d1)
|
||||
.def_readwrite("d1", &MVD1::d1);
|
||||
py::class_<MVE, MVD0, MVD1>(m, "MVE")
|
||||
.def(py::init<>())
|
||||
.def("get_e_b", &MVE::get_e_b)
|
||||
.def("get_e_c", &MVE::get_e_c)
|
||||
.def("get_e_d0", &MVE::get_e_d0)
|
||||
.def("get_e_d1", &MVE::get_e_d1)
|
||||
.def("get_e_e", &MVE::get_e_e)
|
||||
.def_readwrite("e", &MVE::e);
|
||||
py::class_<MVF, MVE>(m, "MVF")
|
||||
.def(py::init<>())
|
||||
.def("get_f_b", &MVF::get_f_b)
|
||||
.def("get_f_c", &MVF::get_f_c)
|
||||
.def("get_f_d0", &MVF::get_f_d0)
|
||||
.def("get_f_d1", &MVF::get_f_d1)
|
||||
.def("get_f_e", &MVF::get_f_e)
|
||||
.def("get_f_f", &MVF::get_f_f)
|
||||
.def_readwrite("f", &MVF::f);
|
||||
}
|
||||
493
third_party/pybind11/tests/test_multiple_inheritance.py
vendored
Normal file
493
third_party/pybind11/tests/test_multiple_inheritance.py
vendored
Normal file
@@ -0,0 +1,493 @@
|
||||
import pytest
|
||||
|
||||
import env # noqa: F401
|
||||
from pybind11_tests import ConstructorStats
|
||||
from pybind11_tests import multiple_inheritance as m
|
||||
|
||||
|
||||
def test_multiple_inheritance_cpp():
|
||||
mt = m.MIType(3, 4)
|
||||
|
||||
assert mt.foo() == 3
|
||||
assert mt.bar() == 4
|
||||
|
||||
|
||||
@pytest.mark.xfail("env.PYPY")
|
||||
def test_multiple_inheritance_mix1():
|
||||
class Base1:
|
||||
def __init__(self, i):
|
||||
self.i = i
|
||||
|
||||
def foo(self):
|
||||
return self.i
|
||||
|
||||
class MITypePy(Base1, m.Base2):
|
||||
def __init__(self, i, j):
|
||||
Base1.__init__(self, i)
|
||||
m.Base2.__init__(self, j)
|
||||
|
||||
mt = MITypePy(3, 4)
|
||||
|
||||
assert mt.foo() == 3
|
||||
assert mt.bar() == 4
|
||||
|
||||
|
||||
def test_multiple_inheritance_mix2():
|
||||
class Base2:
|
||||
def __init__(self, i):
|
||||
self.i = i
|
||||
|
||||
def bar(self):
|
||||
return self.i
|
||||
|
||||
class MITypePy(m.Base1, Base2):
|
||||
def __init__(self, i, j):
|
||||
m.Base1.__init__(self, i)
|
||||
Base2.__init__(self, j)
|
||||
|
||||
mt = MITypePy(3, 4)
|
||||
|
||||
assert mt.foo() == 3
|
||||
assert mt.bar() == 4
|
||||
|
||||
|
||||
@pytest.mark.xfail("env.PYPY")
|
||||
def test_multiple_inheritance_python():
|
||||
class MI1(m.Base1, m.Base2):
|
||||
def __init__(self, i, j):
|
||||
m.Base1.__init__(self, i)
|
||||
m.Base2.__init__(self, j)
|
||||
|
||||
class B1:
|
||||
def v(self):
|
||||
return 1
|
||||
|
||||
class MI2(B1, m.Base1, m.Base2):
|
||||
def __init__(self, i, j):
|
||||
B1.__init__(self)
|
||||
m.Base1.__init__(self, i)
|
||||
m.Base2.__init__(self, j)
|
||||
|
||||
class MI3(MI2):
|
||||
def __init__(self, i, j):
|
||||
MI2.__init__(self, i, j)
|
||||
|
||||
class MI4(MI3, m.Base2):
|
||||
def __init__(self, i, j):
|
||||
MI3.__init__(self, i, j)
|
||||
# This should be ignored (Base2 is already initialized via MI2):
|
||||
m.Base2.__init__(self, i + 100)
|
||||
|
||||
class MI5(m.Base2, B1, m.Base1):
|
||||
def __init__(self, i, j):
|
||||
B1.__init__(self)
|
||||
m.Base1.__init__(self, i)
|
||||
m.Base2.__init__(self, j)
|
||||
|
||||
class MI6(m.Base2, B1):
|
||||
def __init__(self, i):
|
||||
m.Base2.__init__(self, i)
|
||||
B1.__init__(self)
|
||||
|
||||
class B2(B1):
|
||||
def v(self):
|
||||
return 2
|
||||
|
||||
class B3:
|
||||
def v(self):
|
||||
return 3
|
||||
|
||||
class B4(B3, B2):
|
||||
def v(self):
|
||||
return 4
|
||||
|
||||
class MI7(B4, MI6):
|
||||
def __init__(self, i):
|
||||
B4.__init__(self)
|
||||
MI6.__init__(self, i)
|
||||
|
||||
class MI8(MI6, B3):
|
||||
def __init__(self, i):
|
||||
MI6.__init__(self, i)
|
||||
B3.__init__(self)
|
||||
|
||||
class MI8b(B3, MI6):
|
||||
def __init__(self, i):
|
||||
B3.__init__(self)
|
||||
MI6.__init__(self, i)
|
||||
|
||||
mi1 = MI1(1, 2)
|
||||
assert mi1.foo() == 1
|
||||
assert mi1.bar() == 2
|
||||
|
||||
mi2 = MI2(3, 4)
|
||||
assert mi2.v() == 1
|
||||
assert mi2.foo() == 3
|
||||
assert mi2.bar() == 4
|
||||
|
||||
mi3 = MI3(5, 6)
|
||||
assert mi3.v() == 1
|
||||
assert mi3.foo() == 5
|
||||
assert mi3.bar() == 6
|
||||
|
||||
mi4 = MI4(7, 8)
|
||||
assert mi4.v() == 1
|
||||
assert mi4.foo() == 7
|
||||
assert mi4.bar() == 8
|
||||
|
||||
mi5 = MI5(10, 11)
|
||||
assert mi5.v() == 1
|
||||
assert mi5.foo() == 10
|
||||
assert mi5.bar() == 11
|
||||
|
||||
mi6 = MI6(12)
|
||||
assert mi6.v() == 1
|
||||
assert mi6.bar() == 12
|
||||
|
||||
mi7 = MI7(13)
|
||||
assert mi7.v() == 4
|
||||
assert mi7.bar() == 13
|
||||
|
||||
mi8 = MI8(14)
|
||||
assert mi8.v() == 1
|
||||
assert mi8.bar() == 14
|
||||
|
||||
mi8b = MI8b(15)
|
||||
assert mi8b.v() == 3
|
||||
assert mi8b.bar() == 15
|
||||
|
||||
|
||||
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)
|
||||
m.BaseN2.__init__(self, 2)
|
||||
m.BaseN3.__init__(self, 3)
|
||||
m.BaseN4.__init__(self, 4)
|
||||
|
||||
class MIMany58(m.BaseN5, m.BaseN6, m.BaseN7, m.BaseN8):
|
||||
def __init__(self):
|
||||
m.BaseN5.__init__(self, 5)
|
||||
m.BaseN6.__init__(self, 6)
|
||||
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, ):
|
||||
def __init__(self):
|
||||
m.BaseN9.__init__(self, 9)
|
||||
m.BaseN10.__init__(self, 10)
|
||||
m.BaseN11.__init__(self, 11)
|
||||
m.BaseN12.__init__(self, 12)
|
||||
m.BaseN13.__init__(self, 13)
|
||||
m.BaseN14.__init__(self, 14)
|
||||
m.BaseN15.__init__(self, 15)
|
||||
m.BaseN16.__init__(self, 16)
|
||||
|
||||
class MIMany19(MIMany14, MIMany58, m.BaseN9):
|
||||
def __init__(self):
|
||||
MIMany14.__init__(self)
|
||||
MIMany58.__init__(self)
|
||||
m.BaseN9.__init__(self, 9)
|
||||
|
||||
class MIMany117(MIMany14, MIMany58, MIMany916, m.BaseN17):
|
||||
def __init__(self):
|
||||
MIMany14.__init__(self)
|
||||
MIMany58.__init__(self)
|
||||
MIMany916.__init__(self)
|
||||
m.BaseN17.__init__(self, 17)
|
||||
|
||||
# Inherits from 4 registered C++ classes: can fit in one pointer on any modern arch:
|
||||
a = MIMany14()
|
||||
for i in range(1, 4):
|
||||
assert getattr(a, "f" + str(i))() == 2 * i
|
||||
|
||||
# Inherits from 8: requires 1/2 pointers worth of holder flags on 32/64-bit arch:
|
||||
b = MIMany916()
|
||||
for i in range(9, 16):
|
||||
assert getattr(b, "f" + str(i))() == 2 * i
|
||||
|
||||
# Inherits from 9: requires >= 2 pointers worth of holder flags
|
||||
c = MIMany19()
|
||||
for i in range(1, 9):
|
||||
assert getattr(c, "f" + str(i))() == 2 * i
|
||||
|
||||
# Inherits from 17: requires >= 3 pointers worth of holder flags
|
||||
d = MIMany117()
|
||||
for i in range(1, 17):
|
||||
assert getattr(d, "f" + str(i))() == 2 * i
|
||||
|
||||
|
||||
def test_multiple_inheritance_virtbase():
|
||||
class MITypePy(m.Base12a):
|
||||
def __init__(self, i, j):
|
||||
m.Base12a.__init__(self, i, j)
|
||||
|
||||
mt = MITypePy(3, 4)
|
||||
assert mt.bar() == 4
|
||||
assert m.bar_base2a(mt) == 4
|
||||
assert m.bar_base2a_sharedptr(mt) == 4
|
||||
|
||||
|
||||
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"""
|
||||
|
||||
for d in (m.VanillaStaticMix1(), m.VanillaStaticMix2()):
|
||||
assert d.vanilla() == "Vanilla"
|
||||
assert d.static_func1() == "WithStatic1"
|
||||
assert d.static_func2() == "WithStatic2"
|
||||
assert d.static_func() == d.__class__.__name__
|
||||
|
||||
m.WithStatic1.static_value1 = 1
|
||||
m.WithStatic2.static_value2 = 2
|
||||
assert d.static_value1 == 1
|
||||
assert d.static_value2 == 2
|
||||
assert d.static_value == 12
|
||||
|
||||
d.static_value1 = 0
|
||||
assert d.static_value1 == 0
|
||||
d.static_value2 = 0
|
||||
assert d.static_value2 == 0
|
||||
d.static_value = 0
|
||||
assert d.static_value == 0
|
||||
|
||||
|
||||
# Requires PyPy 6+
|
||||
def test_mi_dynamic_attributes():
|
||||
"""Mixing bases with and without dynamic attribute support"""
|
||||
|
||||
for d in (m.VanillaDictMix1(), m.VanillaDictMix2()):
|
||||
d.dynamic = 1
|
||||
assert d.dynamic == 1
|
||||
|
||||
|
||||
def test_mi_unaligned_base():
|
||||
"""Returning an offset (non-first MI) base class pointer should recognize the instance"""
|
||||
|
||||
n_inst = ConstructorStats.detail_reg_inst()
|
||||
|
||||
c = m.I801C()
|
||||
d = m.I801D()
|
||||
# + 4 below because we have the two instances, and each instance has offset base I801B2
|
||||
assert ConstructorStats.detail_reg_inst() == n_inst + 4
|
||||
b1c = m.i801b1_c(c)
|
||||
assert b1c is c
|
||||
b2c = m.i801b2_c(c)
|
||||
assert b2c is c
|
||||
b1d = m.i801b1_d(d)
|
||||
assert b1d is d
|
||||
b2d = m.i801b2_d(d)
|
||||
assert b2d is d
|
||||
|
||||
assert ConstructorStats.detail_reg_inst(
|
||||
) == n_inst + 4 # no extra instances
|
||||
del c, b1c, b2c
|
||||
assert ConstructorStats.detail_reg_inst() == n_inst + 2
|
||||
del d, b1d, b2d
|
||||
assert ConstructorStats.detail_reg_inst() == n_inst
|
||||
|
||||
|
||||
def test_mi_base_return():
|
||||
"""Tests returning an offset (non-first MI) base class pointer to a derived instance"""
|
||||
|
||||
n_inst = ConstructorStats.detail_reg_inst()
|
||||
|
||||
c1 = m.i801c_b1()
|
||||
assert type(c1) is m.I801C
|
||||
assert c1.a == 1
|
||||
assert c1.b == 2
|
||||
|
||||
d1 = m.i801d_b1()
|
||||
assert type(d1) is m.I801D
|
||||
assert d1.a == 1
|
||||
assert d1.b == 2
|
||||
|
||||
assert ConstructorStats.detail_reg_inst() == n_inst + 4
|
||||
|
||||
c2 = m.i801c_b2()
|
||||
assert type(c2) is m.I801C
|
||||
assert c2.a == 1
|
||||
assert c2.b == 2
|
||||
|
||||
d2 = m.i801d_b2()
|
||||
assert type(d2) is m.I801D
|
||||
assert d2.a == 1
|
||||
assert d2.b == 2
|
||||
|
||||
assert ConstructorStats.detail_reg_inst() == n_inst + 8
|
||||
|
||||
del c2
|
||||
assert ConstructorStats.detail_reg_inst() == n_inst + 6
|
||||
del c1, d1, d2
|
||||
assert ConstructorStats.detail_reg_inst() == n_inst
|
||||
|
||||
# Returning an unregistered derived type with a registered base; we won't
|
||||
# pick up the derived type, obviously, but should still work (as an object
|
||||
# of whatever type was returned).
|
||||
e1 = m.i801e_c()
|
||||
assert type(e1) is m.I801C
|
||||
assert e1.a == 1
|
||||
assert e1.b == 2
|
||||
|
||||
e2 = m.i801e_b2()
|
||||
assert type(e2) is m.I801B2
|
||||
assert e2.b == 2
|
||||
|
||||
|
||||
def test_diamond_inheritance():
|
||||
"""Tests that diamond inheritance works as expected (issue #959)"""
|
||||
|
||||
# Issue #959: this shouldn't segfault:
|
||||
d = m.D()
|
||||
|
||||
# Make sure all the various distinct pointers are all recognized as registered instances:
|
||||
assert d is d.c0()
|
||||
assert d is d.c1()
|
||||
assert d is d.b()
|
||||
assert d is d.c0().b()
|
||||
assert d is d.c1().b()
|
||||
assert d is d.c0().c1().b().c0().b()
|
||||
|
||||
|
||||
def test_pr3635_diamond_b():
|
||||
o = m.MVB()
|
||||
assert o.b == 1
|
||||
|
||||
assert o.get_b_b() == 1
|
||||
|
||||
|
||||
def test_pr3635_diamond_c():
|
||||
o = m.MVC()
|
||||
assert o.b == 1
|
||||
assert o.c == 2
|
||||
|
||||
assert o.get_b_b() == 1
|
||||
assert o.get_c_b() == 1
|
||||
|
||||
assert o.get_c_c() == 2
|
||||
|
||||
|
||||
def test_pr3635_diamond_d0():
|
||||
o = m.MVD0()
|
||||
assert o.b == 1
|
||||
assert o.c == 2
|
||||
assert o.d0 == 3
|
||||
|
||||
assert o.get_b_b() == 1
|
||||
assert o.get_c_b() == 1
|
||||
assert o.get_d0_b() == 1
|
||||
|
||||
assert o.get_c_c() == 2
|
||||
assert o.get_d0_c() == 2
|
||||
|
||||
assert o.get_d0_d0() == 3
|
||||
|
||||
|
||||
def test_pr3635_diamond_d1():
|
||||
o = m.MVD1()
|
||||
assert o.b == 1
|
||||
assert o.c == 2
|
||||
assert o.d1 == 4
|
||||
|
||||
assert o.get_b_b() == 1
|
||||
assert o.get_c_b() == 1
|
||||
assert o.get_d1_b() == 1
|
||||
|
||||
assert o.get_c_c() == 2
|
||||
assert o.get_d1_c() == 2
|
||||
|
||||
assert o.get_d1_d1() == 4
|
||||
|
||||
|
||||
def test_pr3635_diamond_e():
|
||||
o = m.MVE()
|
||||
assert o.b == 1
|
||||
assert o.c == 2
|
||||
assert o.d0 == 3
|
||||
assert o.d1 == 4
|
||||
assert o.e == 5
|
||||
|
||||
assert o.get_b_b() == 1
|
||||
assert o.get_c_b() == 1
|
||||
assert o.get_d0_b() == 1
|
||||
assert o.get_d1_b() == 1
|
||||
assert o.get_e_b() == 1
|
||||
|
||||
assert o.get_c_c() == 2
|
||||
assert o.get_d0_c() == 2
|
||||
assert o.get_d1_c() == 2
|
||||
assert o.get_e_c() == 2
|
||||
|
||||
assert o.get_d0_d0() == 3
|
||||
assert o.get_e_d0() == 3
|
||||
|
||||
assert o.get_d1_d1() == 4
|
||||
assert o.get_e_d1() == 4
|
||||
|
||||
assert o.get_e_e() == 5
|
||||
|
||||
|
||||
def test_pr3635_diamond_f():
|
||||
o = m.MVF()
|
||||
assert o.b == 1
|
||||
assert o.c == 2
|
||||
assert o.d0 == 3
|
||||
assert o.d1 == 4
|
||||
assert o.e == 5
|
||||
assert o.f == 6
|
||||
|
||||
assert o.get_b_b() == 1
|
||||
assert o.get_c_b() == 1
|
||||
assert o.get_d0_b() == 1
|
||||
assert o.get_d1_b() == 1
|
||||
assert o.get_e_b() == 1
|
||||
assert o.get_f_b() == 1
|
||||
|
||||
assert o.get_c_c() == 2
|
||||
assert o.get_d0_c() == 2
|
||||
assert o.get_d1_c() == 2
|
||||
assert o.get_e_c() == 2
|
||||
assert o.get_f_c() == 2
|
||||
|
||||
assert o.get_d0_d0() == 3
|
||||
assert o.get_e_d0() == 3
|
||||
assert o.get_f_d0() == 3
|
||||
|
||||
assert o.get_d1_d1() == 4
|
||||
assert o.get_e_d1() == 4
|
||||
assert o.get_f_d1() == 4
|
||||
|
||||
assert o.get_e_e() == 5
|
||||
assert o.get_f_e() == 5
|
||||
|
||||
assert o.get_f_f() == 6
|
||||
|
||||
|
||||
def test_python_inherit_from_mi():
|
||||
"""Tests extending a Python class from a single inheritor of a MI class"""
|
||||
|
||||
class PyMVF(m.MVF):
|
||||
g = 7
|
||||
|
||||
def get_g_g(self):
|
||||
return self.g
|
||||
|
||||
o = PyMVF()
|
||||
|
||||
assert o.b == 1
|
||||
assert o.c == 2
|
||||
assert o.d0 == 3
|
||||
assert o.d1 == 4
|
||||
assert o.e == 5
|
||||
assert o.f == 6
|
||||
assert o.g == 7
|
||||
|
||||
assert o.get_g_g() == 7
|
||||
524
third_party/pybind11/tests/test_numpy_array.cpp
vendored
Normal file
524
third_party/pybind11/tests/test_numpy_array.cpp
vendored
Normal file
@@ -0,0 +1,524 @@
|
||||
/*
|
||||
tests/test_numpy_array.cpp -- test core array functionality
|
||||
|
||||
Copyright (c) 2016 Ivan Smirnov <i.s.smirnov@gmail.com>
|
||||
|
||||
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/numpy.h>
|
||||
#include <pybind11/stl.h>
|
||||
|
||||
#include "pybind11_tests.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <utility>
|
||||
|
||||
// Size / dtype checks.
|
||||
struct DtypeCheck {
|
||||
py::dtype numpy{};
|
||||
py::dtype pybind11{};
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
DtypeCheck get_dtype_check(const char *name) {
|
||||
py::module_ np = py::module_::import("numpy");
|
||||
DtypeCheck check{};
|
||||
check.numpy = np.attr("dtype")(np.attr(name));
|
||||
check.pybind11 = py::dtype::of<T>();
|
||||
return check;
|
||||
}
|
||||
|
||||
std::vector<DtypeCheck> get_concrete_dtype_checks() {
|
||||
return {// Normalization
|
||||
get_dtype_check<std::int8_t>("int8"),
|
||||
get_dtype_check<std::uint8_t>("uint8"),
|
||||
get_dtype_check<std::int16_t>("int16"),
|
||||
get_dtype_check<std::uint16_t>("uint16"),
|
||||
get_dtype_check<std::int32_t>("int32"),
|
||||
get_dtype_check<std::uint32_t>("uint32"),
|
||||
get_dtype_check<std::int64_t>("int64"),
|
||||
get_dtype_check<std::uint64_t>("uint64")};
|
||||
}
|
||||
|
||||
struct DtypeSizeCheck {
|
||||
std::string name{};
|
||||
int size_cpp{};
|
||||
int size_numpy{};
|
||||
// For debugging.
|
||||
py::dtype dtype{};
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
DtypeSizeCheck get_dtype_size_check() {
|
||||
DtypeSizeCheck check{};
|
||||
check.name = py::type_id<T>();
|
||||
check.size_cpp = sizeof(T);
|
||||
check.dtype = py::dtype::of<T>();
|
||||
check.size_numpy = check.dtype.attr("itemsize").template cast<int>();
|
||||
return check;
|
||||
}
|
||||
|
||||
std::vector<DtypeSizeCheck> get_platform_dtype_size_checks() {
|
||||
return {
|
||||
get_dtype_size_check<short>(),
|
||||
get_dtype_size_check<unsigned short>(),
|
||||
get_dtype_size_check<int>(),
|
||||
get_dtype_size_check<unsigned int>(),
|
||||
get_dtype_size_check<long>(),
|
||||
get_dtype_size_check<unsigned long>(),
|
||||
get_dtype_size_check<long long>(),
|
||||
get_dtype_size_check<unsigned long long>(),
|
||||
};
|
||||
}
|
||||
|
||||
// Arrays.
|
||||
using arr = py::array;
|
||||
using arr_t = py::array_t<uint16_t, 0>;
|
||||
static_assert(std::is_same<arr_t::value_type, uint16_t>::value, "");
|
||||
|
||||
template <typename... Ix>
|
||||
arr data(const arr &a, Ix... index) {
|
||||
return arr(a.nbytes() - a.offset_at(index...), (const uint8_t *) a.data(index...));
|
||||
}
|
||||
|
||||
template <typename... Ix>
|
||||
arr data_t(const arr_t &a, Ix... index) {
|
||||
return arr(a.size() - a.index_at(index...), a.data(index...));
|
||||
}
|
||||
|
||||
template <typename... Ix>
|
||||
arr &mutate_data(arr &a, Ix... index) {
|
||||
auto *ptr = (uint8_t *) a.mutable_data(index...);
|
||||
for (py::ssize_t i = 0; i < a.nbytes() - a.offset_at(index...); i++) {
|
||||
ptr[i] = (uint8_t) (ptr[i] * 2);
|
||||
}
|
||||
return a;
|
||||
}
|
||||
|
||||
template <typename... Ix>
|
||||
arr_t &mutate_data_t(arr_t &a, Ix... index) {
|
||||
auto ptr = a.mutable_data(index...);
|
||||
for (py::ssize_t i = 0; i < a.size() - a.index_at(index...); i++) {
|
||||
ptr[i]++;
|
||||
}
|
||||
return a;
|
||||
}
|
||||
|
||||
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) \
|
||||
sm.def(#name, [](type a) { return name(a); }); \
|
||||
sm.def(#name, [](type a, int i) { return name(a, i); }); \
|
||||
sm.def(#name, [](type a, int i, int j) { return name(a, i, j); }); \
|
||||
sm.def(#name, [](type a, int i, int j, int k) { return name(a, i, j, k); });
|
||||
|
||||
template <typename T, typename T2>
|
||||
py::handle auxiliaries(T &&r, T2 &&r2) {
|
||||
if (r.ndim() != 2) {
|
||||
throw std::domain_error("error: ndim != 2");
|
||||
}
|
||||
py::list l;
|
||||
l.append(*r.data(0, 0));
|
||||
l.append(*r2.mutable_data(0, 0));
|
||||
l.append(r.data(0, 1) == r2.mutable_data(0, 1));
|
||||
l.append(r.ndim());
|
||||
l.append(r.itemsize());
|
||||
l.append(r.shape(0));
|
||||
l.append(r.shape(1));
|
||||
l.append(r.size());
|
||||
l.append(r.nbytes());
|
||||
return l.release();
|
||||
}
|
||||
|
||||
// note: declaration at local scope would create a dangling reference!
|
||||
static int data_i = 42;
|
||||
|
||||
TEST_SUBMODULE(numpy_array, sm) {
|
||||
try {
|
||||
py::module_::import("numpy");
|
||||
} catch (...) {
|
||||
return;
|
||||
}
|
||||
|
||||
// test_dtypes
|
||||
py::class_<DtypeCheck>(sm, "DtypeCheck")
|
||||
.def_readonly("numpy", &DtypeCheck::numpy)
|
||||
.def_readonly("pybind11", &DtypeCheck::pybind11)
|
||||
.def("__repr__", [](const DtypeCheck &self) {
|
||||
return py::str("<DtypeCheck numpy={} pybind11={}>").format(self.numpy, self.pybind11);
|
||||
});
|
||||
sm.def("get_concrete_dtype_checks", &get_concrete_dtype_checks);
|
||||
|
||||
py::class_<DtypeSizeCheck>(sm, "DtypeSizeCheck")
|
||||
.def_readonly("name", &DtypeSizeCheck::name)
|
||||
.def_readonly("size_cpp", &DtypeSizeCheck::size_cpp)
|
||||
.def_readonly("size_numpy", &DtypeSizeCheck::size_numpy)
|
||||
.def("__repr__", [](const DtypeSizeCheck &self) {
|
||||
return py::str("<DtypeSizeCheck name='{}' size_cpp={} size_numpy={} dtype={}>")
|
||||
.format(self.name, self.size_cpp, self.size_numpy, self.dtype);
|
||||
});
|
||||
sm.def("get_platform_dtype_size_checks", &get_platform_dtype_size_checks);
|
||||
|
||||
// test_array_attributes
|
||||
sm.def("ndim", [](const arr &a) { return a.ndim(); });
|
||||
sm.def("shape", [](const arr &a) { return arr(a.ndim(), a.shape()); });
|
||||
sm.def("shape", [](const arr &a, py::ssize_t dim) { return a.shape(dim); });
|
||||
sm.def("strides", [](const arr &a) { return arr(a.ndim(), a.strides()); });
|
||||
sm.def("strides", [](const arr &a, py::ssize_t dim) { return a.strides(dim); });
|
||||
sm.def("writeable", [](const arr &a) { return a.writeable(); });
|
||||
sm.def("size", [](const arr &a) { return a.size(); });
|
||||
sm.def("itemsize", [](const arr &a) { return a.itemsize(); });
|
||||
sm.def("nbytes", [](const arr &a) { return a.nbytes(); });
|
||||
sm.def("owndata", [](const arr &a) { return a.owndata(); });
|
||||
|
||||
// test_index_offset
|
||||
def_index_fn(index_at, const arr &);
|
||||
def_index_fn(index_at_t, const arr_t &);
|
||||
def_index_fn(offset_at, const arr &);
|
||||
def_index_fn(offset_at_t, const arr_t &);
|
||||
// test_data
|
||||
def_index_fn(data, const arr &);
|
||||
def_index_fn(data_t, const arr_t &);
|
||||
// test_mutate_data, test_mutate_readonly
|
||||
def_index_fn(mutate_data, arr &);
|
||||
def_index_fn(mutate_data_t, arr_t &);
|
||||
def_index_fn(at_t, const arr_t &);
|
||||
def_index_fn(mutate_at_t, arr_t &);
|
||||
|
||||
// test_make_c_f_array
|
||||
sm.def("make_f_array", [] { return py::array_t<float>({2, 2}, {4, 8}); });
|
||||
sm.def("make_c_array", [] { return py::array_t<float>({2, 2}, {8, 4}); });
|
||||
|
||||
// test_empty_shaped_array
|
||||
sm.def("make_empty_shaped_array", [] { return py::array(py::dtype("f"), {}, {}); });
|
||||
// test numpy scalars (empty shape, ndim==0)
|
||||
sm.def("scalar_int", []() { return py::array(py::dtype("i"), {}, {}, &data_i); });
|
||||
|
||||
// test_wrap
|
||||
sm.def("wrap", [](const py::array &a) {
|
||||
return py::array(a.dtype(),
|
||||
{a.shape(), a.shape() + a.ndim()},
|
||||
{a.strides(), a.strides() + a.ndim()},
|
||||
a.data(),
|
||||
a);
|
||||
});
|
||||
|
||||
// test_numpy_view
|
||||
struct ArrayClass {
|
||||
int data[2] = {1, 2};
|
||||
ArrayClass() { py::print("ArrayClass()"); }
|
||||
~ArrayClass() { py::print("~ArrayClass()"); }
|
||||
};
|
||||
py::class_<ArrayClass>(sm, "ArrayClass")
|
||||
.def(py::init<>())
|
||||
.def("numpy_view", [](py::object &obj) {
|
||||
py::print("ArrayClass::numpy_view()");
|
||||
auto &a = obj.cast<ArrayClass &>();
|
||||
return py::array_t<int>({2}, {4}, a.data, obj);
|
||||
});
|
||||
|
||||
// test_cast_numpy_int64_to_uint64
|
||||
sm.def("function_taking_uint64", [](uint64_t) {});
|
||||
|
||||
// test_isinstance
|
||||
sm.def("isinstance_untyped", [](py::object yes, py::object no) {
|
||||
return py::isinstance<py::array>(std::move(yes))
|
||||
&& !py::isinstance<py::array>(std::move(no));
|
||||
});
|
||||
sm.def("isinstance_typed", [](const py::object &o) {
|
||||
return py::isinstance<py::array_t<double>>(o) && !py::isinstance<py::array_t<int>>(o);
|
||||
});
|
||||
|
||||
// test_constructors
|
||||
sm.def("default_constructors", []() {
|
||||
return py::dict("array"_a = py::array(),
|
||||
"array_t<int32>"_a = py::array_t<std::int32_t>(),
|
||||
"array_t<double>"_a = py::array_t<double>());
|
||||
});
|
||||
sm.def("converting_constructors", [](const py::object &o) {
|
||||
return py::dict("array"_a = py::array(o),
|
||||
"array_t<int32>"_a = py::array_t<std::int32_t>(o),
|
||||
"array_t<double>"_a = py::array_t<double>(o));
|
||||
});
|
||||
|
||||
// test_overload_resolution
|
||||
sm.def("overloaded", [](const py::array_t<double> &) { return "double"; });
|
||||
sm.def("overloaded", [](const py::array_t<float> &) { return "float"; });
|
||||
sm.def("overloaded", [](const py::array_t<int> &) { return "int"; });
|
||||
sm.def("overloaded", [](const py::array_t<unsigned short> &) { return "unsigned short"; });
|
||||
sm.def("overloaded", [](const py::array_t<long long> &) { return "long long"; });
|
||||
sm.def("overloaded",
|
||||
[](const py::array_t<std::complex<double>> &) { return "double complex"; });
|
||||
sm.def("overloaded", [](const py::array_t<std::complex<float>> &) { return "float complex"; });
|
||||
|
||||
sm.def("overloaded2",
|
||||
[](const py::array_t<std::complex<double>> &) { return "double complex"; });
|
||||
sm.def("overloaded2", [](const py::array_t<double> &) { return "double"; });
|
||||
sm.def("overloaded2",
|
||||
[](const py::array_t<std::complex<float>> &) { return "float complex"; });
|
||||
sm.def("overloaded2", [](const py::array_t<float> &) { return "float"; });
|
||||
|
||||
// [workaround(intel)] ICC 20/21 breaks with py::arg().stuff, using py::arg{}.stuff works.
|
||||
|
||||
// Only accept the exact types:
|
||||
sm.def(
|
||||
"overloaded3", [](const py::array_t<int> &) { return "int"; }, py::arg{}.noconvert());
|
||||
sm.def(
|
||||
"overloaded3",
|
||||
[](const py::array_t<double> &) { return "double"; },
|
||||
py::arg{}.noconvert());
|
||||
|
||||
// Make sure we don't do unsafe coercion (e.g. float to int) when not using forcecast, but
|
||||
// rather that float gets converted via the safe (conversion to double) overload:
|
||||
sm.def("overloaded4", [](const py::array_t<long long, 0> &) { return "long long"; });
|
||||
sm.def("overloaded4", [](const py::array_t<double, 0> &) { return "double"; });
|
||||
|
||||
// But we do allow conversion to int if forcecast is enabled (but only if no overload matches
|
||||
// without conversion)
|
||||
sm.def("overloaded5", [](const py::array_t<unsigned int> &) { return "unsigned int"; });
|
||||
sm.def("overloaded5", [](const py::array_t<double> &) { return "double"; });
|
||||
|
||||
// test_greedy_string_overload
|
||||
// Issue 685: ndarray shouldn't go to std::string overload
|
||||
sm.def("issue685", [](const std::string &) { return "string"; });
|
||||
sm.def("issue685", [](const py::array &) { return "array"; });
|
||||
sm.def("issue685", [](const py::object &) { return "other"; });
|
||||
|
||||
// test_array_unchecked_fixed_dims
|
||||
sm.def(
|
||||
"proxy_add2",
|
||||
[](py::array_t<double> a, double v) {
|
||||
auto r = a.mutable_unchecked<2>();
|
||||
for (py::ssize_t i = 0; i < r.shape(0); i++) {
|
||||
for (py::ssize_t j = 0; j < r.shape(1); j++) {
|
||||
r(i, j) += v;
|
||||
}
|
||||
}
|
||||
},
|
||||
py::arg{}.noconvert(),
|
||||
py::arg());
|
||||
|
||||
sm.def("proxy_init3", [](double start) {
|
||||
py::array_t<double, py::array::c_style> a({3, 3, 3});
|
||||
auto r = a.mutable_unchecked<3>();
|
||||
for (py::ssize_t i = 0; i < r.shape(0); i++) {
|
||||
for (py::ssize_t j = 0; j < r.shape(1); j++) {
|
||||
for (py::ssize_t k = 0; k < r.shape(2); k++) {
|
||||
r(i, j, k) = start++;
|
||||
}
|
||||
}
|
||||
}
|
||||
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 (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", [](const py::array_t<double> &a) {
|
||||
auto r = a.unchecked<1>();
|
||||
double sumsq = 0;
|
||||
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;
|
||||
});
|
||||
|
||||
sm.def("proxy_auxiliaries2", [](py::array_t<double> a) {
|
||||
auto r = a.unchecked<2>();
|
||||
auto r2 = a.mutable_unchecked<2>();
|
||||
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 (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 (py::ssize_t i = 0; i < r.shape(0); i++) {
|
||||
for (py::ssize_t j = 0; j < r.shape(1); j++) {
|
||||
for (py::ssize_t k = 0; k < r.shape(2); k++) {
|
||||
r(i, j, k) = start++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return a;
|
||||
});
|
||||
sm.def("proxy_auxiliaries2_dyn", [](py::array_t<double> a) {
|
||||
return auxiliaries(a.unchecked(), a.mutable_unchecked());
|
||||
});
|
||||
|
||||
sm.def("array_auxiliaries2", [](py::array_t<double> a) { return auxiliaries(a, a); });
|
||||
|
||||
// test_array_failures
|
||||
// Issue #785: Uninformative "Unknown internal error" exception when constructing array from
|
||||
// empty object:
|
||||
sm.def("array_fail_test", []() { return py::array(py::object()); });
|
||||
sm.def("array_t_fail_test", []() { return py::array_t<double>(py::object()); });
|
||||
// Make sure the error from numpy is being passed through:
|
||||
sm.def("array_fail_test_negative_size", []() {
|
||||
int c = 0;
|
||||
return py::array(-1, &c);
|
||||
});
|
||||
|
||||
// test_initializer_list
|
||||
// Issue (unnumbered; reported in #788): regression: initializer lists can be ambiguous
|
||||
sm.def("array_initializer_list1", []() { return py::array_t<float>(1); });
|
||||
// { 1 } also works for the above, but clang warns about it
|
||||
sm.def("array_initializer_list2", []() { return py::array_t<float>({1, 2}); });
|
||||
sm.def("array_initializer_list3", []() { return py::array_t<float>({1, 2, 3}); });
|
||||
sm.def("array_initializer_list4", []() { return py::array_t<float>({1, 2, 3, 4}); });
|
||||
|
||||
// test_array_resize
|
||||
// reshape array to 2D without changing size
|
||||
sm.def("array_reshape2", [](py::array_t<double> a) {
|
||||
const auto dim_sz = (py::ssize_t) std::sqrt(a.size());
|
||||
if (dim_sz * dim_sz != a.size()) {
|
||||
throw std::domain_error(
|
||||
"array_reshape2: input array total size is not a squared integer");
|
||||
}
|
||||
a.resize({dim_sz, dim_sz});
|
||||
});
|
||||
|
||||
// resize to 3D array with each dimension = N
|
||||
sm.def("array_resize3", [](py::array_t<double> a, size_t N, bool refcheck) {
|
||||
a.resize({N, N, N}, refcheck);
|
||||
});
|
||||
|
||||
// test_array_create_and_resize
|
||||
// return 2D array with Nrows = Ncols = N
|
||||
sm.def("create_and_resize", [](size_t N) {
|
||||
py::array_t<double> a;
|
||||
a.resize({N, N});
|
||||
std::fill(a.mutable_data(), a.mutable_data() + a.size(), 42.);
|
||||
return a;
|
||||
});
|
||||
|
||||
sm.def("array_view",
|
||||
[](py::array_t<uint8_t> a, const std::string &dtype) { return a.view(dtype); });
|
||||
|
||||
sm.def("reshape_initializer_list", [](py::array_t<int> a, size_t N, size_t M, size_t O) {
|
||||
return a.reshape({N, M, O});
|
||||
});
|
||||
sm.def("reshape_tuple", [](py::array_t<int> a, const std::vector<int> &new_shape) {
|
||||
return a.reshape(new_shape);
|
||||
});
|
||||
|
||||
sm.def("index_using_ellipsis",
|
||||
[](const py::array &a) { return a[py::make_tuple(0, py::ellipsis(), 0)]; });
|
||||
|
||||
// test_argument_conversions
|
||||
sm.def(
|
||||
"accept_double", [](const py::array_t<double, 0> &) {}, py::arg("a"));
|
||||
sm.def(
|
||||
"accept_double_forcecast",
|
||||
[](const py::array_t<double, py::array::forcecast> &) {},
|
||||
py::arg("a"));
|
||||
sm.def(
|
||||
"accept_double_c_style",
|
||||
[](const py::array_t<double, py::array::c_style> &) {},
|
||||
py::arg("a"));
|
||||
sm.def(
|
||||
"accept_double_c_style_forcecast",
|
||||
[](const py::array_t<double, py::array::forcecast | py::array::c_style> &) {},
|
||||
py::arg("a"));
|
||||
sm.def(
|
||||
"accept_double_f_style",
|
||||
[](const py::array_t<double, py::array::f_style> &) {},
|
||||
py::arg("a"));
|
||||
sm.def(
|
||||
"accept_double_f_style_forcecast",
|
||||
[](const py::array_t<double, py::array::forcecast | py::array::f_style> &) {},
|
||||
py::arg("a"));
|
||||
sm.def(
|
||||
"accept_double_noconvert", [](const py::array_t<double, 0> &) {}, "a"_a.noconvert());
|
||||
sm.def(
|
||||
"accept_double_forcecast_noconvert",
|
||||
[](const py::array_t<double, py::array::forcecast> &) {},
|
||||
"a"_a.noconvert());
|
||||
sm.def(
|
||||
"accept_double_c_style_noconvert",
|
||||
[](const py::array_t<double, py::array::c_style> &) {},
|
||||
"a"_a.noconvert());
|
||||
sm.def(
|
||||
"accept_double_c_style_forcecast_noconvert",
|
||||
[](const py::array_t<double, py::array::forcecast | py::array::c_style> &) {},
|
||||
"a"_a.noconvert());
|
||||
sm.def(
|
||||
"accept_double_f_style_noconvert",
|
||||
[](const py::array_t<double, py::array::f_style> &) {},
|
||||
"a"_a.noconvert());
|
||||
sm.def(
|
||||
"accept_double_f_style_forcecast_noconvert",
|
||||
[](const py::array_t<double, py::array::forcecast | py::array::f_style> &) {},
|
||||
"a"_a.noconvert());
|
||||
|
||||
// Check that types returns correct npy format descriptor
|
||||
sm.def("test_fmt_desc_float", [](const py::array_t<float> &) {});
|
||||
sm.def("test_fmt_desc_double", [](const py::array_t<double> &) {});
|
||||
sm.def("test_fmt_desc_const_float", [](const py::array_t<const float> &) {});
|
||||
sm.def("test_fmt_desc_const_double", [](const py::array_t<const double> &) {});
|
||||
}
|
||||
589
third_party/pybind11/tests/test_numpy_array.py
vendored
Normal file
589
third_party/pybind11/tests/test_numpy_array.py
vendored
Normal file
@@ -0,0 +1,589 @@
|
||||
import pytest
|
||||
|
||||
import env # noqa: F401
|
||||
from pybind11_tests import numpy_array as m
|
||||
|
||||
np = pytest.importorskip("numpy")
|
||||
|
||||
|
||||
def test_dtypes():
|
||||
# See issue #1328.
|
||||
# - Platform-dependent sizes.
|
||||
for size_check in m.get_platform_dtype_size_checks():
|
||||
print(size_check)
|
||||
assert size_check.size_cpp == size_check.size_numpy, size_check
|
||||
# - Concrete sizes.
|
||||
for check in m.get_concrete_dtype_checks():
|
||||
print(check)
|
||||
assert check.numpy == check.pybind11, check
|
||||
if check.numpy.num != check.pybind11.num:
|
||||
print(
|
||||
f"NOTE: typenum mismatch for {check}: {check.numpy.num} != {check.pybind11.num}"
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture(scope="function")
|
||||
def arr():
|
||||
return np.array([[1, 2, 3], [4, 5, 6]], "=u2")
|
||||
|
||||
|
||||
def test_array_attributes():
|
||||
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)"
|
||||
with pytest.raises(IndexError) as excinfo:
|
||||
m.strides(a, 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.flags.writeable = False
|
||||
assert m.ndim(a) == 2
|
||||
assert all(m.shape(a) == [2, 3])
|
||||
assert m.shape(a, 0) == 2
|
||||
assert m.shape(a, 1) == 3
|
||||
assert all(m.strides(a) == [6, 2])
|
||||
assert m.strides(a, 0) == 6
|
||||
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)"
|
||||
with pytest.raises(IndexError) as excinfo:
|
||||
m.strides(a, 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
|
||||
assert m.nbytes(a) == 12
|
||||
assert not m.owndata(a)
|
||||
|
||||
|
||||
@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
|
||||
assert m.offset_at(arr, *args) == ret * arr.dtype.itemsize
|
||||
assert m.offset_at_t(arr, *args) == ret * arr.dtype.itemsize
|
||||
|
||||
|
||||
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, ):
|
||||
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]),
|
||||
], )
|
||||
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)
|
||||
|
||||
|
||||
@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) == f"index dimension mismatch: {dim} (ndim = 2)"
|
||||
|
||||
|
||||
def test_at(arr):
|
||||
assert m.at_t(arr, 0, 2) == 3
|
||||
assert m.at_t(arr, 1, 0) == 4
|
||||
|
||||
assert all(m.mutate_at_t(arr, 0, 2).ravel() == [1, 2, 4, 4, 5, 6])
|
||||
assert all(m.mutate_at_t(arr, 1, 0).ravel() == [1, 2, 4, 5, 5, 6])
|
||||
|
||||
|
||||
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)), ):
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
func(arr, *args)
|
||||
assert str(excinfo.value) == "array is not writeable"
|
||||
|
||||
|
||||
def test_mutate_data(arr):
|
||||
assert all(m.mutate_data(arr).ravel() == [2, 4, 6, 8, 10, 12])
|
||||
assert all(m.mutate_data(arr).ravel() == [4, 8, 12, 16, 20, 24])
|
||||
assert all(m.mutate_data(arr, 1).ravel() == [4, 8, 12, 32, 40, 48])
|
||||
assert all(m.mutate_data(arr, 0, 1).ravel() == [4, 16, 24, 64, 80, 96])
|
||||
assert all(m.mutate_data(arr, 1, 2).ravel() == [4, 16, 24, 64, 80, 192])
|
||||
|
||||
assert all(m.mutate_data_t(arr).ravel() == [5, 17, 25, 65, 81, 193])
|
||||
assert all(m.mutate_data_t(arr).ravel() == [6, 18, 26, 66, 82, 194])
|
||||
assert all(m.mutate_data_t(arr, 1).ravel() == [6, 18, 26, 67, 83, 195])
|
||||
assert all(m.mutate_data_t(arr, 0, 1).ravel() == [6, 19, 27, 68, 84, 196])
|
||||
assert all(m.mutate_data_t(arr, 1, 2).ravel() == [6, 19, 27, 68, 84, 197])
|
||||
|
||||
|
||||
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, ):
|
||||
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"
|
||||
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"
|
||||
|
||||
|
||||
def test_make_c_f_array():
|
||||
assert m.make_c_array().flags.c_contiguous
|
||||
assert not m.make_c_array().flags.f_contiguous
|
||||
assert m.make_f_array().flags.f_contiguous
|
||||
assert not m.make_f_array().flags.c_contiguous
|
||||
|
||||
|
||||
def test_make_empty_shaped_array():
|
||||
m.make_empty_shaped_array()
|
||||
|
||||
# empty shape means numpy scalar, PEP 3118
|
||||
assert m.scalar_int().ndim == 0
|
||||
assert m.scalar_int().shape == ()
|
||||
assert m.scalar_int() == 42
|
||||
|
||||
|
||||
def test_wrap():
|
||||
def assert_references(a, b, base=None):
|
||||
if base is None:
|
||||
base = a
|
||||
assert a is not b
|
||||
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
|
||||
assert a.flags.f_contiguous == b.flags.f_contiguous
|
||||
assert a.flags.writeable == b.flags.writeable
|
||||
assert a.flags.aligned == b.flags.aligned
|
||||
# 1.13 supported Python 3.6
|
||||
if tuple(int(x) for x in np.__version__.split(".")[:2]) >= (1, 14):
|
||||
assert a.flags.writebackifcopy == b.flags.writebackifcopy
|
||||
else:
|
||||
assert a.flags.updateifcopy == b.flags.updateifcopy
|
||||
assert np.all(a == b)
|
||||
assert not b.flags.owndata
|
||||
assert b.base is base
|
||||
if a.flags.writeable and a.ndim == 2:
|
||||
a[0, 0] = 1234
|
||||
assert b[0, 0] == 1234
|
||||
|
||||
a1 = np.array([1, 2], dtype=np.int16)
|
||||
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="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.flags.writeable = False
|
||||
a2 = m.wrap(a1)
|
||||
assert_references(a1, a2)
|
||||
|
||||
a1 = np.random.random((4, 4, 4))
|
||||
a2 = m.wrap(a1)
|
||||
assert_references(a1, a2)
|
||||
|
||||
a1t = a1.transpose()
|
||||
a2 = m.wrap(a1t)
|
||||
assert_references(a1t, a2, a1)
|
||||
|
||||
a1d = a1.diagonal()
|
||||
a2 = m.wrap(a1d)
|
||||
assert_references(a1d, a2, a1)
|
||||
|
||||
a1m = a1[::-1, ::-1, ::-1]
|
||||
a2 = m.wrap(a1m)
|
||||
assert_references(a1m, a2, a1)
|
||||
|
||||
|
||||
def test_numpy_view(capture):
|
||||
with capture:
|
||||
ac = m.ArrayClass()
|
||||
ac_view_1 = ac.numpy_view()
|
||||
ac_view_2 = ac.numpy_view()
|
||||
assert np.all(ac_view_1 == np.array([1, 2], dtype=np.int32))
|
||||
del ac
|
||||
pytest.gc_collect()
|
||||
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
|
||||
assert ac_view_2[1] == 3
|
||||
with capture:
|
||||
del ac_view_1
|
||||
del ac_view_2
|
||||
pytest.gc_collect()
|
||||
pytest.gc_collect()
|
||||
assert (capture == """
|
||||
~ArrayClass()
|
||||
""")
|
||||
|
||||
|
||||
def test_cast_numpy_int64_to_uint64():
|
||||
m.function_taking_uint64(123)
|
||||
m.function_taking_uint64(np.uint64(123))
|
||||
|
||||
|
||||
def test_isinstance():
|
||||
assert m.isinstance_untyped(np.array([1, 2, 3]), "not an array")
|
||||
assert m.isinstance_typed(np.array([1.0, 2.0, 3.0]))
|
||||
|
||||
|
||||
def test_constructors():
|
||||
defaults = m.default_constructors()
|
||||
for a in defaults.values():
|
||||
assert a.size == 0
|
||||
assert defaults["array"].dtype == np.array([]).dtype
|
||||
assert defaults["array_t<int32>"].dtype == np.int32
|
||||
assert defaults["array_t<double>"].dtype == np.float64
|
||||
|
||||
results = m.converting_constructors([1, 2, 3])
|
||||
for a in results.values():
|
||||
np.testing.assert_array_equal(a, [1, 2, 3])
|
||||
assert results["array"].dtype == np.int_
|
||||
assert results["array_t<int32>"].dtype == np.int32
|
||||
assert results["array_t<double>"].dtype == np.float64
|
||||
|
||||
|
||||
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"
|
||||
|
||||
# No exact match, should call first convertible version:
|
||||
assert m.overloaded(np.array([1], dtype="uint8")) == "double"
|
||||
|
||||
with pytest.raises(TypeError) as excinfo:
|
||||
m.overloaded("not an array")
|
||||
assert (msg(excinfo.value) == """
|
||||
overloaded(): incompatible function arguments. The following argument types are supported:
|
||||
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.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[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"))
|
||||
with pytest.raises(TypeError) as excinfo:
|
||||
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 + 0.0j]))
|
||||
|
||||
# Exact matches:
|
||||
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.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(123) == "other"
|
||||
|
||||
|
||||
def test_array_unchecked_fixed_dims(msg):
|
||||
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.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")
|
||||
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)
|
||||
|
||||
assert m.proxy_squared_L2_norm(np.array(range(6))) == 55
|
||||
assert m.proxy_squared_L2_norm(np.array(range(6), dtype="float64")) == 55
|
||||
|
||||
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():
|
||||
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")
|
||||
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]
|
||||
assert m.proxy_auxiliaries2_dyn(z1) == m.array_auxiliaries2(z1)
|
||||
|
||||
|
||||
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"
|
||||
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
m.array_t_fail_test()
|
||||
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"
|
||||
|
||||
|
||||
def test_initializer_list():
|
||||
assert m.array_initializer_list1().shape == (1, )
|
||||
assert m.array_initializer_list2().shape == (1, 2)
|
||||
assert m.array_initializer_list3().shape == (1, 2, 3)
|
||||
assert m.array_initializer_list4().shape == (1, 2, 3, 4)
|
||||
|
||||
|
||||
def test_array_resize():
|
||||
a = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9], dtype="float64")
|
||||
m.array_reshape2(a)
|
||||
assert a.size == 9
|
||||
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
|
||||
# ... and fail with refcheck on
|
||||
try:
|
||||
m.array_resize3(a, 3, True)
|
||||
except ValueError as e:
|
||||
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")
|
||||
# ... but reshape should be fine
|
||||
m.array_reshape2(b)
|
||||
assert b.shape == (8, 8)
|
||||
|
||||
|
||||
@pytest.mark.xfail("env.PYPY")
|
||||
def test_array_create_and_resize():
|
||||
a = m.create_and_resize(2)
|
||||
assert a.size == 4
|
||||
assert np.all(a == 42.0)
|
||||
|
||||
|
||||
def test_array_view():
|
||||
a = np.ones(100 * 4).astype("uint8")
|
||||
a_float_view = m.array_view(a, "float32")
|
||||
assert a_float_view.shape == (100 * 1, ) # 1 / 4 bytes = 8 / 32
|
||||
|
||||
a_int16_view = m.array_view(a, "int16") # 1 / 2 bytes = 16 / 32
|
||||
assert a_int16_view.shape == (100 * 2, )
|
||||
|
||||
|
||||
def test_array_view_invalid():
|
||||
a = np.ones(100 * 4).astype("uint8")
|
||||
with pytest.raises(TypeError):
|
||||
m.array_view(a, "deadly_dtype")
|
||||
|
||||
|
||||
def test_reshape_initializer_list():
|
||||
a = np.arange(2 * 7 * 3) + 1
|
||||
x = m.reshape_initializer_list(a, 2, 7, 3)
|
||||
assert x.shape == (2, 7, 3)
|
||||
assert list(x[1][4]) == [34, 35, 36]
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
m.reshape_initializer_list(a, 1, 7, 3)
|
||||
assert str(
|
||||
excinfo.value) == "cannot reshape array of size 42 into shape (1,7,3)"
|
||||
|
||||
|
||||
def test_reshape_tuple():
|
||||
a = np.arange(3 * 7 * 2) + 1
|
||||
x = m.reshape_tuple(a, (3, 7, 2))
|
||||
assert x.shape == (3, 7, 2)
|
||||
assert list(x[1][4]) == [23, 24]
|
||||
y = m.reshape_tuple(x, (x.size, ))
|
||||
assert y.shape == (42, )
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
m.reshape_tuple(a, (3, 7, 1))
|
||||
assert str(
|
||||
excinfo.value) == "cannot reshape array of size 42 into shape (3,7,1)"
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
m.reshape_tuple(a, ())
|
||||
assert str(
|
||||
excinfo.value) == "cannot reshape array of size 42 into shape ()"
|
||||
|
||||
|
||||
def test_index_using_ellipsis():
|
||||
a = m.index_using_ellipsis(np.zeros((5, 6, 7)))
|
||||
assert a.shape == (6, )
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"test_func",
|
||||
[
|
||||
m.test_fmt_desc_float,
|
||||
m.test_fmt_desc_double,
|
||||
m.test_fmt_desc_const_float,
|
||||
m.test_fmt_desc_const_double,
|
||||
], )
|
||||
def test_format_descriptors_for_floating_point_types(test_func):
|
||||
assert "numpy.ndarray[numpy.float" in test_func.__doc__
|
||||
|
||||
|
||||
@pytest.mark.parametrize("forcecast", [False, True])
|
||||
@pytest.mark.parametrize("contiguity", [None, "C", "F"])
|
||||
@pytest.mark.parametrize("noconvert", [False, True])
|
||||
@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)
|
||||
m.ndim(a)
|
||||
after = getrefcount(dtype)
|
||||
assert after == before
|
||||
614
third_party/pybind11/tests/test_numpy_dtypes.cpp
vendored
Normal file
614
third_party/pybind11/tests/test_numpy_dtypes.cpp
vendored
Normal file
@@ -0,0 +1,614 @@
|
||||
/*
|
||||
tests/test_numpy_dtypes.cpp -- Structured and compound NumPy dtypes
|
||||
|
||||
Copyright (c) 2016 Ivan Smirnov
|
||||
|
||||
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/numpy.h>
|
||||
|
||||
#include "pybind11_tests.h"
|
||||
|
||||
#ifdef __GNUC__
|
||||
# define PYBIND11_PACKED(cls) cls __attribute__((__packed__))
|
||||
#else
|
||||
# define PYBIND11_PACKED(cls) __pragma(pack(push, 1)) cls __pragma(pack(pop))
|
||||
#endif
|
||||
|
||||
namespace py = pybind11;
|
||||
|
||||
struct SimpleStruct {
|
||||
bool bool_;
|
||||
uint32_t uint_;
|
||||
float float_;
|
||||
long double ldbl_;
|
||||
};
|
||||
|
||||
std::ostream &operator<<(std::ostream &os, const SimpleStruct &v) {
|
||||
return os << "s:" << v.bool_ << "," << v.uint_ << "," << v.float_ << "," << v.ldbl_;
|
||||
}
|
||||
|
||||
struct SimpleStructReordered {
|
||||
bool bool_;
|
||||
float float_;
|
||||
uint32_t uint_;
|
||||
long double ldbl_;
|
||||
};
|
||||
|
||||
PYBIND11_PACKED(struct PackedStruct {
|
||||
bool bool_;
|
||||
uint32_t uint_;
|
||||
float float_;
|
||||
long double ldbl_;
|
||||
});
|
||||
|
||||
std::ostream &operator<<(std::ostream &os, const PackedStruct &v) {
|
||||
return os << "p:" << v.bool_ << "," << v.uint_ << "," << v.float_ << "," << v.ldbl_;
|
||||
}
|
||||
|
||||
PYBIND11_PACKED(struct NestedStruct {
|
||||
SimpleStruct a;
|
||||
PackedStruct b;
|
||||
});
|
||||
|
||||
std::ostream &operator<<(std::ostream &os, const NestedStruct &v) {
|
||||
return os << "n:a=" << v.a << ";b=" << v.b;
|
||||
}
|
||||
|
||||
struct PartialStruct {
|
||||
bool bool_;
|
||||
uint32_t uint_;
|
||||
float float_;
|
||||
uint64_t dummy2;
|
||||
long double ldbl_;
|
||||
};
|
||||
|
||||
struct PartialNestedStruct {
|
||||
uint64_t dummy1;
|
||||
PartialStruct a;
|
||||
uint64_t dummy2;
|
||||
};
|
||||
|
||||
struct UnboundStruct {};
|
||||
|
||||
struct StringStruct {
|
||||
char a[3];
|
||||
std::array<char, 3> b;
|
||||
};
|
||||
|
||||
struct ComplexStruct {
|
||||
std::complex<float> cflt;
|
||||
std::complex<double> cdbl;
|
||||
};
|
||||
|
||||
std::ostream &operator<<(std::ostream &os, const ComplexStruct &v) {
|
||||
return os << "c:" << v.cflt << "," << v.cdbl;
|
||||
}
|
||||
|
||||
struct ArrayStruct {
|
||||
char a[3][4];
|
||||
int32_t b[2];
|
||||
std::array<uint8_t, 3> c;
|
||||
std::array<float, 2> d[4];
|
||||
};
|
||||
|
||||
PYBIND11_PACKED(struct StructWithUglyNames {
|
||||
int8_t __x__;
|
||||
uint64_t __y__;
|
||||
});
|
||||
|
||||
enum class E1 : int64_t { A = -1, B = 1 };
|
||||
enum E2 : uint8_t { X = 1, Y = 2 };
|
||||
|
||||
PYBIND11_PACKED(struct EnumStruct {
|
||||
E1 e1;
|
||||
E2 e2;
|
||||
});
|
||||
|
||||
std::ostream &operator<<(std::ostream &os, const StringStruct &v) {
|
||||
os << "a='";
|
||||
for (size_t i = 0; i < 3 && (v.a[i] != 0); i++) {
|
||||
os << v.a[i];
|
||||
}
|
||||
os << "',b='";
|
||||
for (size_t i = 0; i < 3 && (v.b[i] != 0); i++) {
|
||||
os << v.b[i];
|
||||
}
|
||||
return os << "'";
|
||||
}
|
||||
|
||||
std::ostream &operator<<(std::ostream &os, const ArrayStruct &v) {
|
||||
os << "a={";
|
||||
for (int i = 0; i < 3; i++) {
|
||||
if (i > 0) {
|
||||
os << ',';
|
||||
}
|
||||
os << '{';
|
||||
for (int j = 0; j < 3; j++) {
|
||||
os << v.a[i][j] << ',';
|
||||
}
|
||||
os << v.a[i][3] << '}';
|
||||
}
|
||||
os << "},b={" << v.b[0] << ',' << v.b[1];
|
||||
os << "},c={" << int(v.c[0]) << ',' << int(v.c[1]) << ',' << int(v.c[2]);
|
||||
os << "},d={";
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if (i > 0) {
|
||||
os << ',';
|
||||
}
|
||||
os << '{' << v.d[i][0] << ',' << v.d[i][1] << '}';
|
||||
}
|
||||
return os << '}';
|
||||
}
|
||||
|
||||
std::ostream &operator<<(std::ostream &os, const EnumStruct &v) {
|
||||
return os << "e1=" << (v.e1 == E1::A ? "A" : "B") << ",e2=" << (v.e2 == E2::X ? "X" : "Y");
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
py::array mkarray_via_buffer(size_t n) {
|
||||
return py::array(py::buffer_info(
|
||||
nullptr, sizeof(T), py::format_descriptor<T>::format(), 1, {n}, {sizeof(T)}));
|
||||
}
|
||||
|
||||
#define SET_TEST_VALS(s, i) \
|
||||
do { \
|
||||
(s).bool_ = (i) % 2 != 0; \
|
||||
(s).uint_ = (uint32_t) (i); \
|
||||
(s).float_ = (float) (i) *1.5f; \
|
||||
(s).ldbl_ = (long double) (i) * -2.5L; \
|
||||
} while (0)
|
||||
|
||||
template <typename S>
|
||||
py::array_t<S, 0> create_recarray(size_t n) {
|
||||
auto arr = mkarray_via_buffer<S>(n);
|
||||
auto req = arr.request();
|
||||
auto *ptr = static_cast<S *>(req.ptr);
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
SET_TEST_VALS(ptr[i], i);
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
||||
template <typename S>
|
||||
py::list print_recarray(py::array_t<S, 0> arr) {
|
||||
const auto req = arr.request();
|
||||
auto *const ptr = static_cast<S *>(req.ptr);
|
||||
auto l = py::list();
|
||||
for (py::ssize_t i = 0; i < req.size; i++) {
|
||||
std::stringstream ss;
|
||||
ss << ptr[i];
|
||||
l.append(py::str(ss.str()));
|
||||
}
|
||||
return l;
|
||||
}
|
||||
|
||||
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<py::ssize_t> shape{3, 2};
|
||||
std::vector<py::ssize_t> strides{8, 4};
|
||||
|
||||
auto *ptr = data.data();
|
||||
auto *vptr = (void *) ptr;
|
||||
auto dtype = py::dtype("int32");
|
||||
|
||||
py::buffer_info buf_ndim1(vptr, 4, "i", 6);
|
||||
py::buffer_info buf_ndim1_null(nullptr, 4, "i", 6);
|
||||
py::buffer_info buf_ndim2(vptr, 4, "i", 2, shape, strides);
|
||||
py::buffer_info buf_ndim2_null(nullptr, 4, "i", 2, shape, strides);
|
||||
|
||||
auto fill = [](py::array arr) {
|
||||
auto req = arr.request();
|
||||
for (int i = 0; i < 6; i++) {
|
||||
((int32_t *) req.ptr)[i] = i + 1;
|
||||
}
|
||||
return arr;
|
||||
};
|
||||
|
||||
switch (i) {
|
||||
// shape: (3, 2)
|
||||
case 10:
|
||||
return arr_t(shape, strides, ptr);
|
||||
case 11:
|
||||
return py::array(shape, strides, ptr);
|
||||
case 12:
|
||||
return py::array(dtype, shape, strides, vptr);
|
||||
case 13:
|
||||
return arr_t(shape, ptr);
|
||||
case 14:
|
||||
return py::array(shape, ptr);
|
||||
case 15:
|
||||
return py::array(dtype, shape, vptr);
|
||||
case 16:
|
||||
return arr_t(buf_ndim2);
|
||||
case 17:
|
||||
return py::array(buf_ndim2);
|
||||
// shape: (3, 2) - post-fill
|
||||
case 20:
|
||||
return fill(arr_t(shape, strides));
|
||||
case 21:
|
||||
return py::array(shape, strides, ptr); // can't have nullptr due to templated ctor
|
||||
case 22:
|
||||
return fill(py::array(dtype, shape, strides));
|
||||
case 23:
|
||||
return fill(arr_t(shape));
|
||||
case 24:
|
||||
return py::array(shape, ptr); // can't have nullptr due to templated ctor
|
||||
case 25:
|
||||
return fill(py::array(dtype, shape));
|
||||
case 26:
|
||||
return fill(arr_t(buf_ndim2_null));
|
||||
case 27:
|
||||
return fill(py::array(buf_ndim2_null));
|
||||
// shape: (6, )
|
||||
case 30:
|
||||
return arr_t(6, ptr);
|
||||
case 31:
|
||||
return py::array(6, ptr);
|
||||
case 32:
|
||||
return py::array(dtype, 6, vptr);
|
||||
case 33:
|
||||
return arr_t(buf_ndim1);
|
||||
case 34:
|
||||
return py::array(buf_ndim1);
|
||||
// shape: (6, )
|
||||
case 40:
|
||||
return fill(arr_t(6));
|
||||
case 41:
|
||||
return py::array(6, ptr); // can't have nullptr due to templated ctor
|
||||
case 42:
|
||||
return fill(py::array(dtype, 6));
|
||||
case 43:
|
||||
return fill(arr_t(buf_ndim1_null));
|
||||
case 44:
|
||||
return fill(py::array(buf_ndim1_null));
|
||||
}
|
||||
return arr_t();
|
||||
}
|
||||
|
||||
py::list test_dtype_ctors() {
|
||||
py::list list;
|
||||
list.append(py::dtype("int32"));
|
||||
list.append(py::dtype(std::string("float64")));
|
||||
list.append(py::dtype::from_args(py::str("bool")));
|
||||
py::list names, offsets, formats;
|
||||
py::dict dict;
|
||||
names.append(py::str("a"));
|
||||
names.append(py::str("b"));
|
||||
dict["names"] = names;
|
||||
offsets.append(py::int_(1));
|
||||
offsets.append(py::int_(10));
|
||||
dict["offsets"] = offsets;
|
||||
formats.append(py::dtype("int32"));
|
||||
formats.append(py::dtype("float64"));
|
||||
dict["formats"] = formats;
|
||||
dict["itemsize"] = py::int_(20);
|
||||
list.append(py::dtype::from_args(dict));
|
||||
list.append(py::dtype(names, formats, offsets, 20));
|
||||
list.append(py::dtype(py::buffer_info((void *) nullptr, sizeof(unsigned int), "I", 1)));
|
||||
list.append(py::dtype(py::buffer_info((void *) nullptr, 0, "T{i:a:f:b:}", 1)));
|
||||
list.append(py::dtype(py::detail::npy_api::NPY_DOUBLE_));
|
||||
return list;
|
||||
}
|
||||
|
||||
struct A {};
|
||||
struct B {};
|
||||
|
||||
TEST_SUBMODULE(numpy_dtypes, m) {
|
||||
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")
|
||||
// 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_);
|
||||
PYBIND11_NUMPY_DTYPE(PackedStruct, bool_, uint_, float_, ldbl_);
|
||||
PYBIND11_NUMPY_DTYPE(NestedStruct, a, b);
|
||||
PYBIND11_NUMPY_DTYPE(PartialStruct, bool_, uint_, float_, ldbl_);
|
||||
PYBIND11_NUMPY_DTYPE(PartialNestedStruct, a);
|
||||
PYBIND11_NUMPY_DTYPE(StringStruct, a, b);
|
||||
PYBIND11_NUMPY_DTYPE(ArrayStruct, a, b, c, d);
|
||||
PYBIND11_NUMPY_DTYPE(EnumStruct, e1, e2);
|
||||
PYBIND11_NUMPY_DTYPE(ComplexStruct, cflt, cdbl);
|
||||
|
||||
// ... or after
|
||||
py::class_<PackedStruct>(m, "PackedStruct");
|
||||
|
||||
PYBIND11_NUMPY_DTYPE_EX(StructWithUglyNames, __x__, "x", __y__, "y");
|
||||
|
||||
#ifdef PYBIND11_NEVER_DEFINED_EVER
|
||||
// If enabled, this should produce a static_assert failure telling the user that the struct
|
||||
// is not a POD type
|
||||
struct NotPOD {
|
||||
std::string v;
|
||||
NotPOD() : v("hi"){};
|
||||
};
|
||||
PYBIND11_NUMPY_DTYPE(NotPOD, v);
|
||||
#endif
|
||||
|
||||
// Check that dtypes can be registered programmatically, both from
|
||||
// initializer lists of field descriptors and from other containers.
|
||||
py::detail::npy_format_descriptor<A>::register_dtype({});
|
||||
py::detail::npy_format_descriptor<B>::register_dtype(
|
||||
std::vector<py::detail::field_descriptor>{});
|
||||
|
||||
// test_recarray, test_scalar_conversion
|
||||
m.def("create_rec_simple", &create_recarray<SimpleStruct>);
|
||||
m.def("create_rec_packed", &create_recarray<PackedStruct>);
|
||||
m.def("create_rec_nested", [](size_t n) { // test_signature
|
||||
py::array_t<NestedStruct, 0> arr = mkarray_via_buffer<NestedStruct>(n);
|
||||
auto req = arr.request();
|
||||
auto *ptr = static_cast<NestedStruct *>(req.ptr);
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
SET_TEST_VALS(ptr[i].a, i);
|
||||
SET_TEST_VALS(ptr[i].b, i + 1);
|
||||
}
|
||||
return arr;
|
||||
});
|
||||
m.def("create_rec_partial", &create_recarray<PartialStruct>);
|
||||
m.def("create_rec_partial_nested", [](size_t n) {
|
||||
py::array_t<PartialNestedStruct, 0> arr = mkarray_via_buffer<PartialNestedStruct>(n);
|
||||
auto req = arr.request();
|
||||
auto *ptr = static_cast<PartialNestedStruct *>(req.ptr);
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
SET_TEST_VALS(ptr[i].a, i);
|
||||
}
|
||||
return arr;
|
||||
});
|
||||
m.def("print_rec_simple", &print_recarray<SimpleStruct>);
|
||||
m.def("print_rec_packed", &print_recarray<PackedStruct>);
|
||||
m.def("print_rec_nested", &print_recarray<NestedStruct>);
|
||||
|
||||
// test_format_descriptors
|
||||
m.def("get_format_unbound", []() { return py::format_descriptor<UnboundStruct>::format(); });
|
||||
m.def("print_format_descriptors", []() {
|
||||
py::list l;
|
||||
for (const auto &fmt : {py::format_descriptor<SimpleStruct>::format(),
|
||||
py::format_descriptor<PackedStruct>::format(),
|
||||
py::format_descriptor<NestedStruct>::format(),
|
||||
py::format_descriptor<PartialStruct>::format(),
|
||||
py::format_descriptor<PartialNestedStruct>::format(),
|
||||
py::format_descriptor<StringStruct>::format(),
|
||||
py::format_descriptor<ArrayStruct>::format(),
|
||||
py::format_descriptor<EnumStruct>::format(),
|
||||
py::format_descriptor<ComplexStruct>::format()}) {
|
||||
l.append(py::cast(fmt));
|
||||
}
|
||||
return l;
|
||||
});
|
||||
|
||||
// test_dtype
|
||||
std::vector<const char *> dtype_names{
|
||||
"byte", "short", "intc", "int_", "longlong", "ubyte", "ushort",
|
||||
"uintc", "uint", "ulonglong", "half", "single", "double", "longdouble",
|
||||
"csingle", "cdouble", "clongdouble", "bool_", "datetime64", "timedelta64", "object_"};
|
||||
|
||||
m.def("print_dtypes", []() {
|
||||
py::list l;
|
||||
for (const py::handle &d : {py::dtype::of<SimpleStruct>(),
|
||||
py::dtype::of<PackedStruct>(),
|
||||
py::dtype::of<NestedStruct>(),
|
||||
py::dtype::of<PartialStruct>(),
|
||||
py::dtype::of<PartialNestedStruct>(),
|
||||
py::dtype::of<StringStruct>(),
|
||||
py::dtype::of<ArrayStruct>(),
|
||||
py::dtype::of<EnumStruct>(),
|
||||
py::dtype::of<StructWithUglyNames>(),
|
||||
py::dtype::of<ComplexStruct>()}) {
|
||||
l.append(py::str(d));
|
||||
}
|
||||
return l;
|
||||
});
|
||||
m.def("test_dtype_ctors", &test_dtype_ctors);
|
||||
m.def("test_dtype_kind", [dtype_names]() {
|
||||
py::list list;
|
||||
for (const auto &dt_name : dtype_names) {
|
||||
list.append(py::dtype(dt_name).kind());
|
||||
}
|
||||
return list;
|
||||
});
|
||||
m.def("test_dtype_char_", [dtype_names]() {
|
||||
py::list list;
|
||||
for (const auto &dt_name : dtype_names) {
|
||||
list.append(py::dtype(dt_name).char_());
|
||||
}
|
||||
return list;
|
||||
});
|
||||
m.def("test_dtype_num", [dtype_names]() {
|
||||
py::list list;
|
||||
for (const auto &dt_name : dtype_names) {
|
||||
list.append(py::dtype(dt_name).num());
|
||||
}
|
||||
return list;
|
||||
});
|
||||
m.def("test_dtype_byteorder", [dtype_names]() {
|
||||
py::list list;
|
||||
for (const auto &dt_name : dtype_names) {
|
||||
list.append(py::dtype(dt_name).byteorder());
|
||||
}
|
||||
return list;
|
||||
});
|
||||
m.def("test_dtype_alignment", [dtype_names]() {
|
||||
py::list list;
|
||||
for (const auto &dt_name : dtype_names) {
|
||||
list.append(py::dtype(dt_name).alignment());
|
||||
}
|
||||
return list;
|
||||
});
|
||||
m.def("test_dtype_flags", [dtype_names]() {
|
||||
py::list list;
|
||||
for (const auto &dt_name : dtype_names) {
|
||||
list.append(py::dtype(dt_name).flags());
|
||||
}
|
||||
return list;
|
||||
});
|
||||
m.def("test_dtype_methods", []() {
|
||||
py::list list;
|
||||
auto dt1 = py::dtype::of<int32_t>();
|
||||
auto dt2 = py::dtype::of<SimpleStruct>();
|
||||
list.append(dt1);
|
||||
list.append(dt2);
|
||||
list.append(py::bool_(dt1.has_fields()));
|
||||
list.append(py::bool_(dt2.has_fields()));
|
||||
list.append(py::int_(dt1.itemsize()));
|
||||
list.append(py::int_(dt2.itemsize()));
|
||||
return list;
|
||||
});
|
||||
struct TrailingPaddingStruct {
|
||||
int32_t a;
|
||||
char b;
|
||||
};
|
||||
PYBIND11_NUMPY_DTYPE(TrailingPaddingStruct, a, b);
|
||||
m.def("trailing_padding_dtype", []() { return py::dtype::of<TrailingPaddingStruct>(); });
|
||||
|
||||
// test_string_array
|
||||
m.def("create_string_array", [](bool non_empty) {
|
||||
py::array_t<StringStruct, 0> arr = mkarray_via_buffer<StringStruct>(non_empty ? 4 : 0);
|
||||
if (non_empty) {
|
||||
auto req = arr.request();
|
||||
auto *ptr = static_cast<StringStruct *>(req.ptr);
|
||||
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';
|
||||
ptr[3].a[0] = 'a';
|
||||
ptr[3].b[0] = 'a';
|
||||
|
||||
ptr[2].a[1] = 'b';
|
||||
ptr[2].b[1] = 'b';
|
||||
ptr[3].a[1] = 'b';
|
||||
ptr[3].b[1] = 'b';
|
||||
|
||||
ptr[3].a[2] = 'c';
|
||||
ptr[3].b[2] = 'c';
|
||||
}
|
||||
return arr;
|
||||
});
|
||||
m.def("print_string_array", &print_recarray<StringStruct>);
|
||||
|
||||
// test_array_array
|
||||
m.def("create_array_array", [](size_t n) {
|
||||
py::array_t<ArrayStruct, 0> arr = mkarray_via_buffer<ArrayStruct>(n);
|
||||
auto *ptr = (ArrayStruct *) arr.mutable_data();
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
for (size_t j = 0; j < 3; j++) {
|
||||
for (size_t k = 0; k < 4; k++) {
|
||||
ptr[i].a[j][k] = char('A' + (i * 100 + j * 10 + k) % 26);
|
||||
}
|
||||
}
|
||||
for (size_t j = 0; j < 2; j++) {
|
||||
ptr[i].b[j] = int32_t(i * 1000 + j);
|
||||
}
|
||||
for (size_t j = 0; j < 3; j++) {
|
||||
ptr[i].c[j] = uint8_t(i * 10 + j);
|
||||
}
|
||||
for (size_t j = 0; j < 4; j++) {
|
||||
for (size_t k = 0; k < 2; k++) {
|
||||
ptr[i].d[j][k] = float(i) * 100.0f + float(j) * 10.0f + float(k);
|
||||
}
|
||||
}
|
||||
}
|
||||
return arr;
|
||||
});
|
||||
m.def("print_array_array", &print_recarray<ArrayStruct>);
|
||||
|
||||
// test_enum_array
|
||||
m.def("create_enum_array", [](size_t n) {
|
||||
py::array_t<EnumStruct, 0> arr = mkarray_via_buffer<EnumStruct>(n);
|
||||
auto *ptr = (EnumStruct *) arr.mutable_data();
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
ptr[i].e1 = static_cast<E1>(-1 + ((int) i % 2) * 2);
|
||||
ptr[i].e2 = static_cast<E2>(1 + (i % 2));
|
||||
}
|
||||
return arr;
|
||||
});
|
||||
m.def("print_enum_array", &print_recarray<EnumStruct>);
|
||||
|
||||
// test_complex_array
|
||||
m.def("create_complex_array", [](size_t n) {
|
||||
py::array_t<ComplexStruct, 0> arr = mkarray_via_buffer<ComplexStruct>(n);
|
||||
auto *ptr = (ComplexStruct *) arr.mutable_data();
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
ptr[i].cflt.real(float(i));
|
||||
ptr[i].cflt.imag(float(i) + 0.25f);
|
||||
ptr[i].cdbl.real(double(i) + 0.5);
|
||||
ptr[i].cdbl.imag(double(i) + 0.75);
|
||||
}
|
||||
return arr;
|
||||
});
|
||||
m.def("print_complex_array", &print_recarray<ComplexStruct>);
|
||||
|
||||
// test_array_constructors
|
||||
m.def("test_array_ctors", &test_array_ctors);
|
||||
|
||||
// test_compare_buffer_info
|
||||
struct CompareStruct {
|
||||
bool x;
|
||||
uint32_t y;
|
||||
float z;
|
||||
};
|
||||
PYBIND11_NUMPY_DTYPE(CompareStruct, x, y, z);
|
||||
m.def("compare_buffer_info", []() {
|
||||
py::list list;
|
||||
list.append(py::bool_(py::detail::compare_buffer_info<float>::compare(
|
||||
py::buffer_info(nullptr, sizeof(float), "f", 1))));
|
||||
list.append(py::bool_(py::detail::compare_buffer_info<unsigned>::compare(
|
||||
py::buffer_info(nullptr, sizeof(int), "I", 1))));
|
||||
list.append(py::bool_(py::detail::compare_buffer_info<long>::compare(
|
||||
py::buffer_info(nullptr, sizeof(long), "l", 1))));
|
||||
list.append(py::bool_(py::detail::compare_buffer_info<long>::compare(
|
||||
py::buffer_info(nullptr, sizeof(long), sizeof(long) == sizeof(int) ? "i" : "q", 1))));
|
||||
list.append(py::bool_(py::detail::compare_buffer_info<CompareStruct>::compare(
|
||||
py::buffer_info(nullptr, sizeof(CompareStruct), "T{?:x:3xI:y:f:z:}", 1))));
|
||||
return list;
|
||||
});
|
||||
m.def("buffer_to_dtype", [](py::buffer &buf) { return py::dtype(buf.request()); });
|
||||
|
||||
// test_scalar_conversion
|
||||
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_); });
|
||||
|
||||
// test_str_leak
|
||||
m.def("dtype_wrapper", [](const py::object &d) { return py::dtype::from_args(d); });
|
||||
}
|
||||
429
third_party/pybind11/tests/test_numpy_dtypes.py
vendored
Normal file
429
third_party/pybind11/tests/test_numpy_dtypes.py
vendored
Normal file
@@ -0,0 +1,429 @@
|
||||
import re
|
||||
|
||||
import pytest
|
||||
|
||||
import env # noqa: F401
|
||||
from pybind11_tests import numpy_dtypes as m
|
||||
|
||||
np = pytest.importorskip("numpy")
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def simple_dtype():
|
||||
ld = np.dtype("longdouble")
|
||||
return np.dtype({
|
||||
"names": ["bool_", "uint_", "float_", "ldbl_"],
|
||||
"formats": ["?", "u4", "f4", f"f{ld.itemsize}"],
|
||||
"offsets": [0, 4, 8, (16 if ld.alignment > 4 else 12)],
|
||||
})
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def packed_dtype():
|
||||
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':{}}}")
|
||||
|
||||
|
||||
def simple_dtype_fmt():
|
||||
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 ">")
|
||||
|
||||
|
||||
def partial_ld_offset():
|
||||
return (12 + 4 * (np.dtype("uint64").alignment > 4) + 8 + 8 *
|
||||
(np.dtype("longdouble").alignment > 8))
|
||||
|
||||
|
||||
def partial_dtype_fmt():
|
||||
ld = np.dtype("longdouble")
|
||||
partial_ld_off = partial_ld_offset()
|
||||
partial_size = partial_ld_off + ld.itemsize
|
||||
partial_end_padding = partial_size % np.dtype("uint64").alignment
|
||||
return dt_fmt().format(ld.itemsize, partial_ld_off,
|
||||
partial_size + partial_end_padding)
|
||||
|
||||
|
||||
def partial_nested_fmt():
|
||||
ld = np.dtype("longdouble")
|
||||
partial_nested_off = 8 + 8 * (ld.alignment > 8)
|
||||
partial_ld_off = partial_ld_offset()
|
||||
partial_size = partial_ld_off + ld.itemsize
|
||||
partial_end_padding = partial_size % np.dtype("uint64").alignment
|
||||
partial_nested_size = partial_nested_off * 2 + partial_size + partial_end_padding
|
||||
return "{{'names':['a'],'formats':[{}],'offsets':[{}],'itemsize':{}}}".format(
|
||||
partial_dtype_fmt(), partial_nested_off, partial_nested_size)
|
||||
|
||||
|
||||
def assert_equal(actual, expected_data, expected_dtype):
|
||||
np.testing.assert_equal(
|
||||
actual, np.array(
|
||||
expected_data, dtype=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))
|
||||
|
||||
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")
|
||||
end_padding = ld.itemsize % np.dtype("uint64").alignment
|
||||
partial_fmt = (
|
||||
"^T{?:bool_:3xI:uint_:f:float_:" +
|
||||
str(4 * (dbl.alignment > 4) + dbl.itemsize + 8 * (ld.alignment > 8)) +
|
||||
"xg:ldbl_:" + (str(end_padding) + "x}" if end_padding > 0 else "}"))
|
||||
nested_extra = str(max(8, ld.alignment))
|
||||
assert m.print_format_descriptors() == [
|
||||
ss_fmt,
|
||||
"^T{?:bool_:I:uint_:f:float_:g:ldbl_:}",
|
||||
"^T{" + ss_fmt + ":a:^T{?:bool_:I:uint_:f:float_:g:ldbl_:}:b:}",
|
||||
partial_fmt,
|
||||
"^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:}",
|
||||
]
|
||||
|
||||
|
||||
def test_dtype(simple_dtype):
|
||||
from sys import byteorder
|
||||
|
||||
e = "<" if byteorder == "little" else ">"
|
||||
|
||||
assert [x.replace(" ", "") for x in m.print_dtypes()] == [
|
||||
simple_dtype_fmt(),
|
||||
packed_dtype_fmt(),
|
||||
f"[('a',{simple_dtype_fmt()}),('b',{packed_dtype_fmt()})]",
|
||||
partial_dtype_fmt(),
|
||||
partial_nested_fmt(),
|
||||
"[('a','S3'),('b','S3')]",
|
||||
("{{'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')]",
|
||||
]
|
||||
|
||||
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,
|
||||
np.dtype("d"),
|
||||
]
|
||||
|
||||
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()))
|
||||
|
||||
expected_chars = "bhilqBHILQefdgFDG?MmO"
|
||||
assert m.test_dtype_kind() == list("iiiiiuuuuuffffcccbMmO")
|
||||
assert m.test_dtype_char_() == list(expected_chars)
|
||||
assert m.test_dtype_num() == [np.dtype(ch).num for ch in expected_chars]
|
||||
assert m.test_dtype_byteorder(
|
||||
) == [np.dtype(ch).byteorder for ch in expected_chars]
|
||||
assert m.test_dtype_alignment(
|
||||
) == [np.dtype(ch).alignment for ch in expected_chars]
|
||||
assert m.test_dtype_flags(
|
||||
) == [chr(np.dtype(ch).flags) for ch in expected_chars]
|
||||
|
||||
|
||||
def test_recarray(simple_dtype, packed_dtype):
|
||||
elements = [(False, 0, 0.0, -0.0), (True, 1, 1.5, -2.5), (False, 2, 3.0,
|
||||
-5.0)]
|
||||
|
||||
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)
|
||||
assert_equal(arr, [], packed_dtype)
|
||||
|
||||
arr = func(3)
|
||||
assert arr.dtype == 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",
|
||||
]
|
||||
else:
|
||||
assert m.print_rec_packed(arr) == [
|
||||
"p:0,0,0,-0",
|
||||
"p:1,1,1.5,-2.5",
|
||||
"p:0,2,3,-5",
|
||||
]
|
||||
|
||||
nested_dtype = np.dtype([("a", simple_dtype), ("b", packed_dtype)])
|
||||
|
||||
arr = m.create_rec_nested(0)
|
||||
assert arr.dtype == nested_dtype
|
||||
assert_equal(arr, [], nested_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 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",
|
||||
]
|
||||
|
||||
arr = m.create_rec_partial(3)
|
||||
assert str(arr.dtype).replace(" ", "") == partial_dtype_fmt()
|
||||
partial_dtype = arr.dtype
|
||||
assert "" not in arr.dtype.fields
|
||||
assert partial_dtype.itemsize > simple_dtype.itemsize
|
||||
assert_equal(arr, elements, simple_dtype)
|
||||
assert_equal(arr, elements, packed_dtype)
|
||||
|
||||
arr = m.create_rec_partial_nested(3)
|
||||
assert str(arr.dtype).replace(" ", "") == partial_nested_fmt()
|
||||
assert "" not in arr.dtype.fields
|
||||
assert "" not in arr.dtype.fields["a"][0].fields
|
||||
assert arr.dtype.itemsize > partial_dtype.itemsize
|
||||
np.testing.assert_equal(arr["a"], m.create_rec_partial(3))
|
||||
|
||||
|
||||
def test_array_constructors():
|
||||
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)))
|
||||
for i in range(5):
|
||||
np.testing.assert_array_equal(m.test_array_ctors(30 + i), data)
|
||||
np.testing.assert_array_equal(m.test_array_ctors(40 + i), data)
|
||||
|
||||
|
||||
def test_string_array():
|
||||
arr = m.create_string_array(True)
|
||||
assert str(arr.dtype) == "[('a', 'S3'), ('b', 'S3')]"
|
||||
assert m.print_string_array(arr) == [
|
||||
"a='',b=''",
|
||||
"a='a',b='a'",
|
||||
"a='ab',b='ab'",
|
||||
"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"]
|
||||
arr = m.create_string_array(False)
|
||||
assert dtype == arr.dtype
|
||||
|
||||
|
||||
def test_array_array():
|
||||
from sys import byteorder
|
||||
|
||||
e = "<" if byteorder == "little" else ">"
|
||||
|
||||
arr = m.create_array_array(3)
|
||||
assert str(arr.dtype).replace(" ", "") == (
|
||||
"{{'names':['a','b','c','d']," + "'formats':[('S4',(3,)),('" + e +
|
||||
"i4',(2,)),('u1',(3,)),('{e}f4',(4,2))]," +
|
||||
"'offsets':[0,12,20,24],'itemsize':56}}").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}}",
|
||||
]
|
||||
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 m.create_array_array(0).dtype == arr.dtype
|
||||
|
||||
|
||||
def test_enum_array():
|
||||
from sys import byteorder
|
||||
|
||||
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 m.create_enum_array(0).dtype == dtype
|
||||
|
||||
|
||||
def test_complex_array():
|
||||
from sys import byteorder
|
||||
|
||||
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 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)",
|
||||
]
|
||||
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]")
|
||||
|
||||
|
||||
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),
|
||||
]
|
||||
funcs = [m.f_simple, m.f_packed, m.f_nested]
|
||||
|
||||
for i, func in enumerate(funcs):
|
||||
for j, arr in enumerate(arrays):
|
||||
if i == j and i < 2:
|
||||
assert [func(arr[k])
|
||||
for k in range(n)] == [k * 10 for k in range(n)]
|
||||
else:
|
||||
with pytest.raises(TypeError) as excinfo:
|
||||
func(arr[0])
|
||||
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)
|
||||
|
||||
|
||||
@pytest.mark.xfail("env.PYPY")
|
||||
def test_str_leak():
|
||||
from sys import getrefcount
|
||||
|
||||
fmt = "f4"
|
||||
pytest.gc_collect()
|
||||
start = getrefcount(fmt)
|
||||
d = m.dtype_wrapper(fmt)
|
||||
assert d is np.dtype("f4")
|
||||
del d
|
||||
pytest.gc_collect()
|
||||
assert getrefcount(fmt) == start
|
||||
|
||||
|
||||
def test_compare_buffer_info():
|
||||
assert all(m.compare_buffer_info())
|
||||
107
third_party/pybind11/tests/test_numpy_vectorize.cpp
vendored
Normal file
107
third_party/pybind11/tests/test_numpy_vectorize.cpp
vendored
Normal file
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
tests/test_numpy_vectorize.cpp -- auto-vectorize functions over NumPy array
|
||||
arguments
|
||||
|
||||
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/numpy.h>
|
||||
|
||||
#include "pybind11_tests.h"
|
||||
|
||||
#include <utility>
|
||||
|
||||
double my_func(int x, float y, double z) {
|
||||
py::print("my_func(x:int={}, y:float={:.0f}, z:float={:.0f})"_s.format(x, y, z));
|
||||
return (float) x * y * z;
|
||||
}
|
||||
|
||||
TEST_SUBMODULE(numpy_vectorize, m) {
|
||||
try {
|
||||
py::module_::import("numpy");
|
||||
} catch (...) {
|
||||
return;
|
||||
}
|
||||
|
||||
// test_vectorize, test_docs, test_array_collapse
|
||||
// Vectorize all arguments of a function (though non-vector arguments are also allowed)
|
||||
m.def("vectorized_func", py::vectorize(my_func));
|
||||
|
||||
// Vectorize a lambda function with a capture object (e.g. to exclude some arguments from the
|
||||
// vectorization)
|
||||
m.def("vectorized_func2", [](py::array_t<int> x, py::array_t<float> y, float z) {
|
||||
return py::vectorize([z](int x, float y) { return my_func(x, y, z); })(std::move(x),
|
||||
std::move(y));
|
||||
});
|
||||
|
||||
// Vectorize a complex-valued function
|
||||
m.def("vectorized_func3",
|
||||
py::vectorize([](std::complex<double> c) { return c * std::complex<double>(2.f); }));
|
||||
|
||||
// test_type_selection
|
||||
// NumPy function which only accepts specific data types
|
||||
// A lot of these no lints could be replaced with const refs, and probably should at some
|
||||
// point.
|
||||
m.def("selective_func",
|
||||
[](const py::array_t<int, py::array::c_style> &) { return "Int branch taken."; });
|
||||
m.def("selective_func",
|
||||
[](const py::array_t<float, py::array::c_style> &) { return "Float branch taken."; });
|
||||
m.def("selective_func", [](const py::array_t<std::complex<float>, py::array::c_style> &) {
|
||||
return "Complex float branch taken.";
|
||||
});
|
||||
|
||||
// test_passthrough_arguments
|
||||
// Passthrough test: references and non-pod types should be automatically passed through (in
|
||||
// the function definition below, only `b`, `d`, and `g` are vectorized):
|
||||
struct NonPODClass {
|
||||
explicit NonPODClass(int v) : value{v} {}
|
||||
int value;
|
||||
};
|
||||
py::class_<NonPODClass>(m, "NonPODClass")
|
||||
.def(py::init<int>())
|
||||
.def_readwrite("value", &NonPODClass::value);
|
||||
m.def("vec_passthrough",
|
||||
py::vectorize([](const double *a,
|
||||
double b,
|
||||
// Changing this broke things
|
||||
// NOLINTNEXTLINE(performance-unnecessary-value-param)
|
||||
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; }));
|
||||
|
||||
// test_method_vectorization
|
||||
struct VectorizeTestClass {
|
||||
explicit VectorizeTestClass(int v) : value{v} {};
|
||||
float method(int x, float y) const { return y + (float) (x + value); }
|
||||
int value = 0;
|
||||
};
|
||||
py::class_<VectorizeTestClass> vtc(m, "VectorizeTestClass");
|
||||
vtc.def(py::init<int>()).def_readwrite("value", &VectorizeTestClass::value);
|
||||
|
||||
// Automatic vectorizing of methods
|
||||
vtc.def("method", py::vectorize(&VectorizeTestClass::method));
|
||||
|
||||
// test_trivial_broadcasting
|
||||
// Internal optimization test for whether the input is trivially broadcastable:
|
||||
py::enum_<py::detail::broadcast_trivial>(m, "trivial")
|
||||
.value("f_trivial", py::detail::broadcast_trivial::f_trivial)
|
||||
.value("c_trivial", py::detail::broadcast_trivial::c_trivial)
|
||||
.value("non_trivial", py::detail::broadcast_trivial::non_trivial);
|
||||
m.def("vectorized_is_trivial",
|
||||
[](const py::array_t<int, py::array::forcecast> &arg1,
|
||||
const py::array_t<float, py::array::forcecast> &arg2,
|
||||
const py::array_t<double, py::array::forcecast> &arg3) {
|
||||
py::ssize_t ndim = 0;
|
||||
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; }));
|
||||
}
|
||||
232
third_party/pybind11/tests/test_numpy_vectorize.py
vendored
Normal file
232
third_party/pybind11/tests/test_numpy_vectorize.py
vendored
Normal file
@@ -0,0 +1,232 @@
|
||||
import pytest
|
||||
|
||||
from pybind11_tests import numpy_vectorize as m
|
||||
|
||||
np = pytest.importorskip("numpy")
|
||||
|
||||
|
||||
def test_vectorize(capture):
|
||||
assert np.isclose(m.vectorized_func3(np.array(3 + 7j)), [6 + 14j])
|
||||
|
||||
for f in [m.vectorized_func, m.vectorized_func2]:
|
||||
with capture:
|
||||
assert np.isclose(f(1, 2, 3), 6)
|
||||
assert capture == "my_func(x:int=1, y:float=2, z:float=3)"
|
||||
with capture:
|
||||
assert np.isclose(f(np.array(1), np.array(2), 3), 6)
|
||||
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 == """
|
||||
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")
|
||||
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 == """
|
||||
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, )
|
||||
assert np.allclose(f(a, b, c), a * b * c)
|
||||
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)
|
||||
my_func(x:int=7, y:float=8, z:float=3)
|
||||
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 == """
|
||||
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)
|
||||
my_func(x:int=4, y:float=2, z:float=2)
|
||||
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 == """
|
||||
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)
|
||||
my_func(x:int=4, y:float=3, z:float=2)
|
||||
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, )
|
||||
assert np.allclose(f(a, b, c), a * b * c)
|
||||
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)
|
||||
my_func(x:int=4, y:float=3, z:float=2)
|
||||
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 == """
|
||||
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, )
|
||||
assert np.allclose(f(a, b, c), a * b * c)
|
||||
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.")
|
||||
|
||||
|
||||
def test_docs(doc):
|
||||
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():
|
||||
trivial, vectorized_is_trivial = m.trivial, m.vectorized_is_trivial
|
||||
|
||||
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 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")
|
||||
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
|
||||
assert vectorized_is_trivial(z1, z2, 1) == trivial.c_trivial
|
||||
assert vectorized_is_trivial(z1[::2, ::2], 1, 1) == trivial.non_trivial
|
||||
assert vectorized_is_trivial(1, 1, z1[::2, ::2]) == trivial.c_trivial
|
||||
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")
|
||||
y2 = np.array(y1)
|
||||
y3 = np.array(y1)
|
||||
assert vectorized_is_trivial(y1, y2, y3) == trivial.f_trivial
|
||||
assert vectorized_is_trivial(y1, 1, 1) == trivial.f_trivial
|
||||
assert vectorized_is_trivial(1, y2, 1) == trivial.f_trivial
|
||||
assert vectorized_is_trivial(1, 1, y3) == trivial.f_trivial
|
||||
assert vectorized_is_trivial(y1, z2, 1) == trivial.non_trivial
|
||||
assert vectorized_is_trivial(z1[1::4, 1::4], y2, 1) == trivial.f_trivial
|
||||
assert vectorized_is_trivial(y1[1::4, 1::4], z2, 1) == trivial.c_trivial
|
||||
|
||||
assert m.vectorized_func(z1, z2, z3).flags.c_contiguous
|
||||
assert m.vectorized_func(y1, y2, y3).flags.f_contiguous
|
||||
assert m.vectorized_func(z1, 1, 1).flags.c_contiguous
|
||||
assert m.vectorized_func(1, y2, 1).flags.f_contiguous
|
||||
assert m.vectorized_func(z1[1::4, 1::4], y2, 1).flags.f_contiguous
|
||||
assert m.vectorized_func(y1[1::4, 1::4], z2, 1).flags.c_contiguous
|
||||
|
||||
|
||||
def test_passthrough_arguments(doc):
|
||||
assert doc(m.vec_passthrough) == ("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
|
||||
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],
|
||||
]))
|
||||
|
||||
|
||||
def test_method_vectorization():
|
||||
o = m.VectorizeTestClass(3)
|
||||
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]])
|
||||
|
||||
|
||||
def test_array_collapse():
|
||||
assert not isinstance(m.vectorized_func(1, 2, 3), np.ndarray)
|
||||
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, )
|
||||
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
|
||||
77
third_party/pybind11/tests/test_opaque_types.cpp
vendored
Normal file
77
third_party/pybind11/tests/test_opaque_types.cpp
vendored
Normal file
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
tests/test_opaque_types.cpp -- opaque types, passing void pointers
|
||||
|
||||
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/stl.h>
|
||||
|
||||
#include "pybind11_tests.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
// IMPORTANT: Disable internal pybind11 translation mechanisms for STL data structures
|
||||
//
|
||||
// This also deliberately doesn't use the below StringList type alias to test
|
||||
// that MAKE_OPAQUE can handle a type containing a `,`. (The `std::allocator`
|
||||
// bit is just the default `std::vector` allocator).
|
||||
PYBIND11_MAKE_OPAQUE(std::vector<std::string, std::allocator<std::string>>);
|
||||
|
||||
using StringList = std::vector<std::string, std::allocator<std::string>>;
|
||||
|
||||
TEST_SUBMODULE(opaque_types, m) {
|
||||
// test_string_list
|
||||
py::class_<StringList>(m, "StringList")
|
||||
.def(py::init<>())
|
||||
.def("pop_back", &StringList::pop_back)
|
||||
/* There are multiple versions of push_back(), etc. Select the right ones. */
|
||||
.def("push_back", (void(StringList::*)(const std::string &)) & StringList::push_back)
|
||||
.def("back", (std::string & (StringList::*) ()) & StringList::back)
|
||||
.def("__len__", [](const StringList &v) { return v.size(); })
|
||||
.def(
|
||||
"__iter__",
|
||||
[](StringList &v) { return py::make_iterator(v.begin(), v.end()); },
|
||||
py::keep_alive<0, 1>());
|
||||
|
||||
class ClassWithSTLVecProperty {
|
||||
public:
|
||||
StringList stringList;
|
||||
};
|
||||
py::class_<ClassWithSTLVecProperty>(m, "ClassWithSTLVecProperty")
|
||||
.def(py::init<>())
|
||||
.def_readwrite("stringList", &ClassWithSTLVecProperty::stringList);
|
||||
|
||||
m.def("print_opaque_list", [](const StringList &l) {
|
||||
std::string ret = "Opaque list: [";
|
||||
bool first = true;
|
||||
for (const auto &entry : l) {
|
||||
if (!first) {
|
||||
ret += ", ";
|
||||
}
|
||||
ret += entry;
|
||||
first = false;
|
||||
}
|
||||
return ret + "]";
|
||||
});
|
||||
|
||||
// test_pointers
|
||||
m.def("return_void_ptr", []() { return (void *) 0x1234; });
|
||||
m.def("get_void_ptr_value", [](void *ptr) { return reinterpret_cast<std::intptr_t>(ptr); });
|
||||
m.def("return_null_str", []() { return (char *) nullptr; });
|
||||
m.def("get_null_str_value", [](char *ptr) { return reinterpret_cast<std::intptr_t>(ptr); });
|
||||
|
||||
m.def("return_unique_ptr", []() -> std::unique_ptr<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);
|
||||
}
|
||||
57
third_party/pybind11/tests/test_opaque_types.py
vendored
Normal file
57
third_party/pybind11/tests/test_opaque_types.py
vendored
Normal file
@@ -0,0 +1,57 @@
|
||||
import pytest
|
||||
|
||||
from pybind11_tests import ConstructorStats, UserType
|
||||
from pybind11_tests import opaque_types as m
|
||||
|
||||
|
||||
def test_string_list():
|
||||
lst = m.StringList()
|
||||
lst.push_back("Element 1")
|
||||
lst.push_back("Element 2")
|
||||
assert m.print_opaque_list(lst) == "Opaque list: [Element 1, Element 2]"
|
||||
assert lst.back() == "Element 2"
|
||||
|
||||
for i, k in enumerate(lst, start=1):
|
||||
assert k == f"Element {i}"
|
||||
lst.pop_back()
|
||||
assert m.print_opaque_list(lst) == "Opaque list: [Element 1]"
|
||||
|
||||
cvp = m.ClassWithSTLVecProperty()
|
||||
assert m.print_opaque_list(cvp.stringList) == "Opaque list: []"
|
||||
|
||||
cvp.stringList = lst
|
||||
cvp.stringList.push_back("Element 3")
|
||||
assert m.print_opaque_list(
|
||||
cvp.stringList) == "Opaque list: [Element 1, Element 3]"
|
||||
|
||||
|
||||
def test_pointers(msg):
|
||||
living_before = ConstructorStats.get(UserType).alive()
|
||||
assert m.get_void_ptr_value(m.return_void_ptr()) == 0x1234
|
||||
assert m.get_void_ptr_value(
|
||||
UserType()) # Should also work for other C++ types
|
||||
assert ConstructorStats.get(UserType).alive() == living_before
|
||||
|
||||
with pytest.raises(TypeError) as excinfo:
|
||||
m.get_void_ptr_value([1, 2, 3]) # This should not work
|
||||
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]
|
||||
""")
|
||||
|
||||
assert m.return_null_str() is None
|
||||
assert m.get_null_str_value(m.return_null_str()) is not None
|
||||
|
||||
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
|
||||
288
third_party/pybind11/tests/test_operator_overloading.cpp
vendored
Normal file
288
third_party/pybind11/tests/test_operator_overloading.cpp
vendored
Normal file
@@ -0,0 +1,288 @@
|
||||
/*
|
||||
tests/test_operator_overloading.cpp -- operator overloading
|
||||
|
||||
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/operators.h>
|
||||
#include <pybind11/stl.h>
|
||||
|
||||
#include "constructor_stats.h"
|
||||
#include "pybind11_tests.h"
|
||||
|
||||
#include <functional>
|
||||
|
||||
class Vector2 {
|
||||
public:
|
||||
Vector2(float x, float y) : x(x), y(y) { print_created(this, toString()); }
|
||||
Vector2(const Vector2 &v) : x(v.x), y(v.y) { print_copy_created(this); }
|
||||
Vector2(Vector2 &&v) noexcept : x(v.x), y(v.y) {
|
||||
print_move_created(this);
|
||||
v.x = v.y = 0;
|
||||
}
|
||||
Vector2 &operator=(const Vector2 &v) {
|
||||
x = v.x;
|
||||
y = v.y;
|
||||
print_copy_assigned(this);
|
||||
return *this;
|
||||
}
|
||||
Vector2 &operator=(Vector2 &&v) noexcept {
|
||||
x = v.x;
|
||||
y = v.y;
|
||||
v.x = v.y = 0;
|
||||
print_move_assigned(this);
|
||||
return *this;
|
||||
}
|
||||
~Vector2() { print_destroyed(this); }
|
||||
|
||||
std::string toString() const {
|
||||
return "[" + std::to_string(x) + ", " + std::to_string(y) + "]";
|
||||
}
|
||||
|
||||
Vector2 operator-() const { return Vector2(-x, -y); }
|
||||
Vector2 operator+(const Vector2 &v) const { return Vector2(x + v.x, y + v.y); }
|
||||
Vector2 operator-(const Vector2 &v) const { return Vector2(x - v.x, y - v.y); }
|
||||
Vector2 operator-(float value) const { return Vector2(x - value, y - value); }
|
||||
Vector2 operator+(float value) const { return Vector2(x + value, y + value); }
|
||||
Vector2 operator*(float value) const { return Vector2(x * value, y * value); }
|
||||
Vector2 operator/(float value) const { return Vector2(x / value, y / value); }
|
||||
Vector2 operator*(const Vector2 &v) const { return Vector2(x * v.x, y * v.y); }
|
||||
Vector2 operator/(const Vector2 &v) const { return Vector2(x / v.x, y / v.y); }
|
||||
Vector2 &operator+=(const Vector2 &v) {
|
||||
x += v.x;
|
||||
y += v.y;
|
||||
return *this;
|
||||
}
|
||||
Vector2 &operator-=(const Vector2 &v) {
|
||||
x -= v.x;
|
||||
y -= v.y;
|
||||
return *this;
|
||||
}
|
||||
Vector2 &operator*=(float v) {
|
||||
x *= v;
|
||||
y *= v;
|
||||
return *this;
|
||||
}
|
||||
Vector2 &operator/=(float v) {
|
||||
x /= v;
|
||||
y /= v;
|
||||
return *this;
|
||||
}
|
||||
Vector2 &operator*=(const Vector2 &v) {
|
||||
x *= v.x;
|
||||
y *= v.y;
|
||||
return *this;
|
||||
}
|
||||
Vector2 &operator/=(const Vector2 &v) {
|
||||
x /= v.x;
|
||||
y /= v.y;
|
||||
return *this;
|
||||
}
|
||||
|
||||
friend Vector2 operator+(float f, const Vector2 &v) { return Vector2(f + v.x, f + v.y); }
|
||||
friend Vector2 operator-(float f, const Vector2 &v) { return Vector2(f - v.x, f - v.y); }
|
||||
friend Vector2 operator*(float f, const Vector2 &v) { return Vector2(f * v.x, f * v.y); }
|
||||
friend Vector2 operator/(float f, const Vector2 &v) { return Vector2(f / v.x, f / v.y); }
|
||||
|
||||
bool operator==(const Vector2 &v) const { return x == v.x && y == v.y; }
|
||||
bool operator!=(const Vector2 &v) const { return x != v.x || y != v.y; }
|
||||
|
||||
private:
|
||||
float x, y;
|
||||
};
|
||||
|
||||
class C1 {};
|
||||
class C2 {};
|
||||
|
||||
int operator+(const C1 &, const C1 &) { return 11; }
|
||||
int operator+(const C2 &, const C2 &) { return 22; }
|
||||
int operator+(const C2 &, const C1 &) { return 21; }
|
||||
int operator+(const C1 &, const C2 &) { return 12; }
|
||||
|
||||
struct HashMe {
|
||||
std::string member;
|
||||
};
|
||||
|
||||
bool operator==(const HashMe &lhs, const HashMe &rhs) { return lhs.member == rhs.member; }
|
||||
|
||||
// Note: Specializing explicit within `namespace std { ... }` is done due to a
|
||||
// bug in GCC<7. If you are supporting compilers later than this, consider
|
||||
// specializing `using template<> struct std::hash<...>` in the global
|
||||
// namespace instead, per this recommendation:
|
||||
// https://en.cppreference.com/w/cpp/language/extending_std#Adding_template_specializations
|
||||
namespace std {
|
||||
template <>
|
||||
struct hash<Vector2> {
|
||||
// Not a good hash function, but easy to test
|
||||
size_t operator()(const Vector2 &) { return 4; }
|
||||
};
|
||||
|
||||
// HashMe has a hash function in C++ but no `__hash__` for Python.
|
||||
template <>
|
||||
struct hash<HashMe> {
|
||||
std::size_t operator()(const HashMe &selector) const {
|
||||
return std::hash<std::string>()(selector.member);
|
||||
}
|
||||
};
|
||||
} // namespace std
|
||||
|
||||
// Not a good abs function, but easy to test.
|
||||
std::string abs(const Vector2 &) { return "abs(Vector2)"; }
|
||||
|
||||
// MSVC & Intel warns about unknown pragmas, and warnings are errors.
|
||||
#if !defined(_MSC_VER) && !defined(__INTEL_COMPILER)
|
||||
# pragma GCC diagnostic push
|
||||
// clang 7.0.0 and Apple LLVM 10.0.1 introduce `-Wself-assign-overloaded` to
|
||||
// `-Wall`, which is used here for overloading (e.g. `py::self += py::self `).
|
||||
// Here, we suppress the warning using `#pragma diagnostic`.
|
||||
// Taken from: https://github.com/RobotLocomotion/drake/commit/aaf84b46
|
||||
// TODO(eric): This could be resolved using a function / functor (e.g. `py::self()`).
|
||||
# if defined(__APPLE__) && defined(__clang__)
|
||||
# if (__clang_major__ >= 10)
|
||||
# pragma GCC diagnostic ignored "-Wself-assign-overloaded"
|
||||
# endif
|
||||
# elif defined(__clang__)
|
||||
# if (__clang_major__ >= 7)
|
||||
# pragma GCC diagnostic ignored "-Wself-assign-overloaded"
|
||||
# endif
|
||||
# endif
|
||||
#endif
|
||||
|
||||
TEST_SUBMODULE(operators, m) {
|
||||
|
||||
// test_operator_overloading
|
||||
py::class_<Vector2>(m, "Vector2")
|
||||
.def(py::init<float, float>())
|
||||
.def(py::self + py::self)
|
||||
.def(py::self + float())
|
||||
.def(py::self - py::self)
|
||||
.def(py::self - float())
|
||||
.def(py::self * float())
|
||||
.def(py::self / float())
|
||||
.def(py::self * py::self)
|
||||
.def(py::self / py::self)
|
||||
.def(py::self += py::self)
|
||||
.def(py::self -= py::self)
|
||||
.def(py::self *= float())
|
||||
.def(py::self /= float())
|
||||
.def(py::self *= py::self)
|
||||
.def(py::self /= py::self)
|
||||
.def(float() + py::self)
|
||||
.def(float() - py::self)
|
||||
.def(float() * py::self)
|
||||
.def(float() / py::self)
|
||||
.def(-py::self)
|
||||
.def("__str__", &Vector2::toString)
|
||||
.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");
|
||||
|
||||
// test_operators_notimplemented
|
||||
// #393: need to return NotSupported to ensure correct arithmetic operator behavior
|
||||
py::class_<C1>(m, "C1").def(py::init<>()).def(py::self + py::self);
|
||||
|
||||
py::class_<C2>(m, "C2")
|
||||
.def(py::init<>())
|
||||
.def(py::self + py::self)
|
||||
.def("__add__", [](const C2 &c2, const C1 &c1) { return c2 + c1; })
|
||||
.def("__radd__", [](const C2 &c2, const C1 &c1) { return c1 + c2; });
|
||||
|
||||
// test_nested
|
||||
// #328: first member in a class can't be used in operators
|
||||
struct NestABase {
|
||||
int value = -2;
|
||||
};
|
||||
py::class_<NestABase>(m, "NestABase")
|
||||
.def(py::init<>())
|
||||
.def_readwrite("value", &NestABase::value);
|
||||
|
||||
struct NestA : NestABase {
|
||||
int value = 3;
|
||||
NestA &operator+=(int i) {
|
||||
value += i;
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
py::class_<NestA>(m, "NestA")
|
||||
.def(py::init<>())
|
||||
.def(py::self += int())
|
||||
.def(
|
||||
"as_base",
|
||||
[](NestA &a) -> NestABase & { return (NestABase &) a; },
|
||||
py::return_value_policy::reference_internal);
|
||||
m.def("get_NestA", [](const NestA &a) { return a.value; });
|
||||
|
||||
struct NestB {
|
||||
NestA a;
|
||||
int value = 4;
|
||||
NestB &operator-=(int i) {
|
||||
value -= i;
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
py::class_<NestB>(m, "NestB")
|
||||
.def(py::init<>())
|
||||
.def(py::self -= int())
|
||||
.def_readwrite("a", &NestB::a);
|
||||
m.def("get_NestB", [](const NestB &b) { return b.value; });
|
||||
|
||||
struct NestC {
|
||||
NestB b;
|
||||
int value = 5;
|
||||
NestC &operator*=(int i) {
|
||||
value *= i;
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
py::class_<NestC>(m, "NestC")
|
||||
.def(py::init<>())
|
||||
.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);
|
||||
|
||||
// define __eq__ but not __hash__
|
||||
py::class_<HashMe>(m, "HashMe").def(py::self == py::self);
|
||||
|
||||
m.def("get_unhashable_HashMe_set", []() { return std::unordered_set<HashMe>{{"one"}}; });
|
||||
}
|
||||
#if !defined(_MSC_VER) && !defined(__INTEL_COMPILER)
|
||||
# pragma GCC diagnostic pop
|
||||
#endif
|
||||
152
third_party/pybind11/tests/test_operator_overloading.py
vendored
Normal file
152
third_party/pybind11/tests/test_operator_overloading.py
vendored
Normal file
@@ -0,0 +1,152 @@
|
||||
import pytest
|
||||
|
||||
from pybind11_tests import ConstructorStats
|
||||
from pybind11_tests import operators as m
|
||||
|
||||
|
||||
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]"
|
||||
|
||||
assert str(-v2) == "[-3.000000, 1.000000]"
|
||||
|
||||
assert str(v1 + v2) == "[4.000000, 1.000000]"
|
||||
assert str(v1 - v2) == "[-2.000000, 3.000000]"
|
||||
assert str(v1 - 8) == "[-7.000000, -6.000000]"
|
||||
assert str(v1 + 8) == "[9.000000, 10.000000]"
|
||||
assert str(v1 * 8) == "[8.000000, 16.000000]"
|
||||
assert str(v1 / 8) == "[0.125000, 0.250000]"
|
||||
assert str(8 - v1) == "[7.000000, 6.000000]"
|
||||
assert str(8 + v1) == "[9.000000, 10.000000]"
|
||||
assert str(8 * v1) == "[8.000000, 16.000000]"
|
||||
assert str(8 / v1) == "[8.000000, 4.000000]"
|
||||
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
|
||||
assert str(v1) == "[4.000000, 1.000000]"
|
||||
v1 *= 2
|
||||
assert str(v1) == "[8.000000, 2.000000]"
|
||||
v1 /= 16
|
||||
assert str(v1) == "[0.500000, 0.125000]"
|
||||
v1 *= v2
|
||||
assert str(v1) == "[1.500000, -0.125000]"
|
||||
v2 /= v1
|
||||
assert str(v2) == "[2.000000, 8.000000]"
|
||||
|
||||
cstats = ConstructorStats.get(m.Vector2)
|
||||
assert cstats.alive() == 3
|
||||
del v1
|
||||
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]",
|
||||
"[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
|
||||
assert cstats.copy_assignments == 0
|
||||
assert cstats.move_assignments == 0
|
||||
|
||||
|
||||
def test_operators_notimplemented():
|
||||
"""#393: need to return NotSupported to ensure correct arithmetic operator behavior"""
|
||||
|
||||
c1, c2 = m.C1(), m.C2()
|
||||
assert c1 + c1 == 11
|
||||
assert c2 + c2 == 22
|
||||
assert c2 + c1 == 21
|
||||
assert c1 + c2 == 12
|
||||
|
||||
|
||||
def test_nested():
|
||||
"""#328: first member in a class can't be used in operators"""
|
||||
|
||||
a = m.NestA()
|
||||
b = m.NestB()
|
||||
c = m.NestC()
|
||||
|
||||
a += 10
|
||||
assert m.get_NestA(a) == 13
|
||||
b.a += 100
|
||||
assert m.get_NestA(b.a) == 103
|
||||
c.b.a += 1000
|
||||
assert m.get_NestA(c.b.a) == 1003
|
||||
b -= 1
|
||||
assert m.get_NestB(b) == 3
|
||||
c.b -= 3
|
||||
assert m.get_NestB(c.b) == 1
|
||||
c *= 7
|
||||
assert m.get_NestC(c) == 35
|
||||
|
||||
abase = a.as_base()
|
||||
assert abase.value == -2
|
||||
a.as_base().value += 44
|
||||
assert abase.value == 42
|
||||
assert c.b.a.as_base().value == -2
|
||||
c.b.a.as_base().value += 44
|
||||
assert c.b.a.as_base().value == 42
|
||||
|
||||
del c
|
||||
pytest.gc_collect()
|
||||
del a # Shouldn't delete while abase is still alive
|
||||
pytest.gc_collect()
|
||||
|
||||
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) as excinfo:
|
||||
hash(m.Comparable(15))
|
||||
assert str(excinfo.value).startswith("unhashable type:")
|
||||
|
||||
for hashable in (m.Hashable, m.Hashable2):
|
||||
assert hashable(15) is not hashable(15)
|
||||
assert hashable(15) == hashable(15)
|
||||
|
||||
assert hash(hashable(15)) == 15
|
||||
assert hash(hashable(15)) == hash(hashable(15))
|
||||
|
||||
|
||||
def test_return_set_of_unhashable():
|
||||
with pytest.raises(TypeError) as excinfo:
|
||||
m.get_unhashable_HashMe_set()
|
||||
assert str(excinfo.value.__cause__).startswith("unhashable type:")
|
||||
194
third_party/pybind11/tests/test_pickling.cpp
vendored
Normal file
194
third_party/pybind11/tests/test_pickling.cpp
vendored
Normal file
@@ -0,0 +1,194 @@
|
||||
/*
|
||||
tests/test_pickling.cpp -- pickle support
|
||||
|
||||
Copyright (c) 2016 Wenzel Jakob <wenzel.jakob@epfl.ch>
|
||||
Copyright (c) 2021 The Pybind Development Team.
|
||||
|
||||
All rights reserved. Use of this source code is governed by a
|
||||
BSD-style license that can be found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "pybind11_tests.h"
|
||||
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
#include <utility>
|
||||
|
||||
namespace exercise_trampoline {
|
||||
|
||||
struct SimpleBase {
|
||||
int num = 0;
|
||||
virtual ~SimpleBase() = default;
|
||||
|
||||
// For compatibility with old clang versions:
|
||||
SimpleBase() = default;
|
||||
SimpleBase(const SimpleBase &) = default;
|
||||
};
|
||||
|
||||
struct SimpleBaseTrampoline : SimpleBase {};
|
||||
|
||||
struct SimpleCppDerived : SimpleBase {};
|
||||
|
||||
void wrap(py::module m) {
|
||||
py::class_<SimpleBase, SimpleBaseTrampoline>(m, "SimpleBase")
|
||||
.def(py::init<>())
|
||||
.def_readwrite("num", &SimpleBase::num)
|
||||
.def(py::pickle(
|
||||
[](const py::object &self) {
|
||||
py::dict d;
|
||||
if (py::hasattr(self, "__dict__")) {
|
||||
d = self.attr("__dict__");
|
||||
}
|
||||
return py::make_tuple(self.attr("num"), d);
|
||||
},
|
||||
[](const py::tuple &t) {
|
||||
if (t.size() != 2) {
|
||||
throw std::runtime_error("Invalid state!");
|
||||
}
|
||||
auto cpp_state = std::unique_ptr<SimpleBase>(new SimpleBaseTrampoline);
|
||||
cpp_state->num = t[0].cast<int>();
|
||||
auto py_state = t[1].cast<py::dict>();
|
||||
return std::make_pair(std::move(cpp_state), py_state);
|
||||
}));
|
||||
|
||||
m.def("make_SimpleCppDerivedAsBase",
|
||||
[]() { return std::unique_ptr<SimpleBase>(new SimpleCppDerived); });
|
||||
m.def("check_dynamic_cast_SimpleCppDerived", [](const SimpleBase *base_ptr) {
|
||||
return dynamic_cast<const SimpleCppDerived *>(base_ptr) != nullptr;
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace exercise_trampoline
|
||||
|
||||
TEST_SUBMODULE(pickling, m) {
|
||||
m.def("simple_callable", []() { return 20220426; });
|
||||
|
||||
// test_roundtrip
|
||||
class Pickleable {
|
||||
public:
|
||||
explicit Pickleable(const std::string &value) : m_value(value) {}
|
||||
const std::string &value() const { return m_value; }
|
||||
|
||||
void setExtra1(int extra1) { m_extra1 = extra1; }
|
||||
void setExtra2(int extra2) { m_extra2 = extra2; }
|
||||
int extra1() const { return m_extra1; }
|
||||
int extra2() const { return m_extra2; }
|
||||
|
||||
private:
|
||||
std::string m_value;
|
||||
int m_extra1 = 0;
|
||||
int m_extra2 = 0;
|
||||
};
|
||||
|
||||
class PickleableNew : public Pickleable {
|
||||
public:
|
||||
using Pickleable::Pickleable;
|
||||
};
|
||||
|
||||
py::class_<Pickleable> pyPickleable(m, "Pickleable");
|
||||
pyPickleable.def(py::init<std::string>())
|
||||
.def("value", &Pickleable::value)
|
||||
.def("extra1", &Pickleable::extra1)
|
||||
.def("extra2", &Pickleable::extra2)
|
||||
.def("setExtra1", &Pickleable::setExtra1)
|
||||
.def("setExtra2", &Pickleable::setExtra2)
|
||||
// For details on the methods below, refer to
|
||||
// http://docs.python.org/3/library/pickle.html#pickling-class-instances
|
||||
.def("__getstate__", [](const Pickleable &p) {
|
||||
/* Return a tuple that fully encodes the state of the object */
|
||||
return py::make_tuple(p.value(), p.extra1(), p.extra2());
|
||||
});
|
||||
ignoreOldStyleInitWarnings([&pyPickleable]() {
|
||||
pyPickleable.def("__setstate__", [](Pickleable &p, const py::tuple &t) {
|
||||
if (t.size() != 3) {
|
||||
throw std::runtime_error("Invalid state!");
|
||||
}
|
||||
/* Invoke the constructor (need to use in-place version) */
|
||||
new (&p) Pickleable(t[0].cast<std::string>());
|
||||
|
||||
/* Assign any additional state */
|
||||
p.setExtra1(t[1].cast<int>());
|
||||
p.setExtra2(t[2].cast<int>());
|
||||
});
|
||||
});
|
||||
|
||||
py::class_<PickleableNew, Pickleable>(m, "PickleableNew")
|
||||
.def(py::init<std::string>())
|
||||
.def(py::pickle(
|
||||
[](const PickleableNew &p) {
|
||||
return py::make_tuple(p.value(), p.extra1(), p.extra2());
|
||||
},
|
||||
[](const py::tuple &t) {
|
||||
if (t.size() != 3) {
|
||||
throw std::runtime_error("Invalid state!");
|
||||
}
|
||||
auto p = PickleableNew(t[0].cast<std::string>());
|
||||
|
||||
p.setExtra1(t[1].cast<int>());
|
||||
p.setExtra2(t[2].cast<int>());
|
||||
return p;
|
||||
}));
|
||||
|
||||
#if !defined(PYPY_VERSION)
|
||||
// test_roundtrip_with_dict
|
||||
class PickleableWithDict {
|
||||
public:
|
||||
explicit PickleableWithDict(const std::string &value) : value(value) {}
|
||||
|
||||
std::string value;
|
||||
int extra;
|
||||
};
|
||||
|
||||
class PickleableWithDictNew : public PickleableWithDict {
|
||||
public:
|
||||
using PickleableWithDict::PickleableWithDict;
|
||||
};
|
||||
|
||||
py::class_<PickleableWithDict> pyPickleableWithDict(
|
||||
m, "PickleableWithDict", py::dynamic_attr());
|
||||
pyPickleableWithDict.def(py::init<std::string>())
|
||||
.def_readwrite("value", &PickleableWithDict::value)
|
||||
.def_readwrite("extra", &PickleableWithDict::extra)
|
||||
.def("__getstate__", [](const py::object &self) {
|
||||
/* Also include __dict__ in state */
|
||||
return py::make_tuple(self.attr("value"), self.attr("extra"), self.attr("__dict__"));
|
||||
});
|
||||
ignoreOldStyleInitWarnings([&pyPickleableWithDict]() {
|
||||
pyPickleableWithDict.def("__setstate__", [](const py::object &self, const py::tuple &t) {
|
||||
if (t.size() != 3) {
|
||||
throw std::runtime_error("Invalid state!");
|
||||
}
|
||||
/* Cast and construct */
|
||||
auto &p = self.cast<PickleableWithDict &>();
|
||||
new (&p) PickleableWithDict(t[0].cast<std::string>());
|
||||
|
||||
/* Assign C++ state */
|
||||
p.extra = t[1].cast<int>();
|
||||
|
||||
/* Assign Python state */
|
||||
self.attr("__dict__") = t[2];
|
||||
});
|
||||
});
|
||||
|
||||
py::class_<PickleableWithDictNew, PickleableWithDict>(m, "PickleableWithDictNew")
|
||||
.def(py::init<std::string>())
|
||||
.def(py::pickle(
|
||||
[](const py::object &self) {
|
||||
return py::make_tuple(
|
||||
self.attr("value"), self.attr("extra"), self.attr("__dict__"));
|
||||
},
|
||||
[](const py::tuple &t) {
|
||||
if (t.size() != 3) {
|
||||
throw std::runtime_error("Invalid state!");
|
||||
}
|
||||
|
||||
auto cpp_state = PickleableWithDictNew(t[0].cast<std::string>());
|
||||
cpp_state.extra = t[1].cast<int>();
|
||||
|
||||
auto py_state = t[2].cast<py::dict>();
|
||||
return std::make_pair(cpp_state, py_state);
|
||||
}));
|
||||
#endif
|
||||
|
||||
exercise_trampoline::wrap(m);
|
||||
}
|
||||
95
third_party/pybind11/tests/test_pickling.py
vendored
Normal file
95
third_party/pybind11/tests/test_pickling.py
vendored
Normal file
@@ -0,0 +1,95 @@
|
||||
import pickle
|
||||
import re
|
||||
|
||||
import pytest
|
||||
|
||||
import env
|
||||
from pybind11_tests import pickling as m
|
||||
|
||||
|
||||
def test_pickle_simple_callable():
|
||||
assert m.simple_callable() == 20220426
|
||||
if env.PYPY:
|
||||
serialized = pickle.dumps(m.simple_callable)
|
||||
deserialized = pickle.loads(serialized)
|
||||
assert deserialized() == 20220426
|
||||
else:
|
||||
# To document broken behavior: currently it fails universally with
|
||||
# all C Python versions.
|
||||
with pytest.raises(TypeError) as excinfo:
|
||||
pickle.dumps(m.simple_callable)
|
||||
assert re.search("can.*t pickle .*PyCapsule.* object",
|
||||
str(excinfo.value))
|
||||
|
||||
|
||||
@pytest.mark.parametrize("cls_name", ["Pickleable", "PickleableNew"])
|
||||
def test_roundtrip(cls_name):
|
||||
cls = getattr(m, cls_name)
|
||||
p = cls("test_value")
|
||||
p.setExtra1(15)
|
||||
p.setExtra2(48)
|
||||
|
||||
data = pickle.dumps(p, 2) # Must use pickle protocol >= 2
|
||||
p2 = pickle.loads(data)
|
||||
assert p2.value() == p.value()
|
||||
assert p2.extra1() == p.extra1()
|
||||
assert p2.extra2() == p.extra2()
|
||||
|
||||
|
||||
@pytest.mark.xfail("env.PYPY")
|
||||
@pytest.mark.parametrize("cls_name",
|
||||
["PickleableWithDict", "PickleableWithDictNew"])
|
||||
def test_roundtrip_with_dict(cls_name):
|
||||
cls = getattr(m, cls_name)
|
||||
p = cls("test_value")
|
||||
p.extra = 15
|
||||
p.dynamic = "Attribute"
|
||||
|
||||
data = pickle.dumps(p, pickle.HIGHEST_PROTOCOL)
|
||||
p2 = pickle.loads(data)
|
||||
assert p2.value == p.value
|
||||
assert p2.extra == p.extra
|
||||
assert p2.dynamic == p.dynamic
|
||||
|
||||
|
||||
def test_enum_pickle():
|
||||
from pybind11_tests import enums as e
|
||||
|
||||
data = pickle.dumps(e.EOne, 2)
|
||||
assert e.EOne == pickle.loads(data)
|
||||
|
||||
|
||||
#
|
||||
# exercise_trampoline
|
||||
#
|
||||
class SimplePyDerived(m.SimpleBase):
|
||||
pass
|
||||
|
||||
|
||||
def test_roundtrip_simple_py_derived():
|
||||
p = SimplePyDerived()
|
||||
p.num = 202
|
||||
p.stored_in_dict = 303
|
||||
data = pickle.dumps(p, pickle.HIGHEST_PROTOCOL)
|
||||
p2 = pickle.loads(data)
|
||||
assert isinstance(p2, SimplePyDerived)
|
||||
assert p2.num == 202
|
||||
assert p2.stored_in_dict == 303
|
||||
|
||||
|
||||
def test_roundtrip_simple_cpp_derived():
|
||||
p = m.make_SimpleCppDerivedAsBase()
|
||||
assert m.check_dynamic_cast_SimpleCppDerived(p)
|
||||
p.num = 404
|
||||
if not env.PYPY:
|
||||
# To ensure that this unit test is not accidentally invalidated.
|
||||
with pytest.raises(AttributeError):
|
||||
# Mimics the `setstate` C++ implementation.
|
||||
setattr(p, "__dict__", {}) # noqa: B010
|
||||
data = pickle.dumps(p, pickle.HIGHEST_PROTOCOL)
|
||||
p2 = pickle.loads(data)
|
||||
assert isinstance(p2, m.SimpleBase)
|
||||
assert p2.num == 404
|
||||
# Issue #3062: pickleable base C++ classes can incur object slicing
|
||||
# if derived typeid is not registered with pybind11
|
||||
assert not m.check_dynamic_cast_SimpleCppDerived(p2)
|
||||
615
third_party/pybind11/tests/test_pytypes.cpp
vendored
Normal file
615
third_party/pybind11/tests/test_pytypes.cpp
vendored
Normal file
@@ -0,0 +1,615 @@
|
||||
/*
|
||||
tests/test_pytypes.cpp -- Python type casters
|
||||
|
||||
Copyright (c) 2017 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 <utility>
|
||||
|
||||
namespace external {
|
||||
namespace detail {
|
||||
bool check(PyObject *o) { return PyFloat_Check(o) != 0; }
|
||||
|
||||
PyObject *conv(PyObject *o) {
|
||||
PyObject *ret = nullptr;
|
||||
if (PyLong_Check(o)) {
|
||||
double v = PyLong_AsDouble(o);
|
||||
if (!(v == -1.0 && PyErr_Occurred())) {
|
||||
ret = PyFloat_FromDouble(v);
|
||||
}
|
||||
} else {
|
||||
PyErr_SetString(PyExc_TypeError, "Unexpected type");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
PyObject *default_constructed() { return PyFloat_FromDouble(0.0); }
|
||||
} // namespace detail
|
||||
class float_ : public py::object {
|
||||
PYBIND11_OBJECT_CVT(float_, py::object, external::detail::check, external::detail::conv)
|
||||
|
||||
float_() : py::object(external::detail::default_constructed(), stolen_t{}) {}
|
||||
|
||||
double get_value() const { return PyFloat_AsDouble(this->ptr()); }
|
||||
};
|
||||
} // namespace external
|
||||
|
||||
TEST_SUBMODULE(pytypes, m) {
|
||||
// test_bool
|
||||
m.def("get_bool", [] { return py::bool_(false); });
|
||||
// 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_float
|
||||
m.def("get_float", [] { return py::float_(0.0f); });
|
||||
// test_list
|
||||
m.def("list_no_args", []() { return py::list{}; });
|
||||
m.def("list_ssize_t", []() { return py::list{(py::ssize_t) 0}; });
|
||||
m.def("list_size_t", []() { return py::list{(py::size_t) 0}; });
|
||||
m.def("list_insert_ssize_t", [](py::list *l) { return l->insert((py::ssize_t) 1, 83); });
|
||||
m.def("list_insert_size_t", [](py::list *l) { return l->insert((py::size_t) 3, 57); });
|
||||
m.def("get_list", []() {
|
||||
py::list list;
|
||||
list.append("value");
|
||||
py::print("Entry at position 0:", list[0]);
|
||||
list[0] = py::str("overwritten");
|
||||
list.insert(0, "inserted-0");
|
||||
list.insert(2, "inserted-2");
|
||||
return list;
|
||||
});
|
||||
m.def("print_list", [](const py::list &list) {
|
||||
int index = 0;
|
||||
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", [](const py::none &none) { py::print("none: {}"_s.format(none)); });
|
||||
|
||||
// test_set, test_frozenset
|
||||
m.def("get_set", []() {
|
||||
py::set set;
|
||||
set.add(py::str("key1"));
|
||||
set.add("key2");
|
||||
set.add(std::string("key3"));
|
||||
return set;
|
||||
});
|
||||
m.def("get_frozenset", []() {
|
||||
py::set set;
|
||||
set.add(py::str("key1"));
|
||||
set.add("key2");
|
||||
set.add(std::string("key3"));
|
||||
return py::frozenset(set);
|
||||
});
|
||||
m.def("print_anyset", [](const py::anyset &set) {
|
||||
for (auto item : set) {
|
||||
py::print("key:", item);
|
||||
}
|
||||
});
|
||||
m.def("anyset_size", [](const py::anyset &set) { return set.size(); });
|
||||
m.def("anyset_empty", [](const py::anyset &set) { return set.empty(); });
|
||||
m.def("anyset_contains",
|
||||
[](const py::anyset &set, const py::object &key) { return set.contains(key); });
|
||||
m.def("anyset_contains",
|
||||
[](const py::anyset &set, const char *key) { return set.contains(key); });
|
||||
m.def("set_add", [](py::set &set, const py::object &key) { set.add(key); });
|
||||
m.def("set_clear", [](py::set &set) { set.clear(); });
|
||||
|
||||
// test_dict
|
||||
m.def("get_dict", []() { return py::dict("key"_a = "value"); });
|
||||
m.def("print_dict", [](const py::dict &dict) {
|
||||
for (auto item : dict) {
|
||||
py::print("key: {}, value={}"_s.format(item.first, item.second));
|
||||
}
|
||||
});
|
||||
m.def("dict_keyword_constructor", []() {
|
||||
auto d1 = py::dict("x"_a = 1, "y"_a = 2);
|
||||
auto d2 = py::dict("z"_a = 3, **d1);
|
||||
return d2;
|
||||
});
|
||||
m.def("dict_contains",
|
||||
[](const py::dict &dict, py::object val) { return dict.contains(val); });
|
||||
m.def("dict_contains",
|
||||
[](const py::dict &dict, const char *val) { return dict.contains(val); });
|
||||
|
||||
// test_tuple
|
||||
m.def("tuple_no_args", []() { return py::tuple{}; });
|
||||
m.def("tuple_ssize_t", []() { return py::tuple{(py::ssize_t) 0}; });
|
||||
m.def("tuple_size_t", []() { return py::tuple{(py::size_t) 0}; });
|
||||
m.def("get_tuple", []() { return py::make_tuple(42, py::none(), "spam"); });
|
||||
|
||||
// test_simple_namespace
|
||||
m.def("get_simple_namespace", []() {
|
||||
auto ns = py::module_::import("types").attr("SimpleNamespace")(
|
||||
"attr"_a = 42, "x"_a = "foo", "wrong"_a = 1);
|
||||
py::delattr(ns, "wrong");
|
||||
py::setattr(ns, "right", py::int_(2));
|
||||
return ns;
|
||||
});
|
||||
|
||||
// test_str
|
||||
m.def("str_from_char_ssize_t", []() { return py::str{"red", (py::ssize_t) 3}; });
|
||||
m.def("str_from_char_size_t", []() { return py::str{"blue", (py::size_t) 4}; });
|
||||
m.def("str_from_string", []() { return py::str(std::string("baz")); });
|
||||
m.def("str_from_bytes", []() { return py::str(py::bytes("boo", 3)); });
|
||||
m.def("str_from_object", [](const py::object &obj) { return py::str(obj); });
|
||||
m.def("repr_from_object", [](const py::object &obj) { return py::repr(obj); });
|
||||
m.def("str_from_handle", [](py::handle h) { return py::str(h); });
|
||||
m.def("str_from_string_from_str",
|
||||
[](const py::str &obj) { return py::str(static_cast<std::string>(obj)); });
|
||||
|
||||
m.def("str_format", []() {
|
||||
auto s1 = "{} + {} = {}"_s.format(1, 2, 3);
|
||||
auto s2 = "{a} + {b} = {c}"_s.format("a"_a = 1, "b"_a = 2, "c"_a = 3);
|
||||
return py::make_tuple(s1, s2);
|
||||
});
|
||||
|
||||
// test_bytes
|
||||
m.def("bytes_from_char_ssize_t", []() { return py::bytes{"green", (py::ssize_t) 5}; });
|
||||
m.def("bytes_from_char_size_t", []() { return py::bytes{"purple", (py::size_t) 6}; });
|
||||
m.def("bytes_from_string", []() { return py::bytes(std::string("foo")); });
|
||||
m.def("bytes_from_str", []() { return py::bytes(py::str("bar", 3)); });
|
||||
|
||||
// test bytearray
|
||||
m.def("bytearray_from_char_ssize_t", []() { return py::bytearray{"$%", (py::ssize_t) 2}; });
|
||||
m.def("bytearray_from_char_size_t", []() { return py::bytearray{"@$!", (py::size_t) 3}; });
|
||||
m.def("bytearray_from_string", []() { return py::bytearray(std::string("foo")); });
|
||||
m.def("bytearray_size", []() { return py::bytearray("foo").size(); });
|
||||
|
||||
// test_capsule
|
||||
m.def("return_capsule_with_destructor", []() {
|
||||
py::print("creating capsule");
|
||||
return py::capsule([]() { py::print("destructing capsule"); });
|
||||
});
|
||||
|
||||
m.def("return_renamed_capsule_with_destructor", []() {
|
||||
py::print("creating capsule");
|
||||
auto cap = py::capsule([]() { py::print("destructing capsule"); });
|
||||
static const char *capsule_name = "test_name1";
|
||||
py::print("renaming capsule");
|
||||
cap.set_name(capsule_name);
|
||||
return cap;
|
||||
});
|
||||
|
||||
m.def("return_capsule_with_destructor_2", []() {
|
||||
py::print("creating capsule");
|
||||
return py::capsule((void *) 1234, [](void *ptr) {
|
||||
py::print("destructing capsule: {}"_s.format((size_t) ptr));
|
||||
});
|
||||
});
|
||||
|
||||
m.def("return_renamed_capsule_with_destructor_2", []() {
|
||||
py::print("creating capsule");
|
||||
auto cap = py::capsule((void *) 1234, [](void *ptr) {
|
||||
py::print("destructing capsule: {}"_s.format((size_t) ptr));
|
||||
});
|
||||
static const char *capsule_name = "test_name2";
|
||||
py::print("renaming capsule");
|
||||
cap.set_name(capsule_name);
|
||||
return cap;
|
||||
});
|
||||
|
||||
m.def("return_capsule_with_name_and_destructor", []() {
|
||||
auto capsule = py::capsule((void *) 12345, "pointer type description", [](PyObject *ptr) {
|
||||
if (ptr) {
|
||||
const auto *name = PyCapsule_GetName(ptr);
|
||||
py::print("destructing capsule ({}, '{}')"_s.format(
|
||||
(size_t) PyCapsule_GetPointer(ptr, name), 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;
|
||||
});
|
||||
|
||||
// test_accessors
|
||||
m.def("accessor_api", [](const py::object &o) {
|
||||
auto d = py::dict();
|
||||
|
||||
d["basic_attr"] = o.attr("basic_attr");
|
||||
|
||||
auto l = py::list();
|
||||
for (auto item : o.attr("begin_end")) {
|
||||
l.append(item);
|
||||
}
|
||||
d["begin_end"] = l;
|
||||
|
||||
d["operator[object]"] = o.attr("d")["operator[object]"_s];
|
||||
d["operator[char *]"] = o.attr("d")["operator[char *]"];
|
||||
|
||||
d["attr(object)"] = o.attr("sub").attr("attr_obj");
|
||||
d["attr(char *)"] = o.attr("sub").attr("attr_char");
|
||||
try {
|
||||
o.attr("sub").attr("missing").ptr();
|
||||
} catch (const py::error_already_set &) {
|
||||
d["missing_attr_ptr"] = "raised"_s;
|
||||
}
|
||||
try {
|
||||
o.attr("missing").attr("doesn't matter");
|
||||
} catch (const py::error_already_set &) {
|
||||
d["missing_attr_chain"] = "raised"_s;
|
||||
}
|
||||
|
||||
d["is_none"] = o.attr("basic_attr").is_none();
|
||||
|
||||
d["operator()"] = o.attr("func")(1);
|
||||
d["operator*"] = o.attr("func")(*o.attr("begin_end"));
|
||||
|
||||
// Test implicit conversion
|
||||
py::list implicit_list = o.attr("begin_end");
|
||||
d["implicit_list"] = implicit_list;
|
||||
py::dict implicit_dict = o.attr("__dict__");
|
||||
d["implicit_dict"] = implicit_dict;
|
||||
|
||||
return d;
|
||||
});
|
||||
|
||||
m.def("tuple_accessor", [](const py::tuple &existing_t) {
|
||||
try {
|
||||
existing_t[0] = 1;
|
||||
} catch (const py::error_already_set &) {
|
||||
// --> Python system error
|
||||
// Only new tuples (refcount == 1) are mutable
|
||||
auto new_t = py::tuple(3);
|
||||
for (size_t i = 0; i < new_t.size(); ++i) {
|
||||
new_t[i] = i;
|
||||
}
|
||||
return new_t;
|
||||
}
|
||||
return py::tuple();
|
||||
});
|
||||
|
||||
m.def("accessor_assignment", []() {
|
||||
auto l = py::list(1);
|
||||
l[0] = 0;
|
||||
|
||||
auto d = py::dict();
|
||||
d["get"] = l[0];
|
||||
auto var = l[0];
|
||||
d["deferred_get"] = var;
|
||||
l[0] = 1;
|
||||
d["set"] = l[0];
|
||||
var = 99; // this assignment should not overwrite l[0]
|
||||
d["deferred_set"] = l[0];
|
||||
d["var"] = var;
|
||||
|
||||
return d;
|
||||
});
|
||||
|
||||
// test_constructors
|
||||
m.def("default_constructors", []() {
|
||||
return py::dict("bytes"_a = py::bytes(),
|
||||
"bytearray"_a = py::bytearray(),
|
||||
"str"_a = py::str(),
|
||||
"bool"_a = py::bool_(),
|
||||
"int"_a = py::int_(),
|
||||
"float"_a = py::float_(),
|
||||
"tuple"_a = py::tuple(),
|
||||
"list"_a = py::list(),
|
||||
"dict"_a = py::dict(),
|
||||
"set"_a = py::set());
|
||||
});
|
||||
|
||||
m.def("converting_constructors", [](const py::dict &d) {
|
||||
return py::dict("bytes"_a = py::bytes(d["bytes"]),
|
||||
"bytearray"_a = py::bytearray(d["bytearray"]),
|
||||
"str"_a = py::str(d["str"]),
|
||||
"bool"_a = py::bool_(d["bool"]),
|
||||
"int"_a = py::int_(d["int"]),
|
||||
"float"_a = py::float_(d["float"]),
|
||||
"tuple"_a = py::tuple(d["tuple"]),
|
||||
"list"_a = py::list(d["list"]),
|
||||
"dict"_a = py::dict(d["dict"]),
|
||||
"set"_a = py::set(d["set"]),
|
||||
"frozenset"_a = py::frozenset(d["frozenset"]),
|
||||
"memoryview"_a = py::memoryview(d["memoryview"]));
|
||||
});
|
||||
|
||||
m.def("cast_functions", [](const py::dict &d) {
|
||||
// When converting between Python types, obj.cast<T>() should be the same as T(obj)
|
||||
return py::dict("bytes"_a = d["bytes"].cast<py::bytes>(),
|
||||
"bytearray"_a = d["bytearray"].cast<py::bytearray>(),
|
||||
"str"_a = d["str"].cast<py::str>(),
|
||||
"bool"_a = d["bool"].cast<py::bool_>(),
|
||||
"int"_a = d["int"].cast<py::int_>(),
|
||||
"float"_a = d["float"].cast<py::float_>(),
|
||||
"tuple"_a = d["tuple"].cast<py::tuple>(),
|
||||
"list"_a = d["list"].cast<py::list>(),
|
||||
"dict"_a = d["dict"].cast<py::dict>(),
|
||||
"set"_a = d["set"].cast<py::set>(),
|
||||
"frozenset"_a = d["frozenset"].cast<py::frozenset>(),
|
||||
"memoryview"_a = d["memoryview"].cast<py::memoryview>());
|
||||
});
|
||||
|
||||
m.def("convert_to_pybind11_str", [](const py::object &o) { return py::str(o); });
|
||||
|
||||
m.def("nonconverting_constructor",
|
||||
[](const std::string &type, py::object value, bool move) -> py::object {
|
||||
if (type == "bytes") {
|
||||
return move ? py::bytes(std::move(value)) : py::bytes(value);
|
||||
}
|
||||
if (type == "none") {
|
||||
return move ? py::none(std::move(value)) : py::none(value);
|
||||
}
|
||||
if (type == "ellipsis") {
|
||||
return move ? py::ellipsis(std::move(value)) : py::ellipsis(value);
|
||||
}
|
||||
if (type == "type") {
|
||||
return move ? py::type(std::move(value)) : py::type(value);
|
||||
}
|
||||
throw std::runtime_error("Invalid type");
|
||||
});
|
||||
|
||||
m.def("get_implicit_casting", []() {
|
||||
py::dict d;
|
||||
d["char*_i1"] = "abc";
|
||||
const char *c2 = "abc";
|
||||
d["char*_i2"] = c2;
|
||||
d["char*_e"] = py::cast(c2);
|
||||
d["char*_p"] = py::str(c2);
|
||||
|
||||
d["int_i1"] = 42;
|
||||
int i = 42;
|
||||
d["int_i2"] = i;
|
||||
i++;
|
||||
d["int_e"] = py::cast(i);
|
||||
i++;
|
||||
d["int_p"] = py::int_(i);
|
||||
|
||||
d["str_i1"] = std::string("str");
|
||||
std::string s2("str1");
|
||||
d["str_i2"] = s2;
|
||||
s2[3] = '2';
|
||||
d["str_e"] = py::cast(s2);
|
||||
s2[3] = '3';
|
||||
d["str_p"] = py::str(s2);
|
||||
|
||||
py::list l(2);
|
||||
l[0] = 3;
|
||||
l[1] = py::cast(6);
|
||||
l.append(9);
|
||||
l.append(py::cast(12));
|
||||
l.append(py::int_(15));
|
||||
|
||||
return py::dict("d"_a = d, "l"_a = l);
|
||||
});
|
||||
|
||||
// test_print
|
||||
m.def("print_function", []() {
|
||||
py::print("Hello, World!");
|
||||
py::print(1, 2.0, "three", true, std::string("-- multiple args"));
|
||||
auto args = py::make_tuple("and", "a", "custom", "separator");
|
||||
py::print("*args", *args, "sep"_a = "-");
|
||||
py::print("no new line here", "end"_a = " -- ");
|
||||
py::print("next print");
|
||||
|
||||
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);
|
||||
|
||||
py::print(
|
||||
"{a} + {b} = {c}"_s.format("a"_a = "py::print", "b"_a = "str.format", "c"_a = "this"));
|
||||
});
|
||||
|
||||
m.def("print_failure", []() { py::print(42, UnregisteredType()); });
|
||||
|
||||
m.def("hash_function", [](py::object obj) { return py::hash(std::move(obj)); });
|
||||
|
||||
m.def("test_number_protocol", [](const py::object &a, const py::object &b) {
|
||||
py::list l;
|
||||
l.append(a.equal(b));
|
||||
l.append(a.not_equal(b));
|
||||
l.append(a < b);
|
||||
l.append(a <= b);
|
||||
l.append(a > b);
|
||||
l.append(a >= b);
|
||||
l.append(a + b);
|
||||
l.append(a - b);
|
||||
l.append(a * b);
|
||||
l.append(a / b);
|
||||
l.append(a | b);
|
||||
l.append(a & b);
|
||||
l.append(a ^ b);
|
||||
l.append(a >> b);
|
||||
l.append(a << b);
|
||||
return l;
|
||||
});
|
||||
|
||||
m.def("test_list_slicing", [](const py::list &a) { return a[py::slice(0, -1, 2)]; });
|
||||
|
||||
// See #2361
|
||||
m.def("issue2361_str_implicit_copy_none", []() {
|
||||
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", [](const py::buffer &b) { return py::memoryview(b); });
|
||||
|
||||
m.def("test_memoryview_buffer_info",
|
||||
[](const py::buffer &b) { return py::memoryview(b.request()); });
|
||||
|
||||
m.def("test_memoryview_from_buffer", [](bool is_unsigned) {
|
||||
static const int16_t si16[] = {3, 1, 4, 1, 5};
|
||||
static const uint16_t ui16[] = {2, 7, 1, 8};
|
||||
if (is_unsigned) {
|
||||
return py::memoryview::from_buffer(ui16, {4}, {sizeof(uint16_t)});
|
||||
}
|
||||
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", {}, {});
|
||||
});
|
||||
|
||||
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)));
|
||||
});
|
||||
|
||||
// test_builtin_functions
|
||||
m.def("get_len", [](py::handle h) { return py::len(h); });
|
||||
|
||||
#ifdef PYBIND11_STR_LEGACY_PERMISSIVE
|
||||
m.attr("PYBIND11_STR_LEGACY_PERMISSIVE") = true;
|
||||
#endif
|
||||
|
||||
m.def("isinstance_pybind11_bytes",
|
||||
[](py::object o) { return py::isinstance<py::bytes>(std::move(o)); });
|
||||
m.def("isinstance_pybind11_str",
|
||||
[](py::object o) { return py::isinstance<py::str>(std::move(o)); });
|
||||
|
||||
m.def("pass_to_pybind11_bytes", [](py::bytes b) { return py::len(std::move(b)); });
|
||||
m.def("pass_to_pybind11_str", [](py::str s) { return py::len(std::move(s)); });
|
||||
m.def("pass_to_std_string", [](const std::string &s) { return s.size(); });
|
||||
|
||||
// test_weakref
|
||||
m.def("weakref_from_handle", [](py::handle h) { return py::weakref(h); });
|
||||
m.def("weakref_from_handle_and_function",
|
||||
[](py::handle h, py::function f) { return py::weakref(h, std::move(f)); });
|
||||
m.def("weakref_from_object", [](const py::object &o) { return py::weakref(o); });
|
||||
m.def("weakref_from_object_and_function",
|
||||
[](py::object o, py::function f) { return py::weakref(std::move(o), std::move(f)); });
|
||||
|
||||
// See PR #3263 for background (https://github.com/pybind/pybind11/pull/3263):
|
||||
// pytypes.h could be changed to enforce the "most correct" user code below, by removing
|
||||
// `const` from iterator `reference` using type aliases, but that will break existing
|
||||
// user code.
|
||||
#if (defined(__APPLE__) && defined(__clang__)) || defined(PYPY_VERSION)
|
||||
// This is "most correct" and enforced on these platforms.
|
||||
# define PYBIND11_AUTO_IT auto it
|
||||
#else
|
||||
// This works on many platforms and is (unfortunately) reflective of existing user code.
|
||||
// NOLINTNEXTLINE(bugprone-macro-parentheses)
|
||||
# define PYBIND11_AUTO_IT auto &it
|
||||
#endif
|
||||
|
||||
m.def("tuple_iterator", []() {
|
||||
auto tup = py::make_tuple(5, 7);
|
||||
int tup_sum = 0;
|
||||
for (PYBIND11_AUTO_IT : tup) {
|
||||
tup_sum += it.cast<int>();
|
||||
}
|
||||
return tup_sum;
|
||||
});
|
||||
|
||||
m.def("dict_iterator", []() {
|
||||
py::dict dct;
|
||||
dct[py::int_(3)] = 5;
|
||||
dct[py::int_(7)] = 11;
|
||||
int kv_sum = 0;
|
||||
for (PYBIND11_AUTO_IT : dct) {
|
||||
kv_sum += it.first.cast<int>() * 100 + it.second.cast<int>();
|
||||
}
|
||||
return kv_sum;
|
||||
});
|
||||
|
||||
m.def("passed_iterator", [](const py::iterator &py_it) {
|
||||
int elem_sum = 0;
|
||||
for (PYBIND11_AUTO_IT : py_it) {
|
||||
elem_sum += it.cast<int>();
|
||||
}
|
||||
return elem_sum;
|
||||
});
|
||||
|
||||
#undef PYBIND11_AUTO_IT
|
||||
|
||||
// Tests below this line are for pybind11 IMPLEMENTATION DETAILS:
|
||||
|
||||
m.def("sequence_item_get_ssize_t", [](const py::object &o) {
|
||||
return py::detail::accessor_policies::sequence_item::get(o, (py::ssize_t) 1);
|
||||
});
|
||||
m.def("sequence_item_set_ssize_t", [](const py::object &o) {
|
||||
auto s = py::str{"peppa", 5};
|
||||
py::detail::accessor_policies::sequence_item::set(o, (py::ssize_t) 1, s);
|
||||
});
|
||||
m.def("sequence_item_get_size_t", [](const py::object &o) {
|
||||
return py::detail::accessor_policies::sequence_item::get(o, (py::size_t) 2);
|
||||
});
|
||||
m.def("sequence_item_set_size_t", [](const py::object &o) {
|
||||
auto s = py::str{"george", 6};
|
||||
py::detail::accessor_policies::sequence_item::set(o, (py::size_t) 2, s);
|
||||
});
|
||||
m.def("list_item_get_ssize_t", [](const py::object &o) {
|
||||
return py::detail::accessor_policies::list_item::get(o, (py::ssize_t) 3);
|
||||
});
|
||||
m.def("list_item_set_ssize_t", [](const py::object &o) {
|
||||
auto s = py::str{"rebecca", 7};
|
||||
py::detail::accessor_policies::list_item::set(o, (py::ssize_t) 3, s);
|
||||
});
|
||||
m.def("list_item_get_size_t", [](const py::object &o) {
|
||||
return py::detail::accessor_policies::list_item::get(o, (py::size_t) 4);
|
||||
});
|
||||
m.def("list_item_set_size_t", [](const py::object &o) {
|
||||
auto s = py::str{"richard", 7};
|
||||
py::detail::accessor_policies::list_item::set(o, (py::size_t) 4, s);
|
||||
});
|
||||
m.def("tuple_item_get_ssize_t", [](const py::object &o) {
|
||||
return py::detail::accessor_policies::tuple_item::get(o, (py::ssize_t) 5);
|
||||
});
|
||||
m.def("tuple_item_set_ssize_t", []() {
|
||||
auto s0 = py::str{"emely", 5};
|
||||
auto s1 = py::str{"edmond", 6};
|
||||
auto o = py::tuple{2};
|
||||
py::detail::accessor_policies::tuple_item::set(o, (py::ssize_t) 0, s0);
|
||||
py::detail::accessor_policies::tuple_item::set(o, (py::ssize_t) 1, s1);
|
||||
return o;
|
||||
});
|
||||
m.def("tuple_item_get_size_t", [](const py::object &o) {
|
||||
return py::detail::accessor_policies::tuple_item::get(o, (py::size_t) 6);
|
||||
});
|
||||
m.def("tuple_item_set_size_t", []() {
|
||||
auto s0 = py::str{"candy", 5};
|
||||
auto s1 = py::str{"cat", 3};
|
||||
auto o = py::tuple{2};
|
||||
py::detail::accessor_policies::tuple_item::set(o, (py::size_t) 1, s1);
|
||||
py::detail::accessor_policies::tuple_item::set(o, (py::size_t) 0, s0);
|
||||
return o;
|
||||
});
|
||||
|
||||
m.def("square_float_", [](const external::float_ &x) -> double {
|
||||
double v = x.get_value();
|
||||
return v * v;
|
||||
});
|
||||
}
|
||||
667
third_party/pybind11/tests/test_pytypes.py
vendored
Normal file
667
third_party/pybind11/tests/test_pytypes.py
vendored
Normal file
@@ -0,0 +1,667 @@
|
||||
import contextlib
|
||||
import sys
|
||||
|
||||
import pytest
|
||||
|
||||
import env
|
||||
from pybind11_tests import detailed_error_messages_enabled
|
||||
from pybind11_tests import pytypes as m
|
||||
|
||||
|
||||
def test_bool(doc):
|
||||
assert doc(m.get_bool) == "get_bool() -> bool"
|
||||
|
||||
|
||||
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_float(doc):
|
||||
assert doc(m.get_float) == "get_float() -> float"
|
||||
|
||||
|
||||
def test_list(capture, doc):
|
||||
assert m.list_no_args() == []
|
||||
assert m.list_ssize_t() == []
|
||||
assert m.list_size_t() == []
|
||||
lins = [1, 2]
|
||||
m.list_insert_ssize_t(lins)
|
||||
assert lins == [1, 83, 2]
|
||||
m.list_insert_size_t(lins)
|
||||
assert lins == [1, 83, 2, 57]
|
||||
|
||||
with capture:
|
||||
lst = m.get_list()
|
||||
assert lst == ["inserted-0", "overwritten", "inserted-2"]
|
||||
|
||||
lst.append("value2")
|
||||
m.print_list(lst)
|
||||
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 isinstance(s, set)
|
||||
assert s == {"key1", "key2", "key3"}
|
||||
|
||||
s.add("key4")
|
||||
with capture:
|
||||
m.print_anyset(s)
|
||||
assert (capture.unordered == """
|
||||
key: key1
|
||||
key: key2
|
||||
key: key3
|
||||
key: key4
|
||||
""")
|
||||
|
||||
m.set_add(s, "key5")
|
||||
assert m.anyset_size(s) == 5
|
||||
|
||||
m.set_clear(s)
|
||||
assert m.anyset_empty(s)
|
||||
|
||||
assert not m.anyset_contains(set(), 42)
|
||||
assert m.anyset_contains({42}, 42)
|
||||
assert m.anyset_contains({"foo"}, "foo")
|
||||
|
||||
assert doc(m.get_set) == "get_set() -> set"
|
||||
assert doc(m.print_anyset) == "print_anyset(arg0: anyset) -> None"
|
||||
|
||||
|
||||
def test_frozenset(capture, doc):
|
||||
s = m.get_frozenset()
|
||||
assert isinstance(s, frozenset)
|
||||
assert s == frozenset({"key1", "key2", "key3"})
|
||||
|
||||
with capture:
|
||||
m.print_anyset(s)
|
||||
assert (capture.unordered == """
|
||||
key: key1
|
||||
key: key2
|
||||
key: key3
|
||||
""")
|
||||
assert m.anyset_size(s) == 3
|
||||
assert not m.anyset_empty(s)
|
||||
|
||||
assert not m.anyset_contains(frozenset(), 42)
|
||||
assert m.anyset_contains(frozenset({42}), 42)
|
||||
assert m.anyset_contains(frozenset({"foo"}), "foo")
|
||||
|
||||
assert doc(m.get_frozenset) == "get_frozenset() -> frozenset"
|
||||
|
||||
|
||||
def test_dict(capture, doc):
|
||||
d = m.get_dict()
|
||||
assert d == {"key": "value"}
|
||||
|
||||
with capture:
|
||||
d["key2"] = "value2"
|
||||
m.print_dict(d)
|
||||
assert (capture.unordered == """
|
||||
key: key, value=value
|
||||
key: key2, value=value2
|
||||
""")
|
||||
|
||||
assert not m.dict_contains({}, 42)
|
||||
assert m.dict_contains({42: None}, 42)
|
||||
assert m.dict_contains({"foo": None}, "foo")
|
||||
|
||||
assert doc(m.get_dict) == "get_dict() -> dict"
|
||||
assert doc(m.print_dict) == "print_dict(arg0: dict) -> None"
|
||||
|
||||
assert m.dict_keyword_constructor() == {"x": 1, "y": 2, "z": 3}
|
||||
|
||||
|
||||
def test_tuple():
|
||||
assert m.tuple_no_args() == ()
|
||||
assert m.tuple_ssize_t() == ()
|
||||
assert m.tuple_size_t() == ()
|
||||
assert m.get_tuple() == (42, None, "spam")
|
||||
|
||||
|
||||
def test_simple_namespace():
|
||||
ns = m.get_simple_namespace()
|
||||
assert ns.attr == 42
|
||||
assert ns.x == "foo"
|
||||
assert ns.right == 2
|
||||
assert not hasattr(ns, "wrong")
|
||||
|
||||
|
||||
def test_str(doc):
|
||||
assert m.str_from_char_ssize_t().encode().decode() == "red"
|
||||
assert m.str_from_char_size_t().encode().decode() == "blue"
|
||||
assert m.str_from_string().encode().decode() == "baz"
|
||||
assert m.str_from_bytes().encode().decode() == "boo"
|
||||
|
||||
assert doc(m.str_from_bytes) == "str_from_bytes() -> str"
|
||||
|
||||
class A:
|
||||
def __str__(self):
|
||||
return "this is a str"
|
||||
|
||||
def __repr__(self):
|
||||
return "this is a repr"
|
||||
|
||||
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"
|
||||
if hasattr(m, "PYBIND11_STR_LEGACY_PERMISSIVE"):
|
||||
assert m.str_from_object(malformed_utf8) is malformed_utf8
|
||||
else:
|
||||
assert m.str_from_object(malformed_utf8) == "b'\\x80'"
|
||||
assert m.str_from_handle(malformed_utf8) == "b'\\x80'"
|
||||
|
||||
assert m.str_from_string_from_str("this is a str") == "this is a str"
|
||||
ucs_surrogates_str = "\udcc3"
|
||||
with pytest.raises(UnicodeEncodeError):
|
||||
m.str_from_string_from_str(ucs_surrogates_str)
|
||||
|
||||
|
||||
def test_bytes(doc):
|
||||
assert m.bytes_from_char_ssize_t().decode() == "green"
|
||||
assert m.bytes_from_char_size_t().decode() == "purple"
|
||||
assert m.bytes_from_string().decode() == "foo"
|
||||
assert m.bytes_from_str().decode() == "bar"
|
||||
|
||||
assert doc(m.bytes_from_str) == "bytes_from_str() -> bytes"
|
||||
|
||||
|
||||
def test_bytearray(doc):
|
||||
assert m.bytearray_from_char_ssize_t().decode() == "$%"
|
||||
assert m.bytearray_from_char_size_t().decode() == "@$!"
|
||||
assert m.bytearray_from_string().decode() == "foo"
|
||||
assert m.bytearray_size() == len("foo")
|
||||
|
||||
|
||||
def test_capsule(capture):
|
||||
pytest.gc_collect()
|
||||
with capture:
|
||||
a = m.return_capsule_with_destructor()
|
||||
del a
|
||||
pytest.gc_collect()
|
||||
assert (capture.unordered == """
|
||||
creating capsule
|
||||
destructing capsule
|
||||
""")
|
||||
|
||||
with capture:
|
||||
a = m.return_renamed_capsule_with_destructor()
|
||||
del a
|
||||
pytest.gc_collect()
|
||||
assert (capture.unordered == """
|
||||
creating capsule
|
||||
renaming capsule
|
||||
destructing capsule
|
||||
""")
|
||||
|
||||
with capture:
|
||||
a = m.return_capsule_with_destructor_2()
|
||||
del a
|
||||
pytest.gc_collect()
|
||||
assert (capture.unordered == """
|
||||
creating capsule
|
||||
destructing capsule: 1234
|
||||
""")
|
||||
|
||||
with capture:
|
||||
a = m.return_renamed_capsule_with_destructor_2()
|
||||
del a
|
||||
pytest.gc_collect()
|
||||
assert (capture.unordered == """
|
||||
creating capsule
|
||||
renaming capsule
|
||||
destructing capsule: 1234
|
||||
""")
|
||||
|
||||
with capture:
|
||||
a = m.return_capsule_with_name_and_destructor()
|
||||
del a
|
||||
pytest.gc_collect()
|
||||
assert (capture.unordered == """
|
||||
created capsule (1234, 'pointer type description')
|
||||
destructing capsule (1234, 'pointer type description')
|
||||
""")
|
||||
|
||||
|
||||
def test_accessors():
|
||||
class SubTestObject:
|
||||
attr_obj = 1
|
||||
attr_char = 2
|
||||
|
||||
class TestObject:
|
||||
basic_attr = 1
|
||||
begin_end = [1, 2, 3]
|
||||
d = {"operator[object]": 1, "operator[char *]": 2}
|
||||
sub = SubTestObject()
|
||||
|
||||
def func(self, x, *args):
|
||||
return self.basic_attr + x + sum(args)
|
||||
|
||||
d = m.accessor_api(TestObject())
|
||||
assert d["basic_attr"] == 1
|
||||
assert d["begin_end"] == [1, 2, 3]
|
||||
assert d["operator[object]"] == 1
|
||||
assert d["operator[char *]"] == 2
|
||||
assert d["attr(object)"] == 1
|
||||
assert d["attr(char *)"] == 2
|
||||
assert d["missing_attr_ptr"] == "raised"
|
||||
assert d["missing_attr_chain"] == "raised"
|
||||
assert d["is_none"] is False
|
||||
assert d["operator()"] == 2
|
||||
assert d["operator*"] == 7
|
||||
assert d["implicit_list"] == [1, 2, 3]
|
||||
assert all(x in TestObject.__dict__ for x in d["implicit_dict"])
|
||||
|
||||
assert m.tuple_accessor(tuple()) == (0, 1, 2)
|
||||
|
||||
d = m.accessor_assignment()
|
||||
assert d["get"] == 0
|
||||
assert d["deferred_get"] == 0
|
||||
assert d["set"] == 1
|
||||
assert d["deferred_set"] == 1
|
||||
assert d["var"] == 99
|
||||
|
||||
|
||||
def test_constructors():
|
||||
"""C++ default and converting constructors are equivalent to type calls in Python"""
|
||||
types = [bytes, bytearray, str, bool, int, float, tuple, list, dict, set]
|
||||
expected = {t.__name__: t() for t in types}
|
||||
assert m.default_constructors() == expected
|
||||
|
||||
data = {
|
||||
bytes: b"41", # Currently no supported or working conversions.
|
||||
bytearray: bytearray(b"41"),
|
||||
str: 42,
|
||||
bool: "Not empty",
|
||||
int: "42",
|
||||
float: "+1e3",
|
||||
tuple: range(3),
|
||||
list: range(3),
|
||||
dict: [("two", 2), ("one", 1), ("three", 3)],
|
||||
set: [4, 4, 5, 6, 6, 6],
|
||||
frozenset: [4, 4, 5, 6, 6, 6],
|
||||
memoryview: b"abc",
|
||||
}
|
||||
inputs = {k.__name__: v for k, v in data.items()}
|
||||
expected = {k.__name__: k(v) for k, v in data.items()}
|
||||
|
||||
assert m.converting_constructors(inputs) == expected
|
||||
assert m.cast_functions(inputs) == expected
|
||||
|
||||
# Converting constructors and cast functions should just reference rather
|
||||
# than copy when no conversion is needed:
|
||||
noconv1 = m.converting_constructors(expected)
|
||||
for k in noconv1:
|
||||
assert noconv1[k] is expected[k]
|
||||
|
||||
noconv2 = m.cast_functions(expected)
|
||||
for k in noconv2:
|
||||
assert noconv2[k] is expected[k]
|
||||
|
||||
|
||||
def test_non_converting_constructors():
|
||||
non_converting_test_cases = [
|
||||
("bytes", range(10)),
|
||||
("none", 42),
|
||||
("ellipsis", 42),
|
||||
("type", 42),
|
||||
]
|
||||
for t, v in non_converting_test_cases:
|
||||
for move in [True, False]:
|
||||
with pytest.raises(TypeError) as excinfo:
|
||||
m.nonconverting_constructor(t, v, move)
|
||||
expected_error = (
|
||||
f"Object of type '{type(v).__name__}' is not an instance of '{t}'"
|
||||
)
|
||||
assert str(excinfo.value) == expected_error
|
||||
|
||||
|
||||
def test_pybind11_str_raw_str():
|
||||
# specifically to exercise pybind11::str::raw_str
|
||||
cvt = m.convert_to_pybind11_str
|
||||
assert cvt("Str") == "Str"
|
||||
assert cvt(b"Bytes") == "b'Bytes'"
|
||||
assert cvt(None) == "None"
|
||||
assert cvt(False) == "False"
|
||||
assert cvt(True) == "True"
|
||||
assert cvt(42) == "42"
|
||||
assert cvt(2**65) == "36893488147419103232"
|
||||
assert cvt(-1.50) == "-1.5"
|
||||
assert cvt(()) == "()"
|
||||
assert cvt((18, )) == "(18,)"
|
||||
assert cvt([]) == "[]"
|
||||
assert cvt([28]) == "[28]"
|
||||
assert cvt({}) == "{}"
|
||||
assert cvt({3: 4}) == "{3: 4}"
|
||||
assert cvt(set()) == "set()"
|
||||
assert cvt({3, 3}) == "{3}"
|
||||
|
||||
valid_orig = "DZ"
|
||||
valid_utf8 = valid_orig.encode("utf-8")
|
||||
valid_cvt = cvt(valid_utf8)
|
||||
if hasattr(m, "PYBIND11_STR_LEGACY_PERMISSIVE"):
|
||||
assert valid_cvt is valid_utf8
|
||||
else:
|
||||
assert type(valid_cvt) is str
|
||||
assert valid_cvt == "b'\\xc7\\xb1'"
|
||||
|
||||
malformed_utf8 = b"\x80"
|
||||
if hasattr(m, "PYBIND11_STR_LEGACY_PERMISSIVE"):
|
||||
assert cvt(malformed_utf8) is malformed_utf8
|
||||
else:
|
||||
malformed_cvt = cvt(malformed_utf8)
|
||||
assert type(malformed_cvt) is str
|
||||
assert malformed_cvt == "b'\\x80'"
|
||||
|
||||
|
||||
def test_implicit_casting():
|
||||
"""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["l"] == [3, 6, 9, 12, 15]
|
||||
|
||||
|
||||
def test_print(capture):
|
||||
with capture:
|
||||
m.print_function()
|
||||
assert (capture == """
|
||||
Hello, World!
|
||||
1 2.0 three True -- multiple args
|
||||
*args-and-a-custom-separator
|
||||
no new line here -- next print
|
||||
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) == "Unable to convert call argument " + (
|
||||
"'1' of type 'UnregisteredType' to Python object"
|
||||
if detailed_error_messages_enabled else
|
||||
"to Python object (#define PYBIND11_DETAILED_ERROR_MESSAGES or compile in debug mode for details)"
|
||||
)
|
||||
|
||||
|
||||
def test_hash():
|
||||
class Hashable:
|
||||
def __init__(self, value):
|
||||
self.value = value
|
||||
|
||||
def __hash__(self):
|
||||
return self.value
|
||||
|
||||
class Unhashable:
|
||||
__hash__ = None
|
||||
|
||||
assert m.hash_function(Hashable(42)) == 42
|
||||
with pytest.raises(TypeError):
|
||||
m.hash_function(Unhashable())
|
||||
|
||||
|
||||
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,
|
||||
]
|
||||
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" in str(excinfo.value)
|
||||
assert "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
|
||||
assert list(view) == 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"
|
||||
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():
|
||||
with pytest.raises(ValueError):
|
||||
m.test_memoryview_from_buffer_nullptr()
|
||||
|
||||
|
||||
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
|
||||
|
||||
|
||||
def test_isinstance_string_types():
|
||||
assert m.isinstance_pybind11_bytes(b"")
|
||||
assert not m.isinstance_pybind11_bytes("")
|
||||
|
||||
assert m.isinstance_pybind11_str("")
|
||||
if hasattr(m, "PYBIND11_STR_LEGACY_PERMISSIVE"):
|
||||
assert m.isinstance_pybind11_str(b"")
|
||||
else:
|
||||
assert not m.isinstance_pybind11_str(b"")
|
||||
|
||||
|
||||
def test_pass_bytes_or_unicode_to_string_types():
|
||||
assert m.pass_to_pybind11_bytes(b"Bytes") == 5
|
||||
with pytest.raises(TypeError):
|
||||
m.pass_to_pybind11_bytes("Str")
|
||||
|
||||
if hasattr(m, "PYBIND11_STR_LEGACY_PERMISSIVE"):
|
||||
assert m.pass_to_pybind11_str(b"Bytes") == 5
|
||||
else:
|
||||
with pytest.raises(TypeError):
|
||||
m.pass_to_pybind11_str(b"Bytes")
|
||||
assert m.pass_to_pybind11_str("Str") == 3
|
||||
|
||||
assert m.pass_to_std_string(b"Bytes") == 5
|
||||
assert m.pass_to_std_string("Str") == 3
|
||||
|
||||
malformed_utf8 = b"\x80"
|
||||
if hasattr(m, "PYBIND11_STR_LEGACY_PERMISSIVE"):
|
||||
assert m.pass_to_pybind11_str(malformed_utf8) == 1
|
||||
else:
|
||||
with pytest.raises(TypeError):
|
||||
m.pass_to_pybind11_str(malformed_utf8)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"create_weakref, create_weakref_with_callback",
|
||||
[
|
||||
(m.weakref_from_handle, m.weakref_from_handle_and_function),
|
||||
(m.weakref_from_object, m.weakref_from_object_and_function),
|
||||
], )
|
||||
def test_weakref(create_weakref, create_weakref_with_callback):
|
||||
from weakref import getweakrefcount
|
||||
|
||||
# Apparently, you cannot weakly reference an object()
|
||||
class WeaklyReferenced:
|
||||
pass
|
||||
|
||||
callback_called = False
|
||||
|
||||
def callback(wr):
|
||||
nonlocal callback_called
|
||||
callback_called = True
|
||||
|
||||
obj = WeaklyReferenced()
|
||||
assert getweakrefcount(obj) == 0
|
||||
wr = create_weakref(obj)
|
||||
assert getweakrefcount(obj) == 1
|
||||
|
||||
obj = WeaklyReferenced()
|
||||
assert getweakrefcount(obj) == 0
|
||||
wr = create_weakref_with_callback(obj, callback) # noqa: F841
|
||||
assert getweakrefcount(obj) == 1
|
||||
assert not callback_called
|
||||
del obj
|
||||
pytest.gc_collect()
|
||||
assert callback_called
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"create_weakref, has_callback",
|
||||
[
|
||||
(m.weakref_from_handle, False),
|
||||
(m.weakref_from_object, False),
|
||||
(m.weakref_from_handle_and_function, True),
|
||||
(m.weakref_from_object_and_function, True),
|
||||
], )
|
||||
def test_weakref_err(create_weakref, has_callback):
|
||||
class C:
|
||||
__slots__ = []
|
||||
|
||||
def callback(_):
|
||||
pass
|
||||
|
||||
ob = C()
|
||||
# Should raise TypeError on CPython
|
||||
with pytest.raises(TypeError) if not env.PYPY else contextlib.nullcontext(
|
||||
):
|
||||
if has_callback:
|
||||
_ = create_weakref(ob, callback)
|
||||
else:
|
||||
_ = create_weakref(ob)
|
||||
|
||||
|
||||
def test_cpp_iterators():
|
||||
assert m.tuple_iterator() == 12
|
||||
assert m.dict_iterator() == 305 + 711
|
||||
assert m.passed_iterator(iter((-7, 3))) == -4
|
||||
|
||||
|
||||
def test_implementation_details():
|
||||
lst = [39, 43, 92, 49, 22, 29, 93, 98, 26, 57, 8]
|
||||
tup = tuple(lst)
|
||||
assert m.sequence_item_get_ssize_t(lst) == 43
|
||||
assert m.sequence_item_set_ssize_t(lst) is None
|
||||
assert lst[1] == "peppa"
|
||||
assert m.sequence_item_get_size_t(lst) == 92
|
||||
assert m.sequence_item_set_size_t(lst) is None
|
||||
assert lst[2] == "george"
|
||||
assert m.list_item_get_ssize_t(lst) == 49
|
||||
assert m.list_item_set_ssize_t(lst) is None
|
||||
assert lst[3] == "rebecca"
|
||||
assert m.list_item_get_size_t(lst) == 22
|
||||
assert m.list_item_set_size_t(lst) is None
|
||||
assert lst[4] == "richard"
|
||||
assert m.tuple_item_get_ssize_t(tup) == 29
|
||||
assert m.tuple_item_set_ssize_t() == ("emely", "edmond")
|
||||
assert m.tuple_item_get_size_t(tup) == 93
|
||||
assert m.tuple_item_set_size_t() == ("candy", "cat")
|
||||
|
||||
|
||||
def test_external_float_():
|
||||
r1 = m.square_float_(2.0)
|
||||
assert r1 == 4.0
|
||||
562
third_party/pybind11/tests/test_sequences_and_iterators.cpp
vendored
Normal file
562
third_party/pybind11/tests/test_sequences_and_iterators.cpp
vendored
Normal file
@@ -0,0 +1,562 @@
|
||||
/*
|
||||
tests/test_sequences_and_iterators.cpp -- supporting Pythons' sequence protocol, iterators,
|
||||
etc.
|
||||
|
||||
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/operators.h>
|
||||
#include <pybind11/stl.h>
|
||||
|
||||
#include "constructor_stats.h"
|
||||
#include "pybind11_tests.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#ifdef PYBIND11_HAS_OPTIONAL
|
||||
# include <optional>
|
||||
#endif // PYBIND11_HAS_OPTIONAL
|
||||
|
||||
template <typename T>
|
||||
class NonZeroIterator {
|
||||
const T *ptr_;
|
||||
|
||||
public:
|
||||
explicit NonZeroIterator(const T *ptr) : ptr_(ptr) {}
|
||||
const T &operator*() const { return *ptr_; }
|
||||
NonZeroIterator &operator++() {
|
||||
++ptr_;
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
class NonZeroSentinel {};
|
||||
|
||||
template <typename A, typename B>
|
||||
bool operator==(const NonZeroIterator<std::pair<A, B>> &it, const NonZeroSentinel &) {
|
||||
return !(*it).first || !(*it).second;
|
||||
}
|
||||
|
||||
/* Iterator where dereferencing returns prvalues instead of references. */
|
||||
template <typename T>
|
||||
class NonRefIterator {
|
||||
const T *ptr_;
|
||||
|
||||
public:
|
||||
explicit NonRefIterator(const T *ptr) : ptr_(ptr) {}
|
||||
T operator*() const { return T(*ptr_); }
|
||||
NonRefIterator &operator++() {
|
||||
++ptr_;
|
||||
return *this;
|
||||
}
|
||||
bool operator==(const NonRefIterator &other) const { return ptr_ == other.ptr_; }
|
||||
};
|
||||
|
||||
class NonCopyableInt {
|
||||
public:
|
||||
explicit NonCopyableInt(int value) : value_(value) {}
|
||||
NonCopyableInt(const NonCopyableInt &) = delete;
|
||||
NonCopyableInt(NonCopyableInt &&other) noexcept : value_(other.value_) {
|
||||
other.value_ = -1; // detect when an unwanted move occurs
|
||||
}
|
||||
NonCopyableInt &operator=(const NonCopyableInt &) = delete;
|
||||
NonCopyableInt &operator=(NonCopyableInt &&other) noexcept {
|
||||
value_ = other.value_;
|
||||
other.value_ = -1; // detect when an unwanted move occurs
|
||||
return *this;
|
||||
}
|
||||
int get() const { return value_; }
|
||||
void set(int value) { value_ = value; }
|
||||
~NonCopyableInt() = default;
|
||||
|
||||
private:
|
||||
int value_;
|
||||
};
|
||||
using NonCopyableIntPair = std::pair<NonCopyableInt, NonCopyableInt>;
|
||||
PYBIND11_MAKE_OPAQUE(std::vector<NonCopyableInt>);
|
||||
PYBIND11_MAKE_OPAQUE(std::vector<NonCopyableIntPair>);
|
||||
|
||||
template <typename PythonType>
|
||||
py::list test_random_access_iterator(PythonType x) {
|
||||
if (x.size() < 5) {
|
||||
throw py::value_error("Please provide at least 5 elements for testing.");
|
||||
}
|
||||
|
||||
auto checks = py::list();
|
||||
auto assert_equal = [&checks](py::handle a, py::handle b) {
|
||||
auto result = PyObject_RichCompareBool(a.ptr(), b.ptr(), Py_EQ);
|
||||
if (result == -1) {
|
||||
throw py::error_already_set();
|
||||
}
|
||||
checks.append(result != 0);
|
||||
};
|
||||
|
||||
auto it = x.begin();
|
||||
assert_equal(x[0], *it);
|
||||
assert_equal(x[0], it[0]);
|
||||
assert_equal(x[1], it[1]);
|
||||
|
||||
assert_equal(x[1], *(++it));
|
||||
assert_equal(x[1], *(it++));
|
||||
assert_equal(x[2], *it);
|
||||
assert_equal(x[3], *(it += 1));
|
||||
assert_equal(x[2], *(--it));
|
||||
assert_equal(x[2], *(it--));
|
||||
assert_equal(x[1], *it);
|
||||
assert_equal(x[0], *(it -= 1));
|
||||
|
||||
assert_equal(it->attr("real"), x[0].attr("real"));
|
||||
assert_equal((it + 1)->attr("real"), x[1].attr("real"));
|
||||
|
||||
assert_equal(x[1], *(it + 1));
|
||||
assert_equal(x[1], *(1 + it));
|
||||
it += 3;
|
||||
assert_equal(x[1], *(it - 2));
|
||||
|
||||
checks.append(static_cast<std::size_t>(x.end() - x.begin()) == x.size());
|
||||
checks.append((x.begin() + static_cast<std::ptrdiff_t>(x.size())) == x.end());
|
||||
checks.append(x.begin() < x.end());
|
||||
|
||||
return checks;
|
||||
}
|
||||
|
||||
TEST_SUBMODULE(sequences_and_iterators, m) {
|
||||
// test_sliceable
|
||||
class Sliceable {
|
||||
public:
|
||||
explicit Sliceable(int n) : size(n) {}
|
||||
int start, stop, step;
|
||||
int size;
|
||||
};
|
||||
py::class_<Sliceable>(m, "Sliceable")
|
||||
.def(py::init<int>())
|
||||
.def("__getitem__", [](const Sliceable &s, const py::slice &slice) {
|
||||
py::ssize_t start = 0, stop = 0, step = 0, slicelength = 0;
|
||||
if (!slice.compute(s.size, &start, &stop, &step, &slicelength)) {
|
||||
throw py::error_already_set();
|
||||
}
|
||||
int istart = static_cast<int>(start);
|
||||
int istop = static_cast<int>(stop);
|
||||
int istep = static_cast<int>(step);
|
||||
return std::make_tuple(istart, istop, istep);
|
||||
});
|
||||
|
||||
m.def("make_forward_slice_size_t", []() { return py::slice(0, -1, 1); });
|
||||
m.def("make_reversed_slice_object",
|
||||
[]() { return py::slice(py::none(), py::none(), py::int_(-1)); });
|
||||
#ifdef PYBIND11_HAS_OPTIONAL
|
||||
m.attr("has_optional") = true;
|
||||
m.def("make_reversed_slice_size_t_optional_verbose",
|
||||
[]() { return py::slice(std::nullopt, std::nullopt, -1); });
|
||||
// Warning: The following spelling may still compile if optional<> is not present and give
|
||||
// wrong answers. Please use with caution.
|
||||
m.def("make_reversed_slice_size_t_optional", []() { return py::slice({}, {}, -1); });
|
||||
#else
|
||||
m.attr("has_optional") = false;
|
||||
#endif
|
||||
|
||||
// test_sequence
|
||||
class Sequence {
|
||||
public:
|
||||
explicit Sequence(size_t size) : m_size(size) {
|
||||
print_created(this, "of size", m_size);
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-prefer-member-initializer)
|
||||
m_data = new float[size];
|
||||
memset(m_data, 0, sizeof(float) * size);
|
||||
}
|
||||
explicit Sequence(const std::vector<float> &value) : m_size(value.size()) {
|
||||
print_created(this, "of size", m_size, "from std::vector");
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-prefer-member-initializer)
|
||||
m_data = new float[m_size];
|
||||
memcpy(m_data, &value[0], sizeof(float) * m_size);
|
||||
}
|
||||
Sequence(const Sequence &s) : m_size(s.m_size) {
|
||||
print_copy_created(this);
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-prefer-member-initializer)
|
||||
m_data = new float[m_size];
|
||||
memcpy(m_data, s.m_data, sizeof(float) * m_size);
|
||||
}
|
||||
Sequence(Sequence &&s) noexcept : m_size(s.m_size), m_data(s.m_data) {
|
||||
print_move_created(this);
|
||||
s.m_size = 0;
|
||||
s.m_data = nullptr;
|
||||
}
|
||||
|
||||
~Sequence() {
|
||||
print_destroyed(this);
|
||||
delete[] m_data;
|
||||
}
|
||||
|
||||
Sequence &operator=(const Sequence &s) {
|
||||
if (&s != this) {
|
||||
delete[] m_data;
|
||||
m_size = s.m_size;
|
||||
m_data = new float[m_size];
|
||||
memcpy(m_data, s.m_data, sizeof(float) * m_size);
|
||||
}
|
||||
print_copy_assigned(this);
|
||||
return *this;
|
||||
}
|
||||
|
||||
Sequence &operator=(Sequence &&s) noexcept {
|
||||
if (&s != this) {
|
||||
delete[] m_data;
|
||||
m_size = s.m_size;
|
||||
m_data = s.m_data;
|
||||
s.m_size = 0;
|
||||
s.m_data = nullptr;
|
||||
}
|
||||
print_move_assigned(this);
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool operator==(const Sequence &s) const {
|
||||
if (m_size != s.size()) {
|
||||
return false;
|
||||
}
|
||||
for (size_t i = 0; i < m_size; ++i) {
|
||||
if (m_data[i] != s[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
bool operator!=(const Sequence &s) const { return !operator==(s); }
|
||||
|
||||
float operator[](size_t index) const { return m_data[index]; }
|
||||
float &operator[](size_t index) { return m_data[index]; }
|
||||
|
||||
bool contains(float v) const {
|
||||
for (size_t i = 0; i < m_size; ++i) {
|
||||
if (v == m_data[i]) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Sequence reversed() const {
|
||||
Sequence result(m_size);
|
||||
for (size_t i = 0; i < m_size; ++i) {
|
||||
result[m_size - i - 1] = m_data[i];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
size_t size() const { return m_size; }
|
||||
|
||||
const float *begin() const { return m_data; }
|
||||
const float *end() const { return m_data + m_size; }
|
||||
|
||||
private:
|
||||
size_t m_size;
|
||||
float *m_data;
|
||||
};
|
||||
py::class_<Sequence>(m, "Sequence")
|
||||
.def(py::init<size_t>())
|
||||
.def(py::init<const std::vector<float> &>())
|
||||
/// Bare bones interface
|
||||
.def("__getitem__",
|
||||
[](const Sequence &s, size_t i) {
|
||||
if (i >= s.size()) {
|
||||
throw py::index_error();
|
||||
}
|
||||
return s[i];
|
||||
})
|
||||
.def("__setitem__",
|
||||
[](Sequence &s, size_t i, float v) {
|
||||
if (i >= s.size()) {
|
||||
throw py::index_error();
|
||||
}
|
||||
s[i] = v;
|
||||
})
|
||||
.def("__len__", &Sequence::size)
|
||||
/// Optional sequence protocol operations
|
||||
.def(
|
||||
"__iter__",
|
||||
[](const Sequence &s) { return py::make_iterator(s.begin(), s.end()); },
|
||||
py::keep_alive<0, 1>() /* Essential: keep object alive while iterator exists */)
|
||||
.def("__contains__", [](const Sequence &s, float v) { return s.contains(v); })
|
||||
.def("__reversed__", [](const Sequence &s) -> Sequence { return s.reversed(); })
|
||||
/// Slicing protocol (optional)
|
||||
.def("__getitem__",
|
||||
[](const Sequence &s, const py::slice &slice) -> Sequence * {
|
||||
size_t start = 0, stop = 0, step = 0, slicelength = 0;
|
||||
if (!slice.compute(s.size(), &start, &stop, &step, &slicelength)) {
|
||||
throw py::error_already_set();
|
||||
}
|
||||
auto *seq = new Sequence(slicelength);
|
||||
for (size_t i = 0; i < slicelength; ++i) {
|
||||
(*seq)[i] = s[start];
|
||||
start += step;
|
||||
}
|
||||
return seq;
|
||||
})
|
||||
.def("__setitem__",
|
||||
[](Sequence &s, const py::slice &slice, const Sequence &value) {
|
||||
size_t start = 0, stop = 0, step = 0, slicelength = 0;
|
||||
if (!slice.compute(s.size(), &start, &stop, &step, &slicelength)) {
|
||||
throw py::error_already_set();
|
||||
}
|
||||
if (slicelength != value.size()) {
|
||||
throw std::runtime_error(
|
||||
"Left and right hand size of slice assignment have different sizes!");
|
||||
}
|
||||
for (size_t i = 0; i < slicelength; ++i) {
|
||||
s[start] = value[i];
|
||||
start += step;
|
||||
}
|
||||
})
|
||||
/// Comparisons
|
||||
.def(py::self == py::self)
|
||||
.def(py::self != py::self)
|
||||
// Could also define py::self + py::self for concatenation, etc.
|
||||
;
|
||||
|
||||
// test_map_iterator
|
||||
// Interface of a map-like object that isn't (directly) an unordered_map, but provides some
|
||||
// basic map-like functionality.
|
||||
class StringMap {
|
||||
public:
|
||||
StringMap() = default;
|
||||
explicit StringMap(std::unordered_map<std::string, std::string> init)
|
||||
: map(std::move(init)) {}
|
||||
|
||||
void set(const std::string &key, std::string val) { map[key] = std::move(val); }
|
||||
std::string get(const std::string &key) const { return map.at(key); }
|
||||
size_t size() const { return map.size(); }
|
||||
|
||||
private:
|
||||
std::unordered_map<std::string, std::string> map;
|
||||
|
||||
public:
|
||||
decltype(map.cbegin()) begin() const { return map.cbegin(); }
|
||||
decltype(map.cend()) end() const { return map.cend(); }
|
||||
};
|
||||
py::class_<StringMap>(m, "StringMap")
|
||||
.def(py::init<>())
|
||||
.def(py::init<std::unordered_map<std::string, std::string>>())
|
||||
.def("__getitem__",
|
||||
[](const StringMap &map, const std::string &key) {
|
||||
try {
|
||||
return map.get(key);
|
||||
} catch (const std::out_of_range &) {
|
||||
throw py::key_error("key '" + key + "' does not exist");
|
||||
}
|
||||
})
|
||||
.def("__setitem__", &StringMap::set)
|
||||
.def("__len__", &StringMap::size)
|
||||
.def(
|
||||
"__iter__",
|
||||
[](const StringMap &map) { return py::make_key_iterator(map.begin(), map.end()); },
|
||||
py::keep_alive<0, 1>())
|
||||
.def(
|
||||
"items",
|
||||
[](const StringMap &map) { return py::make_iterator(map.begin(), map.end()); },
|
||||
py::keep_alive<0, 1>())
|
||||
.def(
|
||||
"values",
|
||||
[](const StringMap &map) { return py::make_value_iterator(map.begin(), map.end()); },
|
||||
py::keep_alive<0, 1>());
|
||||
|
||||
// test_generalized_iterators
|
||||
class IntPairs {
|
||||
public:
|
||||
explicit IntPairs(std::vector<std::pair<int, int>> data) : data_(std::move(data)) {}
|
||||
const std::pair<int, int> *begin() const { return data_.data(); }
|
||||
// .end() only required for py::make_iterator(self) overload
|
||||
const std::pair<int, int> *end() const { return data_.data() + data_.size(); }
|
||||
|
||||
private:
|
||||
std::vector<std::pair<int, int>> data_;
|
||||
};
|
||||
py::class_<IntPairs>(m, "IntPairs")
|
||||
.def(py::init<std::vector<std::pair<int, int>>>())
|
||||
.def(
|
||||
"nonzero",
|
||||
[](const IntPairs &s) {
|
||||
return py::make_iterator(NonZeroIterator<std::pair<int, int>>(s.begin()),
|
||||
NonZeroSentinel());
|
||||
},
|
||||
py::keep_alive<0, 1>())
|
||||
.def(
|
||||
"nonzero_keys",
|
||||
[](const IntPairs &s) {
|
||||
return py::make_key_iterator(NonZeroIterator<std::pair<int, int>>(s.begin()),
|
||||
NonZeroSentinel());
|
||||
},
|
||||
py::keep_alive<0, 1>())
|
||||
.def(
|
||||
"nonzero_values",
|
||||
[](const IntPairs &s) {
|
||||
return py::make_value_iterator(NonZeroIterator<std::pair<int, int>>(s.begin()),
|
||||
NonZeroSentinel());
|
||||
},
|
||||
py::keep_alive<0, 1>())
|
||||
|
||||
// test iterator that returns values instead of references
|
||||
.def(
|
||||
"nonref",
|
||||
[](const IntPairs &s) {
|
||||
return py::make_iterator(NonRefIterator<std::pair<int, int>>(s.begin()),
|
||||
NonRefIterator<std::pair<int, int>>(s.end()));
|
||||
},
|
||||
py::keep_alive<0, 1>())
|
||||
.def(
|
||||
"nonref_keys",
|
||||
[](const IntPairs &s) {
|
||||
return py::make_key_iterator(NonRefIterator<std::pair<int, int>>(s.begin()),
|
||||
NonRefIterator<std::pair<int, int>>(s.end()));
|
||||
},
|
||||
py::keep_alive<0, 1>())
|
||||
.def(
|
||||
"nonref_values",
|
||||
[](const IntPairs &s) {
|
||||
return py::make_value_iterator(NonRefIterator<std::pair<int, int>>(s.begin()),
|
||||
NonRefIterator<std::pair<int, int>>(s.end()));
|
||||
},
|
||||
py::keep_alive<0, 1>())
|
||||
|
||||
// test single-argument make_iterator
|
||||
.def(
|
||||
"simple_iterator",
|
||||
[](IntPairs &self) { return py::make_iterator(self); },
|
||||
py::keep_alive<0, 1>())
|
||||
.def(
|
||||
"simple_keys",
|
||||
[](IntPairs &self) { return py::make_key_iterator(self); },
|
||||
py::keep_alive<0, 1>())
|
||||
.def(
|
||||
"simple_values",
|
||||
[](IntPairs &self) { return py::make_value_iterator(self); },
|
||||
py::keep_alive<0, 1>())
|
||||
|
||||
// Test iterator with an Extra (doesn't do anything useful, so not used
|
||||
// at runtime, but tests need to be able to compile with the correct
|
||||
// overload. See PR #3293.
|
||||
.def(
|
||||
"_make_iterator_extras",
|
||||
[](IntPairs &self) { return py::make_iterator(self, py::call_guard<int>()); },
|
||||
py::keep_alive<0, 1>())
|
||||
.def(
|
||||
"_make_key_extras",
|
||||
[](IntPairs &self) { return py::make_key_iterator(self, py::call_guard<int>()); },
|
||||
py::keep_alive<0, 1>())
|
||||
.def(
|
||||
"_make_value_extras",
|
||||
[](IntPairs &self) { return py::make_value_iterator(self, py::call_guard<int>()); },
|
||||
py::keep_alive<0, 1>());
|
||||
|
||||
// test_iterator_referencing
|
||||
py::class_<NonCopyableInt>(m, "NonCopyableInt")
|
||||
.def(py::init<int>())
|
||||
.def("set", &NonCopyableInt::set)
|
||||
.def("__int__", &NonCopyableInt::get);
|
||||
py::class_<std::vector<NonCopyableInt>>(m, "VectorNonCopyableInt")
|
||||
.def(py::init<>())
|
||||
.def("append",
|
||||
[](std::vector<NonCopyableInt> &vec, int value) { vec.emplace_back(value); })
|
||||
.def("__iter__", [](std::vector<NonCopyableInt> &vec) {
|
||||
return py::make_iterator(vec.begin(), vec.end());
|
||||
});
|
||||
py::class_<std::vector<NonCopyableIntPair>>(m, "VectorNonCopyableIntPair")
|
||||
.def(py::init<>())
|
||||
.def("append",
|
||||
[](std::vector<NonCopyableIntPair> &vec, const std::pair<int, int> &value) {
|
||||
vec.emplace_back(NonCopyableInt(value.first), NonCopyableInt(value.second));
|
||||
})
|
||||
.def("keys",
|
||||
[](std::vector<NonCopyableIntPair> &vec) {
|
||||
return py::make_key_iterator(vec.begin(), vec.end());
|
||||
})
|
||||
.def("values", [](std::vector<NonCopyableIntPair> &vec) {
|
||||
return py::make_value_iterator(vec.begin(), vec.end());
|
||||
});
|
||||
|
||||
#if 0
|
||||
// Obsolete: special data structure for exposing custom iterator types to python
|
||||
// kept here for illustrative purposes because there might be some use cases which
|
||||
// are not covered by the much simpler py::make_iterator
|
||||
|
||||
struct PySequenceIterator {
|
||||
PySequenceIterator(const Sequence &seq, py::object ref) : seq(seq), ref(ref) { }
|
||||
|
||||
float next() {
|
||||
if (index == seq.size())
|
||||
throw py::stop_iteration();
|
||||
return seq[index++];
|
||||
}
|
||||
|
||||
const Sequence &seq;
|
||||
py::object ref; // keep a reference
|
||||
size_t index = 0;
|
||||
};
|
||||
|
||||
py::class_<PySequenceIterator>(seq, "Iterator")
|
||||
.def("__iter__", [](PySequenceIterator &it) -> PySequenceIterator& { return it; })
|
||||
.def("__next__", &PySequenceIterator::next);
|
||||
|
||||
On the actual Sequence object, the iterator would be constructed as follows:
|
||||
.def("__iter__", [](py::object s) { return PySequenceIterator(s.cast<const Sequence &>(), s); })
|
||||
#endif
|
||||
|
||||
// test_python_iterator_in_cpp
|
||||
m.def("object_to_list", [](const py::object &o) {
|
||||
auto l = py::list();
|
||||
for (auto item : o) {
|
||||
l.append(item);
|
||||
}
|
||||
return l;
|
||||
});
|
||||
|
||||
m.def("iterator_to_list", [](py::iterator it) {
|
||||
auto l = py::list();
|
||||
while (it != py::iterator::sentinel()) {
|
||||
l.append(*it);
|
||||
++it;
|
||||
}
|
||||
return l;
|
||||
});
|
||||
|
||||
// test_sequence_length: check that Python sequences can be converted to py::sequence.
|
||||
m.def("sequence_length", [](const py::sequence &seq) { return seq.size(); });
|
||||
|
||||
// Make sure that py::iterator works with std algorithms
|
||||
m.def("count_none", [](const py::object &o) {
|
||||
return std::count_if(o.begin(), o.end(), [](py::handle h) { return h.is_none(); });
|
||||
});
|
||||
|
||||
m.def("find_none", [](const py::object &o) {
|
||||
auto it = std::find_if(o.begin(), o.end(), [](py::handle h) { return h.is_none(); });
|
||||
return it->is_none();
|
||||
});
|
||||
|
||||
m.def("count_nonzeros", [](const py::dict &d) {
|
||||
return std::count_if(d.begin(), d.end(), [](std::pair<py::handle, py::handle> p) {
|
||||
return p.second.cast<int>() != 0;
|
||||
});
|
||||
});
|
||||
|
||||
m.def("tuple_iterator", &test_random_access_iterator<py::tuple>);
|
||||
m.def("list_iterator", &test_random_access_iterator<py::list>);
|
||||
m.def("sequence_iterator", &test_random_access_iterator<py::sequence>);
|
||||
|
||||
// test_iterator_passthrough
|
||||
// #181: iterator passthrough did not compile
|
||||
m.def("iterator_passthrough", [](py::iterator s) -> py::iterator {
|
||||
return py::make_iterator(std::begin(s), std::end(s));
|
||||
});
|
||||
|
||||
// test_iterator_rvp
|
||||
// #388: Can't make iterators via make_iterator() with different r/v policies
|
||||
static std::vector<int> list = {1, 2, 3};
|
||||
m.def("make_iterator_1",
|
||||
[]() { return py::make_iterator<py::return_value_policy::copy>(list); });
|
||||
m.def("make_iterator_2",
|
||||
[]() { return py::make_iterator<py::return_value_policy::automatic>(list); });
|
||||
}
|
||||
253
third_party/pybind11/tests/test_sequences_and_iterators.py
vendored
Normal file
253
third_party/pybind11/tests/test_sequences_and_iterators.py
vendored
Normal file
@@ -0,0 +1,253 @@
|
||||
import pytest
|
||||
from pytest import approx
|
||||
|
||||
from pybind11_tests import ConstructorStats
|
||||
from pybind11_tests import sequences_and_iterators as m
|
||||
|
||||
|
||||
def test_slice_constructors():
|
||||
assert m.make_forward_slice_size_t() == slice(0, -1, 1)
|
||||
assert m.make_reversed_slice_object() == slice(None, None, -1)
|
||||
|
||||
|
||||
@pytest.mark.skipif(not m.has_optional, reason="no <optional>")
|
||||
def test_slice_constructors_explicit_optional():
|
||||
assert m.make_reversed_slice_size_t_optional() == slice(None, None, -1)
|
||||
assert m.make_reversed_slice_size_t_optional_verbose() == slice(None, None,
|
||||
-1)
|
||||
|
||||
|
||||
def test_generalized_iterators():
|
||||
assert list(m.IntPairs([(1, 2), (3, 4), (0, 5)]).nonzero()) == [(1, 2),
|
||||
(3, 4)]
|
||||
assert list(m.IntPairs([(1, 2), (2, 0), (0, 3), (4, 5)]).nonzero()) == [
|
||||
(1, 2)
|
||||
]
|
||||
assert list(m.IntPairs([(0, 3), (1, 2), (3, 4)]).nonzero()) == []
|
||||
|
||||
assert list(m.IntPairs([(1, 2), (3, 4), (0, 5)]).nonzero_keys()) == [1, 3]
|
||||
assert list(m.IntPairs([(1, 2), (2, 0), (0, 3), (4, 5)]).nonzero_keys(
|
||||
)) == [1]
|
||||
assert list(m.IntPairs([(0, 3), (1, 2), (3, 4)]).nonzero_keys()) == []
|
||||
|
||||
assert list(m.IntPairs([(1, 2), (3, 4), (0, 5)]).nonzero_values(
|
||||
)) == [2, 4]
|
||||
assert list(
|
||||
m.IntPairs([(1, 2), (2, 0), (0, 3), (4, 5)]).nonzero_values()) == [2]
|
||||
assert list(m.IntPairs([(0, 3), (1, 2), (3, 4)]).nonzero_values()) == []
|
||||
|
||||
# __next__ must continue to raise StopIteration
|
||||
it = m.IntPairs([(0, 0)]).nonzero()
|
||||
for _ in range(3):
|
||||
with pytest.raises(StopIteration):
|
||||
next(it)
|
||||
|
||||
it = m.IntPairs([(0, 0)]).nonzero_keys()
|
||||
for _ in range(3):
|
||||
with pytest.raises(StopIteration):
|
||||
next(it)
|
||||
|
||||
|
||||
def test_nonref_iterators():
|
||||
pairs = m.IntPairs([(1, 2), (3, 4), (0, 5)])
|
||||
assert list(pairs.nonref()) == [(1, 2), (3, 4), (0, 5)]
|
||||
assert list(pairs.nonref_keys()) == [1, 3, 0]
|
||||
assert list(pairs.nonref_values()) == [2, 4, 5]
|
||||
|
||||
|
||||
def test_generalized_iterators_simple():
|
||||
assert list(m.IntPairs([(1, 2), (3, 4), (0, 5)]).simple_iterator()) == [
|
||||
(1, 2),
|
||||
(3, 4),
|
||||
(0, 5),
|
||||
]
|
||||
assert list(m.IntPairs([(1, 2), (3, 4), (0, 5)]).simple_keys(
|
||||
)) == [1, 3, 0]
|
||||
assert list(m.IntPairs([(1, 2), (3, 4), (0, 5)]).simple_values(
|
||||
)) == [2, 4, 5]
|
||||
|
||||
|
||||
def test_iterator_referencing():
|
||||
"""Test that iterators reference rather than copy their referents."""
|
||||
vec = m.VectorNonCopyableInt()
|
||||
vec.append(3)
|
||||
vec.append(5)
|
||||
assert [int(x) for x in vec] == [3, 5]
|
||||
# Increment everything to make sure the referents can be mutated
|
||||
for x in vec:
|
||||
x.set(int(x) + 1)
|
||||
assert [int(x) for x in vec] == [4, 6]
|
||||
|
||||
vec = m.VectorNonCopyableIntPair()
|
||||
vec.append([3, 4])
|
||||
vec.append([5, 7])
|
||||
assert [int(x) for x in vec.keys()] == [3, 5]
|
||||
assert [int(x) for x in vec.values()] == [4, 7]
|
||||
for x in vec.keys():
|
||||
x.set(int(x) + 1)
|
||||
for x in vec.values():
|
||||
x.set(int(x) + 10)
|
||||
assert [int(x) for x in vec.keys()] == [4, 6]
|
||||
assert [int(x) for x in vec.values()] == [14, 17]
|
||||
|
||||
|
||||
def test_sliceable():
|
||||
sliceable = m.Sliceable(100)
|
||||
assert sliceable[::] == (0, 100, 1)
|
||||
assert sliceable[10::] == (10, 100, 1)
|
||||
assert sliceable[:10:] == (0, 10, 1)
|
||||
assert sliceable[::10] == (0, 100, 10)
|
||||
assert sliceable[-10::] == (90, 100, 1)
|
||||
assert sliceable[:-10:] == (0, 90, 1)
|
||||
assert sliceable[::-10] == (99, -1, -10)
|
||||
assert sliceable[50:60:1] == (50, 60, 1)
|
||||
assert sliceable[50:60:-1] == (50, 60, -1)
|
||||
|
||||
|
||||
def test_sequence():
|
||||
cstats = ConstructorStats.get(m.Sequence)
|
||||
|
||||
s = m.Sequence(5)
|
||||
assert cstats.values() == ["of size", "5"]
|
||||
|
||||
assert "Sequence" in repr(s)
|
||||
assert len(s) == 5
|
||||
assert s[0] == 0 and s[3] == 0
|
||||
assert 12.34 not in s
|
||||
s[0], s[3] = 12.34, 56.78
|
||||
assert 12.34 in s
|
||||
assert s[0] == approx(12.34, rel=1e-05)
|
||||
assert s[3] == approx(56.78, rel=1e-05)
|
||||
|
||||
rev = reversed(s)
|
||||
assert cstats.values() == ["of size", "5"]
|
||||
|
||||
rev2 = s[::-1]
|
||||
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"]
|
||||
|
||||
expected = [0, 56.78, 0, 0, 12.34]
|
||||
assert rev == approx(expected, rel=1e-05)
|
||||
assert rev2 == approx(expected, rel=1e-05)
|
||||
assert rev == rev2
|
||||
|
||||
rev[0::2] = m.Sequence([2.0, 2.0, 2.0])
|
||||
assert cstats.values() == ["of size", "3", "from std::vector"]
|
||||
|
||||
assert rev == approx([2, 56.78, 2, 0, 2], rel=1e-05)
|
||||
|
||||
assert cstats.alive() == 4
|
||||
del it
|
||||
assert cstats.alive() == 3
|
||||
del s
|
||||
assert cstats.alive() == 2
|
||||
del rev
|
||||
assert cstats.alive() == 1
|
||||
del rev2
|
||||
assert cstats.alive() == 0
|
||||
|
||||
assert cstats.values() == []
|
||||
assert cstats.default_constructions == 0
|
||||
assert cstats.copy_constructions == 0
|
||||
assert cstats.move_constructions >= 1
|
||||
assert cstats.copy_assignments == 0
|
||||
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"
|
||||
assert len(sm) == 2
|
||||
assert sm["black"] == "white"
|
||||
|
||||
with pytest.raises(KeyError):
|
||||
assert sm["orange"]
|
||||
sm["orange"] = "banana"
|
||||
assert sm["orange"] == "banana"
|
||||
|
||||
expected = {"hi": "bye", "black": "white", "orange": "banana"}
|
||||
for k in sm:
|
||||
assert sm[k] == expected[k]
|
||||
for k, v in sm.items():
|
||||
assert v == expected[k]
|
||||
assert list(sm.values()) == [expected[k] for k in sm]
|
||||
|
||||
it = iter(m.StringMap({}))
|
||||
for _ in range(3): # __next__ must continue to raise StopIteration
|
||||
with pytest.raises(StopIteration):
|
||||
next(it)
|
||||
|
||||
|
||||
def test_python_iterator_in_cpp():
|
||||
t = (1, 2, 3)
|
||||
assert m.object_to_list(t) == [1, 2, 3]
|
||||
assert m.object_to_list(iter(t)) == [1, 2, 3]
|
||||
assert m.iterator_to_list(iter(t)) == [1, 2, 3]
|
||||
|
||||
with pytest.raises(TypeError) as excinfo:
|
||||
m.object_to_list(1)
|
||||
assert "object is not iterable" in str(excinfo.value)
|
||||
|
||||
with pytest.raises(TypeError) as excinfo:
|
||||
m.iterator_to_list(1)
|
||||
assert "incompatible function arguments" in str(excinfo.value)
|
||||
|
||||
def bad_next_call():
|
||||
raise RuntimeError("py::iterator::advance() should propagate errors")
|
||||
|
||||
with pytest.raises(RuntimeError) as excinfo:
|
||||
m.iterator_to_list(iter(bad_next_call, None))
|
||||
assert str(
|
||||
excinfo.value) == "py::iterator::advance() should propagate errors"
|
||||
|
||||
lst = [1, None, 0, None]
|
||||
assert m.count_none(lst) == 2
|
||||
assert m.find_none(lst) is True
|
||||
assert m.count_nonzeros({"a": 0, "b": 1, "c": 2}) == 2
|
||||
|
||||
r = range(5)
|
||||
assert all(m.tuple_iterator(tuple(r)))
|
||||
assert all(m.list_iterator(list(r)))
|
||||
assert all(m.sequence_iterator(r))
|
||||
|
||||
|
||||
def test_iterator_passthrough():
|
||||
"""#181: iterator passthrough did not compile"""
|
||||
from pybind11_tests.sequences_and_iterators import iterator_passthrough
|
||||
|
||||
values = [3, 5, 7, 9, 11, 13, 15]
|
||||
assert list(iterator_passthrough(iter(values))) == values
|
||||
|
||||
|
||||
def test_iterator_rvp():
|
||||
"""#388: Can't make iterators via make_iterator() with different r/v policies"""
|
||||
import pybind11_tests.sequences_and_iterators as m
|
||||
|
||||
assert list(m.make_iterator_1()) == [1, 2, 3]
|
||||
assert list(m.make_iterator_2()) == [1, 2, 3]
|
||||
assert not isinstance(m.make_iterator_1(), type(m.make_iterator_2()))
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user