Skip to content

Commit c7a0c88

Browse files
committed
Merge branch 'master' into sh_merge_master
2 parents 0aa8c94 + b926396 commit c7a0c88

File tree

6 files changed

+56
-20
lines changed

6 files changed

+56
-20
lines changed

.github/workflows/ci.yml

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ jobs:
8181
run: brew install boost
8282

8383
- name: Update CMake
84-
uses: jwlawson/actions-setup-cmake@v1.12
84+
uses: jwlawson/actions-setup-cmake@v1.13
8585

8686
- name: Cache wheels
8787
if: runner.os == 'macOS'
@@ -203,7 +203,7 @@ jobs:
203203
debug: ${{ matrix.python-debug }}
204204

205205
- name: Update CMake
206-
uses: jwlawson/actions-setup-cmake@v1.12
206+
uses: jwlawson/actions-setup-cmake@v1.13
207207

208208
- name: Valgrind cache
209209
if: matrix.valgrind
@@ -466,7 +466,7 @@ jobs:
466466
run: python3 -m pip install --upgrade pip
467467

468468
- name: Update CMake
469-
uses: jwlawson/actions-setup-cmake@v1.12
469+
uses: jwlawson/actions-setup-cmake@v1.13
470470

471471
- name: Configure
472472
shell: bash
@@ -755,10 +755,10 @@ jobs:
755755
architecture: x86
756756

757757
- name: Update CMake
758-
uses: jwlawson/actions-setup-cmake@v1.12
758+
uses: jwlawson/actions-setup-cmake@v1.13
759759

760760
- name: Prepare MSVC
761-
uses: ilammy/msvc-dev-cmd@v1.11.0
761+
uses: ilammy/msvc-dev-cmd@v1.12.0
762762
with:
763763
arch: x86
764764

@@ -808,10 +808,10 @@ jobs:
808808
architecture: x86
809809

810810
- name: Update CMake
811-
uses: jwlawson/actions-setup-cmake@v1.12
811+
uses: jwlawson/actions-setup-cmake@v1.13
812812

813813
- name: Prepare MSVC
814-
uses: ilammy/msvc-dev-cmd@v1.11.0
814+
uses: ilammy/msvc-dev-cmd@v1.12.0
815815
with:
816816
arch: x86
817817

@@ -859,7 +859,7 @@ jobs:
859859
python3 -m pip install -r tests/requirements.txt
860860
861861
- name: Update CMake
862-
uses: jwlawson/actions-setup-cmake@v1.12
862+
uses: jwlawson/actions-setup-cmake@v1.13
863863

864864
- name: Configure C++20
865865
run: >

.github/workflows/configure.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ jobs:
5252
# An action for adding a specific version of CMake:
5353
# https://github.com/jwlawson/actions-setup-cmake
5454
- name: Setup CMake ${{ matrix.cmake }}
55-
uses: jwlawson/actions-setup-cmake@v1.12
55+
uses: jwlawson/actions-setup-cmake@v1.13
5656
with:
5757
cmake-version: ${{ matrix.cmake }}
5858

.github/workflows/upstream.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ jobs:
3131
run: sudo apt-get install libboost-dev
3232

3333
- name: Update CMake
34-
uses: jwlawson/actions-setup-cmake@v1.12
34+
uses: jwlawson/actions-setup-cmake@v1.13
3535

3636
- name: Prepare env
3737
run: |

include/pybind11/pytypes.h

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1382,7 +1382,7 @@ class iterator : public object {
13821382
private:
13831383
void advance() {
13841384
value = reinterpret_steal<object>(PyIter_Next(m_ptr));
1385-
if (PyErr_Occurred()) {
1385+
if (value.ptr() == nullptr && PyErr_Occurred()) {
13861386
throw error_already_set();
13871387
}
13881388
}
@@ -1809,16 +1809,16 @@ class capsule : public object {
18091809

18101810
explicit capsule(const void *value,
18111811
const char *name = nullptr,
1812-
void (*destructor)(PyObject *) = nullptr)
1812+
PyCapsule_Destructor destructor = nullptr)
18131813
: object(PyCapsule_New(const_cast<void *>(value), name, destructor), stolen_t{}) {
18141814
if (!m_ptr) {
18151815
throw error_already_set();
18161816
}
18171817
}
18181818

1819-
PYBIND11_DEPRECATED("Please pass a destructor that takes a void pointer as input")
1820-
capsule(const void *value, void (*destruct)(PyObject *))
1821-
: object(PyCapsule_New(const_cast<void *>(value), nullptr, destruct), stolen_t{}) {
1819+
PYBIND11_DEPRECATED("Please use the ctor with value, name, destructor args")
1820+
capsule(const void *value, PyCapsule_Destructor destructor)
1821+
: object(PyCapsule_New(const_cast<void *>(value), nullptr, destructor), stolen_t{}) {
18221822
if (!m_ptr) {
18231823
throw error_already_set();
18241824
}
@@ -1829,7 +1829,7 @@ class capsule : public object {
18291829
// guard if destructor called while err indicator is set
18301830
error_scope error_guard;
18311831
auto destructor = reinterpret_cast<void (*)(void *)>(PyCapsule_GetContext(o));
1832-
if (PyErr_Occurred()) {
1832+
if (destructor == nullptr && PyErr_Occurred()) {
18331833
throw error_already_set();
18341834
}
18351835
const char *name = get_name_in_error_scope(o);
@@ -1843,7 +1843,7 @@ class capsule : public object {
18431843
}
18441844
});
18451845

1846-
if (!m_ptr || PyCapsule_SetContext(m_ptr, (void *) destructor) != 0) {
1846+
if (!m_ptr || PyCapsule_SetContext(m_ptr, reinterpret_cast<void *>(destructor)) != 0) {
18471847
throw error_already_set();
18481848
}
18491849
}
@@ -1967,7 +1967,11 @@ class dict : public object {
19671967
void clear() /* py-non-const */ { PyDict_Clear(ptr()); }
19681968
template <typename T>
19691969
bool contains(T &&key) const {
1970-
return PyDict_Contains(m_ptr, detail::object_or_cast(std::forward<T>(key)).ptr()) == 1;
1970+
auto result = PyDict_Contains(m_ptr, detail::object_or_cast(std::forward<T>(key)).ptr());
1971+
if (result == -1) {
1972+
throw error_already_set();
1973+
}
1974+
return result == 1;
19711975
}
19721976

19731977
private:
@@ -2053,7 +2057,11 @@ class anyset : public object {
20532057
bool empty() const { return size() == 0; }
20542058
template <typename T>
20552059
bool contains(T &&val) const {
2056-
return PySet_Contains(m_ptr, detail::object_or_cast(std::forward<T>(val)).ptr()) == 1;
2060+
auto result = PySet_Contains(m_ptr, detail::object_or_cast(std::forward<T>(val)).ptr());
2061+
if (result == -1) {
2062+
throw error_already_set();
2063+
}
2064+
return result == 1;
20572065
}
20582066
};
20592067

tests/test_pytypes.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,7 @@ TEST_SUBMODULE(pytypes, m) {
183183
return d2;
184184
});
185185
m.def("dict_contains",
186-
[](const py::dict &dict, py::object val) { return dict.contains(val); });
186+
[](const py::dict &dict, const py::object &val) { return dict.contains(val); });
187187
m.def("dict_contains",
188188
[](const py::dict &dict, const char *val) { return dict.contains(val); });
189189

@@ -538,6 +538,9 @@ TEST_SUBMODULE(pytypes, m) {
538538

539539
m.def("hash_function", [](py::object obj) { return py::hash(std::move(obj)); });
540540

541+
m.def("obj_contains",
542+
[](py::object &obj, const py::object &key) { return obj.contains(key); });
543+
541544
m.def("test_number_protocol", [](const py::object &a, const py::object &b) {
542545
py::list l;
543546
l.append(a.equal(b));

tests/test_pytypes.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,31 @@ def test_dict(capture, doc):
168168
assert m.dict_keyword_constructor() == {"x": 1, "y": 2, "z": 3}
169169

170170

171+
class CustomContains:
172+
d = {"key": None}
173+
174+
def __contains__(self, m):
175+
return m in self.d
176+
177+
178+
@pytest.mark.parametrize(
179+
"arg,func",
180+
[
181+
(set(), m.anyset_contains),
182+
(dict(), m.dict_contains),
183+
(CustomContains(), m.obj_contains),
184+
],
185+
)
186+
@pytest.mark.xfail("env.PYPY and sys.pypy_version_info < (7, 3, 10)", strict=False)
187+
def test_unhashable_exceptions(arg, func):
188+
class Unhashable:
189+
__hash__ = None
190+
191+
with pytest.raises(TypeError) as exc_info:
192+
func(arg, Unhashable())
193+
assert "unhashable type:" in str(exc_info.value)
194+
195+
171196
def test_tuple():
172197
assert m.tuple_no_args() == ()
173198
assert m.tuple_ssize_t() == ()

0 commit comments

Comments
 (0)