Unverified Commit e315e1fe authored by Henry Schreiner's avatar Henry Schreiner
Browse files

Merge branch 'master' into stable

parents 71fd5241 97976c16
......@@ -35,4 +35,5 @@ add_custom_target(
PYTHONPATH=$<TARGET_FILE_DIR:test_installed_function>
${_Python_EXECUTABLE}
${PROJECT_SOURCE_DIR}/../test.py
${PROJECT_NAME})
${PROJECT_NAME}
DEPENDS test_installed_function)
......@@ -42,4 +42,5 @@ add_custom_target(
PYTHONPATH=$<TARGET_FILE_DIR:test_installed_target>
${_Python_EXECUTABLE}
${PROJECT_SOURCE_DIR}/../test.py
${PROJECT_NAME})
${PROJECT_NAME}
DEPENDS test_installed_target)
......@@ -23,8 +23,10 @@ add_executable(test_subdirectory_embed ../embed.cpp)
target_link_libraries(test_subdirectory_embed PRIVATE pybind11::embed)
set_target_properties(test_subdirectory_embed PROPERTIES OUTPUT_NAME test_cmake_build)
add_custom_target(check_subdirectory_embed $<TARGET_FILE:test_subdirectory_embed>
"${PROJECT_SOURCE_DIR}/../test.py")
add_custom_target(
check_subdirectory_embed
$<TARGET_FILE:test_subdirectory_embed> "${PROJECT_SOURCE_DIR}/../test.py"
DEPENDS test_subdirectory_embed)
# Test custom export group -- PYBIND11_EXPORT_NAME
add_library(test_embed_lib ../embed.cpp)
......
......@@ -31,4 +31,5 @@ add_custom_target(
PYTHONPATH=$<TARGET_FILE_DIR:test_subdirectory_function>
${_Python_EXECUTABLE}
${PROJECT_SOURCE_DIR}/../test.py
${PROJECT_NAME})
${PROJECT_NAME}
DEPENDS test_subdirectory_function)
......@@ -37,4 +37,5 @@ add_custom_target(
PYTHONPATH=$<TARGET_FILE_DIR:test_subdirectory_target>
${_Python_EXECUTABLE}
${PROJECT_SOURCE_DIR}/../test.py
${PROJECT_NAME})
${PROJECT_NAME}
DEPENDS test_subdirectory_target)
# -*- coding: utf-8 -*-
import sys
import test_cmake_build
if str is not bytes: # If not Python2
assert isinstance(__file__, str) # Test this is properly set
assert test_cmake_build.add(1, 2) == 3
print("{} imports, runs, and adds: 1 + 2 = 3".format(sys.argv[1]))
......@@ -37,7 +37,7 @@ template <> lacking_move_ctor empty<lacking_move_ctor>::instance_ = {};
class MoveOnlyInt {
public:
MoveOnlyInt() { print_default_created(this); }
MoveOnlyInt(int v) : value{v} { print_created(this, value); }
explicit MoveOnlyInt(int v) : value{v} { print_created(this, value); }
MoveOnlyInt(MoveOnlyInt &&m) noexcept {
print_move_created(this, m.value);
std::swap(value, m.value);
......@@ -56,7 +56,7 @@ public:
class MoveOrCopyInt {
public:
MoveOrCopyInt() { print_default_created(this); }
MoveOrCopyInt(int v) : value{v} { print_created(this, value); }
explicit MoveOrCopyInt(int v) : value{v} { print_created(this, value); }
MoveOrCopyInt(MoveOrCopyInt &&m) noexcept {
print_move_created(this, m.value);
std::swap(value, m.value);
......@@ -75,7 +75,7 @@ public:
class CopyOnlyInt {
public:
CopyOnlyInt() { print_default_created(this); }
CopyOnlyInt(int v) : value{v} { print_created(this, value); }
explicit CopyOnlyInt(int v) : value{v} { print_created(this, value); }
CopyOnlyInt(const CopyOnlyInt &c) { print_copy_created(this, c.value); value = c.value; }
CopyOnlyInt &operator=(const CopyOnlyInt &c) { print_copy_assigned(this, c.value); value = c.value; return *this; }
~CopyOnlyInt() { print_destroyed(this); }
......@@ -107,8 +107,8 @@ public:
if (!src) return none().release();
return cast(*src, policy, parent);
}
operator CopyOnlyInt*() { return &value; }
operator CopyOnlyInt&() { return value; }
explicit operator CopyOnlyInt *() { return &value; }
explicit operator CopyOnlyInt &() { return value; }
template <typename T> using cast_op_type = pybind11::detail::cast_op_type<T>;
};
PYBIND11_NAMESPACE_END(detail)
......@@ -219,7 +219,7 @@ TEST_SUBMODULE(copy_move_policies, m) {
// #389: rvp::move should fall-through to copy on non-movable objects
struct MoveIssue1 {
int v;
MoveIssue1(int v) : v{v} {}
explicit MoveIssue1(int v) : v{v} {}
MoveIssue1(const MoveIssue1 &c) = default;
MoveIssue1(MoveIssue1 &&) = delete;
};
......@@ -227,7 +227,7 @@ TEST_SUBMODULE(copy_move_policies, m) {
struct MoveIssue2 {
int v;
MoveIssue2(int v) : v{v} {}
explicit MoveIssue2(int v) : v{v} {}
MoveIssue2(MoveIssue2 &&) = default;
};
py::class_<MoveIssue2>(m, "MoveIssue2").def(py::init<int>()).def_readwrite("value", &MoveIssue2::v);
......
# -*- coding: utf-8 -*-
import pytest
from pybind11_tests import copy_move_policies as m
......
# -*- coding: utf-8 -*-
import pytest
from pybind11_tests import custom_type_casters as m
......
/*
tests/test_custom_type_setup.cpp -- Tests `pybind11::custom_type_setup`
Copyright (c) Google LLC
All rights reserved. Use of this source code is governed by a
BSD-style license that can be found in the LICENSE file.
*/
#include <pybind11/pybind11.h>
#include "pybind11_tests.h"
namespace py = pybind11;
namespace {
struct OwnsPythonObjects {
py::object value = py::none();
};
} // namespace
TEST_SUBMODULE(custom_type_setup, m) {
py::class_<OwnsPythonObjects> cls(
m, "OwnsPythonObjects", py::custom_type_setup([](PyHeapTypeObject *heap_type) {
auto *type = &heap_type->ht_type;
type->tp_flags |= Py_TPFLAGS_HAVE_GC;
type->tp_traverse = [](PyObject *self_base, visitproc visit, void *arg) {
auto &self = py::cast<OwnsPythonObjects &>(py::handle(self_base));
Py_VISIT(self.value.ptr());
return 0;
};
type->tp_clear = [](PyObject *self_base) {
auto &self = py::cast<OwnsPythonObjects &>(py::handle(self_base));
self.value = py::none();
return 0;
};
}));
cls.def(py::init<>());
cls.def_readwrite("value", &OwnsPythonObjects::value);
}
# -*- coding: utf-8 -*-
import gc
import weakref
import pytest
import env # noqa: F401
from pybind11_tests import custom_type_setup as m
@pytest.fixture
def gc_tester():
"""Tests that an object is garbage collected.
Assumes that any unreferenced objects are fully collected after calling
`gc.collect()`. That is true on CPython, but does not appear to reliably
hold on PyPy.
"""
weak_refs = []
def add_ref(obj):
# PyPy does not support `gc.is_tracked`.
if hasattr(gc, "is_tracked"):
assert gc.is_tracked(obj)
weak_refs.append(weakref.ref(obj))
yield add_ref
gc.collect()
for ref in weak_refs:
assert ref() is None
# PyPy does not seem to reliably garbage collect.
@pytest.mark.skipif("env.PYPY")
def test_self_cycle(gc_tester):
obj = m.OwnsPythonObjects()
obj.value = obj
gc_tester(obj)
# PyPy does not seem to reliably garbage collect.
@pytest.mark.skipif("env.PYPY")
def test_indirect_cycle(gc_tester):
obj = m.OwnsPythonObjects()
obj_list = [obj]
obj.value = obj_list
gc_tester(obj)
......@@ -178,6 +178,7 @@ TEST_SUBMODULE(eigen, m) {
ReturnTester() { print_created(this); }
~ReturnTester() { print_destroyed(this); }
static Eigen::MatrixXd create() { return Eigen::MatrixXd::Ones(10, 10); }
// NOLINTNEXTLINE(readability-const-return-type)
static const Eigen::MatrixXd createConst() { return Eigen::MatrixXd::Ones(10, 10); }
Eigen::MatrixXd &get() { return mat; }
Eigen::MatrixXd *getPtr() { return &mat; }
......@@ -244,6 +245,9 @@ TEST_SUBMODULE(eigen, m) {
// test_fixed, and various other tests
m.def("fixed_r", [mat]() -> FixedMatrixR { return FixedMatrixR(mat); });
// Our Eigen does a hack which respects constness through the numpy writeable flag.
// Therefore, the const return actually affects this type despite being an rvalue.
// NOLINTNEXTLINE(readability-const-return-type)
m.def("fixed_r_const", [mat]() -> const FixedMatrixR { return FixedMatrixR(mat); });
m.def("fixed_c", [mat]() -> FixedMatrixC { return FixedMatrixC(mat); });
m.def("fixed_copy_r", [](const FixedMatrixR &m) -> FixedMatrixR { return m; });
......
# -*- coding: utf-8 -*-
import pytest
from pybind11_tests import ConstructorStats
np = pytest.importorskip("numpy")
......
......@@ -31,6 +31,7 @@ endif()
add_custom_target(
cpptest
COMMAND "$<TARGET_FILE:test_embed>"
DEPENDS test_embed
WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}")
pybind11_add_module(external_module THIN_LTO external_module.cpp)
......
......@@ -9,7 +9,7 @@ namespace py = pybind11;
PYBIND11_MODULE(external_module, m) {
class A {
public:
A(int value) : v{value} {};
explicit A(int value) : v{value} {};
int v;
};
......
......@@ -8,6 +8,7 @@
#include <catch.hpp>
#include <cstdlib>
#include <fstream>
#include <functional>
#include <thread>
......@@ -18,11 +19,12 @@ using namespace py::literals;
class Widget {
public:
Widget(std::string message) : message(std::move(message)) {}
explicit Widget(std::string message) : message(std::move(message)) {}
virtual ~Widget() = default;
std::string the_message() const { return message; }
virtual int the_answer() const = 0;
virtual std::string argv0() const = 0;
private:
std::string message;
......@@ -32,6 +34,7 @@ class PyWidget final : public Widget {
using Widget::Widget;
int the_answer() const override { PYBIND11_OVERRIDE_PURE(int, Widget, the_answer); }
std::string argv0() const override { PYBIND11_OVERRIDE_PURE(std::string, Widget, argv0); }
};
PYBIND11_EMBEDDED_MODULE(widget_module, m) {
......@@ -74,8 +77,24 @@ TEST_CASE("Import error handling") {
REQUIRE_NOTHROW(py::module_::import("widget_module"));
REQUIRE_THROWS_WITH(py::module_::import("throw_exception"),
"ImportError: C++ Error");
#if PY_VERSION_HEX >= 0x03030000
REQUIRE_THROWS_WITH(py::module_::import("throw_error_already_set"),
Catch::Contains("ImportError: initialization failed"));
auto locals = py::dict("is_keyerror"_a=false, "message"_a="not set");
py::exec(R"(
try:
import throw_error_already_set
except ImportError as e:
is_keyerror = type(e.__cause__) == KeyError
message = str(e.__cause__)
)", py::globals(), locals);
REQUIRE(locals["is_keyerror"].cast<bool>() == true);
REQUIRE(locals["message"].cast<std::string>() == "'missing'");
#else
REQUIRE_THROWS_WITH(py::module_::import("throw_error_already_set"),
Catch::Contains("ImportError: KeyError"));
#endif
}
TEST_CASE("There can be only one interpreter") {
......@@ -283,3 +302,25 @@ TEST_CASE("Reload module from file") {
result = module_.attr("test")().cast<int>();
REQUIRE(result == 2);
}
TEST_CASE("sys.argv gets initialized properly") {
py::finalize_interpreter();
{
py::scoped_interpreter default_scope;
auto module = py::module::import("test_interpreter");
auto py_widget = module.attr("DerivedWidget")("The question");
const auto &cpp_widget = py_widget.cast<const Widget &>();
REQUIRE(cpp_widget.argv0().empty());
}
{
char *argv[] = {strdup("a.out")};
py::scoped_interpreter argv_scope(true, 1, argv);
std::free(argv[0]);
auto module = py::module::import("test_interpreter");
auto py_widget = module.attr("DerivedWidget")("The question");
const auto &cpp_widget = py_widget.cast<const Widget &>();
REQUIRE(cpp_widget.argv0() == "a.out");
}
py::initialize_interpreter();
}
# -*- coding: utf-8 -*-
import sys
from widget_module import Widget
......@@ -8,3 +10,6 @@ class DerivedWidget(Widget):
def the_answer(self):
return 42
def argv0(self):
return sys.argv[0]
......@@ -84,4 +84,65 @@ TEST_SUBMODULE(enums, m) {
.value("ONE", SimpleEnum::THREE)
.export_values();
});
// test_enum_scalar
enum UnscopedUCharEnum : unsigned char {};
enum class ScopedShortEnum : short {};
enum class ScopedLongEnum : long {};
enum UnscopedUInt64Enum : std::uint64_t {};
static_assert(py::detail::all_of<
std::is_same<py::enum_<UnscopedUCharEnum>::Scalar, unsigned char>,
std::is_same<py::enum_<ScopedShortEnum>::Scalar, short>,
std::is_same<py::enum_<ScopedLongEnum>::Scalar, long>,
std::is_same<py::enum_<UnscopedUInt64Enum>::Scalar, std::uint64_t>
>::value, "Error during the deduction of enum's scalar type with normal integer underlying");
// test_enum_scalar_with_char_underlying
enum class ScopedCharEnum : char { Zero, Positive };
enum class ScopedWCharEnum : wchar_t { Zero, Positive };
enum class ScopedChar32Enum : char32_t { Zero, Positive };
enum class ScopedChar16Enum : char16_t { Zero, Positive };
// test the scalar of char type enums according to chapter 'Character types'
// from https://en.cppreference.com/w/cpp/language/types
static_assert(py::detail::any_of<
std::is_same<py::enum_<ScopedCharEnum>::Scalar, signed char>, // e.g. gcc on x86
std::is_same<py::enum_<ScopedCharEnum>::Scalar, unsigned char> // e.g. arm linux
>::value, "char should be cast to either signed char or unsigned char");
static_assert(
sizeof(py::enum_<ScopedWCharEnum>::Scalar) == 2 ||
sizeof(py::enum_<ScopedWCharEnum>::Scalar) == 4
, "wchar_t should be either 16 bits (Windows) or 32 (everywhere else)");
static_assert(py::detail::all_of<
std::is_same<py::enum_<ScopedChar32Enum>::Scalar, std::uint_least32_t>,
std::is_same<py::enum_<ScopedChar16Enum>::Scalar, std::uint_least16_t>
>::value, "char32_t, char16_t (and char8_t)'s size, signedness, and alignment is determined");
#if defined(PYBIND11_HAS_U8STRING)
enum class ScopedChar8Enum : char8_t { Zero, Positive };
static_assert(std::is_same<py::enum_<ScopedChar8Enum>::Scalar, unsigned char>::value);
#endif
// test_char_underlying_enum
py::enum_<ScopedCharEnum>(m, "ScopedCharEnum")
.value("Zero", ScopedCharEnum::Zero)
.value("Positive", ScopedCharEnum::Positive);
py::enum_<ScopedWCharEnum>(m, "ScopedWCharEnum")
.value("Zero", ScopedWCharEnum::Zero)
.value("Positive", ScopedWCharEnum::Positive);
py::enum_<ScopedChar32Enum>(m, "ScopedChar32Enum")
.value("Zero", ScopedChar32Enum::Zero)
.value("Positive", ScopedChar32Enum::Positive);
py::enum_<ScopedChar16Enum>(m, "ScopedChar16Enum")
.value("Zero", ScopedChar16Enum::Zero)
.value("Positive", ScopedChar16Enum::Positive);
// test_bool_underlying_enum
enum class ScopedBoolEnum : bool { FALSE, TRUE };
// bool is unsigned (std::is_signed returns false) and 1-byte long, so represented with u8
static_assert(std::is_same<py::enum_<ScopedBoolEnum>::Scalar, std::uint8_t>::value, "");
py::enum_<ScopedBoolEnum>(m, "ScopedBoolEnum")
.value("FALSE", ScopedBoolEnum::FALSE)
.value("TRUE", ScopedBoolEnum::TRUE);
}
# -*- coding: utf-8 -*-
import pytest
import env
from pybind11_tests import enums as m
......@@ -217,10 +219,16 @@ def test_binary_operators():
def test_enum_to_int():
m.test_enum_to_int(m.Flags.Read)
m.test_enum_to_int(m.ClassWithUnscopedEnum.EMode.EFirstMode)
m.test_enum_to_int(m.ScopedCharEnum.Positive)
m.test_enum_to_int(m.ScopedBoolEnum.TRUE)
m.test_enum_to_uint(m.Flags.Read)
m.test_enum_to_uint(m.ClassWithUnscopedEnum.EMode.EFirstMode)
m.test_enum_to_uint(m.ScopedCharEnum.Positive)
m.test_enum_to_uint(m.ScopedBoolEnum.TRUE)
m.test_enum_to_long_long(m.Flags.Read)
m.test_enum_to_long_long(m.ClassWithUnscopedEnum.EMode.EFirstMode)
m.test_enum_to_long_long(m.ScopedCharEnum.Positive)
m.test_enum_to_long_long(m.ScopedBoolEnum.TRUE)
def test_duplicate_enum_name():
......@@ -229,6 +237,34 @@ def test_duplicate_enum_name():
assert str(excinfo.value) == 'SimpleEnum: element "ONE" already exists!'
def test_char_underlying_enum(): # Issue #1331/PR #1334:
assert type(m.ScopedCharEnum.Positive.__int__()) is int
assert int(m.ScopedChar16Enum.Zero) == 0
assert hash(m.ScopedChar32Enum.Positive) == 1
if env.PY2:
assert m.ScopedCharEnum.Positive.__getstate__() == 1 # long
else:
assert type(m.ScopedCharEnum.Positive.__getstate__()) is int
assert m.ScopedWCharEnum(1) == m.ScopedWCharEnum.Positive
with pytest.raises(TypeError):
# Even if the underlying type is char, only an int can be used to construct the enum:
m.ScopedCharEnum("0")
def test_bool_underlying_enum():
assert type(m.ScopedBoolEnum.TRUE.__int__()) is int
assert int(m.ScopedBoolEnum.FALSE) == 0
assert hash(m.ScopedBoolEnum.TRUE) == 1
if env.PY2:
assert m.ScopedBoolEnum.TRUE.__getstate__() == 1 # long
else:
assert type(m.ScopedBoolEnum.TRUE.__getstate__()) is int
assert m.ScopedBoolEnum(1) == m.ScopedBoolEnum.TRUE
# Enum could construct with a bool
# (bool is a strict subclass of int, and False will be converted to 0)
assert m.ScopedBoolEnum(False) == m.ScopedBoolEnum.FALSE
def test_docstring_signatures():
for enum_type in [m.ScopedEnum, m.UnscopedEnum]:
for attr in enum_type.__dict__.values():
......
......@@ -98,4 +98,22 @@ TEST_SUBMODULE(eval_, m) {
auto int_class = py::eval("isinstance(42, int)", global);
return global;
});
// test_eval_closure
m.def("test_eval_closure", []() {
py::dict global;
global["closure_value"] = 42;
py::dict local;
local["closure_value"] = 0;
py::exec(R"(
local_value = closure_value
def func_global():
return closure_value
def func_local():
return local_value
)", global, local);
return std::make_pair(global, local);
});
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment