mirror of
https://github.com/pybind/pybind11.git
synced 2024-11-22 05:05:11 +00:00
Refactor: Extract Custom Type Casts related tests
This commit is contained in:
parent
ae2ee2a4a5
commit
714424387f
@ -36,6 +36,7 @@ set(PYBIND11_TEST_FILES
|
|||||||
test_class.cpp
|
test_class.cpp
|
||||||
test_constants_and_functions.cpp
|
test_constants_and_functions.cpp
|
||||||
test_copy_move.cpp
|
test_copy_move.cpp
|
||||||
|
test_custom_type_casters.cpp
|
||||||
test_docstring_options.cpp
|
test_docstring_options.cpp
|
||||||
test_eigen.cpp
|
test_eigen.cpp
|
||||||
test_enum.cpp
|
test_enum.cpp
|
||||||
|
125
tests/test_custom_type_casters.cpp
Normal file
125
tests/test_custom_type_casters.cpp
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
/*
|
||||||
|
tests/test_custom_type_casters.cpp -- tests type_caster<T>
|
||||||
|
|
||||||
|
Copyright (c) 2016 Wenzel Jakob <wenzel.jakob@epfl.ch>
|
||||||
|
|
||||||
|
All rights reserved. Use of this source code is governed by a
|
||||||
|
BSD-style license that can be found in the LICENSE file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "pybind11_tests.h"
|
||||||
|
#include "constructor_stats.h"
|
||||||
|
|
||||||
|
|
||||||
|
// py::arg/py::arg_v testing: these arguments just record their argument when invoked
|
||||||
|
class ArgInspector1 { public: std::string arg = "(default arg inspector 1)"; };
|
||||||
|
class ArgInspector2 { public: std::string arg = "(default arg inspector 2)"; };
|
||||||
|
class ArgAlwaysConverts { };
|
||||||
|
namespace pybind11 { namespace detail {
|
||||||
|
template <> struct type_caster<ArgInspector1> {
|
||||||
|
public:
|
||||||
|
PYBIND11_TYPE_CASTER(ArgInspector1, _("ArgInspector1"));
|
||||||
|
|
||||||
|
bool load(handle src, bool convert) {
|
||||||
|
value.arg = "loading ArgInspector1 argument " +
|
||||||
|
std::string(convert ? "WITH" : "WITHOUT") + " conversion allowed. "
|
||||||
|
"Argument value = " + (std::string) str(src);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static handle cast(const ArgInspector1 &src, return_value_policy, handle) {
|
||||||
|
return str(src.arg).release();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
template <> struct type_caster<ArgInspector2> {
|
||||||
|
public:
|
||||||
|
PYBIND11_TYPE_CASTER(ArgInspector2, _("ArgInspector2"));
|
||||||
|
|
||||||
|
bool load(handle src, bool convert) {
|
||||||
|
value.arg = "loading ArgInspector2 argument " +
|
||||||
|
std::string(convert ? "WITH" : "WITHOUT") + " conversion allowed. "
|
||||||
|
"Argument value = " + (std::string) str(src);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static handle cast(const ArgInspector2 &src, return_value_policy, handle) {
|
||||||
|
return str(src.arg).release();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
template <> struct type_caster<ArgAlwaysConverts> {
|
||||||
|
public:
|
||||||
|
PYBIND11_TYPE_CASTER(ArgAlwaysConverts, _("ArgAlwaysConverts"));
|
||||||
|
|
||||||
|
bool load(handle, bool convert) {
|
||||||
|
return convert;
|
||||||
|
}
|
||||||
|
|
||||||
|
static handle cast(const ArgAlwaysConverts &, return_value_policy, handle) {
|
||||||
|
return py::none().release();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}}
|
||||||
|
|
||||||
|
// test_custom_caster_destruction
|
||||||
|
class DestructionTester {
|
||||||
|
public:
|
||||||
|
DestructionTester() { print_default_created(this); }
|
||||||
|
~DestructionTester() { print_destroyed(this); }
|
||||||
|
DestructionTester(const DestructionTester &) { print_copy_created(this); }
|
||||||
|
DestructionTester(DestructionTester &&) { print_move_created(this); }
|
||||||
|
DestructionTester &operator=(const DestructionTester &) { print_copy_assigned(this); return *this; }
|
||||||
|
DestructionTester &operator=(DestructionTester &&) { print_move_assigned(this); return *this; }
|
||||||
|
};
|
||||||
|
namespace pybind11 { namespace detail {
|
||||||
|
template <> struct type_caster<DestructionTester> {
|
||||||
|
PYBIND11_TYPE_CASTER(DestructionTester, _("DestructionTester"));
|
||||||
|
bool load(handle, bool) { return true; }
|
||||||
|
|
||||||
|
static handle cast(const DestructionTester &, return_value_policy, handle) {
|
||||||
|
return py::bool_(true).release();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}}
|
||||||
|
|
||||||
|
TEST_SUBMODULE(custom_type_casters, m) {
|
||||||
|
// test_custom_type_casters
|
||||||
|
|
||||||
|
// test_noconvert_args
|
||||||
|
//
|
||||||
|
// Test converting. The ArgAlwaysConverts is just there to make the first no-conversion pass
|
||||||
|
// fail so that our call always ends up happening via the second dispatch (the one that allows
|
||||||
|
// some conversion).
|
||||||
|
class ArgInspector {
|
||||||
|
public:
|
||||||
|
ArgInspector1 f(ArgInspector1 a, ArgAlwaysConverts) { return a; }
|
||||||
|
std::string g(ArgInspector1 a, const ArgInspector1 &b, int c, ArgInspector2 *d, ArgAlwaysConverts) {
|
||||||
|
return a.arg + "\n" + b.arg + "\n" + std::to_string(c) + "\n" + d->arg;
|
||||||
|
}
|
||||||
|
static ArgInspector2 h(ArgInspector2 a, ArgAlwaysConverts) { return a; }
|
||||||
|
};
|
||||||
|
py::class_<ArgInspector>(m, "ArgInspector")
|
||||||
|
.def(py::init<>())
|
||||||
|
.def("f", &ArgInspector::f, py::arg(), py::arg() = ArgAlwaysConverts())
|
||||||
|
.def("g", &ArgInspector::g, "a"_a.noconvert(), "b"_a, "c"_a.noconvert()=13, "d"_a=ArgInspector2(), py::arg() = ArgAlwaysConverts())
|
||||||
|
.def_static("h", &ArgInspector::h, py::arg().noconvert(), py::arg() = ArgAlwaysConverts())
|
||||||
|
;
|
||||||
|
m.def("arg_inspect_func", [](ArgInspector2 a, ArgInspector1 b, ArgAlwaysConverts) { return a.arg + "\n" + b.arg; },
|
||||||
|
py::arg().noconvert(false), py::arg_v(nullptr, ArgInspector1()).noconvert(true), py::arg() = ArgAlwaysConverts());
|
||||||
|
|
||||||
|
m.def("floats_preferred", [](double f) { return 0.5 * f; }, py::arg("f"));
|
||||||
|
m.def("floats_only", [](double f) { return 0.5 * f; }, py::arg("f").noconvert());
|
||||||
|
m.def("ints_preferred", [](int i) { return i / 2; }, py::arg("i"));
|
||||||
|
m.def("ints_only", [](int i) { return i / 2; }, py::arg("i").noconvert());
|
||||||
|
|
||||||
|
// test_custom_caster_destruction
|
||||||
|
// Test that `take_ownership` works on types with a custom type caster when given a pointer
|
||||||
|
|
||||||
|
// default policy: don't take ownership:
|
||||||
|
m.def("custom_caster_no_destroy", []() { static auto *dt = new DestructionTester(); return dt; });
|
||||||
|
|
||||||
|
m.def("custom_caster_destroy", []() { return new DestructionTester(); },
|
||||||
|
py::return_value_policy::take_ownership); // Takes ownership: destroy when finished
|
||||||
|
m.def("custom_caster_destroy_const", []() -> const DestructionTester * { return new DestructionTester(); },
|
||||||
|
py::return_value_policy::take_ownership); // Likewise (const doesn't inhibit destruction)
|
||||||
|
m.def("destruction_tester_cstats", &ConstructorStats::get<DestructionTester>, py::return_value_policy::reference);
|
||||||
|
}
|
89
tests/test_custom_type_casters.py
Normal file
89
tests/test_custom_type_casters.py
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
import pytest
|
||||||
|
from pybind11_tests import custom_type_casters as m
|
||||||
|
|
||||||
|
|
||||||
|
def test_noconvert_args(msg):
|
||||||
|
a = m.ArgInspector()
|
||||||
|
assert msg(a.f("hi")) == """
|
||||||
|
loading ArgInspector1 argument WITH conversion allowed. Argument value = hi
|
||||||
|
"""
|
||||||
|
assert msg(a.g("this is a", "this is b")) == """
|
||||||
|
loading ArgInspector1 argument WITHOUT conversion allowed. Argument value = this is a
|
||||||
|
loading ArgInspector1 argument WITH conversion allowed. Argument value = this is b
|
||||||
|
13
|
||||||
|
loading ArgInspector2 argument WITH conversion allowed. Argument value = (default arg inspector 2)
|
||||||
|
""" # noqa: E501 line too long
|
||||||
|
assert msg(a.g("this is a", "this is b", 42)) == """
|
||||||
|
loading ArgInspector1 argument WITHOUT conversion allowed. Argument value = this is a
|
||||||
|
loading ArgInspector1 argument WITH conversion allowed. Argument value = this is b
|
||||||
|
42
|
||||||
|
loading ArgInspector2 argument WITH conversion allowed. Argument value = (default arg inspector 2)
|
||||||
|
""" # noqa: E501 line too long
|
||||||
|
assert msg(a.g("this is a", "this is b", 42, "this is d")) == """
|
||||||
|
loading ArgInspector1 argument WITHOUT conversion allowed. Argument value = this is a
|
||||||
|
loading ArgInspector1 argument WITH conversion allowed. Argument value = this is b
|
||||||
|
42
|
||||||
|
loading ArgInspector2 argument WITH conversion allowed. Argument value = this is d
|
||||||
|
"""
|
||||||
|
assert (a.h("arg 1") ==
|
||||||
|
"loading ArgInspector2 argument WITHOUT conversion allowed. Argument value = arg 1")
|
||||||
|
assert msg(m.arg_inspect_func("A1", "A2")) == """
|
||||||
|
loading ArgInspector2 argument WITH conversion allowed. Argument value = A1
|
||||||
|
loading ArgInspector1 argument WITHOUT conversion allowed. Argument value = A2
|
||||||
|
"""
|
||||||
|
|
||||||
|
assert m.floats_preferred(4) == 2.0
|
||||||
|
assert m.floats_only(4.0) == 2.0
|
||||||
|
with pytest.raises(TypeError) as excinfo:
|
||||||
|
m.floats_only(4)
|
||||||
|
assert msg(excinfo.value) == """
|
||||||
|
floats_only(): incompatible function arguments. The following argument types are supported:
|
||||||
|
1. (f: float) -> float
|
||||||
|
|
||||||
|
Invoked with: 4
|
||||||
|
"""
|
||||||
|
|
||||||
|
assert m.ints_preferred(4) == 2
|
||||||
|
assert m.ints_preferred(True) == 0
|
||||||
|
with pytest.raises(TypeError) as excinfo:
|
||||||
|
m.ints_preferred(4.0)
|
||||||
|
assert msg(excinfo.value) == """
|
||||||
|
ints_preferred(): incompatible function arguments. The following argument types are supported:
|
||||||
|
1. (i: int) -> int
|
||||||
|
|
||||||
|
Invoked with: 4.0
|
||||||
|
""" # noqa: E501 line too long
|
||||||
|
|
||||||
|
assert m.ints_only(4) == 2
|
||||||
|
with pytest.raises(TypeError) as excinfo:
|
||||||
|
m.ints_only(4.0)
|
||||||
|
assert msg(excinfo.value) == """
|
||||||
|
ints_only(): incompatible function arguments. The following argument types are supported:
|
||||||
|
1. (i: int) -> int
|
||||||
|
|
||||||
|
Invoked with: 4.0
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def test_custom_caster_destruction():
|
||||||
|
"""Tests that returning a pointer to a type that gets converted with a custom type caster gets
|
||||||
|
destroyed when the function has py::return_value_policy::take_ownership policy applied."""
|
||||||
|
|
||||||
|
cstats = m.destruction_tester_cstats()
|
||||||
|
# This one *doesn't* have take_ownership: the pointer should be used but not destroyed:
|
||||||
|
z = m.custom_caster_no_destroy()
|
||||||
|
assert cstats.alive() == 1 and cstats.default_constructions == 1
|
||||||
|
assert z
|
||||||
|
|
||||||
|
# take_ownership applied: this constructs a new object, casts it, then destroys it:
|
||||||
|
z = m.custom_caster_destroy()
|
||||||
|
assert z
|
||||||
|
assert cstats.default_constructions == 2
|
||||||
|
|
||||||
|
# Same, but with a const pointer return (which should *not* inhibit destruction):
|
||||||
|
z = m.custom_caster_destroy_const()
|
||||||
|
assert z
|
||||||
|
assert cstats.default_constructions == 3
|
||||||
|
|
||||||
|
# Make sure we still only have the original object (from ..._no_destroy()) alive:
|
||||||
|
assert cstats.alive() == 1
|
@ -105,76 +105,6 @@ struct TestPropRVP {
|
|||||||
UserType TestPropRVP::sv1(1);
|
UserType TestPropRVP::sv1(1);
|
||||||
UserType TestPropRVP::sv2(1);
|
UserType TestPropRVP::sv2(1);
|
||||||
|
|
||||||
// py::arg/py::arg_v testing: these arguments just record their argument when invoked
|
|
||||||
class ArgInspector1 { public: std::string arg = "(default arg inspector 1)"; };
|
|
||||||
class ArgInspector2 { public: std::string arg = "(default arg inspector 2)"; };
|
|
||||||
class ArgAlwaysConverts { };
|
|
||||||
namespace pybind11 { namespace detail {
|
|
||||||
template <> struct type_caster<ArgInspector1> {
|
|
||||||
public:
|
|
||||||
PYBIND11_TYPE_CASTER(ArgInspector1, _("ArgInspector1"));
|
|
||||||
|
|
||||||
bool load(handle src, bool convert) {
|
|
||||||
value.arg = "loading ArgInspector1 argument " +
|
|
||||||
std::string(convert ? "WITH" : "WITHOUT") + " conversion allowed. "
|
|
||||||
"Argument value = " + (std::string) str(src);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static handle cast(const ArgInspector1 &src, return_value_policy, handle) {
|
|
||||||
return str(src.arg).release();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
template <> struct type_caster<ArgInspector2> {
|
|
||||||
public:
|
|
||||||
PYBIND11_TYPE_CASTER(ArgInspector2, _("ArgInspector2"));
|
|
||||||
|
|
||||||
bool load(handle src, bool convert) {
|
|
||||||
value.arg = "loading ArgInspector2 argument " +
|
|
||||||
std::string(convert ? "WITH" : "WITHOUT") + " conversion allowed. "
|
|
||||||
"Argument value = " + (std::string) str(src);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static handle cast(const ArgInspector2 &src, return_value_policy, handle) {
|
|
||||||
return str(src.arg).release();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
template <> struct type_caster<ArgAlwaysConverts> {
|
|
||||||
public:
|
|
||||||
PYBIND11_TYPE_CASTER(ArgAlwaysConverts, _("ArgAlwaysConverts"));
|
|
||||||
|
|
||||||
bool load(handle, bool convert) {
|
|
||||||
return convert;
|
|
||||||
}
|
|
||||||
|
|
||||||
static handle cast(const ArgAlwaysConverts &, return_value_policy, handle) {
|
|
||||||
return py::none().release();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}}
|
|
||||||
|
|
||||||
// test_custom_caster_destruction
|
|
||||||
class DestructionTester {
|
|
||||||
public:
|
|
||||||
DestructionTester() { print_default_created(this); }
|
|
||||||
~DestructionTester() { print_destroyed(this); }
|
|
||||||
DestructionTester(const DestructionTester &) { print_copy_created(this); }
|
|
||||||
DestructionTester(DestructionTester &&) { print_move_created(this); }
|
|
||||||
DestructionTester &operator=(const DestructionTester &) { print_copy_assigned(this); return *this; }
|
|
||||||
DestructionTester &operator=(DestructionTester &&) { print_move_assigned(this); return *this; }
|
|
||||||
};
|
|
||||||
namespace pybind11 { namespace detail {
|
|
||||||
template <> struct type_caster<DestructionTester> {
|
|
||||||
PYBIND11_TYPE_CASTER(DestructionTester, _("DestructionTester"));
|
|
||||||
bool load(handle, bool) { return true; }
|
|
||||||
|
|
||||||
static handle cast(const DestructionTester &, return_value_policy, handle) {
|
|
||||||
return py::bool_(true).release();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}}
|
|
||||||
|
|
||||||
// Test None-allowed py::arg argument policy
|
// Test None-allowed py::arg argument policy
|
||||||
class NoneTester { public: int answer = 42; };
|
class NoneTester { public: int answer = 42; };
|
||||||
int none1(const NoneTester &obj) { return obj.answer; }
|
int none1(const NoneTester &obj) { return obj.answer; }
|
||||||
@ -364,33 +294,6 @@ TEST_SUBMODULE(methods_and_attributes, m) {
|
|||||||
.def(py::init());
|
.def(py::init());
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// test_noconvert_args
|
|
||||||
//
|
|
||||||
// Test converting. The ArgAlwaysConverts is just there to make the first no-conversion pass
|
|
||||||
// fail so that our call always ends up happening via the second dispatch (the one that allows
|
|
||||||
// some conversion).
|
|
||||||
class ArgInspector {
|
|
||||||
public:
|
|
||||||
ArgInspector1 f(ArgInspector1 a, ArgAlwaysConverts) { return a; }
|
|
||||||
std::string g(ArgInspector1 a, const ArgInspector1 &b, int c, ArgInspector2 *d, ArgAlwaysConverts) {
|
|
||||||
return a.arg + "\n" + b.arg + "\n" + std::to_string(c) + "\n" + d->arg;
|
|
||||||
}
|
|
||||||
static ArgInspector2 h(ArgInspector2 a, ArgAlwaysConverts) { return a; }
|
|
||||||
};
|
|
||||||
py::class_<ArgInspector>(m, "ArgInspector")
|
|
||||||
.def(py::init<>())
|
|
||||||
.def("f", &ArgInspector::f, py::arg(), py::arg() = ArgAlwaysConverts())
|
|
||||||
.def("g", &ArgInspector::g, "a"_a.noconvert(), "b"_a, "c"_a.noconvert()=13, "d"_a=ArgInspector2(), py::arg() = ArgAlwaysConverts())
|
|
||||||
.def_static("h", &ArgInspector::h, py::arg().noconvert(), py::arg() = ArgAlwaysConverts())
|
|
||||||
;
|
|
||||||
m.def("arg_inspect_func", [](ArgInspector2 a, ArgInspector1 b, ArgAlwaysConverts) { return a.arg + "\n" + b.arg; },
|
|
||||||
py::arg().noconvert(false), py::arg_v(nullptr, ArgInspector1()).noconvert(true), py::arg() = ArgAlwaysConverts());
|
|
||||||
|
|
||||||
m.def("floats_preferred", [](double f) { return 0.5 * f; }, py::arg("f"));
|
|
||||||
m.def("floats_only", [](double f) { return 0.5 * f; }, py::arg("f").noconvert());
|
|
||||||
m.def("ints_preferred", [](int i) { return i / 2; }, py::arg("i"));
|
|
||||||
m.def("ints_only", [](int i) { return i / 2; }, py::arg("i").noconvert());
|
|
||||||
|
|
||||||
// test_bad_arg_default
|
// test_bad_arg_default
|
||||||
// Issue/PR #648: bad arg default debugging output
|
// Issue/PR #648: bad arg default debugging output
|
||||||
#if !defined(NDEBUG)
|
#if !defined(NDEBUG)
|
||||||
@ -454,18 +357,6 @@ TEST_SUBMODULE(methods_and_attributes, m) {
|
|||||||
using Adapted = decltype(py::method_adaptor<RegisteredDerived>(&RegisteredDerived::do_nothing));
|
using Adapted = decltype(py::method_adaptor<RegisteredDerived>(&RegisteredDerived::do_nothing));
|
||||||
static_assert(std::is_same<Adapted, void (RegisteredDerived::*)() const>::value, "");
|
static_assert(std::is_same<Adapted, void (RegisteredDerived::*)() const>::value, "");
|
||||||
|
|
||||||
// test_custom_caster_destruction
|
|
||||||
// Test that `take_ownership` works on types with a custom type caster when given a pointer
|
|
||||||
|
|
||||||
// default policy: don't take ownership:
|
|
||||||
m.def("custom_caster_no_destroy", []() { static auto *dt = new DestructionTester(); return dt; });
|
|
||||||
|
|
||||||
m.def("custom_caster_destroy", []() { return new DestructionTester(); },
|
|
||||||
py::return_value_policy::take_ownership); // Takes ownership: destroy when finished
|
|
||||||
m.def("custom_caster_destroy_const", []() -> const DestructionTester * { return new DestructionTester(); },
|
|
||||||
py::return_value_policy::take_ownership); // Likewise (const doesn't inhibit destruction)
|
|
||||||
m.def("destruction_tester_cstats", &ConstructorStats::get<DestructionTester>, py::return_value_policy::reference);
|
|
||||||
|
|
||||||
// test_methods_and_attributes
|
// test_methods_and_attributes
|
||||||
py::class_<RefQualified>(m, "RefQualified")
|
py::class_<RefQualified>(m, "RefQualified")
|
||||||
.def(py::init<>())
|
.def(py::init<>())
|
||||||
|
@ -321,69 +321,6 @@ def test_cyclic_gc():
|
|||||||
assert cstats.alive() == 0
|
assert cstats.alive() == 0
|
||||||
|
|
||||||
|
|
||||||
def test_noconvert_args(msg):
|
|
||||||
a = m.ArgInspector()
|
|
||||||
assert msg(a.f("hi")) == """
|
|
||||||
loading ArgInspector1 argument WITH conversion allowed. Argument value = hi
|
|
||||||
"""
|
|
||||||
assert msg(a.g("this is a", "this is b")) == """
|
|
||||||
loading ArgInspector1 argument WITHOUT conversion allowed. Argument value = this is a
|
|
||||||
loading ArgInspector1 argument WITH conversion allowed. Argument value = this is b
|
|
||||||
13
|
|
||||||
loading ArgInspector2 argument WITH conversion allowed. Argument value = (default arg inspector 2)
|
|
||||||
""" # noqa: E501 line too long
|
|
||||||
assert msg(a.g("this is a", "this is b", 42)) == """
|
|
||||||
loading ArgInspector1 argument WITHOUT conversion allowed. Argument value = this is a
|
|
||||||
loading ArgInspector1 argument WITH conversion allowed. Argument value = this is b
|
|
||||||
42
|
|
||||||
loading ArgInspector2 argument WITH conversion allowed. Argument value = (default arg inspector 2)
|
|
||||||
""" # noqa: E501 line too long
|
|
||||||
assert msg(a.g("this is a", "this is b", 42, "this is d")) == """
|
|
||||||
loading ArgInspector1 argument WITHOUT conversion allowed. Argument value = this is a
|
|
||||||
loading ArgInspector1 argument WITH conversion allowed. Argument value = this is b
|
|
||||||
42
|
|
||||||
loading ArgInspector2 argument WITH conversion allowed. Argument value = this is d
|
|
||||||
"""
|
|
||||||
assert (a.h("arg 1") ==
|
|
||||||
"loading ArgInspector2 argument WITHOUT conversion allowed. Argument value = arg 1")
|
|
||||||
assert msg(m.arg_inspect_func("A1", "A2")) == """
|
|
||||||
loading ArgInspector2 argument WITH conversion allowed. Argument value = A1
|
|
||||||
loading ArgInspector1 argument WITHOUT conversion allowed. Argument value = A2
|
|
||||||
"""
|
|
||||||
|
|
||||||
assert m.floats_preferred(4) == 2.0
|
|
||||||
assert m.floats_only(4.0) == 2.0
|
|
||||||
with pytest.raises(TypeError) as excinfo:
|
|
||||||
m.floats_only(4)
|
|
||||||
assert msg(excinfo.value) == """
|
|
||||||
floats_only(): incompatible function arguments. The following argument types are supported:
|
|
||||||
1. (f: float) -> float
|
|
||||||
|
|
||||||
Invoked with: 4
|
|
||||||
"""
|
|
||||||
|
|
||||||
assert m.ints_preferred(4) == 2
|
|
||||||
assert m.ints_preferred(True) == 0
|
|
||||||
with pytest.raises(TypeError) as excinfo:
|
|
||||||
m.ints_preferred(4.0)
|
|
||||||
assert msg(excinfo.value) == """
|
|
||||||
ints_preferred(): incompatible function arguments. The following argument types are supported:
|
|
||||||
1. (i: int) -> int
|
|
||||||
|
|
||||||
Invoked with: 4.0
|
|
||||||
""" # noqa: E501 line too long
|
|
||||||
|
|
||||||
assert m.ints_only(4) == 2
|
|
||||||
with pytest.raises(TypeError) as excinfo:
|
|
||||||
m.ints_only(4.0)
|
|
||||||
assert msg(excinfo.value) == """
|
|
||||||
ints_only(): incompatible function arguments. The following argument types are supported:
|
|
||||||
1. (i: int) -> int
|
|
||||||
|
|
||||||
Invoked with: 4.0
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
def test_bad_arg_default(msg):
|
def test_bad_arg_default(msg):
|
||||||
from pybind11_tests import debug_enabled
|
from pybind11_tests import debug_enabled
|
||||||
|
|
||||||
@ -488,30 +425,6 @@ def test_unregistered_base_implementations():
|
|||||||
assert a.ro_value_prop == 1.75
|
assert a.ro_value_prop == 1.75
|
||||||
|
|
||||||
|
|
||||||
def test_custom_caster_destruction():
|
|
||||||
"""Tests that returning a pointer to a type that gets converted with a custom type caster gets
|
|
||||||
destroyed when the function has py::return_value_policy::take_ownership policy applied."""
|
|
||||||
|
|
||||||
cstats = m.destruction_tester_cstats()
|
|
||||||
# This one *doesn't* have take_ownership: the pointer should be used but not destroyed:
|
|
||||||
z = m.custom_caster_no_destroy()
|
|
||||||
assert cstats.alive() == 1 and cstats.default_constructions == 1
|
|
||||||
assert z
|
|
||||||
|
|
||||||
# take_ownership applied: this constructs a new object, casts it, then destroys it:
|
|
||||||
z = m.custom_caster_destroy()
|
|
||||||
assert z
|
|
||||||
assert cstats.default_constructions == 2
|
|
||||||
|
|
||||||
# Same, but with a const pointer return (which should *not* inhibit destruction):
|
|
||||||
z = m.custom_caster_destroy_const()
|
|
||||||
assert z
|
|
||||||
assert cstats.default_constructions == 3
|
|
||||||
|
|
||||||
# Make sure we still only have the original object (from ..._no_destroy()) alive:
|
|
||||||
assert cstats.alive() == 1
|
|
||||||
|
|
||||||
|
|
||||||
def test_ref_qualified():
|
def test_ref_qualified():
|
||||||
"""Tests that explicit lvalue ref-qualified methods can be called just like their
|
"""Tests that explicit lvalue ref-qualified methods can be called just like their
|
||||||
non ref-qualified counterparts."""
|
non ref-qualified counterparts."""
|
||||||
|
Loading…
Reference in New Issue
Block a user