summaryrefslogtreecommitdiff
path: root/ext/pybind11/tests
diff options
context:
space:
mode:
authorAndreas Sandberg <andreas.sandberg@arm.com>2017-05-09 19:22:53 +0100
committerAndreas Sandberg <andreas.sandberg@arm.com>2017-05-22 17:15:09 +0000
commit6914a229a038206341ae1fea46393965a555ca9a (patch)
tree4a11cfaed46dabc827c5ee17cd976f42b5f53d49 /ext/pybind11/tests
parentca1d18d599dcc620bf526fb22042af95b1b60b68 (diff)
downloadgem5-6914a229a038206341ae1fea46393965a555ca9a.tar.xz
ext: Upgrade PyBind11 to version 2.1.1
Change-Id: I16870dec402d661295f9d013dc23e362b2b2c169 Signed-off-by: Andreas Sandberg <andreas.sandberg@arm.com> Reviewed-by: Curtis Dunham <curtis.dunham@arm.com> Reviewed-on: https://gem5-review.googlesource.com/3225 Reviewed-by: Jason Lowe-Power <jason@lowepower.com>
Diffstat (limited to 'ext/pybind11/tests')
-rw-r--r--ext/pybind11/tests/CMakeLists.txt189
-rw-r--r--ext/pybind11/tests/conftest.py18
-rw-r--r--ext/pybind11/tests/constructor_stats.h39
-rw-r--r--ext/pybind11/tests/object.h4
-rw-r--r--ext/pybind11/tests/pybind11_tests.cpp15
-rw-r--r--ext/pybind11/tests/pytest.ini7
-rw-r--r--ext/pybind11/tests/test_alias_initialization.py17
-rw-r--r--ext/pybind11/tests/test_buffers.cpp2
-rw-r--r--ext/pybind11/tests/test_buffers.py55
-rw-r--r--ext/pybind11/tests/test_callbacks.cpp33
-rw-r--r--ext/pybind11/tests/test_callbacks.py6
-rw-r--r--ext/pybind11/tests/test_chrono.cpp6
-rw-r--r--ext/pybind11/tests/test_chrono.py6
-rw-r--r--ext/pybind11/tests/test_cmake_build/installed_function/CMakeLists.txt12
-rw-r--r--ext/pybind11/tests/test_cmake_build/installed_target/CMakeLists.txt22
-rw-r--r--ext/pybind11/tests/test_cmake_build/main.cpp (renamed from ext/pybind11/tests/test_installed_target/main.cpp)4
-rw-r--r--ext/pybind11/tests/test_cmake_build/subdirectory_function/CMakeLists.txt8
-rw-r--r--ext/pybind11/tests/test_cmake_build/subdirectory_target/CMakeLists.txt15
-rw-r--r--ext/pybind11/tests/test_cmake_build/test.py5
-rw-r--r--ext/pybind11/tests/test_constants_and_functions.cpp38
-rw-r--r--ext/pybind11/tests/test_constants_and_functions.py19
-rw-r--r--ext/pybind11/tests/test_docstring_options.cpp9
-rw-r--r--ext/pybind11/tests/test_docstring_options.py12
-rw-r--r--ext/pybind11/tests/test_eigen.cpp304
-rw-r--r--ext/pybind11/tests/test_eigen.py567
-rw-r--r--ext/pybind11/tests/test_enum.py9
-rw-r--r--ext/pybind11/tests/test_inheritance.cpp23
-rw-r--r--ext/pybind11/tests/test_inheritance.py27
-rw-r--r--ext/pybind11/tests/test_installed_module/CMakeLists.txt14
-rw-r--r--ext/pybind11/tests/test_installed_module/main.cpp10
-rw-r--r--ext/pybind11/tests/test_installed_module/test.py3
-rw-r--r--ext/pybind11/tests/test_installed_target/CMakeLists.txt20
-rw-r--r--ext/pybind11/tests/test_installed_target/test.py3
-rw-r--r--ext/pybind11/tests/test_issues.cpp3
-rw-r--r--ext/pybind11/tests/test_issues.py9
-rw-r--r--ext/pybind11/tests/test_keep_alive.py26
-rw-r--r--ext/pybind11/tests/test_kwargs_and_defaults.cpp37
-rw-r--r--ext/pybind11/tests/test_kwargs_and_defaults.py63
-rw-r--r--ext/pybind11/tests/test_methods_and_attributes.cpp142
-rw-r--r--ext/pybind11/tests/test_methods_and_attributes.py139
-rw-r--r--ext/pybind11/tests/test_modules.py8
-rw-r--r--ext/pybind11/tests/test_multiple_inheritance.cpp92
-rw-r--r--ext/pybind11/tests/test_multiple_inheritance.py47
-rw-r--r--ext/pybind11/tests/test_numpy_array.cpp114
-rw-r--r--ext/pybind11/tests/test_numpy_array.py166
-rw-r--r--ext/pybind11/tests/test_numpy_dtypes.cpp76
-rw-r--r--ext/pybind11/tests/test_numpy_dtypes.py139
-rw-r--r--ext/pybind11/tests/test_numpy_vectorize.cpp17
-rw-r--r--ext/pybind11/tests/test_numpy_vectorize.py93
-rw-r--r--ext/pybind11/tests/test_opaque_types.py3
-rw-r--r--ext/pybind11/tests/test_operator_overloading.py1
-rw-r--r--ext/pybind11/tests/test_pickling.cpp4
-rw-r--r--ext/pybind11/tests/test_pickling.py3
-rw-r--r--ext/pybind11/tests/test_python_types.cpp79
-rw-r--r--ext/pybind11/tests/test_python_types.py135
-rw-r--r--ext/pybind11/tests/test_sequences_and_iterators.cpp81
-rw-r--r--ext/pybind11/tests/test_sequences_and_iterators.py41
-rw-r--r--ext/pybind11/tests/test_smart_ptr.cpp52
-rw-r--r--ext/pybind11/tests/test_smart_ptr.py24
-rw-r--r--ext/pybind11/tests/test_stl_binders.cpp37
-rw-r--r--ext/pybind11/tests/test_stl_binders.py58
-rw-r--r--ext/pybind11/tests/test_virtual_functions.py3
62 files changed, 2769 insertions, 444 deletions
diff --git a/ext/pybind11/tests/CMakeLists.txt b/ext/pybind11/tests/CMakeLists.txt
index 27cb65291..11be49e53 100644
--- a/ext/pybind11/tests/CMakeLists.txt
+++ b/ext/pybind11/tests/CMakeLists.txt
@@ -1,3 +1,22 @@
+# 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 2.8.12)
+
+option(PYBIND11_WERROR "Report all warnings as errors" OFF)
+
+if (CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR)
+ # We're being loaded directly, i.e. not via add_subdirectory, so make this
+ # work as its own project and load the pybind11Config to get the tools we need
+ project(pybind11_tests)
+
+ find_package(pybind11 REQUIRED CONFIG)
+endif()
+
if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
message(STATUS "Setting tests build type to MinSizeRel as none was specified")
set(CMAKE_BUILD_TYPE MinSizeRel CACHE STRING "Choose the type of build." FORCE)
@@ -54,9 +73,30 @@ string(REPLACE ".cpp" ".py" PYBIND11_PYTEST_FILES "${PYBIND11_TEST_FILES}")
# skip message).
list(FIND PYBIND11_TEST_FILES test_eigen.cpp PYBIND11_TEST_FILES_EIGEN_I)
if(PYBIND11_TEST_FILES_EIGEN_I GREATER -1)
- find_package(Eigen3 QUIET)
+ # Try loading via newer Eigen's Eigen3Config first (bypassing tools/FindEigen3.cmake).
+ # Eigen 3.3.1+ exports a cmake 3.0+ target for handling dependency requirements, but also
+ # produces a fatal error if loaded from a pre-3.0 cmake.
+ if (NOT CMAKE_VERSION VERSION_LESS 3.0)
+ find_package(Eigen3 QUIET CONFIG)
+ if (EIGEN3_FOUND)
+ if (EIGEN3_VERSION_STRING AND NOT EIGEN3_VERSION_STRING VERSION_LESS 3.3.1)
+ set(PYBIND11_EIGEN_VIA_TARGET 1)
+ endif()
+ endif()
+ endif()
+ if (NOT EIGEN3_FOUND)
+ # Couldn't load via target, so fall back to allowing module mode finding, which will pick up
+ # tools/FindEigen3.cmake
+ find_package(Eigen3 QUIET)
+ endif()
if(EIGEN3_FOUND)
+ # 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})
@@ -64,18 +104,40 @@ if(PYBIND11_TEST_FILES_EIGEN_I GREATER -1)
endif()
endif()
+# Compile with compiler warnings turned on
+function(pybind11_enable_warnings target_name)
+ if(MSVC)
+ target_compile_options(${target_name} PRIVATE /W4)
+ else()
+ target_compile_options(${target_name} PRIVATE -Wall -Wextra -Wconversion -Wcast-qual)
+ endif()
+
+ if(PYBIND11_WERROR)
+ if(MSVC)
+ target_compile_options(${target_name} PRIVATE /WX)
+ else()
+ target_compile_options(${target_name} PRIVATE -Werror)
+ endif()
+ endif()
+endfunction()
+
+
# Create the binding library
-pybind11_add_module(pybind11_tests pybind11_tests.cpp
+pybind11_add_module(pybind11_tests THIN_LTO pybind11_tests.cpp
${PYBIND11_TEST_FILES} ${PYBIND11_HEADERS})
pybind11_enable_warnings(pybind11_tests)
if(EIGEN3_FOUND)
- target_include_directories(pybind11_tests PRIVATE ${EIGEN3_INCLUDE_DIR})
+ if (PYBIND11_EIGEN_VIA_TARGET)
+ target_link_libraries(pybind11_tests PRIVATE Eigen3::Eigen)
+ else()
+ target_include_directories(pybind11_tests PRIVATE ${EIGEN3_INCLUDE_DIR})
+ endif()
target_compile_definitions(pybind11_tests PRIVATE -DPYBIND11_TEST_EIGEN)
endif()
-set(testdir ${PROJECT_SOURCE_DIR}/tests)
+set(testdir ${CMAKE_CURRENT_SOURCE_DIR})
# Always write the output file directly into the 'tests' directory (even on MSVC)
if(NOT CMAKE_LIBRARY_OUTPUT_DIRECTORY)
@@ -88,16 +150,20 @@ endif()
# Make sure pytest is found or produce a fatal error
if(NOT PYBIND11_PYTEST_FOUND)
- execute_process(COMMAND ${PYTHON_EXECUTABLE} -m pytest --version --noconftest OUTPUT_QUIET ERROR_QUIET
- RESULT_VARIABLE PYBIND11_EXEC_PYTHON_ERR)
- if(PYBIND11_EXEC_PYTHON_ERR)
- message(FATAL_ERROR "Running the tests requires pytest. Please install it manually (try: ${PYTHON_EXECUTABLE} -m pip install pytest)")
+ execute_process(COMMAND ${PYTHON_EXECUTABLE} -c "import pytest; print(pytest.__version__)"
+ RESULT_VARIABLE pytest_not_found OUTPUT_VARIABLE pytest_version ERROR_QUIET)
+ if(pytest_not_found)
+ message(FATAL_ERROR "Running the tests requires pytest. Please install it manually"
+ " (try: ${PYTHON_EXECUTABLE} -m pip install pytest)")
+ elseif(pytest_version VERSION_LESS 3.0)
+ message(FATAL_ERROR "Running the tests requires pytest >= 3.0. Found: ${pytest_version}"
+ "Please update it (try: ${PYTHON_EXECUTABLE} -m pip install -U pytest)")
endif()
set(PYBIND11_PYTEST_FOUND TRUE CACHE INTERNAL "")
endif()
# A single command to compile and run the tests
-add_custom_target(pytest COMMAND ${PYTHON_EXECUTABLE} -m pytest -rws ${PYBIND11_PYTEST_FILES}
+add_custom_target(pytest COMMAND ${PYTHON_EXECUTABLE} -m pytest ${PYBIND11_PYTEST_FILES}
DEPENDS pybind11_tests WORKING_DIRECTORY ${testdir})
if(PYBIND11_TEST_OVERRIDE)
@@ -105,55 +171,68 @@ if(PYBIND11_TEST_OVERRIDE)
COMMAND ${CMAKE_COMMAND} -E echo "Note: not all tests run: -DPYBIND11_TEST_OVERRIDE is in effect")
endif()
-# test use of installation
-if(PYBIND11_INSTALL)
- # 2.8.12 needed for test_installed_module
- # 3.0 needed for interface library for test_installed_target
+# 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 (NOT PROJECT_NAME STREQUAL "pybind11")
+ return()
+endif()
+
+# Add a post-build comment to show the .so size and, if a previous size, compare it:
+add_custom_command(TARGET pybind11_tests POST_BUILD
+ COMMAND ${PYTHON_EXECUTABLE} ${PROJECT_SOURCE_DIR}/tools/libsize.py
+ $<TARGET_FILE:pybind11_tests> ${CMAKE_CURRENT_BINARY_DIR}/sosize-$<TARGET_FILE_NAME:pybind11_tests>.txt)
+
+# Test CMake build using functions and targets from subdirectory or installed location
+add_custom_target(test_cmake_build)
+if(NOT CMAKE_VERSION VERSION_LESS 3.1)
+ # 3.0 needed for interface library for subdirectory_target/installed_target
# 3.1 needed for cmake -E env for testing
- if(NOT CMAKE_VERSION VERSION_LESS 3.1)
- add_custom_target(test_installed_target
- COMMAND ${CMAKE_COMMAND}
- "-DCMAKE_INSTALL_PREFIX=${PROJECT_BINARY_DIR}/test_install"
- -P "${PROJECT_BINARY_DIR}/cmake_install.cmake"
- COMMAND ${CMAKE_CTEST_COMMAND}
- --build-and-test "${CMAKE_CURRENT_SOURCE_DIR}/test_installed_target"
- "${CMAKE_CURRENT_BINARY_DIR}/test_installed_target"
- --build-noclean
- --build-generator ${CMAKE_GENERATOR}
- $<$<BOOL:${CMAKE_GENERATOR_PLATFORM}>:--build-generator-platform> ${CMAKE_GENERATOR_PLATFORM}
- --build-makeprogram ${CMAKE_MAKE_PROGRAM}
- --build-target check
- --build-options "-DCMAKE_PREFIX_PATH=${PROJECT_BINARY_DIR}/test_install"
- "-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}"
- "-DPYTHON_EXECUTABLE=${PYTHON_EXECUTABLE}"
- "-DPYBIND11_CPP_STANDARD=${PYBIND11_CPP_STANDARD}"
+
+ include(CMakeParseArguments)
+ function(pybind11_add_build_test name)
+ cmake_parse_arguments(ARG "INSTALL" "" "" ${ARGN})
+
+ set(build_options "-DCMAKE_PREFIX_PATH=${PROJECT_BINARY_DIR}/mock_install"
+ "-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}"
+ "-DPYTHON_EXECUTABLE=${PYTHON_EXECUTABLE}"
+ "-DPYBIND11_CPP_STANDARD=${PYBIND11_CPP_STANDARD}")
+ if(NOT ARG_INSTALL)
+ list(APPEND build_options "-DPYBIND11_PROJECT_DIR=${PROJECT_SOURCE_DIR}")
+ endif()
+
+ add_custom_target(test_${name} ${CMAKE_CTEST_COMMAND}
+ --quiet --output-log test_cmake_build/${name}.log
+ --build-and-test "${CMAKE_CURRENT_SOURCE_DIR}/test_cmake_build/${name}"
+ "${CMAKE_CURRENT_BINARY_DIR}/test_cmake_build/${name}"
+ --build-config Release
+ --build-noclean
+ --build-generator ${CMAKE_GENERATOR}
+ $<$<BOOL:${CMAKE_GENERATOR_PLATFORM}>:--build-generator-platform> ${CMAKE_GENERATOR_PLATFORM}
+ --build-makeprogram ${CMAKE_MAKE_PROGRAM}
+ --build-target check
+ --build-options ${build_options}
)
- add_custom_target(test_installed_module
- COMMAND ${CMAKE_COMMAND}
- "-DCMAKE_INSTALL_PREFIX=${PROJECT_BINARY_DIR}/test_install"
- -P "${PROJECT_BINARY_DIR}/cmake_install.cmake"
- COMMAND ${CMAKE_CTEST_COMMAND}
- --build-and-test "${CMAKE_CURRENT_SOURCE_DIR}/test_installed_module"
- "${CMAKE_CURRENT_BINARY_DIR}/test_installed_module"
- --build-noclean
- --build-generator ${CMAKE_GENERATOR}
- $<$<BOOL:${CMAKE_GENERATOR_PLATFORM}>:--build-generator-platform> ${CMAKE_GENERATOR_PLATFORM}
- --build-makeprogram ${CMAKE_MAKE_PROGRAM}
- --build-target check
- --build-options "-DCMAKE_PREFIX_PATH=${PROJECT_BINARY_DIR}/test_install"
- "-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}"
- "-DPYTHON_EXECUTABLE=${PYTHON_EXECUTABLE}"
- "-DPYBIND11_CPP_STANDARD=${PYBIND11_CPP_STANDARD}"
+ if(ARG_INSTALL)
+ add_dependencies(test_${name} mock_install)
+ endif()
+ add_dependencies(test_cmake_build test_${name})
+ endfunction()
+
+ pybind11_add_build_test(subdirectory_function)
+ pybind11_add_build_test(subdirectory_target)
+
+ if(PYBIND11_INSTALL)
+ add_custom_target(mock_install ${CMAKE_COMMAND}
+ "-DCMAKE_INSTALL_PREFIX=${PROJECT_BINARY_DIR}/mock_install"
+ -P "${PROJECT_BINARY_DIR}/cmake_install.cmake"
)
- else()
- add_custom_target(test_installed_target)
- add_custom_target(test_installed_module)
+
+ pybind11_add_build_test(installed_function INSTALL)
+ pybind11_add_build_test(installed_target INSTALL)
endif()
- add_custom_target(test_install)
- add_dependencies(test_install test_installed_target test_installed_module)
endif()
-# And another to show the .so size and, if a previous size, compare it:
-add_custom_command(TARGET pybind11_tests POST_BUILD
- COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_SOURCE_DIR}/tools/libsize.py
- $<TARGET_FILE:pybind11_tests> ${CMAKE_CURRENT_BINARY_DIR}/sosize-$<TARGET_FILE_NAME:pybind11_tests>.txt)
+add_dependencies(check test_cmake_build)
diff --git a/ext/pybind11/tests/conftest.py b/ext/pybind11/tests/conftest.py
index d4335fc6d..5b08004e3 100644
--- a/ext/pybind11/tests/conftest.py
+++ b/ext/pybind11/tests/conftest.py
@@ -10,6 +10,8 @@ import difflib
import re
import sys
import contextlib
+import platform
+import gc
_unicode_marker = re.compile(r'u(\'[^\']*\')')
_long_marker = re.compile(r'([0-9])L')
@@ -101,9 +103,9 @@ class Capture(object):
@pytest.fixture
-def capture(capfd):
- """Extended `capfd` with context manager and custom equality operators"""
- return Capture(capfd)
+def capture(capsys):
+ """Extended `capsys` with context manager and custom equality operators"""
+ return Capture(capsys)
class SanitizedString(object):
@@ -176,6 +178,13 @@ def suppress(exception):
pass
+def gc_collect():
+ ''' Run the garbage collector twice (needed when running
+ reference counting tests with PyPy) '''
+ gc.collect()
+ gc.collect()
+
+
def pytest_namespace():
"""Add import suppression and test requirements to `pytest` namespace"""
try:
@@ -190,6 +199,7 @@ def pytest_namespace():
from pybind11_tests import have_eigen
except ImportError:
have_eigen = False
+ pypy = platform.python_implementation() == "PyPy"
skipif = pytest.mark.skipif
return {
@@ -200,6 +210,8 @@ def pytest_namespace():
reason="eigen and/or numpy are not installed"),
'requires_eigen_and_scipy': skipif(not have_eigen or not scipy,
reason="eigen and/or scipy are not installed"),
+ 'unsupported_on_pypy': skipif(pypy, reason="unsupported on PyPy"),
+ 'gc_collect': gc_collect
}
diff --git a/ext/pybind11/tests/constructor_stats.h b/ext/pybind11/tests/constructor_stats.h
index eb3e49cab..f66ff71df 100644
--- a/ext/pybind11/tests/constructor_stats.h
+++ b/ext/pybind11/tests/constructor_stats.h
@@ -24,7 +24,7 @@ function calls to constructors:
...
}
-You can find various examples of these in several of the existing example .cpp files. (Of course
+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).
@@ -41,7 +41,7 @@ value constructor) for all of the above methods which will be included in the ou
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 example import ConstructorStats
+ from pybind11_tests import ConstructorStats
cstats = ConstructorStats.get(MyClass)
print(cstats.alive())
print(cstats.default_constructions)
@@ -85,27 +85,51 @@ public:
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()");
+ throw std::runtime_error("cstats.destroyed() called with unknown "
+ "instance; potential double-destruction "
+ "or a missing cstats.created()");
}
- int alive() {
+ 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;
+ for (const auto &p : _instances)
+ if (p.second > 0)
+ total += p.second;
return total;
}
@@ -134,6 +158,9 @@ public:
// Gets constructor stats from a C++ type
template <typename T> static ConstructorStats& get() {
+#if defined(PYPY_VERSION)
+ gc();
+#endif
return get(typeid(T));
}
diff --git a/ext/pybind11/tests/object.h b/ext/pybind11/tests/object.h
index 753f654b2..9235f19c2 100644
--- a/ext/pybind11/tests/object.h
+++ b/ext/pybind11/tests/object.h
@@ -164,10 +164,10 @@ public:
operator T* () { return m_ptr; }
/// Return a const pointer to the referenced object
- T* get() { return m_ptr; }
+ T* get_ptr() { return m_ptr; }
/// Return a pointer to the referenced object
- const T* get() const { return m_ptr; }
+ const T* get_ptr() const { return m_ptr; }
private:
T *m_ptr;
};
diff --git a/ext/pybind11/tests/pybind11_tests.cpp b/ext/pybind11/tests/pybind11_tests.cpp
index 9c593eee1..1d805d75b 100644
--- a/ext/pybind11/tests/pybind11_tests.cpp
+++ b/ext/pybind11/tests/pybind11_tests.cpp
@@ -10,6 +10,19 @@
#include "pybind11_tests.h"
#include "constructor_stats.h"
+/*
+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;
@@ -32,7 +45,7 @@ void bind_ConstructorStats(py::module &m) {
}
PYBIND11_PLUGIN(pybind11_tests) {
- py::module m("pybind11_tests", "pybind example plugin");
+ py::module m("pybind11_tests", "pybind testing plugin");
bind_ConstructorStats(m);
diff --git a/ext/pybind11/tests/pytest.ini b/ext/pybind11/tests/pytest.ini
new file mode 100644
index 000000000..401cbe0ad
--- /dev/null
+++ b/ext/pybind11/tests/pytest.ini
@@ -0,0 +1,7 @@
+[pytest]
+minversion = 3.0
+addopts =
+ # show summary of skipped tests
+ -rs
+ # capture only Python print and C++ py::print, but not C output (low-level Python errors)
+ --capture=sys
diff --git a/ext/pybind11/tests/test_alias_initialization.py b/ext/pybind11/tests/test_alias_initialization.py
index 0ed9d2f79..fb90cfc7b 100644
--- a/ext/pybind11/tests/test_alias_initialization.py
+++ b/ext/pybind11/tests/test_alias_initialization.py
@@ -1,10 +1,11 @@
-import gc
+import pytest
def test_alias_delay_initialization1(capture):
- """A only initializes its trampoline class when we inherit from it; if we just
- create and use an A instance directly, the trampoline initialization is bypassed
- and we only initialize an A() instead (for performance reasons).
+ """
+ A only initializes its trampoline class when we inherit from it; if we just
+ create and use an A instance directly, the trampoline initialization is
+ bypassed and we only initialize an A() instead (for performance reasons).
"""
from pybind11_tests import A, call_f
@@ -20,7 +21,7 @@ def test_alias_delay_initialization1(capture):
a = A()
call_f(a)
del a
- gc.collect()
+ pytest.gc_collect()
assert capture == "A.f()"
# Python version
@@ -28,7 +29,7 @@ def test_alias_delay_initialization1(capture):
b = B()
call_f(b)
del b
- gc.collect()
+ pytest.gc_collect()
assert capture == """
PyA.PyA()
PyA.f()
@@ -57,7 +58,7 @@ def test_alias_delay_initialization2(capture):
a2 = A2()
call_f(a2)
del a2
- gc.collect()
+ pytest.gc_collect()
assert capture == """
PyA2.PyA2()
PyA2.f()
@@ -70,7 +71,7 @@ def test_alias_delay_initialization2(capture):
b2 = B2()
call_f(b2)
del b2
- gc.collect()
+ pytest.gc_collect()
assert capture == """
PyA2.PyA2()
PyA2.f()
diff --git a/ext/pybind11/tests/test_buffers.cpp b/ext/pybind11/tests/test_buffers.cpp
index c3a7a9e02..057250d29 100644
--- a/ext/pybind11/tests/test_buffers.cpp
+++ b/ext/pybind11/tests/test_buffers.cpp
@@ -75,7 +75,7 @@ private:
};
test_initializer buffers([](py::module &m) {
- py::class_<Matrix> mtx(m, "Matrix");
+ py::class_<Matrix> mtx(m, "Matrix", py::buffer_protocol());
mtx.def(py::init<size_t, size_t>())
/// Construct from a buffer
diff --git a/ext/pybind11/tests/test_buffers.py b/ext/pybind11/tests/test_buffers.py
index f0ea964d9..24843d95a 100644
--- a/ext/pybind11/tests/test_buffers.py
+++ b/ext/pybind11/tests/test_buffers.py
@@ -1,11 +1,38 @@
import pytest
from pybind11_tests import Matrix, ConstructorStats
+pytestmark = pytest.requires_numpy
+
with pytest.suppress(ImportError):
import numpy as np
-@pytest.requires_numpy
+def test_from_python():
+ with pytest.raises(RuntimeError) as excinfo:
+ 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 = Matrix(m3)
+
+ for i in range(m4.rows()):
+ for j in range(m4.cols()):
+ assert m3[i, j] == m4[i, j]
+
+ cstats = ConstructorStats.get(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
+
+
+# PyPy: Memory leak in the "np.array(m, copy=False)" call
+# https://bitbucket.org/pypy/pypy/issues/2444
+@pytest.unsupported_on_pypy
def test_to_python():
m = Matrix(5, 5)
@@ -23,35 +50,13 @@ def test_to_python():
cstats = ConstructorStats.get(Matrix)
assert cstats.alive() == 1
del m
+ pytest.gc_collect()
assert cstats.alive() == 1
del m2 # holds an m reference
+ pytest.gc_collect()
assert cstats.alive() == 0
assert cstats.values() == ["5x5 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
-
-
-@pytest.requires_numpy
-def test_from_python():
- with pytest.raises(RuntimeError) as excinfo:
- 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 = Matrix(m3)
-
- for i in range(m4.rows()):
- for j in range(m4.cols()):
- assert m3[i, j] == m4[i, j]
-
- cstats = ConstructorStats.get(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
diff --git a/ext/pybind11/tests/test_callbacks.cpp b/ext/pybind11/tests/test_callbacks.cpp
index 31d4e39aa..f89cc1c79 100644
--- a/ext/pybind11/tests/test_callbacks.cpp
+++ b/ext/pybind11/tests/test_callbacks.cpp
@@ -74,6 +74,27 @@ struct Payload {
/// Something to trigger a conversion error
struct Unregistered {};
+class AbstractBase {
+public:
+ virtual unsigned int func() = 0;
+};
+
+void func_accepting_func_accepting_base(std::function<double(AbstractBase&)>) { }
+
+struct MovableObject {
+ bool valid = true;
+
+ MovableObject() = default;
+ MovableObject(const MovableObject &) = default;
+ MovableObject &operator=(const MovableObject &) = default;
+ MovableObject(MovableObject &&o) : valid(o.valid) { o.valid = false; }
+ MovableObject &operator=(MovableObject &&o) {
+ valid = o.valid;
+ o.valid = false;
+ return *this;
+ }
+};
+
test_initializer callbacks([](py::module &m) {
m.def("test_callback1", &test_callback1);
m.def("test_callback2", &test_callback2);
@@ -136,6 +157,7 @@ test_initializer callbacks([](py::module &m) {
return [p]() {
/* p should be cleaned up when the returned function is garbage collected */
+ (void) p;
};
});
@@ -146,4 +168,15 @@ test_initializer callbacks([](py::module &m) {
m.def("test_dummy_function", &test_dummy_function);
// Export the payload constructor statistics for testing purposes:
m.def("payload_cstats", &ConstructorStats::get<Payload>);
+
+ m.def("func_accepting_func_accepting_base",
+ func_accepting_func_accepting_base);
+
+ py::class_<MovableObject>(m, "MovableObject");
+
+ m.def("callback_with_movable", [](std::function<void(MovableObject &)> f) {
+ auto x = MovableObject();
+ f(x); // lvalue reference shouldn't move out object
+ return x.valid; // must still return `true`
+ });
});
diff --git a/ext/pybind11/tests/test_callbacks.py b/ext/pybind11/tests/test_callbacks.py
index c2668aa95..f94e7b64c 100644
--- a/ext/pybind11/tests/test_callbacks.py
+++ b/ext/pybind11/tests/test_callbacks.py
@@ -96,3 +96,9 @@ def test_function_signatures(doc):
assert doc(test_callback3) == "test_callback3(arg0: Callable[[int], int]) -> str"
assert doc(test_callback4) == "test_callback4() -> Callable[[int], int]"
+
+
+def test_movable_object():
+ from pybind11_tests import callback_with_movable
+
+ assert callback_with_movable(lambda _: None) is True
diff --git a/ext/pybind11/tests/test_chrono.cpp b/ext/pybind11/tests/test_chrono.cpp
index b86f57adf..fcc1b6185 100644
--- a/ext/pybind11/tests/test_chrono.cpp
+++ b/ext/pybind11/tests/test_chrono.cpp
@@ -48,6 +48,11 @@ std::chrono::microseconds test_chrono7(std::chrono::microseconds t) {
return t;
}
+// Float durations (issue #719)
+std::chrono::duration<double> test_chrono_float_diff(std::chrono::duration<float> a, std::chrono::duration<float> b) {
+ return a - b;
+}
+
test_initializer chrono([] (py::module &m) {
m.def("test_chrono1", &test_chrono1);
m.def("test_chrono2", &test_chrono2);
@@ -56,4 +61,5 @@ test_initializer chrono([] (py::module &m) {
m.def("test_chrono5", &test_chrono5);
m.def("test_chrono6", &test_chrono6);
m.def("test_chrono7", &test_chrono7);
+ m.def("test_chrono_float_diff", &test_chrono_float_diff);
});
diff --git a/ext/pybind11/tests/test_chrono.py b/ext/pybind11/tests/test_chrono.py
index 94ca55c76..55094edbf 100644
--- a/ext/pybind11/tests/test_chrono.py
+++ b/ext/pybind11/tests/test_chrono.py
@@ -104,7 +104,7 @@ def test_chrono_steady_clock_roundtrip():
def test_floating_point_duration():
- from pybind11_tests import test_chrono7
+ from pybind11_tests import test_chrono7, test_chrono_float_diff
import datetime
# Test using 35.525123 seconds as an example floating point number in seconds
@@ -114,3 +114,7 @@ def test_floating_point_duration():
assert time.seconds == 35
assert 525122 <= time.microseconds <= 525123
+
+ diff = test_chrono_float_diff(43.789012, 1.123456)
+ assert diff.seconds == 42
+ assert 665556 <= diff.microseconds <= 665557
diff --git a/ext/pybind11/tests/test_cmake_build/installed_function/CMakeLists.txt b/ext/pybind11/tests/test_cmake_build/installed_function/CMakeLists.txt
new file mode 100644
index 000000000..e0c20a8a3
--- /dev/null
+++ b/ext/pybind11/tests/test_cmake_build/installed_function/CMakeLists.txt
@@ -0,0 +1,12 @@
+cmake_minimum_required(VERSION 2.8.12)
+project(test_installed_module CXX)
+
+set(CMAKE_MODULE_PATH "")
+
+find_package(pybind11 CONFIG REQUIRED)
+message(STATUS "Found pybind11 v${pybind11_VERSION}: ${pybind11_INCLUDE_DIRS}")
+
+pybind11_add_module(test_cmake_build SHARED NO_EXTRAS ../main.cpp)
+
+add_custom_target(check ${CMAKE_COMMAND} -E env PYTHONPATH=$<TARGET_FILE_DIR:test_cmake_build>
+ ${PYTHON_EXECUTABLE} ${PROJECT_SOURCE_DIR}/../test.py ${PROJECT_NAME})
diff --git a/ext/pybind11/tests/test_cmake_build/installed_target/CMakeLists.txt b/ext/pybind11/tests/test_cmake_build/installed_target/CMakeLists.txt
new file mode 100644
index 000000000..cd3ae6f7d
--- /dev/null
+++ b/ext/pybind11/tests/test_cmake_build/installed_target/CMakeLists.txt
@@ -0,0 +1,22 @@
+cmake_minimum_required(VERSION 3.0)
+project(test_installed_target CXX)
+
+set(CMAKE_MODULE_PATH "")
+
+find_package(pybind11 CONFIG REQUIRED)
+message(STATUS "Found pybind11 v${pybind11_VERSION}: ${pybind11_INCLUDE_DIRS}")
+
+add_library(test_cmake_build MODULE ../main.cpp)
+
+target_link_libraries(test_cmake_build PRIVATE pybind11::module)
+
+# make sure result is, for example, test_installed_target.so, not libtest_installed_target.dylib
+set_target_properties(test_cmake_build PROPERTIES PREFIX "${PYTHON_MODULE_PREFIX}"
+ SUFFIX "${PYTHON_MODULE_EXTENSION}")
+
+# Do not treat includes from IMPORTED target as SYSTEM (Python headers in pybind11::module).
+# This may be needed to resolve header conflicts, e.g. between Python release and debug headers.
+set_target_properties(test_cmake_build PROPERTIES NO_SYSTEM_FROM_IMPORTED ON)
+
+add_custom_target(check ${CMAKE_COMMAND} -E env PYTHONPATH=$<TARGET_FILE_DIR:test_cmake_build>
+ ${PYTHON_EXECUTABLE} ${PROJECT_SOURCE_DIR}/../test.py ${PROJECT_NAME})
diff --git a/ext/pybind11/tests/test_installed_target/main.cpp b/ext/pybind11/tests/test_cmake_build/main.cpp
index 2a84c11ce..e0f5b69c9 100644
--- a/ext/pybind11/tests/test_installed_target/main.cpp
+++ b/ext/pybind11/tests/test_cmake_build/main.cpp
@@ -1,8 +1,8 @@
#include <pybind11/pybind11.h>
namespace py = pybind11;
-PYBIND11_PLUGIN(test_installed_target) {
- py::module m("test_installed_target");
+PYBIND11_PLUGIN(test_cmake_build) {
+ py::module m("test_cmake_build");
m.def("add", [](int i, int j) { return i + j; });
diff --git a/ext/pybind11/tests/test_cmake_build/subdirectory_function/CMakeLists.txt b/ext/pybind11/tests/test_cmake_build/subdirectory_function/CMakeLists.txt
new file mode 100644
index 000000000..278007aeb
--- /dev/null
+++ b/ext/pybind11/tests/test_cmake_build/subdirectory_function/CMakeLists.txt
@@ -0,0 +1,8 @@
+cmake_minimum_required(VERSION 2.8.12)
+project(test_subdirectory_module CXX)
+
+add_subdirectory(${PYBIND11_PROJECT_DIR} pybind11)
+pybind11_add_module(test_cmake_build THIN_LTO ../main.cpp)
+
+add_custom_target(check ${CMAKE_COMMAND} -E env PYTHONPATH=$<TARGET_FILE_DIR:test_cmake_build>
+ ${PYTHON_EXECUTABLE} ${PROJECT_SOURCE_DIR}/../test.py ${PROJECT_NAME})
diff --git a/ext/pybind11/tests/test_cmake_build/subdirectory_target/CMakeLists.txt b/ext/pybind11/tests/test_cmake_build/subdirectory_target/CMakeLists.txt
new file mode 100644
index 000000000..6b142d62a
--- /dev/null
+++ b/ext/pybind11/tests/test_cmake_build/subdirectory_target/CMakeLists.txt
@@ -0,0 +1,15 @@
+cmake_minimum_required(VERSION 3.0)
+project(test_subdirectory_target CXX)
+
+add_subdirectory(${PYBIND11_PROJECT_DIR} pybind11)
+
+add_library(test_cmake_build MODULE ../main.cpp)
+
+target_link_libraries(test_cmake_build PRIVATE pybind11::module)
+
+# make sure result is, for example, test_installed_target.so, not libtest_installed_target.dylib
+set_target_properties(test_cmake_build PROPERTIES PREFIX "${PYTHON_MODULE_PREFIX}"
+ SUFFIX "${PYTHON_MODULE_EXTENSION}")
+
+add_custom_target(check ${CMAKE_COMMAND} -E env PYTHONPATH=$<TARGET_FILE_DIR:test_cmake_build>
+ ${PYTHON_EXECUTABLE} ${PROJECT_SOURCE_DIR}/../test.py ${PROJECT_NAME})
diff --git a/ext/pybind11/tests/test_cmake_build/test.py b/ext/pybind11/tests/test_cmake_build/test.py
new file mode 100644
index 000000000..1467a61dc
--- /dev/null
+++ b/ext/pybind11/tests/test_cmake_build/test.py
@@ -0,0 +1,5 @@
+import sys
+import test_cmake_build
+
+assert test_cmake_build.add(1, 2) == 3
+print("{} imports, runs, and adds: 1 + 2 = 3".format(sys.argv[1]))
diff --git a/ext/pybind11/tests/test_constants_and_functions.cpp b/ext/pybind11/tests/test_constants_and_functions.cpp
index c8c0392c9..653bdf6b6 100644
--- a/ext/pybind11/tests/test_constants_and_functions.cpp
+++ b/ext/pybind11/tests/test_constants_and_functions.cpp
@@ -41,6 +41,26 @@ std::string print_bytes(py::bytes bytes) {
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 {
+int f1(int x) noexcept { return x+1; }
+int f2(int x) noexcept(true) { return x+2; }
+int f3(int x) noexcept(false) { return x+3; }
+int f4(int x) throw() { return x+4; } // Deprecated equivalent to noexcept(true)
+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; }
+ int m7(int x) throw() { return x-7; }
+ int m8(int x) const throw() { return x-8; }
+};
+}
+
+
test_initializer constants_and_functions([](py::module &m) {
m.attr("some_constant") = py::int_(14);
@@ -63,4 +83,22 @@ test_initializer constants_and_functions([](py::module &m) {
m.def("return_bytes", &return_bytes);
m.def("print_bytes", &print_bytes);
+
+ using namespace test_exc_sp;
+ py::module m2 = m.def_submodule("exc_sp");
+ py::class_<C>(m2, "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)
+ ;
+ m2.def("f1", f1);
+ m2.def("f2", f2);
+ m2.def("f3", f3);
+ m2.def("f4", f4);
});
diff --git a/ext/pybind11/tests/test_constants_and_functions.py b/ext/pybind11/tests/test_constants_and_functions.py
index d13d3af1b..2a570d2e5 100644
--- a/ext/pybind11/tests/test_constants_and_functions.py
+++ b/ext/pybind11/tests/test_constants_and_functions.py
@@ -22,3 +22,22 @@ def test_bytes():
from pybind11_tests import return_bytes, print_bytes
assert print_bytes(return_bytes()) == "bytes[1 0 2 0]"
+
+
+def test_exception_specifiers():
+ from pybind11_tests.exc_sp import C, f1, f2, f3, f4
+
+ c = 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 f1(33) == 34
+ assert f2(53) == 55
+ assert f3(86) == 89
+ assert f4(140) == 144
diff --git a/ext/pybind11/tests/test_docstring_options.cpp b/ext/pybind11/tests/test_docstring_options.cpp
index 74178c272..9a9297cc3 100644
--- a/ext/pybind11/tests/test_docstring_options.cpp
+++ b/ext/pybind11/tests/test_docstring_options.cpp
@@ -24,6 +24,15 @@ test_initializer docstring_generation([](py::module &m) {
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"));
diff --git a/ext/pybind11/tests/test_docstring_options.py b/ext/pybind11/tests/test_docstring_options.py
index 66ad6b89f..5e40f6868 100644
--- a/ext/pybind11/tests/test_docstring_options.py
+++ b/ext/pybind11/tests/test_docstring_options.py
@@ -3,13 +3,23 @@
def test_docstring_options():
from pybind11_tests import (test_function1, test_function2, test_function3,
test_function4, test_function5, test_function6,
- test_function7, DocstringTestFoo)
+ test_function7, DocstringTestFoo,
+ test_overloaded1, test_overloaded2, test_overloaded3)
# options.disable_function_signatures()
assert not test_function1.__doc__
assert test_function2.__doc__ == "A custom docstring"
+ # docstring specified on just the first overload definition:
+ assert test_overloaded1.__doc__ == "Overload docstring"
+
+ # docstring on both overloads:
+ assert test_overloaded2.__doc__ == "overload docstring 1\noverload docstring 2"
+
+ # docstring on only second overload:
+ assert test_overloaded3.__doc__ == "Overload docstr"
+
# options.enable_function_signatures()
assert test_function3.__doc__ .startswith("test_function3(a: int, b: int) -> None")
diff --git a/ext/pybind11/tests/test_eigen.cpp b/ext/pybind11/tests/test_eigen.cpp
index 588cdceb3..f2ec8fd2e 100644
--- a/ext/pybind11/tests/test_eigen.cpp
+++ b/ext/pybind11/tests/test_eigen.cpp
@@ -8,55 +8,160 @@
*/
#include "pybind11_tests.h"
+#include "constructor_stats.h"
#include <pybind11/eigen.h>
#include <Eigen/Cholesky>
-Eigen::VectorXf double_col(const Eigen::VectorXf& x)
-{ return 2.0f * x; }
+using MatrixXdR = Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>;
-Eigen::RowVectorXf double_row(const Eigen::RowVectorXf& x)
-{ return 2.0f * x; }
-Eigen::MatrixXf double_mat_cm(const Eigen::MatrixXf& x)
-{ return 2.0f * x; }
-// Different ways of passing via Eigen::Ref; the first and second are the Eigen-recommended
-Eigen::MatrixXd cholesky1(Eigen::Ref<Eigen::MatrixXd> &x) { return x.llt().matrixL(); }
-Eigen::MatrixXd cholesky2(const Eigen::Ref<const Eigen::MatrixXd> &x) { return x.llt().matrixL(); }
-Eigen::MatrixXd cholesky3(const Eigen::Ref<Eigen::MatrixXd> &x) { return x.llt().matrixL(); }
-Eigen::MatrixXd cholesky4(Eigen::Ref<const Eigen::MatrixXd> &x) { return x.llt().matrixL(); }
-Eigen::MatrixXd cholesky5(Eigen::Ref<Eigen::MatrixXd> x) { return x.llt().matrixL(); }
-Eigen::MatrixXd cholesky6(Eigen::Ref<const Eigen::MatrixXd> x) { return x.llt().matrixL(); }
+// 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;
+}
-typedef Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor> MatrixXfRowMajor;
-MatrixXfRowMajor double_mat_rm(const MatrixXfRowMajor& x)
-{ return 2.0f * x; }
+// 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(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;
+ return ret;
+}
+
+struct CustomOperatorNew {
+ CustomOperatorNew() = default;
+
+ Eigen::Matrix4d a = Eigen::Matrix4d::Zero();
+ Eigen::Matrix4d b = Eigen::Matrix4d::Identity();
+
+ EIGEN_MAKE_ALIGNED_OPERATOR_NEW;
+};
test_initializer eigen([](py::module &m) {
typedef Eigen::Matrix<float, 5, 6, Eigen::RowMajor> FixedMatrixR;
typedef Eigen::Matrix<float, 5, 6> FixedMatrixC;
typedef Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor> DenseMatrixR;
typedef Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic> DenseMatrixC;
+ typedef Eigen::Matrix<float, 4, Eigen::Dynamic> FourRowMatrixC;
+ typedef Eigen::Matrix<float, Eigen::Dynamic, 4> FourColMatrixC;
+ typedef Eigen::Matrix<float, 4, Eigen::Dynamic> FourRowMatrixR;
+ typedef Eigen::Matrix<float, Eigen::Dynamic, 4> FourColMatrixR;
typedef Eigen::SparseMatrix<float, Eigen::RowMajor> SparseMatrixR;
typedef Eigen::SparseMatrix<float> SparseMatrixC;
m.attr("have_eigen") = true;
- // Non-symmetric matrix with zero elements
- 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;
-
- m.def("double_col", &double_col);
- m.def("double_row", &double_row);
- m.def("double_mat_cm", &double_mat_cm);
- m.def("double_mat_rm", &double_mat_rm);
- m.def("cholesky1", &cholesky1);
- m.def("cholesky2", &cholesky2);
- m.def("cholesky3", &cholesky3);
- m.def("cholesky4", &cholesky4);
- m.def("cholesky5", &cholesky5);
- m.def("cholesky6", &cholesky6);
+ 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", [](Eigen::MatrixXf x) -> Eigen::MatrixXf { return 2.0f * x; });
+ m.def("double_mat_rm", [](DenseMatrixR x) -> DenseMatrixR { return 2.0f * x; });
+
+ // Different ways of passing via Eigen::Ref; the first and second are the Eigen-recommended
+ m.def("cholesky1", [](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", [](Eigen::Ref<const MatrixXdR> x) -> Eigen::MatrixXd { return x.llt().matrixL(); });
+
+ // 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 varibles)
+ 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()); });
+ // Just the corners (via a Map instead of a Ref):
+ m.def("get_cm_corners", []() {
+ auto &x = get_cm();
+ return py::EigenDMap<Eigen::Matrix2d>(
+ x.data(),
+ py::EigenDStride(x.outerStride() * (x.rows() - 1), x.innerStride() * (x.cols() - 1)));
+ });
+ m.def("get_cm_corners_const", []() {
+ const auto &x = get_cm();
+ return py::EigenDMap<const Eigen::Matrix2d>(
+ x.data(),
+ py::EigenDStride(x.outerStride() * (x.rows() - 1), x.innerStride() * (x.cols() - 1)));
+ });
+
+ 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(); });
@@ -68,6 +173,52 @@ test_initializer eigen([](py::module &m) {
return x.block(start_row, start_col, block_rows, block_cols);
});
+ // 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); }
+ 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)
+ ;
+
// Returns a DiagonalMatrix with diagonal (1,2,3,...)
m.def("incr_diag", [](int k) {
Eigen::DiagonalMatrix<int, Eigen::Dynamic> m(k);
@@ -84,51 +235,60 @@ test_initializer eigen([](py::module &m) {
return m.selfadjointView<Eigen::Upper>();
});
- m.def("fixed_r", [mat]() -> FixedMatrixR {
- return FixedMatrixR(mat);
- });
-
- m.def("fixed_c", [mat]() -> FixedMatrixC {
- return FixedMatrixC(mat);
- });
-
- m.def("fixed_passthrough_r", [](const FixedMatrixR &m) -> FixedMatrixR {
- return m;
- });
-
- m.def("fixed_passthrough_c", [](const FixedMatrixC &m) -> FixedMatrixC {
- return m;
- });
-
- m.def("dense_r", [mat]() -> DenseMatrixR {
- return DenseMatrixR(mat);
- });
-
- m.def("dense_c", [mat]() -> DenseMatrixC {
- return DenseMatrixC(mat);
- });
+ // 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;
- m.def("dense_passthrough_r", [](const DenseMatrixR &m) -> DenseMatrixR {
- return m;
- });
+ m.def("fixed_r", [mat]() -> FixedMatrixR { return FixedMatrixR(mat); });
+ 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; });
+ m.def("fixed_mutator_r", [](Eigen::Ref<FixedMatrixR>) {});
+ m.def("fixed_mutator_c", [](Eigen::Ref<FixedMatrixC>) {});
+ m.def("fixed_mutator_a", [](py::EigenDRef<FixedMatrixC>) {});
+ 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; });
+ m.def("sparse_r", [mat]() -> SparseMatrixR { 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; });
+ 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; });
- m.def("dense_passthrough_c", [](const DenseMatrixC &m) -> DenseMatrixC {
- return m;
- });
+ // 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); });
- m.def("sparse_r", [mat]() -> SparseMatrixR {
- return Eigen::SparseView<Eigen::MatrixXf>(mat);
- });
- m.def("sparse_c", [mat]() -> SparseMatrixC {
- return Eigen::SparseView<Eigen::MatrixXf>(mat);
- });
+ // Test that we can prevent copying into an argument that would normally copy: First a version
+ // that would allow copying (if types or strides don't match) for comparison:
+ m.def("get_elem", &get_elem);
+ // Now this alternative that calls the tells pybind to fail rather than copy:
+ m.def("get_elem_nocopy", [](Eigen::Ref<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());
- m.def("sparse_passthrough_r", [](const SparseMatrixR &m) -> SparseMatrixR {
- return m;
- });
+ // 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());
- m.def("sparse_passthrough_c", [](const SparseMatrixC &m) -> SparseMatrixC {
- return m;
- });
+ py::class_<CustomOperatorNew>(m, "CustomOperatorNew")
+ .def(py::init<>())
+ .def_readonly("a", &CustomOperatorNew::a)
+ .def_readonly("b", &CustomOperatorNew::b);
});
diff --git a/ext/pybind11/tests/test_eigen.py b/ext/pybind11/tests/test_eigen.py
index b0092fc8b..df08edf3d 100644
--- a/ext/pybind11/tests/test_eigen.py
+++ b/ext/pybind11/tests/test_eigen.py
@@ -1,9 +1,11 @@
import pytest
+pytestmark = pytest.requires_eigen_and_numpy
+
with pytest.suppress(ImportError):
import numpy as np
- ref = np.array([[ 0, 3, 0, 0, 0, 11],
+ ref = np.array([[ 0., 3, 0, 0, 0, 11],
[22, 0, 0, 0, 17, 11],
[ 7, 5, 0, 1, 0, 11],
[ 0, 0, 0, 0, 0, 11],
@@ -18,50 +20,140 @@ def assert_sparse_equal_ref(sparse_mat):
assert_equal_ref(sparse_mat.todense())
-@pytest.requires_eigen_and_numpy
def test_fixed():
- from pybind11_tests import fixed_r, fixed_c, fixed_passthrough_r, fixed_passthrough_c
+ from pybind11_tests import fixed_r, fixed_c, fixed_copy_r, fixed_copy_c
assert_equal_ref(fixed_c())
assert_equal_ref(fixed_r())
- assert_equal_ref(fixed_passthrough_r(fixed_r()))
- assert_equal_ref(fixed_passthrough_c(fixed_c()))
- assert_equal_ref(fixed_passthrough_r(fixed_c()))
- assert_equal_ref(fixed_passthrough_c(fixed_r()))
+ assert_equal_ref(fixed_copy_r(fixed_r()))
+ assert_equal_ref(fixed_copy_c(fixed_c()))
+ assert_equal_ref(fixed_copy_r(fixed_c()))
+ assert_equal_ref(fixed_copy_c(fixed_r()))
-@pytest.requires_eigen_and_numpy
def test_dense():
- from pybind11_tests import dense_r, dense_c, dense_passthrough_r, dense_passthrough_c
+ from pybind11_tests import dense_r, dense_c, dense_copy_r, dense_copy_c
assert_equal_ref(dense_r())
assert_equal_ref(dense_c())
- assert_equal_ref(dense_passthrough_r(dense_r()))
- assert_equal_ref(dense_passthrough_c(dense_c()))
- assert_equal_ref(dense_passthrough_r(dense_c()))
- assert_equal_ref(dense_passthrough_c(dense_r()))
+ assert_equal_ref(dense_copy_r(dense_r()))
+ assert_equal_ref(dense_copy_c(dense_c()))
+ assert_equal_ref(dense_copy_r(dense_c()))
+ assert_equal_ref(dense_copy_c(dense_r()))
+
+
+def test_partially_fixed():
+ from pybind11_tests import (partial_copy_four_rm_r, partial_copy_four_rm_c,
+ partial_copy_four_cm_r, partial_copy_four_cm_c)
+
+ ref2 = np.array([[0., 1, 2, 3], [4, 5, 6, 7], [8, 9, 10, 11], [12, 13, 14, 15]])
+ np.testing.assert_array_equal(partial_copy_four_rm_r(ref2), ref2)
+ np.testing.assert_array_equal(partial_copy_four_rm_c(ref2), ref2)
+ np.testing.assert_array_equal(partial_copy_four_rm_r(ref2[:, 1]), ref2[:, [1]])
+ np.testing.assert_array_equal(partial_copy_four_rm_c(ref2[0, :]), ref2[[0], :])
+ np.testing.assert_array_equal(partial_copy_four_rm_r(ref2[:, (0, 2)]), ref2[:, (0, 2)])
+ np.testing.assert_array_equal(
+ partial_copy_four_rm_c(ref2[(3, 1, 2), :]), ref2[(3, 1, 2), :])
+
+ np.testing.assert_array_equal(partial_copy_four_cm_r(ref2), ref2)
+ np.testing.assert_array_equal(partial_copy_four_cm_c(ref2), ref2)
+ np.testing.assert_array_equal(partial_copy_four_cm_r(ref2[:, 1]), ref2[:, [1]])
+ np.testing.assert_array_equal(partial_copy_four_cm_c(ref2[0, :]), ref2[[0], :])
+ np.testing.assert_array_equal(partial_copy_four_cm_r(ref2[:, (0, 2)]), ref2[:, (0, 2)])
+ np.testing.assert_array_equal(
+ partial_copy_four_cm_c(ref2[(3, 1, 2), :]), ref2[(3, 1, 2), :])
+
+
+def test_mutator_descriptors():
+ from pybind11_tests import fixed_mutator_r, fixed_mutator_c, fixed_mutator_a
+ zr = np.arange(30, dtype='float32').reshape(5, 6) # row-major
+ zc = zr.reshape(6, 5).transpose() # column-major
+
+ fixed_mutator_r(zr)
+ fixed_mutator_c(zc)
+ fixed_mutator_a(zr)
+ fixed_mutator_a(zc)
+ with pytest.raises(TypeError) as excinfo:
+ fixed_mutator_r(zc)
+ assert ('(numpy.ndarray[float32[5, 6], flags.writeable, flags.c_contiguous]) -> arg0: None'
+ in str(excinfo.value))
+ with pytest.raises(TypeError) as excinfo:
+ fixed_mutator_c(zr)
+ assert ('(numpy.ndarray[float32[5, 6], flags.writeable, flags.f_contiguous]) -> arg0: None'
+ in str(excinfo.value))
+ with pytest.raises(TypeError) as excinfo:
+ fixed_mutator_a(np.array([[1, 2], [3, 4]], dtype='float32'))
+ assert ('(numpy.ndarray[float32[5, 6], flags.writeable]) -> arg0: None'
+ in str(excinfo.value))
+ zr.flags.writeable = False
+ with pytest.raises(TypeError):
+ fixed_mutator_r(zr)
+ with pytest.raises(TypeError):
+ fixed_mutator_a(zr)
+
+
+def test_cpp_casting():
+ from pybind11_tests import (cpp_copy, cpp_ref_c, cpp_ref_r, cpp_ref_any,
+ fixed_r, fixed_c, get_cm_ref, get_rm_ref, ReturnTester)
+ assert cpp_copy(fixed_r()) == 22.
+ assert cpp_copy(fixed_c()) == 22.
+ z = np.array([[5., 6], [7, 8]])
+ assert cpp_copy(z) == 7.
+ assert cpp_copy(get_cm_ref()) == 21.
+ assert cpp_copy(get_rm_ref()) == 21.
+ assert cpp_ref_c(get_cm_ref()) == 21.
+ assert cpp_ref_r(get_rm_ref()) == 21.
+ with pytest.raises(RuntimeError) as excinfo:
+ # Can't reference fixed_c: it contains floats, cpp_ref_any wants doubles
+ cpp_ref_any(fixed_c())
+ assert 'Unable to cast Python instance' in str(excinfo.value)
+ with pytest.raises(RuntimeError) as excinfo:
+ # Can't reference fixed_r: it contains floats, cpp_ref_any wants doubles
+ cpp_ref_any(fixed_r())
+ assert 'Unable to cast Python instance' in str(excinfo.value)
+ assert cpp_ref_any(ReturnTester.create()) == 1.
+
+ assert cpp_ref_any(get_cm_ref()) == 21.
+ assert cpp_ref_any(get_cm_ref()) == 21.
+
+
+def test_pass_readonly_array():
+ from pybind11_tests import fixed_copy_r, fixed_r, fixed_r_const
+ z = np.full((5, 6), 42.0)
+ z.flags.writeable = False
+ np.testing.assert_array_equal(z, fixed_copy_r(z))
+ np.testing.assert_array_equal(fixed_r_const(), fixed_r())
+ assert not fixed_r_const().flags.writeable
+ np.testing.assert_array_equal(fixed_copy_r(fixed_r_const()), fixed_r_const())
-@pytest.requires_eigen_and_numpy
def test_nonunit_stride_from_python():
- from pybind11_tests import double_row, double_col, double_mat_cm, double_mat_rm
+ from pybind11_tests import (
+ double_row, double_col, double_complex, double_mat_cm, double_mat_rm,
+ double_threec, double_threer)
counting_mat = np.arange(9.0, dtype=np.float32).reshape((3, 3))
- first_row = counting_mat[0, :]
- first_col = counting_mat[:, 0]
- assert np.array_equal(double_row(first_row), 2.0 * first_row)
- assert np.array_equal(double_col(first_row), 2.0 * first_row)
- assert np.array_equal(double_row(first_col), 2.0 * first_col)
- assert np.array_equal(double_col(first_col), 2.0 * first_col)
+ second_row = counting_mat[1, :]
+ second_col = counting_mat[:, 1]
+ np.testing.assert_array_equal(double_row(second_row), 2.0 * second_row)
+ np.testing.assert_array_equal(double_col(second_row), 2.0 * second_row)
+ np.testing.assert_array_equal(double_complex(second_row), 2.0 * second_row)
+ np.testing.assert_array_equal(double_row(second_col), 2.0 * second_col)
+ np.testing.assert_array_equal(double_col(second_col), 2.0 * second_col)
+ np.testing.assert_array_equal(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 slice_idx, ref_mat in enumerate(slices):
- assert np.array_equal(double_mat_cm(ref_mat), 2.0 * ref_mat)
- assert np.array_equal(double_mat_rm(ref_mat), 2.0 * ref_mat)
+ np.testing.assert_array_equal(double_mat_cm(ref_mat), 2.0 * ref_mat)
+ np.testing.assert_array_equal(double_mat_rm(ref_mat), 2.0 * ref_mat)
+
+ # Mutator:
+ double_threer(second_row)
+ double_threec(second_col)
+ np.testing.assert_array_equal(counting_mat, [[0., 2, 2], [6, 16, 10], [6, 14, 8]])
-@pytest.requires_eigen_and_numpy
def test_nonunit_stride_to_python():
from pybind11_tests import diagonal, diagonal_1, diagonal_n, block
@@ -75,23 +167,390 @@ def test_nonunit_stride_to_python():
assert np.all(block(ref, 1, 4, 3, 2) == ref[1:4, 4:])
-@pytest.requires_eigen_and_numpy
def test_eigen_ref_to_python():
- from pybind11_tests import cholesky1, cholesky2, cholesky3, cholesky4, cholesky5, cholesky6
+ from pybind11_tests import cholesky1, cholesky2, cholesky3, cholesky4
- chols = [cholesky1, cholesky2, cholesky3, cholesky4, cholesky5, cholesky6]
+ chols = [cholesky1, cholesky2, cholesky3, cholesky4]
for i, chol in enumerate(chols, start=1):
- mymat = chol(np.array([[1, 2, 4], [2, 13, 23], [4, 23, 77]]))
+ mymat = chol(np.array([[1., 2, 4], [2, 13, 23], [4, 23, 77]]))
assert np.all(mymat == np.array([[1, 0, 0], [2, 3, 0], [4, 5, 6]])), "cholesky{}".format(i)
-@pytest.requires_eigen_and_numpy
+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"""
+ from pybind11_tests import ReturnTester
+ master = np.ones((10, 10))
+ a = 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):
+ from pybind11_tests import ConstructorStats
+ 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():
+ from pybind11_tests import ReturnTester, ConstructorStats
+ a = ReturnTester()
+
+ cstats = ConstructorStats.get(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 [ReturnTester.get, ReturnTester.get_ptr, ReturnTester.view,
+ ReturnTester.view_ptr, ReturnTester.ref_safe, ReturnTester.ref_const_safe,
+ ReturnTester.corners, ReturnTester.corners_const]:
+ assert_keeps_alive(ReturnTester, meth)
+
+ for meth in [ReturnTester.block_safe, ReturnTester.block_const]:
+ assert_keeps_alive(ReturnTester, meth, 4, 3, 2, 1)
+
+
+def test_eigen_ref_mutators():
+ """Tests whether Eigen can mutate numpy values"""
+ from pybind11_tests import add_rm, add_cm, add_any, add1, add2
+ orig = np.array([[1., 2, 3], [4, 5, 6], [7, 8, 9]])
+ zr = np.array(orig)
+ zc = np.array(orig, order='F')
+ add_rm(zr, 1, 0, 100)
+ assert np.all(zr == np.array([[1., 2, 3], [104, 5, 6], [7, 8, 9]]))
+ add_cm(zc, 1, 0, 200)
+ assert np.all(zc == np.array([[1., 2, 3], [204, 5, 6], [7, 8, 9]]))
+
+ add_any(zr, 1, 0, 20)
+ assert np.all(zr == np.array([[1., 2, 3], [124, 5, 6], [7, 8, 9]]))
+ add_any(zc, 1, 0, 10)
+ assert np.all(zc == np.array([[1., 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):
+ add_rm(zc, 1, 0, 1)
+ with pytest.raises(TypeError):
+ add_cm(zr, 1, 0, 1)
+
+ # Overloads:
+ add1(zr, 1, 0, -100)
+ add2(zr, 1, 0, -20)
+ assert np.all(zr == orig)
+ add1(zc, 1, 0, -200)
+ 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., 3], [7, 9]]))
+ assert np.all(cornersc == np.array([[1., 3], [7, 9]]))
+
+ with pytest.raises(TypeError):
+ add_rm(cornersr, 0, 1, 25)
+ with pytest.raises(TypeError):
+ add_cm(cornersr, 0, 1, 25)
+ with pytest.raises(TypeError):
+ add_rm(cornersc, 0, 1, 25)
+ with pytest.raises(TypeError):
+ add_cm(cornersc, 0, 1, 25)
+ add_any(cornersr, 0, 1, 25)
+ add_any(cornersc, 0, 1, 44)
+ assert np.all(zr == np.array([[1., 2, 28], [4, 5, 6], [7, 8, 9]]))
+ assert np.all(zc == np.array([[1., 2, 47], [4, 5, 6], [7, 8, 9]]))
+
+ # 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):
+ add_rm(zro, 0, 0, 0)
+ with pytest.raises(TypeError):
+ add_any(zro, 0, 0, 0)
+ with pytest.raises(TypeError):
+ add1(zro, 0, 0, 0)
+ with pytest.raises(TypeError):
+ 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):
+ add_rm(zi)
+
+
+def test_numpy_ref_mutators():
+ """Tests numpy mutating Eigen matrices (for returned Eigen::Ref<...>s)"""
+ from pybind11_tests import (
+ get_cm_ref, get_cm_const_ref, get_rm_ref, get_rm_const_ref, reset_refs)
+ reset_refs() # In case another test already changed it
+
+ zc = get_cm_ref()
+ zcro = get_cm_const_ref()
+ zr = get_rm_ref()
+ zrro = 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., 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(get_cm_ref() == expect)
+
+ zr[1, 2] = 99
+ assert np.all(zr == expect)
+ assert np.all(zrro == expect)
+ assert np.all(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(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"""
+ from pybind11_tests import (
+ incr_matrix, get_cm_ref, incr_matrix_any, even_cols, even_rows, reset_refs)
+ reset_refs() # In case another test already changed it
+
+ z = get_cm_ref() # numpy -> eigen
+ z[0, 2] -= 3
+ z2 = incr_matrix(z, 1) # numpy -> eigen -> numpy -> eigen
+ z2[1, 1] += 6
+ z3 = incr_matrix(z, 2) # (numpy -> eigen)^3
+ z3[2, 2] += -5
+ z4 = incr_matrix(z, 3) # (numpy -> eigen)^4
+ z4[1, 1] -= 1
+ z5 = 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., 22, 20], [31, 37, 33], [41, 42, 38]])
+ assert np.all(z == expect)
+
+ y = np.array(range(100), dtype='float64').reshape(10, 10)
+ y2 = incr_matrix_any(y, 10) # np -> eigen -> np
+ y3 = incr_matrix_any(y2[0::2, 0::2], -33) # np -> eigen -> np slice -> np -> eigen -> np
+ y4 = even_rows(y3) # numpy -> eigen slice -> (... y3)
+ y5 = even_cols(y4) # numpy -> eigen slice -> (... y4)
+ y6 = 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():
+ from pybind11_tests import get_elem, get_elem_nocopy, get_elem_rm_nocopy
+ # 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 get_elem(int_matrix_colmajor) == 8
+ assert get_elem(dbl_matrix_colmajor) == 8
+ assert get_elem(int_matrix_rowmajor) == 8
+ assert get_elem(dbl_matrix_rowmajor) == 8
+
+ # All but the second should fail with get_elem_nocopy:
+ with pytest.raises(TypeError) as excinfo:
+ get_elem_nocopy(int_matrix_colmajor)
+ assert ('get_elem_nocopy(): incompatible function arguments.' in str(excinfo.value) and
+ ', flags.f_contiguous' in str(excinfo.value))
+ assert get_elem_nocopy(dbl_matrix_colmajor) == 8
+ with pytest.raises(TypeError) as excinfo:
+ 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:
+ 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:
+ 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:
+ get_elem_rm_nocopy(dbl_matrix_colmajor)
+ assert ('get_elem_rm_nocopy(): incompatible function arguments.' in str(excinfo.value) and
+ ', flags.c_contiguous' in str(excinfo.value))
+ assert get_elem_rm_nocopy(int_matrix_rowmajor) == 8
+ with pytest.raises(TypeError) as excinfo:
+ 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_special_matrix_objects():
from pybind11_tests import incr_diag, symmetric_upper, symmetric_lower
- assert np.all(incr_diag(7) == np.diag([1, 2, 3, 4, 5, 6, 7]))
+ assert np.all(incr_diag(7) == np.diag([1., 2, 3, 4, 5, 6, 7]))
- asymm = np.array([[ 1, 2, 3, 4],
+ asymm = np.array([[ 1., 2, 3, 4],
[ 5, 6, 7, 8],
[ 9, 10, 11, 12],
[13, 14, 15, 16]])
@@ -106,9 +565,8 @@ def test_special_matrix_objects():
assert np.all(symmetric_upper(asymm) == symm_upper)
-@pytest.requires_eigen_and_numpy
def test_dense_signature(doc):
- from pybind11_tests import double_col, double_row, double_mat_rm
+ from pybind11_tests import double_col, double_row, double_complex, double_mat_rm
assert doc(double_col) == """
double_col(arg0: numpy.ndarray[float32[m, 1]]) -> numpy.ndarray[float32[m, 1]]
@@ -116,6 +574,9 @@ def test_dense_signature(doc):
assert doc(double_row) == """
double_row(arg0: numpy.ndarray[float32[1, n]]) -> numpy.ndarray[float32[1, n]]
"""
+ assert doc(double_complex) == """
+ double_complex(arg0: numpy.ndarray[complex64[m, 1]]) -> numpy.ndarray[complex64[m, 1]]
+ """
assert doc(double_mat_rm) == """
double_mat_rm(arg0: numpy.ndarray[float32[m, n]]) -> numpy.ndarray[float32[m, n]]
"""
@@ -123,23 +584,43 @@ def test_dense_signature(doc):
@pytest.requires_eigen_and_scipy
def test_sparse():
- from pybind11_tests import sparse_r, sparse_c, sparse_passthrough_r, sparse_passthrough_c
+ from pybind11_tests import sparse_r, sparse_c, sparse_copy_r, sparse_copy_c
assert_sparse_equal_ref(sparse_r())
assert_sparse_equal_ref(sparse_c())
- assert_sparse_equal_ref(sparse_passthrough_r(sparse_r()))
- assert_sparse_equal_ref(sparse_passthrough_c(sparse_c()))
- assert_sparse_equal_ref(sparse_passthrough_r(sparse_c()))
- assert_sparse_equal_ref(sparse_passthrough_c(sparse_r()))
+ assert_sparse_equal_ref(sparse_copy_r(sparse_r()))
+ assert_sparse_equal_ref(sparse_copy_c(sparse_c()))
+ assert_sparse_equal_ref(sparse_copy_r(sparse_c()))
+ assert_sparse_equal_ref(sparse_copy_c(sparse_r()))
@pytest.requires_eigen_and_scipy
def test_sparse_signature(doc):
- from pybind11_tests import sparse_passthrough_r, sparse_passthrough_c
+ from pybind11_tests import sparse_copy_r, sparse_copy_c
- assert doc(sparse_passthrough_r) == """
- sparse_passthrough_r(arg0: scipy.sparse.csr_matrix[float32]) -> scipy.sparse.csr_matrix[float32]
+ assert doc(sparse_copy_r) == """
+ sparse_copy_r(arg0: scipy.sparse.csr_matrix[float32]) -> scipy.sparse.csr_matrix[float32]
""" # noqa: E501 line too long
- assert doc(sparse_passthrough_c) == """
- sparse_passthrough_c(arg0: scipy.sparse.csc_matrix[float32]) -> scipy.sparse.csc_matrix[float32]
+ assert doc(sparse_copy_c) == """
+ sparse_copy_c(arg0: scipy.sparse.csc_matrix[float32]) -> scipy.sparse.csc_matrix[float32]
""" # noqa: E501 line too long
+
+
+def test_issue738():
+ from pybind11_tests import iss738_f1, iss738_f2
+
+ assert np.all(iss738_f1(np.array([[1., 2, 3]])) == np.array([[1., 102, 203]]))
+ assert np.all(iss738_f1(np.array([[1.], [2], [3]])) == np.array([[1.], [12], [23]]))
+
+ assert np.all(iss738_f2(np.array([[1., 2, 3]])) == np.array([[1., 102, 203]]))
+ assert np.all(iss738_f2(np.array([[1.], [2], [3]])) == np.array([[1.], [12], [23]]))
+
+
+def test_custom_operator_new():
+ """Using Eigen types as member variables requires a class-specific
+ operator new with proper alignment"""
+ from pybind11_tests import CustomOperatorNew
+
+ o = CustomOperatorNew()
+ np.testing.assert_allclose(o.a, 0.0)
+ np.testing.assert_allclose(o.b.diagonal(), 1.0)
diff --git a/ext/pybind11/tests/test_enum.py b/ext/pybind11/tests/test_enum.py
index de5f3c6f6..ba7e22ab0 100644
--- a/ext/pybind11/tests/test_enum.py
+++ b/ext/pybind11/tests/test_enum.py
@@ -7,6 +7,15 @@ def test_unscoped_enum():
assert str(UnscopedEnum.EOne) == "UnscopedEnum.EOne"
assert str(UnscopedEnum.ETwo) == "UnscopedEnum.ETwo"
assert str(EOne) == "UnscopedEnum.EOne"
+ # __members__ property
+ assert UnscopedEnum.__members__ == {"EOne": UnscopedEnum.EOne, "ETwo": UnscopedEnum.ETwo}
+ # __members__ readonly
+ with pytest.raises(AttributeError):
+ UnscopedEnum.__members__ = {}
+ # __members__ returns a copy
+ foo = UnscopedEnum.__members__
+ foo["bar"] = "baz"
+ assert UnscopedEnum.__members__ == {"EOne": UnscopedEnum.EOne, "ETwo": UnscopedEnum.ETwo}
# no TypeError exception for unscoped enum ==/!= int comparisons
y = UnscopedEnum.ETwo
diff --git a/ext/pybind11/tests/test_inheritance.cpp b/ext/pybind11/tests/test_inheritance.cpp
index 2ec0b4a7a..c19f58dc2 100644
--- a/ext/pybind11/tests/test_inheritance.cpp
+++ b/ext/pybind11/tests/test_inheritance.cpp
@@ -36,6 +36,10 @@ public:
Hamster(const std::string &name) : Pet(name, "rodent") {}
};
+class Chimera : public Pet {
+ Chimera() : Pet("Kimmy", "chimera") {}
+};
+
std::string pet_name_species(const Pet &pet) {
return pet.name() + " is a " + pet.species();
}
@@ -49,6 +53,12 @@ struct BaseClass { virtual ~BaseClass() {} };
struct DerivedClass1 : BaseClass { };
struct DerivedClass2 : BaseClass { };
+struct MismatchBase1 { };
+struct MismatchDerived1 : MismatchBase1 { };
+
+struct MismatchBase2 { };
+struct MismatchDerived2 : MismatchBase2 { };
+
test_initializer inheritance([](py::module &m) {
py::class_<Pet> pet_class(m, "Pet");
pet_class
@@ -68,6 +78,8 @@ test_initializer inheritance([](py::module &m) {
py::class_<Hamster, Pet>(m, "Hamster")
.def(py::init<std::string>());
+ py::class_<Chimera, Pet>(m, "Chimera");
+
m.def("pet_name_species", pet_name_species);
m.def("dog_bark", dog_bark);
@@ -97,4 +109,15 @@ test_initializer inheritance([](py::module &m) {
py::isinstance<Unregistered>(l[6])
);
});
+
+ m.def("test_mismatched_holder_type_1", []() {
+ auto m = py::module::import("__main__");
+ py::class_<MismatchBase1, std::shared_ptr<MismatchBase1>>(m, "MismatchBase1");
+ py::class_<MismatchDerived1, MismatchBase1>(m, "MismatchDerived1");
+ });
+ m.def("test_mismatched_holder_type_2", []() {
+ auto m = py::module::import("__main__");
+ py::class_<MismatchBase2>(m, "MismatchBase2");
+ py::class_<MismatchDerived2, std::shared_ptr<MismatchDerived2>, MismatchBase2>(m, "MismatchDerived2");
+ });
});
diff --git a/ext/pybind11/tests/test_inheritance.py b/ext/pybind11/tests/test_inheritance.py
index 7bb52be02..d1f537d1d 100644
--- a/ext/pybind11/tests/test_inheritance.py
+++ b/ext/pybind11/tests/test_inheritance.py
@@ -2,7 +2,7 @@ import pytest
def test_inheritance(msg):
- from pybind11_tests import Pet, Dog, Rabbit, Hamster, dog_bark, pet_name_species
+ from pybind11_tests import Pet, Dog, Rabbit, Hamster, Chimera, dog_bark, pet_name_species
roger = Rabbit('Rabbit')
assert roger.name() + " is a " + roger.species() == "Rabbit is a parrot"
@@ -30,6 +30,10 @@ def test_inheritance(msg):
Invoked with: <m.Pet object at 0>
"""
+ with pytest.raises(TypeError) as excinfo:
+ Chimera("lion", "goat")
+ assert "No constructor defined!" in str(excinfo.value)
+
def test_automatic_upcasting():
from pybind11_tests import return_class_1, return_class_2, return_class_n, return_none
@@ -37,7 +41,8 @@ def test_automatic_upcasting():
assert type(return_class_1()).__name__ == "DerivedClass1"
assert type(return_class_2()).__name__ == "DerivedClass2"
assert type(return_none()).__name__ == "NoneType"
- # Repeat these a few times in a random order to ensure no invalid caching is applied
+ # Repeat these a few times in a random order to ensure no invalid caching
+ # is applied
assert type(return_class_n(1)).__name__ == "DerivedClass1"
assert type(return_class_n(2)).__name__ == "DerivedClass2"
assert type(return_class_n(0)).__name__ == "BaseClass"
@@ -53,3 +58,21 @@ def test_isinstance():
objects = [tuple(), dict(), Pet("Polly", "parrot")] + [Dog("Molly")] * 4
expected = (True, True, True, True, True, False, False)
assert test_isinstance(objects) == expected
+
+
+def test_holder():
+ from pybind11_tests import test_mismatched_holder_type_1, test_mismatched_holder_type_2
+
+ with pytest.raises(RuntimeError) as excinfo:
+ test_mismatched_holder_type_1()
+
+ assert str(excinfo.value) == ("generic_type: type \"MismatchDerived1\" does not have "
+ "a non-default holder type while its base "
+ "\"MismatchBase1\" does")
+
+ with pytest.raises(RuntimeError) as excinfo:
+ test_mismatched_holder_type_2()
+
+ assert str(excinfo.value) == ("generic_type: type \"MismatchDerived2\" has a "
+ "non-default holder type while its base "
+ "\"MismatchBase2\" does not")
diff --git a/ext/pybind11/tests/test_installed_module/CMakeLists.txt b/ext/pybind11/tests/test_installed_module/CMakeLists.txt
deleted file mode 100644
index 77fd49dc2..000000000
--- a/ext/pybind11/tests/test_installed_module/CMakeLists.txt
+++ /dev/null
@@ -1,14 +0,0 @@
-cmake_minimum_required(VERSION 2.8.12)
-project(test_installed_module CXX)
-
-set(CMAKE_MODULE_PATH "")
-
-find_package(pybind11 CONFIG REQUIRED)
-
-message(STATUS "Found pybind11: ${pybind11_INCLUDE_DIRS} (found version ${pybind11_VERSION})")
-message(STATUS "Found Python: ${PYTHON_INCLUDE_DIRS} (found version ${PYTHON_VERSION_STRING})")
-
-pybind11_add_module(test_installed_module SHARED main.cpp)
-
-add_custom_target(check ${CMAKE_COMMAND} -E env PYTHONPATH=$<TARGET_FILE_DIR:test_installed_module>
- ${PYTHON_EXECUTABLE} ${PROJECT_SOURCE_DIR}/test.py)
diff --git a/ext/pybind11/tests/test_installed_module/main.cpp b/ext/pybind11/tests/test_installed_module/main.cpp
deleted file mode 100644
index a0bda4542..000000000
--- a/ext/pybind11/tests/test_installed_module/main.cpp
+++ /dev/null
@@ -1,10 +0,0 @@
-#include <pybind11/pybind11.h>
-namespace py = pybind11;
-
-PYBIND11_PLUGIN(test_installed_module) {
- py::module m("test_installed_module");
-
- m.def("add", [](int i, int j) { return i + j; });
-
- return m.ptr();
-}
diff --git a/ext/pybind11/tests/test_installed_module/test.py b/ext/pybind11/tests/test_installed_module/test.py
deleted file mode 100644
index 2f0632049..000000000
--- a/ext/pybind11/tests/test_installed_module/test.py
+++ /dev/null
@@ -1,3 +0,0 @@
-import test_installed_module
-assert test_installed_module.add(11, 22) == 33
-print('test_installed_module imports, runs, and adds: 11 + 22 = 33')
diff --git a/ext/pybind11/tests/test_installed_target/CMakeLists.txt b/ext/pybind11/tests/test_installed_target/CMakeLists.txt
deleted file mode 100644
index 4333dc107..000000000
--- a/ext/pybind11/tests/test_installed_target/CMakeLists.txt
+++ /dev/null
@@ -1,20 +0,0 @@
-cmake_minimum_required(VERSION 3.0)
-project(test_installed_target CXX)
-
-set(CMAKE_MODULE_PATH "")
-
-find_package(pybind11 CONFIG REQUIRED)
-
-message(STATUS "Found pybind11: ${pybind11_INCLUDE_DIRS} (found version ${pybind11_VERSION})")
-message(STATUS "Found Python: ${PYTHON_INCLUDE_DIRS} (found version ${PYTHON_VERSION_STRING})")
-
-add_library(test_installed_target MODULE main.cpp)
-
-target_link_libraries(test_installed_target PRIVATE pybind11::pybind11)
-
-# make sure result is, for example, test_installed_target.so, not libtest_installed_target.dylib
-set_target_properties(test_installed_target PROPERTIES PREFIX "${PYTHON_MODULE_PREFIX}"
- SUFFIX "${PYTHON_MODULE_EXTENSION}")
-
-add_custom_target(check ${CMAKE_COMMAND} -E env PYTHONPATH=$<TARGET_FILE_DIR:test_installed_target>
- ${PYTHON_EXECUTABLE} ${PROJECT_SOURCE_DIR}/test.py)
diff --git a/ext/pybind11/tests/test_installed_target/test.py b/ext/pybind11/tests/test_installed_target/test.py
deleted file mode 100644
index b2888a72b..000000000
--- a/ext/pybind11/tests/test_installed_target/test.py
+++ /dev/null
@@ -1,3 +0,0 @@
-import test_installed_target
-assert test_installed_target.add(1, 2) == 3
-print('test_installed_target imports, runs, and adds: 1 + 2 = 3')
diff --git a/ext/pybind11/tests/test_issues.cpp b/ext/pybind11/tests/test_issues.cpp
index 4c59a1b12..7da370045 100644
--- a/ext/pybind11/tests/test_issues.cpp
+++ b/ext/pybind11/tests/test_issues.cpp
@@ -74,7 +74,6 @@ namespace std {
template <> struct hash<TplConstrClass> { size_t operator()(const TplConstrClass &t) const { return std::hash<std::string>()(t.str); } };
}
-
void init_issues(py::module &m) {
py::module m2 = m.def_submodule("issues");
@@ -397,5 +396,5 @@ void init_issues(py::module &m) {
#endif
}
-// MSVC workaround: trying to use a lambda here crashes MSCV
+// MSVC workaround: trying to use a lambda here crashes MSVC
test_initializer issues(&init_issues);
diff --git a/ext/pybind11/tests/test_issues.py b/ext/pybind11/tests/test_issues.py
index 2098ff8a3..e60b5ca90 100644
--- a/ext/pybind11/tests/test_issues.py
+++ b/ext/pybind11/tests/test_issues.py
@@ -1,5 +1,4 @@
import pytest
-import gc
from pybind11_tests import ConstructorStats
@@ -55,7 +54,7 @@ def test_shared_ptr_gc():
el = ElementList()
for i in range(10):
el.add(ElementA(i))
- gc.collect()
+ pytest.gc_collect()
for i, v in enumerate(el.get()):
assert i == v.value()
@@ -130,13 +129,13 @@ def test_nested():
assert c.b.a.as_base().value == 42
del c
- gc.collect()
+ pytest.gc_collect()
del a # Should't delete while abase is still alive
- gc.collect()
+ pytest.gc_collect()
assert abase.value == 42
del abase, b
- gc.collect()
+ pytest.gc_collect()
def test_move_fallback():
diff --git a/ext/pybind11/tests/test_keep_alive.py b/ext/pybind11/tests/test_keep_alive.py
index 0cef34658..bfd7d40c3 100644
--- a/ext/pybind11/tests/test_keep_alive.py
+++ b/ext/pybind11/tests/test_keep_alive.py
@@ -1,4 +1,4 @@
-import gc
+import pytest
def test_keep_alive_argument(capture):
@@ -9,14 +9,14 @@ def test_keep_alive_argument(capture):
assert capture == "Allocating parent."
with capture:
p.addChild(Child())
- gc.collect()
+ pytest.gc_collect()
assert capture == """
Allocating child.
Releasing child.
"""
with capture:
del p
- gc.collect()
+ pytest.gc_collect()
assert capture == "Releasing parent."
with capture:
@@ -24,11 +24,11 @@ def test_keep_alive_argument(capture):
assert capture == "Allocating parent."
with capture:
p.addChildKeepAlive(Child())
- gc.collect()
+ pytest.gc_collect()
assert capture == "Allocating child."
with capture:
del p
- gc.collect()
+ pytest.gc_collect()
assert capture == """
Releasing parent.
Releasing child.
@@ -43,14 +43,14 @@ def test_keep_alive_return_value(capture):
assert capture == "Allocating parent."
with capture:
p.returnChild()
- gc.collect()
+ pytest.gc_collect()
assert capture == """
Allocating child.
Releasing child.
"""
with capture:
del p
- gc.collect()
+ pytest.gc_collect()
assert capture == "Releasing parent."
with capture:
@@ -58,11 +58,11 @@ def test_keep_alive_return_value(capture):
assert capture == "Allocating parent."
with capture:
p.returnChildKeepAlive()
- gc.collect()
+ pytest.gc_collect()
assert capture == "Allocating child."
with capture:
del p
- gc.collect()
+ pytest.gc_collect()
assert capture == """
Releasing parent.
Releasing child.
@@ -77,11 +77,11 @@ def test_return_none(capture):
assert capture == "Allocating parent."
with capture:
p.returnNullChildKeepAliveChild()
- gc.collect()
+ pytest.gc_collect()
assert capture == ""
with capture:
del p
- gc.collect()
+ pytest.gc_collect()
assert capture == "Releasing parent."
with capture:
@@ -89,9 +89,9 @@ def test_return_none(capture):
assert capture == "Allocating parent."
with capture:
p.returnNullChildKeepAliveParent()
- gc.collect()
+ pytest.gc_collect()
assert capture == ""
with capture:
del p
- gc.collect()
+ pytest.gc_collect()
assert capture == "Releasing parent."
diff --git a/ext/pybind11/tests/test_kwargs_and_defaults.cpp b/ext/pybind11/tests/test_kwargs_and_defaults.cpp
index 24fc0cd5b..3180123df 100644
--- a/ext/pybind11/tests/test_kwargs_and_defaults.cpp
+++ b/ext/pybind11/tests/test_kwargs_and_defaults.cpp
@@ -28,6 +28,27 @@ py::tuple args_kwargs_function(py::args args, py::kwargs kwargs) {
return py::make_tuple(args, kwargs);
}
+py::tuple mixed_plus_args(int i, double j, py::args args) {
+ return py::make_tuple(i, j, args);
+}
+
+py::tuple mixed_plus_kwargs(int i, double j, py::kwargs kwargs) {
+ return py::make_tuple(i, j, kwargs);
+}
+
+py::tuple mixed_plus_args_kwargs(int i, double j, py::args args, py::kwargs kwargs) {
+ return py::make_tuple(i, j, args, kwargs);
+}
+
+// pybind11 won't allow these to be bound: args and kwargs, if present, must be at the end.
+void bad_args1(py::args, int) {}
+void bad_args2(py::kwargs, int) {}
+void bad_args3(py::kwargs, py::args) {}
+void bad_args4(py::args, int, py::kwargs) {}
+void bad_args5(py::args, py::kwargs, int) {}
+void bad_args6(py::args, py::args) {}
+void bad_args7(py::kwargs, py::kwargs) {}
+
struct KWClass {
void foo(int, float) {}
};
@@ -53,4 +74,20 @@ test_initializer arg_keywords_and_defaults([](py::module &m) {
py::class_<KWClass>(m, "KWClass")
.def("foo0", &KWClass::foo)
.def("foo1", &KWClass::foo, "x"_a, "y"_a);
+
+ m.def("mixed_plus_args", &mixed_plus_args);
+ m.def("mixed_plus_kwargs", &mixed_plus_kwargs);
+ m.def("mixed_plus_args_kwargs", &mixed_plus_args_kwargs);
+
+ m.def("mixed_plus_args_kwargs_defaults", &mixed_plus_args_kwargs,
+ py::arg("i") = 1, py::arg("j") = 3.14159);
+
+ // Uncomment these to test that the static_assert is indeed working:
+// m.def("bad_args1", &bad_args1);
+// m.def("bad_args2", &bad_args2);
+// m.def("bad_args3", &bad_args3);
+// m.def("bad_args4", &bad_args4);
+// m.def("bad_args5", &bad_args5);
+// m.def("bad_args6", &bad_args6);
+// m.def("bad_args7", &bad_args7);
});
diff --git a/ext/pybind11/tests/test_kwargs_and_defaults.py b/ext/pybind11/tests/test_kwargs_and_defaults.py
index 852d03c6e..90f8489ed 100644
--- a/ext/pybind11/tests/test_kwargs_and_defaults.py
+++ b/ext/pybind11/tests/test_kwargs_and_defaults.py
@@ -34,12 +34,8 @@ def test_named_arguments(msg):
with pytest.raises(TypeError) as excinfo:
# noinspection PyArgumentList
kw_func2(x=5, y=10, z=12)
- assert msg(excinfo.value) == """
- kw_func2(): incompatible function arguments. The following argument types are supported:
- 1. (x: int=100, y: int=200) -> str
-
- Invoked with:
- """
+ assert excinfo.match(
+ r'(?s)^kw_func2\(\): incompatible.*Invoked with: kwargs: ((x=5|y=10|z=12)(, |$))' + '{3}$')
assert kw_func4() == "{13 17}"
assert kw_func4(myList=[1, 2, 3]) == "{1 2 3}"
@@ -55,3 +51,58 @@ def test_arg_and_kwargs():
args = 'a1', 'a2'
kwargs = dict(arg3='a3', arg4=4)
assert args_kwargs_function(*args, **kwargs) == (args, kwargs)
+
+
+def test_mixed_args_and_kwargs(msg):
+ from pybind11_tests import (mixed_plus_args, mixed_plus_kwargs, mixed_plus_args_kwargs,
+ mixed_plus_args_kwargs_defaults)
+ mpa = mixed_plus_args
+ mpk = mixed_plus_kwargs
+ mpak = mixed_plus_args_kwargs
+ mpakd = 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
+ """ # noqa: E501 line too long
+ 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:
+ """ # noqa: E501 line too long
+
+ assert mpk(-2, 3.5, pi=3.14159, e=2.71828) == (-2, 3.5, {'e': 2.71828, 'pi': 3.14159})
+ assert 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
+ """ # noqa: E501 line too long
+ 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
+ """ # noqa: E501 line too long
diff --git a/ext/pybind11/tests/test_methods_and_attributes.cpp b/ext/pybind11/tests/test_methods_and_attributes.cpp
index 11fc90091..b7b2edfd8 100644
--- a/ext/pybind11/tests/test_methods_and_attributes.cpp
+++ b/ext/pybind11/tests/test_methods_and_attributes.cpp
@@ -50,10 +50,14 @@ public:
int *internal4() { return &value; } // return by pointer
const int *internal5() { return &value; } // return by const pointer
- py::str overloaded(int, float) { return "(int, float)"; }
- py::str overloaded(float, int) { return "(float, int)"; }
- py::str overloaded(int, float) const { return "(int, float) const"; }
- py::str overloaded(float, int) const { return "(float, int) const"; }
+ 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, 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"; }
int value = 0;
};
@@ -97,6 +101,58 @@ public:
class CppDerivedDynamicClass : public DynamicClass { };
+// py::arg/py::arg_v testing: these arguments just record their argument when invoked
+class ArgInspector1 { public: std::string arg = "(default arg inspector 1)"; };
+class ArgInspector2 { public: std::string arg = "(default arg inspector 2)"; };
+class ArgAlwaysConverts { };
+namespace pybind11 { namespace detail {
+template <> struct type_caster<ArgInspector1> {
+public:
+ PYBIND11_TYPE_CASTER(ArgInspector1, _("ArgInspector1"));
+
+ bool load(handle src, bool convert) {
+ value.arg = "loading ArgInspector1 argument " +
+ std::string(convert ? "WITH" : "WITHOUT") + " conversion allowed. "
+ "Argument value = " + (std::string) str(src);
+ return true;
+ }
+
+ static handle cast(const ArgInspector1 &src, return_value_policy, handle) {
+ return str(src.arg).release();
+ }
+};
+template <> struct type_caster<ArgInspector2> {
+public:
+ PYBIND11_TYPE_CASTER(ArgInspector2, _("ArgInspector2"));
+
+ bool load(handle src, bool convert) {
+ value.arg = "loading ArgInspector2 argument " +
+ std::string(convert ? "WITH" : "WITHOUT") + " conversion allowed. "
+ "Argument value = " + (std::string) str(src);
+ return true;
+ }
+
+ static handle cast(const ArgInspector2 &src, return_value_policy, handle) {
+ return str(src.arg).release();
+ }
+};
+template <> struct type_caster<ArgAlwaysConverts> {
+public:
+ PYBIND11_TYPE_CASTER(ArgAlwaysConverts, _("ArgAlwaysConverts"));
+
+ bool load(handle, bool convert) {
+ return convert;
+ }
+
+ static handle cast(const ArgAlwaysConverts &, return_value_policy, handle) {
+ return py::none();
+ }
+};
+}}
+
+/// Issue/PR #648: bad arg default debugging output
+class NotRegistered {};
+
test_initializer methods_and_attributes([](py::module &m) {
py::class_<ExampleMandA>(m, "ExampleMandA")
.def(py::init<>())
@@ -123,19 +179,28 @@ test_initializer methods_and_attributes([](py::module &m) {
.def("internal4", &ExampleMandA::internal4)
.def("internal5", &ExampleMandA::internal5)
#if defined(PYBIND11_OVERLOAD_CAST)
- .def("overloaded", py::overload_cast<int, float>(&ExampleMandA::overloaded))
- .def("overloaded", py::overload_cast<float, int>(&ExampleMandA::overloaded))
- .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", 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, 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
- .def("overloaded", static_cast<py::str (ExampleMandA::*)(int, float)>(&ExampleMandA::overloaded))
- .def("overloaded", static_cast<py::str (ExampleMandA::*)(float, int)>(&ExampleMandA::overloaded))
- .def("overloaded_const", static_cast<py::str (ExampleMandA::*)(int, float) const>(&ExampleMandA::overloaded))
- .def("overloaded_const", static_cast<py::str (ExampleMandA::*)(float, int) const>(&ExampleMandA::overloaded))
+ .def("overloaded", static_cast<py::str (ExampleMandA::*)(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", static_cast<py::str (ExampleMandA::*)(float, float)>(&ExampleMandA::overloaded))
+ .def("overloaded_const", static_cast<py::str (ExampleMandA::*)(int, float) const>(&ExampleMandA::overloaded))
+ .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
.def("__str__", &ExampleMandA::toString)
- .def_readwrite("value", &ExampleMandA::value)
- ;
+ .def_readwrite("value", &ExampleMandA::value);
py::class_<TestProperties>(m, "TestProperties")
.def(py::init<>())
@@ -149,7 +214,10 @@ test_initializer methods_and_attributes([](py::module &m) {
[](py::object) { return TestProperties::static_get(); })
.def_property_static("def_property_static",
[](py::object) { return TestProperties::static_get(); },
- [](py::object, int v) { return TestProperties::static_set(v); });
+ [](py::object, int v) { TestProperties::static_set(v); })
+ .def_property_static("static_cls",
+ [](py::object cls) { return cls; },
+ [](py::object cls, py::function f) { f(cls); });
py::class_<SimpleValue>(m, "SimpleValue")
.def_readwrite("value", &SimpleValue::value);
@@ -177,9 +245,53 @@ test_initializer methods_and_attributes([](py::module &m) {
.def_property_readonly("rvalue", &TestPropRVP::get_rvalue)
.def_property_readonly_static("static_rvalue", [](py::object) { return SimpleValue(); });
+ struct MetaclassOverride { };
+ py::class_<MetaclassOverride>(m, "MetaclassOverride", py::metaclass((PyObject *) &PyType_Type))
+ .def_property_readonly_static("readonly", [](py::object) { return 1; });
+
+#if !defined(PYPY_VERSION)
py::class_<DynamicClass>(m, "DynamicClass", py::dynamic_attr())
.def(py::init());
py::class_<CppDerivedDynamicClass, DynamicClass>(m, "CppDerivedDynamicClass")
.def(py::init());
+#endif
+
+ // Test converting. The ArgAlwaysConverts is just there to make the first no-conversion pass
+ // fail so that our call always ends up happening via the second dispatch (the one that allows
+ // some conversion).
+ class ArgInspector {
+ public:
+ ArgInspector1 f(ArgInspector1 a, ArgAlwaysConverts) { return a; }
+ std::string g(ArgInspector1 a, const ArgInspector1 &b, int c, ArgInspector2 *d, ArgAlwaysConverts) {
+ return a.arg + "\n" + b.arg + "\n" + std::to_string(c) + "\n" + d->arg;
+ }
+ static ArgInspector2 h(ArgInspector2 a, ArgAlwaysConverts) { return a; }
+ };
+ py::class_<ArgInspector>(m, "ArgInspector")
+ .def(py::init<>())
+ .def("f", &ArgInspector::f, py::arg(), py::arg() = ArgAlwaysConverts())
+ .def("g", &ArgInspector::g, "a"_a.noconvert(), "b"_a, "c"_a.noconvert()=13, "d"_a=ArgInspector2(), py::arg() = ArgAlwaysConverts())
+ .def_static("h", &ArgInspector::h, py::arg().noconvert(), py::arg() = ArgAlwaysConverts())
+ ;
+ m.def("arg_inspect_func", [](ArgInspector2 a, ArgInspector1 b, ArgAlwaysConverts) { return a.arg + "\n" + b.arg; },
+ py::arg().noconvert(false), py::arg_v(nullptr, ArgInspector1()).noconvert(true), py::arg() = ArgAlwaysConverts());
+
+ m.def("floats_preferred", [](double f) { return 0.5 * f; }, py::arg("f"));
+ m.def("floats_only", [](double f) { return 0.5 * f; }, py::arg("f").noconvert());
+
+ /// Issue/PR #648: bad arg default debugging output
+#if !defined(NDEBUG)
+ m.attr("debug_enabled") = true;
+#else
+ m.attr("debug_enabled") = false;
+#endif
+ m.def("bad_arg_def_named", []{
+ auto m = py::module::import("pybind11_tests.issues");
+ m.def("should_fail", [](int, NotRegistered) {}, py::arg(), py::arg("a") = NotRegistered());
+ });
+ m.def("bad_arg_def_unnamed", []{
+ auto m = py::module::import("pybind11_tests.issues");
+ m.def("should_fail", [](int, NotRegistered) {}, py::arg(), py::arg() = NotRegistered());
+ });
});
diff --git a/ext/pybind11/tests/test_methods_and_attributes.py b/ext/pybind11/tests/test_methods_and_attributes.py
index 2b0f8d571..f185ac26d 100644
--- a/ext/pybind11/tests/test_methods_and_attributes.py
+++ b/ext/pybind11/tests/test_methods_and_attributes.py
@@ -33,8 +33,16 @@ def test_methods_and_attributes():
assert instance1.overloaded(1, 1.0) == "(int, float)"
assert instance1.overloaded(2.0, 2) == "(float, int)"
- assert instance1.overloaded_const(3, 3.0) == "(int, float) const"
- assert instance1.overloaded_const(4.0, 4) == "(float, int) const"
+ assert instance1.overloaded(3, 3) == "(int, int)"
+ assert instance1.overloaded(4., 4.) == "(float, float)"
+ assert instance1.overloaded_const(5, 5.0) == "(int, float) const"
+ assert instance1.overloaded_const(6.0, 6) == "(float, int) const"
+ assert instance1.overloaded_const(7, 7) == "(int, int) const"
+ assert instance1.overloaded_const(8., 8.) == "(float, float) const"
+ assert instance1.overloaded_float(1, 1) == "(float, float)"
+ assert instance1.overloaded_float(1, 1.) == "(float, float)"
+ assert instance1.overloaded_float(1., 1) == "(float, float)"
+ assert instance1.overloaded_float(1., 1.) == "(float, float)"
assert instance1.value == 320
instance1.value = 100
@@ -76,19 +84,63 @@ def test_static_properties():
from pybind11_tests import TestProperties as Type
assert Type.def_readonly_static == 1
- with pytest.raises(AttributeError):
+ with pytest.raises(AttributeError) as excinfo:
Type.def_readonly_static = 2
+ assert "can't set attribute" in str(excinfo)
Type.def_readwrite_static = 2
assert Type.def_readwrite_static == 2
assert Type.def_property_readonly_static == 2
- with pytest.raises(AttributeError):
+ with pytest.raises(AttributeError) as excinfo:
Type.def_property_readonly_static = 3
+ assert "can't set attribute" in str(excinfo)
Type.def_property_static = 3
assert Type.def_property_static == 3
+ # Static property read and write via instance
+ instance = Type()
+
+ Type.def_readwrite_static = 0
+ assert Type.def_readwrite_static == 0
+ assert instance.def_readwrite_static == 0
+
+ instance.def_readwrite_static = 2
+ assert Type.def_readwrite_static == 2
+ assert instance.def_readwrite_static == 2
+
+
+def test_static_cls():
+ """Static property getter and setters expect the type object as the their only argument"""
+ from pybind11_tests import TestProperties as Type
+
+ instance = Type()
+ assert Type.static_cls is Type
+ assert instance.static_cls is Type
+
+ def check_self(self):
+ assert self is Type
+
+ Type.static_cls = check_self
+ instance.static_cls = check_self
+
+
+def test_metaclass_override():
+ """Overriding pybind11's default metaclass changes the behavior of `static_property`"""
+ from pybind11_tests import MetaclassOverride
+
+ assert type(ExampleMandA).__name__ == "pybind11_type"
+ assert type(MetaclassOverride).__name__ == "type"
+
+ assert MetaclassOverride.readonly == 1
+ assert type(MetaclassOverride.__dict__["readonly"]).__name__ == "pybind11_static_property"
+
+ # Regular `type` replaces the property instead of calling `__set__()`
+ MetaclassOverride.readonly = 2
+ assert MetaclassOverride.readonly == 2
+ assert isinstance(MetaclassOverride.__dict__["readonly"], int)
+
@pytest.mark.parametrize("access", ["ro", "rw", "static_ro", "static_rw"])
def test_property_return_value_policies(access):
@@ -125,10 +177,19 @@ def test_property_rvalue_policy():
instance = TestPropRVP()
o = instance.rvalue
assert o.value == 1
+
+
+def test_property_rvalue_policy_static():
+ """When returning an rvalue, the return value policy is automatically changed from
+ `reference(_internal)` to `move`. The following would not work otherwise.
+ """
+ from pybind11_tests import TestPropRVP
o = TestPropRVP.static_rvalue
assert o.value == 1
+# https://bitbucket.org/pypy/pypy/issues/2447
+@pytest.unsupported_on_pypy
def test_dynamic_attributes():
from pybind11_tests import DynamicClass, CppDerivedDynamicClass
@@ -171,6 +232,8 @@ def test_dynamic_attributes():
assert cstats.alive() == 0
+# https://bitbucket.org/pypy/pypy/issues/2447
+@pytest.unsupported_on_pypy
def test_cyclic_gc():
from pybind11_tests import DynamicClass
@@ -192,3 +255,71 @@ def test_cyclic_gc():
assert cstats.alive() == 2
del i1, i2
assert cstats.alive() == 0
+
+
+def test_noconvert_args(msg):
+ from pybind11_tests import ArgInspector, arg_inspect_func, floats_only, floats_preferred
+
+ a = ArgInspector()
+ assert msg(a.f("hi")) == """
+ loading ArgInspector1 argument WITH conversion allowed. Argument value = hi
+ """
+ assert msg(a.g("this is a", "this is b")) == """
+ loading ArgInspector1 argument WITHOUT conversion allowed. Argument value = this is a
+ loading ArgInspector1 argument WITH conversion allowed. Argument value = this is b
+ 13
+ loading ArgInspector2 argument WITH conversion allowed. Argument value = (default arg inspector 2)
+ """ # noqa: E501 line too long
+ assert msg(a.g("this is a", "this is b", 42)) == """
+ loading ArgInspector1 argument WITHOUT conversion allowed. Argument value = this is a
+ loading ArgInspector1 argument WITH conversion allowed. Argument value = this is b
+ 42
+ loading ArgInspector2 argument WITH conversion allowed. Argument value = (default arg inspector 2)
+ """ # noqa: E501 line too long
+ assert msg(a.g("this is a", "this is b", 42, "this is d")) == """
+ loading ArgInspector1 argument WITHOUT conversion allowed. Argument value = this is a
+ loading ArgInspector1 argument WITH conversion allowed. Argument value = this is b
+ 42
+ loading ArgInspector2 argument WITH conversion allowed. Argument value = this is d
+ """
+ assert (a.h("arg 1") ==
+ "loading ArgInspector2 argument WITHOUT conversion allowed. Argument value = arg 1")
+ assert msg(arg_inspect_func("A1", "A2")) == """
+ loading ArgInspector2 argument WITH conversion allowed. Argument value = A1
+ loading ArgInspector1 argument WITHOUT conversion allowed. Argument value = A2
+ """
+
+ assert floats_preferred(4) == 2.0
+ assert floats_only(4.0) == 2.0
+ with pytest.raises(TypeError) as excinfo:
+ 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
+ """
+
+
+def test_bad_arg_default(msg):
+ from pybind11_tests import debug_enabled, bad_arg_def_named, bad_arg_def_unnamed
+
+ with pytest.raises(RuntimeError) as excinfo:
+ bad_arg_def_named()
+ assert msg(excinfo.value) == (
+ "arg(): could not convert default argument 'a: NotRegistered' in function 'should_fail' "
+ "into a Python object (type not registered yet?)"
+ if debug_enabled else
+ "arg(): could not convert default argument into a Python object (type not registered "
+ "yet?). Compile in debug mode for more information."
+ )
+
+ with pytest.raises(RuntimeError) as excinfo:
+ bad_arg_def_unnamed()
+ assert msg(excinfo.value) == (
+ "arg(): could not convert default argument 'NotRegistered' in function 'should_fail' "
+ "into a Python object (type not registered yet?)"
+ if debug_enabled else
+ "arg(): could not convert default argument into a Python object (type not registered "
+ "yet?). Compile in debug mode for more information."
+ )
diff --git a/ext/pybind11/tests/test_modules.py b/ext/pybind11/tests/test_modules.py
index fe72f190a..69620949b 100644
--- a/ext/pybind11/tests/test_modules.py
+++ b/ext/pybind11/tests/test_modules.py
@@ -52,3 +52,11 @@ def test_importing():
assert OD is OrderedDict
assert str(OD([(1, 'a'), (2, 'b')])) == "OrderedDict([(1, 'a'), (2, 'b')])"
+
+
+def test_pydoc():
+ """Pydoc needs to be able to provide help() for everything inside a pybind11 module"""
+ import pybind11_tests
+ import pydoc
+
+ assert pydoc.text.docmodule(pybind11_tests)
diff --git a/ext/pybind11/tests/test_multiple_inheritance.cpp b/ext/pybind11/tests/test_multiple_inheritance.cpp
index 3cb12b68d..3ebeb202b 100644
--- a/ext/pybind11/tests/test_multiple_inheritance.cpp
+++ b/ext/pybind11/tests/test_multiple_inheritance.cpp
@@ -10,7 +10,6 @@
#include "pybind11_tests.h"
-
struct Base1 {
Base1(int i) : i(i) { }
int foo() { return i; }
@@ -32,18 +31,27 @@ struct MIType : Base12 {
};
test_initializer multiple_inheritance([](py::module &m) {
- py::class_<Base1>(m, "Base1")
- .def(py::init<int>())
- .def("foo", &Base1::foo);
+ py::class_<Base1> b1(m, "Base1");
+ b1.def(py::init<int>())
+ .def("foo", &Base1::foo);
- py::class_<Base2>(m, "Base2")
- .def(py::init<int>())
- .def("bar", &Base2::bar);
+ py::class_<Base2> b2(m, "Base2");
+ b2.def(py::init<int>())
+ .def("bar", &Base2::bar);
py::class_<Base12, Base1, Base2>(m, "Base12");
py::class_<MIType, Base12>(m, "MIType")
.def(py::init<int, int>());
+
+ // 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 the case where not all base classes are specified,
@@ -82,3 +90,73 @@ test_initializer multiple_inheritance_nonexplicit([](py::module &m) {
m.def("bar_base2a", [](Base2a *b) { return b->bar(); });
m.def("bar_base2a_sharedptr", [](std::shared_ptr<Base2a> b) { return b->bar(); });
});
+
+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 WithDict { };
+
+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;
+};
+
+struct VanillaDictMix1 : Vanilla, WithDict { };
+struct VanillaDictMix2 : WithDict, Vanilla { };
+
+int WithStatic1::static_value1 = 1;
+int WithStatic2::static_value2 = 2;
+int VanillaStaticMix1::static_value = 12;
+int VanillaStaticMix2::static_value = 12;
+
+test_initializer mi_static_properties([](py::module &pm) {
+ auto m = pm.def_submodule("mi");
+
+ 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);
+
+#if !defined(PYPY_VERSION)
+ py::class_<WithDict>(m, "WithDict", py::dynamic_attr()).def(py::init<>());
+ py::class_<VanillaDictMix1, Vanilla, WithDict>(m, "VanillaDictMix1").def(py::init<>());
+ py::class_<VanillaDictMix2, WithDict, Vanilla>(m, "VanillaDictMix2").def(py::init<>());
+#endif
+});
diff --git a/ext/pybind11/tests/test_multiple_inheritance.py b/ext/pybind11/tests/test_multiple_inheritance.py
index 581cf5687..7aaab7cd0 100644
--- a/ext/pybind11/tests/test_multiple_inheritance.py
+++ b/ext/pybind11/tests/test_multiple_inheritance.py
@@ -1,3 +1,4 @@
+import pytest
def test_multiple_inheritance_cpp():
@@ -51,6 +52,17 @@ def test_multiple_inheritance_mix2():
assert mt.bar() == 4
+def test_multiple_inheritance_error():
+ """Inheriting from multiple C++ bases in Python is not supported"""
+ from pybind11_tests import Base1, Base2
+
+ with pytest.raises(TypeError) as excinfo:
+ # noinspection PyUnusedLocal
+ class MI(Base1, Base2):
+ pass
+ assert "Can't inherit from multiple C++ classes in Python" in str(excinfo.value)
+
+
def test_multiple_inheritance_virtbase():
from pybind11_tests import Base12a, bar_base2a, bar_base2a_sharedptr
@@ -62,3 +74,38 @@ def test_multiple_inheritance_virtbase():
assert mt.bar() == 4
assert bar_base2a(mt) == 4
assert 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"""
+ from pybind11_tests import mi
+
+ for d in (mi.VanillaStaticMix1(), mi.VanillaStaticMix2()):
+ assert d.vanilla() == "Vanilla"
+ assert d.static_func1() == "WithStatic1"
+ assert d.static_func2() == "WithStatic2"
+ assert d.static_func() == d.__class__.__name__
+
+ mi.WithStatic1.static_value1 = 1
+ mi.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
+
+
+@pytest.unsupported_on_pypy
+def test_mi_dynamic_attributes():
+ """Mixing bases with and without dynamic attribute support"""
+ from pybind11_tests import mi
+
+ for d in (mi.VanillaDictMix1(), mi.VanillaDictMix2()):
+ d.dynamic = 1
+ assert d.dynamic == 1
diff --git a/ext/pybind11/tests/test_numpy_array.cpp b/ext/pybind11/tests/test_numpy_array.cpp
index 14c4c2999..cd6487249 100644
--- a/ext/pybind11/tests/test_numpy_array.cpp
+++ b/ext/pybind11/tests/test_numpy_array.cpp
@@ -17,6 +17,7 @@
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...));
@@ -67,6 +68,21 @@ template<typename... Ix> arr_t& mutate_at_t(arr_t& a, Ix... idx) { a.mutable_at(
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();
+}
+
test_initializer numpy_array([](py::module &m) {
auto sm = m.def_submodule("array");
@@ -150,4 +166,102 @@ test_initializer numpy_array([](py::module &m) {
"array_t<double>"_a=py::array_t<double>(o)
);
});
+
+ // Overload resolution tests:
+ sm.def("overloaded", [](py::array_t<double>) { return "double"; });
+ sm.def("overloaded", [](py::array_t<float>) { return "float"; });
+ sm.def("overloaded", [](py::array_t<int>) { return "int"; });
+ sm.def("overloaded", [](py::array_t<unsigned short>) { return "unsigned short"; });
+ sm.def("overloaded", [](py::array_t<long long>) { return "long long"; });
+ sm.def("overloaded", [](py::array_t<std::complex<double>>) { return "double complex"; });
+ sm.def("overloaded", [](py::array_t<std::complex<float>>) { return "float complex"; });
+
+ sm.def("overloaded2", [](py::array_t<std::complex<double>>) { return "double complex"; });
+ sm.def("overloaded2", [](py::array_t<double>) { return "double"; });
+ sm.def("overloaded2", [](py::array_t<std::complex<float>>) { return "float complex"; });
+ sm.def("overloaded2", [](py::array_t<float>) { return "float"; });
+
+ // Only accept the exact types:
+ sm.def("overloaded3", [](py::array_t<int>) { return "int"; }, py::arg().noconvert());
+ sm.def("overloaded3", [](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", [](py::array_t<long long, 0>) { return "long long"; });
+ sm.def("overloaded4", [](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", [](py::array_t<unsigned int>) { return "unsigned int"; });
+ sm.def("overloaded5", [](py::array_t<double>) { return "double"; });
+
+ // Issue 685: ndarray shouldn't go to std::string overload
+ sm.def("issue685", [](std::string) { return "string"; });
+ sm.def("issue685", [](py::array) { return "array"; });
+ sm.def("issue685", [](py::object) { return "other"; });
+
+ sm.def("proxy_add2", [](py::array_t<double> a, double v) {
+ auto r = a.mutable_unchecked<2>();
+ for (size_t i = 0; i < r.shape(0); i++)
+ for (size_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 (size_t i = 0; i < r.shape(0); i++)
+ for (size_t j = 0; j < r.shape(1); j++)
+ for (size_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 (size_t k = 0; k < r.shape(2); k++)
+ for (size_t j = 0; j < r.shape(1); j++)
+ for (size_t i = 0; i < r.shape(0); i++)
+ r(i, j, k) = start++;
+ return a;
+ });
+ sm.def("proxy_squared_L2_norm", [](py::array_t<double> a) {
+ auto r = a.unchecked<1>();
+ double sumsq = 0;
+ for (size_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);
+ });
+
+ // 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 (size_t i = 0; i < r.shape(0); i++)
+ for (size_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 (size_t i = 0; i < r.shape(0); i++)
+ for (size_t j = 0; j < r.shape(1); j++)
+ for (size_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);
+ });
});
diff --git a/ext/pybind11/tests/test_numpy_array.py b/ext/pybind11/tests/test_numpy_array.py
index 1c218a10b..14c25b371 100644
--- a/ext/pybind11/tests/test_numpy_array.py
+++ b/ext/pybind11/tests/test_numpy_array.py
@@ -1,5 +1,6 @@
import pytest
-import gc
+
+pytestmark = pytest.requires_numpy
with pytest.suppress(ImportError):
import numpy as np
@@ -7,10 +8,9 @@ with pytest.suppress(ImportError):
@pytest.fixture(scope='function')
def arr():
- return np.array([[1, 2, 3], [4, 5, 6]], '<u2')
+ return np.array([[1, 2, 3], [4, 5, 6]], '=u2')
-@pytest.requires_numpy
def test_array_attributes():
from pybind11_tests.array import (
ndim, shape, strides, writeable, size, itemsize, nbytes, owndata
@@ -54,7 +54,6 @@ def test_array_attributes():
assert not owndata(a)
-@pytest.requires_numpy
@pytest.mark.parametrize('args, ret', [([], 0), ([0], 0), ([1], 3), ([0, 1], 1), ([1, 2], 5)])
def test_index_offset(arr, args, ret):
from pybind11_tests.array import index_at, index_at_t, offset_at, offset_at_t
@@ -64,7 +63,6 @@ def test_index_offset(arr, args, ret):
assert offset_at_t(arr, *args) == ret * arr.dtype.itemsize
-@pytest.requires_numpy
def test_dim_check_fail(arr):
from pybind11_tests.array import (index_at, index_at_t, offset_at, offset_at_t, data, data_t,
mutate_data, mutate_data_t)
@@ -75,7 +73,6 @@ def test_dim_check_fail(arr):
assert str(excinfo.value) == 'too many indices for an array: 3 (ndim = 2)'
-@pytest.requires_numpy
@pytest.mark.parametrize('args, ret',
[([], [1, 2, 3, 4, 5, 6]),
([1], [4, 5, 6]),
@@ -83,22 +80,21 @@ def test_dim_check_fail(arr):
([1, 2], [6])])
def test_data(arr, args, ret):
from pybind11_tests.array import data, data_t
+ from sys import byteorder
assert all(data_t(arr, *args) == ret)
- assert all(data(arr, *args)[::2] == ret)
- assert all(data(arr, *args)[1::2] == 0)
+ assert all(data(arr, *args)[(0 if byteorder == 'little' else 1)::2] == ret)
+ assert all(data(arr, *args)[(1 if byteorder == 'little' else 0)::2] == 0)
-@pytest.requires_numpy
def test_mutate_readonly(arr):
from pybind11_tests.array import mutate_data, mutate_data_t, mutate_at_t
arr.flags.writeable = False
for func, args in (mutate_data, ()), (mutate_data_t, ()), (mutate_at_t, (0, 0)):
- with pytest.raises(RuntimeError) as excinfo:
+ with pytest.raises(ValueError) as excinfo:
func(arr, *args)
assert str(excinfo.value) == 'array is not writeable'
-@pytest.requires_numpy
@pytest.mark.parametrize('dim', [0, 1, 3])
def test_at_fail(arr, dim):
from pybind11_tests.array import at_t, mutate_at_t
@@ -108,7 +104,6 @@ def test_at_fail(arr, dim):
assert str(excinfo.value) == 'index dimension mismatch: {} (ndim = 2)'.format(dim)
-@pytest.requires_numpy
def test_at(arr):
from pybind11_tests.array import at_t, mutate_at_t
@@ -119,7 +114,6 @@ def test_at(arr):
assert all(mutate_at_t(arr, 1, 0).ravel() == [1, 2, 4, 5, 5, 6])
-@pytest.requires_numpy
def test_mutate_data(arr):
from pybind11_tests.array import mutate_data, mutate_data_t
@@ -136,7 +130,6 @@ def test_mutate_data(arr):
assert all(mutate_data_t(arr, 1, 2).ravel() == [6, 19, 27, 68, 84, 197])
-@pytest.requires_numpy
def test_bounds_check(arr):
from pybind11_tests.array import (index_at, index_at_t, data, data_t,
mutate_data, mutate_data_t, at_t, mutate_at_t)
@@ -151,7 +144,6 @@ def test_bounds_check(arr):
assert str(excinfo.value) == 'index 4 is out of bounds for axis 1 with size 3'
-@pytest.requires_numpy
def test_make_c_f_array():
from pybind11_tests.array import (
make_c_array, make_f_array
@@ -162,11 +154,12 @@ def test_make_c_f_array():
assert not make_f_array().flags.c_contiguous
-@pytest.requires_numpy
def test_wrap():
from pybind11_tests.array import wrap
- def assert_references(a, b):
+ 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
@@ -178,7 +171,7 @@ def test_wrap():
assert a.flags.updateifcopy == b.flags.updateifcopy
assert np.all(a == b)
assert not b.flags.owndata
- assert b.base is a
+ assert b.base is base
if a.flags.writeable and a.ndim == 2:
a[0, 0] = 1234
assert b[0, 0] == 1234
@@ -202,16 +195,15 @@ def test_wrap():
a2 = wrap(a1)
assert_references(a1, a2)
- a1 = a1.transpose()
- a2 = wrap(a1)
- assert_references(a1, a2)
+ a1t = a1.transpose()
+ a2 = wrap(a1t)
+ assert_references(a1t, a2, a1)
- a1 = a1.diagonal()
- a2 = wrap(a1)
- assert_references(a1, a2)
+ a1d = a1.diagonal()
+ a2 = wrap(a1d)
+ assert_references(a1d, a2, a1)
-@pytest.requires_numpy
def test_numpy_view(capture):
from pybind11_tests.array import ArrayClass
with capture:
@@ -220,7 +212,7 @@ def test_numpy_view(capture):
ac_view_2 = ac.numpy_view()
assert np.all(ac_view_1 == np.array([1, 2], dtype=np.int32))
del ac
- gc.collect()
+ pytest.gc_collect()
assert capture == """
ArrayClass()
ArrayClass::numpy_view()
@@ -233,20 +225,20 @@ def test_numpy_view(capture):
with capture:
del ac_view_1
del ac_view_2
- gc.collect()
+ pytest.gc_collect()
+ pytest.gc_collect()
assert capture == """
~ArrayClass()
"""
-@pytest.requires_numpy
+@pytest.unsupported_on_pypy
def test_cast_numpy_int64_to_uint64():
from pybind11_tests.array import function_taking_uint64
function_taking_uint64(123)
function_taking_uint64(np.uint64(123))
-@pytest.requires_numpy
def test_isinstance():
from pybind11_tests.array import isinstance_untyped, isinstance_typed
@@ -254,7 +246,6 @@ def test_isinstance():
assert isinstance_typed(np.array([1.0, 2.0, 3.0]))
-@pytest.requires_numpy
def test_constructors():
from pybind11_tests.array import default_constructors, converting_constructors
@@ -271,3 +262,118 @@ def test_constructors():
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):
+ from pybind11_tests.array import overloaded, overloaded2, overloaded3, overloaded4, overloaded5
+
+ # Exact overload matches:
+ assert overloaded(np.array([1], dtype='float64')) == 'double'
+ assert overloaded(np.array([1], dtype='float32')) == 'float'
+ assert overloaded(np.array([1], dtype='ushort')) == 'unsigned short'
+ assert overloaded(np.array([1], dtype='intc')) == 'int'
+ assert overloaded(np.array([1], dtype='longlong')) == 'long long'
+ assert overloaded(np.array([1], dtype='complex')) == 'double complex'
+ assert overloaded(np.array([1], dtype='csingle')) == 'float complex'
+
+ # No exact match, should call first convertible version:
+ assert overloaded(np.array([1], dtype='uint8')) == 'double'
+
+ with pytest.raises(TypeError) as excinfo:
+ overloaded("not an array")
+ assert msg(excinfo.value) == """
+ overloaded(): incompatible function arguments. The following argument types are supported:
+ 1. (arg0: numpy.ndarray[float64]) -> str
+ 2. (arg0: numpy.ndarray[float32]) -> str
+ 3. (arg0: numpy.ndarray[int32]) -> str
+ 4. (arg0: numpy.ndarray[uint16]) -> str
+ 5. (arg0: numpy.ndarray[int64]) -> str
+ 6. (arg0: numpy.ndarray[complex128]) -> str
+ 7. (arg0: numpy.ndarray[complex64]) -> str
+
+ Invoked with: 'not an array'
+ """
+
+ assert overloaded2(np.array([1], dtype='float64')) == 'double'
+ assert overloaded2(np.array([1], dtype='float32')) == 'float'
+ assert overloaded2(np.array([1], dtype='complex64')) == 'float complex'
+ assert overloaded2(np.array([1], dtype='complex128')) == 'double complex'
+ assert overloaded2(np.array([1], dtype='float32')) == 'float'
+
+ assert overloaded3(np.array([1], dtype='float64')) == 'double'
+ assert overloaded3(np.array([1], dtype='intc')) == 'int'
+ expected_exc = """
+ overloaded3(): incompatible function arguments. The following argument types are supported:
+ 1. (arg0: numpy.ndarray[int32]) -> str
+ 2. (arg0: numpy.ndarray[float64]) -> str
+
+ Invoked with:"""
+
+ with pytest.raises(TypeError) as excinfo:
+ overloaded3(np.array([1], dtype='uintc'))
+ assert msg(excinfo.value) == expected_exc + " array([1], dtype=uint32)"
+ with pytest.raises(TypeError) as excinfo:
+ overloaded3(np.array([1], dtype='float32'))
+ assert msg(excinfo.value) == expected_exc + " array([ 1.], dtype=float32)"
+ with pytest.raises(TypeError) as excinfo:
+ overloaded3(np.array([1], dtype='complex'))
+ assert msg(excinfo.value) == expected_exc + " array([ 1.+0.j])"
+
+ # Exact matches:
+ assert overloaded4(np.array([1], dtype='double')) == 'double'
+ assert 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 overloaded4(np.array([1], dtype='float32')) == 'double'
+ assert overloaded4(np.array([1], dtype='short')) == 'long long'
+
+ assert overloaded5(np.array([1], dtype='double')) == 'double'
+ assert overloaded5(np.array([1], dtype='uintc')) == 'unsigned int'
+ assert overloaded5(np.array([1], dtype='float32')) == 'unsigned int'
+
+
+def test_greedy_string_overload(): # issue 685
+ from pybind11_tests.array import issue685
+
+ assert issue685("abc") == "string"
+ assert issue685(np.array([97, 98, 99], dtype='b')) == "array"
+ assert issue685(123) == "other"
+
+
+def test_array_unchecked_fixed_dims(msg):
+ from pybind11_tests.array import (proxy_add2, proxy_init3F, proxy_init3, proxy_squared_L2_norm,
+ proxy_auxiliaries2, array_auxiliaries2)
+
+ z1 = np.array([[1, 2], [3, 4]], dtype='float64')
+ proxy_add2(z1, 10)
+ assert np.all(z1 == [[11, 12], [13, 14]])
+
+ with pytest.raises(ValueError) as excinfo:
+ proxy_add2(np.array([1., 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(proxy_init3(3.0) == expect_c)
+ expect_f = np.transpose(expect_c)
+ assert np.all(proxy_init3F(3.0) == expect_f)
+
+ assert proxy_squared_L2_norm(np.array(range(6))) == 55
+ assert proxy_squared_L2_norm(np.array(range(6), dtype="float64")) == 55
+
+ assert proxy_auxiliaries2(z1) == [11, 11, True, 2, 8, 2, 2, 4, 32]
+ assert proxy_auxiliaries2(z1) == array_auxiliaries2(z1)
+
+
+def test_array_unchecked_dyn_dims(msg):
+ from pybind11_tests.array import (proxy_add2_dyn, proxy_init3_dyn, proxy_auxiliaries2_dyn,
+ array_auxiliaries2)
+ z1 = np.array([[1, 2], [3, 4]], dtype='float64')
+ 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(proxy_init3_dyn(3.0) == expect_c)
+
+ assert proxy_auxiliaries2_dyn(z1) == [11, 11, True, 2, 8, 2, 2, 4, 32]
+ assert proxy_auxiliaries2_dyn(z1) == array_auxiliaries2(z1)
diff --git a/ext/pybind11/tests/test_numpy_dtypes.cpp b/ext/pybind11/tests/test_numpy_dtypes.cpp
index 3894f6a30..1f6c85704 100644
--- a/ext/pybind11/tests/test_numpy_dtypes.cpp
+++ b/ext/pybind11/tests/test_numpy_dtypes.cpp
@@ -19,23 +19,25 @@
namespace py = pybind11;
struct SimpleStruct {
- bool x;
- uint32_t y;
- float z;
+ bool bool_;
+ uint32_t uint_;
+ float float_;
+ long double ldbl_;
};
std::ostream& operator<<(std::ostream& os, const SimpleStruct& v) {
- return os << "s:" << v.x << "," << v.y << "," << v.z;
+ return os << "s:" << v.bool_ << "," << v.uint_ << "," << v.float_ << "," << v.ldbl_;
}
PYBIND11_PACKED(struct PackedStruct {
- bool x;
- uint32_t y;
- float z;
+ bool bool_;
+ uint32_t uint_;
+ float float_;
+ long double ldbl_;
});
std::ostream& operator<<(std::ostream& os, const PackedStruct& v) {
- return os << "p:" << v.x << "," << v.y << "," << v.z;
+ return os << "p:" << v.bool_ << "," << v.uint_ << "," << v.float_ << "," << v.ldbl_;
}
PYBIND11_PACKED(struct NestedStruct {
@@ -48,10 +50,11 @@ std::ostream& operator<<(std::ostream& os, const NestedStruct& v) {
}
struct PartialStruct {
- bool x;
- uint32_t y;
- float z;
+ bool bool_;
+ uint32_t uint_;
+ float float_;
uint64_t dummy2;
+ long double ldbl_;
};
struct PartialNestedStruct {
@@ -99,13 +102,19 @@ py::array mkarray_via_buffer(size_t n) {
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++) {
- ptr[i].x = i % 2 != 0; ptr[i].y = (uint32_t) i; ptr[i].z = (float) i * 1.5f;
+ SET_TEST_VALS(ptr[i], i);
}
return arr;
}
@@ -119,8 +128,8 @@ py::array_t<NestedStruct, 0> create_nested(size_t n) {
auto req = arr.request();
auto ptr = static_cast<NestedStruct*>(req.ptr);
for (size_t i = 0; i < n; i++) {
- ptr[i].a.x = i % 2 != 0; ptr[i].a.y = (uint32_t) i; ptr[i].a.z = (float) i * 1.5f;
- ptr[i].b.x = (i + 1) % 2 != 0; ptr[i].b.y = (uint32_t) (i + 1); ptr[i].b.z = (float) (i + 1) * 1.5f;
+ SET_TEST_VALS(ptr[i].a, i);
+ SET_TEST_VALS(ptr[i].b, i + 1);
}
return arr;
}
@@ -130,7 +139,7 @@ py::array_t<PartialNestedStruct, 0> create_partial_nested(size_t n) {
auto req = arr.request();
auto ptr = static_cast<PartialNestedStruct*>(req.ptr);
for (size_t i = 0; i < n; i++) {
- ptr[i].a.x = i % 2 != 0; ptr[i].a.y = (uint32_t) i; ptr[i].a.z = (float) i * 1.5f;
+ SET_TEST_VALS(ptr[i].a, i);
}
return arr;
}
@@ -310,6 +319,22 @@ py::list test_dtype_methods() {
return list;
}
+struct CompareStruct {
+ bool x;
+ uint32_t y;
+ float z;
+};
+
+py::list test_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;
+}
+
test_initializer numpy_dtypes([](py::module &m) {
try {
py::module::import("numpy");
@@ -320,20 +345,26 @@ test_initializer numpy_dtypes([](py::module &m) {
// typeinfo may be registered before the dtype descriptor for scalar casts to work...
py::class_<SimpleStruct>(m, "SimpleStruct");
- PYBIND11_NUMPY_DTYPE(SimpleStruct, x, y, z);
- PYBIND11_NUMPY_DTYPE(PackedStruct, x, y, z);
+ PYBIND11_NUMPY_DTYPE(SimpleStruct, bool_, uint_, float_, ldbl_);
+ PYBIND11_NUMPY_DTYPE(PackedStruct, bool_, uint_, float_, ldbl_);
PYBIND11_NUMPY_DTYPE(NestedStruct, a, b);
- PYBIND11_NUMPY_DTYPE(PartialStruct, x, y, z);
+ PYBIND11_NUMPY_DTYPE(PartialStruct, bool_, uint_, float_, ldbl_);
PYBIND11_NUMPY_DTYPE(PartialNestedStruct, a);
PYBIND11_NUMPY_DTYPE(StringStruct, a, b);
PYBIND11_NUMPY_DTYPE(EnumStruct, e1, e2);
PYBIND11_NUMPY_DTYPE(TrailingPaddingStruct, a, b);
+ PYBIND11_NUMPY_DTYPE(CompareStruct, x, y, z);
// ... or after
py::class_<PackedStruct>(m, "PackedStruct");
PYBIND11_NUMPY_DTYPE_EX(StructWithUglyNames, __x__, "x", __y__, "y");
+ // If uncommented, 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);
+
m.def("create_rec_simple", &create_recarray<SimpleStruct>);
m.def("create_rec_packed", &create_recarray<PackedStruct>);
m.def("create_rec_nested", &create_nested);
@@ -352,12 +383,13 @@ test_initializer numpy_dtypes([](py::module &m) {
m.def("test_array_ctors", &test_array_ctors);
m.def("test_dtype_ctors", &test_dtype_ctors);
m.def("test_dtype_methods", &test_dtype_methods);
+ m.def("compare_buffer_info", &test_compare_buffer_info);
m.def("trailing_padding_dtype", &trailing_padding_dtype);
m.def("buffer_to_dtype", &buffer_to_dtype);
- m.def("f_simple", [](SimpleStruct s) { return s.y * 10; });
- m.def("f_packed", [](PackedStruct s) { return s.y * 10; });
- m.def("f_nested", [](NestedStruct s) { return s.a.y * 10; });
- m.def("register_dtype", []() { PYBIND11_NUMPY_DTYPE(SimpleStruct, x, y, z); });
+ m.def("f_simple", [](SimpleStruct s) { return s.uint_ * 10; });
+ m.def("f_packed", [](PackedStruct s) { return s.uint_ * 10; });
+ m.def("f_nested", [](NestedStruct s) { return s.a.uint_ * 10; });
+ m.def("register_dtype", []() { PYBIND11_NUMPY_DTYPE(SimpleStruct, bool_, uint_, float_, ldbl_); });
});
#undef PYBIND11_PACKED
diff --git a/ext/pybind11/tests/test_numpy_dtypes.py b/ext/pybind11/tests/test_numpy_dtypes.py
index 52ebe0ede..f63814f9d 100644
--- a/ext/pybind11/tests/test_numpy_dtypes.py
+++ b/ext/pybind11/tests/test_numpy_dtypes.py
@@ -1,27 +1,69 @@
import re
import pytest
+pytestmark = pytest.requires_numpy
+
with pytest.suppress(ImportError):
import numpy as np
@pytest.fixture(scope='module')
def simple_dtype():
- return np.dtype({'names': ['x', 'y', 'z'],
- 'formats': ['?', 'u4', 'f4'],
- 'offsets': [0, 4, 8]})
+ ld = np.dtype('longdouble')
+ return np.dtype({'names': ['bool_', 'uint_', 'float_', 'ldbl_'],
+ 'formats': ['?', 'u4', 'f4', 'f{}'.format(ld.itemsize)],
+ 'offsets': [0, 4, 8, (16 if ld.alignment > 4 else 12)]})
@pytest.fixture(scope='module')
def packed_dtype():
- return np.dtype([('x', '?'), ('y', 'u4'), ('z', 'f4')])
+ 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()
+ return dt_fmt().format(ld.itemsize, partial_ld_off, partial_ld_off + ld.itemsize)
+
+
+def partial_nested_fmt():
+ ld = np.dtype('longdouble')
+ partial_nested_off = 8 + 8 * (ld.alignment > 8)
+ partial_ld_off = partial_ld_offset()
+ partial_nested_size = partial_nested_off * 2 + partial_ld_off + ld.itemsize
+ return "{{'names':['a'], 'formats':[{}], 'offsets':[{}], 'itemsize':{}}}".format(
+ partial_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))
-@pytest.requires_numpy
def test_format_descriptors():
from pybind11_tests import get_format_unbound, print_format_descriptors
@@ -29,33 +71,40 @@ def test_format_descriptors():
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')
+ partial_fmt = ("T{?:bool_:3xI:uint_:f:float_:" +
+ str(4 * (dbl.alignment > 4) + dbl.itemsize + 8 * (ld.alignment > 8)) +
+ "xg:ldbl_:}")
+ nested_extra = str(max(8, ld.alignment))
assert print_format_descriptors() == [
- "T{?:x:3xI:y:f:z:}",
- "T{?:x:=I:y:=f:z:}",
- "T{T{?:x:3xI:y:f:z:}:a:T{?:x:=I:y:=f:z:}:b:}",
- "T{?:x:3xI:y:f:z:12x}",
- "T{8xT{?:x:3xI:y:f:z:12x}:a:8x}",
+ 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{q:e1:B:e2:}'
]
-@pytest.requires_numpy
def test_dtype(simple_dtype):
from pybind11_tests import (print_dtypes, test_dtype_ctors, test_dtype_methods,
trailing_padding_dtype, buffer_to_dtype)
+ from sys import byteorder
+ e = '<' if byteorder == 'little' else '>'
assert print_dtypes() == [
- "{'names':['x','y','z'], 'formats':['?','<u4','<f4'], 'offsets':[0,4,8], 'itemsize':12}",
- "[('x', '?'), ('y', '<u4'), ('z', '<f4')]",
- "[('a', {'names':['x','y','z'], 'formats':['?','<u4','<f4'], 'offsets':[0,4,8],"
- " 'itemsize':12}), ('b', [('x', '?'), ('y', '<u4'), ('z', '<f4')])]",
- "{'names':['x','y','z'], 'formats':['?','<u4','<f4'], 'offsets':[0,4,8], 'itemsize':24}",
- "{'names':['a'], 'formats':[{'names':['x','y','z'], 'formats':['?','<u4','<f4'],"
- " 'offsets':[0,4,8], 'itemsize':24}], 'offsets':[8], 'itemsize':40}",
+ simple_dtype_fmt(),
+ packed_dtype_fmt(),
+ "[('a', {}), ('b', {})]".format(simple_dtype_fmt(), packed_dtype_fmt()),
+ partial_dtype_fmt(),
+ partial_nested_fmt(),
"[('a', 'S3'), ('b', 'S3')]",
- "[('e1', '<i8'), ('e2', 'u1')]",
- "[('x', 'i1'), ('y', '<u8')]"
+ "[('e1', '" + e + "i8'), ('e2', 'u1')]",
+ "[('x', 'i1'), ('y', '" + e + "u8')]"
]
d1 = np.dtype({'names': ['a', 'b'], 'formats': ['int32', 'float64'],
@@ -70,13 +119,12 @@ def test_dtype(simple_dtype):
assert trailing_padding_dtype() == buffer_to_dtype(np.zeros(1, trailing_padding_dtype()))
-@pytest.requires_numpy
def test_recarray(simple_dtype, packed_dtype):
from pybind11_tests import (create_rec_simple, create_rec_packed, create_rec_nested,
print_rec_simple, print_rec_packed, print_rec_nested,
create_rec_partial, create_rec_partial_nested)
- elements = [(False, 0, 0.0), (True, 1, 1.5), (False, 2, 3.0)]
+ elements = [(False, 0, 0.0, -0.0), (True, 1, 1.5, -2.5), (False, 2, 3.0, -5.0)]
for func, dtype in [(create_rec_simple, simple_dtype), (create_rec_packed, packed_dtype)]:
arr = func(0)
@@ -91,15 +139,15 @@ def test_recarray(simple_dtype, packed_dtype):
if dtype == simple_dtype:
assert print_rec_simple(arr) == [
- "s:0,0,0",
- "s:1,1,1.5",
- "s:0,2,3"
+ "s:0,0,0,-0",
+ "s:1,1,1.5,-2.5",
+ "s:0,2,3,-5"
]
else:
assert print_rec_packed(arr) == [
- "p:0,0,0",
- "p:1,1,1.5",
- "p:0,2,3"
+ "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)])
@@ -110,18 +158,17 @@ def test_recarray(simple_dtype, packed_dtype):
arr = create_rec_nested(3)
assert arr.dtype == nested_dtype
- assert_equal(arr, [((False, 0, 0.0), (True, 1, 1.5)),
- ((True, 1, 1.5), (False, 2, 3.0)),
- ((False, 2, 3.0), (True, 3, 4.5))], nested_dtype)
+ assert_equal(arr, [((False, 0, 0.0, -0.0), (True, 1, 1.5, -2.5)),
+ ((True, 1, 1.5, -2.5), (False, 2, 3.0, -5.0)),
+ ((False, 2, 3.0, -5.0), (True, 3, 4.5, -7.5))], nested_dtype)
assert print_rec_nested(arr) == [
- "n:a=s:0,0,0;b=p:1,1,1.5",
- "n:a=s:1,1,1.5;b=p:0,2,3",
- "n:a=s:0,2,3;b=p:1,3,4.5"
+ "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 = create_rec_partial(3)
- assert str(arr.dtype) == \
- "{'names':['x','y','z'], 'formats':['?','<u4','<f4'], 'offsets':[0,4,8], 'itemsize':24}"
+ assert str(arr.dtype) == partial_dtype_fmt()
partial_dtype = arr.dtype
assert '' not in arr.dtype.fields
assert partial_dtype.itemsize > simple_dtype.itemsize
@@ -129,16 +176,13 @@ def test_recarray(simple_dtype, packed_dtype):
assert_equal(arr, elements, packed_dtype)
arr = create_rec_partial_nested(3)
- assert str(arr.dtype) == \
- "{'names':['a'], 'formats':[{'names':['x','y','z'], 'formats':['?','<u4','<f4']," \
- " 'offsets':[0,4,8], 'itemsize':24}], 'offsets':[8], 'itemsize':40}"
+ assert str(arr.dtype) == 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'], create_rec_partial(3))
-@pytest.requires_numpy
def test_array_constructors():
from pybind11_tests import test_array_ctors
@@ -151,7 +195,6 @@ def test_array_constructors():
np.testing.assert_array_equal(test_array_ctors(40 + i), data)
-@pytest.requires_numpy
def test_string_array():
from pybind11_tests import create_string_array, print_string_array
@@ -170,13 +213,14 @@ def test_string_array():
assert dtype == arr.dtype
-@pytest.requires_numpy
def test_enum_array():
from pybind11_tests import create_enum_array, print_enum_array
+ from sys import byteorder
+ e = '<' if byteorder == 'little' else '>'
arr = create_enum_array(3)
dtype = arr.dtype
- assert dtype == np.dtype([('e1', '<i8'), ('e2', 'u1')])
+ assert dtype == np.dtype([('e1', e + 'i8'), ('e2', 'u1')])
assert print_enum_array(arr) == [
"e1=A,e2=X",
"e1=B,e2=Y",
@@ -187,14 +231,12 @@ def test_enum_array():
assert create_enum_array(0).dtype == dtype
-@pytest.requires_numpy
def test_signature(doc):
from pybind11_tests import create_rec_nested
assert doc(create_rec_nested) == "create_rec_nested(arg0: int) -> numpy.ndarray[NestedStruct]"
-@pytest.requires_numpy
def test_scalar_conversion():
from pybind11_tests import (create_rec_simple, f_simple,
create_rec_packed, f_packed,
@@ -216,10 +258,15 @@ def test_scalar_conversion():
assert 'incompatible function arguments' in str(excinfo.value)
-@pytest.requires_numpy
def test_register_dtype():
from pybind11_tests import register_dtype
with pytest.raises(RuntimeError) as excinfo:
register_dtype()
assert 'dtype is already registered' in str(excinfo.value)
+
+
+@pytest.requires_numpy
+def test_compare_buffer_info():
+ from pybind11_tests import compare_buffer_info
+ assert all(compare_buffer_info())
diff --git a/ext/pybind11/tests/test_numpy_vectorize.cpp b/ext/pybind11/tests/test_numpy_vectorize.cpp
index 6d94db2a1..8e951c6e1 100644
--- a/ext/pybind11/tests/test_numpy_vectorize.cpp
+++ b/ext/pybind11/tests/test_numpy_vectorize.cpp
@@ -38,4 +38,21 @@ test_initializer numpy_vectorize([](py::module &m) {
m.def("selective_func", [](py::array_t<int, py::array::c_style>) { return "Int branch taken."; });
m.def("selective_func", [](py::array_t<float, py::array::c_style>) { return "Float branch taken."; });
m.def("selective_func", [](py::array_t<std::complex<float>, py::array::c_style>) { return "Complex float branch taken."; });
+
+
+ // 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", [](
+ py::array_t<int, py::array::forcecast> arg1,
+ py::array_t<float, py::array::forcecast> arg2,
+ py::array_t<double, py::array::forcecast> arg3
+ ) {
+ size_t ndim;
+ std::vector<size_t> shape;
+ std::array<py::buffer_info, 3> buffers {{ arg1.request(), arg2.request(), arg3.request() }};
+ return py::detail::broadcast(buffers, ndim, shape);
+ });
});
diff --git a/ext/pybind11/tests/test_numpy_vectorize.py b/ext/pybind11/tests/test_numpy_vectorize.py
index 718646efa..7ae777227 100644
--- a/ext/pybind11/tests/test_numpy_vectorize.py
+++ b/ext/pybind11/tests/test_numpy_vectorize.py
@@ -1,10 +1,11 @@
import pytest
+pytestmark = pytest.requires_numpy
+
with pytest.suppress(ImportError):
import numpy as np
-@pytest.requires_numpy
def test_vectorize(capture):
from pybind11_tests import vectorized_func, vectorized_func2, vectorized_func3
@@ -24,6 +25,20 @@ def test_vectorize(capture):
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 == """
@@ -56,9 +71,37 @@ def test_vectorize(capture):
my_func(x:int=5, y:float=3, z:float=2)
my_func(x:int=6, y:float=3, z:float=2)
"""
+ with capture:
+ a, b, c = np.array([[1, 2, 3], [4, 5, 6]], order='F'), np.array([[2], [3]]), 2
+ 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)
+ """
-@pytest.requires_numpy
def test_type_selection():
from pybind11_tests import selective_func
@@ -67,10 +110,52 @@ def test_type_selection():
assert selective_func(np.array([1.0j], dtype=np.complex64)) == "Complex float branch taken."
-@pytest.requires_numpy
def test_docs(doc):
from pybind11_tests import vectorized_func
assert doc(vectorized_func) == """
- vectorized_func(arg0: numpy.ndarray[int], arg1: numpy.ndarray[float], arg2: numpy.ndarray[float]) -> object
+ vectorized_func(arg0: numpy.ndarray[int32], arg1: numpy.ndarray[float32], arg2: numpy.ndarray[float64]) -> object
""" # noqa: E501 line too long
+
+
+def test_trivial_broadcasting():
+ from pybind11_tests import vectorized_is_trivial, trivial, vectorized_func
+
+ 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 vectorized_func(z1, z2, z3).flags.c_contiguous
+ assert vectorized_func(y1, y2, y3).flags.f_contiguous
+ assert vectorized_func(z1, 1, 1).flags.c_contiguous
+ assert vectorized_func(1, y2, 1).flags.f_contiguous
+ assert vectorized_func(z1[1::4, 1::4], y2, 1).flags.f_contiguous
+ assert vectorized_func(y1[1::4, 1::4], z2, 1).flags.c_contiguous
diff --git a/ext/pybind11/tests/test_opaque_types.py b/ext/pybind11/tests/test_opaque_types.py
index 7781943b4..1cd410208 100644
--- a/ext/pybind11/tests/test_opaque_types.py
+++ b/ext/pybind11/tests/test_opaque_types.py
@@ -28,9 +28,10 @@ def test_pointers(msg):
print_opaque_list, return_null_str, get_null_str_value,
return_unique_ptr, ConstructorStats)
+ living_before = ConstructorStats.get(ExampleMandA).alive()
assert get_void_ptr_value(return_void_ptr()) == 0x1234
assert get_void_ptr_value(ExampleMandA()) # Should also work for other C++ types
- assert ConstructorStats.get(ExampleMandA).alive() == 0
+ assert ConstructorStats.get(ExampleMandA).alive() == living_before
with pytest.raises(TypeError) as excinfo:
get_void_ptr_value([1, 2, 3]) # This should not work
diff --git a/ext/pybind11/tests/test_operator_overloading.py b/ext/pybind11/tests/test_operator_overloading.py
index e0d42391e..02ccb9633 100644
--- a/ext/pybind11/tests/test_operator_overloading.py
+++ b/ext/pybind11/tests/test_operator_overloading.py
@@ -1,4 +1,3 @@
-
def test_operator_overloading():
from pybind11_tests import Vector2, Vector, ConstructorStats
diff --git a/ext/pybind11/tests/test_pickling.cpp b/ext/pybind11/tests/test_pickling.cpp
index 3941dc593..52b1dbc30 100644
--- a/ext/pybind11/tests/test_pickling.cpp
+++ b/ext/pybind11/tests/test_pickling.cpp
@@ -57,6 +57,7 @@ test_initializer pickling([](py::module &m) {
p.setExtra2(t[2].cast<int>());
});
+#if !defined(PYPY_VERSION)
py::class_<PickleableWithDict>(m, "PickleableWithDict", py::dynamic_attr())
.def(py::init<std::string>())
.def_readwrite("value", &PickleableWithDict::value)
@@ -70,7 +71,7 @@ test_initializer pickling([](py::module &m) {
throw std::runtime_error("Invalid state!");
/* Cast and construct */
auto& p = self.cast<PickleableWithDict&>();
- new (&p) Pickleable(t[0].cast<std::string>());
+ new (&p) PickleableWithDict(t[0].cast<std::string>());
/* Assign C++ state */
p.extra = t[1].cast<int>();
@@ -78,4 +79,5 @@ test_initializer pickling([](py::module &m) {
/* Assign Python state */
self.attr("__dict__") = t[2];
});
+#endif
});
diff --git a/ext/pybind11/tests/test_pickling.py b/ext/pybind11/tests/test_pickling.py
index 5e62e1fcc..548c618af 100644
--- a/ext/pybind11/tests/test_pickling.py
+++ b/ext/pybind11/tests/test_pickling.py
@@ -1,3 +1,5 @@
+import pytest
+
try:
import cPickle as pickle # Use cPickle on Python 2.7
except ImportError:
@@ -18,6 +20,7 @@ def test_roundtrip():
assert p2.extra2() == p.extra2()
+@pytest.unsupported_on_pypy
def test_roundtrip_with_dict():
from pybind11_tests import PickleableWithDict
diff --git a/ext/pybind11/tests/test_python_types.cpp b/ext/pybind11/tests/test_python_types.cpp
index 33c655b52..5696239b4 100644
--- a/ext/pybind11/tests/test_python_types.cpp
+++ b/ext/pybind11/tests/test_python_types.cpp
@@ -17,6 +17,11 @@
# include <fcntl.h>
#endif
+#if defined(_MSC_VER)
+# pragma warning(push)
+# pragma warning(disable: 4127) // warning C4127: Conditional expression is constant
+#endif
+
class ExamplePythonTypes {
public:
static ExamplePythonTypes *new_instance() {
@@ -212,8 +217,7 @@ test_initializer python_types([](py::module &m) {
.def("test_print", &ExamplePythonTypes::test_print, "test the print function")
.def_static("new_instance", &ExamplePythonTypes::new_instance, "Return an instance")
.def_readwrite_static("value", &ExamplePythonTypes::value, "Static value member")
- .def_readonly_static("value2", &ExamplePythonTypes::value2, "Static value member (readonly)")
- ;
+ .def_readonly_static("value2", &ExamplePythonTypes::value2, "Static value member (readonly)");
m.def("test_print_function", []() {
py::print("Hello, World!");
@@ -327,14 +331,14 @@ test_initializer python_types([](py::module &m) {
#ifdef PYBIND11_HAS_EXP_OPTIONAL
has_exp_optional = true;
- using opt_int = std::experimental::optional<int>;
- m.def("double_or_zero_exp", [](const opt_int& x) -> int {
+ using exp_opt_int = std::experimental::optional<int>;
+ m.def("double_or_zero_exp", [](const exp_opt_int& x) -> int {
return x.value_or(0) * 2;
});
- m.def("half_or_none_exp", [](int x) -> opt_int {
- return x ? opt_int(x / 2) : opt_int();
+ m.def("half_or_none_exp", [](int x) -> exp_opt_int {
+ return x ? exp_opt_int(x / 2) : exp_opt_int();
});
- m.def("test_nullopt_exp", [](opt_int x) {
+ m.def("test_nullopt_exp", [](exp_opt_int x) {
return x.value_or(42);
}, py::arg_v("x", std::experimental::nullopt, "None"));
#endif
@@ -427,4 +431,65 @@ test_initializer python_types([](py::module &m) {
"l"_a=l
);
});
+
+ // 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 (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(u8"Say utf8\u203d \U0001f382 \U0001d400"); }); // Say utf8β€½ πŸŽ‚ 𝐀
+ m.def("good_utf16_string", [=]() { return std::u16string({ b16, ib16, cake16_1, cake16_2, mathbfA16_1, mathbfA16_2, z16 }); }); // bβ€½πŸŽ‚π€z
+ m.def("good_utf32_string", [=]() { return std::u32string({ a32, mathbfA32, cake32, ib32, z32 }); }); // aπ€πŸŽ‚β€½z
+ m.def("good_wchar_string", [=]() { return wstr; }); // a‽𝐀z
+ m.def("bad_utf8_string", []() { return std::string("abc\xd0" "def"); });
+ m.def("bad_utf16_string", [=]() { return std::u16string({ b16, char16_t(0xd800), z16 }); });
+ // Under Python 2.7, invalid unicode UTF-32 characters don't appear to trigger UnicodeDecodeError
+ if (PY_MAJOR_VERSION >= 3)
+ m.def("bad_utf32_string", [=]() { return std::u32string({ a32, char32_t(0xd800), z32 }); });
+ if (PY_MAJOR_VERSION >= 3 || sizeof(wchar_t) == 2)
+ m.def("bad_wchar_string", [=]() { return std::wstring({ wchar_t(0x61), wchar_t(0xd800) }); });
+ m.def("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; });
+
+ 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_char16", [](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; });
+
+ 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_capsule_with_destructor",
+ []() {
+ py::print("creating capsule");
+ return py::capsule([]() {
+ py::print("destructing capsule");
+ });
+ }
+ );
+
+ 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));
+ });
+ }
+ );
});
+
+#if defined(_MSC_VER)
+# pragma warning(pop)
+#endif
diff --git a/ext/pybind11/tests/test_python_types.py b/ext/pybind11/tests/test_python_types.py
index 9fe1ef71e..7956c7cc6 100644
--- a/ext/pybind11/tests/test_python_types.py
+++ b/ext/pybind11/tests/test_python_types.py
@@ -1,8 +1,15 @@
+# Python < 3 needs this: coding=utf-8
import pytest
from pybind11_tests import ExamplePythonTypes, ConstructorStats, has_optional, has_exp_optional
+def test_repr():
+ # In Python 3.3+, repr() accesses __qualname__
+ assert "pybind11_type" in repr(type(ExamplePythonTypes))
+ assert "ExamplePythonTypes" in repr(ExamplePythonTypes)
+
+
def test_static():
ExamplePythonTypes.value = 15
assert ExamplePythonTypes.value == 15
@@ -132,8 +139,12 @@ def test_instance(capture):
assert cstats.alive() == 0
-def test_docs(doc):
+# PyPy does not seem to propagate the tp_docs field at the moment
+def test_class_docs(doc):
assert doc(ExamplePythonTypes) == "Example 2 documentation"
+
+
+def test_method_docs(doc):
assert doc(ExamplePythonTypes.get_dict) == """
get_dict(self: m.ExamplePythonTypes) -> dict
@@ -400,3 +411,125 @@ def test_implicit_casting():
'int_i1': 42, 'int_i2': 42, 'int_e': 43, 'int_p': 44
}
assert z['l'] == [3, 6, 9, 12, 15]
+
+
+def test_unicode_conversion():
+ """Tests unicode conversion and error reporting."""
+ import pybind11_tests
+ from pybind11_tests import (good_utf8_string, bad_utf8_string,
+ good_utf16_string, bad_utf16_string,
+ good_utf32_string, # bad_utf32_string,
+ good_wchar_string, # bad_wchar_string,
+ u8_Z, u8_eacute, u16_ibang, u32_mathbfA, wchar_heart)
+
+ assert good_utf8_string() == u"Say utf8β€½ πŸŽ‚ 𝐀"
+ assert good_utf16_string() == u"bβ€½πŸŽ‚π€z"
+ assert good_utf32_string() == u"aπ€πŸŽ‚β€½z"
+ assert good_wchar_string() == u"aβΈ˜π€z"
+
+ with pytest.raises(UnicodeDecodeError):
+ bad_utf8_string()
+
+ with pytest.raises(UnicodeDecodeError):
+ bad_utf16_string()
+
+ # These are provided only if they actually fail (they don't when 32-bit and under Python 2.7)
+ if hasattr(pybind11_tests, "bad_utf32_string"):
+ with pytest.raises(UnicodeDecodeError):
+ pybind11_tests.bad_utf32_string()
+ if hasattr(pybind11_tests, "bad_wchar_string"):
+ with pytest.raises(UnicodeDecodeError):
+ pybind11_tests.bad_wchar_string()
+
+ assert u8_Z() == 'Z'
+ assert u8_eacute() == u'Γ©'
+ assert u16_ibang() == u'β€½'
+ assert u32_mathbfA() == u'𝐀'
+ assert wchar_heart() == u'β™₯'
+
+
+def test_single_char_arguments():
+ """Tests failures for passing invalid inputs to char-accepting functions"""
+ from pybind11_tests import ord_char, ord_char16, ord_char32, ord_wchar, wchar_size
+
+ def toobig_message(r):
+ return "Character code point not in range({0:#x})".format(r)
+ toolong_message = "Expected a character, but multi-character string found"
+
+ assert ord_char(u'a') == 0x61 # simple ASCII
+ assert ord_char(u'Γ©') == 0xE9 # requires 2 bytes in utf-8, but can be stuffed in a char
+ with pytest.raises(ValueError) as excinfo:
+ assert ord_char(u'Δ€') == 0x100 # requires 2 bytes, doesn't fit in a char
+ assert str(excinfo.value) == toobig_message(0x100)
+ with pytest.raises(ValueError) as excinfo:
+ assert ord_char(u'ab')
+ assert str(excinfo.value) == toolong_message
+
+ assert ord_char16(u'a') == 0x61
+ assert ord_char16(u'Γ©') == 0xE9
+ assert ord_char16(u'Δ€') == 0x100
+ assert ord_char16(u'β€½') == 0x203d
+ assert ord_char16(u'β™₯') == 0x2665
+ with pytest.raises(ValueError) as excinfo:
+ assert ord_char16(u'πŸŽ‚') == 0x1F382 # requires surrogate pair
+ assert str(excinfo.value) == toobig_message(0x10000)
+ with pytest.raises(ValueError) as excinfo:
+ assert ord_char16(u'aa')
+ assert str(excinfo.value) == toolong_message
+
+ assert ord_char32(u'a') == 0x61
+ assert ord_char32(u'Γ©') == 0xE9
+ assert ord_char32(u'Δ€') == 0x100
+ assert ord_char32(u'β€½') == 0x203d
+ assert ord_char32(u'β™₯') == 0x2665
+ assert ord_char32(u'πŸŽ‚') == 0x1F382
+ with pytest.raises(ValueError) as excinfo:
+ assert ord_char32(u'aa')
+ assert str(excinfo.value) == toolong_message
+
+ assert ord_wchar(u'a') == 0x61
+ assert ord_wchar(u'Γ©') == 0xE9
+ assert ord_wchar(u'Δ€') == 0x100
+ assert ord_wchar(u'β€½') == 0x203d
+ assert ord_wchar(u'β™₯') == 0x2665
+ if wchar_size == 2:
+ with pytest.raises(ValueError) as excinfo:
+ assert ord_wchar(u'πŸŽ‚') == 0x1F382 # requires surrogate pair
+ assert str(excinfo.value) == toobig_message(0x10000)
+ else:
+ assert ord_wchar(u'πŸŽ‚') == 0x1F382
+ with pytest.raises(ValueError) as excinfo:
+ assert ord_wchar(u'aa')
+ assert str(excinfo.value) == toolong_message
+
+
+def test_builtins_cast_return_none():
+ """Casters produced with PYBIND11_TYPE_CASTER() should convert nullptr to None"""
+ import pybind11_tests as m
+
+ 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
+
+
+def test_capsule_with_destructor(capture):
+ import pybind11_tests as m
+ 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_capsule_with_destructor_2()
+ del a
+ pytest.gc_collect()
+ assert capture.unordered == """
+ creating capsule
+ destructing capsule: 1234
+ """
diff --git a/ext/pybind11/tests/test_sequences_and_iterators.cpp b/ext/pybind11/tests/test_sequences_and_iterators.cpp
index 323b4bf00..c2051fadb 100644
--- a/ext/pybind11/tests/test_sequences_and_iterators.cpp
+++ b/ext/pybind11/tests/test_sequences_and_iterators.cpp
@@ -169,7 +169,49 @@ bool operator==(const NonZeroIterator<std::pair<A, B>>& it, const NonZeroSentine
return !(*it).first || !(*it).second;
}
-test_initializer sequences_and_iterators([](py::module &m) {
+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_initializer sequences_and_iterators([](py::module &pm) {
+ auto m = pm.def_submodule("sequences_and_iterators");
py::class_<Sequence> seq(m, "Sequence");
@@ -272,4 +314,41 @@ test_initializer sequences_and_iterators([](py::module &m) {
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
+
+ m.def("object_to_list", [](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;
+ });
+
+ // Make sure that py::iterator works with std algorithms
+ m.def("count_none", [](py::object o) {
+ return std::count_if(o.begin(), o.end(), [](py::handle h) { return h.is_none(); });
+ });
+
+ m.def("find_none", [](py::object o) {
+ auto it = std::find_if(o.begin(), o.end(), [](py::handle h) { return h.is_none(); });
+ return it->is_none();
+ });
+
+ m.def("count_nonzeros", [](py::dict d) {
+ return std::count_if(d.begin(), d.end(), [](std::pair<py::handle, py::handle> p) {
+ return p.second.cast<int>() != 0;
+ });
+ });
+
+ m.def("tuple_iterator", [](py::tuple x) { return test_random_access_iterator(x); });
+ m.def("list_iterator", [](py::list x) { return test_random_access_iterator(x); });
+ m.def("sequence_iterator", [](py::sequence x) { return test_random_access_iterator(x); });
});
diff --git a/ext/pybind11/tests/test_sequences_and_iterators.py b/ext/pybind11/tests/test_sequences_and_iterators.py
index 76b9f43f6..30b6aaf4b 100644
--- a/ext/pybind11/tests/test_sequences_and_iterators.py
+++ b/ext/pybind11/tests/test_sequences_and_iterators.py
@@ -11,7 +11,7 @@ def allclose(a_list, b_list, rel_tol=1e-05, abs_tol=0.0):
def test_generalized_iterators():
- from pybind11_tests import IntPairs
+ from pybind11_tests.sequences_and_iterators import IntPairs
assert list(IntPairs([(1, 2), (3, 4), (0, 5)]).nonzero()) == [(1, 2), (3, 4)]
assert list(IntPairs([(1, 2), (2, 0), (0, 3), (4, 5)]).nonzero()) == [(1, 2)]
@@ -23,7 +23,8 @@ def test_generalized_iterators():
def test_sequence():
- from pybind11_tests import Sequence, ConstructorStats
+ from pybind11_tests import ConstructorStats
+ from pybind11_tests.sequences_and_iterators import Sequence
cstats = ConstructorStats.get(Sequence)
@@ -71,7 +72,7 @@ def test_sequence():
def test_map_iterator():
- from pybind11_tests import StringMap
+ from pybind11_tests.sequences_and_iterators import StringMap
m = StringMap({'hi': 'bye', 'black': 'white'})
assert m['hi'] == 'bye'
@@ -88,3 +89,37 @@ def test_map_iterator():
assert m[k] == expected[k]
for k, v in m.items():
assert v == expected[k]
+
+
+def test_python_iterator_in_cpp():
+ import pybind11_tests.sequences_and_iterators as m
+
+ 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"
+
+ l = [1, None, 0, None]
+ assert m.count_none(l) == 2
+ assert m.find_none(l) 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))
diff --git a/ext/pybind11/tests/test_smart_ptr.cpp b/ext/pybind11/tests/test_smart_ptr.cpp
index 07c3cb066..83c1c018a 100644
--- a/ext/pybind11/tests/test_smart_ptr.cpp
+++ b/ext/pybind11/tests/test_smart_ptr.cpp
@@ -82,10 +82,22 @@ private:
};
/// Make pybind aware of the ref-counted wrapper type (s)
-PYBIND11_DECLARE_HOLDER_TYPE(T, ref<T>); // Required for custom holder type
+
+// ref<T> is a wrapper for 'Object' which uses intrusive reference counting
+// It is always possible to construct a ref<T> from an Object* pointer without
+// possible incosistencies, hence the 'true' argument at the end.
+PYBIND11_DECLARE_HOLDER_TYPE(T, ref<T>, true);
PYBIND11_DECLARE_HOLDER_TYPE(T, std::shared_ptr<T>); // Not required any more for std::shared_ptr,
// but it should compile without error
+// Make pybind11 aware of the non-standard getter member function
+namespace pybind11 { namespace detail {
+ template <typename T>
+ struct holder_helper<ref<T>> {
+ static const T *get(const ref<T> &p) { return p.get_ptr(); }
+ };
+}}
+
Object *make_object_1() { return new MyObject1(1); }
ref<Object> make_object_2() { return new MyObject1(2); }
@@ -125,6 +137,18 @@ test_initializer smart_ptr([](py::module &m) {
py::class_<MyObject1, ref<MyObject1>>(m, "MyObject1", obj)
.def(py::init<int>());
+ m.def("test_object1_refcounting",
+ []() -> bool {
+ ref<MyObject1> o = new MyObject1(0);
+ bool good = o->getRefCount() == 1;
+ py::object o2 = py::cast(o, py::return_value_policy::reference);
+ // always request (partial) ownership for objects with intrusive
+ // reference counting even when using the 'reference' RVP
+ good &= o->getRefCount() == 2;
+ return good;
+ }
+ );
+
m.def("make_object_1", &make_object_1);
m.def("make_object_2", &make_object_2);
m.def("make_myobject1_1", &make_myobject1_1);
@@ -190,6 +214,18 @@ struct SharedFromThisRef {
std::shared_ptr<B> shared = std::make_shared<B>();
};
+template <typename T>
+class CustomUniquePtr {
+ std::unique_ptr<T> impl;
+
+public:
+ CustomUniquePtr(T* p) : impl(p) { }
+ T* get() const { return impl.get(); }
+ T* release_ptr() { return impl.release(); }
+};
+
+PYBIND11_DECLARE_HOLDER_TYPE(T, CustomUniquePtr<T>);
+
test_initializer smart_ptr_and_references([](py::module &pm) {
auto m = pm.def_submodule("smart_ptr");
@@ -221,4 +257,18 @@ test_initializer smart_ptr_and_references([](py::module &pm) {
py::return_value_policy::copy)
.def("set_ref", [](SharedFromThisRef &, const B &) { return true; })
.def("set_holder", [](SharedFromThisRef &, std::shared_ptr<B>) { return true; });
+
+ struct C {
+ C() { print_created(this); }
+ ~C() { print_destroyed(this); }
+ };
+
+ py::class_<C, CustomUniquePtr<C>>(m, "TypeWithMoveOnlyHolder")
+ .def_static("make", []() { return CustomUniquePtr<C>(new C); });
+
+ struct HeldByDefaultHolder { };
+
+ py::class_<HeldByDefaultHolder>(m, "HeldByDefaultHolder")
+ .def(py::init<>())
+ .def_static("load_shared_ptr", [](std::shared_ptr<HeldByDefaultHolder>) {});
});
diff --git a/ext/pybind11/tests/test_smart_ptr.py b/ext/pybind11/tests/test_smart_ptr.py
index 3a33e1761..b5af3bd38 100644
--- a/ext/pybind11/tests/test_smart_ptr.py
+++ b/ext/pybind11/tests/test_smart_ptr.py
@@ -116,6 +116,11 @@ def test_smart_ptr(capture):
assert cstats.move_assignments == 0
+def test_smart_ptr_refcounting():
+ from pybind11_tests import test_object1_refcounting
+ assert test_object1_refcounting()
+
+
def test_unique_nodelete():
from pybind11_tests import MyObject4
o = MyObject4(23)
@@ -196,3 +201,22 @@ def test_shared_ptr_from_this_and_references():
del ref, bad_wp, copy, holder_ref, holder_copy, s
assert stats.alive() == 0
+
+
+def test_move_only_holder():
+ from pybind11_tests.smart_ptr import TypeWithMoveOnlyHolder
+
+ a = TypeWithMoveOnlyHolder.make()
+ stats = ConstructorStats.get(TypeWithMoveOnlyHolder)
+ assert stats.alive() == 1
+ del a
+ assert stats.alive() == 0
+
+
+def test_smart_ptr_from_default():
+ from pybind11_tests.smart_ptr import HeldByDefaultHolder
+
+ instance = HeldByDefaultHolder()
+ with pytest.raises(RuntimeError) as excinfo:
+ HeldByDefaultHolder.load_shared_ptr(instance)
+ assert "Unable to load a custom holder type from a default-holder instance" in str(excinfo)
diff --git a/ext/pybind11/tests/test_stl_binders.cpp b/ext/pybind11/tests/test_stl_binders.cpp
index b9b56c15d..f636c0b55 100644
--- a/ext/pybind11/tests/test_stl_binders.cpp
+++ b/ext/pybind11/tests/test_stl_binders.cpp
@@ -10,10 +10,16 @@
#include "pybind11_tests.h"
#include <pybind11/stl_bind.h>
+#include <pybind11/numpy.h>
#include <map>
#include <deque>
#include <unordered_map>
+#ifdef _MSC_VER
+// We get some really long type names here which causes MSVC to emit warnings
+# pragma warning(disable: 4503) // warning C4503: decorated name length exceeded, name was truncated
+#endif
+
class El {
public:
El() = delete;
@@ -53,17 +59,45 @@ template <class Map> Map *times_ten(int n) {
return m;
}
+struct VStruct {
+ bool w;
+ uint32_t x;
+ double y;
+ bool z;
+};
+
+struct VUndeclStruct { //dtype not declared for this version
+ bool w;
+ uint32_t x;
+ double y;
+ bool z;
+};
+
test_initializer stl_binder_vector([](py::module &m) {
py::class_<El>(m, "El")
.def(py::init<int>());
- py::bind_vector<std::vector<unsigned int>>(m, "VectorInt");
+ py::bind_vector<std::vector<unsigned char>>(m, "VectorUChar", py::buffer_protocol());
+ py::bind_vector<std::vector<unsigned int>>(m, "VectorInt", py::buffer_protocol());
py::bind_vector<std::vector<bool>>(m, "VectorBool");
py::bind_vector<std::vector<El>>(m, "VectorEl");
py::bind_vector<std::vector<std::vector<El>>>(m, "VectorVectorEl");
+ m.def("create_undeclstruct", [m] () mutable {
+ py::bind_vector<std::vector<VUndeclStruct>>(m, "VectorUndeclStruct", py::buffer_protocol());
+ });
+
+ try {
+ py::module::import("numpy");
+ } catch (...) {
+ return;
+ }
+ PYBIND11_NUMPY_DTYPE(VStruct, w, x, y, z);
+ py::class_<VStruct>(m, "VStruct").def_readwrite("x", &VStruct::x);
+ py::bind_vector<std::vector<VStruct>>(m, "VectorStruct", py::buffer_protocol());
+ m.def("get_vectorstruct", [] {return std::vector<VStruct> {{0, 5, 3.0, 1}, {1, 30, -1e4, 0}};});
});
test_initializer stl_binder_map([](py::module &m) {
@@ -92,4 +126,3 @@ test_initializer stl_binder_noncopyable([](py::module &m) {
py::bind_map<std::unordered_map<int, E_nc>>(m, "UmapENC");
m.def("get_umnc", &times_ten<std::unordered_map<int, E_nc>>, py::return_value_policy::reference);
});
-
diff --git a/ext/pybind11/tests/test_stl_binders.py b/ext/pybind11/tests/test_stl_binders.py
index c9bcc7935..0edf9e26e 100644
--- a/ext/pybind11/tests/test_stl_binders.py
+++ b/ext/pybind11/tests/test_stl_binders.py
@@ -1,3 +1,10 @@
+import pytest
+import sys
+
+with pytest.suppress(ImportError):
+ import numpy as np
+
+
def test_vector_int():
from pybind11_tests import VectorInt
@@ -26,6 +33,57 @@ def test_vector_int():
assert v_int2 == VectorInt([0, 99, 2, 3])
+@pytest.unsupported_on_pypy
+def test_vector_buffer():
+ from pybind11_tests import VectorUChar, create_undeclstruct
+ b = bytearray([1, 2, 3, 4])
+ v = VectorUChar(b)
+ assert v[1] == 2
+ v[2] = 5
+ m = memoryview(v) # We expose the buffer interface
+ if sys.version_info.major > 2:
+ assert m[2] == 5
+ m[2] = 6
+ else:
+ assert m[2] == '\x05'
+ m[2] = '\x06'
+ assert v[2] == 6
+
+ with pytest.raises(RuntimeError):
+ create_undeclstruct() # Undeclared struct contents, no buffer interface
+
+
+@pytest.requires_numpy
+def test_vector_buffer_numpy():
+ from pybind11_tests import VectorInt, VectorStruct, get_vectorstruct
+
+ a = np.array([1, 2, 3, 4], dtype=np.int32)
+ with pytest.raises(TypeError):
+ VectorInt(a)
+
+ a = np.array([[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]], dtype=np.uintc)
+ v = VectorInt(a[0, :])
+ assert len(v) == 4
+ assert v[2] == 3
+ m = np.asarray(v)
+ m[2] = 5
+ assert v[2] == 5
+
+ v = VectorInt(a[:, 1])
+ assert len(v) == 3
+ assert v[2] == 10
+
+ v = get_vectorstruct()
+ assert v[0].x == 5
+ m = np.asarray(v)
+ m[1]['x'] = 99
+ assert v[1].x == 99
+
+ v = VectorStruct(np.zeros(3, dtype=np.dtype([('w', 'bool'), ('x', 'I'),
+ ('y', 'float64'), ('z', 'bool')], align=True)))
+ assert len(v) == 3
+
+
def test_vector_custom():
from pybind11_tests import El, VectorEl, VectorVectorEl
diff --git a/ext/pybind11/tests/test_virtual_functions.py b/ext/pybind11/tests/test_virtual_functions.py
index a9aecd67f..b11c699df 100644
--- a/ext/pybind11/tests/test_virtual_functions.py
+++ b/ext/pybind11/tests/test_virtual_functions.py
@@ -206,6 +206,9 @@ def test_inheriting_repeat():
assert obj.say_everything() == "BT -7"
+# PyPy: Reference count > 1 causes call with noncopyable instance
+# to fail in ncv1.print_nc()
+@pytest.unsupported_on_pypy
@pytest.mark.skipif(not hasattr(pybind11_tests, 'NCVirt'),
reason="NCVirt test broken on ICPC")
def test_move_support():