Skip to content

Commit 2092295

Browse files
committed
Fix use of __index__ on Python 2
1 parent 8356073 commit 2092295

File tree

2 files changed

+33
-28
lines changed

2 files changed

+33
-28
lines changed

include/pybind11/cast.h

Lines changed: 17 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1040,26 +1040,30 @@ struct type_caster<T, enable_if_t<std::is_arithmetic<T>::value && !is_std_char_t
10401040
return false;
10411041
} else if (PyFloat_Check(src.ptr())) {
10421042
return false;
1043-
} else if (!convert && !index_check(src.ptr()) && !PYBIND11_LONG_CHECK(src.ptr())) {
1043+
} else if (!convert && !PYBIND11_LONG_CHECK(src.ptr()) && !index_check(src.ptr())) {
10441044
return false;
10451045
} else {
10461046
handle src_or_index = src;
1047-
py_value = (py_type) -1;
10481047
#if PY_VERSION_HEX < 0x03080000
10491048
object index;
1050-
if (!PYBIND11_LONG_CHECK(src.ptr()) && index_check(src.ptr())) {
1049+
if (!PYBIND11_LONG_CHECK(src.ptr())) { // So: index_check(src.ptr())
10511050
index = reinterpret_steal<object>(PyNumber_Index(src.ptr()));
1052-
src_or_index = index ? index : handle();
1051+
if (!index) {
1052+
PyErr_Clear();
1053+
if (!convert)
1054+
return false;
1055+
}
1056+
else {
1057+
src_or_index = index;
1058+
}
10531059
}
10541060
#endif
1055-
if (src_or_index) {
1056-
if (std::is_unsigned<py_type>::value) {
1057-
py_value = as_unsigned<py_type>(src_or_index.ptr());
1058-
} else { // signed integer:
1059-
py_value = sizeof(T) <= sizeof(long)
1060-
? (py_type) PyLong_AsLong(src_or_index.ptr())
1061-
: (py_type) PYBIND11_LONG_AS_LONGLONG(src_or_index.ptr());
1062-
}
1061+
if (std::is_unsigned<py_type>::value) {
1062+
py_value = as_unsigned<py_type>(src_or_index.ptr());
1063+
} else { // signed integer:
1064+
py_value = sizeof(T) <= sizeof(long)
1065+
? (py_type) PyLong_AsLong(src_or_index.ptr())
1066+
: (py_type) PYBIND11_LONG_AS_LONGLONG(src_or_index.ptr());
10631067
}
10641068
}
10651069

@@ -1069,15 +1073,8 @@ struct type_caster<T, enable_if_t<std::is_arithmetic<T>::value && !is_std_char_t
10691073
// Check to see if the conversion is valid (integers should match exactly)
10701074
// Signed/unsigned checks happen elsewhere
10711075
if (py_err || (std::is_integral<T>::value && sizeof(py_type) != sizeof(T) && py_value != (py_type) (T) py_value)) {
1072-
bool type_error = py_err && PyErr_ExceptionMatches(
1073-
#if PY_VERSION_HEX < 0x03000000 && !defined(PYPY_VERSION)
1074-
PyExc_SystemError
1075-
#else
1076-
PyExc_TypeError
1077-
#endif
1078-
);
10791076
PyErr_Clear();
1080-
if (type_error && convert && PyNumber_Check(src.ptr())) {
1077+
if (py_err && convert && PyNumber_Check(src.ptr())) {
10811078
auto tmp = reinterpret_steal<object>(std::is_floating_point<T>::value
10821079
? PyNumber_Float(src.ptr())
10831080
: PyNumber_Long(src.ptr()));

tests/test_builtin_casters.py

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -251,12 +251,18 @@ def test_integer_casting():
251251
assert "incompatible function arguments" in str(excinfo.value)
252252

253253

254-
@pytest.mark.filterwarnings("ignore:an integer is required:DeprecationWarning")
255254
def test_int_convert():
256255
class DeepThought(object):
257256
def __int__(self):
258257
return 42
259258

259+
class DoubleThought(object):
260+
def __int__(self):
261+
return 42
262+
263+
def __index__(self):
264+
return 0
265+
260266
class ShallowThought(object):
261267
pass
262268

@@ -284,7 +290,7 @@ def __int__(self):
284290

285291
convert, noconvert = m.int_passthrough, m.int_passthrough_noconvert
286292

287-
def require_implicit(v):
293+
def requires_conversion(v):
288294
pytest.raises(TypeError, noconvert, v)
289295

290296
def cant_convert(v):
@@ -294,20 +300,22 @@ def cant_convert(v):
294300
assert noconvert(7) == 7
295301
cant_convert(3.14159)
296302
assert convert(DeepThought()) == 42
297-
require_implicit(DeepThought())
303+
requires_conversion(DeepThought())
304+
assert convert(DoubleThought()) == 0 # Fishy; `int(DoubleThought)` == 42
305+
assert noconvert(DoubleThought()) == 0
298306
cant_convert(ShallowThought())
299307
cant_convert(FuzzyThought())
300308

301-
# Before Python 3.8, `int(obj)` does not pick up on `obj.__index__`, but pybind11
302-
# "backports" this behavior.
309+
# Before Python 3.8, `PyLong_AsLong` does not pick up on `obj.__index__`,
310+
# but pybind11 "backports" this behavior.
303311
assert convert(IndexedThought()) == 42
304312
assert noconvert(IndexedThought()) == 42
305313
assert convert(TypeErrorThought()) == 42
306-
require_implicit(TypeErrorThought())
307-
cant_convert(RaisingThought()) # no fall-back to `__int__`if `__index__` raises
314+
requires_conversion(TypeErrorThought())
315+
assert convert(RaisingThought()) == 42
316+
requires_conversion(RaisingThought())
308317

309318

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

0 commit comments

Comments
 (0)