Skip to content
Merged
40 changes: 38 additions & 2 deletions include/pybind11/stl_bind.h
Original file line number Diff line number Diff line change
Expand Up @@ -694,6 +694,40 @@ struct ItemsViewImpl : public detail::items_view {
Map ↦
};

inline str format_message_key_error_key_object(handle py_key) {
str message = "pybind11::bind_map key";
if (!py_key) {
return message;
}
try {
message = str(py_key);
} catch (const std::exception &) {
try {
message = repr(py_key);
} catch (const std::exception &) {
return message;
}
}
const ssize_t cut_length = 100;
if (len(message) > 2 * cut_length + 3) {
return str(message[slice(0, cut_length, 1)]) + str("✄✄✄")
+ str(message[slice(-cut_length, static_cast<ssize_t>(len(message)), 1)]);
}
return message;
}

template <typename KeyType>
str format_message_key_error(const KeyType &key) {
object py_key;
try {
py_key = cast(key);
} catch (const std::exception &) {
do { // Trick to avoid "empty catch" warning/error.
} while (false);
}
return format_message_key_error_key_object(py_key);
}

PYBIND11_NAMESPACE_END(detail)

template <typename Map, typename holder_type = std::unique_ptr<Map>, typename... Args>
Expand Down Expand Up @@ -785,7 +819,8 @@ class_<Map, holder_type> bind_map(handle scope, const std::string &name, Args &&
[](Map &m, const KeyType &k) -> MappedType & {
auto it = m.find(k);
if (it == m.end()) {
throw key_error();
set_error(PyExc_KeyError, detail::format_message_key_error(k));
throw error_already_set();
}
return it->second;
},
Expand All @@ -808,7 +843,8 @@ class_<Map, holder_type> bind_map(handle scope, const std::string &name, Args &&
cl.def("__delitem__", [](Map &m, const KeyType &k) {
auto it = m.find(k);
if (it == m.end()) {
throw key_error();
set_error(PyExc_KeyError, detail::format_message_key_error(k));
throw error_already_set();
}
m.erase(it);
});
Expand Down
19 changes: 19 additions & 0 deletions tests/test_stl_binders.py
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,25 @@ def test_map_delitem():
assert list(mm) == ["b"]
assert list(mm.items()) == [("b", 2.5)]

with pytest.raises(KeyError) as excinfo:
mm["a_long_key"]
assert "a_long_key" in str(excinfo.value)

with pytest.raises(KeyError) as excinfo:
del mm["a_long_key"]
assert "a_long_key" in str(excinfo.value)

cut_length = 100
k_very_long = "ab" * cut_length + "xyz"
with pytest.raises(KeyError) as excinfo:
mm[k_very_long]
assert k_very_long in str(excinfo.value)
k_very_long += "@"
with pytest.raises(KeyError) as excinfo:
mm[k_very_long]
k_repr = k_very_long[:cut_length] + "✄✄✄" + k_very_long[-cut_length:]
assert k_repr in str(excinfo.value)

um = m.UnorderedMapStringDouble()
um["ua"] = 1.1
um["ub"] = 2.6
Expand Down