mirror of
https://github.com/pybind/pybind11.git
synced 2024-11-25 14:45:12 +00:00
Add additional info to TypeError when C++->Python casting fails (#3605)
* Add additional info to TypeInfo when C++->Python casting fails * Fix typo * Address reviewer comments
This commit is contained in:
parent
b66328b043
commit
ef070f7750
@ -996,6 +996,13 @@ protected:
|
|||||||
"Python type! The signature was\n\t";
|
"Python type! The signature was\n\t";
|
||||||
msg += it->signature;
|
msg += it->signature;
|
||||||
append_note_if_missing_header_is_suspected(msg);
|
append_note_if_missing_header_is_suspected(msg);
|
||||||
|
#if PY_VERSION_HEX >= 0x03030000
|
||||||
|
// Attach additional error info to the exception if supported
|
||||||
|
if (PyErr_Occurred()) {
|
||||||
|
raise_from(PyExc_TypeError, msg.c_str());
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
PyErr_SetString(PyExc_TypeError, msg.c_str());
|
PyErr_SetString(PyExc_TypeError, msg.c_str());
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
@ -7,10 +7,11 @@
|
|||||||
BSD-style license that can be found in the LICENSE file.
|
BSD-style license that can be found in the LICENSE file.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "pybind11_tests.h"
|
|
||||||
#include "constructor_stats.h"
|
#include "constructor_stats.h"
|
||||||
#include <pybind11/operators.h>
|
#include "pybind11_tests.h"
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
#include <pybind11/operators.h>
|
||||||
|
#include <pybind11/stl.h>
|
||||||
|
|
||||||
class Vector2 {
|
class Vector2 {
|
||||||
public:
|
public:
|
||||||
@ -71,6 +72,12 @@ int operator+(const C2 &, const C2 &) { return 22; }
|
|||||||
int operator+(const C2 &, const C1 &) { return 21; }
|
int operator+(const C2 &, const C1 &) { return 21; }
|
||||||
int operator+(const C1 &, const C2 &) { return 12; }
|
int operator+(const C1 &, const C2 &) { return 12; }
|
||||||
|
|
||||||
|
struct HashMe {
|
||||||
|
std::string member;
|
||||||
|
};
|
||||||
|
|
||||||
|
bool operator==(const HashMe &lhs, const HashMe &rhs) { return lhs.member == rhs.member; }
|
||||||
|
|
||||||
// Note: Specializing explicit within `namespace std { ... }` is done due to a
|
// Note: Specializing explicit within `namespace std { ... }` is done due to a
|
||||||
// bug in GCC<7. If you are supporting compilers later than this, consider
|
// bug in GCC<7. If you are supporting compilers later than this, consider
|
||||||
// specializing `using template<> struct std::hash<...>` in the global
|
// specializing `using template<> struct std::hash<...>` in the global
|
||||||
@ -82,6 +89,14 @@ namespace std {
|
|||||||
// Not a good hash function, but easy to test
|
// Not a good hash function, but easy to test
|
||||||
size_t operator()(const Vector2 &) { return 4; }
|
size_t operator()(const Vector2 &) { return 4; }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// HashMe has a hash function in C++ but no `__hash__` for Python.
|
||||||
|
template <>
|
||||||
|
struct hash<HashMe> {
|
||||||
|
std::size_t operator()(const HashMe &selector) const {
|
||||||
|
return std::hash<std::string>()(selector.member);
|
||||||
|
}
|
||||||
|
};
|
||||||
} // namespace std
|
} // namespace std
|
||||||
|
|
||||||
// Not a good abs function, but easy to test.
|
// Not a good abs function, but easy to test.
|
||||||
@ -228,8 +243,12 @@ TEST_SUBMODULE(operators, m) {
|
|||||||
.def("__hash__", &Hashable::hash)
|
.def("__hash__", &Hashable::hash)
|
||||||
.def(py::init<int>())
|
.def(py::init<int>())
|
||||||
.def(py::self == py::self);
|
.def(py::self == py::self);
|
||||||
}
|
|
||||||
|
|
||||||
|
// define __eq__ but not __hash__
|
||||||
|
py::class_<HashMe>(m, "HashMe").def(py::self == py::self);
|
||||||
|
|
||||||
|
m.def("get_unhashable_HashMe_set", []() { return std::unordered_set<HashMe>{{"one"}}; });
|
||||||
|
}
|
||||||
#if !defined(_MSC_VER) && !defined(__INTEL_COMPILER)
|
#if !defined(_MSC_VER) && !defined(__INTEL_COMPILER)
|
||||||
#pragma GCC diagnostic pop
|
#pragma GCC diagnostic pop
|
||||||
#endif
|
#endif
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
import env
|
||||||
from pybind11_tests import ConstructorStats
|
from pybind11_tests import ConstructorStats
|
||||||
from pybind11_tests import operators as m
|
from pybind11_tests import operators as m
|
||||||
|
|
||||||
@ -135,8 +136,9 @@ def test_overriding_eq_reset_hash():
|
|||||||
assert m.Comparable(15) is not m.Comparable(15)
|
assert m.Comparable(15) is not m.Comparable(15)
|
||||||
assert m.Comparable(15) == m.Comparable(15)
|
assert m.Comparable(15) == m.Comparable(15)
|
||||||
|
|
||||||
with pytest.raises(TypeError):
|
with pytest.raises(TypeError) as excinfo:
|
||||||
hash(m.Comparable(15)) # TypeError: unhashable type: 'm.Comparable'
|
hash(m.Comparable(15))
|
||||||
|
assert str(excinfo.value).startswith("unhashable type:")
|
||||||
|
|
||||||
for hashable in (m.Hashable, m.Hashable2):
|
for hashable in (m.Hashable, m.Hashable2):
|
||||||
assert hashable(15) is not hashable(15)
|
assert hashable(15) is not hashable(15)
|
||||||
@ -144,3 +146,10 @@ def test_overriding_eq_reset_hash():
|
|||||||
|
|
||||||
assert hash(hashable(15)) == 15
|
assert hash(hashable(15)) == 15
|
||||||
assert hash(hashable(15)) == hash(hashable(15))
|
assert hash(hashable(15)) == hash(hashable(15))
|
||||||
|
|
||||||
|
|
||||||
|
def test_return_set_of_unhashable():
|
||||||
|
with pytest.raises(TypeError) as excinfo:
|
||||||
|
m.get_unhashable_HashMe_set()
|
||||||
|
if not env.PY2:
|
||||||
|
assert str(excinfo.value.__cause__).startswith("unhashable type:")
|
||||||
|
Loading…
Reference in New Issue
Block a user