From 9e900fa2ff01e6d98adf935935c57c5e2362eee6 Mon Sep 17 00:00:00 2001 From: Yannick Jadoul Date: Fri, 14 Aug 2020 02:21:34 +0200 Subject: [PATCH 1/2] Add check if `str(handle)` correctly converted the object, and throw py::error_already_set if not --- include/pybind11/pytypes.h | 4 ++-- tests/test_pytypes.cpp | 1 + tests/test_pytypes.py | 9 +++++++++ 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/include/pybind11/pytypes.h b/include/pybind11/pytypes.h index 094b191a0c..64805a4769 100644 --- a/include/pybind11/pytypes.h +++ b/include/pybind11/pytypes.h @@ -918,7 +918,7 @@ class str : public object { Return a string representation of the object. This is analogous to the ``str()`` function in Python. \endrst */ - explicit str(handle h) : object(raw_str(h.ptr()), stolen_t{}) { } + explicit str(handle h) : object(raw_str(h.ptr()), stolen_t{}) { if (!m_ptr) throw error_already_set(); } operator std::string() const { object temp = *this; @@ -943,8 +943,8 @@ class str : public object { /// Return string representation -- always returns a new reference, even if already a str static PyObject *raw_str(PyObject *op) { PyObject *str_value = PyObject_Str(op); - if (!str_value) throw error_already_set(); #if PY_MAJOR_VERSION < 3 + if (!str_value) throw error_already_set(); PyObject *unicode = PyUnicode_FromEncodedObject(str_value, "utf-8", nullptr); Py_XDECREF(str_value); str_value = unicode; #endif diff --git a/tests/test_pytypes.cpp b/tests/test_pytypes.cpp index 008bccae4d..10e03b44d0 100644 --- a/tests/test_pytypes.cpp +++ b/tests/test_pytypes.cpp @@ -80,6 +80,7 @@ TEST_SUBMODULE(pytypes, m) { m.def("str_from_bytes", []() { return py::str(py::bytes("boo", 3)); }); m.def("str_from_object", [](const py::object& obj) { return py::str(obj); }); m.def("repr_from_object", [](const py::object& obj) { return py::repr(obj); }); + m.def("str_from_handle", [](py::handle h) { return py::str(h); }); m.def("str_format", []() { auto s1 = "{} + {} = {}"_s.format(1, 2, 3); diff --git a/tests/test_pytypes.py b/tests/test_pytypes.py index e50f8b1247..82cd5eda23 100644 --- a/tests/test_pytypes.py +++ b/tests/test_pytypes.py @@ -104,11 +104,20 @@ def __repr__(self): assert m.str_from_object(A()) == "this is a str" assert m.repr_from_object(A()) == "this is a repr" + assert m.str_from_handle(A()) == "this is a str" s1, s2 = m.str_format() assert s1 == "1 + 2 = 3" assert s1 == s2 + malformed_utf8 = b"\x80" + with pytest.raises(UnicodeDecodeError) as excinfo: + assert m.str_from_object(malformed_utf8) + assert 'invalid start byte' in str(excinfo.value) + with pytest.raises(UnicodeDecodeError) as excinfo: + assert m.str_from_handle(malformed_utf8) + assert 'invalid start byte' in str(excinfo.value) + def test_bytes(doc): assert m.bytes_from_string().decode() == "foo" From 3dbafa69178ff45eae148a09b06a70df57aa7f60 Mon Sep 17 00:00:00 2001 From: Yannick Jadoul Date: Fri, 14 Aug 2020 17:27:40 +0200 Subject: [PATCH 2/2] Fix tests on Python 3 --- tests/test_pytypes.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/tests/test_pytypes.py b/tests/test_pytypes.py index 82cd5eda23..f2c5ddeeaf 100644 --- a/tests/test_pytypes.py +++ b/tests/test_pytypes.py @@ -111,12 +111,14 @@ def __repr__(self): assert s1 == s2 malformed_utf8 = b"\x80" - with pytest.raises(UnicodeDecodeError) as excinfo: - assert m.str_from_object(malformed_utf8) - assert 'invalid start byte' in str(excinfo.value) - with pytest.raises(UnicodeDecodeError) as excinfo: - assert m.str_from_handle(malformed_utf8) - assert 'invalid start byte' in str(excinfo.value) + if env.PY2: + with pytest.raises(UnicodeDecodeError): + assert m.str_from_object(malformed_utf8) + with pytest.raises(UnicodeDecodeError): + assert m.str_from_handle(malformed_utf8) + else: + assert m.str_from_object(malformed_utf8) == "b'\\x80'" + assert m.str_from_handle(malformed_utf8) == "b'\\x80'" def test_bytes(doc):