diff --git a/docs/advanced/functions.rst b/docs/advanced/functions.rst index ff00c9c8ac..c9092cdabc 100644 --- a/docs/advanced/functions.rst +++ b/docs/advanced/functions.rst @@ -437,10 +437,10 @@ Certain argument types may support conversion from one type to another. Some examples of conversions are: * :ref:`implicit_conversions` declared using ``py::implicitly_convertible()`` -* Calling a method accepting a double with an integer argument -* Calling a ``std::complex`` argument with a non-complex python type - (for example, with a float). (Requires the optional ``pybind11/complex.h`` - header). +* Passing an argument that implements ``__float__`` or ``__index__`` to ``float`` or ``double``. +* Passing an argument that implements ``__int__`` or ``__index__`` to ``int``. +* Passing an argument that implements ``__complex__``, ``__float__``, or ``__index__`` to ``std::complex``. + (Requires the optional ``pybind11/complex.h`` header). * Calling a function taking an Eigen matrix reference with a numpy array of the wrong type or of an incompatible data layout. (Requires the optional ``pybind11/eigen.h`` header). @@ -452,24 +452,37 @@ object, such as: .. code-block:: cpp - m.def("floats_only", [](double f) { return 0.5 * f; }, py::arg("f").noconvert()); - m.def("floats_preferred", [](double f) { return 0.5 * f; }, py::arg("f")); + m.def("supports_float", [](double f) { return 0.5 * f; }, py::arg("f")); + m.def("only_float", [](double f) { return 0.5 * f; }, py::arg("f").noconvert()); -Attempting the call the second function (the one without ``.noconvert()``) with -an integer will succeed, but attempting to call the ``.noconvert()`` version -will fail with a ``TypeError``: +``supports_float`` will accept any argument that implements ``__float__`` or ``__index__``. +``only_float`` will only accept a float or int argument. Anything else will fail with a ``TypeError``: + +.. note:: + + The noconvert behaviour of float, double and complex has changed to match PEP 484. + A float/double argument marked noconvert will accept float or int. + A std::complex argument will accept complex, float or int. .. code-block:: pycon - >>> floats_preferred(4) + class MyFloat: + def __init__(self, value: float) -> None: + self._value = float(value) + def __repr__(self) -> str: + return f"MyFloat({self._value})" + def __float__(self) -> float: + return self._value + + >>> supports_float(MyFloat(4)) 2.0 - >>> floats_only(4) + >>> only_float(MyFloat(4)) Traceback (most recent call last): File "", line 1, in - TypeError: floats_only(): incompatible function arguments. The following argument types are supported: + TypeError: only_float(): incompatible function arguments. The following argument types are supported: 1. (f: float) -> float - Invoked with: 4 + Invoked with: MyFloat(4) You may, of course, combine this with the :var:`_a` shorthand notation (see :ref:`keyword_args`) and/or :ref:`default_args`. It is also permitted to omit diff --git a/include/pybind11/cast.h b/include/pybind11/cast.h index 4708101d80..3639c4f840 100644 --- a/include/pybind11/cast.h +++ b/include/pybind11/cast.h @@ -244,29 +244,18 @@ struct type_caster::value && !is_std_char_t return false; } -#if !defined(PYPY_VERSION) - auto index_check = [](PyObject *o) { return PyIndex_Check(o); }; -#else - // In PyPy 7.3.3, `PyIndex_Check` is implemented by calling `__index__`, - // while CPython only considers the existence of `nb_index`/`__index__`. - auto index_check = [](PyObject *o) { return hasattr(o, "__index__"); }; -#endif - if (std::is_floating_point::value) { - if (convert || PyFloat_Check(src.ptr())) { + if (convert || PyFloat_Check(src.ptr()) || PYBIND11_LONG_CHECK(src.ptr())) { py_value = (py_type) PyFloat_AsDouble(src.ptr()); } else { return false; } - } else if (PyFloat_Check(src.ptr()) - || (!convert && !PYBIND11_LONG_CHECK(src.ptr()) && !index_check(src.ptr()))) { - return false; - } else { + } else if (convert || PYBIND11_LONG_CHECK(src.ptr()) || PYBIND11_INDEX_CHECK(src.ptr())) { handle src_or_index = src; // PyPy: 7.3.7's 3.8 does not implement PyLong_*'s __index__ calls. #if defined(PYPY_VERSION) object index; - if (!PYBIND11_LONG_CHECK(src.ptr())) { // So: index_check(src.ptr()) + if (!PYBIND11_LONG_CHECK(src.ptr())) { // So: PYBIND11_INDEX_CHECK(src.ptr()) index = reinterpret_steal(PyNumber_Index(src.ptr())); if (!index) { PyErr_Clear(); @@ -284,6 +273,8 @@ struct type_caster::value && !is_std_char_t ? (py_type) PyLong_AsLong(src_or_index.ptr()) : (py_type) PYBIND11_LONG_AS_LONGLONG(src_or_index.ptr()); } + } else { + return false; } // Python API reported an error @@ -347,9 +338,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..8f285d778f 100644 --- a/include/pybind11/complex.h +++ b/include/pybind11/complex.h @@ -51,10 +51,28 @@ class type_caster> { if (!src) { return false; } - if (!convert && !PyComplex_Check(src.ptr())) { + if (!convert + && !(PyComplex_Check(src.ptr()) || PyFloat_Check(src.ptr()) + || PYBIND11_LONG_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 +86,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/conftest.py b/tests/conftest.py index 39de4e1381..b96055fc10 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -17,10 +17,12 @@ import textwrap import traceback import weakref -from typing import Callable +from typing import Callable, SupportsIndex, TypeVar import pytest +import env + # Early diagnostic for failed imports try: import pybind11_tests @@ -311,3 +313,27 @@ def backport(sanatized_string: SanitizedString) -> SanitizedString: return sanatized_string return backport + + +_EXPECTED_T = TypeVar("_EXPECTED_T") + + +# TODO: Avoid DeprecationWarning in `PyLong_AsLong` (and similar) +# TODO: PyPy 3.8 does not behave like CPython 3.8 here yet (7.3.7) +# https://github.com/pybind/pybind11/issues/3408 +@pytest.fixture +def avoid_PyLong_AsLong_deprecation() -> Callable[ + [Callable[[SupportsIndex], _EXPECTED_T], SupportsIndex, _EXPECTED_T], bool +]: + def check( + convert: Callable[[SupportsIndex], _EXPECTED_T], + value: SupportsIndex, + expected: _EXPECTED_T, + ) -> bool: + if sys.version_info < (3, 10) and env.CPYTHON: + with pytest.deprecated_call(): + return convert(value) == expected + else: + return convert(value) == expected + + return check diff --git a/tests/env.py b/tests/env.py index 1773925918..ccb1fd30b6 100644 --- a/tests/env.py +++ b/tests/env.py @@ -4,8 +4,6 @@ import sys import sysconfig -import pytest - ANDROID = sys.platform.startswith("android") LINUX = sys.platform.startswith("linux") MACOS = sys.platform.startswith("darwin") @@ -29,19 +27,3 @@ or GRAALPY or (CPYTHON and PY_GIL_DISABLED and (3, 13) <= sys.version_info < (3, 14)) ) - - -def deprecated_call(): - """ - pytest.deprecated_call() seems broken in pytest<3.9.x; concretely, it - doesn't work on CPython 3.8.0 with pytest==3.3.2 on Ubuntu 18.04 (#2922). - - This is a narrowed reimplementation of the following PR :( - https://github.com/pytest-dev/pytest/pull/4104 - """ - # TODO: Remove this when testing requires pytest>=3.9. - pieces = pytest.__version__.split(".") - pytest_major_minor = (int(pieces[0]), int(pieces[1])) - if pytest_major_minor < (3, 9): - return pytest.warns((DeprecationWarning, PendingDeprecationWarning)) - return pytest.deprecated_call() diff --git a/tests/test_builtin_casters.cpp b/tests/test_builtin_casters.cpp index c516f8de7e..7e6b97d14d 100644 --- a/tests/test_builtin_casters.cpp +++ b/tests/test_builtin_casters.cpp @@ -363,6 +363,13 @@ 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_cast_strict", + [](std::complex x) { return "({}, {})"_s.format(x.real(), x.imag()); }, + py::arg{}.noconvert()); + + 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..7776705cf1 100644 --- a/tests/test_builtin_casters.py +++ b/tests/test_builtin_casters.py @@ -1,10 +1,7 @@ from __future__ import annotations -import sys - import pytest -import env from pybind11_tests import IncType, UserType from pybind11_tests import builtin_casters as m @@ -247,7 +244,7 @@ def test_integer_casting(): assert "incompatible function arguments" in str(excinfo.value) -def test_int_convert(doc): +def test_int_convert(doc, avoid_PyLong_AsLong_deprecation): class Int: def __int__(self): return 42 @@ -286,7 +283,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): @@ -297,14 +297,9 @@ def cant_convert(v): assert convert(7) == 7 assert noconvert(7) == 7 - cant_convert(3.14159) - # TODO: Avoid DeprecationWarning in `PyLong_AsLong` (and similar) - # TODO: PyPy 3.8 does not behave like CPython 3.8 here yet (7.3.7) - if sys.version_info < (3, 10) and env.CPYTHON: - with env.deprecated_call(): - assert convert(Int()) == 42 - else: - assert convert(Int()) == 42 + assert avoid_PyLong_AsLong_deprecation(convert, 3.14159, 3) + requires_conversion(3.14159) + assert avoid_PyLong_AsLong_deprecation(convert, Int(), 42) requires_conversion(Int()) cant_convert(NotInt()) cant_convert(Float()) @@ -312,6 +307,7 @@ def cant_convert(v): # Before Python 3.8, `PyLong_AsLong` does not pick up on `obj.__index__`, # but pybind11 "backports" this behavior. assert convert(Index()) == 42 + assert isinstance(convert(Index()), int) assert noconvert(Index()) == 42 assert convert(IntAndIndex()) == 0 # Fishy; `int(DoubleThought)` == 42 assert noconvert(IntAndIndex()) == 0 @@ -322,22 +318,42 @@ 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 + assert pytest.approx(noconvert(3)) == 3.0 + cant_convert(Int()) -def test_numpy_int_convert(): +def test_numpy_int_convert(avoid_PyLong_AsLong_deprecation): np = pytest.importorskip("numpy") convert, noconvert = m.int_passthrough, m.int_passthrough_noconvert @@ -349,15 +365,7 @@ def require_implicit(v): assert convert(np.intc(42)) == 42 assert noconvert(np.intc(42)) == 42 - # The implicit conversion from np.float32 is undesirable but currently accepted. - # TODO: Avoid DeprecationWarning in `PyLong_AsLong` (and similar) - # TODO: PyPy 3.8 does not behave like CPython 3.8 here yet (7.3.7) - # https://github.com/pybind/pybind11/issues/3408 - if (3, 8) <= sys.version_info < (3, 10) and env.CPYTHON: - with env.deprecated_call(): - assert convert(np.float32(3.14159)) == 3 - else: - assert convert(np.float32(3.14159)) == 3 + assert avoid_PyLong_AsLong_deprecation(convert, np.float32(3.14159), 3) require_implicit(np.float32(3.14159)) @@ -381,7 +389,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 +466,66 @@ 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)" + assert m.complex_cast_strict(1) == "(1.0, 0.0)" + assert m.complex_cast_strict(3.0) == "(3.0, 0.0)" + assert m.complex_cast_strict(complex(5, 4)) == "(5.0, 4.0)" + assert m.complex_cast_strict(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) + + assert noconvert(1) == 1.0 + assert noconvert(2.0) == 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..ad1dca20e6 100644 --- a/tests/test_custom_type_casters.py +++ b/tests/test_custom_type_casters.py @@ -6,7 +6,7 @@ from pybind11_tests import custom_type_casters as m -def test_noconvert_args(msg): +def test_noconvert_args(msg, avoid_PyLong_AsLong_deprecation): a = m.ArgInspector() assert ( msg(a.f("hi")) @@ -55,31 +55,11 @@ def test_noconvert_args(msg): assert m.floats_preferred(4) == 2.0 assert m.floats_only(4.0) == 2.0 - with pytest.raises(TypeError) as excinfo: - m.floats_only(4) - assert ( - msg(excinfo.value) - == """ - floats_only(): incompatible function arguments. The following argument types are supported: - 1. (f: float) -> float - - Invoked with: 4 - """ - ) + assert m.floats_only(4) == 2.0 assert m.ints_preferred(4) == 2 assert m.ints_preferred(True) == 0 - with pytest.raises(TypeError) as excinfo: - m.ints_preferred(4.0) - assert ( - msg(excinfo.value) - == """ - ints_preferred(): incompatible function arguments. The following argument types are supported: - 1. (i: typing.SupportsInt) -> int - - Invoked with: 4.0 - """ - ) + assert avoid_PyLong_AsLong_deprecation(m.ints_preferred, 4.0, 2) assert m.ints_only(4) == 2 with pytest.raises(TypeError) as excinfo: 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..aede6524b4 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.cpp b/tests/test_factory_constructors.cpp index a387cd2e76..65a8e5cf15 100644 --- a/tests/test_factory_constructors.cpp +++ b/tests/test_factory_constructors.cpp @@ -401,11 +401,10 @@ TEST_SUBMODULE(factory_constructors, m) { pyNoisyAlloc.def(py::init([](double d, int) { return NoisyAlloc(d); })); // Old-style placement new init; requires preallocation ignoreOldStyleInitWarnings([&pyNoisyAlloc]() { - pyNoisyAlloc.def("__init__", - [](NoisyAlloc &a, double d, double) { new (&a) NoisyAlloc(d); }); + pyNoisyAlloc.def("__init__", [](NoisyAlloc &a, int i, double) { new (&a) NoisyAlloc(i); }); }); // Requires deallocation of previous overload preallocated value: - pyNoisyAlloc.def(py::init([](int i, double) { return new NoisyAlloc(i); })); + pyNoisyAlloc.def(py::init([](double d, double) { return new NoisyAlloc(d); })); // Regular again: requires yet another preallocation ignoreOldStyleInitWarnings([&pyNoisyAlloc]() { pyNoisyAlloc.def( diff --git a/tests/test_factory_constructors.py b/tests/test_factory_constructors.py index 67f859b9ab..635d762bb3 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 """ ) @@ -430,12 +430,12 @@ def test_reallocation_d(capture, msg): @pytest.mark.skipif("env.GRAALPY", reason="Cannot reliably trigger GC") def test_reallocation_e(capture, msg): with capture: - create_and_destroy(3.5, 4.5) + create_and_destroy(4, 4.5) assert msg(capture) == strip_comments( """ noisy new # preallocation needed before invoking placement-new overload noisy placement new # Placement new - NoisyAlloc(double 3.5) # construction + NoisyAlloc(int 4) # construction --- ~NoisyAlloc() # Destructor noisy delete # operator delete @@ -446,13 +446,13 @@ def test_reallocation_e(capture, msg): @pytest.mark.skipif("env.GRAALPY", reason="Cannot reliably trigger GC") def test_reallocation_f(capture, msg): with capture: - create_and_destroy(4, 0.5) + create_and_destroy(3.5, 0.5) assert msg(capture) == strip_comments( """ noisy new # preallocation needed before invoking placement-new overload noisy delete # deallocation of preallocated storage noisy new # Factory pointer allocation - NoisyAlloc(int 4) # factory pointer construction + NoisyAlloc(double 3.5) # factory pointer construction --- ~NoisyAlloc() # Destructor noisy delete # operator delete 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.cpp b/tests/test_methods_and_attributes.cpp index e324c8bdd4..f5fb02d121 100644 --- a/tests/test_methods_and_attributes.cpp +++ b/tests/test_methods_and_attributes.cpp @@ -240,33 +240,33 @@ TEST_SUBMODULE(methods_and_attributes, m) { #if defined(PYBIND11_OVERLOAD_CAST) .def("overloaded", py::overload_cast<>(&ExampleMandA::overloaded)) .def("overloaded", py::overload_cast(&ExampleMandA::overloaded)) + .def("overloaded", py::overload_cast(&ExampleMandA::overloaded)) .def("overloaded", py::overload_cast(&ExampleMandA::overloaded)) .def("overloaded", py::overload_cast(&ExampleMandA::overloaded)) - .def("overloaded", py::overload_cast(&ExampleMandA::overloaded)) .def("overloaded", py::overload_cast(&ExampleMandA::overloaded)) .def("overloaded_float", py::overload_cast(&ExampleMandA::overloaded)) .def("overloaded_const", py::overload_cast(&ExampleMandA::overloaded, py::const_)) + .def("overloaded_const", + py::overload_cast(&ExampleMandA::overloaded, py::const_)) .def("overloaded_const", py::overload_cast(&ExampleMandA::overloaded, py::const_)) .def("overloaded_const", py::overload_cast(&ExampleMandA::overloaded, py::const_)) - .def("overloaded_const", - py::overload_cast(&ExampleMandA::overloaded, py::const_)) .def("overloaded_const", py::overload_cast(&ExampleMandA::overloaded, py::const_)) #else // Use both the traditional static_cast method and the C++11 compatible overload_cast_ .def("overloaded", overload_cast_<>()(&ExampleMandA::overloaded)) .def("overloaded", overload_cast_()(&ExampleMandA::overloaded)) - .def("overloaded", overload_cast_()(&ExampleMandA::overloaded)) - .def("overloaded", static_cast(&ExampleMandA::overloaded)) - .def("overloaded", static_cast(&ExampleMandA::overloaded)) + .def("overloaded", overload_cast_()(&ExampleMandA::overloaded)) + .def("overloaded", static_cast(&ExampleMandA::overloaded)) + .def("overloaded", static_cast(&ExampleMandA::overloaded)) .def("overloaded", static_cast(&ExampleMandA::overloaded)) .def("overloaded_float", overload_cast_()(&ExampleMandA::overloaded)) .def("overloaded_const", overload_cast_()(&ExampleMandA::overloaded, py::const_)) - .def("overloaded_const", overload_cast_()(&ExampleMandA::overloaded, py::const_)) - .def("overloaded_const", static_cast(&ExampleMandA::overloaded)) - .def("overloaded_const", static_cast(&ExampleMandA::overloaded)) + .def("overloaded_const", overload_cast_()(&ExampleMandA::overloaded, py::const_)) + .def("overloaded_const", static_cast(&ExampleMandA::overloaded)) + .def("overloaded_const", static_cast(&ExampleMandA::overloaded)) .def("overloaded_const", static_cast(&ExampleMandA::overloaded)) #endif // test_no_mixed_overloads diff --git a/tests/test_methods_and_attributes.py b/tests/test_methods_and_attributes.py index 6a8d993cb6..647580de9b 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) + m.overload_order([]) - 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.cpp b/tests/test_stl.cpp index 6084d517df..8bddbb1f38 100644 --- a/tests/test_stl.cpp +++ b/tests/test_stl.cpp @@ -528,7 +528,7 @@ TEST_SUBMODULE(stl, m) { m.def("load_variant", [](const variant &v) { return py::detail::visit_helper::call(visitor(), v); }); - m.def("load_variant_2pass", [](variant v) { + m.def("load_variant_2pass", [](variant v) { return py::detail::visit_helper::call(visitor(), v); }); m.def("cast_variant", []() { diff --git a/tests/test_stl.py b/tests/test_stl.py index 4a57635e27..fb42636771 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 """ @@ -422,7 +422,7 @@ def test_missing_header_message(): ) with pytest.raises(TypeError) as excinfo: - cm.missing_header_arg([1.0, 2.0, 3.0]) + cm.missing_header_arg([1.0, "bar", 3.0]) assert expected_message in str(excinfo.value) with pytest.raises(TypeError) as excinfo: @@ -491,7 +491,7 @@ def test_pass_std_vector_pair_int(): def test_list_caster_fully_consumes_generator_object(): def gen_invalid(): - yield from [1, 2.0, 3] + yield from [1, "bar", 3] gen_obj = gen_invalid() with pytest.raises(TypeError): @@ -514,15 +514,15 @@ def test_pass_std_set_int(): def test_set_caster_dict_keys_failure(): - dict_keys = {1: None, 2.0: None, 3: None}.keys() + dict_keys = {1: None, "bar": None, 3: None}.keys() # The asserts does not really exercise anything in pybind11, but if one of # them fails in some future version of Python, the set_caster load # implementation may need to be revisited. - assert tuple(dict_keys) == (1, 2.0, 3) - assert tuple(dict_keys) == (1, 2.0, 3) + assert tuple(dict_keys) == (1, "bar", 3) + assert tuple(dict_keys) == (1, "bar", 3) with pytest.raises(TypeError): m.pass_std_set_int(dict_keys) - assert tuple(dict_keys) == (1, 2.0, 3) + assert tuple(dict_keys) == (1, "bar", 3) class FakePyMappingMissingItems: @@ -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..3c608ce923 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))