Skip to content

Commit fe9ee86

Browse files
Add check if str(handle) correctly converted the object, and throw py::error_already_set if not (bis) (#2477)
* Add check if `str(handle)` correctly converted the object, and throw py::error_already_set if not * Fix tests on Python 3 * Apply @rwgk's fixes to cherry-picked commits from #2392
1 parent b47efd3 commit fe9ee86

File tree

3 files changed

+15
-2
lines changed

3 files changed

+15
-2
lines changed

include/pybind11/pytypes.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -920,7 +920,7 @@ class str : public object {
920920
Return a string representation of the object. This is analogous to
921921
the ``str()`` function in Python.
922922
\endrst */
923-
explicit str(handle h) : object(raw_str(h.ptr()), stolen_t{}) { }
923+
explicit str(handle h) : object(raw_str(h.ptr()), stolen_t{}) { if (!m_ptr) throw error_already_set(); }
924924

925925
operator std::string() const {
926926
object temp = *this;
@@ -945,8 +945,8 @@ class str : public object {
945945
/// Return string representation -- always returns a new reference, even if already a str
946946
static PyObject *raw_str(PyObject *op) {
947947
PyObject *str_value = PyObject_Str(op);
948-
if (!str_value) throw error_already_set();
949948
#if PY_MAJOR_VERSION < 3
949+
if (!str_value) throw error_already_set();
950950
PyObject *unicode = PyUnicode_FromEncodedObject(str_value, "utf-8", nullptr);
951951
Py_XDECREF(str_value); str_value = unicode;
952952
#endif

tests/test_pytypes.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ TEST_SUBMODULE(pytypes, m) {
8080
m.def("str_from_bytes", []() { return py::str(py::bytes("boo", 3)); });
8181
m.def("str_from_object", [](const py::object& obj) { return py::str(obj); });
8282
m.def("repr_from_object", [](const py::object& obj) { return py::repr(obj); });
83+
m.def("str_from_handle", [](py::handle h) { return py::str(h); });
8384

8485
m.def("str_format", []() {
8586
auto s1 = "{} + {} = {}"_s.format(1, 2, 3);

tests/test_pytypes.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,11 +104,23 @@ def __repr__(self):
104104

105105
assert m.str_from_object(A()) == "this is a str"
106106
assert m.repr_from_object(A()) == "this is a repr"
107+
assert m.str_from_handle(A()) == "this is a str"
107108

108109
s1, s2 = m.str_format()
109110
assert s1 == "1 + 2 = 3"
110111
assert s1 == s2
111112

113+
malformed_utf8 = b"\x80"
114+
assert m.str_from_object(malformed_utf8) is malformed_utf8 # To be fixed; see #2380
115+
if env.PY2:
116+
# with pytest.raises(UnicodeDecodeError):
117+
# m.str_from_object(malformed_utf8)
118+
with pytest.raises(UnicodeDecodeError):
119+
m.str_from_handle(malformed_utf8)
120+
else:
121+
# assert m.str_from_object(malformed_utf8) == "b'\\x80'"
122+
assert m.str_from_handle(malformed_utf8) == "b'\\x80'"
123+
112124

113125
def test_bytes(doc):
114126
assert m.bytes_from_string().decode() == "foo"

0 commit comments

Comments
 (0)