Skip to content

Commit f4ed7b7

Browse files
committed
fix pypy __index__ problems
1 parent c13f21e commit f4ed7b7

File tree

4 files changed

+28
-11
lines changed

4 files changed

+28
-11
lines changed

include/pybind11/cast.h

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -244,26 +244,18 @@ struct type_caster<T, enable_if_t<std::is_arithmetic<T>::value && !is_std_char_t
244244
return false;
245245
}
246246

247-
#if !defined(PYPY_VERSION)
248-
auto index_check = [](PyObject *o) { return PyIndex_Check(o); };
249-
#else
250-
// In PyPy 7.3.3, `PyIndex_Check` is implemented by calling `__index__`,
251-
// while CPython only considers the existence of `nb_index`/`__index__`.
252-
auto index_check = [](PyObject *o) { return hasattr(o, "__index__"); };
253-
#endif
254-
255247
if (std::is_floating_point<T>::value) {
256248
if (convert || PyFloat_Check(src.ptr()) || PYBIND11_LONG_CHECK(src.ptr())) {
257249
py_value = (py_type) PyFloat_AsDouble(src.ptr());
258250
} else {
259251
return false;
260252
}
261-
} else if (convert || PYBIND11_LONG_CHECK(src.ptr()) || index_check(src.ptr())) {
253+
} else if (convert || PYBIND11_LONG_CHECK(src.ptr()) || PYBIND11_INDEX_CHECK(src.ptr())) {
262254
handle src_or_index = src;
263255
// PyPy: 7.3.7's 3.8 does not implement PyLong_*'s __index__ calls.
264256
#if defined(PYPY_VERSION)
265257
object index;
266-
if (!PYBIND11_LONG_CHECK(src.ptr())) { // So: index_check(src.ptr())
258+
if (!PYBIND11_LONG_CHECK(src.ptr())) { // So: PYBIND11_INDEX_CHECK(src.ptr())
267259
index = reinterpret_steal<object>(PyNumber_Index(src.ptr()));
268260
if (!index) {
269261
PyErr_Clear();

include/pybind11/complex.h

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,21 @@ class type_caster<std::complex<T>> {
5656
|| PYBIND11_LONG_CHECK(src.ptr()))) {
5757
return false;
5858
}
59-
Py_complex result = PyComplex_AsCComplex(src.ptr());
59+
handle src_or_index = src;
60+
#if defined(PYPY_VERSION)
61+
object index;
62+
if (PYBIND11_INDEX_CHECK(src.ptr())) {
63+
index = reinterpret_steal<object>(PyNumber_Index(src.ptr()));
64+
if (!index) {
65+
PyErr_Clear();
66+
if (!convert)
67+
return false;
68+
} else {
69+
src_or_index = index;
70+
}
71+
}
72+
#endif
73+
Py_complex result = PyComplex_AsCComplex(src_or_index.ptr());
6074
if (result.real == -1.0 && PyErr_Occurred()) {
6175
PyErr_Clear();
6276
return false;

include/pybind11/detail/common.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,13 @@
322322
#define PYBIND11_BYTES_AS_STRING PyBytes_AsString
323323
#define PYBIND11_BYTES_SIZE PyBytes_Size
324324
#define PYBIND11_LONG_CHECK(o) PyLong_Check(o)
325+
// In PyPy 7.3.3, `PyIndex_Check` is implemented by calling `__index__`,
326+
// while CPython only considers the existence of `nb_index`/`__index__`.
327+
#if !defined(PYPY_VERSION)
328+
# define PYBIND11_INDEX_CHECK(o) PyIndex_Check(o)
329+
#else
330+
# define PYBIND11_INDEX_CHECK(o) hasattr(o, "__index__")
331+
#endif
325332
#define PYBIND11_LONG_AS_LONGLONG(o) PyLong_AsLongLong(o)
326333
#define PYBIND11_LONG_FROM_SIGNED(o) PyLong_FromSsize_t((ssize_t) (o))
327334
#define PYBIND11_LONG_FROM_UNSIGNED(o) PyLong_FromSize_t((size_t) (o))

tests/test_builtin_casters.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -316,6 +316,7 @@ def cant_convert(v):
316316
# Before Python 3.8, `PyLong_AsLong` does not pick up on `obj.__index__`,
317317
# but pybind11 "backports" this behavior.
318318
assert convert(Index()) == 42
319+
assert isinstance(convert(Index()), int)
319320
assert noconvert(Index()) == 42
320321
assert convert(IntAndIndex()) == 0 # Fishy; `int(DoubleThought)` == 42
321322
assert noconvert(IntAndIndex()) == 0
@@ -355,6 +356,7 @@ def cant_convert(v):
355356
requires_conversion(Index())
356357
assert pytest.approx(convert(Float())) == 41.45
357358
assert pytest.approx(convert(Index())) == -7.0
359+
assert isinstance(convert(Float()), float)
358360
assert pytest.approx(convert(3)) == 3.0
359361
assert pytest.approx(noconvert(3)) == 3.0
360362
cant_convert(Int())
@@ -529,8 +531,10 @@ def cant_convert(v):
529531
assert convert(1 + 5j) == 1.0 + 5.0j
530532
assert convert(Complex()) == 5.0 + 4j
531533
assert convert(Float()) == 5.0
534+
assert isinstance(convert(Float()), complex)
532535
cant_convert(Int())
533536
assert convert(Index()) == 1
537+
assert isinstance(convert(Index()), complex)
534538

535539
assert noconvert(1) == 1.0
536540
assert noconvert(2.0) == 2.0

0 commit comments

Comments
 (0)