summaryrefslogtreecommitdiff
path: root/ext/pybind11/tests
diff options
context:
space:
mode:
authorAndreas Sandberg <andreas.sandberg@arm.com>2017-02-27 13:17:51 +0000
committerAndreas Sandberg <andreas.sandberg@arm.com>2017-05-02 12:37:32 +0000
commitc79706ff4ce591df2151db5504d3c224f3c9965f (patch)
treeb56cd2bfe704a40575a71075e78194a4c516c98d /ext/pybind11/tests
parent359cb08623324b62d7c34973ae54d5bc7f23f9fd (diff)
downloadgem5-c79706ff4ce591df2151db5504d3c224f3c9965f.tar.xz
ext: Add pybind rev f4b81b3
Change-Id: I52e4fc9ebf2f59da57d8cf8f3e37cc79598c2f5f Signed-off-by: Andreas Sandberg <andreas.sandberg@arm.com> Reviewed-by: Andreas Hansson <andreas.hansson@arm.com> Reviewed-by: Curtis Dunham <curtis.dunham@arm.com> Reviewed-on: https://gem5-review.googlesource.com/2229 Reviewed-by: Tony Gutierrez <anthony.gutierrez@amd.com> Reviewed-by: Jason Lowe-Power <jason@lowepower.com> Reviewed-by: Pierre-Yves PĂ©neau <pierre-yves.peneau@lirmm.fr>
Diffstat (limited to 'ext/pybind11/tests')
-rw-r--r--ext/pybind11/tests/CMakeLists.txt159
-rw-r--r--ext/pybind11/tests/conftest.py227
-rw-r--r--ext/pybind11/tests/constructor_stats.h249
-rw-r--r--ext/pybind11/tests/object.h175
-rw-r--r--ext/pybind11/tests/pybind11_tests.cpp45
-rw-r--r--ext/pybind11/tests/pybind11_tests.h12
-rw-r--r--ext/pybind11/tests/test_alias_initialization.cpp62
-rw-r--r--ext/pybind11/tests/test_alias_initialization.py79
-rw-r--r--ext/pybind11/tests/test_buffers.cpp117
-rw-r--r--ext/pybind11/tests/test_buffers.py57
-rw-r--r--ext/pybind11/tests/test_callbacks.cpp149
-rw-r--r--ext/pybind11/tests/test_callbacks.py98
-rw-r--r--ext/pybind11/tests/test_chrono.cpp59
-rw-r--r--ext/pybind11/tests/test_chrono.py116
-rw-r--r--ext/pybind11/tests/test_class_args.cpp68
-rw-r--r--ext/pybind11/tests/test_class_args.py8
-rw-r--r--ext/pybind11/tests/test_constants_and_functions.cpp66
-rw-r--r--ext/pybind11/tests/test_constants_and_functions.py24
-rw-r--r--ext/pybind11/tests/test_copy_move_policies.cpp41
-rw-r--r--ext/pybind11/tests/test_copy_move_policies.py15
-rw-r--r--ext/pybind11/tests/test_docstring_options.cpp53
-rw-r--r--ext/pybind11/tests/test_docstring_options.py32
-rw-r--r--ext/pybind11/tests/test_eigen.cpp134
-rw-r--r--ext/pybind11/tests/test_eigen.py145
-rw-r--r--ext/pybind11/tests/test_enum.cpp68
-rw-r--r--ext/pybind11/tests/test_enum.py108
-rw-r--r--ext/pybind11/tests/test_eval.cpp79
-rw-r--r--ext/pybind11/tests/test_eval.py19
-rw-r--r--ext/pybind11/tests/test_eval_call.py4
-rw-r--r--ext/pybind11/tests/test_exceptions.cpp173
-rw-r--r--ext/pybind11/tests/test_exceptions.py74
-rw-r--r--ext/pybind11/tests/test_inheritance.cpp100
-rw-r--r--ext/pybind11/tests/test_inheritance.py55
-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/main.cpp10
-rw-r--r--ext/pybind11/tests/test_installed_target/test.py3
-rw-r--r--ext/pybind11/tests/test_issues.cpp401
-rw-r--r--ext/pybind11/tests/test_issues.py252
-rw-r--r--ext/pybind11/tests/test_keep_alive.cpp40
-rw-r--r--ext/pybind11/tests/test_keep_alive.py97
-rw-r--r--ext/pybind11/tests/test_kwargs_and_defaults.cpp56
-rw-r--r--ext/pybind11/tests/test_kwargs_and_defaults.py57
-rw-r--r--ext/pybind11/tests/test_methods_and_attributes.cpp185
-rw-r--r--ext/pybind11/tests/test_methods_and_attributes.py194
-rw-r--r--ext/pybind11/tests/test_modules.cpp58
-rw-r--r--ext/pybind11/tests/test_modules.py54
-rw-r--r--ext/pybind11/tests/test_multiple_inheritance.cpp84
-rw-r--r--ext/pybind11/tests/test_multiple_inheritance.py64
-rw-r--r--ext/pybind11/tests/test_numpy_array.cpp153
-rw-r--r--ext/pybind11/tests/test_numpy_array.py273
-rw-r--r--ext/pybind11/tests/test_numpy_dtypes.cpp363
-rw-r--r--ext/pybind11/tests/test_numpy_dtypes.py225
-rw-r--r--ext/pybind11/tests/test_numpy_vectorize.cpp41
-rw-r--r--ext/pybind11/tests/test_numpy_vectorize.py76
-rw-r--r--ext/pybind11/tests/test_opaque_types.cpp62
-rw-r--r--ext/pybind11/tests/test_opaque_types.py49
-rw-r--r--ext/pybind11/tests/test_operator_overloading.cpp76
-rw-r--r--ext/pybind11/tests/test_operator_overloading.py41
-rw-r--r--ext/pybind11/tests/test_pickling.cpp81
-rw-r--r--ext/pybind11/tests/test_pickling.py32
-rw-r--r--ext/pybind11/tests/test_python_types.cpp430
-rw-r--r--ext/pybind11/tests/test_python_types.py402
-rw-r--r--ext/pybind11/tests/test_sequences_and_iterators.cpp275
-rw-r--r--ext/pybind11/tests/test_sequences_and_iterators.py90
-rw-r--r--ext/pybind11/tests/test_smart_ptr.cpp224
-rw-r--r--ext/pybind11/tests/test_smart_ptr.py198
-rw-r--r--ext/pybind11/tests/test_stl_binders.cpp95
-rw-r--r--ext/pybind11/tests/test_stl_binders.py140
-rw-r--r--ext/pybind11/tests/test_virtual_functions.cpp347
-rw-r--r--ext/pybind11/tests/test_virtual_functions.py256
73 files changed, 8401 insertions, 0 deletions
diff --git a/ext/pybind11/tests/CMakeLists.txt b/ext/pybind11/tests/CMakeLists.txt
new file mode 100644
index 000000000..27cb65291
--- /dev/null
+++ b/ext/pybind11/tests/CMakeLists.txt
@@ -0,0 +1,159 @@
+if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
+ message(STATUS "Setting tests build type to MinSizeRel as none was specified")
+ set(CMAKE_BUILD_TYPE MinSizeRel CACHE STRING "Choose the type of build." FORCE)
+ set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release"
+ "MinSizeRel" "RelWithDebInfo")
+endif()
+
+# Full set of test files (you can override these; see below)
+set(PYBIND11_TEST_FILES
+ test_alias_initialization.cpp
+ test_buffers.cpp
+ test_callbacks.cpp
+ test_chrono.cpp
+ test_class_args.cpp
+ test_constants_and_functions.cpp
+ test_copy_move_policies.cpp
+ test_docstring_options.cpp
+ test_eigen.cpp
+ test_enum.cpp
+ test_eval.cpp
+ test_exceptions.cpp
+ test_inheritance.cpp
+ test_issues.cpp
+ test_keep_alive.cpp
+ test_kwargs_and_defaults.cpp
+ test_methods_and_attributes.cpp
+ test_modules.cpp
+ test_multiple_inheritance.cpp
+ test_numpy_array.cpp
+ test_numpy_dtypes.cpp
+ test_numpy_vectorize.cpp
+ test_opaque_types.cpp
+ test_operator_overloading.cpp
+ test_pickling.cpp
+ test_python_types.cpp
+ test_sequences_and_iterators.cpp
+ test_smart_ptr.cpp
+ test_stl_binders.cpp
+ test_virtual_functions.cpp
+)
+
+# Invoking cmake with something like:
+# cmake -DPYBIND11_TEST_OVERRIDE="test_issues.cpp;test_picking.cpp" ..
+# lets you override the tests that get compiled and run. You can restore to all tests with:
+# cmake -DPYBIND11_TEST_OVERRIDE= ..
+if (PYBIND11_TEST_OVERRIDE)
+ set(PYBIND11_TEST_FILES ${PYBIND11_TEST_OVERRIDE})
+endif()
+
+string(REPLACE ".cpp" ".py" PYBIND11_PYTEST_FILES "${PYBIND11_TEST_FILES}")
+
+# Check if Eigen is available; if not, remove from PYBIND11_TEST_FILES (but
+# keep it in PYBIND11_PYTEST_FILES, so that we get the "eigen is not installed"
+# skip message).
+list(FIND PYBIND11_TEST_FILES test_eigen.cpp PYBIND11_TEST_FILES_EIGEN_I)
+if(PYBIND11_TEST_FILES_EIGEN_I GREATER -1)
+ find_package(Eigen3 QUIET)
+
+ if(EIGEN3_FOUND)
+ message(STATUS "Building tests with Eigen v${EIGEN3_VERSION}")
+ else()
+ list(REMOVE_AT PYBIND11_TEST_FILES ${PYBIND11_TEST_FILES_EIGEN_I})
+ message(STATUS "Building tests WITHOUT Eigen")
+ endif()
+endif()
+
+# Create the binding library
+pybind11_add_module(pybind11_tests 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})
+ target_compile_definitions(pybind11_tests PRIVATE -DPYBIND11_TEST_EIGEN)
+endif()
+
+set(testdir ${PROJECT_SOURCE_DIR}/tests)
+
+# Always write the output file directly into the 'tests' directory (even on MSVC)
+if(NOT CMAKE_LIBRARY_OUTPUT_DIRECTORY)
+ set_target_properties(pybind11_tests PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${testdir})
+ foreach(config ${CMAKE_CONFIGURATION_TYPES})
+ string(TOUPPER ${config} config)
+ set_target_properties(pybind11_tests PROPERTIES LIBRARY_OUTPUT_DIRECTORY_${config} ${testdir})
+ endforeach()
+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)")
+ 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}
+ DEPENDS pybind11_tests WORKING_DIRECTORY ${testdir})
+
+if(PYBIND11_TEST_OVERRIDE)
+ add_custom_command(TARGET pytest POST_BUILD
+ COMMAND ${CMAKE_COMMAND} -E echo "Note: not all tests run: -DPYBIND11_TEST_OVERRIDE is in effect")
+endif()
+
+# 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
+ # 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}"
+ )
+ 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}"
+ )
+ else()
+ add_custom_target(test_installed_target)
+ add_custom_target(test_installed_module)
+ 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)
diff --git a/ext/pybind11/tests/conftest.py b/ext/pybind11/tests/conftest.py
new file mode 100644
index 000000000..d4335fc6d
--- /dev/null
+++ b/ext/pybind11/tests/conftest.py
@@ -0,0 +1,227 @@
+"""pytest configuration
+
+Extends output capture as needed by pybind11: ignore constructors, optional unordered lines.
+Adds docstring and exceptions message sanitizers: ignore Python 2 vs 3 differences.
+"""
+
+import pytest
+import textwrap
+import difflib
+import re
+import sys
+import contextlib
+
+_unicode_marker = re.compile(r'u(\'[^\']*\')')
+_long_marker = re.compile(r'([0-9])L')
+_hexadecimal = re.compile(r'0x[0-9a-fA-F]+')
+
+
+def _strip_and_dedent(s):
+ """For triple-quote strings"""
+ return textwrap.dedent(s.lstrip('\n').rstrip())
+
+
+def _split_and_sort(s):
+ """For output which does not require specific line order"""
+ return sorted(_strip_and_dedent(s).splitlines())
+
+
+def _make_explanation(a, b):
+ """Explanation for a failed assert -- the a and b arguments are List[str]"""
+ return ["--- actual / +++ expected"] + [line.strip('\n') for line in difflib.ndiff(a, b)]
+
+
+class Output(object):
+ """Basic output post-processing and comparison"""
+ def __init__(self, string):
+ self.string = string
+ self.explanation = []
+
+ def __str__(self):
+ return self.string
+
+ def __eq__(self, other):
+ # Ignore constructor/destructor output which is prefixed with "###"
+ a = [line for line in self.string.strip().splitlines() if not line.startswith("###")]
+ b = _strip_and_dedent(other).splitlines()
+ if a == b:
+ return True
+ else:
+ self.explanation = _make_explanation(a, b)
+ return False
+
+
+class Unordered(Output):
+ """Custom comparison for output without strict line ordering"""
+ def __eq__(self, other):
+ a = _split_and_sort(self.string)
+ b = _split_and_sort(other)
+ if a == b:
+ return True
+ else:
+ self.explanation = _make_explanation(a, b)
+ return False
+
+
+class Capture(object):
+ def __init__(self, capfd):
+ self.capfd = capfd
+ self.out = ""
+ self.err = ""
+
+ def __enter__(self):
+ self.capfd.readouterr()
+ return self
+
+ def __exit__(self, *_):
+ self.out, self.err = self.capfd.readouterr()
+
+ def __eq__(self, other):
+ a = Output(self.out)
+ b = other
+ if a == b:
+ return True
+ else:
+ self.explanation = a.explanation
+ return False
+
+ def __str__(self):
+ return self.out
+
+ def __contains__(self, item):
+ return item in self.out
+
+ @property
+ def unordered(self):
+ return Unordered(self.out)
+
+ @property
+ def stderr(self):
+ return Output(self.err)
+
+
+@pytest.fixture
+def capture(capfd):
+ """Extended `capfd` with context manager and custom equality operators"""
+ return Capture(capfd)
+
+
+class SanitizedString(object):
+ def __init__(self, sanitizer):
+ self.sanitizer = sanitizer
+ self.string = ""
+ self.explanation = []
+
+ def __call__(self, thing):
+ self.string = self.sanitizer(thing)
+ return self
+
+ def __eq__(self, other):
+ a = self.string
+ b = _strip_and_dedent(other)
+ if a == b:
+ return True
+ else:
+ self.explanation = _make_explanation(a.splitlines(), b.splitlines())
+ return False
+
+
+def _sanitize_general(s):
+ s = s.strip()
+ s = s.replace("pybind11_tests.", "m.")
+ s = s.replace("unicode", "str")
+ s = _long_marker.sub(r"\1", s)
+ s = _unicode_marker.sub(r"\1", s)
+ return s
+
+
+def _sanitize_docstring(thing):
+ s = thing.__doc__
+ s = _sanitize_general(s)
+ return s
+
+
+@pytest.fixture
+def doc():
+ """Sanitize docstrings and add custom failure explanation"""
+ return SanitizedString(_sanitize_docstring)
+
+
+def _sanitize_message(thing):
+ s = str(thing)
+ s = _sanitize_general(s)
+ s = _hexadecimal.sub("0", s)
+ return s
+
+
+@pytest.fixture
+def msg():
+ """Sanitize messages and add custom failure explanation"""
+ return SanitizedString(_sanitize_message)
+
+
+# noinspection PyUnusedLocal
+def pytest_assertrepr_compare(op, left, right):
+ """Hook to insert custom failure explanation"""
+ if hasattr(left, 'explanation'):
+ return left.explanation
+
+
+@contextlib.contextmanager
+def suppress(exception):
+ """Suppress the desired exception"""
+ try:
+ yield
+ except exception:
+ pass
+
+
+def pytest_namespace():
+ """Add import suppression and test requirements to `pytest` namespace"""
+ try:
+ import numpy as np
+ except ImportError:
+ np = None
+ try:
+ import scipy
+ except ImportError:
+ scipy = None
+ try:
+ from pybind11_tests import have_eigen
+ except ImportError:
+ have_eigen = False
+
+ skipif = pytest.mark.skipif
+ return {
+ 'suppress': suppress,
+ 'requires_numpy': skipif(not np, reason="numpy is not installed"),
+ 'requires_scipy': skipif(not np, reason="scipy is not installed"),
+ 'requires_eigen_and_numpy': skipif(not have_eigen or not np,
+ 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"),
+ }
+
+
+def _test_import_pybind11():
+ """Early diagnostic for test module initialization errors
+
+ When there is an error during initialization, the first import will report the
+ real error while all subsequent imports will report nonsense. This import test
+ is done early (in the pytest configuration file, before any tests) in order to
+ avoid the noise of having all tests fail with identical error messages.
+
+ Any possible exception is caught here and reported manually *without* the stack
+ trace. This further reduces noise since the trace would only show pytest internals
+ which are not useful for debugging pybind11 module issues.
+ """
+ # noinspection PyBroadException
+ try:
+ import pybind11_tests # noqa: F401 imported but unused
+ except Exception as e:
+ print("Failed to import pybind11_tests from pytest:")
+ print(" {}: {}".format(type(e).__name__, e))
+ sys.exit(1)
+
+
+_test_import_pybind11()
diff --git a/ext/pybind11/tests/constructor_stats.h b/ext/pybind11/tests/constructor_stats.h
new file mode 100644
index 000000000..eb3e49cab
--- /dev/null
+++ b/ext/pybind11/tests/constructor_stats.h
@@ -0,0 +1,249 @@
+#pragma once
+/*
+ tests/constructor_stats.h -- framework for printing and tracking object
+ instance lifetimes in example/test code.
+
+ Copyright (c) 2016 Jason Rhinelander <jason@imaginary.ca>
+
+ All rights reserved. Use of this source code is governed by a
+ BSD-style license that can be found in the LICENSE file.
+
+This header provides a few useful tools for writing examples or tests that want to check and/or
+display object instance lifetimes. It requires that you include this header and add the following
+function calls to constructors:
+
+ class MyClass {
+ MyClass() { ...; print_default_created(this); }
+ ~MyClass() { ...; print_destroyed(this); }
+ MyClass(const MyClass &c) { ...; print_copy_created(this); }
+ MyClass(MyClass &&c) { ...; print_move_created(this); }
+ MyClass(int a, int b) { ...; print_created(this, a, b); }
+ MyClass &operator=(const MyClass &c) { ...; print_copy_assigned(this); }
+ MyClass &operator=(MyClass &&c) { ...; print_move_assigned(this); }
+
+ ...
+ }
+
+You can find various examples of these in several of the existing example .cpp files. (Of course
+you don't need to add any of the above constructors/operators that you don't actually have, except
+for the destructor).
+
+Each of these will print an appropriate message such as:
+
+ ### MyClass @ 0x2801910 created via default constructor
+ ### MyClass @ 0x27fa780 created 100 200
+ ### MyClass @ 0x2801910 destroyed
+ ### MyClass @ 0x27fa780 destroyed
+
+You can also include extra arguments (such as the 100, 200 in the output above, coming from the
+value constructor) for all of the above methods which will be included in the output.
+
+For testing, each of these also keeps track the created instances and allows you to check how many
+of the various constructors have been invoked from the Python side via code such as:
+
+ from example import ConstructorStats
+ cstats = ConstructorStats.get(MyClass)
+ print(cstats.alive())
+ print(cstats.default_constructions)
+
+Note that `.alive()` should usually be the first thing you call as it invokes Python's garbage
+collector to actually destroy objects that aren't yet referenced.
+
+For everything except copy and move constructors and destructors, any extra values given to the
+print_...() function is stored in a class-specific values list which you can retrieve and inspect
+from the ConstructorStats instance `.values()` method.
+
+In some cases, when you need to track instances of a C++ class not registered with pybind11, you
+need to add a function returning the ConstructorStats for the C++ class; this can be done with:
+
+ m.def("get_special_cstats", &ConstructorStats::get<SpecialClass>, py::return_value_policy::reference)
+
+Finally, you can suppress the output messages, but keep the constructor tracking (for
+inspection/testing in python) by using the functions with `print_` replaced with `track_` (e.g.
+`track_copy_created(this)`).
+
+*/
+
+#include "pybind11_tests.h"
+#include <unordered_map>
+#include <list>
+#include <typeindex>
+#include <sstream>
+
+class ConstructorStats {
+protected:
+ std::unordered_map<void*, int> _instances; // Need a map rather than set because members can shared address with parents
+ std::list<std::string> _values; // Used to track values (e.g. of value constructors)
+public:
+ int default_constructions = 0;
+ int copy_constructions = 0;
+ int move_constructions = 0;
+ int copy_assignments = 0;
+ int move_assignments = 0;
+
+ void copy_created(void *inst) {
+ created(inst);
+ copy_constructions++;
+ }
+ void move_created(void *inst) {
+ created(inst);
+ move_constructions++;
+ }
+ void default_created(void *inst) {
+ created(inst);
+ default_constructions++;
+ }
+ void created(void *inst) {
+ ++_instances[inst];
+ };
+ void destroyed(void *inst) {
+ if (--_instances[inst] < 0)
+ throw std::runtime_error("cstats.destroyed() called with unknown instance; potential double-destruction or a missing cstats.created()");
+ }
+
+ int alive() {
+ // Force garbage collection to ensure any pending destructors are invoked:
+ py::module::import("gc").attr("collect")();
+ int total = 0;
+ for (const auto &p : _instances) if (p.second > 0) total += p.second;
+ return total;
+ }
+
+ void value() {} // Recursion terminator
+ // Takes one or more values, converts them to strings, then stores them.
+ template <typename T, typename... Tmore> void value(const T &v, Tmore &&...args) {
+ std::ostringstream oss;
+ oss << v;
+ _values.push_back(oss.str());
+ value(std::forward<Tmore>(args)...);
+ }
+
+ // Move out stored values
+ py::list values() {
+ py::list l;
+ for (const auto &v : _values) l.append(py::cast(v));
+ _values.clear();
+ return l;
+ }
+
+ // Gets constructor stats from a C++ type index
+ static ConstructorStats& get(std::type_index type) {
+ static std::unordered_map<std::type_index, ConstructorStats> all_cstats;
+ return all_cstats[type];
+ }
+
+ // Gets constructor stats from a C++ type
+ template <typename T> static ConstructorStats& get() {
+ return get(typeid(T));
+ }
+
+ // Gets constructor stats from a Python class
+ static ConstructorStats& get(py::object class_) {
+ auto &internals = py::detail::get_internals();
+ const std::type_index *t1 = nullptr, *t2 = nullptr;
+ try {
+ auto *type_info = internals.registered_types_py.at(class_.ptr());
+ for (auto &p : internals.registered_types_cpp) {
+ if (p.second == type_info) {
+ if (t1) {
+ t2 = &p.first;
+ break;
+ }
+ t1 = &p.first;
+ }
+ }
+ }
+ catch (std::out_of_range) {}
+ if (!t1) throw std::runtime_error("Unknown class passed to ConstructorStats::get()");
+ auto &cs1 = get(*t1);
+ // If we have both a t1 and t2 match, one is probably the trampoline class; return whichever
+ // has more constructions (typically one or the other will be 0)
+ if (t2) {
+ auto &cs2 = get(*t2);
+ int cs1_total = cs1.default_constructions + cs1.copy_constructions + cs1.move_constructions + (int) cs1._values.size();
+ int cs2_total = cs2.default_constructions + cs2.copy_constructions + cs2.move_constructions + (int) cs2._values.size();
+ if (cs2_total > cs1_total) return cs2;
+ }
+ return cs1;
+ }
+};
+
+// To track construction/destruction, you need to call these methods from the various
+// constructors/operators. The ones that take extra values record the given values in the
+// constructor stats values for later inspection.
+template <class T> void track_copy_created(T *inst) { ConstructorStats::get<T>().copy_created(inst); }
+template <class T> void track_move_created(T *inst) { ConstructorStats::get<T>().move_created(inst); }
+template <class T, typename... Values> void track_copy_assigned(T *, Values &&...values) {
+ auto &cst = ConstructorStats::get<T>();
+ cst.copy_assignments++;
+ cst.value(std::forward<Values>(values)...);
+}
+template <class T, typename... Values> void track_move_assigned(T *, Values &&...values) {
+ auto &cst = ConstructorStats::get<T>();
+ cst.move_assignments++;
+ cst.value(std::forward<Values>(values)...);
+}
+template <class T, typename... Values> void track_default_created(T *inst, Values &&...values) {
+ auto &cst = ConstructorStats::get<T>();
+ cst.default_created(inst);
+ cst.value(std::forward<Values>(values)...);
+}
+template <class T, typename... Values> void track_created(T *inst, Values &&...values) {
+ auto &cst = ConstructorStats::get<T>();
+ cst.created(inst);
+ cst.value(std::forward<Values>(values)...);
+}
+template <class T, typename... Values> void track_destroyed(T *inst) {
+ ConstructorStats::get<T>().destroyed(inst);
+}
+template <class T, typename... Values> void track_values(T *, Values &&...values) {
+ ConstructorStats::get<T>().value(std::forward<Values>(values)...);
+}
+
+/// Don't cast pointers to Python, print them as strings
+inline const char *format_ptrs(const char *p) { return p; }
+template <typename T>
+py::str format_ptrs(T *p) { return "{:#x}"_s.format(reinterpret_cast<std::uintptr_t>(p)); }
+template <typename T>
+auto format_ptrs(T &&x) -> decltype(std::forward<T>(x)) { return std::forward<T>(x); }
+
+template <class T, typename... Output>
+void print_constr_details(T *inst, const std::string &action, Output &&...output) {
+ py::print("###", py::type_id<T>(), "@", format_ptrs(inst), action,
+ format_ptrs(std::forward<Output>(output))...);
+}
+
+// Verbose versions of the above:
+template <class T, typename... Values> void print_copy_created(T *inst, Values &&...values) { // NB: this prints, but doesn't store, given values
+ print_constr_details(inst, "created via copy constructor", values...);
+ track_copy_created(inst);
+}
+template <class T, typename... Values> void print_move_created(T *inst, Values &&...values) { // NB: this prints, but doesn't store, given values
+ print_constr_details(inst, "created via move constructor", values...);
+ track_move_created(inst);
+}
+template <class T, typename... Values> void print_copy_assigned(T *inst, Values &&...values) {
+ print_constr_details(inst, "assigned via copy assignment", values...);
+ track_copy_assigned(inst, values...);
+}
+template <class T, typename... Values> void print_move_assigned(T *inst, Values &&...values) {
+ print_constr_details(inst, "assigned via move assignment", values...);
+ track_move_assigned(inst, values...);
+}
+template <class T, typename... Values> void print_default_created(T *inst, Values &&...values) {
+ print_constr_details(inst, "created via default constructor", values...);
+ track_default_created(inst, values...);
+}
+template <class T, typename... Values> void print_created(T *inst, Values &&...values) {
+ print_constr_details(inst, "created", values...);
+ track_created(inst, values...);
+}
+template <class T, typename... Values> void print_destroyed(T *inst, Values &&...values) { // Prints but doesn't store given values
+ print_constr_details(inst, "destroyed", values...);
+ track_destroyed(inst);
+}
+template <class T, typename... Values> void print_values(T *inst, Values &&...values) {
+ print_constr_details(inst, ":", values...);
+ track_values(inst, values...);
+}
+
diff --git a/ext/pybind11/tests/object.h b/ext/pybind11/tests/object.h
new file mode 100644
index 000000000..753f654b2
--- /dev/null
+++ b/ext/pybind11/tests/object.h
@@ -0,0 +1,175 @@
+#if !defined(__OBJECT_H)
+#define __OBJECT_H
+
+#include <atomic>
+#include "constructor_stats.h"
+
+/// Reference counted object base class
+class Object {
+public:
+ /// Default constructor
+ Object() { print_default_created(this); }
+
+ /// Copy constructor
+ Object(const Object &) : m_refCount(0) { print_copy_created(this); }
+
+ /// Return the current reference count
+ int getRefCount() const { return m_refCount; };
+
+ /// Increase the object's reference count by one
+ void incRef() const { ++m_refCount; }
+
+ /** \brief Decrease the reference count of
+ * the object and possibly deallocate it.
+ *
+ * The object will automatically be deallocated once
+ * the reference count reaches zero.
+ */
+ void decRef(bool dealloc = true) const {
+ --m_refCount;
+ if (m_refCount == 0 && dealloc)
+ delete this;
+ else if (m_refCount < 0)
+ throw std::runtime_error("Internal error: reference count < 0!");
+ }
+
+ virtual std::string toString() const = 0;
+protected:
+ /** \brief Virtual protected deconstructor.
+ * (Will only be called by \ref ref)
+ */
+ virtual ~Object() { print_destroyed(this); }
+private:
+ mutable std::atomic<int> m_refCount { 0 };
+};
+
+// Tag class used to track constructions of ref objects. When we track constructors, below, we
+// track and print out the actual class (e.g. ref<MyObject>), and *also* add a fake tracker for
+// ref_tag. This lets us check that the total number of ref<Anything> constructors/destructors is
+// correct without having to check each individual ref<Whatever> type individually.
+class ref_tag {};
+
+/**
+ * \brief Reference counting helper
+ *
+ * The \a ref refeference template is a simple wrapper to store a
+ * pointer to an object. It takes care of increasing and decreasing
+ * the reference count of the object. When the last reference goes
+ * out of scope, the associated object will be deallocated.
+ *
+ * \ingroup libcore
+ */
+template <typename T> class ref {
+public:
+ /// Create a nullptr reference
+ ref() : m_ptr(nullptr) { print_default_created(this); track_default_created((ref_tag*) this); }
+
+ /// Construct a reference from a pointer
+ ref(T *ptr) : m_ptr(ptr) {
+ if (m_ptr) ((Object *) m_ptr)->incRef();
+
+ print_created(this, "from pointer", m_ptr); track_created((ref_tag*) this, "from pointer");
+
+ }
+
+ /// Copy constructor
+ ref(const ref &r) : m_ptr(r.m_ptr) {
+ if (m_ptr)
+ ((Object *) m_ptr)->incRef();
+
+ print_copy_created(this, "with pointer", m_ptr); track_copy_created((ref_tag*) this);
+ }
+
+ /// Move constructor
+ ref(ref &&r) : m_ptr(r.m_ptr) {
+ r.m_ptr = nullptr;
+
+ print_move_created(this, "with pointer", m_ptr); track_move_created((ref_tag*) this);
+ }
+
+ /// Destroy this reference
+ ~ref() {
+ if (m_ptr)
+ ((Object *) m_ptr)->decRef();
+
+ print_destroyed(this); track_destroyed((ref_tag*) this);
+ }
+
+ /// Move another reference into the current one
+ ref& operator=(ref&& r) {
+ print_move_assigned(this, "pointer", r.m_ptr); track_move_assigned((ref_tag*) this);
+
+ if (*this == r)
+ return *this;
+ if (m_ptr)
+ ((Object *) m_ptr)->decRef();
+ m_ptr = r.m_ptr;
+ r.m_ptr = nullptr;
+ return *this;
+ }
+
+ /// Overwrite this reference with another reference
+ ref& operator=(const ref& r) {
+ print_copy_assigned(this, "pointer", r.m_ptr); track_copy_assigned((ref_tag*) this);
+
+ if (m_ptr == r.m_ptr)
+ return *this;
+ if (m_ptr)
+ ((Object *) m_ptr)->decRef();
+ m_ptr = r.m_ptr;
+ if (m_ptr)
+ ((Object *) m_ptr)->incRef();
+ return *this;
+ }
+
+ /// Overwrite this reference with a pointer to another object
+ ref& operator=(T *ptr) {
+ print_values(this, "assigned pointer"); track_values((ref_tag*) this, "assigned pointer");
+
+ if (m_ptr == ptr)
+ return *this;
+ if (m_ptr)
+ ((Object *) m_ptr)->decRef();
+ m_ptr = ptr;
+ if (m_ptr)
+ ((Object *) m_ptr)->incRef();
+ return *this;
+ }
+
+ /// Compare this reference with another reference
+ bool operator==(const ref &r) const { return m_ptr == r.m_ptr; }
+
+ /// Compare this reference with another reference
+ bool operator!=(const ref &r) const { return m_ptr != r.m_ptr; }
+
+ /// Compare this reference with a pointer
+ bool operator==(const T* ptr) const { return m_ptr == ptr; }
+
+ /// Compare this reference with a pointer
+ bool operator!=(const T* ptr) const { return m_ptr != ptr; }
+
+ /// Access the object referenced by this reference
+ T* operator->() { return m_ptr; }
+
+ /// Access the object referenced by this reference
+ const T* operator->() const { return m_ptr; }
+
+ /// Return a C++ reference to the referenced object
+ T& operator*() { return *m_ptr; }
+
+ /// Return a const C++ reference to the referenced object
+ const T& operator*() const { return *m_ptr; }
+
+ /// Return a pointer to the referenced object
+ operator T* () { return m_ptr; }
+
+ /// Return a const pointer to the referenced object
+ T* get() { return m_ptr; }
+
+ /// Return a pointer to the referenced object
+ const T* get() const { return m_ptr; }
+private:
+ T *m_ptr;
+};
+
+#endif /* __OBJECT_H */
diff --git a/ext/pybind11/tests/pybind11_tests.cpp b/ext/pybind11/tests/pybind11_tests.cpp
new file mode 100644
index 000000000..9c593eee1
--- /dev/null
+++ b/ext/pybind11/tests/pybind11_tests.cpp
@@ -0,0 +1,45 @@
+/*
+ tests/pybind11_tests.cpp -- pybind example plugin
+
+ Copyright (c) 2016 Wenzel Jakob <wenzel.jakob@epfl.ch>
+
+ All rights reserved. Use of this source code is governed by a
+ BSD-style license that can be found in the LICENSE file.
+*/
+
+#include "pybind11_tests.h"
+#include "constructor_stats.h"
+
+std::list<std::function<void(py::module &)>> &initializers() {
+ static std::list<std::function<void(py::module &)>> inits;
+ return inits;
+}
+
+test_initializer::test_initializer(std::function<void(py::module &)> initializer) {
+ initializers().push_back(std::move(initializer));
+}
+
+void bind_ConstructorStats(py::module &m) {
+ py::class_<ConstructorStats>(m, "ConstructorStats")
+ .def("alive", &ConstructorStats::alive)
+ .def("values", &ConstructorStats::values)
+ .def_readwrite("default_constructions", &ConstructorStats::default_constructions)
+ .def_readwrite("copy_assignments", &ConstructorStats::copy_assignments)
+ .def_readwrite("move_assignments", &ConstructorStats::move_assignments)
+ .def_readwrite("copy_constructions", &ConstructorStats::copy_constructions)
+ .def_readwrite("move_constructions", &ConstructorStats::move_constructions)
+ .def_static("get", (ConstructorStats &(*)(py::object)) &ConstructorStats::get, py::return_value_policy::reference_internal);
+}
+
+PYBIND11_PLUGIN(pybind11_tests) {
+ py::module m("pybind11_tests", "pybind example plugin");
+
+ bind_ConstructorStats(m);
+
+ for (const auto &initializer : initializers())
+ initializer(m);
+
+ if (!py::hasattr(m, "have_eigen")) m.attr("have_eigen") = false;
+
+ return m.ptr();
+}
diff --git a/ext/pybind11/tests/pybind11_tests.h b/ext/pybind11/tests/pybind11_tests.h
new file mode 100644
index 000000000..c11b687b2
--- /dev/null
+++ b/ext/pybind11/tests/pybind11_tests.h
@@ -0,0 +1,12 @@
+#pragma once
+#include <pybind11/pybind11.h>
+#include <functional>
+#include <list>
+
+namespace py = pybind11;
+using namespace pybind11::literals;
+
+class test_initializer {
+public:
+ test_initializer(std::function<void(py::module &)> initializer);
+};
diff --git a/ext/pybind11/tests/test_alias_initialization.cpp b/ext/pybind11/tests/test_alias_initialization.cpp
new file mode 100644
index 000000000..48e595695
--- /dev/null
+++ b/ext/pybind11/tests/test_alias_initialization.cpp
@@ -0,0 +1,62 @@
+/*
+ tests/test_alias_initialization.cpp -- test cases and example of different trampoline
+ initialization modes
+
+ Copyright (c) 2016 Wenzel Jakob <wenzel.jakob@epfl.ch>, Jason Rhinelander <jason@imaginary.ca>
+
+ All rights reserved. Use of this source code is governed by a
+ BSD-style license that can be found in the LICENSE file.
+*/
+
+#include "pybind11_tests.h"
+
+test_initializer alias_initialization([](py::module &m) {
+ // don't invoke Python dispatch classes by default when instantiating C++ classes that were not
+ // extended on the Python side
+ struct A {
+ virtual ~A() {}
+ virtual void f() { py::print("A.f()"); }
+ };
+
+ struct PyA : A {
+ PyA() { py::print("PyA.PyA()"); }
+ ~PyA() { py::print("PyA.~PyA()"); }
+
+ void f() override {
+ py::print("PyA.f()");
+ PYBIND11_OVERLOAD(void, A, f);
+ }
+ };
+
+ auto call_f = [](A *a) { a->f(); };
+
+ py::class_<A, PyA>(m, "A")
+ .def(py::init<>())
+ .def("f", &A::f);
+
+ m.def("call_f", call_f);
+
+
+ // ... unless we explicitly request it, as in this example:
+ struct A2 {
+ virtual ~A2() {}
+ virtual void f() { py::print("A2.f()"); }
+ };
+
+ struct PyA2 : A2 {
+ PyA2() { py::print("PyA2.PyA2()"); }
+ ~PyA2() { py::print("PyA2.~PyA2()"); }
+ void f() override {
+ py::print("PyA2.f()");
+ PYBIND11_OVERLOAD(void, A2, f);
+ }
+ };
+
+ py::class_<A2, PyA2>(m, "A2")
+ .def(py::init_alias<>())
+ .def("f", &A2::f);
+
+ m.def("call_f", [](A2 *a2) { a2->f(); });
+
+});
+
diff --git a/ext/pybind11/tests/test_alias_initialization.py b/ext/pybind11/tests/test_alias_initialization.py
new file mode 100644
index 000000000..0ed9d2f79
--- /dev/null
+++ b/ext/pybind11/tests/test_alias_initialization.py
@@ -0,0 +1,79 @@
+import gc
+
+
+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).
+ """
+ from pybind11_tests import A, call_f
+
+ class B(A):
+ def __init__(self):
+ super(B, self).__init__()
+
+ def f(self):
+ print("In python f()")
+
+ # C++ version
+ with capture:
+ a = A()
+ call_f(a)
+ del a
+ gc.collect()
+ assert capture == "A.f()"
+
+ # Python version
+ with capture:
+ b = B()
+ call_f(b)
+ del b
+ gc.collect()
+ assert capture == """
+ PyA.PyA()
+ PyA.f()
+ In python f()
+ PyA.~PyA()
+ """
+
+
+def test_alias_delay_initialization2(capture):
+ """A2, unlike the above, is configured to always initialize the alias; while
+ the extra initialization and extra class layer has small virtual dispatch
+ performance penalty, it also allows us to do more things with the trampoline
+ class such as defining local variables and performing construction/destruction.
+ """
+ from pybind11_tests import A2, call_f
+
+ class B2(A2):
+ def __init__(self):
+ super(B2, self).__init__()
+
+ def f(self):
+ print("In python B2.f()")
+
+ # No python subclass version
+ with capture:
+ a2 = A2()
+ call_f(a2)
+ del a2
+ gc.collect()
+ assert capture == """
+ PyA2.PyA2()
+ PyA2.f()
+ A2.f()
+ PyA2.~PyA2()
+ """
+
+ # Python subclass version
+ with capture:
+ b2 = B2()
+ call_f(b2)
+ del b2
+ gc.collect()
+ assert capture == """
+ PyA2.PyA2()
+ PyA2.f()
+ In python B2.f()
+ PyA2.~PyA2()
+ """
diff --git a/ext/pybind11/tests/test_buffers.cpp b/ext/pybind11/tests/test_buffers.cpp
new file mode 100644
index 000000000..c3a7a9e02
--- /dev/null
+++ b/ext/pybind11/tests/test_buffers.cpp
@@ -0,0 +1,117 @@
+/*
+ tests/test_buffers.cpp -- supporting Pythons' buffer protocol
+
+ Copyright (c) 2016 Wenzel Jakob <wenzel.jakob@epfl.ch>
+
+ All rights reserved. Use of this source code is governed by a
+ BSD-style license that can be found in the LICENSE file.
+*/
+
+#include "pybind11_tests.h"
+#include "constructor_stats.h"
+
+class Matrix {
+public:
+ Matrix(size_t rows, size_t cols) : m_rows(rows), m_cols(cols) {
+ print_created(this, std::to_string(m_rows) + "x" + std::to_string(m_cols) + " matrix");
+ m_data = new float[rows*cols];
+ memset(m_data, 0, sizeof(float) * rows * cols);
+ }
+
+ Matrix(const Matrix &s) : m_rows(s.m_rows), m_cols(s.m_cols) {
+ print_copy_created(this, std::to_string(m_rows) + "x" + std::to_string(m_cols) + " matrix");
+ m_data = new float[m_rows * m_cols];
+ memcpy(m_data, s.m_data, sizeof(float) * m_rows * m_cols);
+ }
+
+ Matrix(Matrix &&s) : m_rows(s.m_rows), m_cols(s.m_cols), m_data(s.m_data) {
+ print_move_created(this);
+ s.m_rows = 0;
+ s.m_cols = 0;
+ s.m_data = nullptr;
+ }
+
+ ~Matrix() {
+ print_destroyed(this, std::to_string(m_rows) + "x" + std::to_string(m_cols) + " matrix");
+ delete[] m_data;
+ }
+
+ Matrix &operator=(const Matrix &s) {
+ print_copy_assigned(this, std::to_string(m_rows) + "x" + std::to_string(m_cols) + " matrix");
+ delete[] m_data;
+ m_rows = s.m_rows;
+ m_cols = s.m_cols;
+ m_data = new float[m_rows * m_cols];
+ memcpy(m_data, s.m_data, sizeof(float) * m_rows * m_cols);
+ return *this;
+ }
+
+ Matrix &operator=(Matrix &&s) {
+ print_move_assigned(this, std::to_string(m_rows) + "x" + std::to_string(m_cols) + " matrix");
+ if (&s != this) {
+ delete[] m_data;
+ m_rows = s.m_rows; m_cols = s.m_cols; m_data = s.m_data;
+ s.m_rows = 0; s.m_cols = 0; s.m_data = nullptr;
+ }
+ return *this;
+ }
+
+ float operator()(size_t i, size_t j) const {
+ return m_data[i*m_cols + j];
+ }
+
+ float &operator()(size_t i, size_t j) {
+ return m_data[i*m_cols + j];
+ }
+
+ float *data() { return m_data; }
+
+ size_t rows() const { return m_rows; }
+ size_t cols() const { return m_cols; }
+private:
+ size_t m_rows;
+ size_t m_cols;
+ float *m_data;
+};
+
+test_initializer buffers([](py::module &m) {
+ py::class_<Matrix> mtx(m, "Matrix");
+
+ mtx.def(py::init<size_t, size_t>())
+ /// Construct from a buffer
+ .def("__init__", [](Matrix &v, py::buffer b) {
+ py::buffer_info info = b.request();
+ if (info.format != py::format_descriptor<float>::format() || info.ndim != 2)
+ throw std::runtime_error("Incompatible buffer format!");
+ new (&v) Matrix(info.shape[0], info.shape[1]);
+ memcpy(v.data(), info.ptr, sizeof(float) * v.rows() * v.cols());
+ })
+
+ .def("rows", &Matrix::rows)
+ .def("cols", &Matrix::cols)
+
+ /// Bare bones interface
+ .def("__getitem__", [](const Matrix &m, std::pair<size_t, size_t> i) {
+ if (i.first >= m.rows() || i.second >= m.cols())
+ throw py::index_error();
+ return m(i.first, i.second);
+ })
+ .def("__setitem__", [](Matrix &m, std::pair<size_t, size_t> i, float v) {
+ if (i.first >= m.rows() || i.second >= m.cols())
+ throw py::index_error();
+ m(i.first, i.second) = v;
+ })
+ /// Provide buffer access
+ .def_buffer([](Matrix &m) -> py::buffer_info {
+ return py::buffer_info(
+ m.data(), /* Pointer to buffer */
+ sizeof(float), /* Size of one scalar */
+ py::format_descriptor<float>::format(), /* Python struct-style format descriptor */
+ 2, /* Number of dimensions */
+ { m.rows(), m.cols() }, /* Buffer dimensions */
+ { sizeof(float) * m.rows(), /* Strides (in bytes) for each index */
+ sizeof(float) }
+ );
+ })
+ ;
+});
diff --git a/ext/pybind11/tests/test_buffers.py b/ext/pybind11/tests/test_buffers.py
new file mode 100644
index 000000000..f0ea964d9
--- /dev/null
+++ b/ext/pybind11/tests/test_buffers.py
@@ -0,0 +1,57 @@
+import pytest
+from pybind11_tests import Matrix, ConstructorStats
+
+with pytest.suppress(ImportError):
+ import numpy as np
+
+
+@pytest.requires_numpy
+def test_to_python():
+ m = Matrix(5, 5)
+
+ assert m[2, 3] == 0
+ m[2, 3] = 4
+ assert m[2, 3] == 4
+
+ m2 = np.array(m, copy=False)
+ assert m2.shape == (5, 5)
+ assert abs(m2).sum() == 4
+ assert m2[2, 3] == 4
+ m2[2, 3] = 5
+ assert m2[2, 3] == 5
+
+ cstats = ConstructorStats.get(Matrix)
+ assert cstats.alive() == 1
+ del m
+ assert cstats.alive() == 1
+ del m2 # holds an m reference
+ 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
new file mode 100644
index 000000000..31d4e39aa
--- /dev/null
+++ b/ext/pybind11/tests/test_callbacks.cpp
@@ -0,0 +1,149 @@
+/*
+ tests/test_callbacks.cpp -- callbacks
+
+ Copyright (c) 2016 Wenzel Jakob <wenzel.jakob@epfl.ch>
+
+ All rights reserved. Use of this source code is governed by a
+ BSD-style license that can be found in the LICENSE file.
+*/
+
+#include "pybind11_tests.h"
+#include "constructor_stats.h"
+#include <pybind11/functional.h>
+
+
+py::object test_callback1(py::object func) {
+ return func();
+}
+
+py::tuple test_callback2(py::object func) {
+ return func("Hello", 'x', true, 5);
+}
+
+std::string test_callback3(const std::function<int(int)> &func) {
+ return "func(43) = " + std::to_string(func(43));
+}
+
+std::function<int(int)> test_callback4() {
+ return [](int i) { return i+1; };
+}
+
+py::cpp_function test_callback5() {
+ return py::cpp_function([](int i) { return i+1; },
+ py::arg("number"));
+}
+
+int dummy_function(int i) { return i + 1; }
+int dummy_function2(int i, int j) { return i + j; }
+std::function<int(int)> roundtrip(std::function<int(int)> f, bool expect_none = false) {
+ if (expect_none && f) {
+ throw std::runtime_error("Expected None to be converted to empty std::function");
+ }
+ return f;
+}
+
+std::string test_dummy_function(const std::function<int(int)> &f) {
+ using fn_type = int (*)(int);
+ auto result = f.target<fn_type>();
+ if (!result) {
+ auto r = f(1);
+ return "can't convert to function pointer: eval(1) = " + std::to_string(r);
+ } else if (*result == dummy_function) {
+ auto r = (*result)(1);
+ return "matches dummy_function: eval(1) = " + std::to_string(r);
+ } else {
+ return "argument does NOT match dummy_function. This should never happen!";
+ }
+}
+
+struct Payload {
+ Payload() {
+ print_default_created(this);
+ }
+ ~Payload() {
+ print_destroyed(this);
+ }
+ Payload(const Payload &) {
+ print_copy_created(this);
+ }
+ Payload(Payload &&) {
+ print_move_created(this);
+ }
+};
+
+/// Something to trigger a conversion error
+struct Unregistered {};
+
+test_initializer callbacks([](py::module &m) {
+ m.def("test_callback1", &test_callback1);
+ m.def("test_callback2", &test_callback2);
+ m.def("test_callback3", &test_callback3);
+ m.def("test_callback4", &test_callback4);
+ m.def("test_callback5", &test_callback5);
+
+ // Test keyword args and generalized unpacking
+ m.def("test_tuple_unpacking", [](py::function f) {
+ auto t1 = py::make_tuple(2, 3);
+ auto t2 = py::make_tuple(5, 6);
+ return f("positional", 1, *t1, 4, *t2);
+ });
+
+ m.def("test_dict_unpacking", [](py::function f) {
+ auto d1 = py::dict("key"_a="value", "a"_a=1);
+ auto d2 = py::dict();
+ auto d3 = py::dict("b"_a=2);
+ return f("positional", 1, **d1, **d2, **d3);
+ });
+
+ m.def("test_keyword_args", [](py::function f) {
+ return f("x"_a=10, "y"_a=20);
+ });
+
+ m.def("test_unpacking_and_keywords1", [](py::function f) {
+ auto args = py::make_tuple(2);
+ auto kwargs = py::dict("d"_a=4);
+ return f(1, *args, "c"_a=3, **kwargs);
+ });
+
+ m.def("test_unpacking_and_keywords2", [](py::function f) {
+ auto kwargs1 = py::dict("a"_a=1);
+ auto kwargs2 = py::dict("c"_a=3, "d"_a=4);
+ return f("positional", *py::make_tuple(1), 2, *py::make_tuple(3, 4), 5,
+ "key"_a="value", **kwargs1, "b"_a=2, **kwargs2, "e"_a=5);
+ });
+
+ m.def("test_unpacking_error1", [](py::function f) {
+ auto kwargs = py::dict("x"_a=3);
+ return f("x"_a=1, "y"_a=2, **kwargs); // duplicate ** after keyword
+ });
+
+ m.def("test_unpacking_error2", [](py::function f) {
+ auto kwargs = py::dict("x"_a=3);
+ return f(**kwargs, "x"_a=1); // duplicate keyword after **
+ });
+
+ m.def("test_arg_conversion_error1", [](py::function f) {
+ f(234, Unregistered(), "kw"_a=567);
+ });
+
+ m.def("test_arg_conversion_error2", [](py::function f) {
+ f(234, "expected_name"_a=Unregistered(), "kw"_a=567);
+ });
+
+ /* Test cleanup of lambda closure */
+ m.def("test_cleanup", []() -> std::function<void(void)> {
+ Payload p;
+
+ return [p]() {
+ /* p should be cleaned up when the returned function is garbage collected */
+ };
+ });
+
+ /* Test if passing a function pointer from C++ -> Python -> C++ yields the original pointer */
+ m.def("dummy_function", &dummy_function);
+ m.def("dummy_function2", &dummy_function2);
+ m.def("roundtrip", &roundtrip, py::arg("f"), py::arg("expect_none")=false);
+ m.def("test_dummy_function", &test_dummy_function);
+ // Export the payload constructor statistics for testing purposes:
+ m.def("payload_cstats", &ConstructorStats::get<Payload>);
+});
diff --git a/ext/pybind11/tests/test_callbacks.py b/ext/pybind11/tests/test_callbacks.py
new file mode 100644
index 000000000..c2668aa95
--- /dev/null
+++ b/ext/pybind11/tests/test_callbacks.py
@@ -0,0 +1,98 @@
+import pytest
+
+
+def test_callbacks():
+ from functools import partial
+ from pybind11_tests import (test_callback1, test_callback2, test_callback3,
+ test_callback4, test_callback5)
+
+ def func1():
+ return "func1"
+
+ def func2(a, b, c, d):
+ return "func2", a, b, c, d
+
+ def func3(a):
+ return "func3({})".format(a)
+
+ assert test_callback1(func1) == "func1"
+ assert test_callback2(func2) == ("func2", "Hello", "x", True, 5)
+ assert test_callback1(partial(func2, 1, 2, 3, 4)) == ("func2", 1, 2, 3, 4)
+ assert test_callback1(partial(func3, "partial")) == "func3(partial)"
+ assert test_callback3(lambda i: i + 1) == "func(43) = 44"
+
+ f = test_callback4()
+ assert f(43) == 44
+ f = test_callback5()
+ assert f(number=43) == 44
+
+
+def test_keyword_args_and_generalized_unpacking():
+ from pybind11_tests import (test_tuple_unpacking, test_dict_unpacking, test_keyword_args,
+ test_unpacking_and_keywords1, test_unpacking_and_keywords2,
+ test_unpacking_error1, test_unpacking_error2,
+ test_arg_conversion_error1, test_arg_conversion_error2)
+
+ def f(*args, **kwargs):
+ return args, kwargs
+
+ assert test_tuple_unpacking(f) == (("positional", 1, 2, 3, 4, 5, 6), {})
+ assert test_dict_unpacking(f) == (("positional", 1), {"key": "value", "a": 1, "b": 2})
+ assert test_keyword_args(f) == ((), {"x": 10, "y": 20})
+ assert test_unpacking_and_keywords1(f) == ((1, 2), {"c": 3, "d": 4})
+ assert test_unpacking_and_keywords2(f) == (
+ ("positional", 1, 2, 3, 4, 5),
+ {"key": "value", "a": 1, "b": 2, "c": 3, "d": 4, "e": 5}
+ )
+
+ with pytest.raises(TypeError) as excinfo:
+ test_unpacking_error1(f)
+ assert "Got multiple values for keyword argument" in str(excinfo.value)
+
+ with pytest.raises(TypeError) as excinfo:
+ test_unpacking_error2(f)
+ assert "Got multiple values for keyword argument" in str(excinfo.value)
+
+ with pytest.raises(RuntimeError) as excinfo:
+ test_arg_conversion_error1(f)
+ assert "Unable to convert call argument" in str(excinfo.value)
+
+ with pytest.raises(RuntimeError) as excinfo:
+ test_arg_conversion_error2(f)
+ assert "Unable to convert call argument" in str(excinfo.value)
+
+
+def test_lambda_closure_cleanup():
+ from pybind11_tests import test_cleanup, payload_cstats
+
+ test_cleanup()
+ cstats = payload_cstats()
+ assert cstats.alive() == 0
+ assert cstats.copy_constructions == 1
+ assert cstats.move_constructions >= 1
+
+
+def test_cpp_function_roundtrip():
+ """Test if passing a function pointer from C++ -> Python -> C++ yields the original pointer"""
+ from pybind11_tests import dummy_function, dummy_function2, test_dummy_function, roundtrip
+
+ assert test_dummy_function(dummy_function) == "matches dummy_function: eval(1) = 2"
+ assert test_dummy_function(roundtrip(dummy_function)) == "matches dummy_function: eval(1) = 2"
+ assert roundtrip(None, expect_none=True) is None
+ assert test_dummy_function(lambda x: x + 2) == "can't convert to function pointer: eval(1) = 3"
+
+ with pytest.raises(TypeError) as excinfo:
+ test_dummy_function(dummy_function2)
+ assert "incompatible function arguments" in str(excinfo.value)
+
+ with pytest.raises(TypeError) as excinfo:
+ test_dummy_function(lambda x, y: x + y)
+ assert any(s in str(excinfo.value) for s in ("missing 1 required positional argument",
+ "takes exactly 2 arguments"))
+
+
+def test_function_signatures(doc):
+ from pybind11_tests import test_callback3, test_callback4
+
+ assert doc(test_callback3) == "test_callback3(arg0: Callable[[int], int]) -> str"
+ assert doc(test_callback4) == "test_callback4() -> Callable[[int], int]"
diff --git a/ext/pybind11/tests/test_chrono.cpp b/ext/pybind11/tests/test_chrono.cpp
new file mode 100644
index 000000000..b86f57adf
--- /dev/null
+++ b/ext/pybind11/tests/test_chrono.cpp
@@ -0,0 +1,59 @@
+/*
+ tests/test_chrono.cpp -- test conversions to/from std::chrono types
+
+ Copyright (c) 2016 Trent Houliston <trent@houliston.me> and
+ Wenzel Jakob <wenzel.jakob@epfl.ch>
+
+ All rights reserved. Use of this source code is governed by a
+ BSD-style license that can be found in the LICENSE file.
+*/
+
+
+#include "pybind11_tests.h"
+#include "constructor_stats.h"
+#include <pybind11/chrono.h>
+
+// Return the current time off the wall clock
+std::chrono::system_clock::time_point test_chrono1() {
+ return std::chrono::system_clock::now();
+}
+
+// Round trip the passed in system clock time
+std::chrono::system_clock::time_point test_chrono2(std::chrono::system_clock::time_point t) {
+ return t;
+}
+
+// Round trip the passed in duration
+std::chrono::system_clock::duration test_chrono3(std::chrono::system_clock::duration d) {
+ return d;
+}
+
+// Difference between two passed in time_points
+std::chrono::system_clock::duration test_chrono4(std::chrono::system_clock::time_point a, std::chrono::system_clock::time_point b) {
+ return a - b;
+}
+
+// Return the current time off the steady_clock
+std::chrono::steady_clock::time_point test_chrono5() {
+ return std::chrono::steady_clock::now();
+}
+
+// Round trip a steady clock timepoint
+std::chrono::steady_clock::time_point test_chrono6(std::chrono::steady_clock::time_point t) {
+ return t;
+}
+
+// Roundtrip a duration in microseconds from a float argument
+std::chrono::microseconds test_chrono7(std::chrono::microseconds t) {
+ return t;
+}
+
+test_initializer chrono([] (py::module &m) {
+ m.def("test_chrono1", &test_chrono1);
+ m.def("test_chrono2", &test_chrono2);
+ m.def("test_chrono3", &test_chrono3);
+ m.def("test_chrono4", &test_chrono4);
+ m.def("test_chrono5", &test_chrono5);
+ m.def("test_chrono6", &test_chrono6);
+ m.def("test_chrono7", &test_chrono7);
+});
diff --git a/ext/pybind11/tests/test_chrono.py b/ext/pybind11/tests/test_chrono.py
new file mode 100644
index 000000000..94ca55c76
--- /dev/null
+++ b/ext/pybind11/tests/test_chrono.py
@@ -0,0 +1,116 @@
+
+
+def test_chrono_system_clock():
+ from pybind11_tests import test_chrono1
+ import datetime
+
+ # Get the time from both c++ and datetime
+ date1 = test_chrono1()
+ date2 = datetime.datetime.today()
+
+ # The returned value should be a datetime
+ assert isinstance(date1, datetime.datetime)
+
+ # The numbers should vary by a very small amount (time it took to execute)
+ diff = abs(date1 - date2)
+
+ # There should never be a days/seconds difference
+ assert diff.days == 0
+ assert diff.seconds == 0
+
+ # We test that no more than about 0.5 seconds passes here
+ # This makes sure that the dates created are very close to the same
+ # but if the testing system is incredibly overloaded this should still pass
+ assert diff.microseconds < 500000
+
+
+def test_chrono_system_clock_roundtrip():
+ from pybind11_tests import test_chrono2
+ import datetime
+
+ date1 = datetime.datetime.today()
+
+ # Roundtrip the time
+ date2 = test_chrono2(date1)
+
+ # The returned value should be a datetime
+ assert isinstance(date2, datetime.datetime)
+
+ # They should be identical (no information lost on roundtrip)
+ diff = abs(date1 - date2)
+ assert diff.days == 0
+ assert diff.seconds == 0
+ assert diff.microseconds == 0
+
+
+def test_chrono_duration_roundtrip():
+ from pybind11_tests import test_chrono3
+ import datetime
+
+ # Get the difference between two times (a timedelta)
+ date1 = datetime.datetime.today()
+ date2 = datetime.datetime.today()
+ diff = date2 - date1
+
+ # Make sure this is a timedelta
+ assert isinstance(diff, datetime.timedelta)
+
+ cpp_diff = test_chrono3(diff)
+
+ assert cpp_diff.days == diff.days
+ assert cpp_diff.seconds == diff.seconds
+ assert cpp_diff.microseconds == diff.microseconds
+
+
+def test_chrono_duration_subtraction_equivalence():
+ from pybind11_tests import test_chrono4
+ import datetime
+
+ date1 = datetime.datetime.today()
+ date2 = datetime.datetime.today()
+
+ diff = date2 - date1
+ cpp_diff = test_chrono4(date2, date1)
+
+ assert cpp_diff.days == diff.days
+ assert cpp_diff.seconds == diff.seconds
+ assert cpp_diff.microseconds == diff.microseconds
+
+
+def test_chrono_steady_clock():
+ from pybind11_tests import test_chrono5
+ import datetime
+
+ time1 = test_chrono5()
+ time2 = test_chrono5()
+
+ assert isinstance(time1, datetime.timedelta)
+ assert isinstance(time2, datetime.timedelta)
+
+
+def test_chrono_steady_clock_roundtrip():
+ from pybind11_tests import test_chrono6
+ import datetime
+
+ time1 = datetime.timedelta(days=10, seconds=10, microseconds=100)
+ time2 = test_chrono6(time1)
+
+ assert isinstance(time2, datetime.timedelta)
+
+ # They should be identical (no information lost on roundtrip)
+ assert time1.days == time2.days
+ assert time1.seconds == time2.seconds
+ assert time1.microseconds == time2.microseconds
+
+
+def test_floating_point_duration():
+ from pybind11_tests import test_chrono7
+ import datetime
+
+ # Test using 35.525123 seconds as an example floating point number in seconds
+ time = test_chrono7(35.525123)
+
+ assert isinstance(time, datetime.timedelta)
+
+ assert time.seconds == 35
+ assert 525122 <= time.microseconds <= 525123
diff --git a/ext/pybind11/tests/test_class_args.cpp b/ext/pybind11/tests/test_class_args.cpp
new file mode 100644
index 000000000..e18b39db2
--- /dev/null
+++ b/ext/pybind11/tests/test_class_args.cpp
@@ -0,0 +1,68 @@
+/*
+ tests/test_class_args.cpp -- tests that various way of defining a class work
+
+ Copyright (c) 2016 Wenzel Jakob <wenzel.jakob@epfl.ch>
+
+ All rights reserved. Use of this source code is governed by a
+ BSD-style license that can be found in the LICENSE file.
+*/
+
+#include "pybind11_tests.h"
+
+
+template <int N> class BreaksBase {};
+template <int N> class BreaksTramp : public BreaksBase<N> {};
+// These should all compile just fine:
+typedef py::class_<BreaksBase<1>, std::unique_ptr<BreaksBase<1>>, BreaksTramp<1>> DoesntBreak1;
+typedef py::class_<BreaksBase<2>, BreaksTramp<2>, std::unique_ptr<BreaksBase<2>>> DoesntBreak2;
+typedef py::class_<BreaksBase<3>, std::unique_ptr<BreaksBase<3>>> DoesntBreak3;
+typedef py::class_<BreaksBase<4>, BreaksTramp<4>> DoesntBreak4;
+typedef py::class_<BreaksBase<5>> DoesntBreak5;
+typedef py::class_<BreaksBase<6>, std::shared_ptr<BreaksBase<6>>, BreaksTramp<6>> DoesntBreak6;
+typedef py::class_<BreaksBase<7>, BreaksTramp<7>, std::shared_ptr<BreaksBase<7>>> DoesntBreak7;
+typedef py::class_<BreaksBase<8>, std::shared_ptr<BreaksBase<8>>> DoesntBreak8;
+#define CHECK_BASE(N) static_assert(std::is_same<typename DoesntBreak##N::type, BreaksBase<N>>::value, \
+ "DoesntBreak" #N " has wrong type!")
+CHECK_BASE(1); CHECK_BASE(2); CHECK_BASE(3); CHECK_BASE(4); CHECK_BASE(5); CHECK_BASE(6); CHECK_BASE(7); CHECK_BASE(8);
+#define CHECK_ALIAS(N) static_assert(DoesntBreak##N::has_alias && std::is_same<typename DoesntBreak##N::type_alias, BreaksTramp<N>>::value, \
+ "DoesntBreak" #N " has wrong type_alias!")
+#define CHECK_NOALIAS(N) static_assert(!DoesntBreak##N::has_alias && std::is_void<typename DoesntBreak##N::type_alias>::value, \
+ "DoesntBreak" #N " has type alias, but shouldn't!")
+CHECK_ALIAS(1); CHECK_ALIAS(2); CHECK_NOALIAS(3); CHECK_ALIAS(4); CHECK_NOALIAS(5); CHECK_ALIAS(6); CHECK_ALIAS(7); CHECK_NOALIAS(8);
+#define CHECK_HOLDER(N, TYPE) static_assert(std::is_same<typename DoesntBreak##N::holder_type, std::TYPE##_ptr<BreaksBase<N>>>::value, \
+ "DoesntBreak" #N " has wrong holder_type!")
+CHECK_HOLDER(1, unique); CHECK_HOLDER(2, unique); CHECK_HOLDER(3, unique); CHECK_HOLDER(4, unique); CHECK_HOLDER(5, unique);
+CHECK_HOLDER(6, shared); CHECK_HOLDER(7, shared); CHECK_HOLDER(8, shared);
+
+// There's no nice way to test that these fail because they fail to compile; leave them here,
+// though, so that they can be manually tested by uncommenting them (and seeing that compilation
+// failures occurs).
+
+// We have to actually look into the type: the typedef alone isn't enough to instantiate the type:
+#define CHECK_BROKEN(N) static_assert(std::is_same<typename Breaks##N::type, BreaksBase<-N>>::value, \
+ "Breaks1 has wrong type!");
+
+//// Two holder classes:
+//typedef py::class_<BreaksBase<-1>, std::unique_ptr<BreaksBase<-1>>, std::unique_ptr<BreaksBase<-1>>> Breaks1;
+//CHECK_BROKEN(1);
+//// Two aliases:
+//typedef py::class_<BreaksBase<-2>, BreaksTramp<-2>, BreaksTramp<-2>> Breaks2;
+//CHECK_BROKEN(2);
+//// Holder + 2 aliases
+//typedef py::class_<BreaksBase<-3>, std::unique_ptr<BreaksBase<-3>>, BreaksTramp<-3>, BreaksTramp<-3>> Breaks3;
+//CHECK_BROKEN(3);
+//// Alias + 2 holders
+//typedef py::class_<BreaksBase<-4>, std::unique_ptr<BreaksBase<-4>>, BreaksTramp<-4>, std::shared_ptr<BreaksBase<-4>>> Breaks4;
+//CHECK_BROKEN(4);
+//// Invalid option (not a subclass or holder)
+//typedef py::class_<BreaksBase<-5>, BreaksTramp<-4>> Breaks5;
+//CHECK_BROKEN(5);
+//// Invalid option: multiple inheritance not supported:
+//template <> struct BreaksBase<-8> : BreaksBase<-6>, BreaksBase<-7> {};
+//typedef py::class_<BreaksBase<-8>, BreaksBase<-6>, BreaksBase<-7>> Breaks8;
+//CHECK_BROKEN(8);
+
+test_initializer class_args([](py::module &m) {
+ // Just test that this compiled okay
+ m.def("class_args_noop", []() {});
+});
diff --git a/ext/pybind11/tests/test_class_args.py b/ext/pybind11/tests/test_class_args.py
new file mode 100644
index 000000000..40cbcec9f
--- /dev/null
+++ b/ext/pybind11/tests/test_class_args.py
@@ -0,0 +1,8 @@
+
+
+def test_class_args():
+ """There's basically nothing to test here; just make sure the code compiled
+ and declared its definition
+ """
+ from pybind11_tests import class_args_noop
+ class_args_noop()
diff --git a/ext/pybind11/tests/test_constants_and_functions.cpp b/ext/pybind11/tests/test_constants_and_functions.cpp
new file mode 100644
index 000000000..c8c0392c9
--- /dev/null
+++ b/ext/pybind11/tests/test_constants_and_functions.cpp
@@ -0,0 +1,66 @@
+/*
+ tests/test_constants_and_functions.cpp -- global constants and functions, enumerations, raw byte strings
+
+ Copyright (c) 2016 Wenzel Jakob <wenzel.jakob@epfl.ch>
+
+ All rights reserved. Use of this source code is governed by a
+ BSD-style license that can be found in the LICENSE file.
+*/
+
+#include "pybind11_tests.h"
+
+enum MyEnum { EFirstEntry = 1, ESecondEntry };
+
+std::string test_function1() {
+ return "test_function()";
+}
+
+std::string test_function2(MyEnum k) {
+ return "test_function(enum=" + std::to_string(k) + ")";
+}
+
+std::string test_function3(int i) {
+ return "test_function(" + std::to_string(i) + ")";
+}
+
+py::str test_function4(int, float) { return "test_function(int, float)"; }
+py::str test_function4(float, int) { return "test_function(float, int)"; }
+
+py::bytes return_bytes() {
+ const char *data = "\x01\x00\x02\x00";
+ return std::string(data, 4);
+}
+
+std::string print_bytes(py::bytes bytes) {
+ std::string ret = "bytes[";
+ const auto value = static_cast<std::string>(bytes);
+ for (size_t i = 0; i < value.length(); ++i) {
+ ret += std::to_string(static_cast<int>(value[i])) + " ";
+ }
+ ret.back() = ']';
+ return ret;
+}
+
+test_initializer constants_and_functions([](py::module &m) {
+ m.attr("some_constant") = py::int_(14);
+
+ m.def("test_function", &test_function1);
+ m.def("test_function", &test_function2);
+ m.def("test_function", &test_function3);
+
+#if defined(PYBIND11_OVERLOAD_CAST)
+ m.def("test_function", py::overload_cast<int, float>(&test_function4));
+ m.def("test_function", py::overload_cast<float, int>(&test_function4));
+#else
+ m.def("test_function", static_cast<py::str (*)(int, float)>(&test_function4));
+ m.def("test_function", static_cast<py::str (*)(float, int)>(&test_function4));
+#endif
+
+ py::enum_<MyEnum>(m, "MyEnum")
+ .value("EFirstEntry", EFirstEntry)
+ .value("ESecondEntry", ESecondEntry)
+ .export_values();
+
+ m.def("return_bytes", &return_bytes);
+ m.def("print_bytes", &print_bytes);
+});
diff --git a/ext/pybind11/tests/test_constants_and_functions.py b/ext/pybind11/tests/test_constants_and_functions.py
new file mode 100644
index 000000000..d13d3af1b
--- /dev/null
+++ b/ext/pybind11/tests/test_constants_and_functions.py
@@ -0,0 +1,24 @@
+
+
+def test_constants():
+ from pybind11_tests import some_constant
+
+ assert some_constant == 14
+
+
+def test_function_overloading():
+ from pybind11_tests import MyEnum, test_function
+
+ assert test_function() == "test_function()"
+ assert test_function(7) == "test_function(7)"
+ assert test_function(MyEnum.EFirstEntry) == "test_function(enum=1)"
+ assert test_function(MyEnum.ESecondEntry) == "test_function(enum=2)"
+
+ assert test_function(1, 1.0) == "test_function(int, float)"
+ assert test_function(2.0, 2) == "test_function(float, int)"
+
+
+def test_bytes():
+ from pybind11_tests import return_bytes, print_bytes
+
+ assert print_bytes(return_bytes()) == "bytes[1 0 2 0]"
diff --git a/ext/pybind11/tests/test_copy_move_policies.cpp b/ext/pybind11/tests/test_copy_move_policies.cpp
new file mode 100644
index 000000000..6f7907c1f
--- /dev/null
+++ b/ext/pybind11/tests/test_copy_move_policies.cpp
@@ -0,0 +1,41 @@
+/*
+ tests/test_copy_move_policies.cpp -- 'copy' and 'move'
+ return value policies
+
+ Copyright (c) 2016 Ben North <ben@redfrontdoor.org>
+
+ All rights reserved. Use of this source code is governed by a
+ BSD-style license that can be found in the LICENSE file.
+*/
+
+#include "pybind11_tests.h"
+
+template <typename derived>
+struct empty {
+ static const derived& get_one() { return instance_; }
+ static derived instance_;
+};
+
+struct lacking_copy_ctor : public empty<lacking_copy_ctor> {
+ lacking_copy_ctor() {}
+ lacking_copy_ctor(const lacking_copy_ctor& other) = delete;
+};
+
+template <> lacking_copy_ctor empty<lacking_copy_ctor>::instance_ = {};
+
+struct lacking_move_ctor : public empty<lacking_move_ctor> {
+ lacking_move_ctor() {}
+ lacking_move_ctor(const lacking_move_ctor& other) = delete;
+ lacking_move_ctor(lacking_move_ctor&& other) = delete;
+};
+
+template <> lacking_move_ctor empty<lacking_move_ctor>::instance_ = {};
+
+test_initializer copy_move_policies([](py::module &m) {
+ py::class_<lacking_copy_ctor>(m, "lacking_copy_ctor")
+ .def_static("get_one", &lacking_copy_ctor::get_one,
+ py::return_value_policy::copy);
+ py::class_<lacking_move_ctor>(m, "lacking_move_ctor")
+ .def_static("get_one", &lacking_move_ctor::get_one,
+ py::return_value_policy::move);
+});
diff --git a/ext/pybind11/tests/test_copy_move_policies.py b/ext/pybind11/tests/test_copy_move_policies.py
new file mode 100644
index 000000000..edcf38075
--- /dev/null
+++ b/ext/pybind11/tests/test_copy_move_policies.py
@@ -0,0 +1,15 @@
+import pytest
+
+
+def test_lacking_copy_ctor():
+ from pybind11_tests import lacking_copy_ctor
+ with pytest.raises(RuntimeError) as excinfo:
+ lacking_copy_ctor.get_one()
+ assert "the object is non-copyable!" in str(excinfo.value)
+
+
+def test_lacking_move_ctor():
+ from pybind11_tests import lacking_move_ctor
+ with pytest.raises(RuntimeError) as excinfo:
+ lacking_move_ctor.get_one()
+ assert "the object is neither movable nor copyable!" in str(excinfo.value)
diff --git a/ext/pybind11/tests/test_docstring_options.cpp b/ext/pybind11/tests/test_docstring_options.cpp
new file mode 100644
index 000000000..74178c272
--- /dev/null
+++ b/ext/pybind11/tests/test_docstring_options.cpp
@@ -0,0 +1,53 @@
+/*
+ tests/test_docstring_options.cpp -- generation of docstrings and signatures
+
+ Copyright (c) 2016 Wenzel Jakob <wenzel.jakob@epfl.ch>
+
+ All rights reserved. Use of this source code is governed by a
+ BSD-style license that can be found in the LICENSE file.
+*/
+
+#include "pybind11_tests.h"
+
+struct DocstringTestFoo {
+ int value;
+ void setValue(int v) { value = v; }
+ int getValue() const { return value; }
+};
+
+test_initializer docstring_generation([](py::module &m) {
+
+ {
+ py::options options;
+ options.disable_function_signatures();
+
+ m.def("test_function1", [](int, int) {}, py::arg("a"), py::arg("b"));
+ m.def("test_function2", [](int, int) {}, py::arg("a"), py::arg("b"), "A custom docstring");
+
+ options.enable_function_signatures();
+
+ m.def("test_function3", [](int, int) {}, py::arg("a"), py::arg("b"));
+ m.def("test_function4", [](int, int) {}, py::arg("a"), py::arg("b"), "A custom docstring");
+
+ options.disable_function_signatures().disable_user_defined_docstrings();
+
+ m.def("test_function5", [](int, int) {}, py::arg("a"), py::arg("b"), "A custom docstring");
+
+ {
+ py::options nested_options;
+ nested_options.enable_user_defined_docstrings();
+ m.def("test_function6", [](int, int) {}, py::arg("a"), py::arg("b"), "A custom docstring");
+ }
+ }
+
+ m.def("test_function7", [](int, int) {}, py::arg("a"), py::arg("b"), "A custom docstring");
+
+ {
+ py::options options;
+ options.disable_user_defined_docstrings();
+
+ py::class_<DocstringTestFoo>(m, "DocstringTestFoo", "This is a class docstring")
+ .def_property("value_prop", &DocstringTestFoo::getValue, &DocstringTestFoo::setValue, "This is a property docstring")
+ ;
+ }
+});
diff --git a/ext/pybind11/tests/test_docstring_options.py b/ext/pybind11/tests/test_docstring_options.py
new file mode 100644
index 000000000..66ad6b89f
--- /dev/null
+++ b/ext/pybind11/tests/test_docstring_options.py
@@ -0,0 +1,32 @@
+
+
+def test_docstring_options():
+ from pybind11_tests import (test_function1, test_function2, test_function3,
+ test_function4, test_function5, test_function6,
+ test_function7, DocstringTestFoo)
+
+ # options.disable_function_signatures()
+ assert not test_function1.__doc__
+
+ assert test_function2.__doc__ == "A custom docstring"
+
+ # options.enable_function_signatures()
+ assert test_function3.__doc__ .startswith("test_function3(a: int, b: int) -> None")
+
+ assert test_function4.__doc__ .startswith("test_function4(a: int, b: int) -> None")
+ assert test_function4.__doc__ .endswith("A custom docstring\n")
+
+ # options.disable_function_signatures()
+ # options.disable_user_defined_docstrings()
+ assert not test_function5.__doc__
+
+ # nested options.enable_user_defined_docstrings()
+ assert test_function6.__doc__ == "A custom docstring"
+
+ # RAII destructor
+ assert test_function7.__doc__ .startswith("test_function7(a: int, b: int) -> None")
+ assert test_function7.__doc__ .endswith("A custom docstring\n")
+
+ # Suppression of user-defined docstrings for non-function objects
+ assert not DocstringTestFoo.__doc__
+ assert not DocstringTestFoo.value_prop.__doc__
diff --git a/ext/pybind11/tests/test_eigen.cpp b/ext/pybind11/tests/test_eigen.cpp
new file mode 100644
index 000000000..588cdceb3
--- /dev/null
+++ b/ext/pybind11/tests/test_eigen.cpp
@@ -0,0 +1,134 @@
+/*
+ tests/eigen.cpp -- automatic conversion of Eigen types
+
+ Copyright (c) 2016 Wenzel Jakob <wenzel.jakob@epfl.ch>
+
+ All rights reserved. Use of this source code is governed by a
+ BSD-style license that can be found in the LICENSE file.
+*/
+
+#include "pybind11_tests.h"
+#include <pybind11/eigen.h>
+#include <Eigen/Cholesky>
+
+Eigen::VectorXf double_col(const Eigen::VectorXf& x)
+{ return 2.0f * x; }
+
+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(); }
+
+typedef Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor> MatrixXfRowMajor;
+MatrixXfRowMajor double_mat_rm(const MatrixXfRowMajor& x)
+{ return 2.0f * x; }
+
+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::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);
+
+ // Returns diagonals: a vector-like object with an inner stride != 1
+ m.def("diagonal", [](const Eigen::Ref<const Eigen::MatrixXd> &x) { return x.diagonal(); });
+ m.def("diagonal_1", [](const Eigen::Ref<const Eigen::MatrixXd> &x) { return x.diagonal<1>(); });
+ m.def("diagonal_n", [](const Eigen::Ref<const Eigen::MatrixXd> &x, int index) { return x.diagonal(index); });
+
+ // Return a block of a matrix (gives non-standard strides)
+ m.def("block", [](const Eigen::Ref<const Eigen::MatrixXd> &x, int start_row, int start_col, int block_rows, int block_cols) {
+ return x.block(start_row, start_col, block_rows, block_cols);
+ });
+
+ // Returns a DiagonalMatrix with diagonal (1,2,3,...)
+ m.def("incr_diag", [](int k) {
+ Eigen::DiagonalMatrix<int, Eigen::Dynamic> m(k);
+ for (int i = 0; i < k; i++) m.diagonal()[i] = i+1;
+ return m;
+ });
+
+ // Returns a SelfAdjointView referencing the lower triangle of m
+ m.def("symmetric_lower", [](const Eigen::MatrixXi &m) {
+ return m.selfadjointView<Eigen::Lower>();
+ });
+ // Returns a SelfAdjointView referencing the lower triangle of m
+ m.def("symmetric_upper", [](const Eigen::MatrixXi &m) {
+ return m.selfadjointView<Eigen::Upper>();
+ });
+
+ 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);
+ });
+
+ m.def("dense_passthrough_r", [](const DenseMatrixR &m) -> DenseMatrixR {
+ return m;
+ });
+
+ m.def("dense_passthrough_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_passthrough_r", [](const SparseMatrixR &m) -> SparseMatrixR {
+ return m;
+ });
+
+ m.def("sparse_passthrough_c", [](const SparseMatrixC &m) -> SparseMatrixC {
+ return m;
+ });
+});
diff --git a/ext/pybind11/tests/test_eigen.py b/ext/pybind11/tests/test_eigen.py
new file mode 100644
index 000000000..b0092fc8b
--- /dev/null
+++ b/ext/pybind11/tests/test_eigen.py
@@ -0,0 +1,145 @@
+import pytest
+
+with pytest.suppress(ImportError):
+ import numpy as np
+
+ ref = np.array([[ 0, 3, 0, 0, 0, 11],
+ [22, 0, 0, 0, 17, 11],
+ [ 7, 5, 0, 1, 0, 11],
+ [ 0, 0, 0, 0, 0, 11],
+ [ 0, 0, 14, 0, 8, 11]])
+
+
+def assert_equal_ref(mat):
+ np.testing.assert_array_equal(mat, ref)
+
+
+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
+
+ 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()))
+
+
+@pytest.requires_eigen_and_numpy
+def test_dense():
+ from pybind11_tests import dense_r, dense_c, dense_passthrough_r, dense_passthrough_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()))
+
+
+@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
+
+ 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)
+
+ 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)
+
+
+@pytest.requires_eigen_and_numpy
+def test_nonunit_stride_to_python():
+ from pybind11_tests import diagonal, diagonal_1, diagonal_n, block
+
+ assert np.all(diagonal(ref) == ref.diagonal())
+ assert np.all(diagonal_1(ref) == ref.diagonal(1))
+ for i in range(-5, 7):
+ assert np.all(diagonal_n(ref, i) == ref.diagonal(i)), "diagonal_n({})".format(i)
+
+ assert np.all(block(ref, 2, 1, 3, 3) == ref[2:5, 1:4])
+ assert np.all(block(ref, 1, 4, 4, 2) == ref[1:, 4:])
+ 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
+
+ chols = [cholesky1, cholesky2, cholesky3, cholesky4, cholesky5, cholesky6]
+ for i, chol in enumerate(chols, start=1):
+ mymat = chol(np.array([[1, 2, 4], [2, 13, 23], [4, 23, 77]]))
+ assert np.all(mymat == np.array([[1, 0, 0], [2, 3, 0], [4, 5, 6]])), "cholesky{}".format(i)
+
+
+@pytest.requires_eigen_and_numpy
+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]))
+
+ asymm = np.array([[ 1, 2, 3, 4],
+ [ 5, 6, 7, 8],
+ [ 9, 10, 11, 12],
+ [13, 14, 15, 16]])
+ symm_lower = np.array(asymm)
+ symm_upper = np.array(asymm)
+ for i in range(4):
+ for j in range(i + 1, 4):
+ symm_lower[i, j] = symm_lower[j, i]
+ symm_upper[j, i] = symm_upper[i, j]
+
+ assert np.all(symmetric_lower(asymm) == symm_lower)
+ 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
+
+ assert doc(double_col) == """
+ double_col(arg0: numpy.ndarray[float32[m, 1]]) -> numpy.ndarray[float32[m, 1]]
+ """
+ assert doc(double_row) == """
+ double_row(arg0: numpy.ndarray[float32[1, n]]) -> numpy.ndarray[float32[1, n]]
+ """
+ assert doc(double_mat_rm) == """
+ double_mat_rm(arg0: numpy.ndarray[float32[m, n]]) -> numpy.ndarray[float32[m, n]]
+ """
+
+
+@pytest.requires_eigen_and_scipy
+def test_sparse():
+ from pybind11_tests import sparse_r, sparse_c, sparse_passthrough_r, sparse_passthrough_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()))
+
+
+@pytest.requires_eigen_and_scipy
+def test_sparse_signature(doc):
+ from pybind11_tests import sparse_passthrough_r, sparse_passthrough_c
+
+ assert doc(sparse_passthrough_r) == """
+ sparse_passthrough_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]
+ """ # noqa: E501 line too long
diff --git a/ext/pybind11/tests/test_enum.cpp b/ext/pybind11/tests/test_enum.cpp
new file mode 100644
index 000000000..09f334cdb
--- /dev/null
+++ b/ext/pybind11/tests/test_enum.cpp
@@ -0,0 +1,68 @@
+/*
+ tests/test_enums.cpp -- enumerations
+
+ Copyright (c) 2016 Wenzel Jakob <wenzel.jakob@epfl.ch>
+
+ All rights reserved. Use of this source code is governed by a
+ BSD-style license that can be found in the LICENSE file.
+*/
+
+#include "pybind11_tests.h"
+
+enum UnscopedEnum {
+ EOne = 1,
+ ETwo
+};
+
+enum class ScopedEnum {
+ Two = 2,
+ Three
+};
+
+enum Flags {
+ Read = 4,
+ Write = 2,
+ Execute = 1
+};
+
+class ClassWithUnscopedEnum {
+public:
+ enum EMode {
+ EFirstMode = 1,
+ ESecondMode
+ };
+
+ static EMode test_function(EMode mode) {
+ return mode;
+ }
+};
+
+std::string test_scoped_enum(ScopedEnum z) {
+ return "ScopedEnum::" + std::string(z == ScopedEnum::Two ? "Two" : "Three");
+}
+
+test_initializer enums([](py::module &m) {
+ m.def("test_scoped_enum", &test_scoped_enum);
+
+ py::enum_<UnscopedEnum>(m, "UnscopedEnum", py::arithmetic())
+ .value("EOne", EOne)
+ .value("ETwo", ETwo)
+ .export_values();
+
+ py::enum_<ScopedEnum>(m, "ScopedEnum", py::arithmetic())
+ .value("Two", ScopedEnum::Two)
+ .value("Three", ScopedEnum::Three);
+
+ py::enum_<Flags>(m, "Flags", py::arithmetic())
+ .value("Read", Flags::Read)
+ .value("Write", Flags::Write)
+ .value("Execute", Flags::Execute)
+ .export_values();
+
+ py::class_<ClassWithUnscopedEnum> exenum_class(m, "ClassWithUnscopedEnum");
+ exenum_class.def_static("test_function", &ClassWithUnscopedEnum::test_function);
+ py::enum_<ClassWithUnscopedEnum::EMode>(exenum_class, "EMode")
+ .value("EFirstMode", ClassWithUnscopedEnum::EFirstMode)
+ .value("ESecondMode", ClassWithUnscopedEnum::ESecondMode)
+ .export_values();
+});
diff --git a/ext/pybind11/tests/test_enum.py b/ext/pybind11/tests/test_enum.py
new file mode 100644
index 000000000..de5f3c6f6
--- /dev/null
+++ b/ext/pybind11/tests/test_enum.py
@@ -0,0 +1,108 @@
+import pytest
+
+
+def test_unscoped_enum():
+ from pybind11_tests import UnscopedEnum, EOne
+
+ assert str(UnscopedEnum.EOne) == "UnscopedEnum.EOne"
+ assert str(UnscopedEnum.ETwo) == "UnscopedEnum.ETwo"
+ assert str(EOne) == "UnscopedEnum.EOne"
+
+ # no TypeError exception for unscoped enum ==/!= int comparisons
+ y = UnscopedEnum.ETwo
+ assert y == 2
+ assert y != 3
+
+ assert int(UnscopedEnum.ETwo) == 2
+ assert str(UnscopedEnum(2)) == "UnscopedEnum.ETwo"
+
+ # order
+ assert UnscopedEnum.EOne < UnscopedEnum.ETwo
+ assert UnscopedEnum.EOne < 2
+ assert UnscopedEnum.ETwo > UnscopedEnum.EOne
+ assert UnscopedEnum.ETwo > 1
+ assert UnscopedEnum.ETwo <= 2
+ assert UnscopedEnum.ETwo >= 2
+ assert UnscopedEnum.EOne <= UnscopedEnum.ETwo
+ assert UnscopedEnum.EOne <= 2
+ assert UnscopedEnum.ETwo >= UnscopedEnum.EOne
+ assert UnscopedEnum.ETwo >= 1
+ assert not (UnscopedEnum.ETwo < UnscopedEnum.EOne)
+ assert not (2 < UnscopedEnum.EOne)
+
+
+def test_scoped_enum():
+ from pybind11_tests import ScopedEnum, test_scoped_enum
+
+ assert test_scoped_enum(ScopedEnum.Three) == "ScopedEnum::Three"
+ z = ScopedEnum.Two
+ assert test_scoped_enum(z) == "ScopedEnum::Two"
+
+ # expected TypeError exceptions for scoped enum ==/!= int comparisons
+ with pytest.raises(TypeError):
+ assert z == 2
+ with pytest.raises(TypeError):
+ assert z != 3
+
+ # order
+ assert ScopedEnum.Two < ScopedEnum.Three
+ assert ScopedEnum.Three > ScopedEnum.Two
+ assert ScopedEnum.Two <= ScopedEnum.Three
+ assert ScopedEnum.Two <= ScopedEnum.Two
+ assert ScopedEnum.Two >= ScopedEnum.Two
+ assert ScopedEnum.Three >= ScopedEnum.Two
+
+
+def test_implicit_conversion():
+ from pybind11_tests import ClassWithUnscopedEnum
+
+ assert str(ClassWithUnscopedEnum.EMode.EFirstMode) == "EMode.EFirstMode"
+ assert str(ClassWithUnscopedEnum.EFirstMode) == "EMode.EFirstMode"
+
+ f = ClassWithUnscopedEnum.test_function
+ first = ClassWithUnscopedEnum.EFirstMode
+ second = ClassWithUnscopedEnum.ESecondMode
+
+ assert f(first) == 1
+
+ assert f(first) == f(first)
+ assert not f(first) != f(first)
+
+ assert f(first) != f(second)
+ assert not f(first) == f(second)
+
+ assert f(first) == int(f(first))
+ assert not f(first) != int(f(first))
+
+ assert f(first) != int(f(second))
+ assert not f(first) == int(f(second))
+
+ # noinspection PyDictCreation
+ x = {f(first): 1, f(second): 2}
+ x[f(first)] = 3
+ x[f(second)] = 4
+ # Hashing test
+ assert str(x) == "{EMode.EFirstMode: 3, EMode.ESecondMode: 4}"
+
+
+def test_binary_operators():
+ from pybind11_tests import Flags
+
+ assert int(Flags.Read) == 4
+ assert int(Flags.Write) == 2
+ assert int(Flags.Execute) == 1
+ assert int(Flags.Read | Flags.Write | Flags.Execute) == 7
+ assert int(Flags.Read | Flags.Write) == 6
+ assert int(Flags.Read | Flags.Execute) == 5
+ assert int(Flags.Write | Flags.Execute) == 3
+ assert int(Flags.Write | 1) == 3
+
+ state = Flags.Read | Flags.Write
+ assert (state & Flags.Read) != 0
+ assert (state & Flags.Write) != 0
+ assert (state & Flags.Execute) == 0
+ assert (state & 1) == 0
+
+ state2 = ~state
+ assert state2 == -7
+ assert int(state ^ state2) == -1
diff --git a/ext/pybind11/tests/test_eval.cpp b/ext/pybind11/tests/test_eval.cpp
new file mode 100644
index 000000000..ed4c226fe
--- /dev/null
+++ b/ext/pybind11/tests/test_eval.cpp
@@ -0,0 +1,79 @@
+/*
+ tests/test_eval.cpp -- Usage of eval() and eval_file()
+
+ Copyright (c) 2016 Klemens D. Morgenstern
+
+ All rights reserved. Use of this source code is governed by a
+ BSD-style license that can be found in the LICENSE file.
+*/
+
+
+#include <pybind11/eval.h>
+#include "pybind11_tests.h"
+
+test_initializer eval([](py::module &m) {
+ auto global = py::dict(py::module::import("__main__").attr("__dict__"));
+
+ m.def("test_eval_statements", [global]() {
+ auto local = py::dict();
+ local["call_test"] = py::cpp_function([&]() -> int {
+ return 42;
+ });
+
+ auto result = py::eval<py::eval_statements>(
+ "print('Hello World!');\n"
+ "x = call_test();",
+ global, local
+ );
+ auto x = local["x"].cast<int>();
+
+ return result == py::none() && x == 42;
+ });
+
+ m.def("test_eval", [global]() {
+ auto local = py::dict();
+ local["x"] = py::int_(42);
+ auto x = py::eval("x", global, local);
+ return x.cast<int>() == 42;
+ });
+
+ m.def("test_eval_single_statement", []() {
+ auto local = py::dict();
+ local["call_test"] = py::cpp_function([&]() -> int {
+ return 42;
+ });
+
+ auto result = py::eval<py::eval_single_statement>("x = call_test()", py::dict(), local);
+ auto x = local["x"].cast<int>();
+ return result == py::none() && x == 42;
+ });
+
+ m.def("test_eval_file", [global](py::str filename) {
+ auto local = py::dict();
+ local["y"] = py::int_(43);
+
+ int val_out;
+ local["call_test2"] = py::cpp_function([&](int value) { val_out = value; });
+
+ auto result = py::eval_file(filename, global, local);
+ return val_out == 43 && result == py::none();
+ });
+
+ m.def("test_eval_failure", []() {
+ try {
+ py::eval("nonsense code ...");
+ } catch (py::error_already_set &) {
+ return true;
+ }
+ return false;
+ });
+
+ m.def("test_eval_file_failure", []() {
+ try {
+ py::eval_file("non-existing file");
+ } catch (std::exception &) {
+ return true;
+ }
+ return false;
+ });
+});
diff --git a/ext/pybind11/tests/test_eval.py b/ext/pybind11/tests/test_eval.py
new file mode 100644
index 000000000..8715dbadb
--- /dev/null
+++ b/ext/pybind11/tests/test_eval.py
@@ -0,0 +1,19 @@
+import os
+
+
+def test_evals(capture):
+ from pybind11_tests import (test_eval_statements, test_eval, test_eval_single_statement,
+ test_eval_file, test_eval_failure, test_eval_file_failure)
+
+ with capture:
+ assert test_eval_statements()
+ assert capture == "Hello World!"
+
+ assert test_eval()
+ assert test_eval_single_statement()
+
+ filename = os.path.join(os.path.dirname(__file__), "test_eval_call.py")
+ assert test_eval_file(filename)
+
+ assert test_eval_failure()
+ assert test_eval_file_failure()
diff --git a/ext/pybind11/tests/test_eval_call.py b/ext/pybind11/tests/test_eval_call.py
new file mode 100644
index 000000000..53c7e721f
--- /dev/null
+++ b/ext/pybind11/tests/test_eval_call.py
@@ -0,0 +1,4 @@
+# This file is called from 'test_eval.py'
+
+if 'call_test2' in locals():
+ call_test2(y) # noqa: F821 undefined name
diff --git a/ext/pybind11/tests/test_exceptions.cpp b/ext/pybind11/tests/test_exceptions.cpp
new file mode 100644
index 000000000..706b500f2
--- /dev/null
+++ b/ext/pybind11/tests/test_exceptions.cpp
@@ -0,0 +1,173 @@
+/*
+ tests/test_custom-exceptions.cpp -- exception translation
+
+ Copyright (c) 2016 Pim Schellart <P.Schellart@princeton.edu>
+
+ All rights reserved. Use of this source code is governed by a
+ BSD-style license that can be found in the LICENSE file.
+*/
+
+#include "pybind11_tests.h"
+
+// A type that should be raised as an exeption in Python
+class MyException : public std::exception {
+public:
+ explicit MyException(const char * m) : message{m} {}
+ virtual const char * what() const noexcept override {return message.c_str();}
+private:
+ std::string message = "";
+};
+
+// A type that should be translated to a standard Python exception
+class MyException2 : public std::exception {
+public:
+ explicit MyException2(const char * m) : message{m} {}
+ virtual const char * what() const noexcept override {return message.c_str();}
+private:
+ std::string message = "";
+};
+
+// A type that is not derived from std::exception (and is thus unknown)
+class MyException3 {
+public:
+ explicit MyException3(const char * m) : message{m} {}
+ virtual const char * what() const noexcept {return message.c_str();}
+private:
+ std::string message = "";
+};
+
+// A type that should be translated to MyException
+// and delegated to its exception translator
+class MyException4 : public std::exception {
+public:
+ explicit MyException4(const char * m) : message{m} {}
+ virtual const char * what() const noexcept override {return message.c_str();}
+private:
+ std::string message = "";
+};
+
+
+// Like the above, but declared via the helper function
+class MyException5 : public std::logic_error {
+public:
+ explicit MyException5(const std::string &what) : std::logic_error(what) {}
+};
+
+// Inherits from MyException5
+class MyException5_1 : public MyException5 {
+ using MyException5::MyException5;
+};
+
+void throws1() {
+ throw MyException("this error should go to a custom type");
+}
+
+void throws2() {
+ throw MyException2("this error should go to a standard Python exception");
+}
+
+void throws3() {
+ throw MyException3("this error cannot be translated");
+}
+
+void throws4() {
+ throw MyException4("this error is rethrown");
+}
+
+void throws5() {
+ throw MyException5("this is a helper-defined translated exception");
+}
+
+void throws5_1() {
+ throw MyException5_1("MyException5 subclass");
+}
+
+void throws_logic_error() {
+ throw std::logic_error("this error should fall through to the standard handler");
+}
+
+struct PythonCallInDestructor {
+ PythonCallInDestructor(const py::dict &d) : d(d) {}
+ ~PythonCallInDestructor() { d["good"] = true; }
+
+ py::dict d;
+};
+
+test_initializer custom_exceptions([](py::module &m) {
+ // make a new custom exception and use it as a translation target
+ static py::exception<MyException> ex(m, "MyException");
+ py::register_exception_translator([](std::exception_ptr p) {
+ try {
+ if (p) std::rethrow_exception(p);
+ } catch (const MyException &e) {
+ // Set MyException as the active python error
+ ex(e.what());
+ }
+ });
+
+ // register new translator for MyException2
+ // no need to store anything here because this type will
+ // never by visible from Python
+ py::register_exception_translator([](std::exception_ptr p) {
+ try {
+ if (p) std::rethrow_exception(p);
+ } catch (const MyException2 &e) {
+ // Translate this exception to a standard RuntimeError
+ PyErr_SetString(PyExc_RuntimeError, e.what());
+ }
+ });
+
+ // register new translator for MyException4
+ // which will catch it and delegate to the previously registered
+ // translator for MyException by throwing a new exception
+ py::register_exception_translator([](std::exception_ptr p) {
+ try {
+ if (p) std::rethrow_exception(p);
+ } catch (const MyException4 &e) {
+ throw MyException(e.what());
+ }
+ });
+
+ // A simple exception translation:
+ auto ex5 = py::register_exception<MyException5>(m, "MyException5");
+ // A slightly more complicated one that declares MyException5_1 as a subclass of MyException5
+ py::register_exception<MyException5_1>(m, "MyException5_1", ex5.ptr());
+
+ m.def("throws1", &throws1);
+ m.def("throws2", &throws2);
+ m.def("throws3", &throws3);
+ m.def("throws4", &throws4);
+ m.def("throws5", &throws5);
+ m.def("throws5_1", &throws5_1);
+ m.def("throws_logic_error", &throws_logic_error);
+
+ m.def("throw_already_set", [](bool err) {
+ if (err)
+ PyErr_SetString(PyExc_ValueError, "foo");
+ try {
+ throw py::error_already_set();
+ } catch (const std::runtime_error& e) {
+ if ((err && e.what() != std::string("ValueError: foo")) ||
+ (!err && e.what() != std::string("Unknown internal error occurred")))
+ {
+ PyErr_Clear();
+ throw std::runtime_error("error message mismatch");
+ }
+ }
+ PyErr_Clear();
+ if (err)
+ PyErr_SetString(PyExc_ValueError, "foo");
+ throw py::error_already_set();
+ });
+
+ m.def("python_call_in_destructor", [](py::dict d) {
+ try {
+ PythonCallInDestructor set_dict_in_destructor(d);
+ PyErr_SetString(PyExc_ValueError, "foo");
+ throw py::error_already_set();
+ } catch (const py::error_already_set&) {
+ return true;
+ }
+ return false;
+ });
+});
diff --git a/ext/pybind11/tests/test_exceptions.py b/ext/pybind11/tests/test_exceptions.py
new file mode 100644
index 000000000..0025e4eb6
--- /dev/null
+++ b/ext/pybind11/tests/test_exceptions.py
@@ -0,0 +1,74 @@
+import pytest
+
+
+def test_error_already_set(msg):
+ from pybind11_tests import throw_already_set
+
+ with pytest.raises(RuntimeError) as excinfo:
+ throw_already_set(False)
+ assert msg(excinfo.value) == "Unknown internal error occurred"
+
+ with pytest.raises(ValueError) as excinfo:
+ throw_already_set(True)
+ assert msg(excinfo.value) == "foo"
+
+
+def test_python_call_in_catch():
+ from pybind11_tests import python_call_in_destructor
+
+ d = {}
+ assert python_call_in_destructor(d) is True
+ assert d["good"] is True
+
+
+def test_custom(msg):
+ from pybind11_tests import (MyException, MyException5, MyException5_1,
+ throws1, throws2, throws3, throws4, throws5, throws5_1,
+ throws_logic_error)
+
+ # Can we catch a MyException?"
+ with pytest.raises(MyException) as excinfo:
+ throws1()
+ assert msg(excinfo.value) == "this error should go to a custom type"
+
+ # Can we translate to standard Python exceptions?
+ with pytest.raises(RuntimeError) as excinfo:
+ throws2()
+ assert msg(excinfo.value) == "this error should go to a standard Python exception"
+
+ # Can we handle unknown exceptions?
+ with pytest.raises(RuntimeError) as excinfo:
+ throws3()
+ assert msg(excinfo.value) == "Caught an unknown exception!"
+
+ # Can we delegate to another handler by rethrowing?
+ with pytest.raises(MyException) as excinfo:
+ throws4()
+ assert msg(excinfo.value) == "this error is rethrown"
+
+ # "Can we fall-through to the default handler?"
+ with pytest.raises(RuntimeError) as excinfo:
+ throws_logic_error()
+ assert msg(excinfo.value) == "this error should fall through to the standard handler"
+
+ # Can we handle a helper-declared exception?
+ with pytest.raises(MyException5) as excinfo:
+ throws5()
+ assert msg(excinfo.value) == "this is a helper-defined translated exception"
+
+ # Exception subclassing:
+ with pytest.raises(MyException5) as excinfo:
+ throws5_1()
+ assert msg(excinfo.value) == "MyException5 subclass"
+ assert isinstance(excinfo.value, MyException5_1)
+
+ with pytest.raises(MyException5_1) as excinfo:
+ throws5_1()
+ assert msg(excinfo.value) == "MyException5 subclass"
+
+ with pytest.raises(MyException5) as excinfo:
+ try:
+ throws5()
+ except MyException5_1:
+ raise RuntimeError("Exception error: caught child from parent")
+ assert msg(excinfo.value) == "this is a helper-defined translated exception"
diff --git a/ext/pybind11/tests/test_inheritance.cpp b/ext/pybind11/tests/test_inheritance.cpp
new file mode 100644
index 000000000..2ec0b4a7a
--- /dev/null
+++ b/ext/pybind11/tests/test_inheritance.cpp
@@ -0,0 +1,100 @@
+/*
+ tests/test_inheritance.cpp -- inheritance, automatic upcasting for polymorphic types
+
+ Copyright (c) 2016 Wenzel Jakob <wenzel.jakob@epfl.ch>
+
+ All rights reserved. Use of this source code is governed by a
+ BSD-style license that can be found in the LICENSE file.
+*/
+
+#include "pybind11_tests.h"
+
+class Pet {
+public:
+ Pet(const std::string &name, const std::string &species)
+ : m_name(name), m_species(species) {}
+ std::string name() const { return m_name; }
+ std::string species() const { return m_species; }
+private:
+ std::string m_name;
+ std::string m_species;
+};
+
+class Dog : public Pet {
+public:
+ Dog(const std::string &name) : Pet(name, "dog") {}
+ std::string bark() const { return "Woof!"; }
+};
+
+class Rabbit : public Pet {
+public:
+ Rabbit(const std::string &name) : Pet(name, "parrot") {}
+};
+
+class Hamster : public Pet {
+public:
+ Hamster(const std::string &name) : Pet(name, "rodent") {}
+};
+
+std::string pet_name_species(const Pet &pet) {
+ return pet.name() + " is a " + pet.species();
+}
+
+std::string dog_bark(const Dog &dog) {
+ return dog.bark();
+}
+
+
+struct BaseClass { virtual ~BaseClass() {} };
+struct DerivedClass1 : BaseClass { };
+struct DerivedClass2 : BaseClass { };
+
+test_initializer inheritance([](py::module &m) {
+ py::class_<Pet> pet_class(m, "Pet");
+ pet_class
+ .def(py::init<std::string, std::string>())
+ .def("name", &Pet::name)
+ .def("species", &Pet::species);
+
+ /* One way of declaring a subclass relationship: reference parent's class_ object */
+ py::class_<Dog>(m, "Dog", pet_class)
+ .def(py::init<std::string>());
+
+ /* Another way of declaring a subclass relationship: reference parent's C++ type */
+ py::class_<Rabbit, Pet>(m, "Rabbit")
+ .def(py::init<std::string>());
+
+ /* And another: list parent in class template arguments */
+ py::class_<Hamster, Pet>(m, "Hamster")
+ .def(py::init<std::string>());
+
+ m.def("pet_name_species", pet_name_species);
+ m.def("dog_bark", dog_bark);
+
+ py::class_<BaseClass>(m, "BaseClass").def(py::init<>());
+ py::class_<DerivedClass1>(m, "DerivedClass1").def(py::init<>());
+ py::class_<DerivedClass2>(m, "DerivedClass2").def(py::init<>());
+
+ m.def("return_class_1", []() -> BaseClass* { return new DerivedClass1(); });
+ m.def("return_class_2", []() -> BaseClass* { return new DerivedClass2(); });
+ m.def("return_class_n", [](int n) -> BaseClass* {
+ if (n == 1) return new DerivedClass1();
+ if (n == 2) return new DerivedClass2();
+ return new BaseClass();
+ });
+ m.def("return_none", []() -> BaseClass* { return nullptr; });
+
+ m.def("test_isinstance", [](py::list l) {
+ struct Unregistered { }; // checks missing type_info code path
+
+ return py::make_tuple(
+ py::isinstance<py::tuple>(l[0]),
+ py::isinstance<py::dict>(l[1]),
+ py::isinstance<Pet>(l[2]),
+ py::isinstance<Pet>(l[3]),
+ py::isinstance<Dog>(l[4]),
+ py::isinstance<Rabbit>(l[5]),
+ py::isinstance<Unregistered>(l[6])
+ );
+ });
+});
diff --git a/ext/pybind11/tests/test_inheritance.py b/ext/pybind11/tests/test_inheritance.py
new file mode 100644
index 000000000..7bb52be02
--- /dev/null
+++ b/ext/pybind11/tests/test_inheritance.py
@@ -0,0 +1,55 @@
+import pytest
+
+
+def test_inheritance(msg):
+ from pybind11_tests import Pet, Dog, Rabbit, Hamster, dog_bark, pet_name_species
+
+ roger = Rabbit('Rabbit')
+ assert roger.name() + " is a " + roger.species() == "Rabbit is a parrot"
+ assert pet_name_species(roger) == "Rabbit is a parrot"
+
+ polly = Pet('Polly', 'parrot')
+ assert polly.name() + " is a " + polly.species() == "Polly is a parrot"
+ assert pet_name_species(polly) == "Polly is a parrot"
+
+ molly = Dog('Molly')
+ assert molly.name() + " is a " + molly.species() == "Molly is a dog"
+ assert pet_name_species(molly) == "Molly is a dog"
+
+ fred = Hamster('Fred')
+ assert fred.name() + " is a " + fred.species() == "Fred is a rodent"
+
+ assert dog_bark(molly) == "Woof!"
+
+ with pytest.raises(TypeError) as excinfo:
+ dog_bark(polly)
+ assert msg(excinfo.value) == """
+ dog_bark(): incompatible function arguments. The following argument types are supported:
+ 1. (arg0: m.Dog) -> str
+
+ Invoked with: <m.Pet object at 0>
+ """
+
+
+def test_automatic_upcasting():
+ from pybind11_tests import return_class_1, return_class_2, return_class_n, return_none
+
+ 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
+ assert type(return_class_n(1)).__name__ == "DerivedClass1"
+ assert type(return_class_n(2)).__name__ == "DerivedClass2"
+ assert type(return_class_n(0)).__name__ == "BaseClass"
+ assert type(return_class_n(2)).__name__ == "DerivedClass2"
+ assert type(return_class_n(2)).__name__ == "DerivedClass2"
+ assert type(return_class_n(0)).__name__ == "BaseClass"
+ assert type(return_class_n(1)).__name__ == "DerivedClass1"
+
+
+def test_isinstance():
+ from pybind11_tests import test_isinstance, Pet, Dog
+
+ objects = [tuple(), dict(), Pet("Polly", "parrot")] + [Dog("Molly")] * 4
+ expected = (True, True, True, True, True, False, False)
+ assert test_isinstance(objects) == expected
diff --git a/ext/pybind11/tests/test_installed_module/CMakeLists.txt b/ext/pybind11/tests/test_installed_module/CMakeLists.txt
new file mode 100644
index 000000000..77fd49dc2
--- /dev/null
+++ b/ext/pybind11/tests/test_installed_module/CMakeLists.txt
@@ -0,0 +1,14 @@
+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
new file mode 100644
index 000000000..a0bda4542
--- /dev/null
+++ b/ext/pybind11/tests/test_installed_module/main.cpp
@@ -0,0 +1,10 @@
+#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
new file mode 100644
index 000000000..2f0632049
--- /dev/null
+++ b/ext/pybind11/tests/test_installed_module/test.py
@@ -0,0 +1,3 @@
+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
new file mode 100644
index 000000000..4333dc107
--- /dev/null
+++ b/ext/pybind11/tests/test_installed_target/CMakeLists.txt
@@ -0,0 +1,20 @@
+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/main.cpp b/ext/pybind11/tests/test_installed_target/main.cpp
new file mode 100644
index 000000000..2a84c11ce
--- /dev/null
+++ b/ext/pybind11/tests/test_installed_target/main.cpp
@@ -0,0 +1,10 @@
+#include <pybind11/pybind11.h>
+namespace py = pybind11;
+
+PYBIND11_PLUGIN(test_installed_target) {
+ py::module m("test_installed_target");
+
+ m.def("add", [](int i, int j) { return i + j; });
+
+ return m.ptr();
+}
diff --git a/ext/pybind11/tests/test_installed_target/test.py b/ext/pybind11/tests/test_installed_target/test.py
new file mode 100644
index 000000000..b2888a72b
--- /dev/null
+++ b/ext/pybind11/tests/test_installed_target/test.py
@@ -0,0 +1,3 @@
+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
new file mode 100644
index 000000000..4c59a1b12
--- /dev/null
+++ b/ext/pybind11/tests/test_issues.cpp
@@ -0,0 +1,401 @@
+/*
+ tests/test_issues.cpp -- collection of testcases for miscellaneous issues
+
+ Copyright (c) 2016 Wenzel Jakob <wenzel.jakob@epfl.ch>
+
+ All rights reserved. Use of this source code is governed by a
+ BSD-style license that can be found in the LICENSE file.
+*/
+
+#include "pybind11_tests.h"
+#include "constructor_stats.h"
+#include <pybind11/stl.h>
+#include <pybind11/operators.h>
+#include <pybind11/complex.h>
+
+#define TRACKERS(CLASS) CLASS() { print_default_created(this); } ~CLASS() { print_destroyed(this); }
+struct NestABase { int value = -2; TRACKERS(NestABase) };
+struct NestA : NestABase { int value = 3; NestA& operator+=(int i) { value += i; return *this; } TRACKERS(NestA) };
+struct NestB { NestA a; int value = 4; NestB& operator-=(int i) { value -= i; return *this; } TRACKERS(NestB) };
+struct NestC { NestB b; int value = 5; NestC& operator*=(int i) { value *= i; return *this; } TRACKERS(NestC) };
+
+/// #393
+class OpTest1 {};
+class OpTest2 {};
+
+OpTest1 operator+(const OpTest1 &, const OpTest1 &) {
+ py::print("Add OpTest1 with OpTest1");
+ return OpTest1();
+}
+OpTest2 operator+(const OpTest2 &, const OpTest2 &) {
+ py::print("Add OpTest2 with OpTest2");
+ return OpTest2();
+}
+OpTest2 operator+(const OpTest2 &, const OpTest1 &) {
+ py::print("Add OpTest2 with OpTest1");
+ return OpTest2();
+}
+
+// #461
+class Dupe1 {
+public:
+ Dupe1(int v) : v_{v} {}
+ int get_value() const { return v_; }
+private:
+ int v_;
+};
+class Dupe2 {};
+class Dupe3 {};
+class DupeException : public std::runtime_error {};
+
+// #478
+template <typename T> class custom_unique_ptr {
+public:
+ custom_unique_ptr() { print_default_created(this); }
+ custom_unique_ptr(T *ptr) : _ptr{ptr} { print_created(this, ptr); }
+ custom_unique_ptr(custom_unique_ptr<T> &&move) : _ptr{move._ptr} { move._ptr = nullptr; print_move_created(this); }
+ custom_unique_ptr &operator=(custom_unique_ptr<T> &&move) { print_move_assigned(this); if (_ptr) destruct_ptr(); _ptr = move._ptr; move._ptr = nullptr; return *this; }
+ custom_unique_ptr(const custom_unique_ptr<T> &) = delete;
+ void operator=(const custom_unique_ptr<T> &copy) = delete;
+ ~custom_unique_ptr() { print_destroyed(this); if (_ptr) destruct_ptr(); }
+private:
+ T *_ptr = nullptr;
+ void destruct_ptr() { delete _ptr; }
+};
+PYBIND11_DECLARE_HOLDER_TYPE(T, custom_unique_ptr<T>);
+
+/// Issue #528: templated constructor
+struct TplConstrClass {
+ template <typename T> TplConstrClass(const T &arg) : str{arg} {}
+ std::string str;
+ bool operator==(const TplConstrClass &t) const { return t.str == str; }
+};
+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");
+
+#if !defined(_MSC_VER)
+ // Visual Studio 2015 currently cannot compile this test
+ // (see the comment in type_caster_base::make_copy_constructor)
+ // #70 compilation issue if operator new is not public
+ class NonConstructible { private: void *operator new(size_t bytes) throw(); };
+ py::class_<NonConstructible>(m, "Foo");
+ m2.def("getstmt", []() -> NonConstructible * { return nullptr; },
+ py::return_value_policy::reference);
+#endif
+
+ // #137: const char* isn't handled properly
+ m2.def("print_cchar", [](const char *s) { return std::string(s); });
+
+ // #150: char bindings broken
+ m2.def("print_char", [](char c) { return std::string(1, c); });
+
+ // #159: virtual function dispatch has problems with similar-named functions
+ struct Base { virtual std::string dispatch() const {
+ /* for some reason MSVC2015 can't compile this if the function is pure virtual */
+ return {};
+ }; };
+
+ struct DispatchIssue : Base {
+ virtual std::string dispatch() const {
+ PYBIND11_OVERLOAD_PURE(std::string, Base, dispatch, /* no arguments */);
+ }
+ };
+
+ py::class_<Base, DispatchIssue>(m2, "DispatchIssue")
+ .def(py::init<>())
+ .def("dispatch", &Base::dispatch);
+
+ m2.def("dispatch_issue_go", [](const Base * b) { return b->dispatch(); });
+
+ struct Placeholder { int i; Placeholder(int i) : i(i) { } };
+
+ py::class_<Placeholder>(m2, "Placeholder")
+ .def(py::init<int>())
+ .def("__repr__", [](const Placeholder &p) { return "Placeholder[" + std::to_string(p.i) + "]"; });
+
+ // #171: Can't return reference wrappers (or STL datastructures containing them)
+ m2.def("return_vec_of_reference_wrapper", [](std::reference_wrapper<Placeholder> p4) {
+ Placeholder *p1 = new Placeholder{1};
+ Placeholder *p2 = new Placeholder{2};
+ Placeholder *p3 = new Placeholder{3};
+ std::vector<std::reference_wrapper<Placeholder>> v;
+ v.push_back(std::ref(*p1));
+ v.push_back(std::ref(*p2));
+ v.push_back(std::ref(*p3));
+ v.push_back(p4);
+ return v;
+ });
+
+ // #181: iterator passthrough did not compile
+ m2.def("iterator_passthrough", [](py::iterator s) -> py::iterator {
+ return py::make_iterator(std::begin(s), std::end(s));
+ });
+
+ // #187: issue involving std::shared_ptr<> return value policy & garbage collection
+ struct ElementBase { virtual void foo() { } /* Force creation of virtual table */ };
+ struct ElementA : ElementBase {
+ ElementA(int v) : v(v) { }
+ int value() { return v; }
+ int v;
+ };
+
+ struct ElementList {
+ void add(std::shared_ptr<ElementBase> e) { l.push_back(e); }
+ std::vector<std::shared_ptr<ElementBase>> l;
+ };
+
+ py::class_<ElementBase, std::shared_ptr<ElementBase>> (m2, "ElementBase");
+
+ py::class_<ElementA, ElementBase, std::shared_ptr<ElementA>>(m2, "ElementA")
+ .def(py::init<int>())
+ .def("value", &ElementA::value);
+
+ py::class_<ElementList, std::shared_ptr<ElementList>>(m2, "ElementList")
+ .def(py::init<>())
+ .def("add", &ElementList::add)
+ .def("get", [](ElementList &el) {
+ py::list list;
+ for (auto &e : el.l)
+ list.append(py::cast(e));
+ return list;
+ });
+
+ // (no id): should not be able to pass 'None' to a reference argument
+ m2.def("get_element", [](ElementA &el) { return el.value(); });
+
+ // (no id): don't cast doubles to ints
+ m2.def("expect_float", [](float f) { return f; });
+ m2.def("expect_int", [](int i) { return i; });
+
+ try {
+ py::class_<Placeholder>(m2, "Placeholder");
+ throw std::logic_error("Expected an exception!");
+ } catch (std::runtime_error &) {
+ /* All good */
+ }
+
+ // Issue #283: __str__ called on uninitialized instance when constructor arguments invalid
+ class StrIssue {
+ public:
+ StrIssue(int i) : val{i} {}
+ StrIssue() : StrIssue(-1) {}
+ int value() const { return val; }
+ private:
+ int val;
+ };
+ py::class_<StrIssue> si(m2, "StrIssue");
+ si .def(py::init<int>())
+ .def(py::init<>())
+ .def("__str__", [](const StrIssue &si) { return "StrIssue[" + std::to_string(si.value()) + "]"; })
+ ;
+
+ // Issue #328: first member in a class can't be used in operators
+ py::class_<NestABase>(m2, "NestABase").def(py::init<>()).def_readwrite("value", &NestABase::value);
+ py::class_<NestA>(m2, "NestA").def(py::init<>()).def(py::self += int())
+ .def("as_base", [](NestA &a) -> NestABase& { return (NestABase&) a; }, py::return_value_policy::reference_internal);
+ py::class_<NestB>(m2, "NestB").def(py::init<>()).def(py::self -= int()).def_readwrite("a", &NestB::a);
+ py::class_<NestC>(m2, "NestC").def(py::init<>()).def(py::self *= int()).def_readwrite("b", &NestC::b);
+ m2.def("get_NestA", [](const NestA &a) { return a.value; });
+ m2.def("get_NestB", [](const NestB &b) { return b.value; });
+ m2.def("get_NestC", [](const NestC &c) { return c.value; });
+
+ // Issue 389: r_v_p::move should fall-through to copy on non-movable objects
+ class MoveIssue1 {
+ public:
+ MoveIssue1(int v) : v{v} {}
+ MoveIssue1(const MoveIssue1 &c) { v = c.v; }
+ MoveIssue1(MoveIssue1 &&) = delete;
+ int v;
+ };
+ class MoveIssue2 {
+ public:
+ MoveIssue2(int v) : v{v} {}
+ MoveIssue2(MoveIssue2 &&) = default;
+ int v;
+ };
+ py::class_<MoveIssue1>(m2, "MoveIssue1").def(py::init<int>()).def_readwrite("value", &MoveIssue1::v);
+ py::class_<MoveIssue2>(m2, "MoveIssue2").def(py::init<int>()).def_readwrite("value", &MoveIssue2::v);
+ m2.def("get_moveissue1", [](int i) -> MoveIssue1 * { return new MoveIssue1(i); }, py::return_value_policy::move);
+ m2.def("get_moveissue2", [](int i) { return MoveIssue2(i); }, py::return_value_policy::move);
+
+ // Issues 392/397: overridding reference-returning functions
+ class OverrideTest {
+ public:
+ struct A { std::string value = "hi"; };
+ std::string v;
+ A a;
+ explicit OverrideTest(const std::string &v) : v{v} {}
+ virtual std::string str_value() { return v; }
+ virtual std::string &str_ref() { return v; }
+ virtual A A_value() { return a; }
+ virtual A &A_ref() { return a; }
+ };
+ class PyOverrideTest : public OverrideTest {
+ public:
+ using OverrideTest::OverrideTest;
+ std::string str_value() override { PYBIND11_OVERLOAD(std::string, OverrideTest, str_value); }
+ // Not allowed (uncommenting should hit a static_assert failure): we can't get a reference
+ // to a python numeric value, since we only copy values in the numeric type caster:
+// std::string &str_ref() override { PYBIND11_OVERLOAD(std::string &, OverrideTest, str_ref); }
+ // But we can work around it like this:
+ private:
+ std::string _tmp;
+ std::string str_ref_helper() { PYBIND11_OVERLOAD(std::string, OverrideTest, str_ref); }
+ public:
+ std::string &str_ref() override { return _tmp = str_ref_helper(); }
+
+ A A_value() override { PYBIND11_OVERLOAD(A, OverrideTest, A_value); }
+ A &A_ref() override { PYBIND11_OVERLOAD(A &, OverrideTest, A_ref); }
+ };
+ py::class_<OverrideTest::A>(m2, "OverrideTest_A")
+ .def_readwrite("value", &OverrideTest::A::value);
+ py::class_<OverrideTest, PyOverrideTest>(m2, "OverrideTest")
+ .def(py::init<const std::string &>())
+ .def("str_value", &OverrideTest::str_value)
+// .def("str_ref", &OverrideTest::str_ref)
+ .def("A_value", &OverrideTest::A_value)
+ .def("A_ref", &OverrideTest::A_ref);
+
+ /// Issue 393: need to return NotSupported to ensure correct arithmetic operator behavior
+ py::class_<OpTest1>(m2, "OpTest1")
+ .def(py::init<>())
+ .def(py::self + py::self);
+
+ py::class_<OpTest2>(m2, "OpTest2")
+ .def(py::init<>())
+ .def(py::self + py::self)
+ .def("__add__", [](const OpTest2& c2, const OpTest1& c1) { return c2 + c1; })
+ .def("__radd__", [](const OpTest2& c2, const OpTest1& c1) { return c2 + c1; });
+
+ // Issue 388: Can't make iterators via make_iterator() with different r/v policies
+ static std::vector<int> list = { 1, 2, 3 };
+ m2.def("make_iterator_1", []() { return py::make_iterator<py::return_value_policy::copy>(list); });
+ m2.def("make_iterator_2", []() { return py::make_iterator<py::return_value_policy::automatic>(list); });
+
+ static std::vector<std::string> nothrows;
+ // Issue 461: registering two things with the same name:
+ py::class_<Dupe1>(m2, "Dupe1")
+ .def("get_value", &Dupe1::get_value)
+ ;
+ m2.def("dupe1_factory", [](int v) { return new Dupe1(v); });
+
+ py::class_<Dupe2>(m2, "Dupe2");
+ py::exception<DupeException>(m2, "DupeException");
+
+ try {
+ m2.def("Dupe1", [](int v) { return new Dupe1(v); });
+ nothrows.emplace_back("Dupe1");
+ }
+ catch (std::runtime_error &) {}
+ try {
+ py::class_<Dupe3>(m2, "dupe1_factory");
+ nothrows.emplace_back("dupe1_factory");
+ }
+ catch (std::runtime_error &) {}
+ try {
+ py::exception<Dupe3>(m2, "Dupe2");
+ nothrows.emplace_back("Dupe2");
+ }
+ catch (std::runtime_error &) {}
+ try {
+ m2.def("DupeException", []() { return 30; });
+ nothrows.emplace_back("DupeException1");
+ }
+ catch (std::runtime_error &) {}
+ try {
+ py::class_<DupeException>(m2, "DupeException");
+ nothrows.emplace_back("DupeException2");
+ }
+ catch (std::runtime_error &) {}
+ m2.def("dupe_exception_failures", []() {
+ py::list l;
+ for (auto &e : nothrows) l.append(py::cast(e));
+ return l;
+ });
+
+ /// Issue #471: shared pointer instance not dellocated
+ class SharedChild : public std::enable_shared_from_this<SharedChild> {
+ public:
+ SharedChild() { print_created(this); }
+ ~SharedChild() { print_destroyed(this); }
+ };
+
+ class SharedParent {
+ public:
+ SharedParent() : child(std::make_shared<SharedChild>()) { }
+ const SharedChild &get_child() const { return *child; }
+
+ private:
+ std::shared_ptr<SharedChild> child;
+ };
+
+ py::class_<SharedChild, std::shared_ptr<SharedChild>>(m, "SharedChild");
+ py::class_<SharedParent, std::shared_ptr<SharedParent>>(m, "SharedParent")
+ .def(py::init<>())
+ .def("get_child", &SharedParent::get_child, py::return_value_policy::reference);
+
+ /// Issue/PR #478: unique ptrs constructed and freed without destruction
+ class SpecialHolderObj {
+ public:
+ int val = 0;
+ SpecialHolderObj *ch = nullptr;
+ SpecialHolderObj(int v, bool make_child = true) : val{v}, ch{make_child ? new SpecialHolderObj(val+1, false) : nullptr}
+ { print_created(this, val); }
+ ~SpecialHolderObj() { delete ch; print_destroyed(this); }
+ SpecialHolderObj *child() { return ch; }
+ };
+
+ py::class_<SpecialHolderObj, custom_unique_ptr<SpecialHolderObj>>(m, "SpecialHolderObj")
+ .def(py::init<int>())
+ .def("child", &SpecialHolderObj::child, pybind11::return_value_policy::reference_internal)
+ .def_readwrite("val", &SpecialHolderObj::val)
+ .def_static("holder_cstats", &ConstructorStats::get<custom_unique_ptr<SpecialHolderObj>>,
+ py::return_value_policy::reference);
+
+ /// Issue #484: number conversion generates unhandled exceptions
+ m2.def("test_complex", [](float x) { py::print("{}"_s.format(x)); });
+ m2.def("test_complex", [](std::complex<float> x) { py::print("({}, {})"_s.format(x.real(), x.imag())); });
+
+ /// Issue #511: problem with inheritance + overwritten def_static
+ struct MyBase {
+ static std::unique_ptr<MyBase> make() {
+ return std::unique_ptr<MyBase>(new MyBase());
+ }
+ };
+
+ struct MyDerived : MyBase {
+ static std::unique_ptr<MyDerived> make() {
+ return std::unique_ptr<MyDerived>(new MyDerived());
+ }
+ };
+
+ py::class_<MyBase>(m2, "MyBase")
+ .def_static("make", &MyBase::make);
+
+ py::class_<MyDerived, MyBase>(m2, "MyDerived")
+ .def_static("make", &MyDerived::make)
+ .def_static("make2", &MyDerived::make);
+
+ py::dict d;
+ std::string bar = "bar";
+ d["str"] = bar;
+ d["num"] = 3.7;
+
+ /// Issue #528: templated constructor
+ m2.def("tpl_constr_vector", [](std::vector<TplConstrClass> &) {});
+ m2.def("tpl_constr_map", [](std::unordered_map<TplConstrClass, TplConstrClass> &) {});
+ m2.def("tpl_constr_set", [](std::unordered_set<TplConstrClass> &) {});
+#if defined(PYBIND11_HAS_OPTIONAL)
+ m2.def("tpl_constr_optional", [](std::optional<TplConstrClass> &) {});
+#elif defined(PYBIND11_HAS_EXP_OPTIONAL)
+ m2.def("tpl_constr_optional", [](std::experimental::optional<TplConstrClass> &) {});
+#endif
+}
+
+// MSVC workaround: trying to use a lambda here crashes MSCV
+test_initializer issues(&init_issues);
diff --git a/ext/pybind11/tests/test_issues.py b/ext/pybind11/tests/test_issues.py
new file mode 100644
index 000000000..2098ff8a3
--- /dev/null
+++ b/ext/pybind11/tests/test_issues.py
@@ -0,0 +1,252 @@
+import pytest
+import gc
+from pybind11_tests import ConstructorStats
+
+
+def test_regressions():
+ from pybind11_tests.issues import print_cchar, print_char
+
+ # #137: const char* isn't handled properly
+ assert print_cchar("const char *") == "const char *"
+ # #150: char bindings broken
+ assert print_char("c") == "c"
+
+
+def test_dispatch_issue(msg):
+ """#159: virtual function dispatch has problems with similar-named functions"""
+ from pybind11_tests.issues import DispatchIssue, dispatch_issue_go
+
+ class PyClass1(DispatchIssue):
+ def dispatch(self):
+ return "Yay.."
+
+ class PyClass2(DispatchIssue):
+ def dispatch(self):
+ with pytest.raises(RuntimeError) as excinfo:
+ super(PyClass2, self).dispatch()
+ assert msg(excinfo.value) == 'Tried to call pure virtual function "Base::dispatch"'
+
+ p = PyClass1()
+ return dispatch_issue_go(p)
+
+ b = PyClass2()
+ assert dispatch_issue_go(b) == "Yay.."
+
+
+def test_reference_wrapper():
+ """#171: Can't return reference wrappers (or STL data structures containing them)"""
+ from pybind11_tests.issues import Placeholder, return_vec_of_reference_wrapper
+
+ assert str(return_vec_of_reference_wrapper(Placeholder(4))) == \
+ "[Placeholder[1], Placeholder[2], Placeholder[3], Placeholder[4]]"
+
+
+def test_iterator_passthrough():
+ """#181: iterator passthrough did not compile"""
+ from pybind11_tests.issues import iterator_passthrough
+
+ assert list(iterator_passthrough(iter([3, 5, 7, 9, 11, 13, 15]))) == [3, 5, 7, 9, 11, 13, 15]
+
+
+def test_shared_ptr_gc():
+ """// #187: issue involving std::shared_ptr<> return value policy & garbage collection"""
+ from pybind11_tests.issues import ElementList, ElementA
+
+ el = ElementList()
+ for i in range(10):
+ el.add(ElementA(i))
+ gc.collect()
+ for i, v in enumerate(el.get()):
+ assert i == v.value()
+
+
+def test_no_id(msg):
+ from pybind11_tests.issues import get_element, expect_float, expect_int
+
+ with pytest.raises(TypeError) as excinfo:
+ get_element(None)
+ assert msg(excinfo.value) == """
+ get_element(): incompatible function arguments. The following argument types are supported:
+ 1. (arg0: m.issues.ElementA) -> int
+
+ Invoked with: None
+ """
+
+ with pytest.raises(TypeError) as excinfo:
+ expect_int(5.2)
+ assert msg(excinfo.value) == """
+ expect_int(): incompatible function arguments. The following argument types are supported:
+ 1. (arg0: int) -> int
+
+ Invoked with: 5.2
+ """
+ assert expect_float(12) == 12
+
+
+def test_str_issue(msg):
+ """Issue #283: __str__ called on uninitialized instance when constructor arguments invalid"""
+ from pybind11_tests.issues import StrIssue
+
+ assert str(StrIssue(3)) == "StrIssue[3]"
+
+ with pytest.raises(TypeError) as excinfo:
+ str(StrIssue("no", "such", "constructor"))
+ assert msg(excinfo.value) == """
+ __init__(): incompatible constructor arguments. The following argument types are supported:
+ 1. m.issues.StrIssue(arg0: int)
+ 2. m.issues.StrIssue()
+
+ Invoked with: 'no', 'such', 'constructor'
+ """
+
+
+def test_nested():
+ """ #328: first member in a class can't be used in operators"""
+ from pybind11_tests.issues import NestA, NestB, NestC, get_NestA, get_NestB, get_NestC
+
+ a = NestA()
+ b = NestB()
+ c = NestC()
+
+ a += 10
+ assert get_NestA(a) == 13
+ b.a += 100
+ assert get_NestA(b.a) == 103
+ c.b.a += 1000
+ assert get_NestA(c.b.a) == 1003
+ b -= 1
+ assert get_NestB(b) == 3
+ c.b -= 3
+ assert get_NestB(c.b) == 1
+ c *= 7
+ assert get_NestC(c) == 35
+
+ abase = a.as_base()
+ assert abase.value == -2
+ a.as_base().value += 44
+ assert abase.value == 42
+ assert c.b.a.as_base().value == -2
+ c.b.a.as_base().value += 44
+ assert c.b.a.as_base().value == 42
+
+ del c
+ gc.collect()
+ del a # Should't delete while abase is still alive
+ gc.collect()
+
+ assert abase.value == 42
+ del abase, b
+ gc.collect()
+
+
+def test_move_fallback():
+ from pybind11_tests.issues import get_moveissue1, get_moveissue2
+ m2 = get_moveissue2(2)
+ assert m2.value == 2
+ m1 = get_moveissue1(1)
+ assert m1.value == 1
+
+
+def test_override_ref():
+ from pybind11_tests.issues import OverrideTest
+ o = OverrideTest("asdf")
+
+ # Not allowed (see associated .cpp comment)
+ # i = o.str_ref()
+ # assert o.str_ref() == "asdf"
+ assert o.str_value() == "asdf"
+
+ assert o.A_value().value == "hi"
+ a = o.A_ref()
+ assert a.value == "hi"
+ a.value = "bye"
+ assert a.value == "bye"
+
+
+def test_operators_notimplemented(capture):
+ from pybind11_tests.issues import OpTest1, OpTest2
+ with capture:
+ c1, c2 = OpTest1(), OpTest2()
+ c1 + c1
+ c2 + c2
+ c2 + c1
+ c1 + c2
+ assert capture == """
+ Add OpTest1 with OpTest1
+ Add OpTest2 with OpTest2
+ Add OpTest2 with OpTest1
+ Add OpTest2 with OpTest1
+ """
+
+
+def test_iterator_rvpolicy():
+ """ Issue 388: Can't make iterators via make_iterator() with different r/v policies """
+ from pybind11_tests.issues import make_iterator_1
+ from pybind11_tests.issues import make_iterator_2
+
+ assert list(make_iterator_1()) == [1, 2, 3]
+ assert list(make_iterator_2()) == [1, 2, 3]
+ assert not isinstance(make_iterator_1(), type(make_iterator_2()))
+
+
+def test_dupe_assignment():
+ """ Issue 461: overwriting a class with a function """
+ from pybind11_tests.issues import dupe_exception_failures
+ assert dupe_exception_failures() == []
+
+
+def test_enable_shared_from_this_with_reference_rvp():
+ """ Issue #471: shared pointer instance not dellocated """
+ from pybind11_tests import SharedParent, SharedChild
+
+ parent = SharedParent()
+ child = parent.get_child()
+
+ cstats = ConstructorStats.get(SharedChild)
+ assert cstats.alive() == 1
+ del child, parent
+ assert cstats.alive() == 0
+
+
+def test_non_destructed_holders():
+ """ Issue #478: unique ptrs constructed and freed without destruction """
+ from pybind11_tests import SpecialHolderObj
+
+ a = SpecialHolderObj(123)
+ b = a.child()
+
+ assert a.val == 123
+ assert b.val == 124
+
+ cstats = SpecialHolderObj.holder_cstats()
+ assert cstats.alive() == 1
+ del b
+ assert cstats.alive() == 1
+ del a
+ assert cstats.alive() == 0
+
+
+def test_complex_cast(capture):
+ """ Issue #484: number conversion generates unhandled exceptions """
+ from pybind11_tests.issues import test_complex
+
+ with capture:
+ test_complex(1)
+ test_complex(2j)
+
+ assert capture == """
+ 1.0
+ (0.0, 2.0)
+ """
+
+
+def test_inheritance_override_def_static():
+ from pybind11_tests.issues import MyBase, MyDerived
+
+ b = MyBase.make()
+ d1 = MyDerived.make2()
+ d2 = MyDerived.make()
+
+ assert isinstance(b, MyBase)
+ assert isinstance(d1, MyDerived)
+ assert isinstance(d2, MyDerived)
diff --git a/ext/pybind11/tests/test_keep_alive.cpp b/ext/pybind11/tests/test_keep_alive.cpp
new file mode 100644
index 000000000..cd62a02e8
--- /dev/null
+++ b/ext/pybind11/tests/test_keep_alive.cpp
@@ -0,0 +1,40 @@
+/*
+ tests/test_keep_alive.cpp -- keep_alive modifier (pybind11's version
+ of Boost.Python's with_custodian_and_ward / with_custodian_and_ward_postcall)
+
+ Copyright (c) 2016 Wenzel Jakob <wenzel.jakob@epfl.ch>
+
+ All rights reserved. Use of this source code is governed by a
+ BSD-style license that can be found in the LICENSE file.
+*/
+
+#include "pybind11_tests.h"
+
+class Child {
+public:
+ Child() { py::print("Allocating child."); }
+ ~Child() { py::print("Releasing child."); }
+};
+
+class Parent {
+public:
+ Parent() { py::print("Allocating parent."); }
+ ~Parent() { py::print("Releasing parent."); }
+ void addChild(Child *) { }
+ Child *returnChild() { return new Child(); }
+ Child *returnNullChild() { return nullptr; }
+};
+
+test_initializer keep_alive([](py::module &m) {
+ py::class_<Parent>(m, "Parent")
+ .def(py::init<>())
+ .def("addChild", &Parent::addChild)
+ .def("addChildKeepAlive", &Parent::addChild, py::keep_alive<1, 2>())
+ .def("returnChild", &Parent::returnChild)
+ .def("returnChildKeepAlive", &Parent::returnChild, py::keep_alive<1, 0>())
+ .def("returnNullChildKeepAliveChild", &Parent::returnNullChild, py::keep_alive<1, 0>())
+ .def("returnNullChildKeepAliveParent", &Parent::returnNullChild, py::keep_alive<0, 1>());
+
+ py::class_<Child>(m, "Child")
+ .def(py::init<>());
+});
diff --git a/ext/pybind11/tests/test_keep_alive.py b/ext/pybind11/tests/test_keep_alive.py
new file mode 100644
index 000000000..0cef34658
--- /dev/null
+++ b/ext/pybind11/tests/test_keep_alive.py
@@ -0,0 +1,97 @@
+import gc
+
+
+def test_keep_alive_argument(capture):
+ from pybind11_tests import Parent, Child
+
+ with capture:
+ p = Parent()
+ assert capture == "Allocating parent."
+ with capture:
+ p.addChild(Child())
+ gc.collect()
+ assert capture == """
+ Allocating child.
+ Releasing child.
+ """
+ with capture:
+ del p
+ gc.collect()
+ assert capture == "Releasing parent."
+
+ with capture:
+ p = Parent()
+ assert capture == "Allocating parent."
+ with capture:
+ p.addChildKeepAlive(Child())
+ gc.collect()
+ assert capture == "Allocating child."
+ with capture:
+ del p
+ gc.collect()
+ assert capture == """
+ Releasing parent.
+ Releasing child.
+ """
+
+
+def test_keep_alive_return_value(capture):
+ from pybind11_tests import Parent
+
+ with capture:
+ p = Parent()
+ assert capture == "Allocating parent."
+ with capture:
+ p.returnChild()
+ gc.collect()
+ assert capture == """
+ Allocating child.
+ Releasing child.
+ """
+ with capture:
+ del p
+ gc.collect()
+ assert capture == "Releasing parent."
+
+ with capture:
+ p = Parent()
+ assert capture == "Allocating parent."
+ with capture:
+ p.returnChildKeepAlive()
+ gc.collect()
+ assert capture == "Allocating child."
+ with capture:
+ del p
+ gc.collect()
+ assert capture == """
+ Releasing parent.
+ Releasing child.
+ """
+
+
+def test_return_none(capture):
+ from pybind11_tests import Parent
+
+ with capture:
+ p = Parent()
+ assert capture == "Allocating parent."
+ with capture:
+ p.returnNullChildKeepAliveChild()
+ gc.collect()
+ assert capture == ""
+ with capture:
+ del p
+ gc.collect()
+ assert capture == "Releasing parent."
+
+ with capture:
+ p = Parent()
+ assert capture == "Allocating parent."
+ with capture:
+ p.returnNullChildKeepAliveParent()
+ gc.collect()
+ assert capture == ""
+ with capture:
+ del p
+ 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
new file mode 100644
index 000000000..24fc0cd5b
--- /dev/null
+++ b/ext/pybind11/tests/test_kwargs_and_defaults.cpp
@@ -0,0 +1,56 @@
+/*
+ tests/test_kwargs_and_defaults.cpp -- keyword arguments and default values
+
+ Copyright (c) 2016 Wenzel Jakob <wenzel.jakob@epfl.ch>
+
+ All rights reserved. Use of this source code is governed by a
+ BSD-style license that can be found in the LICENSE file.
+*/
+
+#include "pybind11_tests.h"
+#include <pybind11/stl.h>
+
+std::string kw_func(int x, int y) { return "x=" + std::to_string(x) + ", y=" + std::to_string(y); }
+
+std::string kw_func4(const std::vector<int> &entries) {
+ std::string ret = "{";
+ for (int i : entries)
+ ret += std::to_string(i) + " ";
+ ret.back() = '}';
+ return ret;
+}
+
+py::tuple args_function(py::args args) {
+ return args;
+}
+
+py::tuple args_kwargs_function(py::args args, py::kwargs kwargs) {
+ return py::make_tuple(args, kwargs);
+}
+
+struct KWClass {
+ void foo(int, float) {}
+};
+
+test_initializer arg_keywords_and_defaults([](py::module &m) {
+ m.def("kw_func0", &kw_func);
+ m.def("kw_func1", &kw_func, py::arg("x"), py::arg("y"));
+ m.def("kw_func2", &kw_func, py::arg("x") = 100, py::arg("y") = 200);
+ m.def("kw_func3", [](const char *) { }, py::arg("data") = std::string("Hello world!"));
+
+ /* A fancier default argument */
+ std::vector<int> list;
+ list.push_back(13);
+ list.push_back(17);
+ m.def("kw_func4", &kw_func4, py::arg("myList") = list);
+
+ m.def("args_function", &args_function);
+ m.def("args_kwargs_function", &args_kwargs_function);
+
+ m.def("kw_func_udl", &kw_func, "x"_a, "y"_a=300);
+ m.def("kw_func_udl_z", &kw_func, "x"_a, "y"_a=0);
+
+ py::class_<KWClass>(m, "KWClass")
+ .def("foo0", &KWClass::foo)
+ .def("foo1", &KWClass::foo, "x"_a, "y"_a);
+});
diff --git a/ext/pybind11/tests/test_kwargs_and_defaults.py b/ext/pybind11/tests/test_kwargs_and_defaults.py
new file mode 100644
index 000000000..852d03c6e
--- /dev/null
+++ b/ext/pybind11/tests/test_kwargs_and_defaults.py
@@ -0,0 +1,57 @@
+import pytest
+from pybind11_tests import (kw_func0, kw_func1, kw_func2, kw_func3, kw_func4, args_function,
+ args_kwargs_function, kw_func_udl, kw_func_udl_z, KWClass)
+
+
+def test_function_signatures(doc):
+ assert doc(kw_func0) == "kw_func0(arg0: int, arg1: int) -> str"
+ assert doc(kw_func1) == "kw_func1(x: int, y: int) -> str"
+ assert doc(kw_func2) == "kw_func2(x: int=100, y: int=200) -> str"
+ assert doc(kw_func3) == "kw_func3(data: str='Hello world!') -> None"
+ assert doc(kw_func4) == "kw_func4(myList: List[int]=[13, 17]) -> str"
+ assert doc(kw_func_udl) == "kw_func_udl(x: int, y: int=300) -> str"
+ assert doc(kw_func_udl_z) == "kw_func_udl_z(x: int, y: int=0) -> str"
+ assert doc(args_function) == "args_function(*args) -> tuple"
+ assert doc(args_kwargs_function) == "args_kwargs_function(*args, **kwargs) -> tuple"
+ assert doc(KWClass.foo0) == "foo0(self: m.KWClass, arg0: int, arg1: float) -> None"
+ assert doc(KWClass.foo1) == "foo1(self: m.KWClass, x: int, y: float) -> None"
+
+
+def test_named_arguments(msg):
+ assert kw_func0(5, 10) == "x=5, y=10"
+
+ assert kw_func1(5, 10) == "x=5, y=10"
+ assert kw_func1(5, y=10) == "x=5, y=10"
+ assert kw_func1(y=10, x=5) == "x=5, y=10"
+
+ assert kw_func2() == "x=100, y=200"
+ assert kw_func2(5) == "x=5, y=200"
+ assert kw_func2(x=5) == "x=5, y=200"
+ assert kw_func2(y=10) == "x=100, y=10"
+ assert kw_func2(5, 10) == "x=5, y=10"
+ assert kw_func2(x=5, y=10) == "x=5, y=10"
+
+ 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 kw_func4() == "{13 17}"
+ assert kw_func4(myList=[1, 2, 3]) == "{1 2 3}"
+
+ assert kw_func_udl(x=5, y=10) == "x=5, y=10"
+ assert kw_func_udl_z(x=5) == "x=5, y=0"
+
+
+def test_arg_and_kwargs():
+ args = 'arg1_value', 'arg2_value', 3
+ assert args_function(*args) == args
+
+ args = 'a1', 'a2'
+ kwargs = dict(arg3='a3', arg4=4)
+ assert args_kwargs_function(*args, **kwargs) == (args, kwargs)
diff --git a/ext/pybind11/tests/test_methods_and_attributes.cpp b/ext/pybind11/tests/test_methods_and_attributes.cpp
new file mode 100644
index 000000000..11fc90091
--- /dev/null
+++ b/ext/pybind11/tests/test_methods_and_attributes.cpp
@@ -0,0 +1,185 @@
+/*
+ tests/test_methods_and_attributes.cpp -- constructors, deconstructors, attribute access,
+ __str__, argument and return value conventions
+
+ Copyright (c) 2016 Wenzel Jakob <wenzel.jakob@epfl.ch>
+
+ All rights reserved. Use of this source code is governed by a
+ BSD-style license that can be found in the LICENSE file.
+*/
+
+#include "pybind11_tests.h"
+#include "constructor_stats.h"
+
+class ExampleMandA {
+public:
+ ExampleMandA() { print_default_created(this); }
+ ExampleMandA(int value) : value(value) { print_created(this, value); }
+ ExampleMandA(const ExampleMandA &e) : value(e.value) { print_copy_created(this); }
+ ExampleMandA(ExampleMandA &&e) : value(e.value) { print_move_created(this); }
+ ~ExampleMandA() { print_destroyed(this); }
+
+ std::string toString() {
+ return "ExampleMandA[value=" + std::to_string(value) + "]";
+ }
+
+ void operator=(const ExampleMandA &e) { print_copy_assigned(this); value = e.value; }
+ void operator=(ExampleMandA &&e) { print_move_assigned(this); value = e.value; }
+
+ void add1(ExampleMandA other) { value += other.value; } // passing by value
+ void add2(ExampleMandA &other) { value += other.value; } // passing by reference
+ void add3(const ExampleMandA &other) { value += other.value; } // passing by const reference
+ void add4(ExampleMandA *other) { value += other->value; } // passing by pointer
+ void add5(const ExampleMandA *other) { value += other->value; } // passing by const pointer
+
+ void add6(int other) { value += other; } // passing by value
+ void add7(int &other) { value += other; } // passing by reference
+ void add8(const int &other) { value += other; } // passing by const reference
+ void add9(int *other) { value += *other; } // passing by pointer
+ void add10(const int *other) { value += *other; } // passing by const pointer
+
+ ExampleMandA self1() { return *this; } // return by value
+ ExampleMandA &self2() { return *this; } // return by reference
+ const ExampleMandA &self3() { return *this; } // return by const reference
+ ExampleMandA *self4() { return this; } // return by pointer
+ const ExampleMandA *self5() { return this; } // return by const pointer
+
+ int internal1() { return value; } // return by value
+ int &internal2() { return value; } // return by reference
+ const int &internal3() { return value; } // return by const reference
+ int *internal4() { return &value; } // return by pointer
+ const int *internal5() { return &value; } // return by const pointer
+
+ py::str overloaded(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"; }
+
+ int value = 0;
+};
+
+struct TestProperties {
+ int value = 1;
+ static int static_value;
+
+ int get() const { return value; }
+ void set(int v) { value = v; }
+
+ static int static_get() { return static_value; }
+ static void static_set(int v) { static_value = v; }
+};
+
+int TestProperties::static_value = 1;
+
+struct SimpleValue { int value = 1; };
+
+struct TestPropRVP {
+ SimpleValue v1;
+ SimpleValue v2;
+ static SimpleValue sv1;
+ static SimpleValue sv2;
+
+ const SimpleValue &get1() const { return v1; }
+ const SimpleValue &get2() const { return v2; }
+ SimpleValue get_rvalue() const { return v2; }
+ void set1(int v) { v1.value = v; }
+ void set2(int v) { v2.value = v; }
+};
+
+SimpleValue TestPropRVP::sv1{};
+SimpleValue TestPropRVP::sv2{};
+
+class DynamicClass {
+public:
+ DynamicClass() { print_default_created(this); }
+ ~DynamicClass() { print_destroyed(this); }
+};
+
+class CppDerivedDynamicClass : public DynamicClass { };
+
+test_initializer methods_and_attributes([](py::module &m) {
+ py::class_<ExampleMandA>(m, "ExampleMandA")
+ .def(py::init<>())
+ .def(py::init<int>())
+ .def(py::init<const ExampleMandA&>())
+ .def("add1", &ExampleMandA::add1)
+ .def("add2", &ExampleMandA::add2)
+ .def("add3", &ExampleMandA::add3)
+ .def("add4", &ExampleMandA::add4)
+ .def("add5", &ExampleMandA::add5)
+ .def("add6", &ExampleMandA::add6)
+ .def("add7", &ExampleMandA::add7)
+ .def("add8", &ExampleMandA::add8)
+ .def("add9", &ExampleMandA::add9)
+ .def("add10", &ExampleMandA::add10)
+ .def("self1", &ExampleMandA::self1)
+ .def("self2", &ExampleMandA::self2)
+ .def("self3", &ExampleMandA::self3)
+ .def("self4", &ExampleMandA::self4)
+ .def("self5", &ExampleMandA::self5)
+ .def("internal1", &ExampleMandA::internal1)
+ .def("internal2", &ExampleMandA::internal2)
+ .def("internal3", &ExampleMandA::internal3)
+ .def("internal4", &ExampleMandA::internal4)
+ .def("internal5", &ExampleMandA::internal5)
+#if defined(PYBIND11_OVERLOAD_CAST)
+ .def("overloaded", py::overload_cast<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_))
+#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))
+#endif
+ .def("__str__", &ExampleMandA::toString)
+ .def_readwrite("value", &ExampleMandA::value)
+ ;
+
+ py::class_<TestProperties>(m, "TestProperties")
+ .def(py::init<>())
+ .def_readonly("def_readonly", &TestProperties::value)
+ .def_readwrite("def_readwrite", &TestProperties::value)
+ .def_property_readonly("def_property_readonly", &TestProperties::get)
+ .def_property("def_property", &TestProperties::get, &TestProperties::set)
+ .def_readonly_static("def_readonly_static", &TestProperties::static_value)
+ .def_readwrite_static("def_readwrite_static", &TestProperties::static_value)
+ .def_property_readonly_static("def_property_readonly_static",
+ [](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::class_<SimpleValue>(m, "SimpleValue")
+ .def_readwrite("value", &SimpleValue::value);
+
+ auto static_get1 = [](py::object) -> const SimpleValue & { return TestPropRVP::sv1; };
+ auto static_get2 = [](py::object) -> const SimpleValue & { return TestPropRVP::sv2; };
+ auto static_set1 = [](py::object, int v) { TestPropRVP::sv1.value = v; };
+ auto static_set2 = [](py::object, int v) { TestPropRVP::sv2.value = v; };
+ auto rvp_copy = py::return_value_policy::copy;
+
+ py::class_<TestPropRVP>(m, "TestPropRVP")
+ .def(py::init<>())
+ .def_property_readonly("ro_ref", &TestPropRVP::get1)
+ .def_property_readonly("ro_copy", &TestPropRVP::get2, rvp_copy)
+ .def_property_readonly("ro_func", py::cpp_function(&TestPropRVP::get2, rvp_copy))
+ .def_property("rw_ref", &TestPropRVP::get1, &TestPropRVP::set1)
+ .def_property("rw_copy", &TestPropRVP::get2, &TestPropRVP::set2, rvp_copy)
+ .def_property("rw_func", py::cpp_function(&TestPropRVP::get2, rvp_copy), &TestPropRVP::set2)
+ .def_property_readonly_static("static_ro_ref", static_get1)
+ .def_property_readonly_static("static_ro_copy", static_get2, rvp_copy)
+ .def_property_readonly_static("static_ro_func", py::cpp_function(static_get2, rvp_copy))
+ .def_property_static("static_rw_ref", static_get1, static_set1)
+ .def_property_static("static_rw_copy", static_get2, static_set2, rvp_copy)
+ .def_property_static("static_rw_func", py::cpp_function(static_get2, rvp_copy), static_set2)
+ .def_property_readonly("rvalue", &TestPropRVP::get_rvalue)
+ .def_property_readonly_static("static_rvalue", [](py::object) { return SimpleValue(); });
+
+ py::class_<DynamicClass>(m, "DynamicClass", py::dynamic_attr())
+ .def(py::init());
+
+ py::class_<CppDerivedDynamicClass, DynamicClass>(m, "CppDerivedDynamicClass")
+ .def(py::init());
+});
diff --git a/ext/pybind11/tests/test_methods_and_attributes.py b/ext/pybind11/tests/test_methods_and_attributes.py
new file mode 100644
index 000000000..2b0f8d571
--- /dev/null
+++ b/ext/pybind11/tests/test_methods_and_attributes.py
@@ -0,0 +1,194 @@
+import pytest
+from pybind11_tests import ExampleMandA, ConstructorStats
+
+
+def test_methods_and_attributes():
+ instance1 = ExampleMandA()
+ instance2 = ExampleMandA(32)
+
+ instance1.add1(instance2)
+ instance1.add2(instance2)
+ instance1.add3(instance2)
+ instance1.add4(instance2)
+ instance1.add5(instance2)
+ instance1.add6(32)
+ instance1.add7(32)
+ instance1.add8(32)
+ instance1.add9(32)
+ instance1.add10(32)
+
+ assert str(instance1) == "ExampleMandA[value=320]"
+ assert str(instance2) == "ExampleMandA[value=32]"
+ assert str(instance1.self1()) == "ExampleMandA[value=320]"
+ assert str(instance1.self2()) == "ExampleMandA[value=320]"
+ assert str(instance1.self3()) == "ExampleMandA[value=320]"
+ assert str(instance1.self4()) == "ExampleMandA[value=320]"
+ assert str(instance1.self5()) == "ExampleMandA[value=320]"
+
+ assert instance1.internal1() == 320
+ assert instance1.internal2() == 320
+ assert instance1.internal3() == 320
+ assert instance1.internal4() == 320
+ assert instance1.internal5() == 320
+
+ assert instance1.overloaded(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.value == 320
+ instance1.value = 100
+ assert str(instance1) == "ExampleMandA[value=100]"
+
+ cstats = ConstructorStats.get(ExampleMandA)
+ assert cstats.alive() == 2
+ del instance1, instance2
+ assert cstats.alive() == 0
+ assert cstats.values() == ["32"]
+ assert cstats.default_constructions == 1
+ assert cstats.copy_constructions == 3
+ assert cstats.move_constructions >= 1
+ assert cstats.copy_assignments == 0
+ assert cstats.move_assignments == 0
+
+
+def test_properties():
+ from pybind11_tests import TestProperties
+
+ instance = TestProperties()
+
+ assert instance.def_readonly == 1
+ with pytest.raises(AttributeError):
+ instance.def_readonly = 2
+
+ instance.def_readwrite = 2
+ assert instance.def_readwrite == 2
+
+ assert instance.def_property_readonly == 2
+ with pytest.raises(AttributeError):
+ instance.def_property_readonly = 3
+
+ instance.def_property = 3
+ assert instance.def_property == 3
+
+
+def test_static_properties():
+ from pybind11_tests import TestProperties as Type
+
+ assert Type.def_readonly_static == 1
+ with pytest.raises(AttributeError):
+ Type.def_readonly_static = 2
+
+ Type.def_readwrite_static = 2
+ assert Type.def_readwrite_static == 2
+
+ assert Type.def_property_readonly_static == 2
+ with pytest.raises(AttributeError):
+ Type.def_property_readonly_static = 3
+
+ Type.def_property_static = 3
+ assert Type.def_property_static == 3
+
+
+@pytest.mark.parametrize("access", ["ro", "rw", "static_ro", "static_rw"])
+def test_property_return_value_policies(access):
+ from pybind11_tests import TestPropRVP
+
+ if not access.startswith("static"):
+ obj = TestPropRVP()
+ else:
+ obj = TestPropRVP
+
+ ref = getattr(obj, access + "_ref")
+ assert ref.value == 1
+ ref.value = 2
+ assert getattr(obj, access + "_ref").value == 2
+ ref.value = 1 # restore original value for static properties
+
+ copy = getattr(obj, access + "_copy")
+ assert copy.value == 1
+ copy.value = 2
+ assert getattr(obj, access + "_copy").value == 1
+
+ copy = getattr(obj, access + "_func")
+ assert copy.value == 1
+ copy.value = 2
+ assert getattr(obj, access + "_func").value == 1
+
+
+def test_property_rvalue_policy():
+ """When returning an rvalue, the return value policy is automatically changed from
+ `reference(_internal)` to `move`. The following would not work otherwise.
+ """
+ from pybind11_tests import TestPropRVP
+
+ instance = TestPropRVP()
+ o = instance.rvalue
+ assert o.value == 1
+ o = TestPropRVP.static_rvalue
+ assert o.value == 1
+
+
+def test_dynamic_attributes():
+ from pybind11_tests import DynamicClass, CppDerivedDynamicClass
+
+ instance = DynamicClass()
+ assert not hasattr(instance, "foo")
+ assert "foo" not in dir(instance)
+
+ # Dynamically add attribute
+ instance.foo = 42
+ assert hasattr(instance, "foo")
+ assert instance.foo == 42
+ assert "foo" in dir(instance)
+
+ # __dict__ should be accessible and replaceable
+ assert "foo" in instance.__dict__
+ instance.__dict__ = {"bar": True}
+ assert not hasattr(instance, "foo")
+ assert hasattr(instance, "bar")
+
+ with pytest.raises(TypeError) as excinfo:
+ instance.__dict__ = []
+ assert str(excinfo.value) == "__dict__ must be set to a dictionary, not a 'list'"
+
+ cstats = ConstructorStats.get(DynamicClass)
+ assert cstats.alive() == 1
+ del instance
+ assert cstats.alive() == 0
+
+ # Derived classes should work as well
+ class PythonDerivedDynamicClass(DynamicClass):
+ pass
+
+ for cls in CppDerivedDynamicClass, PythonDerivedDynamicClass:
+ derived = cls()
+ derived.foobar = 100
+ assert derived.foobar == 100
+
+ assert cstats.alive() == 1
+ del derived
+ assert cstats.alive() == 0
+
+
+def test_cyclic_gc():
+ from pybind11_tests import DynamicClass
+
+ # One object references itself
+ instance = DynamicClass()
+ instance.circular_reference = instance
+
+ cstats = ConstructorStats.get(DynamicClass)
+ assert cstats.alive() == 1
+ del instance
+ assert cstats.alive() == 0
+
+ # Two object reference each other
+ i1 = DynamicClass()
+ i2 = DynamicClass()
+ i1.cycle = i2
+ i2.cycle = i1
+
+ assert cstats.alive() == 2
+ del i1, i2
+ assert cstats.alive() == 0
diff --git a/ext/pybind11/tests/test_modules.cpp b/ext/pybind11/tests/test_modules.cpp
new file mode 100644
index 000000000..50c7d8412
--- /dev/null
+++ b/ext/pybind11/tests/test_modules.cpp
@@ -0,0 +1,58 @@
+/*
+ tests/test_modules.cpp -- nested modules, importing modules, and
+ internal references
+
+ Copyright (c) 2016 Wenzel Jakob <wenzel.jakob@epfl.ch>
+
+ All rights reserved. Use of this source code is governed by a
+ BSD-style license that can be found in the LICENSE file.
+*/
+
+#include "pybind11_tests.h"
+#include "constructor_stats.h"
+
+std::string submodule_func() {
+ return "submodule_func()";
+}
+
+class A {
+public:
+ A(int v) : v(v) { print_created(this, v); }
+ ~A() { print_destroyed(this); }
+ A(const A&) { print_copy_created(this); }
+ A& operator=(const A &copy) { print_copy_assigned(this); v = copy.v; return *this; }
+ std::string toString() { return "A[" + std::to_string(v) + "]"; }
+private:
+ int v;
+};
+
+class B {
+public:
+ B() { print_default_created(this); }
+ ~B() { print_destroyed(this); }
+ B(const B&) { print_copy_created(this); }
+ B& operator=(const B &copy) { print_copy_assigned(this); a1 = copy.a1; a2 = copy.a2; return *this; }
+ A &get_a1() { return a1; }
+ A &get_a2() { return a2; }
+
+ A a1{1};
+ A a2{2};
+};
+
+test_initializer modules([](py::module &m) {
+ py::module m_sub = m.def_submodule("submodule");
+ m_sub.def("submodule_func", &submodule_func);
+
+ py::class_<A>(m_sub, "A")
+ .def(py::init<int>())
+ .def("__repr__", &A::toString);
+
+ py::class_<B>(m_sub, "B")
+ .def(py::init<>())
+ .def("get_a1", &B::get_a1, "Return the internal A 1", py::return_value_policy::reference_internal)
+ .def("get_a2", &B::get_a2, "Return the internal A 2", py::return_value_policy::reference_internal)
+ .def_readwrite("a1", &B::a1) // def_readonly uses an internal reference return policy by default
+ .def_readwrite("a2", &B::a2);
+
+ m.attr("OD") = py::module::import("collections").attr("OrderedDict");
+});
diff --git a/ext/pybind11/tests/test_modules.py b/ext/pybind11/tests/test_modules.py
new file mode 100644
index 000000000..fe72f190a
--- /dev/null
+++ b/ext/pybind11/tests/test_modules.py
@@ -0,0 +1,54 @@
+
+def test_nested_modules():
+ import pybind11_tests
+ from pybind11_tests.submodule import submodule_func
+
+ assert pybind11_tests.__name__ == "pybind11_tests"
+ assert pybind11_tests.submodule.__name__ == "pybind11_tests.submodule"
+
+ assert submodule_func() == "submodule_func()"
+
+
+def test_reference_internal():
+ from pybind11_tests import ConstructorStats
+ from pybind11_tests.submodule import A, B
+
+ b = B()
+ assert str(b.get_a1()) == "A[1]"
+ assert str(b.a1) == "A[1]"
+ assert str(b.get_a2()) == "A[2]"
+ assert str(b.a2) == "A[2]"
+
+ b.a1 = A(42)
+ b.a2 = A(43)
+ assert str(b.get_a1()) == "A[42]"
+ assert str(b.a1) == "A[42]"
+ assert str(b.get_a2()) == "A[43]"
+ assert str(b.a2) == "A[43]"
+
+ astats, bstats = ConstructorStats.get(A), ConstructorStats.get(B)
+ assert astats.alive() == 2
+ assert bstats.alive() == 1
+ del b
+ assert astats.alive() == 0
+ assert bstats.alive() == 0
+ assert astats.values() == ['1', '2', '42', '43']
+ assert bstats.values() == []
+ assert astats.default_constructions == 0
+ assert bstats.default_constructions == 1
+ assert astats.copy_constructions == 0
+ assert bstats.copy_constructions == 0
+ # assert astats.move_constructions >= 0 # Don't invoke any
+ # assert bstats.move_constructions >= 0 # Don't invoke any
+ assert astats.copy_assignments == 2
+ assert bstats.copy_assignments == 0
+ assert astats.move_assignments == 0
+ assert bstats.move_assignments == 0
+
+
+def test_importing():
+ from pybind11_tests import OD
+ from collections import OrderedDict
+
+ assert OD is OrderedDict
+ assert str(OD([(1, 'a'), (2, 'b')])) == "OrderedDict([(1, 'a'), (2, 'b')])"
diff --git a/ext/pybind11/tests/test_multiple_inheritance.cpp b/ext/pybind11/tests/test_multiple_inheritance.cpp
new file mode 100644
index 000000000..3cb12b68d
--- /dev/null
+++ b/ext/pybind11/tests/test_multiple_inheritance.cpp
@@ -0,0 +1,84 @@
+/*
+ tests/test_multiple_inheritance.cpp -- multiple inheritance,
+ implicit MI casts
+
+ Copyright (c) 2016 Wenzel Jakob <wenzel.jakob@epfl.ch>
+
+ All rights reserved. Use of this source code is governed by a
+ BSD-style license that can be found in the LICENSE file.
+*/
+
+#include "pybind11_tests.h"
+
+
+struct Base1 {
+ Base1(int i) : i(i) { }
+ int foo() { return i; }
+ int i;
+};
+
+struct Base2 {
+ Base2(int i) : i(i) { }
+ int bar() { return i; }
+ int i;
+};
+
+struct Base12 : Base1, Base2 {
+ Base12(int i, int j) : Base1(i), Base2(j) { }
+};
+
+struct MIType : Base12 {
+ MIType(int i, int j) : Base12(i, j) { }
+};
+
+test_initializer multiple_inheritance([](py::module &m) {
+ py::class_<Base1>(m, "Base1")
+ .def(py::init<int>())
+ .def("foo", &Base1::foo);
+
+ py::class_<Base2>(m, "Base2")
+ .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>());
+});
+
+/* Test the case where not all base classes are specified,
+ and where pybind11 requires the py::multiple_inheritance
+ flag to perform proper casting between types */
+
+struct Base1a {
+ Base1a(int i) : i(i) { }
+ int foo() { return i; }
+ int i;
+};
+
+struct Base2a {
+ Base2a(int i) : i(i) { }
+ int bar() { return i; }
+ int i;
+};
+
+struct Base12a : Base1a, Base2a {
+ Base12a(int i, int j) : Base1a(i), Base2a(j) { }
+};
+
+test_initializer multiple_inheritance_nonexplicit([](py::module &m) {
+ py::class_<Base1a, std::shared_ptr<Base1a>>(m, "Base1a")
+ .def(py::init<int>())
+ .def("foo", &Base1a::foo);
+
+ py::class_<Base2a, std::shared_ptr<Base2a>>(m, "Base2a")
+ .def(py::init<int>())
+ .def("bar", &Base2a::bar);
+
+ py::class_<Base12a, /* Base1 missing */ Base2a,
+ std::shared_ptr<Base12a>>(m, "Base12a", py::multiple_inheritance())
+ .def(py::init<int, int>());
+
+ m.def("bar_base2a", [](Base2a *b) { return b->bar(); });
+ m.def("bar_base2a_sharedptr", [](std::shared_ptr<Base2a> b) { return b->bar(); });
+});
diff --git a/ext/pybind11/tests/test_multiple_inheritance.py b/ext/pybind11/tests/test_multiple_inheritance.py
new file mode 100644
index 000000000..581cf5687
--- /dev/null
+++ b/ext/pybind11/tests/test_multiple_inheritance.py
@@ -0,0 +1,64 @@
+
+
+def test_multiple_inheritance_cpp():
+ from pybind11_tests import MIType
+
+ mt = MIType(3, 4)
+
+ assert mt.foo() == 3
+ assert mt.bar() == 4
+
+
+def test_multiple_inheritance_mix1():
+ from pybind11_tests import Base2
+
+ class Base1:
+ def __init__(self, i):
+ self.i = i
+
+ def foo(self):
+ return self.i
+
+ class MITypePy(Base1, Base2):
+ def __init__(self, i, j):
+ Base1.__init__(self, i)
+ Base2.__init__(self, j)
+
+ mt = MITypePy(3, 4)
+
+ assert mt.foo() == 3
+ assert mt.bar() == 4
+
+
+def test_multiple_inheritance_mix2():
+ from pybind11_tests import Base1
+
+ class Base2:
+ def __init__(self, i):
+ self.i = i
+
+ def bar(self):
+ return self.i
+
+ class MITypePy(Base1, Base2):
+ def __init__(self, i, j):
+ Base1.__init__(self, i)
+ Base2.__init__(self, j)
+
+ mt = MITypePy(3, 4)
+
+ assert mt.foo() == 3
+ assert mt.bar() == 4
+
+
+def test_multiple_inheritance_virtbase():
+ from pybind11_tests import Base12a, bar_base2a, bar_base2a_sharedptr
+
+ class MITypePy(Base12a):
+ def __init__(self, i, j):
+ Base12a.__init__(self, i, j)
+
+ mt = MITypePy(3, 4)
+ assert mt.bar() == 4
+ assert bar_base2a(mt) == 4
+ assert bar_base2a_sharedptr(mt) == 4
diff --git a/ext/pybind11/tests/test_numpy_array.cpp b/ext/pybind11/tests/test_numpy_array.cpp
new file mode 100644
index 000000000..14c4c2999
--- /dev/null
+++ b/ext/pybind11/tests/test_numpy_array.cpp
@@ -0,0 +1,153 @@
+/*
+ tests/test_numpy_array.cpp -- test core array functionality
+
+ Copyright (c) 2016 Ivan Smirnov <i.s.smirnov@gmail.com>
+
+ All rights reserved. Use of this source code is governed by a
+ BSD-style license that can be found in the LICENSE file.
+*/
+
+#include "pybind11_tests.h"
+
+#include <pybind11/numpy.h>
+#include <pybind11/stl.h>
+
+#include <cstdint>
+#include <vector>
+
+using arr = py::array;
+using arr_t = py::array_t<uint16_t, 0>;
+
+template<typename... Ix> arr data(const arr& a, Ix... index) {
+ return arr(a.nbytes() - a.offset_at(index...), (const uint8_t *) a.data(index...));
+}
+
+template<typename... Ix> arr data_t(const arr_t& a, Ix... index) {
+ return arr(a.size() - a.index_at(index...), a.data(index...));
+}
+
+arr& mutate_data(arr& a) {
+ auto ptr = (uint8_t *) a.mutable_data();
+ for (size_t i = 0; i < a.nbytes(); i++)
+ ptr[i] = (uint8_t) (ptr[i] * 2);
+ return a;
+}
+
+arr_t& mutate_data_t(arr_t& a) {
+ auto ptr = a.mutable_data();
+ for (size_t i = 0; i < a.size(); i++)
+ ptr[i]++;
+ return a;
+}
+
+template<typename... Ix> arr& mutate_data(arr& a, Ix... index) {
+ auto ptr = (uint8_t *) a.mutable_data(index...);
+ for (size_t i = 0; i < a.nbytes() - a.offset_at(index...); i++)
+ ptr[i] = (uint8_t) (ptr[i] * 2);
+ return a;
+}
+
+template<typename... Ix> arr_t& mutate_data_t(arr_t& a, Ix... index) {
+ auto ptr = a.mutable_data(index...);
+ for (size_t i = 0; i < a.size() - a.index_at(index...); i++)
+ ptr[i]++;
+ return a;
+}
+
+template<typename... Ix> size_t index_at(const arr& a, Ix... idx) { return a.index_at(idx...); }
+template<typename... Ix> size_t index_at_t(const arr_t& a, Ix... idx) { return a.index_at(idx...); }
+template<typename... Ix> size_t offset_at(const arr& a, Ix... idx) { return a.offset_at(idx...); }
+template<typename... Ix> size_t offset_at_t(const arr_t& a, Ix... idx) { return a.offset_at(idx...); }
+template<typename... Ix> size_t at_t(const arr_t& a, Ix... idx) { return a.at(idx...); }
+template<typename... Ix> arr_t& mutate_at_t(arr_t& a, Ix... idx) { a.mutable_at(idx...)++; return a; }
+
+#define def_index_fn(name, type) \
+ sm.def(#name, [](type a) { return name(a); }); \
+ sm.def(#name, [](type a, int i) { return name(a, i); }); \
+ sm.def(#name, [](type a, int i, int j) { return name(a, i, j); }); \
+ sm.def(#name, [](type a, int i, int j, int k) { return name(a, i, j, k); });
+
+test_initializer numpy_array([](py::module &m) {
+ auto sm = m.def_submodule("array");
+
+ sm.def("ndim", [](const arr& a) { return a.ndim(); });
+ sm.def("shape", [](const arr& a) { return arr(a.ndim(), a.shape()); });
+ sm.def("shape", [](const arr& a, size_t dim) { return a.shape(dim); });
+ sm.def("strides", [](const arr& a) { return arr(a.ndim(), a.strides()); });
+ sm.def("strides", [](const arr& a, size_t dim) { return a.strides(dim); });
+ sm.def("writeable", [](const arr& a) { return a.writeable(); });
+ sm.def("size", [](const arr& a) { return a.size(); });
+ sm.def("itemsize", [](const arr& a) { return a.itemsize(); });
+ sm.def("nbytes", [](const arr& a) { return a.nbytes(); });
+ sm.def("owndata", [](const arr& a) { return a.owndata(); });
+
+ def_index_fn(data, const arr&);
+ def_index_fn(data_t, const arr_t&);
+ def_index_fn(index_at, const arr&);
+ def_index_fn(index_at_t, const arr_t&);
+ def_index_fn(offset_at, const arr&);
+ def_index_fn(offset_at_t, const arr_t&);
+ def_index_fn(mutate_data, arr&);
+ def_index_fn(mutate_data_t, arr_t&);
+ def_index_fn(at_t, const arr_t&);
+ def_index_fn(mutate_at_t, arr_t&);
+
+ sm.def("make_f_array", [] {
+ return py::array_t<float>({ 2, 2 }, { 4, 8 });
+ });
+
+ sm.def("make_c_array", [] {
+ return py::array_t<float>({ 2, 2 }, { 8, 4 });
+ });
+
+ sm.def("wrap", [](py::array a) {
+ return py::array(
+ a.dtype(),
+ std::vector<size_t>(a.shape(), a.shape() + a.ndim()),
+ std::vector<size_t>(a.strides(), a.strides() + a.ndim()),
+ a.data(),
+ a
+ );
+ });
+
+ struct ArrayClass {
+ int data[2] = { 1, 2 };
+ ArrayClass() { py::print("ArrayClass()"); }
+ ~ArrayClass() { py::print("~ArrayClass()"); }
+ };
+
+ py::class_<ArrayClass>(sm, "ArrayClass")
+ .def(py::init<>())
+ .def("numpy_view", [](py::object &obj) {
+ py::print("ArrayClass::numpy_view()");
+ ArrayClass &a = obj.cast<ArrayClass&>();
+ return py::array_t<int>({2}, {4}, a.data, obj);
+ }
+ );
+
+ sm.def("function_taking_uint64", [](uint64_t) { });
+
+ sm.def("isinstance_untyped", [](py::object yes, py::object no) {
+ return py::isinstance<py::array>(yes) && !py::isinstance<py::array>(no);
+ });
+
+ sm.def("isinstance_typed", [](py::object o) {
+ return py::isinstance<py::array_t<double>>(o) && !py::isinstance<py::array_t<int>>(o);
+ });
+
+ sm.def("default_constructors", []() {
+ return py::dict(
+ "array"_a=py::array(),
+ "array_t<int32>"_a=py::array_t<std::int32_t>(),
+ "array_t<double>"_a=py::array_t<double>()
+ );
+ });
+
+ sm.def("converting_constructors", [](py::object o) {
+ return py::dict(
+ "array"_a=py::array(o),
+ "array_t<int32>"_a=py::array_t<std::int32_t>(o),
+ "array_t<double>"_a=py::array_t<double>(o)
+ );
+ });
+});
diff --git a/ext/pybind11/tests/test_numpy_array.py b/ext/pybind11/tests/test_numpy_array.py
new file mode 100644
index 000000000..1c218a10b
--- /dev/null
+++ b/ext/pybind11/tests/test_numpy_array.py
@@ -0,0 +1,273 @@
+import pytest
+import gc
+
+with pytest.suppress(ImportError):
+ import numpy as np
+
+
+@pytest.fixture(scope='function')
+def arr():
+ 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
+ )
+
+ a = np.array(0, 'f8')
+ assert ndim(a) == 0
+ assert all(shape(a) == [])
+ assert all(strides(a) == [])
+ with pytest.raises(IndexError) as excinfo:
+ shape(a, 0)
+ assert str(excinfo.value) == 'invalid axis: 0 (ndim = 0)'
+ with pytest.raises(IndexError) as excinfo:
+ strides(a, 0)
+ assert str(excinfo.value) == 'invalid axis: 0 (ndim = 0)'
+ assert writeable(a)
+ assert size(a) == 1
+ assert itemsize(a) == 8
+ assert nbytes(a) == 8
+ assert owndata(a)
+
+ a = np.array([[1, 2, 3], [4, 5, 6]], 'u2').view()
+ a.flags.writeable = False
+ assert ndim(a) == 2
+ assert all(shape(a) == [2, 3])
+ assert shape(a, 0) == 2
+ assert shape(a, 1) == 3
+ assert all(strides(a) == [6, 2])
+ assert strides(a, 0) == 6
+ assert strides(a, 1) == 2
+ with pytest.raises(IndexError) as excinfo:
+ shape(a, 2)
+ assert str(excinfo.value) == 'invalid axis: 2 (ndim = 2)'
+ with pytest.raises(IndexError) as excinfo:
+ strides(a, 2)
+ assert str(excinfo.value) == 'invalid axis: 2 (ndim = 2)'
+ assert not writeable(a)
+ assert size(a) == 6
+ assert itemsize(a) == 2
+ assert nbytes(a) == 12
+ 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
+ assert index_at(arr, *args) == ret
+ assert index_at_t(arr, *args) == ret
+ assert offset_at(arr, *args) == ret * arr.dtype.itemsize
+ 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)
+ for func in (index_at, index_at_t, offset_at, offset_at_t, data, data_t,
+ mutate_data, mutate_data_t):
+ with pytest.raises(IndexError) as excinfo:
+ func(arr, 1, 2, 3)
+ assert str(excinfo.value) == 'too many indices for an array: 3 (ndim = 2)'
+
+
+@pytest.requires_numpy
+@pytest.mark.parametrize('args, ret',
+ [([], [1, 2, 3, 4, 5, 6]),
+ ([1], [4, 5, 6]),
+ ([0, 1], [2, 3, 4, 5, 6]),
+ ([1, 2], [6])])
+def test_data(arr, args, ret):
+ from pybind11_tests.array import data, data_t
+ assert all(data_t(arr, *args) == ret)
+ assert all(data(arr, *args)[::2] == ret)
+ assert all(data(arr, *args)[1::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:
+ 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
+ for func in at_t, mutate_at_t:
+ with pytest.raises(IndexError) as excinfo:
+ func(arr, *([0] * 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
+
+ assert at_t(arr, 0, 2) == 3
+ assert at_t(arr, 1, 0) == 4
+
+ assert all(mutate_at_t(arr, 0, 2).ravel() == [1, 2, 4, 4, 5, 6])
+ 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
+
+ assert all(mutate_data(arr).ravel() == [2, 4, 6, 8, 10, 12])
+ assert all(mutate_data(arr).ravel() == [4, 8, 12, 16, 20, 24])
+ assert all(mutate_data(arr, 1).ravel() == [4, 8, 12, 32, 40, 48])
+ assert all(mutate_data(arr, 0, 1).ravel() == [4, 16, 24, 64, 80, 96])
+ assert all(mutate_data(arr, 1, 2).ravel() == [4, 16, 24, 64, 80, 192])
+
+ assert all(mutate_data_t(arr).ravel() == [5, 17, 25, 65, 81, 193])
+ assert all(mutate_data_t(arr).ravel() == [6, 18, 26, 66, 82, 194])
+ assert all(mutate_data_t(arr, 1).ravel() == [6, 18, 26, 67, 83, 195])
+ assert all(mutate_data_t(arr, 0, 1).ravel() == [6, 19, 27, 68, 84, 196])
+ 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)
+ funcs = (index_at, index_at_t, data, data_t,
+ mutate_data, mutate_data_t, at_t, mutate_at_t)
+ for func in funcs:
+ with pytest.raises(IndexError) as excinfo:
+ func(arr, 2, 0)
+ assert str(excinfo.value) == 'index 2 is out of bounds for axis 0 with size 2'
+ with pytest.raises(IndexError) as excinfo:
+ func(arr, 0, 4)
+ assert str(excinfo.value) == 'index 4 is out of bounds for axis 1 with size 3'
+
+
+@pytest.requires_numpy
+def test_make_c_f_array():
+ from pybind11_tests.array import (
+ make_c_array, make_f_array
+ )
+ assert make_c_array().flags.c_contiguous
+ assert not make_c_array().flags.f_contiguous
+ assert make_f_array().flags.f_contiguous
+ 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):
+ assert a is not b
+ assert a.__array_interface__['data'][0] == b.__array_interface__['data'][0]
+ assert a.shape == b.shape
+ assert a.strides == b.strides
+ assert a.flags.c_contiguous == b.flags.c_contiguous
+ assert a.flags.f_contiguous == b.flags.f_contiguous
+ assert a.flags.writeable == b.flags.writeable
+ assert a.flags.aligned == b.flags.aligned
+ assert a.flags.updateifcopy == b.flags.updateifcopy
+ assert np.all(a == b)
+ assert not b.flags.owndata
+ assert b.base is a
+ if a.flags.writeable and a.ndim == 2:
+ a[0, 0] = 1234
+ assert b[0, 0] == 1234
+
+ a1 = np.array([1, 2], dtype=np.int16)
+ assert a1.flags.owndata and a1.base is None
+ a2 = wrap(a1)
+ assert_references(a1, a2)
+
+ a1 = np.array([[1, 2], [3, 4]], dtype=np.float32, order='F')
+ assert a1.flags.owndata and a1.base is None
+ a2 = wrap(a1)
+ assert_references(a1, a2)
+
+ a1 = np.array([[1, 2], [3, 4]], dtype=np.float32, order='C')
+ a1.flags.writeable = False
+ a2 = wrap(a1)
+ assert_references(a1, a2)
+
+ a1 = np.random.random((4, 4, 4))
+ a2 = wrap(a1)
+ assert_references(a1, a2)
+
+ a1 = a1.transpose()
+ a2 = wrap(a1)
+ assert_references(a1, a2)
+
+ a1 = a1.diagonal()
+ a2 = wrap(a1)
+ assert_references(a1, a2)
+
+
+@pytest.requires_numpy
+def test_numpy_view(capture):
+ from pybind11_tests.array import ArrayClass
+ with capture:
+ ac = ArrayClass()
+ ac_view_1 = ac.numpy_view()
+ ac_view_2 = ac.numpy_view()
+ assert np.all(ac_view_1 == np.array([1, 2], dtype=np.int32))
+ del ac
+ gc.collect()
+ assert capture == """
+ ArrayClass()
+ ArrayClass::numpy_view()
+ ArrayClass::numpy_view()
+ """
+ ac_view_1[0] = 4
+ ac_view_1[1] = 3
+ assert ac_view_2[0] == 4
+ assert ac_view_2[1] == 3
+ with capture:
+ del ac_view_1
+ del ac_view_2
+ gc.collect()
+ assert capture == """
+ ~ArrayClass()
+ """
+
+
+@pytest.requires_numpy
+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
+
+ assert isinstance_untyped(np.array([1, 2, 3]), "not an array")
+ 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
+
+ defaults = default_constructors()
+ for a in defaults.values():
+ assert a.size == 0
+ assert defaults["array"].dtype == np.array([]).dtype
+ assert defaults["array_t<int32>"].dtype == np.int32
+ assert defaults["array_t<double>"].dtype == np.float64
+
+ results = converting_constructors([1, 2, 3])
+ for a in results.values():
+ np.testing.assert_array_equal(a, [1, 2, 3])
+ assert results["array"].dtype == np.int_
+ assert results["array_t<int32>"].dtype == np.int32
+ assert results["array_t<double>"].dtype == np.float64
diff --git a/ext/pybind11/tests/test_numpy_dtypes.cpp b/ext/pybind11/tests/test_numpy_dtypes.cpp
new file mode 100644
index 000000000..3894f6a30
--- /dev/null
+++ b/ext/pybind11/tests/test_numpy_dtypes.cpp
@@ -0,0 +1,363 @@
+/*
+ tests/test_numpy_dtypes.cpp -- Structured and compound NumPy dtypes
+
+ Copyright (c) 2016 Ivan Smirnov
+
+ All rights reserved. Use of this source code is governed by a
+ BSD-style license that can be found in the LICENSE file.
+*/
+
+#include "pybind11_tests.h"
+#include <pybind11/numpy.h>
+
+#ifdef __GNUC__
+#define PYBIND11_PACKED(cls) cls __attribute__((__packed__))
+#else
+#define PYBIND11_PACKED(cls) __pragma(pack(push, 1)) cls __pragma(pack(pop))
+#endif
+
+namespace py = pybind11;
+
+struct SimpleStruct {
+ bool x;
+ uint32_t y;
+ float z;
+};
+
+std::ostream& operator<<(std::ostream& os, const SimpleStruct& v) {
+ return os << "s:" << v.x << "," << v.y << "," << v.z;
+}
+
+PYBIND11_PACKED(struct PackedStruct {
+ bool x;
+ uint32_t y;
+ float z;
+});
+
+std::ostream& operator<<(std::ostream& os, const PackedStruct& v) {
+ return os << "p:" << v.x << "," << v.y << "," << v.z;
+}
+
+PYBIND11_PACKED(struct NestedStruct {
+ SimpleStruct a;
+ PackedStruct b;
+});
+
+std::ostream& operator<<(std::ostream& os, const NestedStruct& v) {
+ return os << "n:a=" << v.a << ";b=" << v.b;
+}
+
+struct PartialStruct {
+ bool x;
+ uint32_t y;
+ float z;
+ uint64_t dummy2;
+};
+
+struct PartialNestedStruct {
+ uint64_t dummy1;
+ PartialStruct a;
+ uint64_t dummy2;
+};
+
+struct UnboundStruct { };
+
+struct StringStruct {
+ char a[3];
+ std::array<char, 3> b;
+};
+
+PYBIND11_PACKED(struct StructWithUglyNames {
+ int8_t __x__;
+ uint64_t __y__;
+});
+
+enum class E1 : int64_t { A = -1, B = 1 };
+enum E2 : uint8_t { X = 1, Y = 2 };
+
+PYBIND11_PACKED(struct EnumStruct {
+ E1 e1;
+ E2 e2;
+});
+
+std::ostream& operator<<(std::ostream& os, const StringStruct& v) {
+ os << "a='";
+ for (size_t i = 0; i < 3 && v.a[i]; i++) os << v.a[i];
+ os << "',b='";
+ for (size_t i = 0; i < 3 && v.b[i]; i++) os << v.b[i];
+ return os << "'";
+}
+
+std::ostream& operator<<(std::ostream& os, const EnumStruct& v) {
+ return os << "e1=" << (v.e1 == E1::A ? "A" : "B") << ",e2=" << (v.e2 == E2::X ? "X" : "Y");
+}
+
+template <typename T>
+py::array mkarray_via_buffer(size_t n) {
+ return py::array(py::buffer_info(nullptr, sizeof(T),
+ py::format_descriptor<T>::format(),
+ 1, { n }, { sizeof(T) }));
+}
+
+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;
+ }
+ return arr;
+}
+
+std::string get_format_unbound() {
+ return py::format_descriptor<UnboundStruct>::format();
+}
+
+py::array_t<NestedStruct, 0> create_nested(size_t n) {
+ auto arr = mkarray_via_buffer<NestedStruct>(n);
+ auto req = arr.request();
+ auto ptr = static_cast<NestedStruct*>(req.ptr);
+ for (size_t i = 0; i < n; i++) {
+ 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;
+ }
+ return arr;
+}
+
+py::array_t<PartialNestedStruct, 0> create_partial_nested(size_t n) {
+ auto arr = mkarray_via_buffer<PartialNestedStruct>(n);
+ auto req = arr.request();
+ auto ptr = static_cast<PartialNestedStruct*>(req.ptr);
+ for (size_t i = 0; i < n; i++) {
+ ptr[i].a.x = i % 2 != 0; ptr[i].a.y = (uint32_t) i; ptr[i].a.z = (float) i * 1.5f;
+ }
+ return arr;
+}
+
+py::array_t<StringStruct, 0> create_string_array(bool non_empty) {
+ auto arr = mkarray_via_buffer<StringStruct>(non_empty ? 4 : 0);
+ if (non_empty) {
+ auto req = arr.request();
+ auto ptr = static_cast<StringStruct*>(req.ptr);
+ for (size_t i = 0; i < req.size * req.itemsize; i++)
+ static_cast<char*>(req.ptr)[i] = 0;
+ ptr[1].a[0] = 'a'; ptr[1].b[0] = 'a';
+ ptr[2].a[0] = 'a'; ptr[2].b[0] = 'a';
+ ptr[3].a[0] = 'a'; ptr[3].b[0] = 'a';
+
+ ptr[2].a[1] = 'b'; ptr[2].b[1] = 'b';
+ ptr[3].a[1] = 'b'; ptr[3].b[1] = 'b';
+
+ ptr[3].a[2] = 'c'; ptr[3].b[2] = 'c';
+ }
+ return arr;
+}
+
+py::array_t<EnumStruct, 0> create_enum_array(size_t n) {
+ auto arr = mkarray_via_buffer<EnumStruct>(n);
+ auto ptr = (EnumStruct *) arr.mutable_data();
+ for (size_t i = 0; i < n; i++) {
+ ptr[i].e1 = static_cast<E1>(-1 + ((int) i % 2) * 2);
+ ptr[i].e2 = static_cast<E2>(1 + (i % 2));
+ }
+ return arr;
+}
+
+template <typename S>
+py::list print_recarray(py::array_t<S, 0> arr) {
+ const auto req = arr.request();
+ const auto ptr = static_cast<S*>(req.ptr);
+ auto l = py::list();
+ for (size_t i = 0; i < req.size; i++) {
+ std::stringstream ss;
+ ss << ptr[i];
+ l.append(py::str(ss.str()));
+ }
+ return l;
+}
+
+py::list print_format_descriptors() {
+ const auto fmts = {
+ py::format_descriptor<SimpleStruct>::format(),
+ py::format_descriptor<PackedStruct>::format(),
+ py::format_descriptor<NestedStruct>::format(),
+ py::format_descriptor<PartialStruct>::format(),
+ py::format_descriptor<PartialNestedStruct>::format(),
+ py::format_descriptor<StringStruct>::format(),
+ py::format_descriptor<EnumStruct>::format()
+ };
+ auto l = py::list();
+ for (const auto &fmt : fmts) {
+ l.append(py::cast(fmt));
+ }
+ return l;
+}
+
+py::list print_dtypes() {
+ const auto dtypes = {
+ py::str(py::dtype::of<SimpleStruct>()),
+ py::str(py::dtype::of<PackedStruct>()),
+ py::str(py::dtype::of<NestedStruct>()),
+ py::str(py::dtype::of<PartialStruct>()),
+ py::str(py::dtype::of<PartialNestedStruct>()),
+ py::str(py::dtype::of<StringStruct>()),
+ py::str(py::dtype::of<EnumStruct>()),
+ py::str(py::dtype::of<StructWithUglyNames>())
+ };
+ auto l = py::list();
+ for (const auto &s : dtypes) {
+ l.append(s);
+ }
+ return l;
+}
+
+py::array_t<int32_t, 0> test_array_ctors(int i) {
+ using arr_t = py::array_t<int32_t, 0>;
+
+ std::vector<int32_t> data { 1, 2, 3, 4, 5, 6 };
+ std::vector<size_t> shape { 3, 2 };
+ std::vector<size_t> strides { 8, 4 };
+
+ auto ptr = data.data();
+ auto vptr = (void *) ptr;
+ auto dtype = py::dtype("int32");
+
+ py::buffer_info buf_ndim1(vptr, 4, "i", 6);
+ py::buffer_info buf_ndim1_null(nullptr, 4, "i", 6);
+ py::buffer_info buf_ndim2(vptr, 4, "i", 2, shape, strides);
+ py::buffer_info buf_ndim2_null(nullptr, 4, "i", 2, shape, strides);
+
+ auto fill = [](py::array arr) {
+ auto req = arr.request();
+ for (int i = 0; i < 6; i++) ((int32_t *) req.ptr)[i] = i + 1;
+ return arr;
+ };
+
+ switch (i) {
+ // shape: (3, 2)
+ case 10: return arr_t(shape, strides, ptr);
+ case 11: return py::array(shape, strides, ptr);
+ case 12: return py::array(dtype, shape, strides, vptr);
+ case 13: return arr_t(shape, ptr);
+ case 14: return py::array(shape, ptr);
+ case 15: return py::array(dtype, shape, vptr);
+ case 16: return arr_t(buf_ndim2);
+ case 17: return py::array(buf_ndim2);
+ // shape: (3, 2) - post-fill
+ case 20: return fill(arr_t(shape, strides));
+ case 21: return py::array(shape, strides, ptr); // can't have nullptr due to templated ctor
+ case 22: return fill(py::array(dtype, shape, strides));
+ case 23: return fill(arr_t(shape));
+ case 24: return py::array(shape, ptr); // can't have nullptr due to templated ctor
+ case 25: return fill(py::array(dtype, shape));
+ case 26: return fill(arr_t(buf_ndim2_null));
+ case 27: return fill(py::array(buf_ndim2_null));
+ // shape: (6, )
+ case 30: return arr_t(6, ptr);
+ case 31: return py::array(6, ptr);
+ case 32: return py::array(dtype, 6, vptr);
+ case 33: return arr_t(buf_ndim1);
+ case 34: return py::array(buf_ndim1);
+ // shape: (6, )
+ case 40: return fill(arr_t(6));
+ case 41: return py::array(6, ptr); // can't have nullptr due to templated ctor
+ case 42: return fill(py::array(dtype, 6));
+ case 43: return fill(arr_t(buf_ndim1_null));
+ case 44: return fill(py::array(buf_ndim1_null));
+ }
+ return arr_t();
+}
+
+py::list test_dtype_ctors() {
+ py::list list;
+ list.append(py::dtype("int32"));
+ list.append(py::dtype(std::string("float64")));
+ list.append(py::dtype::from_args(py::str("bool")));
+ py::list names, offsets, formats;
+ py::dict dict;
+ names.append(py::str("a")); names.append(py::str("b")); dict["names"] = names;
+ offsets.append(py::int_(1)); offsets.append(py::int_(10)); dict["offsets"] = offsets;
+ formats.append(py::dtype("int32")); formats.append(py::dtype("float64")); dict["formats"] = formats;
+ dict["itemsize"] = py::int_(20);
+ list.append(py::dtype::from_args(dict));
+ list.append(py::dtype(names, formats, offsets, 20));
+ list.append(py::dtype(py::buffer_info((void *) 0, sizeof(unsigned int), "I", 1)));
+ list.append(py::dtype(py::buffer_info((void *) 0, 0, "T{i:a:f:b:}", 1)));
+ return list;
+}
+
+struct TrailingPaddingStruct {
+ int32_t a;
+ char b;
+};
+
+py::dtype trailing_padding_dtype() {
+ return py::dtype::of<TrailingPaddingStruct>();
+}
+
+py::dtype buffer_to_dtype(py::buffer& buf) {
+ return py::dtype(buf.request());
+}
+
+py::list test_dtype_methods() {
+ py::list list;
+ auto dt1 = py::dtype::of<int32_t>();
+ auto dt2 = py::dtype::of<SimpleStruct>();
+ list.append(dt1); list.append(dt2);
+ list.append(py::bool_(dt1.has_fields())); list.append(py::bool_(dt2.has_fields()));
+ list.append(py::int_(dt1.itemsize())); list.append(py::int_(dt2.itemsize()));
+ return list;
+}
+
+test_initializer numpy_dtypes([](py::module &m) {
+ try {
+ py::module::import("numpy");
+ } catch (...) {
+ return;
+ }
+
+ // typeinfo may be registered before the dtype descriptor for scalar casts to work...
+ py::class_<SimpleStruct>(m, "SimpleStruct");
+
+ PYBIND11_NUMPY_DTYPE(SimpleStruct, x, y, z);
+ PYBIND11_NUMPY_DTYPE(PackedStruct, x, y, z);
+ PYBIND11_NUMPY_DTYPE(NestedStruct, a, b);
+ PYBIND11_NUMPY_DTYPE(PartialStruct, x, y, z);
+ PYBIND11_NUMPY_DTYPE(PartialNestedStruct, a);
+ PYBIND11_NUMPY_DTYPE(StringStruct, a, b);
+ PYBIND11_NUMPY_DTYPE(EnumStruct, e1, e2);
+ PYBIND11_NUMPY_DTYPE(TrailingPaddingStruct, a, b);
+
+ // ... or after
+ py::class_<PackedStruct>(m, "PackedStruct");
+
+ PYBIND11_NUMPY_DTYPE_EX(StructWithUglyNames, __x__, "x", __y__, "y");
+
+ m.def("create_rec_simple", &create_recarray<SimpleStruct>);
+ m.def("create_rec_packed", &create_recarray<PackedStruct>);
+ m.def("create_rec_nested", &create_nested);
+ m.def("create_rec_partial", &create_recarray<PartialStruct>);
+ m.def("create_rec_partial_nested", &create_partial_nested);
+ m.def("print_format_descriptors", &print_format_descriptors);
+ m.def("print_rec_simple", &print_recarray<SimpleStruct>);
+ m.def("print_rec_packed", &print_recarray<PackedStruct>);
+ m.def("print_rec_nested", &print_recarray<NestedStruct>);
+ m.def("print_dtypes", &print_dtypes);
+ m.def("get_format_unbound", &get_format_unbound);
+ m.def("create_string_array", &create_string_array);
+ m.def("print_string_array", &print_recarray<StringStruct>);
+ m.def("create_enum_array", &create_enum_array);
+ m.def("print_enum_array", &print_recarray<EnumStruct>);
+ 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("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); });
+});
+
+#undef PYBIND11_PACKED
diff --git a/ext/pybind11/tests/test_numpy_dtypes.py b/ext/pybind11/tests/test_numpy_dtypes.py
new file mode 100644
index 000000000..52ebe0ede
--- /dev/null
+++ b/ext/pybind11/tests/test_numpy_dtypes.py
@@ -0,0 +1,225 @@
+import re
+import pytest
+
+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]})
+
+
+@pytest.fixture(scope='module')
+def packed_dtype():
+ return np.dtype([('x', '?'), ('y', 'u4'), ('z', 'f4')])
+
+
+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
+
+ with pytest.raises(RuntimeError) as excinfo:
+ get_format_unbound()
+ assert re.match('^NumPy type info missing for .*UnboundStruct.*$', str(excinfo.value))
+
+ 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}",
+ "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)
+
+ 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}",
+ "[('a', 'S3'), ('b', 'S3')]",
+ "[('e1', '<i8'), ('e2', 'u1')]",
+ "[('x', 'i1'), ('y', '<u8')]"
+ ]
+
+ d1 = np.dtype({'names': ['a', 'b'], 'formats': ['int32', 'float64'],
+ 'offsets': [1, 10], 'itemsize': 20})
+ d2 = np.dtype([('a', 'i4'), ('b', 'f4')])
+ assert test_dtype_ctors() == [np.dtype('int32'), np.dtype('float64'),
+ np.dtype('bool'), d1, d1, np.dtype('uint32'), d2]
+
+ assert test_dtype_methods() == [np.dtype('int32'), simple_dtype, False, True,
+ np.dtype('int32').itemsize, simple_dtype.itemsize]
+
+ 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)]
+
+ for func, dtype in [(create_rec_simple, simple_dtype), (create_rec_packed, packed_dtype)]:
+ arr = func(0)
+ assert arr.dtype == dtype
+ assert_equal(arr, [], simple_dtype)
+ assert_equal(arr, [], packed_dtype)
+
+ arr = func(3)
+ assert arr.dtype == dtype
+ assert_equal(arr, elements, simple_dtype)
+ assert_equal(arr, elements, packed_dtype)
+
+ if dtype == simple_dtype:
+ assert print_rec_simple(arr) == [
+ "s:0,0,0",
+ "s:1,1,1.5",
+ "s:0,2,3"
+ ]
+ else:
+ assert print_rec_packed(arr) == [
+ "p:0,0,0",
+ "p:1,1,1.5",
+ "p:0,2,3"
+ ]
+
+ nested_dtype = np.dtype([('a', simple_dtype), ('b', packed_dtype)])
+
+ arr = create_rec_nested(0)
+ assert arr.dtype == nested_dtype
+ assert_equal(arr, [], nested_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 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"
+ ]
+
+ arr = create_rec_partial(3)
+ assert str(arr.dtype) == \
+ "{'names':['x','y','z'], 'formats':['?','<u4','<f4'], 'offsets':[0,4,8], 'itemsize':24}"
+ partial_dtype = arr.dtype
+ assert '' not in arr.dtype.fields
+ assert partial_dtype.itemsize > simple_dtype.itemsize
+ assert_equal(arr, elements, simple_dtype)
+ assert_equal(arr, elements, packed_dtype)
+
+ arr = 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 '' 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
+
+ data = np.arange(1, 7, dtype='int32')
+ for i in range(8):
+ np.testing.assert_array_equal(test_array_ctors(10 + i), data.reshape((3, 2)))
+ np.testing.assert_array_equal(test_array_ctors(20 + i), data.reshape((3, 2)))
+ for i in range(5):
+ np.testing.assert_array_equal(test_array_ctors(30 + i), data)
+ 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
+
+ arr = create_string_array(True)
+ assert str(arr.dtype) == "[('a', 'S3'), ('b', 'S3')]"
+ assert print_string_array(arr) == [
+ "a='',b=''",
+ "a='a',b='a'",
+ "a='ab',b='ab'",
+ "a='abc',b='abc'"
+ ]
+ dtype = arr.dtype
+ assert arr['a'].tolist() == [b'', b'a', b'ab', b'abc']
+ assert arr['b'].tolist() == [b'', b'a', b'ab', b'abc']
+ arr = create_string_array(False)
+ assert dtype == arr.dtype
+
+
+@pytest.requires_numpy
+def test_enum_array():
+ from pybind11_tests import create_enum_array, print_enum_array
+
+ arr = create_enum_array(3)
+ dtype = arr.dtype
+ assert dtype == np.dtype([('e1', '<i8'), ('e2', 'u1')])
+ assert print_enum_array(arr) == [
+ "e1=A,e2=X",
+ "e1=B,e2=Y",
+ "e1=A,e2=X"
+ ]
+ assert arr['e1'].tolist() == [-1, 1, -1]
+ assert arr['e2'].tolist() == [1, 2, 1]
+ assert 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,
+ create_rec_nested, f_nested,
+ create_enum_array)
+
+ n = 3
+ arrays = [create_rec_simple(n), create_rec_packed(n),
+ create_rec_nested(n), create_enum_array(n)]
+ funcs = [f_simple, f_packed, f_nested]
+
+ for i, func in enumerate(funcs):
+ for j, arr in enumerate(arrays):
+ if i == j and i < 2:
+ assert [func(arr[k]) for k in range(n)] == [k * 10 for k in range(n)]
+ else:
+ with pytest.raises(TypeError) as excinfo:
+ func(arr[0])
+ assert 'incompatible function arguments' in str(excinfo.value)
+
+
+@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)
diff --git a/ext/pybind11/tests/test_numpy_vectorize.cpp b/ext/pybind11/tests/test_numpy_vectorize.cpp
new file mode 100644
index 000000000..6d94db2a1
--- /dev/null
+++ b/ext/pybind11/tests/test_numpy_vectorize.cpp
@@ -0,0 +1,41 @@
+/*
+ tests/test_numpy_vectorize.cpp -- auto-vectorize functions over NumPy array
+ arguments
+
+ Copyright (c) 2016 Wenzel Jakob <wenzel.jakob@epfl.ch>
+
+ All rights reserved. Use of this source code is governed by a
+ BSD-style license that can be found in the LICENSE file.
+*/
+
+#include "pybind11_tests.h"
+#include <pybind11/numpy.h>
+
+double my_func(int x, float y, double z) {
+ py::print("my_func(x:int={}, y:float={:.0f}, z:float={:.0f})"_s.format(x, y, z));
+ return (float) x*y*z;
+}
+
+std::complex<double> my_func3(std::complex<double> c) {
+ return c * std::complex<double>(2.f);
+}
+
+test_initializer numpy_vectorize([](py::module &m) {
+ // Vectorize all arguments of a function (though non-vector arguments are also allowed)
+ m.def("vectorized_func", py::vectorize(my_func));
+
+ // Vectorize a lambda function with a capture object (e.g. to exclude some arguments from the vectorization)
+ m.def("vectorized_func2",
+ [](py::array_t<int> x, py::array_t<float> y, float z) {
+ return py::vectorize([z](int x, float y) { return my_func(x, y, z); })(x, y);
+ }
+ );
+
+ // Vectorize a complex-valued function
+ m.def("vectorized_func3", py::vectorize(my_func3));
+
+ /// Numpy function which only accepts specific data types
+ m.def("selective_func", [](py::array_t<int, py::array::c_style>) { return "Int branch taken."; });
+ m.def("selective_func", [](py::array_t<float, py::array::c_style>) { return "Float branch taken."; });
+ m.def("selective_func", [](py::array_t<std::complex<float>, py::array::c_style>) { return "Complex float branch taken."; });
+});
diff --git a/ext/pybind11/tests/test_numpy_vectorize.py b/ext/pybind11/tests/test_numpy_vectorize.py
new file mode 100644
index 000000000..718646efa
--- /dev/null
+++ b/ext/pybind11/tests/test_numpy_vectorize.py
@@ -0,0 +1,76 @@
+import pytest
+
+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
+
+ assert np.isclose(vectorized_func3(np.array(3 + 7j)), [6 + 14j])
+
+ for f in [vectorized_func, vectorized_func2]:
+ with capture:
+ assert np.isclose(f(1, 2, 3), 6)
+ assert capture == "my_func(x:int=1, y:float=2, z:float=3)"
+ with capture:
+ assert np.isclose(f(np.array(1), np.array(2), 3), 6)
+ assert capture == "my_func(x:int=1, y:float=2, z:float=3)"
+ with capture:
+ assert np.allclose(f(np.array([1, 3]), np.array([2, 4]), 3), [6, 36])
+ assert capture == """
+ my_func(x:int=1, y:float=2, z:float=3)
+ my_func(x:int=3, y:float=4, z:float=3)
+ """
+ with capture:
+ a, b, c = np.array([[1, 3, 5], [7, 9, 11]]), np.array([[2, 4, 6], [8, 10, 12]]), 3
+ assert np.allclose(f(a, b, c), a * b * c)
+ assert capture == """
+ my_func(x:int=1, y:float=2, z:float=3)
+ my_func(x:int=3, y:float=4, z:float=3)
+ my_func(x:int=5, y:float=6, z:float=3)
+ my_func(x:int=7, y:float=8, z:float=3)
+ my_func(x:int=9, y:float=10, z:float=3)
+ my_func(x:int=11, y:float=12, z:float=3)
+ """
+ with capture:
+ a, b, c = np.array([[1, 2, 3], [4, 5, 6]]), np.array([2, 3, 4]), 2
+ assert np.allclose(f(a, b, c), a * b * c)
+ assert capture == """
+ my_func(x:int=1, y:float=2, z:float=2)
+ my_func(x:int=2, y:float=3, z:float=2)
+ my_func(x:int=3, y:float=4, z:float=2)
+ my_func(x:int=4, y:float=2, z:float=2)
+ my_func(x:int=5, y:float=3, z:float=2)
+ my_func(x:int=6, y:float=4, z:float=2)
+ """
+ with capture:
+ a, b, c = np.array([[1, 2, 3], [4, 5, 6]]), np.array([[2], [3]]), 2
+ assert np.allclose(f(a, b, c), a * b * c)
+ assert capture == """
+ my_func(x:int=1, y:float=2, z:float=2)
+ my_func(x:int=2, y:float=2, z:float=2)
+ my_func(x:int=3, y:float=2, z:float=2)
+ my_func(x:int=4, y:float=3, z:float=2)
+ my_func(x:int=5, y:float=3, z:float=2)
+ my_func(x:int=6, y:float=3, z:float=2)
+ """
+
+
+@pytest.requires_numpy
+def test_type_selection():
+ from pybind11_tests import selective_func
+
+ assert selective_func(np.array([1], dtype=np.int32)) == "Int branch taken."
+ assert selective_func(np.array([1.0], dtype=np.float32)) == "Float branch taken."
+ 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
+ """ # noqa: E501 line too long
diff --git a/ext/pybind11/tests/test_opaque_types.cpp b/ext/pybind11/tests/test_opaque_types.cpp
new file mode 100644
index 000000000..54f4dc7a5
--- /dev/null
+++ b/ext/pybind11/tests/test_opaque_types.cpp
@@ -0,0 +1,62 @@
+/*
+ tests/test_opaque_types.cpp -- opaque types, passing void pointers
+
+ Copyright (c) 2016 Wenzel Jakob <wenzel.jakob@epfl.ch>
+
+ All rights reserved. Use of this source code is governed by a
+ BSD-style license that can be found in the LICENSE file.
+*/
+
+#include "pybind11_tests.h"
+#include <pybind11/stl.h>
+#include <vector>
+
+typedef std::vector<std::string> StringList;
+
+class ClassWithSTLVecProperty {
+public:
+ StringList stringList;
+};
+
+/* IMPORTANT: Disable internal pybind11 translation mechanisms for STL data structures */
+PYBIND11_MAKE_OPAQUE(StringList);
+
+test_initializer opaque_types([](py::module &m) {
+ py::class_<StringList>(m, "StringList")
+ .def(py::init<>())
+ .def("pop_back", &StringList::pop_back)
+ /* There are multiple versions of push_back(), etc. Select the right ones. */
+ .def("push_back", (void (StringList::*)(const std::string &)) &StringList::push_back)
+ .def("back", (std::string &(StringList::*)()) &StringList::back)
+ .def("__len__", [](const StringList &v) { return v.size(); })
+ .def("__iter__", [](StringList &v) {
+ return py::make_iterator(v.begin(), v.end());
+ }, py::keep_alive<0, 1>());
+
+ py::class_<ClassWithSTLVecProperty>(m, "ClassWithSTLVecProperty")
+ .def(py::init<>())
+ .def_readwrite("stringList", &ClassWithSTLVecProperty::stringList);
+
+ m.def("print_opaque_list", [](const StringList &l) {
+ std::string ret = "Opaque list: [";
+ bool first = true;
+ for (auto entry : l) {
+ if (!first)
+ ret += ", ";
+ ret += entry;
+ first = false;
+ }
+ return ret + "]";
+ });
+
+ m.def("return_void_ptr", []() { return (void *) 0x1234; });
+ m.def("get_void_ptr_value", [](void *ptr) { return reinterpret_cast<std::intptr_t>(ptr); });
+ m.def("return_null_str", []() { return (char *) nullptr; });
+ m.def("get_null_str_value", [](char *ptr) { return reinterpret_cast<std::intptr_t>(ptr); });
+
+ m.def("return_unique_ptr", []() -> std::unique_ptr<StringList> {
+ StringList *result = new StringList();
+ result->push_back("some value");
+ return std::unique_ptr<StringList>(result);
+ });
+});
diff --git a/ext/pybind11/tests/test_opaque_types.py b/ext/pybind11/tests/test_opaque_types.py
new file mode 100644
index 000000000..7781943b4
--- /dev/null
+++ b/ext/pybind11/tests/test_opaque_types.py
@@ -0,0 +1,49 @@
+import pytest
+
+
+def test_string_list():
+ from pybind11_tests import StringList, ClassWithSTLVecProperty, print_opaque_list
+
+ l = StringList()
+ l.push_back("Element 1")
+ l.push_back("Element 2")
+ assert print_opaque_list(l) == "Opaque list: [Element 1, Element 2]"
+ assert l.back() == "Element 2"
+
+ for i, k in enumerate(l, start=1):
+ assert k == "Element {}".format(i)
+ l.pop_back()
+ assert print_opaque_list(l) == "Opaque list: [Element 1]"
+
+ cvp = ClassWithSTLVecProperty()
+ assert print_opaque_list(cvp.stringList) == "Opaque list: []"
+
+ cvp.stringList = l
+ cvp.stringList.push_back("Element 3")
+ assert print_opaque_list(cvp.stringList) == "Opaque list: [Element 1, Element 3]"
+
+
+def test_pointers(msg):
+ from pybind11_tests import (return_void_ptr, get_void_ptr_value, ExampleMandA,
+ print_opaque_list, return_null_str, get_null_str_value,
+ return_unique_ptr, ConstructorStats)
+
+ 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
+
+ with pytest.raises(TypeError) as excinfo:
+ get_void_ptr_value([1, 2, 3]) # This should not work
+ assert msg(excinfo.value) == """
+ get_void_ptr_value(): incompatible function arguments. The following argument types are supported:
+ 1. (arg0: capsule) -> int
+
+ Invoked with: [1, 2, 3]
+ """ # noqa: E501 line too long
+
+ assert return_null_str() is None
+ assert get_null_str_value(return_null_str()) is not None
+
+ ptr = return_unique_ptr()
+ assert "StringList" in repr(ptr)
+ assert print_opaque_list(ptr) == "Opaque list: [some value]"
diff --git a/ext/pybind11/tests/test_operator_overloading.cpp b/ext/pybind11/tests/test_operator_overloading.cpp
new file mode 100644
index 000000000..93aea8010
--- /dev/null
+++ b/ext/pybind11/tests/test_operator_overloading.cpp
@@ -0,0 +1,76 @@
+/*
+ tests/test_operator_overloading.cpp -- operator overloading
+
+ Copyright (c) 2016 Wenzel Jakob <wenzel.jakob@epfl.ch>
+
+ All rights reserved. Use of this source code is governed by a
+ BSD-style license that can be found in the LICENSE file.
+*/
+
+#include "pybind11_tests.h"
+#include "constructor_stats.h"
+#include <pybind11/operators.h>
+
+class Vector2 {
+public:
+ Vector2(float x, float y) : x(x), y(y) { print_created(this, toString()); }
+ Vector2(const Vector2 &v) : x(v.x), y(v.y) { print_copy_created(this); }
+ Vector2(Vector2 &&v) : x(v.x), y(v.y) { print_move_created(this); v.x = v.y = 0; }
+ ~Vector2() { print_destroyed(this); }
+
+ std::string toString() const {
+ return "[" + std::to_string(x) + ", " + std::to_string(y) + "]";
+ }
+
+ void operator=(const Vector2 &v) {
+ print_copy_assigned(this);
+ x = v.x;
+ y = v.y;
+ }
+
+ void operator=(Vector2 &&v) {
+ print_move_assigned(this);
+ x = v.x; y = v.y; v.x = v.y = 0;
+ }
+
+ Vector2 operator+(const Vector2 &v) const { return Vector2(x + v.x, y + v.y); }
+ Vector2 operator-(const Vector2 &v) const { return Vector2(x - v.x, y - v.y); }
+ Vector2 operator-(float value) const { return Vector2(x - value, y - value); }
+ Vector2 operator+(float value) const { return Vector2(x + value, y + value); }
+ Vector2 operator*(float value) const { return Vector2(x * value, y * value); }
+ Vector2 operator/(float value) const { return Vector2(x / value, y / value); }
+ Vector2& operator+=(const Vector2 &v) { x += v.x; y += v.y; return *this; }
+ Vector2& operator-=(const Vector2 &v) { x -= v.x; y -= v.y; return *this; }
+ Vector2& operator*=(float v) { x *= v; y *= v; return *this; }
+ Vector2& operator/=(float v) { x /= v; y /= v; return *this; }
+
+ friend Vector2 operator+(float f, const Vector2 &v) { return Vector2(f + v.x, f + v.y); }
+ friend Vector2 operator-(float f, const Vector2 &v) { return Vector2(f - v.x, f - v.y); }
+ friend Vector2 operator*(float f, const Vector2 &v) { return Vector2(f * v.x, f * v.y); }
+ friend Vector2 operator/(float f, const Vector2 &v) { return Vector2(f / v.x, f / v.y); }
+private:
+ float x, y;
+};
+
+test_initializer operator_overloading([](py::module &m) {
+ py::class_<Vector2>(m, "Vector2")
+ .def(py::init<float, float>())
+ .def(py::self + py::self)
+ .def(py::self + float())
+ .def(py::self - py::self)
+ .def(py::self - float())
+ .def(py::self * float())
+ .def(py::self / float())
+ .def(py::self += py::self)
+ .def(py::self -= py::self)
+ .def(py::self *= float())
+ .def(py::self /= float())
+ .def(float() + py::self)
+ .def(float() - py::self)
+ .def(float() * py::self)
+ .def(float() / py::self)
+ .def("__str__", &Vector2::toString)
+ ;
+
+ m.attr("Vector") = m.attr("Vector2");
+});
diff --git a/ext/pybind11/tests/test_operator_overloading.py b/ext/pybind11/tests/test_operator_overloading.py
new file mode 100644
index 000000000..e0d42391e
--- /dev/null
+++ b/ext/pybind11/tests/test_operator_overloading.py
@@ -0,0 +1,41 @@
+
+def test_operator_overloading():
+ from pybind11_tests import Vector2, Vector, ConstructorStats
+
+ v1 = Vector2(1, 2)
+ v2 = Vector(3, -1)
+ assert str(v1) == "[1.000000, 2.000000]"
+ assert str(v2) == "[3.000000, -1.000000]"
+
+ assert str(v1 + v2) == "[4.000000, 1.000000]"
+ assert str(v1 - v2) == "[-2.000000, 3.000000]"
+ assert str(v1 - 8) == "[-7.000000, -6.000000]"
+ assert str(v1 + 8) == "[9.000000, 10.000000]"
+ assert str(v1 * 8) == "[8.000000, 16.000000]"
+ assert str(v1 / 8) == "[0.125000, 0.250000]"
+ assert str(8 - v1) == "[7.000000, 6.000000]"
+ assert str(8 + v1) == "[9.000000, 10.000000]"
+ assert str(8 * v1) == "[8.000000, 16.000000]"
+ assert str(8 / v1) == "[8.000000, 4.000000]"
+
+ v1 += v2
+ v1 *= 2
+ assert str(v1) == "[8.000000, 2.000000]"
+
+ cstats = ConstructorStats.get(Vector2)
+ assert cstats.alive() == 2
+ del v1
+ assert cstats.alive() == 1
+ del v2
+ assert cstats.alive() == 0
+ assert cstats.values() == ['[1.000000, 2.000000]', '[3.000000, -1.000000]',
+ '[4.000000, 1.000000]', '[-2.000000, 3.000000]',
+ '[-7.000000, -6.000000]', '[9.000000, 10.000000]',
+ '[8.000000, 16.000000]', '[0.125000, 0.250000]',
+ '[7.000000, 6.000000]', '[9.000000, 10.000000]',
+ '[8.000000, 16.000000]', '[8.000000, 4.000000]']
+ assert cstats.default_constructions == 0
+ assert cstats.copy_constructions == 0
+ assert cstats.move_constructions >= 10
+ assert cstats.copy_assignments == 0
+ assert cstats.move_assignments == 0
diff --git a/ext/pybind11/tests/test_pickling.cpp b/ext/pybind11/tests/test_pickling.cpp
new file mode 100644
index 000000000..3941dc593
--- /dev/null
+++ b/ext/pybind11/tests/test_pickling.cpp
@@ -0,0 +1,81 @@
+/*
+ tests/test_pickling.cpp -- pickle support
+
+ Copyright (c) 2016 Wenzel Jakob <wenzel.jakob@epfl.ch>
+
+ All rights reserved. Use of this source code is governed by a
+ BSD-style license that can be found in the LICENSE file.
+*/
+
+#include "pybind11_tests.h"
+
+class Pickleable {
+public:
+ Pickleable(const std::string &value) : m_value(value) { }
+ const std::string &value() const { return m_value; }
+
+ void setExtra1(int extra1) { m_extra1 = extra1; }
+ void setExtra2(int extra2) { m_extra2 = extra2; }
+ int extra1() const { return m_extra1; }
+ int extra2() const { return m_extra2; }
+private:
+ std::string m_value;
+ int m_extra1 = 0;
+ int m_extra2 = 0;
+};
+
+class PickleableWithDict {
+public:
+ PickleableWithDict(const std::string &value) : value(value) { }
+
+ std::string value;
+ int extra;
+};
+
+test_initializer pickling([](py::module &m) {
+ py::class_<Pickleable>(m, "Pickleable")
+ .def(py::init<std::string>())
+ .def("value", &Pickleable::value)
+ .def("extra1", &Pickleable::extra1)
+ .def("extra2", &Pickleable::extra2)
+ .def("setExtra1", &Pickleable::setExtra1)
+ .def("setExtra2", &Pickleable::setExtra2)
+ // For details on the methods below, refer to
+ // http://docs.python.org/3/library/pickle.html#pickling-class-instances
+ .def("__getstate__", [](const Pickleable &p) {
+ /* Return a tuple that fully encodes the state of the object */
+ return py::make_tuple(p.value(), p.extra1(), p.extra2());
+ })
+ .def("__setstate__", [](Pickleable &p, py::tuple t) {
+ if (t.size() != 3)
+ throw std::runtime_error("Invalid state!");
+ /* Invoke the constructor (need to use in-place version) */
+ new (&p) Pickleable(t[0].cast<std::string>());
+
+ /* Assign any additional state */
+ p.setExtra1(t[1].cast<int>());
+ p.setExtra2(t[2].cast<int>());
+ });
+
+ py::class_<PickleableWithDict>(m, "PickleableWithDict", py::dynamic_attr())
+ .def(py::init<std::string>())
+ .def_readwrite("value", &PickleableWithDict::value)
+ .def_readwrite("extra", &PickleableWithDict::extra)
+ .def("__getstate__", [](py::object self) {
+ /* Also include __dict__ in state */
+ return py::make_tuple(self.attr("value"), self.attr("extra"), self.attr("__dict__"));
+ })
+ .def("__setstate__", [](py::object self, py::tuple t) {
+ if (t.size() != 3)
+ throw std::runtime_error("Invalid state!");
+ /* Cast and construct */
+ auto& p = self.cast<PickleableWithDict&>();
+ new (&p) Pickleable(t[0].cast<std::string>());
+
+ /* Assign C++ state */
+ p.extra = t[1].cast<int>();
+
+ /* Assign Python state */
+ self.attr("__dict__") = t[2];
+ });
+});
diff --git a/ext/pybind11/tests/test_pickling.py b/ext/pybind11/tests/test_pickling.py
new file mode 100644
index 000000000..5e62e1fcc
--- /dev/null
+++ b/ext/pybind11/tests/test_pickling.py
@@ -0,0 +1,32 @@
+try:
+ import cPickle as pickle # Use cPickle on Python 2.7
+except ImportError:
+ import pickle
+
+
+def test_roundtrip():
+ from pybind11_tests import Pickleable
+
+ p = Pickleable("test_value")
+ p.setExtra1(15)
+ p.setExtra2(48)
+
+ data = pickle.dumps(p, 2) # Must use pickle protocol >= 2
+ p2 = pickle.loads(data)
+ assert p2.value() == p.value()
+ assert p2.extra1() == p.extra1()
+ assert p2.extra2() == p.extra2()
+
+
+def test_roundtrip_with_dict():
+ from pybind11_tests import PickleableWithDict
+
+ p = PickleableWithDict("test_value")
+ p.extra = 15
+ p.dynamic = "Attribute"
+
+ data = pickle.dumps(p, pickle.HIGHEST_PROTOCOL)
+ p2 = pickle.loads(data)
+ assert p2.value == p.value
+ assert p2.extra == p.extra
+ assert p2.dynamic == p.dynamic
diff --git a/ext/pybind11/tests/test_python_types.cpp b/ext/pybind11/tests/test_python_types.cpp
new file mode 100644
index 000000000..33c655b52
--- /dev/null
+++ b/ext/pybind11/tests/test_python_types.cpp
@@ -0,0 +1,430 @@
+/*
+ tests/test_python_types.cpp -- singleton design pattern, static functions and
+ variables, passing and interacting with Python types
+
+ Copyright (c) 2016 Wenzel Jakob <wenzel.jakob@epfl.ch>
+
+ All rights reserved. Use of this source code is governed by a
+ BSD-style license that can be found in the LICENSE file.
+*/
+
+#include "pybind11_tests.h"
+#include "constructor_stats.h"
+#include <pybind11/stl.h>
+
+#ifdef _WIN32
+# include <io.h>
+# include <fcntl.h>
+#endif
+
+class ExamplePythonTypes {
+public:
+ static ExamplePythonTypes *new_instance() {
+ auto *ptr = new ExamplePythonTypes();
+ print_created(ptr, "via new_instance");
+ return ptr;
+ }
+ ~ExamplePythonTypes() { print_destroyed(this); }
+
+ /* Create and return a Python dictionary */
+ py::dict get_dict() {
+ py::dict dict;
+ dict[py::str("key")] = py::str("value");
+ return dict;
+ }
+
+ /* Create and return a Python set */
+ py::set get_set() {
+ py::set set;
+ set.add(py::str("key1"));
+ set.add("key2");
+ set.add(std::string("key3"));
+ return set;
+ }
+
+ /* Create and return a C++ dictionary */
+ std::map<std::string, std::string> get_dict_2() {
+ std::map<std::string, std::string> result;
+ result["key"] = "value";
+ return result;
+ }
+
+ /* Create and return a C++ set */
+ std::set<std::string> get_set_2() {
+ std::set<std::string> result;
+ result.insert("key1");
+ result.insert("key2");
+ return result;
+ }
+
+ /* Create, manipulate, and return a Python list */
+ py::list get_list() {
+ py::list list;
+ list.append("value");
+ py::print("Entry at position 0:", list[0]);
+ list[0] = py::str("overwritten");
+ return list;
+ }
+
+ /* C++ STL data types are automatically casted */
+ std::vector<std::wstring> get_list_2() {
+ std::vector<std::wstring> list;
+ list.push_back(L"value");
+ return list;
+ }
+
+ /* C++ STL data types are automatically casted */
+ std::array<std::string, 2> get_array() {
+ return std::array<std::string, 2> {{ "array entry 1" , "array entry 2"}};
+ }
+
+ std::valarray<int> get_valarray() {
+ return std::valarray<int>({ 1, 4, 9 });
+ }
+
+ /* Easily iterate over a dictionary using a C++11 range-based for loop */
+ void print_dict(py::dict dict) {
+ for (auto item : dict)
+ py::print("key: {}, value={}"_s.format(item.first, item.second));
+ }
+
+ /* Easily iterate over a set using a C++11 range-based for loop */
+ void print_set(py::set set) {
+ for (auto item : set)
+ py::print("key:", item);
+ }
+
+ /* Easily iterate over a list using a C++11 range-based for loop */
+ void print_list(py::list list) {
+ int index = 0;
+ for (auto item : list)
+ py::print("list item {}: {}"_s.format(index++, item));
+ }
+
+ /* STL data types (such as maps) are automatically casted from Python */
+ void print_dict_2(const std::map<std::string, std::string> &dict) {
+ for (auto item : dict)
+ py::print("key: {}, value={}"_s.format(item.first, item.second));
+ }
+
+ /* STL data types (such as sets) are automatically casted from Python */
+ void print_set_2(const std::set<std::string> &set) {
+ for (auto item : set)
+ py::print("key:", item);
+ }
+
+ /* STL data types (such as vectors) are automatically casted from Python */
+ void print_list_2(std::vector<std::wstring> &list) {
+ int index = 0;
+ for (auto item : list)
+ py::print("list item {}: {}"_s.format(index++, item));
+ }
+
+ /* pybind automatically translates between C++11 and Python tuples */
+ std::pair<std::string, bool> pair_passthrough(std::pair<bool, std::string> input) {
+ return std::make_pair(input.second, input.first);
+ }
+
+ /* pybind automatically translates between C++11 and Python tuples */
+ std::tuple<int, std::string, bool> tuple_passthrough(std::tuple<bool, std::string, int> input) {
+ return std::make_tuple(std::get<2>(input), std::get<1>(input), std::get<0>(input));
+ }
+
+ /* STL data types (such as arrays) are automatically casted from Python */
+ void print_array(std::array<std::string, 2> &array) {
+ int index = 0;
+ for (auto item : array)
+ py::print("array item {}: {}"_s.format(index++, item));
+ }
+
+ void print_valarray(std::valarray<int> &varray) {
+ int index = 0;
+ for (auto item : varray)
+ py::print("valarray item {}: {}"_s.format(index++, item));
+ }
+
+ void throw_exception() {
+ throw std::runtime_error("This exception was intentionally thrown.");
+ }
+
+ py::bytes get_bytes_from_string() {
+ return (py::bytes) std::string("foo");
+ }
+
+ py::bytes get_bytes_from_str() {
+ return (py::bytes) py::str("bar", 3);
+ }
+
+ py::str get_str_from_string() {
+ return (py::str) std::string("baz");
+ }
+
+ py::str get_str_from_bytes() {
+ return (py::str) py::bytes("boo", 3);
+ }
+
+ void test_print(const py::object& obj) {
+ py::print(py::str(obj));
+ py::print(py::repr(obj));
+ }
+
+ static int value;
+ static const int value2;
+};
+
+int ExamplePythonTypes::value = 0;
+const int ExamplePythonTypes::value2 = 5;
+
+struct MoveOutContainer {
+ struct Value { int value; };
+
+ std::list<Value> move_list() const { return {{0}, {1}, {2}}; }
+};
+
+
+test_initializer python_types([](py::module &m) {
+ /* No constructor is explicitly defined below. An exception is raised when
+ trying to construct it directly from Python */
+ py::class_<ExamplePythonTypes>(m, "ExamplePythonTypes", "Example 2 documentation")
+ .def("get_dict", &ExamplePythonTypes::get_dict, "Return a Python dictionary")
+ .def("get_dict_2", &ExamplePythonTypes::get_dict_2, "Return a C++ dictionary")
+ .def("get_list", &ExamplePythonTypes::get_list, "Return a Python list")
+ .def("get_list_2", &ExamplePythonTypes::get_list_2, "Return a C++ list")
+ .def("get_set", &ExamplePythonTypes::get_set, "Return a Python set")
+ .def("get_set2", &ExamplePythonTypes::get_set_2, "Return a C++ set")
+ .def("get_array", &ExamplePythonTypes::get_array, "Return a C++ array")
+ .def("get_valarray", &ExamplePythonTypes::get_valarray, "Return a C++ valarray")
+ .def("print_dict", &ExamplePythonTypes::print_dict, "Print entries of a Python dictionary")
+ .def("print_dict_2", &ExamplePythonTypes::print_dict_2, "Print entries of a C++ dictionary")
+ .def("print_set", &ExamplePythonTypes::print_set, "Print entries of a Python set")
+ .def("print_set_2", &ExamplePythonTypes::print_set_2, "Print entries of a C++ set")
+ .def("print_list", &ExamplePythonTypes::print_list, "Print entries of a Python list")
+ .def("print_list_2", &ExamplePythonTypes::print_list_2, "Print entries of a C++ list")
+ .def("print_array", &ExamplePythonTypes::print_array, "Print entries of a C++ array")
+ .def("print_valarray", &ExamplePythonTypes::print_valarray, "Print entries of a C++ valarray")
+ .def("pair_passthrough", &ExamplePythonTypes::pair_passthrough, "Return a pair in reversed order")
+ .def("tuple_passthrough", &ExamplePythonTypes::tuple_passthrough, "Return a triple in reversed order")
+ .def("throw_exception", &ExamplePythonTypes::throw_exception, "Throw an exception")
+ .def("get_bytes_from_string", &ExamplePythonTypes::get_bytes_from_string, "py::bytes from std::string")
+ .def("get_bytes_from_str", &ExamplePythonTypes::get_bytes_from_str, "py::bytes from py::str")
+ .def("get_str_from_string", &ExamplePythonTypes::get_str_from_string, "py::str from std::string")
+ .def("get_str_from_bytes", &ExamplePythonTypes::get_str_from_bytes, "py::str from py::bytes")
+ .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)")
+ ;
+
+ m.def("test_print_function", []() {
+ py::print("Hello, World!");
+ py::print(1, 2.0, "three", true, std::string("-- multiple args"));
+ auto args = py::make_tuple("and", "a", "custom", "separator");
+ py::print("*args", *args, "sep"_a="-");
+ py::print("no new line here", "end"_a=" -- ");
+ py::print("next print");
+
+ auto py_stderr = py::module::import("sys").attr("stderr");
+ py::print("this goes to stderr", "file"_a=py_stderr);
+
+ py::print("flush", "flush"_a=true);
+
+ py::print("{a} + {b} = {c}"_s.format("a"_a="py::print", "b"_a="str.format", "c"_a="this"));
+ });
+
+ m.def("test_str_format", []() {
+ auto s1 = "{} + {} = {}"_s.format(1, 2, 3);
+ auto s2 = "{a} + {b} = {c}"_s.format("a"_a=1, "b"_a=2, "c"_a=3);
+ return py::make_tuple(s1, s2);
+ });
+
+ m.def("test_dict_keyword_constructor", []() {
+ auto d1 = py::dict("x"_a=1, "y"_a=2);
+ auto d2 = py::dict("z"_a=3, **d1);
+ return d2;
+ });
+
+ m.def("test_accessor_api", [](py::object o) {
+ auto d = py::dict();
+
+ d["basic_attr"] = o.attr("basic_attr");
+
+ auto l = py::list();
+ for (const auto &item : o.attr("begin_end")) {
+ l.append(item);
+ }
+ d["begin_end"] = l;
+
+ d["operator[object]"] = o.attr("d")["operator[object]"_s];
+ d["operator[char *]"] = o.attr("d")["operator[char *]"];
+
+ d["attr(object)"] = o.attr("sub").attr("attr_obj");
+ d["attr(char *)"] = o.attr("sub").attr("attr_char");
+ try {
+ o.attr("sub").attr("missing").ptr();
+ } catch (const py::error_already_set &) {
+ d["missing_attr_ptr"] = "raised"_s;
+ }
+ try {
+ o.attr("missing").attr("doesn't matter");
+ } catch (const py::error_already_set &) {
+ d["missing_attr_chain"] = "raised"_s;
+ }
+
+ d["is_none"] = o.attr("basic_attr").is_none();
+
+ d["operator()"] = o.attr("func")(1);
+ d["operator*"] = o.attr("func")(*o.attr("begin_end"));
+
+ return d;
+ });
+
+ m.def("test_tuple_accessor", [](py::tuple existing_t) {
+ try {
+ existing_t[0] = 1;
+ } catch (const py::error_already_set &) {
+ // --> Python system error
+ // Only new tuples (refcount == 1) are mutable
+ auto new_t = py::tuple(3);
+ for (size_t i = 0; i < new_t.size(); ++i) {
+ new_t[i] = i;
+ }
+ return new_t;
+ }
+ return py::tuple();
+ });
+
+ m.def("test_accessor_assignment", []() {
+ auto l = py::list(1);
+ l[0] = 0;
+
+ auto d = py::dict();
+ d["get"] = l[0];
+ auto var = l[0];
+ d["deferred_get"] = var;
+ l[0] = 1;
+ d["set"] = l[0];
+ var = 99; // this assignment should not overwrite l[0]
+ d["deferred_set"] = l[0];
+ d["var"] = var;
+
+ return d;
+ });
+
+ bool has_optional = false, has_exp_optional = false;
+#ifdef PYBIND11_HAS_OPTIONAL
+ has_optional = true;
+ using opt_int = std::optional<int>;
+ m.def("double_or_zero", [](const opt_int& x) -> int {
+ return x.value_or(0) * 2;
+ });
+ m.def("half_or_none", [](int x) -> opt_int {
+ return x ? opt_int(x / 2) : opt_int();
+ });
+ m.def("test_nullopt", [](opt_int x) {
+ return x.value_or(42);
+ }, py::arg_v("x", std::nullopt, "None"));
+#endif
+
+#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 {
+ 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("test_nullopt_exp", [](opt_int x) {
+ return x.value_or(42);
+ }, py::arg_v("x", std::experimental::nullopt, "None"));
+#endif
+
+ m.attr("has_optional") = has_optional;
+ m.attr("has_exp_optional") = has_exp_optional;
+
+ m.def("test_default_constructors", []() {
+ return py::dict(
+ "str"_a=py::str(),
+ "bool"_a=py::bool_(),
+ "int"_a=py::int_(),
+ "float"_a=py::float_(),
+ "tuple"_a=py::tuple(),
+ "list"_a=py::list(),
+ "dict"_a=py::dict(),
+ "set"_a=py::set()
+ );
+ });
+
+ m.def("test_converting_constructors", [](py::dict d) {
+ return py::dict(
+ "str"_a=py::str(d["str"]),
+ "bool"_a=py::bool_(d["bool"]),
+ "int"_a=py::int_(d["int"]),
+ "float"_a=py::float_(d["float"]),
+ "tuple"_a=py::tuple(d["tuple"]),
+ "list"_a=py::list(d["list"]),
+ "dict"_a=py::dict(d["dict"]),
+ "set"_a=py::set(d["set"]),
+ "memoryview"_a=py::memoryview(d["memoryview"])
+ );
+ });
+
+ m.def("test_cast_functions", [](py::dict d) {
+ // When converting between Python types, obj.cast<T>() should be the same as T(obj)
+ return py::dict(
+ "str"_a=d["str"].cast<py::str>(),
+ "bool"_a=d["bool"].cast<py::bool_>(),
+ "int"_a=d["int"].cast<py::int_>(),
+ "float"_a=d["float"].cast<py::float_>(),
+ "tuple"_a=d["tuple"].cast<py::tuple>(),
+ "list"_a=d["list"].cast<py::list>(),
+ "dict"_a=d["dict"].cast<py::dict>(),
+ "set"_a=d["set"].cast<py::set>(),
+ "memoryview"_a=d["memoryview"].cast<py::memoryview>()
+ );
+ });
+
+ py::class_<MoveOutContainer::Value>(m, "MoveOutContainerValue")
+ .def_readonly("value", &MoveOutContainer::Value::value);
+
+ py::class_<MoveOutContainer>(m, "MoveOutContainer")
+ .def(py::init<>())
+ .def_property_readonly("move_list", &MoveOutContainer::move_list);
+
+ m.def("get_implicit_casting", []() {
+ py::dict d;
+ d["char*_i1"] = "abc";
+ const char *c2 = "abc";
+ d["char*_i2"] = c2;
+ d["char*_e"] = py::cast(c2);
+ d["char*_p"] = py::str(c2);
+
+ d["int_i1"] = 42;
+ int i = 42;
+ d["int_i2"] = i;
+ i++;
+ d["int_e"] = py::cast(i);
+ i++;
+ d["int_p"] = py::int_(i);
+
+ d["str_i1"] = std::string("str");
+ std::string s2("str1");
+ d["str_i2"] = s2;
+ s2[3] = '2';
+ d["str_e"] = py::cast(s2);
+ s2[3] = '3';
+ d["str_p"] = py::str(s2);
+
+ py::list l(2);
+ l[0] = 3;
+ l[1] = py::cast(6);
+ l.append(9);
+ l.append(py::cast(12));
+ l.append(py::int_(15));
+
+ return py::dict(
+ "d"_a=d,
+ "l"_a=l
+ );
+ });
+});
diff --git a/ext/pybind11/tests/test_python_types.py b/ext/pybind11/tests/test_python_types.py
new file mode 100644
index 000000000..9fe1ef71e
--- /dev/null
+++ b/ext/pybind11/tests/test_python_types.py
@@ -0,0 +1,402 @@
+import pytest
+
+from pybind11_tests import ExamplePythonTypes, ConstructorStats, has_optional, has_exp_optional
+
+
+def test_static():
+ ExamplePythonTypes.value = 15
+ assert ExamplePythonTypes.value == 15
+ assert ExamplePythonTypes.value2 == 5
+
+ with pytest.raises(AttributeError) as excinfo:
+ ExamplePythonTypes.value2 = 15
+ assert str(excinfo.value) == "can't set attribute"
+
+
+def test_instance(capture):
+ with pytest.raises(TypeError) as excinfo:
+ ExamplePythonTypes()
+ assert str(excinfo.value) == "pybind11_tests.ExamplePythonTypes: No constructor defined!"
+
+ instance = ExamplePythonTypes.new_instance()
+
+ with capture:
+ dict_result = instance.get_dict()
+ dict_result['key2'] = 'value2'
+ instance.print_dict(dict_result)
+ assert capture.unordered == """
+ key: key, value=value
+ key: key2, value=value2
+ """
+ with capture:
+ dict_result = instance.get_dict_2()
+ dict_result['key2'] = 'value2'
+ instance.print_dict_2(dict_result)
+ assert capture.unordered == """
+ key: key, value=value
+ key: key2, value=value2
+ """
+ with capture:
+ set_result = instance.get_set()
+ set_result.add('key4')
+ instance.print_set(set_result)
+ assert capture.unordered == """
+ key: key1
+ key: key2
+ key: key3
+ key: key4
+ """
+ with capture:
+ set_result = instance.get_set2()
+ set_result.add('key3')
+ instance.print_set_2(set_result)
+ assert capture.unordered == """
+ key: key1
+ key: key2
+ key: key3
+ """
+ with capture:
+ list_result = instance.get_list()
+ list_result.append('value2')
+ instance.print_list(list_result)
+ assert capture.unordered == """
+ Entry at position 0: value
+ list item 0: overwritten
+ list item 1: value2
+ """
+ with capture:
+ list_result = instance.get_list_2()
+ list_result.append('value2')
+ instance.print_list_2(list_result)
+ assert capture.unordered == """
+ list item 0: value
+ list item 1: value2
+ """
+ with capture:
+ list_result = instance.get_list_2()
+ list_result.append('value2')
+ instance.print_list_2(tuple(list_result))
+ assert capture.unordered == """
+ list item 0: value
+ list item 1: value2
+ """
+ array_result = instance.get_array()
+ assert array_result == ['array entry 1', 'array entry 2']
+ with capture:
+ instance.print_array(array_result)
+ assert capture.unordered == """
+ array item 0: array entry 1
+ array item 1: array entry 2
+ """
+ varray_result = instance.get_valarray()
+ assert varray_result == [1, 4, 9]
+ with capture:
+ instance.print_valarray(varray_result)
+ assert capture.unordered == """
+ valarray item 0: 1
+ valarray item 1: 4
+ valarray item 2: 9
+ """
+ with pytest.raises(RuntimeError) as excinfo:
+ instance.throw_exception()
+ assert str(excinfo.value) == "This exception was intentionally thrown."
+
+ assert instance.pair_passthrough((True, "test")) == ("test", True)
+ assert instance.tuple_passthrough((True, "test", 5)) == (5, "test", True)
+ # Any sequence can be cast to a std::pair or std::tuple
+ assert instance.pair_passthrough([True, "test"]) == ("test", True)
+ assert instance.tuple_passthrough([True, "test", 5]) == (5, "test", True)
+
+ assert instance.get_bytes_from_string().decode() == "foo"
+ assert instance.get_bytes_from_str().decode() == "bar"
+ assert instance.get_str_from_string().encode().decode() == "baz"
+ assert instance.get_str_from_bytes().encode().decode() == "boo"
+
+ class A(object):
+ def __str__(self):
+ return "this is a str"
+
+ def __repr__(self):
+ return "this is a repr"
+
+ with capture:
+ instance.test_print(A())
+ assert capture == """
+ this is a str
+ this is a repr
+ """
+
+ cstats = ConstructorStats.get(ExamplePythonTypes)
+ assert cstats.alive() == 1
+ del instance
+ assert cstats.alive() == 0
+
+
+def test_docs(doc):
+ assert doc(ExamplePythonTypes) == "Example 2 documentation"
+ assert doc(ExamplePythonTypes.get_dict) == """
+ get_dict(self: m.ExamplePythonTypes) -> dict
+
+ Return a Python dictionary
+ """
+ assert doc(ExamplePythonTypes.get_dict_2) == """
+ get_dict_2(self: m.ExamplePythonTypes) -> Dict[str, str]
+
+ Return a C++ dictionary
+ """
+ assert doc(ExamplePythonTypes.get_list) == """
+ get_list(self: m.ExamplePythonTypes) -> list
+
+ Return a Python list
+ """
+ assert doc(ExamplePythonTypes.get_list_2) == """
+ get_list_2(self: m.ExamplePythonTypes) -> List[str]
+
+ Return a C++ list
+ """
+ assert doc(ExamplePythonTypes.get_dict) == """
+ get_dict(self: m.ExamplePythonTypes) -> dict
+
+ Return a Python dictionary
+ """
+ assert doc(ExamplePythonTypes.get_set) == """
+ get_set(self: m.ExamplePythonTypes) -> set
+
+ Return a Python set
+ """
+ assert doc(ExamplePythonTypes.get_set2) == """
+ get_set2(self: m.ExamplePythonTypes) -> Set[str]
+
+ Return a C++ set
+ """
+ assert doc(ExamplePythonTypes.get_array) == """
+ get_array(self: m.ExamplePythonTypes) -> List[str[2]]
+
+ Return a C++ array
+ """
+ assert doc(ExamplePythonTypes.get_valarray) == """
+ get_valarray(self: m.ExamplePythonTypes) -> List[int]
+
+ Return a C++ valarray
+ """
+ assert doc(ExamplePythonTypes.print_dict) == """
+ print_dict(self: m.ExamplePythonTypes, arg0: dict) -> None
+
+ Print entries of a Python dictionary
+ """
+ assert doc(ExamplePythonTypes.print_dict_2) == """
+ print_dict_2(self: m.ExamplePythonTypes, arg0: Dict[str, str]) -> None
+
+ Print entries of a C++ dictionary
+ """
+ assert doc(ExamplePythonTypes.print_set) == """
+ print_set(self: m.ExamplePythonTypes, arg0: set) -> None
+
+ Print entries of a Python set
+ """
+ assert doc(ExamplePythonTypes.print_set_2) == """
+ print_set_2(self: m.ExamplePythonTypes, arg0: Set[str]) -> None
+
+ Print entries of a C++ set
+ """
+ assert doc(ExamplePythonTypes.print_list) == """
+ print_list(self: m.ExamplePythonTypes, arg0: list) -> None
+
+ Print entries of a Python list
+ """
+ assert doc(ExamplePythonTypes.print_list_2) == """
+ print_list_2(self: m.ExamplePythonTypes, arg0: List[str]) -> None
+
+ Print entries of a C++ list
+ """
+ assert doc(ExamplePythonTypes.print_array) == """
+ print_array(self: m.ExamplePythonTypes, arg0: List[str[2]]) -> None
+
+ Print entries of a C++ array
+ """
+ assert doc(ExamplePythonTypes.pair_passthrough) == """
+ pair_passthrough(self: m.ExamplePythonTypes, arg0: Tuple[bool, str]) -> Tuple[str, bool]
+
+ Return a pair in reversed order
+ """
+ assert doc(ExamplePythonTypes.tuple_passthrough) == """
+ tuple_passthrough(self: m.ExamplePythonTypes, arg0: Tuple[bool, str, int]) -> Tuple[int, str, bool]
+
+ Return a triple in reversed order
+ """ # noqa: E501 line too long
+ assert doc(ExamplePythonTypes.throw_exception) == """
+ throw_exception(self: m.ExamplePythonTypes) -> None
+
+ Throw an exception
+ """
+ assert doc(ExamplePythonTypes.new_instance) == """
+ new_instance() -> m.ExamplePythonTypes
+
+ Return an instance
+ """
+
+
+def test_module():
+ import pybind11_tests
+
+ assert pybind11_tests.__name__ == "pybind11_tests"
+ assert ExamplePythonTypes.__name__ == "ExamplePythonTypes"
+ assert ExamplePythonTypes.__module__ == "pybind11_tests"
+ assert ExamplePythonTypes.get_set.__name__ == "get_set"
+ assert ExamplePythonTypes.get_set.__module__ == "pybind11_tests"
+
+
+def test_print(capture):
+ from pybind11_tests import test_print_function
+
+ with capture:
+ test_print_function()
+ assert capture == """
+ Hello, World!
+ 1 2.0 three True -- multiple args
+ *args-and-a-custom-separator
+ no new line here -- next print
+ flush
+ py::print + str.format = this
+ """
+ assert capture.stderr == "this goes to stderr"
+
+
+def test_str_api():
+ from pybind11_tests import test_str_format
+
+ s1, s2 = test_str_format()
+ assert s1 == "1 + 2 = 3"
+ assert s1 == s2
+
+
+def test_dict_api():
+ from pybind11_tests import test_dict_keyword_constructor
+
+ assert test_dict_keyword_constructor() == {"x": 1, "y": 2, "z": 3}
+
+
+def test_accessors():
+ from pybind11_tests import test_accessor_api, test_tuple_accessor, test_accessor_assignment
+
+ class SubTestObject:
+ attr_obj = 1
+ attr_char = 2
+
+ class TestObject:
+ basic_attr = 1
+ begin_end = [1, 2, 3]
+ d = {"operator[object]": 1, "operator[char *]": 2}
+ sub = SubTestObject()
+
+ def func(self, x, *args):
+ return self.basic_attr + x + sum(args)
+
+ d = test_accessor_api(TestObject())
+ assert d["basic_attr"] == 1
+ assert d["begin_end"] == [1, 2, 3]
+ assert d["operator[object]"] == 1
+ assert d["operator[char *]"] == 2
+ assert d["attr(object)"] == 1
+ assert d["attr(char *)"] == 2
+ assert d["missing_attr_ptr"] == "raised"
+ assert d["missing_attr_chain"] == "raised"
+ assert d["is_none"] is False
+ assert d["operator()"] == 2
+ assert d["operator*"] == 7
+
+ assert test_tuple_accessor(tuple()) == (0, 1, 2)
+
+ d = test_accessor_assignment()
+ assert d["get"] == 0
+ assert d["deferred_get"] == 0
+ assert d["set"] == 1
+ assert d["deferred_set"] == 1
+ assert d["var"] == 99
+
+
+@pytest.mark.skipif(not has_optional, reason='no <optional>')
+def test_optional():
+ from pybind11_tests import double_or_zero, half_or_none, test_nullopt
+
+ assert double_or_zero(None) == 0
+ assert double_or_zero(42) == 84
+ pytest.raises(TypeError, double_or_zero, 'foo')
+
+ assert half_or_none(0) is None
+ assert half_or_none(42) == 21
+ pytest.raises(TypeError, half_or_none, 'foo')
+
+ assert test_nullopt() == 42
+ assert test_nullopt(None) == 42
+ assert test_nullopt(42) == 42
+ assert test_nullopt(43) == 43
+
+
+@pytest.mark.skipif(not has_exp_optional, reason='no <experimental/optional>')
+def test_exp_optional():
+ from pybind11_tests import double_or_zero_exp, half_or_none_exp, test_nullopt_exp
+
+ assert double_or_zero_exp(None) == 0
+ assert double_or_zero_exp(42) == 84
+ pytest.raises(TypeError, double_or_zero_exp, 'foo')
+
+ assert half_or_none_exp(0) is None
+ assert half_or_none_exp(42) == 21
+ pytest.raises(TypeError, half_or_none_exp, 'foo')
+
+ assert test_nullopt_exp() == 42
+ assert test_nullopt_exp(None) == 42
+ assert test_nullopt_exp(42) == 42
+ assert test_nullopt_exp(43) == 43
+
+
+def test_constructors():
+ """C++ default and converting constructors are equivalent to type calls in Python"""
+ from pybind11_tests import (test_default_constructors, test_converting_constructors,
+ test_cast_functions)
+
+ types = [str, bool, int, float, tuple, list, dict, set]
+ expected = {t.__name__: t() for t in types}
+ assert test_default_constructors() == expected
+
+ data = {
+ str: 42,
+ bool: "Not empty",
+ int: "42",
+ float: "+1e3",
+ tuple: range(3),
+ list: range(3),
+ dict: [("two", 2), ("one", 1), ("three", 3)],
+ set: [4, 4, 5, 6, 6, 6],
+ memoryview: b'abc'
+ }
+ inputs = {k.__name__: v for k, v in data.items()}
+ expected = {k.__name__: k(v) for k, v in data.items()}
+ assert test_converting_constructors(inputs) == expected
+ assert test_cast_functions(inputs) == expected
+
+
+def test_move_out_container():
+ """Properties use the `reference_internal` policy by default. If the underlying function
+ returns an rvalue, the policy is automatically changed to `move` to avoid referencing
+ a temporary. In case the return value is a container of user-defined types, the policy
+ also needs to be applied to the elements, not just the container."""
+ from pybind11_tests import MoveOutContainer
+
+ c = MoveOutContainer()
+ moved_out_list = c.move_list
+ assert [x.value for x in moved_out_list] == [0, 1, 2]
+
+
+def test_implicit_casting():
+ """Tests implicit casting when assigning or appending to dicts and lists."""
+ from pybind11_tests import get_implicit_casting
+
+ z = get_implicit_casting()
+ assert z['d'] == {
+ 'char*_i1': 'abc', 'char*_i2': 'abc', 'char*_e': 'abc', 'char*_p': 'abc',
+ 'str_i1': 'str', 'str_i2': 'str1', 'str_e': 'str2', 'str_p': 'str3',
+ 'int_i1': 42, 'int_i2': 42, 'int_e': 43, 'int_p': 44
+ }
+ assert z['l'] == [3, 6, 9, 12, 15]
diff --git a/ext/pybind11/tests/test_sequences_and_iterators.cpp b/ext/pybind11/tests/test_sequences_and_iterators.cpp
new file mode 100644
index 000000000..323b4bf00
--- /dev/null
+++ b/ext/pybind11/tests/test_sequences_and_iterators.cpp
@@ -0,0 +1,275 @@
+/*
+ tests/test_sequences_and_iterators.cpp -- supporting Pythons' sequence protocol, iterators,
+ etc.
+
+ Copyright (c) 2016 Wenzel Jakob <wenzel.jakob@epfl.ch>
+
+ All rights reserved. Use of this source code is governed by a
+ BSD-style license that can be found in the LICENSE file.
+*/
+
+#include "pybind11_tests.h"
+#include "constructor_stats.h"
+#include <pybind11/operators.h>
+#include <pybind11/stl.h>
+
+class Sequence {
+public:
+ Sequence(size_t size) : m_size(size) {
+ print_created(this, "of size", m_size);
+ m_data = new float[size];
+ memset(m_data, 0, sizeof(float) * size);
+ }
+
+ Sequence(const std::vector<float> &value) : m_size(value.size()) {
+ print_created(this, "of size", m_size, "from std::vector");
+ m_data = new float[m_size];
+ memcpy(m_data, &value[0], sizeof(float) * m_size);
+ }
+
+ Sequence(const Sequence &s) : m_size(s.m_size) {
+ print_copy_created(this);
+ m_data = new float[m_size];
+ memcpy(m_data, s.m_data, sizeof(float)*m_size);
+ }
+
+ Sequence(Sequence &&s) : m_size(s.m_size), m_data(s.m_data) {
+ print_move_created(this);
+ s.m_size = 0;
+ s.m_data = nullptr;
+ }
+
+ ~Sequence() {
+ print_destroyed(this);
+ delete[] m_data;
+ }
+
+ Sequence &operator=(const Sequence &s) {
+ if (&s != this) {
+ delete[] m_data;
+ m_size = s.m_size;
+ m_data = new float[m_size];
+ memcpy(m_data, s.m_data, sizeof(float)*m_size);
+ }
+
+ print_copy_assigned(this);
+
+ return *this;
+ }
+
+ Sequence &operator=(Sequence &&s) {
+ if (&s != this) {
+ delete[] m_data;
+ m_size = s.m_size;
+ m_data = s.m_data;
+ s.m_size = 0;
+ s.m_data = nullptr;
+ }
+
+ print_move_assigned(this);
+
+ return *this;
+ }
+
+ bool operator==(const Sequence &s) const {
+ if (m_size != s.size())
+ return false;
+ for (size_t i=0; i<m_size; ++i)
+ if (m_data[i] != s[i])
+ return false;
+ return true;
+ }
+
+ bool operator!=(const Sequence &s) const {
+ return !operator==(s);
+ }
+
+ float operator[](size_t index) const {
+ return m_data[index];
+ }
+
+ float &operator[](size_t index) {
+ return m_data[index];
+ }
+
+ bool contains(float v) const {
+ for (size_t i=0; i<m_size; ++i)
+ if (v == m_data[i])
+ return true;
+ return false;
+ }
+
+ Sequence reversed() const {
+ Sequence result(m_size);
+ for (size_t i=0; i<m_size; ++i)
+ result[m_size-i-1] = m_data[i];
+ return result;
+ }
+
+ size_t size() const { return m_size; }
+
+ const float *begin() const { return m_data; }
+ const float *end() const { return m_data+m_size; }
+
+private:
+ size_t m_size;
+ float *m_data;
+};
+
+class IntPairs {
+public:
+ IntPairs(std::vector<std::pair<int, int>> data) : data_(std::move(data)) {}
+ const std::pair<int, int>* begin() const { return data_.data(); }
+
+private:
+ std::vector<std::pair<int, int>> data_;
+};
+
+// Interface of a map-like object that isn't (directly) an unordered_map, but provides some basic
+// map-like functionality.
+class StringMap {
+public:
+ StringMap() = default;
+ StringMap(std::unordered_map<std::string, std::string> init)
+ : map(std::move(init)) {}
+
+ void set(std::string key, std::string val) {
+ map[key] = val;
+ }
+
+ std::string get(std::string key) const {
+ return map.at(key);
+ }
+
+ size_t size() const {
+ return map.size();
+ }
+
+private:
+ std::unordered_map<std::string, std::string> map;
+
+public:
+ decltype(map.cbegin()) begin() const { return map.cbegin(); }
+ decltype(map.cend()) end() const { return map.cend(); }
+};
+
+template<typename T>
+class NonZeroIterator {
+ const T* ptr_;
+public:
+ NonZeroIterator(const T* ptr) : ptr_(ptr) {}
+ const T& operator*() const { return *ptr_; }
+ NonZeroIterator& operator++() { ++ptr_; return *this; }
+};
+
+class NonZeroSentinel {};
+
+template<typename A, typename B>
+bool operator==(const NonZeroIterator<std::pair<A, B>>& it, const NonZeroSentinel&) {
+ return !(*it).first || !(*it).second;
+}
+
+test_initializer sequences_and_iterators([](py::module &m) {
+
+ py::class_<Sequence> seq(m, "Sequence");
+
+ seq.def(py::init<size_t>())
+ .def(py::init<const std::vector<float>&>())
+ /// Bare bones interface
+ .def("__getitem__", [](const Sequence &s, size_t i) {
+ if (i >= s.size())
+ throw py::index_error();
+ return s[i];
+ })
+ .def("__setitem__", [](Sequence &s, size_t i, float v) {
+ if (i >= s.size())
+ throw py::index_error();
+ s[i] = v;
+ })
+ .def("__len__", &Sequence::size)
+ /// Optional sequence protocol operations
+ .def("__iter__", [](const Sequence &s) { return py::make_iterator(s.begin(), s.end()); },
+ py::keep_alive<0, 1>() /* Essential: keep object alive while iterator exists */)
+ .def("__contains__", [](const Sequence &s, float v) { return s.contains(v); })
+ .def("__reversed__", [](const Sequence &s) -> Sequence { return s.reversed(); })
+ /// Slicing protocol (optional)
+ .def("__getitem__", [](const Sequence &s, py::slice slice) -> Sequence* {
+ size_t start, stop, step, slicelength;
+ if (!slice.compute(s.size(), &start, &stop, &step, &slicelength))
+ throw py::error_already_set();
+ Sequence *seq = new Sequence(slicelength);
+ for (size_t i=0; i<slicelength; ++i) {
+ (*seq)[i] = s[start]; start += step;
+ }
+ return seq;
+ })
+ .def("__setitem__", [](Sequence &s, py::slice slice, const Sequence &value) {
+ size_t start, stop, step, slicelength;
+ if (!slice.compute(s.size(), &start, &stop, &step, &slicelength))
+ throw py::error_already_set();
+ if (slicelength != value.size())
+ throw std::runtime_error("Left and right hand size of slice assignment have different sizes!");
+ for (size_t i=0; i<slicelength; ++i) {
+ s[start] = value[i]; start += step;
+ }
+ })
+ /// Comparisons
+ .def(py::self == py::self)
+ .def(py::self != py::self);
+ // Could also define py::self + py::self for concatenation, etc.
+
+ py::class_<StringMap> map(m, "StringMap");
+
+ map .def(py::init<>())
+ .def(py::init<std::unordered_map<std::string, std::string>>())
+ .def("__getitem__", [](const StringMap &map, std::string key) {
+ try { return map.get(key); }
+ catch (const std::out_of_range&) {
+ throw py::key_error("key '" + key + "' does not exist");
+ }
+ })
+ .def("__setitem__", &StringMap::set)
+ .def("__len__", &StringMap::size)
+ .def("__iter__", [](const StringMap &map) { return py::make_key_iterator(map.begin(), map.end()); },
+ py::keep_alive<0, 1>())
+ .def("items", [](const StringMap &map) { return py::make_iterator(map.begin(), map.end()); },
+ py::keep_alive<0, 1>())
+ ;
+
+ py::class_<IntPairs>(m, "IntPairs")
+ .def(py::init<std::vector<std::pair<int, int>>>())
+ .def("nonzero", [](const IntPairs& s) {
+ return py::make_iterator(NonZeroIterator<std::pair<int, int>>(s.begin()), NonZeroSentinel());
+ }, py::keep_alive<0, 1>())
+ .def("nonzero_keys", [](const IntPairs& s) {
+ return py::make_key_iterator(NonZeroIterator<std::pair<int, int>>(s.begin()), NonZeroSentinel());
+ }, py::keep_alive<0, 1>());
+
+
+#if 0
+ // Obsolete: special data structure for exposing custom iterator types to python
+ // kept here for illustrative purposes because there might be some use cases which
+ // are not covered by the much simpler py::make_iterator
+
+ struct PySequenceIterator {
+ PySequenceIterator(const Sequence &seq, py::object ref) : seq(seq), ref(ref) { }
+
+ float next() {
+ if (index == seq.size())
+ throw py::stop_iteration();
+ return seq[index++];
+ }
+
+ const Sequence &seq;
+ py::object ref; // keep a reference
+ size_t index = 0;
+ };
+
+ py::class_<PySequenceIterator>(seq, "Iterator")
+ .def("__iter__", [](PySequenceIterator &it) -> PySequenceIterator& { return it; })
+ .def("__next__", &PySequenceIterator::next);
+
+ On the actual Sequence object, the iterator would be constructed as follows:
+ .def("__iter__", [](py::object s) { return PySequenceIterator(s.cast<const Sequence &>(), s); })
+#endif
+});
diff --git a/ext/pybind11/tests/test_sequences_and_iterators.py b/ext/pybind11/tests/test_sequences_and_iterators.py
new file mode 100644
index 000000000..76b9f43f6
--- /dev/null
+++ b/ext/pybind11/tests/test_sequences_and_iterators.py
@@ -0,0 +1,90 @@
+import pytest
+
+
+def isclose(a, b, rel_tol=1e-05, abs_tol=0.0):
+ """Like math.isclose() from Python 3.5"""
+ return abs(a - b) <= max(rel_tol * max(abs(a), abs(b)), abs_tol)
+
+
+def allclose(a_list, b_list, rel_tol=1e-05, abs_tol=0.0):
+ return all(isclose(a, b, rel_tol=rel_tol, abs_tol=abs_tol) for a, b in zip(a_list, b_list))
+
+
+def test_generalized_iterators():
+ from pybind11_tests 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)]
+ assert list(IntPairs([(0, 3), (1, 2), (3, 4)]).nonzero()) == []
+
+ assert list(IntPairs([(1, 2), (3, 4), (0, 5)]).nonzero_keys()) == [1, 3]
+ assert list(IntPairs([(1, 2), (2, 0), (0, 3), (4, 5)]).nonzero_keys()) == [1]
+ assert list(IntPairs([(0, 3), (1, 2), (3, 4)]).nonzero_keys()) == []
+
+
+def test_sequence():
+ from pybind11_tests import Sequence, ConstructorStats
+
+ cstats = ConstructorStats.get(Sequence)
+
+ s = Sequence(5)
+ assert cstats.values() == ['of size', '5']
+
+ assert "Sequence" in repr(s)
+ assert len(s) == 5
+ assert s[0] == 0 and s[3] == 0
+ assert 12.34 not in s
+ s[0], s[3] = 12.34, 56.78
+ assert 12.34 in s
+ assert isclose(s[0], 12.34) and isclose(s[3], 56.78)
+
+ rev = reversed(s)
+ assert cstats.values() == ['of size', '5']
+
+ rev2 = s[::-1]
+ assert cstats.values() == ['of size', '5']
+
+ expected = [0, 56.78, 0, 0, 12.34]
+ assert allclose(rev, expected)
+ assert allclose(rev2, expected)
+ assert rev == rev2
+
+ rev[0::2] = Sequence([2.0, 2.0, 2.0])
+ assert cstats.values() == ['of size', '3', 'from std::vector']
+
+ assert allclose(rev, [2, 56.78, 2, 0, 2])
+
+ assert cstats.alive() == 3
+ del s
+ assert cstats.alive() == 2
+ del rev
+ assert cstats.alive() == 1
+ del rev2
+ assert cstats.alive() == 0
+
+ assert cstats.values() == []
+ assert cstats.default_constructions == 0
+ assert cstats.copy_constructions == 0
+ assert cstats.move_constructions >= 1
+ assert cstats.copy_assignments == 0
+ assert cstats.move_assignments == 0
+
+
+def test_map_iterator():
+ from pybind11_tests import StringMap
+
+ m = StringMap({'hi': 'bye', 'black': 'white'})
+ assert m['hi'] == 'bye'
+ assert len(m) == 2
+ assert m['black'] == 'white'
+
+ with pytest.raises(KeyError):
+ assert m['orange']
+ m['orange'] = 'banana'
+ assert m['orange'] == 'banana'
+
+ expected = {'hi': 'bye', 'black': 'white', 'orange': 'banana'}
+ for k in m:
+ assert m[k] == expected[k]
+ for k, v in m.items():
+ assert v == expected[k]
diff --git a/ext/pybind11/tests/test_smart_ptr.cpp b/ext/pybind11/tests/test_smart_ptr.cpp
new file mode 100644
index 000000000..07c3cb066
--- /dev/null
+++ b/ext/pybind11/tests/test_smart_ptr.cpp
@@ -0,0 +1,224 @@
+/*
+ tests/test_smart_ptr.cpp -- binding classes with custom reference counting,
+ implicit conversions between types
+
+ Copyright (c) 2016 Wenzel Jakob <wenzel.jakob@epfl.ch>
+
+ All rights reserved. Use of this source code is governed by a
+ BSD-style license that can be found in the LICENSE file.
+*/
+
+#include "pybind11_tests.h"
+#include "object.h"
+
+/// Custom object with builtin reference counting (see 'object.h' for the implementation)
+class MyObject1 : public Object {
+public:
+ MyObject1(int value) : value(value) {
+ print_created(this, toString());
+ }
+
+ std::string toString() const {
+ return "MyObject1[" + std::to_string(value) + "]";
+ }
+
+protected:
+ virtual ~MyObject1() {
+ print_destroyed(this);
+ }
+
+private:
+ int value;
+};
+
+/// Object managed by a std::shared_ptr<>
+class MyObject2 {
+public:
+ MyObject2(int value) : value(value) {
+ print_created(this, toString());
+ }
+
+ std::string toString() const {
+ return "MyObject2[" + std::to_string(value) + "]";
+ }
+
+ virtual ~MyObject2() {
+ print_destroyed(this);
+ }
+
+private:
+ int value;
+};
+
+/// Object managed by a std::shared_ptr<>, additionally derives from std::enable_shared_from_this<>
+class MyObject3 : public std::enable_shared_from_this<MyObject3> {
+public:
+ MyObject3(int value) : value(value) {
+ print_created(this, toString());
+ }
+
+ std::string toString() const {
+ return "MyObject3[" + std::to_string(value) + "]";
+ }
+
+ virtual ~MyObject3() {
+ print_destroyed(this);
+ }
+
+private:
+ int value;
+};
+
+class MyObject4 {
+public:
+ MyObject4(int value) : value{value} {
+ print_created(this);
+ }
+ int value;
+private:
+ ~MyObject4() {
+ print_destroyed(this);
+ }
+};
+
+/// Make pybind aware of the ref-counted wrapper type (s)
+PYBIND11_DECLARE_HOLDER_TYPE(T, ref<T>); // Required for custom holder type
+PYBIND11_DECLARE_HOLDER_TYPE(T, std::shared_ptr<T>); // Not required any more for std::shared_ptr,
+ // but it should compile without error
+
+Object *make_object_1() { return new MyObject1(1); }
+ref<Object> make_object_2() { return new MyObject1(2); }
+
+MyObject1 *make_myobject1_1() { return new MyObject1(4); }
+ref<MyObject1> make_myobject1_2() { return new MyObject1(5); }
+
+MyObject2 *make_myobject2_1() { return new MyObject2(6); }
+std::shared_ptr<MyObject2> make_myobject2_2() { return std::make_shared<MyObject2>(7); }
+
+MyObject3 *make_myobject3_1() { return new MyObject3(8); }
+std::shared_ptr<MyObject3> make_myobject3_2() { return std::make_shared<MyObject3>(9); }
+
+void print_object_1(const Object *obj) { py::print(obj->toString()); }
+void print_object_2(ref<Object> obj) { py::print(obj->toString()); }
+void print_object_3(const ref<Object> &obj) { py::print(obj->toString()); }
+void print_object_4(const ref<Object> *obj) { py::print((*obj)->toString()); }
+
+void print_myobject1_1(const MyObject1 *obj) { py::print(obj->toString()); }
+void print_myobject1_2(ref<MyObject1> obj) { py::print(obj->toString()); }
+void print_myobject1_3(const ref<MyObject1> &obj) { py::print(obj->toString()); }
+void print_myobject1_4(const ref<MyObject1> *obj) { py::print((*obj)->toString()); }
+
+void print_myobject2_1(const MyObject2 *obj) { py::print(obj->toString()); }
+void print_myobject2_2(std::shared_ptr<MyObject2> obj) { py::print(obj->toString()); }
+void print_myobject2_3(const std::shared_ptr<MyObject2> &obj) { py::print(obj->toString()); }
+void print_myobject2_4(const std::shared_ptr<MyObject2> *obj) { py::print((*obj)->toString()); }
+
+void print_myobject3_1(const MyObject3 *obj) { py::print(obj->toString()); }
+void print_myobject3_2(std::shared_ptr<MyObject3> obj) { py::print(obj->toString()); }
+void print_myobject3_3(const std::shared_ptr<MyObject3> &obj) { py::print(obj->toString()); }
+void print_myobject3_4(const std::shared_ptr<MyObject3> *obj) { py::print((*obj)->toString()); }
+
+test_initializer smart_ptr([](py::module &m) {
+ py::class_<Object, ref<Object>> obj(m, "Object");
+ obj.def("getRefCount", &Object::getRefCount);
+
+ py::class_<MyObject1, ref<MyObject1>>(m, "MyObject1", obj)
+ .def(py::init<int>());
+
+ m.def("make_object_1", &make_object_1);
+ m.def("make_object_2", &make_object_2);
+ m.def("make_myobject1_1", &make_myobject1_1);
+ m.def("make_myobject1_2", &make_myobject1_2);
+ m.def("print_object_1", &print_object_1);
+ m.def("print_object_2", &print_object_2);
+ m.def("print_object_3", &print_object_3);
+ m.def("print_object_4", &print_object_4);
+ m.def("print_myobject1_1", &print_myobject1_1);
+ m.def("print_myobject1_2", &print_myobject1_2);
+ m.def("print_myobject1_3", &print_myobject1_3);
+ m.def("print_myobject1_4", &print_myobject1_4);
+
+ py::class_<MyObject2, std::shared_ptr<MyObject2>>(m, "MyObject2")
+ .def(py::init<int>());
+ m.def("make_myobject2_1", &make_myobject2_1);
+ m.def("make_myobject2_2", &make_myobject2_2);
+ m.def("print_myobject2_1", &print_myobject2_1);
+ m.def("print_myobject2_2", &print_myobject2_2);
+ m.def("print_myobject2_3", &print_myobject2_3);
+ m.def("print_myobject2_4", &print_myobject2_4);
+
+ py::class_<MyObject3, std::shared_ptr<MyObject3>>(m, "MyObject3")
+ .def(py::init<int>());
+ m.def("make_myobject3_1", &make_myobject3_1);
+ m.def("make_myobject3_2", &make_myobject3_2);
+ m.def("print_myobject3_1", &print_myobject3_1);
+ m.def("print_myobject3_2", &print_myobject3_2);
+ m.def("print_myobject3_3", &print_myobject3_3);
+ m.def("print_myobject3_4", &print_myobject3_4);
+
+ py::class_<MyObject4, std::unique_ptr<MyObject4, py::nodelete>>(m, "MyObject4")
+ .def(py::init<int>())
+ .def_readwrite("value", &MyObject4::value);
+
+ py::implicitly_convertible<py::int_, MyObject1>();
+
+ // Expose constructor stats for the ref type
+ m.def("cstats_ref", &ConstructorStats::get<ref_tag>);
+});
+
+struct SharedPtrRef {
+ struct A {
+ A() { print_created(this); }
+ A(const A &) { print_copy_created(this); }
+ A(A &&) { print_move_created(this); }
+ ~A() { print_destroyed(this); }
+ };
+
+ A value = {};
+ std::shared_ptr<A> shared = std::make_shared<A>();
+};
+
+struct SharedFromThisRef {
+ struct B : std::enable_shared_from_this<B> {
+ B() { print_created(this); }
+ B(const B &) : std::enable_shared_from_this<B>() { print_copy_created(this); }
+ B(B &&) : std::enable_shared_from_this<B>() { print_move_created(this); }
+ ~B() { print_destroyed(this); }
+ };
+
+ B value = {};
+ std::shared_ptr<B> shared = std::make_shared<B>();
+};
+
+test_initializer smart_ptr_and_references([](py::module &pm) {
+ auto m = pm.def_submodule("smart_ptr");
+
+ using A = SharedPtrRef::A;
+ py::class_<A, std::shared_ptr<A>>(m, "A");
+
+ py::class_<SharedPtrRef>(m, "SharedPtrRef")
+ .def(py::init<>())
+ .def_readonly("ref", &SharedPtrRef::value)
+ .def_property_readonly("copy", [](const SharedPtrRef &s) { return s.value; },
+ py::return_value_policy::copy)
+ .def_readonly("holder_ref", &SharedPtrRef::shared)
+ .def_property_readonly("holder_copy", [](const SharedPtrRef &s) { return s.shared; },
+ py::return_value_policy::copy)
+ .def("set_ref", [](SharedPtrRef &, const A &) { return true; })
+ .def("set_holder", [](SharedPtrRef &, std::shared_ptr<A>) { return true; });
+
+ using B = SharedFromThisRef::B;
+ py::class_<B, std::shared_ptr<B>>(m, "B");
+
+ py::class_<SharedFromThisRef>(m, "SharedFromThisRef")
+ .def(py::init<>())
+ .def_readonly("bad_wp", &SharedFromThisRef::value)
+ .def_property_readonly("ref", [](const SharedFromThisRef &s) -> const B & { return *s.shared; })
+ .def_property_readonly("copy", [](const SharedFromThisRef &s) { return s.value; },
+ py::return_value_policy::copy)
+ .def_readonly("holder_ref", &SharedFromThisRef::shared)
+ .def_property_readonly("holder_copy", [](const SharedFromThisRef &s) { return s.shared; },
+ py::return_value_policy::copy)
+ .def("set_ref", [](SharedFromThisRef &, const B &) { return true; })
+ .def("set_holder", [](SharedFromThisRef &, std::shared_ptr<B>) { return true; });
+});
diff --git a/ext/pybind11/tests/test_smart_ptr.py b/ext/pybind11/tests/test_smart_ptr.py
new file mode 100644
index 000000000..3a33e1761
--- /dev/null
+++ b/ext/pybind11/tests/test_smart_ptr.py
@@ -0,0 +1,198 @@
+import pytest
+from pybind11_tests import ConstructorStats
+
+
+def test_smart_ptr(capture):
+ # Object1
+ from pybind11_tests import (MyObject1, make_object_1, make_object_2,
+ print_object_1, print_object_2, print_object_3, print_object_4)
+
+ for i, o in enumerate([make_object_1(), make_object_2(), MyObject1(3)], start=1):
+ assert o.getRefCount() == 1
+ with capture:
+ print_object_1(o)
+ print_object_2(o)
+ print_object_3(o)
+ print_object_4(o)
+ assert capture == "MyObject1[{i}]\n".format(i=i) * 4
+
+ from pybind11_tests import (make_myobject1_1, make_myobject1_2,
+ print_myobject1_1, print_myobject1_2,
+ print_myobject1_3, print_myobject1_4)
+
+ for i, o in enumerate([make_myobject1_1(), make_myobject1_2(), MyObject1(6), 7], start=4):
+ print(o)
+ with capture:
+ if not isinstance(o, int):
+ print_object_1(o)
+ print_object_2(o)
+ print_object_3(o)
+ print_object_4(o)
+ print_myobject1_1(o)
+ print_myobject1_2(o)
+ print_myobject1_3(o)
+ print_myobject1_4(o)
+ assert capture == "MyObject1[{i}]\n".format(i=i) * (4 if isinstance(o, int) else 8)
+
+ cstats = ConstructorStats.get(MyObject1)
+ assert cstats.alive() == 0
+ expected_values = ['MyObject1[{}]'.format(i) for i in range(1, 7)] + ['MyObject1[7]'] * 4
+ assert cstats.values() == expected_values
+ assert cstats.default_constructions == 0
+ assert cstats.copy_constructions == 0
+ # assert cstats.move_constructions >= 0 # Doesn't invoke any
+ assert cstats.copy_assignments == 0
+ assert cstats.move_assignments == 0
+
+ # Object2
+ from pybind11_tests import (MyObject2, make_myobject2_1, make_myobject2_2,
+ make_myobject3_1, make_myobject3_2,
+ print_myobject2_1, print_myobject2_2,
+ print_myobject2_3, print_myobject2_4)
+
+ for i, o in zip([8, 6, 7], [MyObject2(8), make_myobject2_1(), make_myobject2_2()]):
+ print(o)
+ with capture:
+ print_myobject2_1(o)
+ print_myobject2_2(o)
+ print_myobject2_3(o)
+ print_myobject2_4(o)
+ assert capture == "MyObject2[{i}]\n".format(i=i) * 4
+
+ cstats = ConstructorStats.get(MyObject2)
+ assert cstats.alive() == 1
+ o = None
+ assert cstats.alive() == 0
+ assert cstats.values() == ['MyObject2[8]', 'MyObject2[6]', 'MyObject2[7]']
+ assert cstats.default_constructions == 0
+ assert cstats.copy_constructions == 0
+ # assert cstats.move_constructions >= 0 # Doesn't invoke any
+ assert cstats.copy_assignments == 0
+ assert cstats.move_assignments == 0
+
+ # Object3
+ from pybind11_tests import (MyObject3, print_myobject3_1, print_myobject3_2,
+ print_myobject3_3, print_myobject3_4)
+
+ for i, o in zip([9, 8, 9], [MyObject3(9), make_myobject3_1(), make_myobject3_2()]):
+ print(o)
+ with capture:
+ print_myobject3_1(o)
+ print_myobject3_2(o)
+ print_myobject3_3(o)
+ print_myobject3_4(o)
+ assert capture == "MyObject3[{i}]\n".format(i=i) * 4
+
+ cstats = ConstructorStats.get(MyObject3)
+ assert cstats.alive() == 1
+ o = None
+ assert cstats.alive() == 0
+ assert cstats.values() == ['MyObject3[9]', 'MyObject3[8]', 'MyObject3[9]']
+ assert cstats.default_constructions == 0
+ assert cstats.copy_constructions == 0
+ # assert cstats.move_constructions >= 0 # Doesn't invoke any
+ assert cstats.copy_assignments == 0
+ assert cstats.move_assignments == 0
+
+ # Object and ref
+ from pybind11_tests import Object, cstats_ref
+
+ cstats = ConstructorStats.get(Object)
+ assert cstats.alive() == 0
+ assert cstats.values() == []
+ assert cstats.default_constructions == 10
+ assert cstats.copy_constructions == 0
+ # assert cstats.move_constructions >= 0 # Doesn't invoke any
+ assert cstats.copy_assignments == 0
+ assert cstats.move_assignments == 0
+
+ cstats = cstats_ref()
+ assert cstats.alive() == 0
+ assert cstats.values() == ['from pointer'] * 10
+ assert cstats.default_constructions == 30
+ assert cstats.copy_constructions == 12
+ # assert cstats.move_constructions >= 0 # Doesn't invoke any
+ assert cstats.copy_assignments == 30
+ assert cstats.move_assignments == 0
+
+
+def test_unique_nodelete():
+ from pybind11_tests import MyObject4
+ o = MyObject4(23)
+ assert o.value == 23
+ cstats = ConstructorStats.get(MyObject4)
+ assert cstats.alive() == 1
+ del o
+ cstats = ConstructorStats.get(MyObject4)
+ assert cstats.alive() == 1 # Leak, but that's intentional
+
+
+def test_shared_ptr_and_references():
+ from pybind11_tests.smart_ptr import SharedPtrRef, A
+
+ s = SharedPtrRef()
+ stats = ConstructorStats.get(A)
+ assert stats.alive() == 2
+
+ ref = s.ref # init_holder_helper(holder_ptr=false, owned=false)
+ assert stats.alive() == 2
+ assert s.set_ref(ref)
+ with pytest.raises(RuntimeError) as excinfo:
+ assert s.set_holder(ref)
+ assert "Unable to cast from non-held to held instance" in str(excinfo.value)
+
+ copy = s.copy # init_holder_helper(holder_ptr=false, owned=true)
+ assert stats.alive() == 3
+ assert s.set_ref(copy)
+ assert s.set_holder(copy)
+
+ holder_ref = s.holder_ref # init_holder_helper(holder_ptr=true, owned=false)
+ assert stats.alive() == 3
+ assert s.set_ref(holder_ref)
+ assert s.set_holder(holder_ref)
+
+ holder_copy = s.holder_copy # init_holder_helper(holder_ptr=true, owned=true)
+ assert stats.alive() == 3
+ assert s.set_ref(holder_copy)
+ assert s.set_holder(holder_copy)
+
+ del ref, copy, holder_ref, holder_copy, s
+ assert stats.alive() == 0
+
+
+def test_shared_ptr_from_this_and_references():
+ from pybind11_tests.smart_ptr import SharedFromThisRef, B
+
+ s = SharedFromThisRef()
+ stats = ConstructorStats.get(B)
+ assert stats.alive() == 2
+
+ ref = s.ref # init_holder_helper(holder_ptr=false, owned=false, bad_wp=false)
+ assert stats.alive() == 2
+ assert s.set_ref(ref)
+ assert s.set_holder(ref) # std::enable_shared_from_this can create a holder from a reference
+
+ bad_wp = s.bad_wp # init_holder_helper(holder_ptr=false, owned=false, bad_wp=true)
+ assert stats.alive() == 2
+ assert s.set_ref(bad_wp)
+ with pytest.raises(RuntimeError) as excinfo:
+ assert s.set_holder(bad_wp)
+ assert "Unable to cast from non-held to held instance" in str(excinfo.value)
+
+ copy = s.copy # init_holder_helper(holder_ptr=false, owned=true, bad_wp=false)
+ assert stats.alive() == 3
+ assert s.set_ref(copy)
+ assert s.set_holder(copy)
+
+ holder_ref = s.holder_ref # init_holder_helper(holder_ptr=true, owned=false, bad_wp=false)
+ assert stats.alive() == 3
+ assert s.set_ref(holder_ref)
+ assert s.set_holder(holder_ref)
+
+ holder_copy = s.holder_copy # init_holder_helper(holder_ptr=true, owned=true, bad_wp=false)
+ assert stats.alive() == 3
+ assert s.set_ref(holder_copy)
+ assert s.set_holder(holder_copy)
+
+ del ref, bad_wp, copy, holder_ref, holder_copy, s
+ assert stats.alive() == 0
diff --git a/ext/pybind11/tests/test_stl_binders.cpp b/ext/pybind11/tests/test_stl_binders.cpp
new file mode 100644
index 000000000..b9b56c15d
--- /dev/null
+++ b/ext/pybind11/tests/test_stl_binders.cpp
@@ -0,0 +1,95 @@
+/*
+ tests/test_stl_binders.cpp -- Usage of stl_binders functions
+
+ Copyright (c) 2016 Sergey Lyskov
+
+ All rights reserved. Use of this source code is governed by a
+ BSD-style license that can be found in the LICENSE file.
+*/
+
+#include "pybind11_tests.h"
+
+#include <pybind11/stl_bind.h>
+#include <map>
+#include <deque>
+#include <unordered_map>
+
+class El {
+public:
+ El() = delete;
+ El(int v) : a(v) { }
+
+ int a;
+};
+
+std::ostream & operator<<(std::ostream &s, El const&v) {
+ s << "El{" << v.a << '}';
+ return s;
+}
+
+/// Issue #487: binding std::vector<E> with E non-copyable
+class E_nc {
+public:
+ explicit E_nc(int i) : value{i} {}
+ E_nc(const E_nc &) = delete;
+ E_nc &operator=(const E_nc &) = delete;
+ E_nc(E_nc &&) = default;
+ E_nc &operator=(E_nc &&) = default;
+
+ int value;
+};
+
+template <class Container> Container *one_to_n(int n) {
+ auto v = new Container();
+ for (int i = 1; i <= n; i++)
+ v->emplace_back(i);
+ return v;
+}
+
+template <class Map> Map *times_ten(int n) {
+ auto m = new Map();
+ for (int i = 1; i <= n; i++)
+ m->emplace(int(i), E_nc(10*i));
+ return m;
+}
+
+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<bool>>(m, "VectorBool");
+
+ py::bind_vector<std::vector<El>>(m, "VectorEl");
+
+ py::bind_vector<std::vector<std::vector<El>>>(m, "VectorVectorEl");
+
+});
+
+test_initializer stl_binder_map([](py::module &m) {
+ py::bind_map<std::map<std::string, double>>(m, "MapStringDouble");
+ py::bind_map<std::unordered_map<std::string, double>>(m, "UnorderedMapStringDouble");
+
+ py::bind_map<std::map<std::string, double const>>(m, "MapStringDoubleConst");
+ py::bind_map<std::unordered_map<std::string, double const>>(m, "UnorderedMapStringDoubleConst");
+
+});
+
+test_initializer stl_binder_noncopyable([](py::module &m) {
+ py::class_<E_nc>(m, "ENC")
+ .def(py::init<int>())
+ .def_readwrite("value", &E_nc::value);
+
+ py::bind_vector<std::vector<E_nc>>(m, "VectorENC");
+ m.def("get_vnc", &one_to_n<std::vector<E_nc>>, py::return_value_policy::reference);
+
+ py::bind_vector<std::deque<E_nc>>(m, "DequeENC");
+ m.def("get_dnc", &one_to_n<std::deque<E_nc>>, py::return_value_policy::reference);
+
+ py::bind_map<std::map<int, E_nc>>(m, "MapENC");
+ m.def("get_mnc", &times_ten<std::map<int, E_nc>>, py::return_value_policy::reference);
+
+ 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
new file mode 100644
index 000000000..c9bcc7935
--- /dev/null
+++ b/ext/pybind11/tests/test_stl_binders.py
@@ -0,0 +1,140 @@
+def test_vector_int():
+ from pybind11_tests import VectorInt
+
+ v_int = VectorInt([0, 0])
+ assert len(v_int) == 2
+ assert bool(v_int) is True
+
+ v_int2 = VectorInt([0, 0])
+ assert v_int == v_int2
+ v_int2[1] = 1
+ assert v_int != v_int2
+
+ v_int2.append(2)
+ v_int2.append(3)
+ v_int2.insert(0, 1)
+ v_int2.insert(0, 2)
+ v_int2.insert(0, 3)
+ assert str(v_int2) == "VectorInt[3, 2, 1, 0, 1, 2, 3]"
+
+ v_int.append(99)
+ v_int2[2:-2] = v_int
+ assert v_int2 == VectorInt([3, 2, 0, 0, 99, 2, 3])
+ del v_int2[1:3]
+ assert v_int2 == VectorInt([3, 0, 99, 2, 3])
+ del v_int2[0]
+ assert v_int2 == VectorInt([0, 99, 2, 3])
+
+
+def test_vector_custom():
+ from pybind11_tests import El, VectorEl, VectorVectorEl
+
+ v_a = VectorEl()
+ v_a.append(El(1))
+ v_a.append(El(2))
+ assert str(v_a) == "VectorEl[El{1}, El{2}]"
+
+ vv_a = VectorVectorEl()
+ vv_a.append(v_a)
+ vv_b = vv_a[0]
+ assert str(vv_b) == "VectorEl[El{1}, El{2}]"
+
+
+def test_vector_bool():
+ from pybind11_tests import VectorBool
+
+ vv_c = VectorBool()
+ for i in range(10):
+ vv_c.append(i % 2 == 0)
+ for i in range(10):
+ assert vv_c[i] == (i % 2 == 0)
+ assert str(vv_c) == "VectorBool[1, 0, 1, 0, 1, 0, 1, 0, 1, 0]"
+
+
+def test_map_string_double():
+ from pybind11_tests import MapStringDouble, UnorderedMapStringDouble
+
+ m = MapStringDouble()
+ m['a'] = 1
+ m['b'] = 2.5
+
+ assert list(m) == ['a', 'b']
+ assert list(m.items()) == [('a', 1), ('b', 2.5)]
+ assert str(m) == "MapStringDouble{a: 1, b: 2.5}"
+
+ um = UnorderedMapStringDouble()
+ um['ua'] = 1.1
+ um['ub'] = 2.6
+
+ assert sorted(list(um)) == ['ua', 'ub']
+ assert sorted(list(um.items())) == [('ua', 1.1), ('ub', 2.6)]
+ assert "UnorderedMapStringDouble" in str(um)
+
+
+def test_map_string_double_const():
+ from pybind11_tests import MapStringDoubleConst, UnorderedMapStringDoubleConst
+
+ mc = MapStringDoubleConst()
+ mc['a'] = 10
+ mc['b'] = 20.5
+ assert str(mc) == "MapStringDoubleConst{a: 10, b: 20.5}"
+
+ umc = UnorderedMapStringDoubleConst()
+ umc['a'] = 11
+ umc['b'] = 21.5
+
+ str(umc)
+
+
+def test_noncopyable_vector():
+ from pybind11_tests import get_vnc
+
+ vnc = get_vnc(5)
+ for i in range(0, 5):
+ assert vnc[i].value == i + 1
+
+ for i, j in enumerate(vnc, start=1):
+ assert j.value == i
+
+
+def test_noncopyable_deque():
+ from pybind11_tests import get_dnc
+
+ dnc = get_dnc(5)
+ for i in range(0, 5):
+ assert dnc[i].value == i + 1
+
+ i = 1
+ for j in dnc:
+ assert(j.value == i)
+ i += 1
+
+
+def test_noncopyable_map():
+ from pybind11_tests import get_mnc
+
+ mnc = get_mnc(5)
+ for i in range(1, 6):
+ assert mnc[i].value == 10 * i
+
+ vsum = 0
+ for k, v in mnc.items():
+ assert v.value == 10 * k
+ vsum += v.value
+
+ assert vsum == 150
+
+
+def test_noncopyable_unordered_map():
+ from pybind11_tests import get_umnc
+
+ mnc = get_umnc(5)
+ for i in range(1, 6):
+ assert mnc[i].value == 10 * i
+
+ vsum = 0
+ for k, v in mnc.items():
+ assert v.value == 10 * k
+ vsum += v.value
+
+ assert vsum == 150
diff --git a/ext/pybind11/tests/test_virtual_functions.cpp b/ext/pybind11/tests/test_virtual_functions.cpp
new file mode 100644
index 000000000..0f8ed2afb
--- /dev/null
+++ b/ext/pybind11/tests/test_virtual_functions.cpp
@@ -0,0 +1,347 @@
+/*
+ tests/test_virtual_functions.cpp -- overriding virtual functions from Python
+
+ Copyright (c) 2016 Wenzel Jakob <wenzel.jakob@epfl.ch>
+
+ All rights reserved. Use of this source code is governed by a
+ BSD-style license that can be found in the LICENSE file.
+*/
+
+#include "pybind11_tests.h"
+#include "constructor_stats.h"
+#include <pybind11/functional.h>
+
+/* This is an example class that we'll want to be able to extend from Python */
+class ExampleVirt {
+public:
+ ExampleVirt(int state) : state(state) { print_created(this, state); }
+ ExampleVirt(const ExampleVirt &e) : state(e.state) { print_copy_created(this); }
+ ExampleVirt(ExampleVirt &&e) : state(e.state) { print_move_created(this); e.state = 0; }
+ ~ExampleVirt() { print_destroyed(this); }
+
+ virtual int run(int value) {
+ py::print("Original implementation of "
+ "ExampleVirt::run(state={}, value={}, str1={}, str2={})"_s.format(state, value, get_string1(), *get_string2()));
+ return state + value;
+ }
+
+ virtual bool run_bool() = 0;
+ virtual void pure_virtual() = 0;
+
+ // Returning a reference/pointer to a type converted from python (numbers, strings, etc.) is a
+ // bit trickier, because the actual int& or std::string& or whatever only exists temporarily, so
+ // we have to handle it specially in the trampoline class (see below).
+ virtual const std::string &get_string1() { return str1; }
+ virtual const std::string *get_string2() { return &str2; }
+
+private:
+ int state;
+ const std::string str1{"default1"}, str2{"default2"};
+};
+
+/* This is a wrapper class that must be generated */
+class PyExampleVirt : public ExampleVirt {
+public:
+ using ExampleVirt::ExampleVirt; /* Inherit constructors */
+
+ int run(int value) override {
+ /* Generate wrapping code that enables native function overloading */
+ PYBIND11_OVERLOAD(
+ int, /* Return type */
+ ExampleVirt, /* Parent class */
+ run, /* Name of function */
+ value /* Argument(s) */
+ );
+ }
+
+ bool run_bool() override {
+ PYBIND11_OVERLOAD_PURE(
+ bool, /* Return type */
+ ExampleVirt, /* Parent class */
+ run_bool, /* Name of function */
+ /* This function has no arguments. The trailing comma
+ in the previous line is needed for some compilers */
+ );
+ }
+
+ void pure_virtual() override {
+ PYBIND11_OVERLOAD_PURE(
+ void, /* Return type */
+ ExampleVirt, /* Parent class */
+ pure_virtual, /* Name of function */
+ /* This function has no arguments. The trailing comma
+ in the previous line is needed for some compilers */
+ );
+ }
+
+ // We can return reference types for compatibility with C++ virtual interfaces that do so, but
+ // note they have some significant limitations (see the documentation).
+ const std::string &get_string1() override {
+ PYBIND11_OVERLOAD(
+ const std::string &, /* Return type */
+ ExampleVirt, /* Parent class */
+ get_string1, /* Name of function */
+ /* (no arguments) */
+ );
+ }
+
+ const std::string *get_string2() override {
+ PYBIND11_OVERLOAD(
+ const std::string *, /* Return type */
+ ExampleVirt, /* Parent class */
+ get_string2, /* Name of function */
+ /* (no arguments) */
+ );
+ }
+
+};
+
+class NonCopyable {
+public:
+ NonCopyable(int a, int b) : value{new int(a*b)} { print_created(this, a, b); }
+ NonCopyable(NonCopyable &&o) { value = std::move(o.value); print_move_created(this); }
+ NonCopyable(const NonCopyable &) = delete;
+ NonCopyable() = delete;
+ void operator=(const NonCopyable &) = delete;
+ void operator=(NonCopyable &&) = delete;
+ std::string get_value() const {
+ if (value) return std::to_string(*value); else return "(null)";
+ }
+ ~NonCopyable() { print_destroyed(this); }
+
+private:
+ std::unique_ptr<int> value;
+};
+
+// This is like the above, but is both copy and movable. In effect this means it should get moved
+// when it is not referenced elsewhere, but copied if it is still referenced.
+class Movable {
+public:
+ Movable(int a, int b) : value{a+b} { print_created(this, a, b); }
+ Movable(const Movable &m) { value = m.value; print_copy_created(this); }
+ Movable(Movable &&m) { value = std::move(m.value); print_move_created(this); }
+ std::string get_value() const { return std::to_string(value); }
+ ~Movable() { print_destroyed(this); }
+private:
+ int value;
+};
+
+class NCVirt {
+public:
+ virtual NonCopyable get_noncopyable(int a, int b) { return NonCopyable(a, b); }
+ virtual Movable get_movable(int a, int b) = 0;
+
+ std::string print_nc(int a, int b) { return get_noncopyable(a, b).get_value(); }
+ std::string print_movable(int a, int b) { return get_movable(a, b).get_value(); }
+};
+class NCVirtTrampoline : public NCVirt {
+#if !defined(__INTEL_COMPILER)
+ NonCopyable get_noncopyable(int a, int b) override {
+ PYBIND11_OVERLOAD(NonCopyable, NCVirt, get_noncopyable, a, b);
+ }
+#endif
+ Movable get_movable(int a, int b) override {
+ PYBIND11_OVERLOAD_PURE(Movable, NCVirt, get_movable, a, b);
+ }
+};
+
+int runExampleVirt(ExampleVirt *ex, int value) {
+ return ex->run(value);
+}
+
+bool runExampleVirtBool(ExampleVirt* ex) {
+ return ex->run_bool();
+}
+
+void runExampleVirtVirtual(ExampleVirt *ex) {
+ ex->pure_virtual();
+}
+
+
+// Inheriting virtual methods. We do two versions here: the repeat-everything version and the
+// templated trampoline versions mentioned in docs/advanced.rst.
+//
+// These base classes are exactly the same, but we technically need distinct
+// classes for this example code because we need to be able to bind them
+// properly (pybind11, sensibly, doesn't allow us to bind the same C++ class to
+// multiple python classes).
+class A_Repeat {
+#define A_METHODS \
+public: \
+ virtual int unlucky_number() = 0; \
+ virtual std::string say_something(unsigned times) { \
+ std::string s = ""; \
+ for (unsigned i = 0; i < times; ++i) \
+ s += "hi"; \
+ return s; \
+ } \
+ std::string say_everything() { \
+ return say_something(1) + " " + std::to_string(unlucky_number()); \
+ }
+A_METHODS
+};
+class B_Repeat : public A_Repeat {
+#define B_METHODS \
+public: \
+ int unlucky_number() override { return 13; } \
+ std::string say_something(unsigned times) override { \
+ return "B says hi " + std::to_string(times) + " times"; \
+ } \
+ virtual double lucky_number() { return 7.0; }
+B_METHODS
+};
+class C_Repeat : public B_Repeat {
+#define C_METHODS \
+public: \
+ int unlucky_number() override { return 4444; } \
+ double lucky_number() override { return 888; }
+C_METHODS
+};
+class D_Repeat : public C_Repeat {
+#define D_METHODS // Nothing overridden.
+D_METHODS
+};
+
+// Base classes for templated inheritance trampolines. Identical to the repeat-everything version:
+class A_Tpl { A_METHODS };
+class B_Tpl : public A_Tpl { B_METHODS };
+class C_Tpl : public B_Tpl { C_METHODS };
+class D_Tpl : public C_Tpl { D_METHODS };
+
+
+// Inheritance approach 1: each trampoline gets every virtual method (11 in total)
+class PyA_Repeat : public A_Repeat {
+public:
+ using A_Repeat::A_Repeat;
+ int unlucky_number() override { PYBIND11_OVERLOAD_PURE(int, A_Repeat, unlucky_number, ); }
+ std::string say_something(unsigned times) override { PYBIND11_OVERLOAD(std::string, A_Repeat, say_something, times); }
+};
+class PyB_Repeat : public B_Repeat {
+public:
+ using B_Repeat::B_Repeat;
+ int unlucky_number() override { PYBIND11_OVERLOAD(int, B_Repeat, unlucky_number, ); }
+ std::string say_something(unsigned times) override { PYBIND11_OVERLOAD(std::string, B_Repeat, say_something, times); }
+ double lucky_number() override { PYBIND11_OVERLOAD(double, B_Repeat, lucky_number, ); }
+};
+class PyC_Repeat : public C_Repeat {
+public:
+ using C_Repeat::C_Repeat;
+ int unlucky_number() override { PYBIND11_OVERLOAD(int, C_Repeat, unlucky_number, ); }
+ std::string say_something(unsigned times) override { PYBIND11_OVERLOAD(std::string, C_Repeat, say_something, times); }
+ double lucky_number() override { PYBIND11_OVERLOAD(double, C_Repeat, lucky_number, ); }
+};
+class PyD_Repeat : public D_Repeat {
+public:
+ using D_Repeat::D_Repeat;
+ int unlucky_number() override { PYBIND11_OVERLOAD(int, D_Repeat, unlucky_number, ); }
+ std::string say_something(unsigned times) override { PYBIND11_OVERLOAD(std::string, D_Repeat, say_something, times); }
+ double lucky_number() override { PYBIND11_OVERLOAD(double, D_Repeat, lucky_number, ); }
+};
+
+// Inheritance approach 2: templated trampoline classes.
+//
+// Advantages:
+// - we have only 2 (template) class and 4 method declarations (one per virtual method, plus one for
+// any override of a pure virtual method), versus 4 classes and 6 methods (MI) or 4 classes and 11
+// methods (repeat).
+// - Compared to MI, we also don't have to change the non-trampoline inheritance to virtual, and can
+// properly inherit constructors.
+//
+// Disadvantage:
+// - the compiler must still generate and compile 14 different methods (more, even, than the 11
+// required for the repeat approach) instead of the 6 required for MI. (If there was no pure
+// method (or no pure method override), the number would drop down to the same 11 as the repeat
+// approach).
+template <class Base = A_Tpl>
+class PyA_Tpl : public Base {
+public:
+ using Base::Base; // Inherit constructors
+ int unlucky_number() override { PYBIND11_OVERLOAD_PURE(int, Base, unlucky_number, ); }
+ std::string say_something(unsigned times) override { PYBIND11_OVERLOAD(std::string, Base, say_something, times); }
+};
+template <class Base = B_Tpl>
+class PyB_Tpl : public PyA_Tpl<Base> {
+public:
+ using PyA_Tpl<Base>::PyA_Tpl; // Inherit constructors (via PyA_Tpl's inherited constructors)
+ int unlucky_number() override { PYBIND11_OVERLOAD(int, Base, unlucky_number, ); }
+ double lucky_number() override { PYBIND11_OVERLOAD(double, Base, lucky_number, ); }
+};
+// Since C_Tpl and D_Tpl don't declare any new virtual methods, we don't actually need these (we can
+// use PyB_Tpl<C_Tpl> and PyB_Tpl<D_Tpl> for the trampoline classes instead):
+/*
+template <class Base = C_Tpl> class PyC_Tpl : public PyB_Tpl<Base> {
+public:
+ using PyB_Tpl<Base>::PyB_Tpl;
+};
+template <class Base = D_Tpl> class PyD_Tpl : public PyC_Tpl<Base> {
+public:
+ using PyC_Tpl<Base>::PyC_Tpl;
+};
+*/
+
+
+void initialize_inherited_virtuals(py::module &m) {
+ // Method 1: repeat
+ py::class_<A_Repeat, PyA_Repeat>(m, "A_Repeat")
+ .def(py::init<>())
+ .def("unlucky_number", &A_Repeat::unlucky_number)
+ .def("say_something", &A_Repeat::say_something)
+ .def("say_everything", &A_Repeat::say_everything);
+ py::class_<B_Repeat, A_Repeat, PyB_Repeat>(m, "B_Repeat")
+ .def(py::init<>())
+ .def("lucky_number", &B_Repeat::lucky_number);
+ py::class_<C_Repeat, B_Repeat, PyC_Repeat>(m, "C_Repeat")
+ .def(py::init<>());
+ py::class_<D_Repeat, C_Repeat, PyD_Repeat>(m, "D_Repeat")
+ .def(py::init<>());
+
+ // Method 2: Templated trampolines
+ py::class_<A_Tpl, PyA_Tpl<>>(m, "A_Tpl")
+ .def(py::init<>())
+ .def("unlucky_number", &A_Tpl::unlucky_number)
+ .def("say_something", &A_Tpl::say_something)
+ .def("say_everything", &A_Tpl::say_everything);
+ py::class_<B_Tpl, A_Tpl, PyB_Tpl<>>(m, "B_Tpl")
+ .def(py::init<>())
+ .def("lucky_number", &B_Tpl::lucky_number);
+ py::class_<C_Tpl, B_Tpl, PyB_Tpl<C_Tpl>>(m, "C_Tpl")
+ .def(py::init<>());
+ py::class_<D_Tpl, C_Tpl, PyB_Tpl<D_Tpl>>(m, "D_Tpl")
+ .def(py::init<>());
+
+};
+
+
+test_initializer virtual_functions([](py::module &m) {
+ /* Important: indicate the trampoline class PyExampleVirt using the third
+ argument to py::class_. The second argument with the unique pointer
+ is simply the default holder type used by pybind11. */
+ py::class_<ExampleVirt, PyExampleVirt>(m, "ExampleVirt")
+ .def(py::init<int>())
+ /* Reference original class in function definitions */
+ .def("run", &ExampleVirt::run)
+ .def("run_bool", &ExampleVirt::run_bool)
+ .def("pure_virtual", &ExampleVirt::pure_virtual);
+
+ py::class_<NonCopyable>(m, "NonCopyable")
+ .def(py::init<int, int>());
+
+ py::class_<Movable>(m, "Movable")
+ .def(py::init<int, int>());
+
+#if !defined(__INTEL_COMPILER)
+ py::class_<NCVirt, NCVirtTrampoline>(m, "NCVirt")
+ .def(py::init<>())
+ .def("get_noncopyable", &NCVirt::get_noncopyable)
+ .def("get_movable", &NCVirt::get_movable)
+ .def("print_nc", &NCVirt::print_nc)
+ .def("print_movable", &NCVirt::print_movable);
+#endif
+
+ m.def("runExampleVirt", &runExampleVirt);
+ m.def("runExampleVirtBool", &runExampleVirtBool);
+ m.def("runExampleVirtVirtual", &runExampleVirtVirtual);
+
+ m.def("cstats_debug", &ConstructorStats::get<ExampleVirt>);
+ initialize_inherited_virtuals(m);
+});
diff --git a/ext/pybind11/tests/test_virtual_functions.py b/ext/pybind11/tests/test_virtual_functions.py
new file mode 100644
index 000000000..a9aecd67f
--- /dev/null
+++ b/ext/pybind11/tests/test_virtual_functions.py
@@ -0,0 +1,256 @@
+import pytest
+import pybind11_tests
+from pybind11_tests import ConstructorStats
+
+
+def test_override(capture, msg):
+ from pybind11_tests import (ExampleVirt, runExampleVirt, runExampleVirtVirtual,
+ runExampleVirtBool)
+
+ class ExtendedExampleVirt(ExampleVirt):
+ def __init__(self, state):
+ super(ExtendedExampleVirt, self).__init__(state + 1)
+ self.data = "Hello world"
+
+ def run(self, value):
+ print('ExtendedExampleVirt::run(%i), calling parent..' % value)
+ return super(ExtendedExampleVirt, self).run(value + 1)
+
+ def run_bool(self):
+ print('ExtendedExampleVirt::run_bool()')
+ return False
+
+ def get_string1(self):
+ return "override1"
+
+ def pure_virtual(self):
+ print('ExtendedExampleVirt::pure_virtual(): %s' % self.data)
+
+ class ExtendedExampleVirt2(ExtendedExampleVirt):
+ def __init__(self, state):
+ super(ExtendedExampleVirt2, self).__init__(state + 1)
+
+ def get_string2(self):
+ return "override2"
+
+ ex12 = ExampleVirt(10)
+ with capture:
+ assert runExampleVirt(ex12, 20) == 30
+ assert capture == """
+ Original implementation of ExampleVirt::run(state=10, value=20, str1=default1, str2=default2)
+ """ # noqa: E501 line too long
+
+ with pytest.raises(RuntimeError) as excinfo:
+ runExampleVirtVirtual(ex12)
+ assert msg(excinfo.value) == 'Tried to call pure virtual function "ExampleVirt::pure_virtual"'
+
+ ex12p = ExtendedExampleVirt(10)
+ with capture:
+ assert runExampleVirt(ex12p, 20) == 32
+ assert capture == """
+ ExtendedExampleVirt::run(20), calling parent..
+ Original implementation of ExampleVirt::run(state=11, value=21, str1=override1, str2=default2)
+ """ # noqa: E501 line too long
+ with capture:
+ assert runExampleVirtBool(ex12p) is False
+ assert capture == "ExtendedExampleVirt::run_bool()"
+ with capture:
+ runExampleVirtVirtual(ex12p)
+ assert capture == "ExtendedExampleVirt::pure_virtual(): Hello world"
+
+ ex12p2 = ExtendedExampleVirt2(15)
+ with capture:
+ assert runExampleVirt(ex12p2, 50) == 68
+ assert capture == """
+ ExtendedExampleVirt::run(50), calling parent..
+ Original implementation of ExampleVirt::run(state=17, value=51, str1=override1, str2=override2)
+ """ # noqa: E501 line too long
+
+ cstats = ConstructorStats.get(ExampleVirt)
+ assert cstats.alive() == 3
+ del ex12, ex12p, ex12p2
+ assert cstats.alive() == 0
+ assert cstats.values() == ['10', '11', '17']
+ assert cstats.copy_constructions == 0
+ assert cstats.move_constructions >= 0
+
+
+def test_inheriting_repeat():
+ from pybind11_tests import A_Repeat, B_Repeat, C_Repeat, D_Repeat, A_Tpl, B_Tpl, C_Tpl, D_Tpl
+
+ class AR(A_Repeat):
+ def unlucky_number(self):
+ return 99
+
+ class AT(A_Tpl):
+ def unlucky_number(self):
+ return 999
+
+ obj = AR()
+ assert obj.say_something(3) == "hihihi"
+ assert obj.unlucky_number() == 99
+ assert obj.say_everything() == "hi 99"
+
+ obj = AT()
+ assert obj.say_something(3) == "hihihi"
+ assert obj.unlucky_number() == 999
+ assert obj.say_everything() == "hi 999"
+
+ for obj in [B_Repeat(), B_Tpl()]:
+ assert obj.say_something(3) == "B says hi 3 times"
+ assert obj.unlucky_number() == 13
+ assert obj.lucky_number() == 7.0
+ assert obj.say_everything() == "B says hi 1 times 13"
+
+ for obj in [C_Repeat(), C_Tpl()]:
+ assert obj.say_something(3) == "B says hi 3 times"
+ assert obj.unlucky_number() == 4444
+ assert obj.lucky_number() == 888.0
+ assert obj.say_everything() == "B says hi 1 times 4444"
+
+ class CR(C_Repeat):
+ def lucky_number(self):
+ return C_Repeat.lucky_number(self) + 1.25
+
+ obj = CR()
+ assert obj.say_something(3) == "B says hi 3 times"
+ assert obj.unlucky_number() == 4444
+ assert obj.lucky_number() == 889.25
+ assert obj.say_everything() == "B says hi 1 times 4444"
+
+ class CT(C_Tpl):
+ pass
+
+ obj = CT()
+ assert obj.say_something(3) == "B says hi 3 times"
+ assert obj.unlucky_number() == 4444
+ assert obj.lucky_number() == 888.0
+ assert obj.say_everything() == "B says hi 1 times 4444"
+
+ class CCR(CR):
+ def lucky_number(self):
+ return CR.lucky_number(self) * 10
+
+ obj = CCR()
+ assert obj.say_something(3) == "B says hi 3 times"
+ assert obj.unlucky_number() == 4444
+ assert obj.lucky_number() == 8892.5
+ assert obj.say_everything() == "B says hi 1 times 4444"
+
+ class CCT(CT):
+ def lucky_number(self):
+ return CT.lucky_number(self) * 1000
+
+ obj = CCT()
+ assert obj.say_something(3) == "B says hi 3 times"
+ assert obj.unlucky_number() == 4444
+ assert obj.lucky_number() == 888000.0
+ assert obj.say_everything() == "B says hi 1 times 4444"
+
+ class DR(D_Repeat):
+ def unlucky_number(self):
+ return 123
+
+ def lucky_number(self):
+ return 42.0
+
+ for obj in [D_Repeat(), D_Tpl()]:
+ assert obj.say_something(3) == "B says hi 3 times"
+ assert obj.unlucky_number() == 4444
+ assert obj.lucky_number() == 888.0
+ assert obj.say_everything() == "B says hi 1 times 4444"
+
+ obj = DR()
+ assert obj.say_something(3) == "B says hi 3 times"
+ assert obj.unlucky_number() == 123
+ assert obj.lucky_number() == 42.0
+ assert obj.say_everything() == "B says hi 1 times 123"
+
+ class DT(D_Tpl):
+ def say_something(self, times):
+ return "DT says:" + (' quack' * times)
+
+ def unlucky_number(self):
+ return 1234
+
+ def lucky_number(self):
+ return -4.25
+
+ obj = DT()
+ assert obj.say_something(3) == "DT says: quack quack quack"
+ assert obj.unlucky_number() == 1234
+ assert obj.lucky_number() == -4.25
+ assert obj.say_everything() == "DT says: quack 1234"
+
+ class DT2(DT):
+ def say_something(self, times):
+ return "DT2: " + ('QUACK' * times)
+
+ def unlucky_number(self):
+ return -3
+
+ class BT(B_Tpl):
+ def say_something(self, times):
+ return "BT" * times
+
+ def unlucky_number(self):
+ return -7
+
+ def lucky_number(self):
+ return -1.375
+
+ obj = BT()
+ assert obj.say_something(3) == "BTBTBT"
+ assert obj.unlucky_number() == -7
+ assert obj.lucky_number() == -1.375
+ assert obj.say_everything() == "BT -7"
+
+
+@pytest.mark.skipif(not hasattr(pybind11_tests, 'NCVirt'),
+ reason="NCVirt test broken on ICPC")
+def test_move_support():
+ from pybind11_tests import NCVirt, NonCopyable, Movable
+
+ class NCVirtExt(NCVirt):
+ def get_noncopyable(self, a, b):
+ # Constructs and returns a new instance:
+ nc = NonCopyable(a * a, b * b)
+ return nc
+
+ def get_movable(self, a, b):
+ # Return a referenced copy
+ self.movable = Movable(a, b)
+ return self.movable
+
+ class NCVirtExt2(NCVirt):
+ def get_noncopyable(self, a, b):
+ # Keep a reference: this is going to throw an exception
+ self.nc = NonCopyable(a, b)
+ return self.nc
+
+ def get_movable(self, a, b):
+ # Return a new instance without storing it
+ return Movable(a, b)
+
+ ncv1 = NCVirtExt()
+ assert ncv1.print_nc(2, 3) == "36"
+ assert ncv1.print_movable(4, 5) == "9"
+ ncv2 = NCVirtExt2()
+ assert ncv2.print_movable(7, 7) == "14"
+ # Don't check the exception message here because it differs under debug/non-debug mode
+ with pytest.raises(RuntimeError):
+ ncv2.print_nc(9, 9)
+
+ nc_stats = ConstructorStats.get(NonCopyable)
+ mv_stats = ConstructorStats.get(Movable)
+ assert nc_stats.alive() == 1
+ assert mv_stats.alive() == 1
+ del ncv1, ncv2
+ assert nc_stats.alive() == 0
+ assert mv_stats.alive() == 0
+ assert nc_stats.values() == ['4', '9', '9', '9']
+ assert mv_stats.values() == ['4', '5', '7', '7']
+ assert nc_stats.copy_constructions == 0
+ assert mv_stats.copy_constructions == 1
+ assert nc_stats.move_constructions >= 0
+ assert mv_stats.move_constructions >= 0