diff --git a/include/pybind11/cast.h b/include/pybind11/cast.h index 4708101d80..556bdb7e34 100644 --- a/include/pybind11/cast.h +++ b/include/pybind11/cast.h @@ -347,9 +347,12 @@ struct type_caster::value && !is_std_char_t return PyLong_FromUnsignedLongLong((unsigned long long) src); } - PYBIND11_TYPE_CASTER(T, - io_name::value>( - "typing.SupportsInt", "int", "typing.SupportsFloat", "float")); + PYBIND11_TYPE_CASTER( + T, + io_name::value>("typing.SupportsInt | typing.SupportsIndex", + "int", + "typing.SupportsFloat | typing.SupportsIndex", + "float")); }; template diff --git a/include/pybind11/complex.h b/include/pybind11/complex.h index 8a831c12ce..0b6f49365d 100644 --- a/include/pybind11/complex.h +++ b/include/pybind11/complex.h @@ -54,7 +54,23 @@ class type_caster> { if (!convert && !PyComplex_Check(src.ptr())) { return false; } - Py_complex result = PyComplex_AsCComplex(src.ptr()); + handle src_or_index = src; + // PyPy: 7.3.7's 3.8 does not implement PyLong_*'s __index__ calls. + // The same logic is used in numeric_caster for ints and floats +#if defined(PYPY_VERSION) + object index; + if (PYBIND11_INDEX_CHECK(src.ptr())) { + index = reinterpret_steal(PyNumber_Index(src.ptr())); + if (!index) { + PyErr_Clear(); + if (!convert) + return false; + } else { + src_or_index = index; + } + } +#endif + Py_complex result = PyComplex_AsCComplex(src_or_index.ptr()); if (result.real == -1.0 && PyErr_Occurred()) { PyErr_Clear(); return false; @@ -68,7 +84,10 @@ class type_caster> { return PyComplex_FromDoubles((double) src.real(), (double) src.imag()); } - PYBIND11_TYPE_CASTER(std::complex, const_name("complex")); + PYBIND11_TYPE_CASTER( + std::complex, + io_name("typing.SupportsComplex | typing.SupportsFloat | typing.SupportsIndex", + "complex")); }; PYBIND11_NAMESPACE_END(detail) PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/include/pybind11/detail/common.h b/include/pybind11/detail/common.h index 16952c5829..07c0943006 100644 --- a/include/pybind11/detail/common.h +++ b/include/pybind11/detail/common.h @@ -322,6 +322,13 @@ #define PYBIND11_BYTES_AS_STRING PyBytes_AsString #define PYBIND11_BYTES_SIZE PyBytes_Size #define PYBIND11_LONG_CHECK(o) PyLong_Check(o) +// In PyPy 7.3.3, `PyIndex_Check` is implemented by calling `__index__`, +// while CPython only considers the existence of `nb_index`/`__index__`. +#if !defined(PYPY_VERSION) +# define PYBIND11_INDEX_CHECK(o) PyIndex_Check(o) +#else +# define PYBIND11_INDEX_CHECK(o) hasattr(o, "__index__") +#endif #define PYBIND11_LONG_AS_LONGLONG(o) PyLong_AsLongLong(o) #define PYBIND11_LONG_FROM_SIGNED(o) PyLong_FromSsize_t((ssize_t) (o)) #define PYBIND11_LONG_FROM_UNSIGNED(o) PyLong_FromSize_t((size_t) (o)) diff --git a/tests/test_builtin_casters.cpp b/tests/test_builtin_casters.cpp index c516f8de7e..1aa9f89b42 100644 --- a/tests/test_builtin_casters.cpp +++ b/tests/test_builtin_casters.cpp @@ -363,6 +363,8 @@ TEST_SUBMODULE(builtin_casters, m) { m.def("complex_cast", [](float x) { return "{}"_s.format(x); }); m.def("complex_cast", [](std::complex x) { return "({}, {})"_s.format(x.real(), x.imag()); }); + m.def("complex_convert", [](std::complex x) { return x; }); + m.def("complex_noconvert", [](std::complex x) { return x; }, py::arg{}.noconvert()); // test int vs. long (Python 2) m.def("int_cast", []() { return (int) 42; }); diff --git a/tests/test_builtin_casters.py b/tests/test_builtin_casters.py index 7a2c6a4d8f..ae1b1bd179 100644 --- a/tests/test_builtin_casters.py +++ b/tests/test_builtin_casters.py @@ -286,7 +286,10 @@ def __int__(self): convert, noconvert = m.int_passthrough, m.int_passthrough_noconvert - assert doc(convert) == "int_passthrough(arg0: typing.SupportsInt) -> int" + assert ( + doc(convert) + == "int_passthrough(arg0: typing.SupportsInt | typing.SupportsIndex) -> int" + ) assert doc(noconvert) == "int_passthrough_noconvert(arg0: int) -> int" def requires_conversion(v): @@ -322,19 +325,39 @@ def cant_convert(v): def test_float_convert(doc): + class Int: + def __int__(self): + return -5 + + class Index: + def __index__(self) -> int: + return -7 + class Float: def __float__(self): return 41.45 convert, noconvert = m.float_passthrough, m.float_passthrough_noconvert - assert doc(convert) == "float_passthrough(arg0: typing.SupportsFloat) -> float" + assert ( + doc(convert) + == "float_passthrough(arg0: typing.SupportsFloat | typing.SupportsIndex) -> float" + ) assert doc(noconvert) == "float_passthrough_noconvert(arg0: float) -> float" def requires_conversion(v): pytest.raises(TypeError, noconvert, v) + def cant_convert(v): + pytest.raises(TypeError, convert, v) + requires_conversion(Float()) + requires_conversion(Index()) assert pytest.approx(convert(Float())) == 41.45 + assert pytest.approx(convert(Index())) == -7.0 + assert isinstance(convert(Float()), float) + assert pytest.approx(convert(3)) == 3.0 + requires_conversion(3) + cant_convert(Int()) def test_numpy_int_convert(): @@ -381,7 +404,7 @@ def test_tuple(doc): assert ( doc(m.tuple_passthrough) == """ - tuple_passthrough(arg0: tuple[bool, str, typing.SupportsInt]) -> tuple[int, str, bool] + tuple_passthrough(arg0: tuple[bool, str, typing.SupportsInt | typing.SupportsIndex]) -> tuple[int, str, bool] Return a triple in reversed order """ @@ -458,11 +481,61 @@ def test_reference_wrapper(): assert m.refwrap_call_iiw(IncType(10), m.refwrap_iiw) == [10, 10, 10, 10] -def test_complex_cast(): +def test_complex_cast(doc): """std::complex casts""" + + class Complex: + def __complex__(self) -> complex: + return complex(5, 4) + + class Float: + def __float__(self) -> float: + return 5.0 + + class Int: + def __int__(self) -> int: + return 3 + + class Index: + def __index__(self) -> int: + return 1 + assert m.complex_cast(1) == "1.0" + assert m.complex_cast(1.0) == "1.0" + assert m.complex_cast(Complex()) == "(5.0, 4.0)" assert m.complex_cast(2j) == "(0.0, 2.0)" + convert, noconvert = m.complex_convert, m.complex_noconvert + + def requires_conversion(v): + pytest.raises(TypeError, noconvert, v) + + def cant_convert(v): + pytest.raises(TypeError, convert, v) + + assert ( + doc(convert) + == "complex_convert(arg0: typing.SupportsComplex | typing.SupportsFloat | typing.SupportsIndex) -> complex" + ) + assert doc(noconvert) == "complex_noconvert(arg0: complex) -> complex" + + assert convert(1) == 1.0 + assert convert(2.0) == 2.0 + assert convert(1 + 5j) == 1.0 + 5.0j + assert convert(Complex()) == 5.0 + 4j + assert convert(Float()) == 5.0 + assert isinstance(convert(Float()), complex) + cant_convert(Int()) + assert convert(Index()) == 1 + assert isinstance(convert(Index()), complex) + + requires_conversion(1) + requires_conversion(2.0) + assert noconvert(1 + 5j) == 1.0 + 5.0j + requires_conversion(Complex()) + requires_conversion(Float()) + requires_conversion(Index()) + def test_bool_caster(): """Test bool caster implicit conversions.""" diff --git a/tests/test_callbacks.py b/tests/test_callbacks.py index 09dadd94c3..c0a57a7b86 100644 --- a/tests/test_callbacks.py +++ b/tests/test_callbacks.py @@ -140,11 +140,11 @@ def test_cpp_function_roundtrip(): def test_function_signatures(doc): assert ( doc(m.test_callback3) - == "test_callback3(arg0: collections.abc.Callable[[typing.SupportsInt], int]) -> str" + == "test_callback3(arg0: collections.abc.Callable[[typing.SupportsInt | typing.SupportsIndex], int]) -> str" ) assert ( doc(m.test_callback4) - == "test_callback4() -> collections.abc.Callable[[typing.SupportsInt], int]" + == "test_callback4() -> collections.abc.Callable[[typing.SupportsInt | typing.SupportsIndex], int]" ) diff --git a/tests/test_class.py b/tests/test_class.py index 1e82930361..fae6a31899 100644 --- a/tests/test_class.py +++ b/tests/test_class.py @@ -163,13 +163,13 @@ def test_qualname(doc): assert ( doc(m.NestBase.Nested.fn) == """ - fn(self: m.class_.NestBase.Nested, arg0: typing.SupportsInt, arg1: m.class_.NestBase, arg2: m.class_.NestBase.Nested) -> None + fn(self: m.class_.NestBase.Nested, arg0: typing.SupportsInt | typing.SupportsIndex, arg1: m.class_.NestBase, arg2: m.class_.NestBase.Nested) -> None """ ) assert ( doc(m.NestBase.Nested.fa) == """ - fa(self: m.class_.NestBase.Nested, a: typing.SupportsInt, b: m.class_.NestBase, c: m.class_.NestBase.Nested) -> None + fa(self: m.class_.NestBase.Nested, a: typing.SupportsInt | typing.SupportsIndex, b: m.class_.NestBase, c: m.class_.NestBase.Nested) -> None """ ) assert m.NestBase.__module__ == "pybind11_tests.class_" diff --git a/tests/test_custom_type_casters.py b/tests/test_custom_type_casters.py index bf31d3f374..6ed1c564f0 100644 --- a/tests/test_custom_type_casters.py +++ b/tests/test_custom_type_casters.py @@ -75,7 +75,7 @@ def test_noconvert_args(msg): msg(excinfo.value) == """ ints_preferred(): incompatible function arguments. The following argument types are supported: - 1. (i: typing.SupportsInt) -> int + 1. (i: typing.SupportsInt | typing.SupportsIndex) -> int Invoked with: 4.0 """ diff --git a/tests/test_docstring_options.py b/tests/test_docstring_options.py index f2a10480ca..802a1ec9e5 100644 --- a/tests/test_docstring_options.py +++ b/tests/test_docstring_options.py @@ -20,11 +20,11 @@ def test_docstring_options(): # options.enable_function_signatures() assert m.test_function3.__doc__.startswith( - "test_function3(a: typing.SupportsInt, b: typing.SupportsInt) -> None" + "test_function3(a: typing.SupportsInt | typing.SupportsIndex, b: typing.SupportsInt | typing.SupportsIndex) -> None" ) assert m.test_function4.__doc__.startswith( - "test_function4(a: typing.SupportsInt, b: typing.SupportsInt) -> None" + "test_function4(a: typing.SupportsInt | typing.SupportsIndex, b: typing.SupportsInt | typing.SupportsIndex) -> None" ) assert m.test_function4.__doc__.endswith("A custom docstring\n") @@ -37,7 +37,7 @@ def test_docstring_options(): # RAII destructor assert m.test_function7.__doc__.startswith( - "test_function7(a: typing.SupportsInt, b: typing.SupportsInt) -> None" + "test_function7(a: typing.SupportsInt | typing.SupportsIndex, b: typing.SupportsInt | typing.SupportsIndex) -> None" ) assert m.test_function7.__doc__.endswith("A custom docstring\n") diff --git a/tests/test_enum.py b/tests/test_enum.py index 99d4a88c8a..f295b01457 100644 --- a/tests/test_enum.py +++ b/tests/test_enum.py @@ -328,7 +328,7 @@ def test_generated_dunder_methods_pos_only(): ) assert ( re.match( - r"^__setstate__\(self: [\w\.]+, state: [\w\.]+, /\)", + r"^__setstate__\(self: [\w\.]+, state: [\w\. \|]+, /\)", enum_type.__setstate__.__doc__, ) is not None diff --git a/tests/test_factory_constructors.py b/tests/test_factory_constructors.py index 67f859b9ab..c6ae98c7fb 100644 --- a/tests/test_factory_constructors.py +++ b/tests/test_factory_constructors.py @@ -78,10 +78,10 @@ def test_init_factory_signature(msg): msg(excinfo.value) == """ __init__(): incompatible constructor arguments. The following argument types are supported: - 1. m.factory_constructors.TestFactory1(arg0: m.factory_constructors.tag.unique_ptr_tag, arg1: typing.SupportsInt) + 1. m.factory_constructors.TestFactory1(arg0: m.factory_constructors.tag.unique_ptr_tag, arg1: typing.SupportsInt | typing.SupportsIndex) 2. m.factory_constructors.TestFactory1(arg0: str) 3. m.factory_constructors.TestFactory1(arg0: m.factory_constructors.tag.pointer_tag) - 4. m.factory_constructors.TestFactory1(arg0: object, arg1: typing.SupportsInt, arg2: object) + 4. m.factory_constructors.TestFactory1(arg0: object, arg1: typing.SupportsInt | typing.SupportsIndex, arg2: object) Invoked with: 'invalid', 'constructor', 'arguments' """ @@ -93,13 +93,13 @@ def test_init_factory_signature(msg): __init__(*args, **kwargs) Overloaded function. - 1. __init__(self: m.factory_constructors.TestFactory1, arg0: m.factory_constructors.tag.unique_ptr_tag, arg1: typing.SupportsInt) -> None + 1. __init__(self: m.factory_constructors.TestFactory1, arg0: m.factory_constructors.tag.unique_ptr_tag, arg1: typing.SupportsInt | typing.SupportsIndex) -> None 2. __init__(self: m.factory_constructors.TestFactory1, arg0: str) -> None 3. __init__(self: m.factory_constructors.TestFactory1, arg0: m.factory_constructors.tag.pointer_tag) -> None - 4. __init__(self: m.factory_constructors.TestFactory1, arg0: object, arg1: typing.SupportsInt, arg2: object) -> None + 4. __init__(self: m.factory_constructors.TestFactory1, arg0: object, arg1: typing.SupportsInt | typing.SupportsIndex, arg2: object) -> None """ ) diff --git a/tests/test_kwargs_and_defaults.py b/tests/test_kwargs_and_defaults.py index b62e4b7412..57345f128f 100644 --- a/tests/test_kwargs_and_defaults.py +++ b/tests/test_kwargs_and_defaults.py @@ -9,28 +9,28 @@ def test_function_signatures(doc): assert ( doc(m.kw_func0) - == "kw_func0(arg0: typing.SupportsInt, arg1: typing.SupportsInt) -> str" + == "kw_func0(arg0: typing.SupportsInt | typing.SupportsIndex, arg1: typing.SupportsInt | typing.SupportsIndex) -> str" ) assert ( doc(m.kw_func1) - == "kw_func1(x: typing.SupportsInt, y: typing.SupportsInt) -> str" + == "kw_func1(x: typing.SupportsInt | typing.SupportsIndex, y: typing.SupportsInt | typing.SupportsIndex) -> str" ) assert ( doc(m.kw_func2) - == "kw_func2(x: typing.SupportsInt = 100, y: typing.SupportsInt = 200) -> str" + == "kw_func2(x: typing.SupportsInt | typing.SupportsIndex = 100, y: typing.SupportsInt | typing.SupportsIndex = 200) -> str" ) assert doc(m.kw_func3) == "kw_func3(data: str = 'Hello world!') -> None" assert ( doc(m.kw_func4) - == "kw_func4(myList: collections.abc.Sequence[typing.SupportsInt] = [13, 17]) -> str" + == "kw_func4(myList: collections.abc.Sequence[typing.SupportsInt | typing.SupportsIndex] = [13, 17]) -> str" ) assert ( doc(m.kw_func_udl) - == "kw_func_udl(x: typing.SupportsInt, y: typing.SupportsInt = 300) -> str" + == "kw_func_udl(x: typing.SupportsInt | typing.SupportsIndex, y: typing.SupportsInt | typing.SupportsIndex = 300) -> str" ) assert ( doc(m.kw_func_udl_z) - == "kw_func_udl_z(x: typing.SupportsInt, y: typing.SupportsInt = 0) -> str" + == "kw_func_udl_z(x: typing.SupportsInt | typing.SupportsIndex, y: typing.SupportsInt | typing.SupportsIndex = 0) -> str" ) assert doc(m.args_function) == "args_function(*args) -> tuple" assert ( @@ -42,11 +42,11 @@ def test_function_signatures(doc): ) assert ( doc(m.KWClass.foo0) - == "foo0(self: m.kwargs_and_defaults.KWClass, arg0: typing.SupportsInt, arg1: typing.SupportsFloat) -> None" + == "foo0(self: m.kwargs_and_defaults.KWClass, arg0: typing.SupportsInt | typing.SupportsIndex, arg1: typing.SupportsFloat | typing.SupportsIndex) -> None" ) assert ( doc(m.KWClass.foo1) - == "foo1(self: m.kwargs_and_defaults.KWClass, x: typing.SupportsInt, y: typing.SupportsFloat) -> None" + == "foo1(self: m.kwargs_and_defaults.KWClass, x: typing.SupportsInt | typing.SupportsIndex, y: typing.SupportsFloat | typing.SupportsIndex) -> None" ) assert ( doc(m.kw_lb_func0) @@ -138,7 +138,7 @@ def test_mixed_args_and_kwargs(msg): msg(excinfo.value) == """ mixed_plus_args(): incompatible function arguments. The following argument types are supported: - 1. (arg0: typing.SupportsInt, arg1: typing.SupportsFloat, *args) -> tuple + 1. (arg0: typing.SupportsInt | typing.SupportsIndex, arg1: typing.SupportsFloat | typing.SupportsIndex, *args) -> tuple Invoked with: 1 """ @@ -149,7 +149,7 @@ def test_mixed_args_and_kwargs(msg): msg(excinfo.value) == """ mixed_plus_args(): incompatible function arguments. The following argument types are supported: - 1. (arg0: typing.SupportsInt, arg1: typing.SupportsFloat, *args) -> tuple + 1. (arg0: typing.SupportsInt | typing.SupportsIndex, arg1: typing.SupportsFloat | typing.SupportsIndex, *args) -> tuple Invoked with: """ @@ -183,7 +183,7 @@ def test_mixed_args_and_kwargs(msg): msg(excinfo.value) == """ mixed_plus_args_kwargs_defaults(): incompatible function arguments. The following argument types are supported: - 1. (i: typing.SupportsInt = 1, j: typing.SupportsFloat = 3.14159, *args, **kwargs) -> tuple + 1. (i: typing.SupportsInt | typing.SupportsIndex = 1, j: typing.SupportsFloat | typing.SupportsIndex = 3.14159, *args, **kwargs) -> tuple Invoked with: 1; kwargs: i=1 """ @@ -194,7 +194,7 @@ def test_mixed_args_and_kwargs(msg): msg(excinfo.value) == """ mixed_plus_args_kwargs_defaults(): incompatible function arguments. The following argument types are supported: - 1. (i: typing.SupportsInt = 1, j: typing.SupportsFloat = 3.14159, *args, **kwargs) -> tuple + 1. (i: typing.SupportsInt | typing.SupportsIndex = 1, j: typing.SupportsFloat | typing.SupportsIndex = 3.14159, *args, **kwargs) -> tuple Invoked with: 1, 2; kwargs: j=1 """ @@ -211,7 +211,7 @@ def test_mixed_args_and_kwargs(msg): msg(excinfo.value) == """ args_kwonly(): incompatible function arguments. The following argument types are supported: - 1. (i: typing.SupportsInt, j: typing.SupportsFloat, *args, z: typing.SupportsInt) -> tuple + 1. (i: typing.SupportsInt | typing.SupportsIndex, j: typing.SupportsFloat | typing.SupportsIndex, *args, z: typing.SupportsInt | typing.SupportsIndex) -> tuple Invoked with: 2, 2.5, 22 """ @@ -233,12 +233,12 @@ def test_mixed_args_and_kwargs(msg): ) assert ( m.args_kwonly_kwargs.__doc__ - == "args_kwonly_kwargs(i: typing.SupportsInt, j: typing.SupportsFloat, *args, z: typing.SupportsInt, **kwargs) -> tuple\n" + == "args_kwonly_kwargs(i: typing.SupportsInt | typing.SupportsIndex, j: typing.SupportsFloat | typing.SupportsIndex, *args, z: typing.SupportsInt | typing.SupportsIndex, **kwargs) -> tuple\n" ) assert ( m.args_kwonly_kwargs_defaults.__doc__ - == "args_kwonly_kwargs_defaults(i: typing.SupportsInt = 1, j: typing.SupportsFloat = 3.14159, *args, z: typing.SupportsInt = 42, **kwargs) -> tuple\n" + == "args_kwonly_kwargs_defaults(i: typing.SupportsInt | typing.SupportsIndex = 1, j: typing.SupportsFloat | typing.SupportsIndex = 3.14159, *args, z: typing.SupportsInt | typing.SupportsIndex = 42, **kwargs) -> tuple\n" ) assert m.args_kwonly_kwargs_defaults() == (1, 3.14159, (), 42, {}) assert m.args_kwonly_kwargs_defaults(2) == (2, 3.14159, (), 42, {}) @@ -294,11 +294,11 @@ def test_keyword_only_args(msg): x.method(i=1, j=2) assert ( m.first_arg_kw_only.__init__.__doc__ - == "__init__(self: pybind11_tests.kwargs_and_defaults.first_arg_kw_only, *, i: typing.SupportsInt = 0) -> None\n" + == "__init__(self: pybind11_tests.kwargs_and_defaults.first_arg_kw_only, *, i: typing.SupportsInt | typing.SupportsIndex = 0) -> None\n" ) assert ( m.first_arg_kw_only.method.__doc__ - == "method(self: pybind11_tests.kwargs_and_defaults.first_arg_kw_only, *, i: typing.SupportsInt = 1, j: typing.SupportsInt = 2) -> None\n" + == "method(self: pybind11_tests.kwargs_and_defaults.first_arg_kw_only, *, i: typing.SupportsInt | typing.SupportsIndex = 1, j: typing.SupportsInt | typing.SupportsIndex = 2) -> None\n" ) @@ -344,7 +344,7 @@ def test_positional_only_args(): # Mix it with args and kwargs: assert ( m.args_kwonly_full_monty.__doc__ - == "args_kwonly_full_monty(arg0: typing.SupportsInt = 1, arg1: typing.SupportsInt = 2, /, j: typing.SupportsFloat = 3.14159, *args, z: typing.SupportsInt = 42, **kwargs) -> tuple\n" + == "args_kwonly_full_monty(arg0: typing.SupportsInt | typing.SupportsIndex = 1, arg1: typing.SupportsInt | typing.SupportsIndex = 2, /, j: typing.SupportsFloat | typing.SupportsIndex = 3.14159, *args, z: typing.SupportsInt | typing.SupportsIndex = 42, **kwargs) -> tuple\n" ) assert m.args_kwonly_full_monty() == (1, 2, 3.14159, (), 42, {}) assert m.args_kwonly_full_monty(8) == (8, 2, 3.14159, (), 42, {}) @@ -387,30 +387,30 @@ def test_positional_only_args(): # https://github.com/pybind/pybind11/pull/3402#issuecomment-963341987 assert ( m.first_arg_kw_only.pos_only.__doc__ - == "pos_only(self: pybind11_tests.kwargs_and_defaults.first_arg_kw_only, /, i: typing.SupportsInt, j: typing.SupportsInt) -> None\n" + == "pos_only(self: pybind11_tests.kwargs_and_defaults.first_arg_kw_only, /, i: typing.SupportsInt | typing.SupportsIndex, j: typing.SupportsInt | typing.SupportsIndex) -> None\n" ) def test_signatures(): assert ( m.kw_only_all.__doc__ - == "kw_only_all(*, i: typing.SupportsInt, j: typing.SupportsInt) -> tuple\n" + == "kw_only_all(*, i: typing.SupportsInt | typing.SupportsIndex, j: typing.SupportsInt | typing.SupportsIndex) -> tuple\n" ) assert ( m.kw_only_mixed.__doc__ - == "kw_only_mixed(i: typing.SupportsInt, *, j: typing.SupportsInt) -> tuple\n" + == "kw_only_mixed(i: typing.SupportsInt | typing.SupportsIndex, *, j: typing.SupportsInt | typing.SupportsIndex) -> tuple\n" ) assert ( m.pos_only_all.__doc__ - == "pos_only_all(i: typing.SupportsInt, j: typing.SupportsInt, /) -> tuple\n" + == "pos_only_all(i: typing.SupportsInt | typing.SupportsIndex, j: typing.SupportsInt | typing.SupportsIndex, /) -> tuple\n" ) assert ( m.pos_only_mix.__doc__ - == "pos_only_mix(i: typing.SupportsInt, /, j: typing.SupportsInt) -> tuple\n" + == "pos_only_mix(i: typing.SupportsInt | typing.SupportsIndex, /, j: typing.SupportsInt | typing.SupportsIndex) -> tuple\n" ) assert ( m.pos_kw_only_mix.__doc__ - == "pos_kw_only_mix(i: typing.SupportsInt, /, j: typing.SupportsInt, *, k: typing.SupportsInt) -> tuple\n" + == "pos_kw_only_mix(i: typing.SupportsInt | typing.SupportsIndex, /, j: typing.SupportsInt | typing.SupportsIndex, *, k: typing.SupportsInt | typing.SupportsIndex) -> tuple\n" ) diff --git a/tests/test_methods_and_attributes.py b/tests/test_methods_and_attributes.py index 6a8d993cb6..553d5bfc1b 100644 --- a/tests/test_methods_and_attributes.py +++ b/tests/test_methods_and_attributes.py @@ -251,7 +251,7 @@ def test_no_mixed_overloads(): "#define PYBIND11_DETAILED_ERROR_MESSAGES or compile in debug mode for more details" if not detailed_error_messages_enabled else "error while attempting to bind static method ExampleMandA.overload_mixed1" - "(arg0: typing.SupportsFloat) -> str" + "(arg0: typing.SupportsFloat | typing.SupportsIndex) -> str" ) ) @@ -264,7 +264,7 @@ def test_no_mixed_overloads(): "#define PYBIND11_DETAILED_ERROR_MESSAGES or compile in debug mode for more details" if not detailed_error_messages_enabled else "error while attempting to bind instance method ExampleMandA.overload_mixed2" - "(self: pybind11_tests.methods_and_attributes.ExampleMandA, arg0: typing.SupportsInt, arg1: typing.SupportsInt)" + "(self: pybind11_tests.methods_and_attributes.ExampleMandA, arg0: typing.SupportsInt | typing.SupportsIndex, arg1: typing.SupportsInt | typing.SupportsIndex)" " -> str" ) ) @@ -491,7 +491,7 @@ def test_str_issue(msg): msg(excinfo.value) == """ __init__(): incompatible constructor arguments. The following argument types are supported: - 1. m.methods_and_attributes.StrIssue(arg0: typing.SupportsInt) + 1. m.methods_and_attributes.StrIssue(arg0: typing.SupportsInt | typing.SupportsIndex) 2. m.methods_and_attributes.StrIssue() Invoked with: 'no', 'such', 'constructor' @@ -534,21 +534,27 @@ def test_overload_ordering(): assert m.overload_order(0) == 4 assert ( - "1. overload_order(arg0: typing.SupportsInt) -> int" in m.overload_order.__doc__ + "1. overload_order(arg0: typing.SupportsInt | typing.SupportsIndex) -> int" + in m.overload_order.__doc__ ) assert "2. overload_order(arg0: str) -> int" in m.overload_order.__doc__ assert "3. overload_order(arg0: str) -> int" in m.overload_order.__doc__ assert ( - "4. overload_order(arg0: typing.SupportsInt) -> int" in m.overload_order.__doc__ + "4. overload_order(arg0: typing.SupportsInt | typing.SupportsIndex) -> int" + in m.overload_order.__doc__ ) with pytest.raises(TypeError) as err: m.overload_order(1.1) - assert "1. (arg0: typing.SupportsInt) -> int" in str(err.value) + assert "1. (arg0: typing.SupportsInt | typing.SupportsIndex) -> int" in str( + err.value + ) assert "2. (arg0: str) -> int" in str(err.value) assert "3. (arg0: str) -> int" in str(err.value) - assert "4. (arg0: typing.SupportsInt) -> int" in str(err.value) + assert "4. (arg0: typing.SupportsInt | typing.SupportsIndex) -> int" in str( + err.value + ) def test_rvalue_ref_param(): diff --git a/tests/test_numpy_dtypes.py b/tests/test_numpy_dtypes.py index 9f85742809..22814aba5a 100644 --- a/tests/test_numpy_dtypes.py +++ b/tests/test_numpy_dtypes.py @@ -367,7 +367,7 @@ def test_complex_array(): def test_signature(doc): assert ( doc(m.create_rec_nested) - == "create_rec_nested(arg0: typing.SupportsInt) -> numpy.typing.NDArray[NestedStruct]" + == "create_rec_nested(arg0: typing.SupportsInt | typing.SupportsIndex) -> numpy.typing.NDArray[NestedStruct]" ) diff --git a/tests/test_numpy_vectorize.py b/tests/test_numpy_vectorize.py index d405e68002..05f7c704f5 100644 --- a/tests/test_numpy_vectorize.py +++ b/tests/test_numpy_vectorize.py @@ -211,11 +211,11 @@ def test_passthrough_arguments(doc): "vec_passthrough(" + ", ".join( [ - "arg0: typing.SupportsFloat", + "arg0: typing.SupportsFloat | typing.SupportsIndex", "arg1: typing.Annotated[numpy.typing.ArrayLike, numpy.float64]", "arg2: typing.Annotated[numpy.typing.ArrayLike, numpy.float64]", "arg3: typing.Annotated[numpy.typing.ArrayLike, numpy.int32]", - "arg4: typing.SupportsInt", + "arg4: typing.SupportsInt | typing.SupportsIndex", "arg5: m.numpy_vectorize.NonPODClass", "arg6: typing.Annotated[numpy.typing.ArrayLike, numpy.float64]", ] diff --git a/tests/test_pytypes.py b/tests/test_pytypes.py index c1798f924c..09fc5f37ee 100644 --- a/tests/test_pytypes.py +++ b/tests/test_pytypes.py @@ -944,14 +944,14 @@ def test_tuple_variable_length_annotations(doc): def test_dict_annotations(doc): assert ( doc(m.annotate_dict_str_int) - == "annotate_dict_str_int(arg0: dict[str, typing.SupportsInt]) -> None" + == "annotate_dict_str_int(arg0: dict[str, typing.SupportsInt | typing.SupportsIndex]) -> None" ) def test_list_annotations(doc): assert ( doc(m.annotate_list_int) - == "annotate_list_int(arg0: list[typing.SupportsInt]) -> None" + == "annotate_list_int(arg0: list[typing.SupportsInt | typing.SupportsIndex]) -> None" ) @@ -969,7 +969,7 @@ def test_iterable_annotations(doc): def test_iterator_annotations(doc): assert ( doc(m.annotate_iterator_int) - == "annotate_iterator_int(arg0: collections.abc.Iterator[typing.SupportsInt]) -> None" + == "annotate_iterator_int(arg0: collections.abc.Iterator[typing.SupportsInt | typing.SupportsIndex]) -> None" ) @@ -989,7 +989,8 @@ def test_fn_return_only(doc): def test_type_annotation(doc): assert ( - doc(m.annotate_type) == "annotate_type(arg0: type[typing.SupportsInt]) -> type" + doc(m.annotate_type) + == "annotate_type(arg0: type[typing.SupportsInt | typing.SupportsIndex]) -> type" ) @@ -1007,7 +1008,7 @@ def test_union_typing_only(doc): def test_union_object_annotations(doc): assert ( doc(m.annotate_union_to_object) - == "annotate_union_to_object(arg0: typing.SupportsInt | str) -> object" + == "annotate_union_to_object(arg0: typing.SupportsInt | typing.SupportsIndex | str) -> object" ) @@ -1044,7 +1045,7 @@ def test_never_annotation(doc, backport_typehints): def test_optional_object_annotations(doc): assert ( doc(m.annotate_optional_to_object) - == "annotate_optional_to_object(arg0: typing.SupportsInt | None) -> object" + == "annotate_optional_to_object(arg0: typing.SupportsInt | typing.SupportsIndex | None) -> object" ) @@ -1167,7 +1168,10 @@ def get_annotations_helper(o): def test_module_attribute_types() -> None: module_annotations = get_annotations_helper(m) - assert module_annotations["list_int"] == "list[typing.SupportsInt]" + assert ( + module_annotations["list_int"] + == "list[typing.SupportsInt | typing.SupportsIndex]" + ) assert module_annotations["set_str"] == "set[str]" assert module_annotations["foo"] == "pybind11_tests.pytypes.foo" @@ -1190,7 +1194,10 @@ def test_get_annotations_compliance() -> None: module_annotations = get_annotations(m) - assert module_annotations["list_int"] == "list[typing.SupportsInt]" + assert ( + module_annotations["list_int"] + == "list[typing.SupportsInt | typing.SupportsIndex]" + ) assert module_annotations["set_str"] == "set[str]" @@ -1204,10 +1211,13 @@ def test_class_attribute_types() -> None: instance_annotations = get_annotations_helper(m.Instance) assert empty_annotations is None - assert static_annotations["x"] == "typing.ClassVar[typing.SupportsFloat]" + assert ( + static_annotations["x"] + == "typing.ClassVar[typing.SupportsFloat | typing.SupportsIndex]" + ) assert ( static_annotations["dict_str_int"] - == "typing.ClassVar[dict[str, typing.SupportsInt]]" + == "typing.ClassVar[dict[str, typing.SupportsInt | typing.SupportsIndex]]" ) assert m.Static.x == 1.0 @@ -1219,7 +1229,7 @@ def test_class_attribute_types() -> None: static.dict_str_int["hi"] = 3 assert m.Static().dict_str_int == {"hi": 3} - assert instance_annotations["y"] == "typing.SupportsFloat" + assert instance_annotations["y"] == "typing.SupportsFloat | typing.SupportsIndex" instance1 = m.Instance() instance1.y = 4.0 @@ -1236,7 +1246,10 @@ def test_class_attribute_types() -> None: def test_redeclaration_attr_with_type_hint() -> None: obj = m.Instance() m.attr_with_type_hint_float_x(obj) - assert get_annotations_helper(obj)["x"] == "typing.SupportsFloat" + assert ( + get_annotations_helper(obj)["x"] + == "typing.SupportsFloat | typing.SupportsIndex" + ) with pytest.raises( RuntimeError, match=r'^__annotations__\["x"\] was set already\.$' ): diff --git a/tests/test_stl.py b/tests/test_stl.py index 4a57635e27..b04f55c9f8 100644 --- a/tests/test_stl.py +++ b/tests/test_stl.py @@ -22,7 +22,7 @@ def test_vector(doc): assert doc(m.cast_vector) == "cast_vector() -> list[int]" assert ( doc(m.load_vector) - == "load_vector(arg0: collections.abc.Sequence[typing.SupportsInt]) -> bool" + == "load_vector(arg0: collections.abc.Sequence[typing.SupportsInt | typing.SupportsIndex]) -> bool" ) # Test regression caused by 936: pointers to stl containers weren't castable @@ -51,7 +51,7 @@ def test_array(doc): ) assert ( doc(m.load_array) - == 'load_array(arg0: typing.Annotated[collections.abc.Sequence[typing.SupportsInt], "FixedSize(2)"]) -> bool' + == 'load_array(arg0: typing.Annotated[collections.abc.Sequence[typing.SupportsInt | typing.SupportsIndex], "FixedSize(2)"]) -> bool' ) @@ -72,7 +72,7 @@ def test_valarray(doc): assert doc(m.cast_valarray) == "cast_valarray() -> list[int]" assert ( doc(m.load_valarray) - == "load_valarray(arg0: collections.abc.Sequence[typing.SupportsInt]) -> bool" + == "load_valarray(arg0: collections.abc.Sequence[typing.SupportsInt | typing.SupportsIndex]) -> bool" ) @@ -234,7 +234,7 @@ def test_reference_sensitive_optional(doc): assert ( doc(m.double_or_zero_refsensitive) - == "double_or_zero_refsensitive(arg0: typing.SupportsInt | None) -> int" + == "double_or_zero_refsensitive(arg0: typing.SupportsInt | typing.SupportsIndex | None) -> int" ) assert m.half_or_none_refsensitive(0) is None @@ -352,7 +352,7 @@ def test_variant(doc): assert ( doc(m.load_variant) - == "load_variant(arg0: typing.SupportsInt | str | typing.SupportsFloat | None) -> str" + == "load_variant(arg0: typing.SupportsInt | typing.SupportsIndex | str | typing.SupportsFloat | typing.SupportsIndex | None) -> str" ) @@ -368,7 +368,7 @@ def test_variant_monostate(doc): assert ( doc(m.load_monostate_variant) - == "load_monostate_variant(arg0: None | typing.SupportsInt | str) -> str" + == "load_monostate_variant(arg0: None | typing.SupportsInt | typing.SupportsIndex | str) -> str" ) @@ -388,7 +388,7 @@ def test_stl_pass_by_pointer(msg): msg(excinfo.value) == """ stl_pass_by_pointer(): incompatible function arguments. The following argument types are supported: - 1. (v: collections.abc.Sequence[typing.SupportsInt] = None) -> list[int] + 1. (v: collections.abc.Sequence[typing.SupportsInt | typing.SupportsIndex] = None) -> list[int] Invoked with: """ @@ -400,7 +400,7 @@ def test_stl_pass_by_pointer(msg): msg(excinfo.value) == """ stl_pass_by_pointer(): incompatible function arguments. The following argument types are supported: - 1. (v: collections.abc.Sequence[typing.SupportsInt] = None) -> list[int] + 1. (v: collections.abc.Sequence[typing.SupportsInt | typing.SupportsIndex] = None) -> list[int] Invoked with: None """ @@ -615,7 +615,7 @@ class FormalSequenceLike(BareSequenceLike, Sequence): # convert mode assert ( doc(m.roundtrip_std_vector_int) - == "roundtrip_std_vector_int(arg0: collections.abc.Sequence[typing.SupportsInt]) -> list[int]" + == "roundtrip_std_vector_int(arg0: collections.abc.Sequence[typing.SupportsInt | typing.SupportsIndex]) -> list[int]" ) assert m.roundtrip_std_vector_int([1, 2, 3]) == [1, 2, 3] assert m.roundtrip_std_vector_int((1, 2, 3)) == [1, 2, 3] @@ -668,7 +668,7 @@ class FormalMappingLike(BareMappingLike, Mapping): # convert mode assert ( doc(m.roundtrip_std_map_str_int) - == "roundtrip_std_map_str_int(arg0: collections.abc.Mapping[str, typing.SupportsInt]) -> dict[str, int]" + == "roundtrip_std_map_str_int(arg0: collections.abc.Mapping[str, typing.SupportsInt | typing.SupportsIndex]) -> dict[str, int]" ) assert m.roundtrip_std_map_str_int(a1b2c3) == a1b2c3 assert m.roundtrip_std_map_str_int(FormalMappingLike(**a1b2c3)) == a1b2c3 @@ -714,7 +714,7 @@ class FormalSetLike(BareSetLike, Set): # convert mode assert ( doc(m.roundtrip_std_set_int) - == "roundtrip_std_set_int(arg0: collections.abc.Set[typing.SupportsInt]) -> set[int]" + == "roundtrip_std_set_int(arg0: collections.abc.Set[typing.SupportsInt | typing.SupportsIndex]) -> set[int]" ) assert m.roundtrip_std_set_int({1, 2, 3}) == {1, 2, 3} assert m.roundtrip_std_set_int(FormalSetLike(1, 2, 3)) == {1, 2, 3} diff --git a/tests/test_type_caster_pyobject_ptr.py b/tests/test_type_caster_pyobject_ptr.py index 5df8ca0196..f9abd0063a 100644 --- a/tests/test_type_caster_pyobject_ptr.py +++ b/tests/test_type_caster_pyobject_ptr.py @@ -103,7 +103,8 @@ def test_return_list_pyobject_ptr_reference(): def test_type_caster_name_via_incompatible_function_arguments_type_error(): with pytest.raises( - TypeError, match=r"1\. \(arg0: object, arg1: typing.SupportsInt\) -> None" + TypeError, + match=r"1\. \(arg0: object, arg1: typing.SupportsInt \| typing.SupportsIndex\) -> None", ): m.pass_pyobject_ptr_and_int(ValueHolder(101), ValueHolder(202))