diff --git a/include/pybind11/cast.h b/include/pybind11/cast.h index 6079283ffe..64683f8e4b 100644 --- a/include/pybind11/cast.h +++ b/include/pybind11/cast.h @@ -1040,26 +1040,30 @@ struct type_caster::value && !is_std_char_t return false; } else if (PyFloat_Check(src.ptr())) { return false; - } else if (!convert && !index_check(src.ptr()) && !PYBIND11_LONG_CHECK(src.ptr())) { + } else if (!convert && !PYBIND11_LONG_CHECK(src.ptr()) && !index_check(src.ptr())) { return false; } else { handle src_or_index = src; - py_value = (py_type) -1; #if PY_VERSION_HEX < 0x03080000 object index; - if (!PYBIND11_LONG_CHECK(src.ptr()) && index_check(src.ptr())) { + if (!PYBIND11_LONG_CHECK(src.ptr())) { // So: index_check(src.ptr()) index = reinterpret_steal(PyNumber_Index(src.ptr())); - src_or_index = index ? index : handle(); + if (!index) { + PyErr_Clear(); + if (!convert) + return false; + } + else { + src_or_index = index; + } } #endif - if (src_or_index) { - if (std::is_unsigned::value) { - py_value = as_unsigned(src_or_index.ptr()); - } else { // signed integer: - py_value = sizeof(T) <= sizeof(long) - ? (py_type) PyLong_AsLong(src_or_index.ptr()) - : (py_type) PYBIND11_LONG_AS_LONGLONG(src_or_index.ptr()); - } + if (std::is_unsigned::value) { + py_value = as_unsigned(src_or_index.ptr()); + } else { // signed integer: + py_value = sizeof(T) <= sizeof(long) + ? (py_type) PyLong_AsLong(src_or_index.ptr()) + : (py_type) PYBIND11_LONG_AS_LONGLONG(src_or_index.ptr()); } } @@ -1069,15 +1073,8 @@ struct type_caster::value && !is_std_char_t // Check to see if the conversion is valid (integers should match exactly) // Signed/unsigned checks happen elsewhere if (py_err || (std::is_integral::value && sizeof(py_type) != sizeof(T) && py_value != (py_type) (T) py_value)) { - bool type_error = py_err && PyErr_ExceptionMatches( -#if PY_VERSION_HEX < 0x03000000 && !defined(PYPY_VERSION) - PyExc_SystemError -#else - PyExc_TypeError -#endif - ); PyErr_Clear(); - if (type_error && convert && PyNumber_Check(src.ptr())) { + if (py_err && convert && PyNumber_Check(src.ptr())) { auto tmp = reinterpret_steal(std::is_floating_point::value ? PyNumber_Float(src.ptr()) : PyNumber_Long(src.ptr())); diff --git a/tests/test_builtin_casters.py b/tests/test_builtin_casters.py index cfa07f04b5..6385346014 100644 --- a/tests/test_builtin_casters.py +++ b/tests/test_builtin_casters.py @@ -251,12 +251,18 @@ def test_integer_casting(): assert "incompatible function arguments" in str(excinfo.value) -@pytest.mark.filterwarnings("ignore:an integer is required:DeprecationWarning") def test_int_convert(): class DeepThought(object): def __int__(self): return 42 + class DoubleThought(object): + def __int__(self): + return 42 + + def __index__(self): + return 0 + class ShallowThought(object): pass @@ -284,7 +290,7 @@ def __int__(self): convert, noconvert = m.int_passthrough, m.int_passthrough_noconvert - def require_implicit(v): + def requires_conversion(v): pytest.raises(TypeError, noconvert, v) def cant_convert(v): @@ -294,20 +300,22 @@ def cant_convert(v): assert noconvert(7) == 7 cant_convert(3.14159) assert convert(DeepThought()) == 42 - require_implicit(DeepThought()) + requires_conversion(DeepThought()) + assert convert(DoubleThought()) == 0 # Fishy; `int(DoubleThought)` == 42 + assert noconvert(DoubleThought()) == 0 cant_convert(ShallowThought()) cant_convert(FuzzyThought()) - # Before Python 3.8, `int(obj)` does not pick up on `obj.__index__`, but pybind11 - # "backports" this behavior. + # Before Python 3.8, `PyLong_AsLong` does not pick up on `obj.__index__`, + # but pybind11 "backports" this behavior. assert convert(IndexedThought()) == 42 assert noconvert(IndexedThought()) == 42 assert convert(TypeErrorThought()) == 42 - require_implicit(TypeErrorThought()) - cant_convert(RaisingThought()) # no fall-back to `__int__`if `__index__` raises + requires_conversion(TypeErrorThought()) + assert convert(RaisingThought()) == 42 + requires_conversion(RaisingThought()) -@pytest.mark.filterwarnings("ignore:an integer is required:DeprecationWarning") def test_numpy_int_convert(): np = pytest.importorskip("numpy")