Skip to content

Commit e29f7a9

Browse files
committed
Merge branch 'master' into skylion007/311-testing
2 parents 056965c + a8b3ff3 commit e29f7a9

13 files changed

+130
-42
lines changed

.github/workflows/ci.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -315,8 +315,8 @@ jobs:
315315
# Testing NVCC; forces sources to behave like .cu files
316316
cuda:
317317
runs-on: ubuntu-latest
318-
name: "🐍 3.8 • CUDA 11 • Ubuntu 20.04"
319-
container: nvidia/cuda:11.0-devel-ubuntu20.04
318+
name: "🐍 3.8 • CUDA 11.2 • Ubuntu 20.04"
319+
container: nvidia/cuda:11.2.2-devel-ubuntu20.04
320320

321321
steps:
322322
- uses: actions/checkout@v3

.pre-commit-config.yaml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ repos:
3232

3333
# Upgrade old Python syntax
3434
- repo: https://github.com/asottile/pyupgrade
35-
rev: "v2.32.0"
35+
rev: "v2.32.1"
3636
hooks:
3737
- id: pyupgrade
3838
args: [--py36-plus]
@@ -71,7 +71,7 @@ repos:
7171

7272
# Autoremoves unused imports
7373
- repo: https://github.com/hadialqattan/pycln
74-
rev: "v1.3.1"
74+
rev: "v1.3.2"
7575
hooks:
7676
- id: pycln
7777
stages: [manual]
@@ -107,7 +107,7 @@ repos:
107107

108108
# PyLint has native support - not always usable, but works for us
109109
- repo: https://github.com/PyCQA/pylint
110-
rev: "v2.13.5"
110+
rev: "v2.13.8"
111111
hooks:
112112
- id: pylint
113113
files: ^pybind11
@@ -123,7 +123,7 @@ repos:
123123

124124
# Check static types with mypy
125125
- repo: https://github.com/pre-commit/mirrors-mypy
126-
rev: "v0.942"
126+
rev: "v0.950"
127127
hooks:
128128
- id: mypy
129129
args: []
@@ -164,7 +164,7 @@ repos:
164164

165165
# Clang format the codebase automatically
166166
- repo: https://github.com/pre-commit/mirrors-clang-format
167-
rev: "v14.0.1"
167+
rev: "v14.0.3"
168168
hooks:
169169
- id: clang-format
170170
types_or: [c++, c, cuda]

include/pybind11/cast.h

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -908,6 +908,14 @@ struct handle_type_name<kwargs> {
908908

909909
template <typename type>
910910
struct pyobject_caster {
911+
template <typename T = type, enable_if_t<std::is_same<T, handle>::value, int> = 0>
912+
pyobject_caster() : value() {}
913+
914+
// `type` may not be default constructible (e.g. frozenset, anyset). Initializing `value`
915+
// to a nil handle is safe since it will only be accessed if `load` succeeds.
916+
template <typename T = type, enable_if_t<std::is_base_of<object, T>::value, int> = 0>
917+
pyobject_caster() : value(reinterpret_steal<type>(handle())) {}
918+
911919
template <typename T = type, enable_if_t<std::is_same<T, handle>::value, int> = 0>
912920
bool load(handle src, bool /* convert */) {
913921
value = src;
@@ -1001,6 +1009,8 @@ struct return_value_policy_override<
10011009
// Basic python -> C++ casting; throws if casting fails
10021010
template <typename T, typename SFINAE>
10031011
type_caster<T, SFINAE> &load_type(type_caster<T, SFINAE> &conv, const handle &handle) {
1012+
static_assert(!detail::is_pyobject<T>::value,
1013+
"Internal error: type_caster should only be used for C++ types");
10041014
if (!conv.load(handle, true)) {
10051015
#if !defined(PYBIND11_DETAILED_ERROR_MESSAGES)
10061016
throw cast_error("Unable to cast Python instance to C++ type (#define "
@@ -1091,21 +1101,30 @@ detail::enable_if_t<!detail::move_never<T>::value, T> move(object &&obj) {
10911101
// - If both movable and copyable, check ref count: if 1, move; otherwise copy
10921102
// - Otherwise (not movable), copy.
10931103
template <typename T>
1094-
detail::enable_if_t<detail::move_always<T>::value, T> cast(object &&object) {
1104+
detail::enable_if_t<!detail::is_pyobject<T>::value && detail::move_always<T>::value, T>
1105+
cast(object &&object) {
10951106
return move<T>(std::move(object));
10961107
}
10971108
template <typename T>
1098-
detail::enable_if_t<detail::move_if_unreferenced<T>::value, T> cast(object &&object) {
1109+
detail::enable_if_t<!detail::is_pyobject<T>::value && detail::move_if_unreferenced<T>::value, T>
1110+
cast(object &&object) {
10991111
if (object.ref_count() > 1) {
11001112
return cast<T>(object);
11011113
}
11021114
return move<T>(std::move(object));
11031115
}
11041116
template <typename T>
1105-
detail::enable_if_t<detail::move_never<T>::value, T> cast(object &&object) {
1117+
detail::enable_if_t<!detail::is_pyobject<T>::value && detail::move_never<T>::value, T>
1118+
cast(object &&object) {
11061119
return cast<T>(object);
11071120
}
11081121

1122+
// pytype rvalue -> pytype (calls converting constructor)
1123+
template <typename T>
1124+
detail::enable_if_t<detail::is_pyobject<T>::value, T> cast(object &&object) {
1125+
return T(std::move(object));
1126+
}
1127+
11091128
template <typename T>
11101129
T object::cast() const & {
11111130
return pybind11::cast<T>(*this);

include/pybind11/pybind11.h

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1926,7 +1926,8 @@ struct enum_base {
19261926
[](const object &arg) -> str {
19271927
handle type = type::handle_of(arg);
19281928
object type_name = type.attr("__name__");
1929-
return pybind11::str("<{}.{}: {}>").format(type_name, enum_name(arg), int_(arg));
1929+
return pybind11::str("<{}.{}: {}>")
1930+
.format(std::move(type_name), enum_name(arg), int_(arg));
19301931
},
19311932
name("__repr__"),
19321933
is_method(m_base));
@@ -1936,7 +1937,7 @@ struct enum_base {
19361937
m_base.attr("__str__") = cpp_function(
19371938
[](handle arg) -> str {
19381939
object type_name = type::handle_of(arg).attr("__name__");
1939-
return pybind11::str("{}.{}").format(type_name, enum_name(arg));
1940+
return pybind11::str("{}.{}").format(std::move(type_name), enum_name(arg));
19401941
},
19411942
name("name"),
19421943
is_method(m_base));
@@ -2576,8 +2577,8 @@ PYBIND11_NOINLINE void print(const tuple &args, const dict &kwargs) {
25762577
for (size_t i = 0; i < args.size(); ++i) {
25772578
strings[i] = str(args[i]);
25782579
}
2579-
auto sep = kwargs.contains("sep") ? kwargs["sep"] : cast(" ");
2580-
auto line = sep.attr("join")(strings);
2580+
auto sep = kwargs.contains("sep") ? kwargs["sep"] : str(" ");
2581+
auto line = sep.attr("join")(std::move(strings));
25812582

25822583
object file;
25832584
if (kwargs.contains("file")) {
@@ -2596,7 +2597,7 @@ PYBIND11_NOINLINE void print(const tuple &args, const dict &kwargs) {
25962597

25972598
auto write = file.attr("write");
25982599
write(line);
2599-
write(kwargs.contains("end") ? kwargs["end"] : cast("\n"));
2600+
write(kwargs.contains("end") ? kwargs["end"] : str("\n"));
26002601

26012602
if (kwargs.contains("flush") && kwargs["flush"].cast<bool>()) {
26022603
file.attr("flush")();
@@ -2639,7 +2640,7 @@ get_type_override(const void *this_ptr, const type_info *this_type, const char *
26392640

26402641
function override = getattr(self, name, function());
26412642
if (override.is_cpp_function()) {
2642-
cache.insert(key);
2643+
cache.insert(std::move(key));
26432644
return function();
26442645
}
26452646

include/pybind11/pytypes.h

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1784,25 +1784,35 @@ class kwargs : public dict {
17841784
PYBIND11_OBJECT_DEFAULT(kwargs, dict, PyDict_Check)
17851785
};
17861786

1787-
class set : public object {
1787+
class anyset : public object {
17881788
public:
1789-
PYBIND11_OBJECT_CVT(set, object, PySet_Check, PySet_New)
1790-
set() : object(PySet_New(nullptr), stolen_t{}) {
1789+
PYBIND11_OBJECT(anyset, object, PyAnySet_Check)
1790+
size_t size() const { return static_cast<size_t>(PySet_Size(m_ptr)); }
1791+
bool empty() const { return size() == 0; }
1792+
template <typename T>
1793+
bool contains(T &&val) const {
1794+
return PySet_Contains(m_ptr, detail::object_or_cast(std::forward<T>(val)).ptr()) == 1;
1795+
}
1796+
};
1797+
1798+
class set : public anyset {
1799+
public:
1800+
PYBIND11_OBJECT_CVT(set, anyset, PySet_Check, PySet_New)
1801+
set() : anyset(PySet_New(nullptr), stolen_t{}) {
17911802
if (!m_ptr) {
17921803
pybind11_fail("Could not allocate set object!");
17931804
}
17941805
}
1795-
size_t size() const { return (size_t) PySet_Size(m_ptr); }
1796-
bool empty() const { return size() == 0; }
17971806
template <typename T>
17981807
bool add(T &&val) /* py-non-const */ {
17991808
return PySet_Add(m_ptr, detail::object_or_cast(std::forward<T>(val)).ptr()) == 0;
18001809
}
18011810
void clear() /* py-non-const */ { PySet_Clear(m_ptr); }
1802-
template <typename T>
1803-
bool contains(T &&val) const {
1804-
return PySet_Contains(m_ptr, detail::object_or_cast(std::forward<T>(val)).ptr()) == 1;
1805-
}
1811+
};
1812+
1813+
class frozenset : public anyset {
1814+
public:
1815+
PYBIND11_OBJECT_CVT(frozenset, anyset, PyFrozenSet_Check, PyFrozenSet_New)
18061816
};
18071817

18081818
class function : public object {

include/pybind11/stl.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,9 @@
1313
#include "detail/common.h"
1414

1515
#include <deque>
16-
#include <iostream>
1716
#include <list>
1817
#include <map>
18+
#include <ostream>
1919
#include <set>
2020
#include <unordered_map>
2121
#include <unordered_set>
@@ -55,10 +55,10 @@ struct set_caster {
5555
using key_conv = make_caster<Key>;
5656

5757
bool load(handle src, bool convert) {
58-
if (!isinstance<pybind11::set>(src)) {
58+
if (!isinstance<anyset>(src)) {
5959
return false;
6060
}
61-
auto s = reinterpret_borrow<pybind11::set>(src);
61+
auto s = reinterpret_borrow<anyset>(src);
6262
value.clear();
6363
for (auto entry : s) {
6464
key_conv conv;

tests/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -351,7 +351,7 @@ endif()
351351
# Compile with compiler warnings turned on
352352
function(pybind11_enable_warnings target_name)
353353
if(MSVC)
354-
target_compile_options(${target_name} PRIVATE /W4)
354+
target_compile_options(${target_name} PRIVATE /W4 /wd4189)
355355
elseif(CMAKE_CXX_COMPILER_ID MATCHES "(GNU|Intel|Clang)" AND NOT PYBIND11_CUDA_TESTS)
356356
target_compile_options(
357357
${target_name}

tests/test_copy_move.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -289,4 +289,7 @@ TEST_SUBMODULE(copy_move_policies, m) {
289289
py::return_value_policy::move);
290290
m.def(
291291
"get_moveissue2", [](int i) { return MoveIssue2(i); }, py::return_value_policy::move);
292+
293+
// Make sure that cast from pytype rvalue to other pytype works
294+
m.def("get_pytype_rvalue_castissue", [](double i) { return py::float_(i).cast<py::int_>(); });
292295
}

tests/test_copy_move.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,3 +123,10 @@ def test_move_fallback():
123123
assert m1.value == 1
124124
m2 = m.get_moveissue2(2)
125125
assert m2.value == 2
126+
127+
128+
def test_pytype_rvalue_cast():
129+
"""Make sure that cast from pytype rvalue to other pytype works"""
130+
131+
value = m.get_pytype_rvalue_castissue(1.0)
132+
assert value == 1

tests/test_pytypes.cpp

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -75,22 +75,34 @@ TEST_SUBMODULE(pytypes, m) {
7575
m.def("get_none", [] { return py::none(); });
7676
m.def("print_none", [](const py::none &none) { py::print("none: {}"_s.format(none)); });
7777

78-
// test_set
78+
// test_set, test_frozenset
7979
m.def("get_set", []() {
8080
py::set set;
8181
set.add(py::str("key1"));
8282
set.add("key2");
8383
set.add(std::string("key3"));
8484
return set;
8585
});
86-
m.def("print_set", [](const py::set &set) {
86+
m.def("get_frozenset", []() {
87+
py::set set;
88+
set.add(py::str("key1"));
89+
set.add("key2");
90+
set.add(std::string("key3"));
91+
return py::frozenset(set);
92+
});
93+
m.def("print_anyset", [](const py::anyset &set) {
8794
for (auto item : set) {
8895
py::print("key:", item);
8996
}
9097
});
91-
m.def("set_contains",
92-
[](const py::set &set, const py::object &key) { return set.contains(key); });
93-
m.def("set_contains", [](const py::set &set, const char *key) { return set.contains(key); });
98+
m.def("anyset_size", [](const py::anyset &set) { return set.size(); });
99+
m.def("anyset_empty", [](const py::anyset &set) { return set.empty(); });
100+
m.def("anyset_contains",
101+
[](const py::anyset &set, const py::object &key) { return set.contains(key); });
102+
m.def("anyset_contains",
103+
[](const py::anyset &set, const char *key) { return set.contains(key); });
104+
m.def("set_add", [](py::set &set, const py::object &key) { set.add(key); });
105+
m.def("set_clear", [](py::set &set) { set.clear(); });
94106

95107
// test_dict
96108
m.def("get_dict", []() { return py::dict("key"_a = "value"); });
@@ -310,6 +322,7 @@ TEST_SUBMODULE(pytypes, m) {
310322
"list"_a = py::list(d["list"]),
311323
"dict"_a = py::dict(d["dict"]),
312324
"set"_a = py::set(d["set"]),
325+
"frozenset"_a = py::frozenset(d["frozenset"]),
313326
"memoryview"_a = py::memoryview(d["memoryview"]));
314327
});
315328

@@ -325,6 +338,7 @@ TEST_SUBMODULE(pytypes, m) {
325338
"list"_a = d["list"].cast<py::list>(),
326339
"dict"_a = d["dict"].cast<py::dict>(),
327340
"set"_a = d["set"].cast<py::set>(),
341+
"frozenset"_a = d["frozenset"].cast<py::frozenset>(),
328342
"memoryview"_a = d["memoryview"].cast<py::memoryview>());
329343
});
330344

tests/test_pytypes.py

Lines changed: 40 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -66,11 +66,12 @@ def test_none(capture, doc):
6666

6767
def test_set(capture, doc):
6868
s = m.get_set()
69+
assert isinstance(s, set)
6970
assert s == {"key1", "key2", "key3"}
7071

72+
s.add("key4")
7173
with capture:
72-
s.add("key4")
73-
m.print_set(s)
74+
m.print_anyset(s)
7475
assert (
7576
capture.unordered
7677
== """
@@ -81,12 +82,43 @@ def test_set(capture, doc):
8182
"""
8283
)
8384

84-
assert not m.set_contains(set(), 42)
85-
assert m.set_contains({42}, 42)
86-
assert m.set_contains({"foo"}, "foo")
85+
m.set_add(s, "key5")
86+
assert m.anyset_size(s) == 5
8787

88-
assert doc(m.get_list) == "get_list() -> list"
89-
assert doc(m.print_list) == "print_list(arg0: list) -> None"
88+
m.set_clear(s)
89+
assert m.anyset_empty(s)
90+
91+
assert not m.anyset_contains(set(), 42)
92+
assert m.anyset_contains({42}, 42)
93+
assert m.anyset_contains({"foo"}, "foo")
94+
95+
assert doc(m.get_set) == "get_set() -> set"
96+
assert doc(m.print_anyset) == "print_anyset(arg0: anyset) -> None"
97+
98+
99+
def test_frozenset(capture, doc):
100+
s = m.get_frozenset()
101+
assert isinstance(s, frozenset)
102+
assert s == frozenset({"key1", "key2", "key3"})
103+
104+
with capture:
105+
m.print_anyset(s)
106+
assert (
107+
capture.unordered
108+
== """
109+
key: key1
110+
key: key2
111+
key: key3
112+
"""
113+
)
114+
assert m.anyset_size(s) == 3
115+
assert not m.anyset_empty(s)
116+
117+
assert not m.anyset_contains(frozenset(), 42)
118+
assert m.anyset_contains(frozenset({42}), 42)
119+
assert m.anyset_contains(frozenset({"foo"}), "foo")
120+
121+
assert doc(m.get_frozenset) == "get_frozenset() -> frozenset"
90122

91123

92124
def test_dict(capture, doc):
@@ -302,6 +334,7 @@ def test_constructors():
302334
list: range(3),
303335
dict: [("two", 2), ("one", 1), ("three", 3)],
304336
set: [4, 4, 5, 6, 6, 6],
337+
frozenset: [4, 4, 5, 6, 6, 6],
305338
memoryview: b"abc",
306339
}
307340
inputs = {k.__name__: v for k, v in data.items()}

tests/test_stl.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ def test_set(doc):
7373
assert s == {"key1", "key2"}
7474
s.add("key3")
7575
assert m.load_set(s)
76+
assert m.load_set(frozenset(s))
7677

7778
assert doc(m.cast_set) == "cast_set() -> Set[str]"
7879
assert doc(m.load_set) == "load_set(arg0: Set[str]) -> bool"

0 commit comments

Comments
 (0)