Skip to content

Commit 4d8024f

Browse files
committed
Always call PyNumber_Index when casting from Python to a C++ integral type, also pre-3.8
1 parent 3609fe5 commit 4d8024f

File tree

2 files changed

+30
-11
lines changed

2 files changed

+30
-11
lines changed

include/pybind11/cast.h

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1029,12 +1029,30 @@ struct type_caster<T, enable_if_t<std::is_arithmetic<T>::value && !is_std_char_t
10291029
return false;
10301030
} else if (!convert && !PyIndex_Check(src.ptr()) && !PYBIND11_LONG_CHECK(src.ptr())) {
10311031
return false;
1032-
} else if (std::is_unsigned<py_type>::value) {
1033-
py_value = as_unsigned<py_type>(src.ptr());
1034-
} else { // signed integer:
1035-
py_value = sizeof(T) <= sizeof(long)
1036-
? (py_type) PyLong_AsLong(src.ptr())
1037-
: (py_type) PYBIND11_LONG_AS_LONGLONG(src.ptr());
1032+
} else {
1033+
handle obj = src;
1034+
#if PY_VERSION_HEX < 0x03080000
1035+
bool do_decref = false;
1036+
if (PyIndex_Check(src.ptr())) {
1037+
PyObject *tmp = PyNumber_Index(src.ptr());
1038+
if (!tmp) {
1039+
py_value = (py_type) -1;
1040+
}
1041+
do_decref = true;
1042+
obj = tmp;
1043+
}
1044+
#endif
1045+
if (std::is_unsigned<py_type>::value) {
1046+
py_value = as_unsigned<py_type>(obj.ptr());
1047+
} else { // signed integer:
1048+
py_value = sizeof(T) <= sizeof(long)
1049+
? (py_type) PyLong_AsLong(obj.ptr())
1050+
: (py_type) PYBIND11_LONG_AS_LONGLONG(obj.ptr());
1051+
}
1052+
#if PY_VERSION_HEX < 0x03080000
1053+
if (do_decref)
1054+
obj.dec_ref();
1055+
#endif
10381056
}
10391057

10401058
// Python API reported an error

tests/test_builtin_casters.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -289,11 +289,12 @@ def cant_convert(v):
289289
require_implicit(DeepThought())
290290
cant_convert(ShallowThought())
291291
cant_convert(FuzzyThought())
292-
if env.PY >= (3, 8):
293-
# Before Python 3.8, `int(obj)` does not pick up on `obj.__index__`
294-
assert convert(IndexedThought()) == 42
295-
assert noconvert(IndexedThought()) == 42
296-
cant_convert(RaisingThought()) # no fall-back to `__int__`if `__index__` raises
292+
293+
# Before Python 3.8, `int(obj)` does not pick up on `obj.__index__`, but pybind11
294+
# "backports" this behavior.
295+
assert convert(IndexedThought()) == 42
296+
assert noconvert(IndexedThought()) == 42
297+
cant_convert(RaisingThought()) # no fall-back to `__int__`if `__index__` raises
297298

298299

299300
def test_numpy_int_convert():

0 commit comments

Comments
 (0)