Skip to content
26 changes: 20 additions & 6 deletions include/pybind11/cast.h
Original file line number Diff line number Diff line change
Expand Up @@ -1042,12 +1042,26 @@ struct type_caster<T, enable_if_t<std::is_arithmetic<T>::value && !is_std_char_t
return false;
} else if (!convert && !index_check(src.ptr()) && !PYBIND11_LONG_CHECK(src.ptr())) {
return false;
} else if (std::is_unsigned<py_type>::value) {
py_value = as_unsigned<py_type>(src.ptr());
} else { // signed integer:
py_value = sizeof(T) <= sizeof(long)
? (py_type) PyLong_AsLong(src.ptr())
: (py_type) PYBIND11_LONG_AS_LONGLONG(src.ptr());
} else {
handle src_or_index = src;
#if PY_VERSION_HEX < 0x03080000
object index;
if (index_check(src.ptr())) {
index = reinterpret_steal<object>(PyNumber_Index(src.ptr()));
if (!index) {
PyErr_Clear();
return false;
}
src_or_index = index;
}
#endif
if (std::is_unsigned<py_type>::value) {
py_value = as_unsigned<py_type>(src_or_index.ptr());
} else { // signed integer:
py_value = sizeof(T) <= sizeof(long)
? (py_type) PyLong_AsLong(src_or_index.ptr())
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, another note: this results in warnings, and I think that's correct. Because it's not just getting out the long from the PyLong object, but it's also trying to do the conversion.

Yes, the C API is quite messy here. And it's only made worse by the structure of this caster.

: (py_type) PYBIND11_LONG_AS_LONGLONG(src_or_index.ptr());
}
}

// Python API reported an error
Expand Down
13 changes: 8 additions & 5 deletions tests/test_builtin_casters.py
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,7 @@ 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):
Expand Down Expand Up @@ -289,13 +290,15 @@ def cant_convert(v):
require_implicit(DeepThought())
cant_convert(ShallowThought())
cant_convert(FuzzyThought())
if env.PY >= (3, 8):
# Before Python 3.8, `int(obj)` does not pick up on `obj.__index__`
assert convert(IndexedThought()) == 42
assert noconvert(IndexedThought()) == 42
cant_convert(RaisingThought()) # no fall-back to `__int__`if `__index__` raises

# Before Python 3.8, `int(obj)` does not pick up on `obj.__index__`, but pybind11
# "backports" this behavior.
assert convert(IndexedThought()) == 42
assert noconvert(IndexedThought()) == 42
cant_convert(RaisingThought()) # no fall-back to `__int__`if `__index__` raises


@pytest.mark.filterwarnings("ignore:an integer is required:DeprecationWarning")
def test_numpy_int_convert():
np = pytest.importorskip("numpy")

Expand Down