Skip to content

Commit d86e1ce

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

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
@@ -1037,12 +1037,30 @@ struct type_caster<T, enable_if_t<std::is_arithmetic<T>::value && !is_std_char_t
10371037
return false;
10381038
} else if (!convert && !index_check(src.ptr()) && !PYBIND11_LONG_CHECK(src.ptr())) {
10391039
return false;
1040-
} else if (std::is_unsigned<py_type>::value) {
1041-
py_value = as_unsigned<py_type>(src.ptr());
1042-
} else { // signed integer:
1043-
py_value = sizeof(T) <= sizeof(long)
1044-
? (py_type) PyLong_AsLong(src.ptr())
1045-
: (py_type) PYBIND11_LONG_AS_LONGLONG(src.ptr());
1040+
} else {
1041+
handle obj = src;
1042+
#if PY_VERSION_HEX < 0x03080000
1043+
bool do_decref = false;
1044+
if (PyIndex_Check(src.ptr())) {
1045+
PyObject *tmp = PyNumber_Index(src.ptr());
1046+
if (!tmp) {
1047+
py_value = (py_type) -1;
1048+
}
1049+
do_decref = true;
1050+
obj = tmp;
1051+
}
1052+
#endif
1053+
if (std::is_unsigned<py_type>::value) {
1054+
py_value = as_unsigned<py_type>(obj.ptr());
1055+
} else { // signed integer:
1056+
py_value = sizeof(T) <= sizeof(long)
1057+
? (py_type) PyLong_AsLong(obj.ptr())
1058+
: (py_type) PYBIND11_LONG_AS_LONGLONG(obj.ptr());
1059+
}
1060+
#if PY_VERSION_HEX < 0x03080000
1061+
if (do_decref)
1062+
obj.dec_ref();
1063+
#endif
10461064
}
10471065

10481066
// 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)