diff --git a/include/pybind11/options.h b/include/pybind11/options.h index 3105551ddd..2e2f1d8d78 100644 --- a/include/pybind11/options.h +++ b/include/pybind11/options.h @@ -38,12 +38,14 @@ class options { options& enable_function_signatures() & { global_state().show_function_signatures = true; return *this; } + // Getter methods (return the global state): static bool show_user_defined_docstrings() { return global_state().show_user_defined_docstrings; } static bool show_function_signatures() { return global_state().show_function_signatures; } + // This type is not meant to be allocated on the heap. void* operator new(size_t) = delete; @@ -52,6 +54,8 @@ class options { struct state { bool show_user_defined_docstrings = true; //< Include user-supplied texts in docstrings. bool show_function_signatures = true; //< Include auto-generated function signatures in docstrings. + bool type_error_print_repr = true; //< If true, if args mismatch __repr__ of argument is shown if true, + //< else __str__ is used }; static state &global_state() { diff --git a/include/pybind11/pybind11.h b/include/pybind11/pybind11.h index 2bfd81cf38..a3e2667cae 100644 --- a/include/pybind11/pybind11.h +++ b/include/pybind11/pybind11.h @@ -681,7 +681,12 @@ class cpp_function : public function { for (size_t ti = overloads->is_constructor ? 1 : 0; ti < args_.size(); ++ti) { if (!some_args) some_args = true; else msg += ", "; - msg += pybind11::repr(args_[ti]); + const std::string argRepr = pybind11::repr(args_[ti]); + if(argRepr.size() > 100){ + msg += pybind11::str("{:.100} ...[truncated by pybind11]").format(argRepr); + } + else + msg += argRepr; } if (kwargs_in) { auto kwargs = reinterpret_borrow(kwargs_in); @@ -692,7 +697,10 @@ class cpp_function : public function { for (auto kwarg : kwargs) { if (first) first = false; else msg += ", "; - msg += pybind11::str("{}={!r}").format(kwarg.first, kwarg.second); + const std::string argRepr = pybind11::repr( kwarg.second); + if(argRepr.size()>100){ + msg += pybind11::str("{}={!r:.100} ...[truncated by pybind11]").format(kwarg.first, argRepr); + } } } } diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index d40c25ac4c..21ea9358bd 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -56,6 +56,7 @@ set(PYBIND11_TEST_FILES test_smart_ptr.cpp test_stl_binders.cpp test_virtual_functions.cpp + test_type_error_truncation.cpp ) # Invoking cmake with something like: diff --git a/tests/test_type_error_truncation.cpp b/tests/test_type_error_truncation.cpp new file mode 100644 index 0000000000..0d23bbb1e1 --- /dev/null +++ b/tests/test_type_error_truncation.cpp @@ -0,0 +1,45 @@ +/* + tests/test_type_error_truncation.cpp -- exception translation + + Copyright (c) 2017 Thorsten Beier + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#include "pybind11_tests.h" +#include + + +// A type that should be raised as an exeption in Python +struct TypeWithLongRepr { + TypeWithLongRepr(const uint32_t reprSize = 100) + : reprSize_(reprSize){ + + } + std::string repr()const{ + std::string ret(reprSize_,'*'); + ret[0] = '<'; + ret[reprSize_-1] = '>'; + return ret; + } + void foo(const TypeWithLongRepr & , const std::string )const{ + + } + uint32_t reprSize_; +}; + + + +test_initializer type_error_truncation([](py::module &m) { + + py::class_(m, "TypeWithLongRepr") + .def(py::init()) + .def("__repr__", &TypeWithLongRepr::repr) + .def("foo",&TypeWithLongRepr::foo, + py::arg("arg1"), + py::arg("arg2") + ) + ; + +}); diff --git a/tests/test_type_error_truncation.py b/tests/test_type_error_truncation.py new file mode 100644 index 0000000000..b060bfc538 --- /dev/null +++ b/tests/test_type_error_truncation.py @@ -0,0 +1,67 @@ +import pytest +import pybind11_tests + + + + + + +def test_type_error_truncation(): + from pybind11_tests import TypeWithLongRepr + + size_of_repr = 200 + objA = TypeWithLongRepr(200) + objB = TypeWithLongRepr(50) + objC = TypeWithLongRepr(150) + reprStrA = repr(objA) + reprStrB = repr(objB) + reprStrC = repr(objC) + #print(reprStr) + assert len(reprStrA) == 200 + assert len(reprStrB) == 50 + assert len(reprStrC) == 150 + + + assert reprStrA.startswith('<') + assert reprStrA.endswith('>') + + + + with pytest.raises(TypeError) as excinfo: + objA.foo(objC, objC) + typeErrorMsg = str(excinfo.value) + print(typeErrorMsg) + assert len(typeErrorMsg) < 3 * 200 + assert typeErrorMsg.contains('...[truncated by pybind11]') + + + with pytest.raises(TypeError) as excinfo: + objB.foo(objB, objB) + typeErrorMsg = str(excinfo.value) + print(typeErrorMsg) + assert len(typeErrorMsg) < 3 * 200 + assert not typeErrorMsg.contains('...[truncated by pybind11]') + + + + + + with pytest.raises(TypeError) as excinfo: + objA.foo(objC, arg2=objC) + typeErrorMsg = str(excinfo.value) + print(typeErrorMsg) + assert len(typeErrorMsg) < 3 * 200 + assert typeErrorMsg.contains('...[truncated by pybind11]') + + + with pytest.raises(TypeError) as excinfo: + objB.foo(objB, objB) + typeErrorMsg = str(excinfo.value) + print(typeErrorMsg) + assert len(typeErrorMsg) < 3 * 200 + assert not typeErrorMsg.contains('...[truncated by pybind11]') + + + + +test_type_error_truncation() \ No newline at end of file