Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions include/pybind11/cast.h
Original file line number Diff line number Diff line change
Expand Up @@ -404,7 +404,7 @@ class type_caster<void> : public type_caster<void_type> {
template <typename T>
using cast_op_type = void *&;
explicit operator void *&() { return value; }
static constexpr auto name = const_name("types.CapsuleType");
static constexpr auto name = const_name(PYBIND11_CAPSULE_TYPE_TYPE_HINT);

private:
void *value = nullptr;
Expand Down Expand Up @@ -1361,7 +1361,7 @@ struct handle_type_name<dict> {
};
template <>
struct handle_type_name<anyset> {
static constexpr auto name = const_name("typing.Union[set, frozenset]");
static constexpr auto name = const_name("set | frozenset");
};
template <>
struct handle_type_name<set> {
Expand Down Expand Up @@ -1441,15 +1441,15 @@ struct handle_type_name<type> {
};
template <>
struct handle_type_name<capsule> {
static constexpr auto name = const_name("types.CapsuleType");
static constexpr auto name = const_name(PYBIND11_CAPSULE_TYPE_TYPE_HINT);
};
template <>
struct handle_type_name<ellipsis> {
static constexpr auto name = const_name("ellipsis");
};
template <>
struct handle_type_name<weakref> {
static constexpr auto name = const_name("weakref");
static constexpr auto name = const_name("weakref.ReferenceType");
};
template <>
struct handle_type_name<args> {
Expand Down
23 changes: 23 additions & 0 deletions include/pybind11/detail/common.h
Original file line number Diff line number Diff line change
Expand Up @@ -261,13 +261,36 @@
# define PYBIND11_HAS_SUBINTERPRETER_SUPPORT
#endif

// 3.13 Compatibility
#if 0x030D0000 <= PY_VERSION_HEX
# define PYBIND11_TYPE_IS_TYPE_HINT "typing.TypeIs"
# define PYBIND11_CAPSULE_TYPE_TYPE_HINT "types.CapsuleType"
#else
# define PYBIND11_TYPE_IS_TYPE_HINT "typing_extensions.TypeIs"
# define PYBIND11_CAPSULE_TYPE_TYPE_HINT "typing_extensions.CapsuleType"
#endif

// 3.12 Compatibility
#if 0x030C0000 <= PY_VERSION_HEX
# define PYBIND11_BUFFER_TYPE_HINT "collections.abc.Buffer"
#else
# define PYBIND11_BUFFER_TYPE_HINT "typing_extensions.Buffer"
#endif

// 3.11 Compatibility
#if 0x030B0000 <= PY_VERSION_HEX
# define PYBIND11_NEVER_TYPE_HINT "typing.Never"
#else
# define PYBIND11_NEVER_TYPE_HINT "typing_extensions.Never"
#endif

// 3.10 Compatibility
#if 0x030A0000 <= PY_VERSION_HEX
# define PYBIND11_TYPE_GUARD_TYPE_HINT "typing.TypeGuard"
#else
# define PYBIND11_TYPE_GUARD_TYPE_HINT "typing_extensions.TypeGuard"
#endif

// #define PYBIND11_STR_LEGACY_PERMISSIVE
// If DEFINED, pybind11::str can hold PyUnicodeObject or PyBytesObject
// (probably surprising and never documented, but this was the
Expand Down
25 changes: 25 additions & 0 deletions include/pybind11/detail/descr.h
Original file line number Diff line number Diff line change
Expand Up @@ -157,12 +157,24 @@ constexpr descr<1, Type> _() {
#endif // #ifndef _

constexpr descr<0> concat() { return {}; }
constexpr descr<0> union_concat() { return {}; }

template <size_t N, typename... Ts>
constexpr descr<N, Ts...> concat(const descr<N, Ts...> &descr) {
return descr;
}

template <size_t N, typename... Ts>
constexpr descr<N, Ts...> union_concat(const descr<N, Ts...> &descr) {
return descr;
}

template <size_t N1, size_t N2, typename... Ts1, typename... Ts2>
constexpr descr<N1 + N2 + 3, Ts1..., Ts2...> operator|(const descr<N1, Ts1...> &a,
const descr<N2, Ts2...> &b) {
return a + const_name(" | ") + b;
}

#ifdef __cpp_fold_expressions
template <size_t N1, size_t N2, typename... Ts1, typename... Ts2>
constexpr descr<N1 + N2 + 2, Ts1..., Ts2...> operator,(const descr<N1, Ts1...> &a,
Expand All @@ -174,12 +186,25 @@ template <size_t N, typename... Ts, typename... Args>
constexpr auto concat(const descr<N, Ts...> &d, const Args &...args) {
return (d, ..., args);
}

template <size_t N, typename... Ts, typename... Args>
constexpr auto union_concat(const descr<N, Ts...> &d, const Args &...args) {
return (d | ... | args);
}

#else
template <size_t N, typename... Ts, typename... Args>
constexpr auto concat(const descr<N, Ts...> &d, const Args &...args)
-> decltype(std::declval<descr<N + 2, Ts...>>() + concat(args...)) {
return d + const_name(", ") + concat(args...);
}

template <size_t N, typename... Ts, typename... Args>
constexpr auto union_concat(const descr<N, Ts...> &d, const Args &...args)
-> decltype(std::declval<descr<N + 3, Ts...>>() + union_concat(args...)) {
return d + const_name(" | ") + union_concat(args...);
}

#endif

template <size_t N, typename... Ts>
Expand Down
8 changes: 2 additions & 6 deletions include/pybind11/stl.h
Original file line number Diff line number Diff line change
Expand Up @@ -557,8 +557,7 @@ struct optional_caster {
return true;
}

PYBIND11_TYPE_CASTER(Type,
const_name("typing.Optional[") + value_conv::name + const_name("]"));
PYBIND11_TYPE_CASTER(Type, value_conv::name | make_caster<none>::name);
};

#if defined(PYBIND11_HAS_OPTIONAL)
Expand Down Expand Up @@ -642,10 +641,7 @@ struct variant_caster<V<Ts...>> {
}

using Type = V<Ts...>;
PYBIND11_TYPE_CASTER(Type,
const_name("typing.Union[")
+ ::pybind11::detail::concat(make_caster<Ts>::name...)
+ const_name("]"));
PYBIND11_TYPE_CASTER(Type, ::pybind11::detail::union_concat(make_caster<Ts>::name...));
};

#if defined(PYBIND11_HAS_VARIANT)
Expand Down
2 changes: 1 addition & 1 deletion include/pybind11/stl/filesystem.h
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ struct path_caster {
return true;
}

PYBIND11_TYPE_CASTER(T, io_name("typing.Union[os.PathLike, str, bytes]", "pathlib.Path"));
PYBIND11_TYPE_CASTER(T, io_name("os.PathLike | str | bytes", "pathlib.Path"));
};

#endif // PYBIND11_HAS_FILESYSTEM || defined(PYBIND11_HAS_EXPERIMENTAL_FILESYSTEM)
Expand Down
17 changes: 7 additions & 10 deletions include/pybind11/typing.h
Original file line number Diff line number Diff line change
Expand Up @@ -218,15 +218,12 @@ struct handle_type_name<typing::Type<T>> {

template <typename... Types>
struct handle_type_name<typing::Union<Types...>> {
static constexpr auto name = const_name("typing.Union[")
+ ::pybind11::detail::concat(make_caster<Types>::name...)
+ const_name("]");
static constexpr auto name = ::pybind11::detail::union_concat(make_caster<Types>::name...);
};

template <typename T>
struct handle_type_name<typing::Optional<T>> {
static constexpr auto name
= const_name("typing.Optional[") + make_caster<T>::name + const_name("]");
static constexpr auto name = make_caster<T>::name | make_caster<none>::name;
};

template <typename T>
Expand All @@ -244,14 +241,14 @@ struct handle_type_name<typing::ClassVar<T>> {

template <typename T>
struct handle_type_name<typing::TypeGuard<T>> {
static constexpr auto name
= const_name("typing.TypeGuard[") + make_caster<T>::name + const_name("]");
static constexpr auto name = const_name(PYBIND11_TYPE_GUARD_TYPE_HINT) + const_name("[")
+ make_caster<T>::name + const_name("]");
};

template <typename T>
struct handle_type_name<typing::TypeIs<T>> {
static constexpr auto name
= const_name("typing.TypeIs[") + make_caster<T>::name + const_name("]");
static constexpr auto name = const_name(PYBIND11_TYPE_IS_TYPE_HINT) + const_name("[")
+ make_caster<T>::name + const_name("]");
};

template <>
Expand All @@ -261,7 +258,7 @@ struct handle_type_name<typing::NoReturn> {

template <>
struct handle_type_name<typing::Never> {
static constexpr auto name = const_name("typing.Never");
static constexpr auto name = const_name(PYBIND11_NEVER_TYPE_HINT);
};

#if defined(PYBIND11_TYPING_H_HAS_STRING_LITERAL)
Expand Down
23 changes: 23 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import sysconfig
import textwrap
import traceback
from typing import Callable

import pytest

Expand Down Expand Up @@ -242,3 +243,25 @@ def pytest_report_header():
lines.append("free-threaded Python build")

return lines


@pytest.fixture
def backport_typehints() -> Callable[[SanitizedString], SanitizedString]:
d = {}
if sys.version_info < (3, 13):
d["typing_extensions.TypeIs"] = "typing.TypeIs"
d["typing_extensions.CapsuleType"] = "types.CapsuleType"
if sys.version_info < (3, 12):
d["typing_extensions.Buffer"] = "collections.abc.Buffer"
if sys.version_info < (3, 11):
d["typing_extensions.Never"] = "typing.Never"
if sys.version_info < (3, 10):
d["typing_extensions.TypeGuard"] = "typing.TypeGuard"

def backport(sanatized_string: SanitizedString) -> SanitizedString:
for old, new in d.items():
sanatized_string.string = sanatized_string.string.replace(old, new)

return sanatized_string

return backport
12 changes: 5 additions & 7 deletions tests/test_buffers.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import ctypes
import io
import struct
import sys

import pytest

Expand Down Expand Up @@ -228,12 +227,11 @@ def test_ctypes_from_buffer():
assert not cinfo.readonly


def test_buffer_docstring():
if sys.version_info >= (3, 12):
docstring = "get_buffer_info(arg0: collections.abc.Buffer) -> pybind11_tests.buffers.buffer_info"
else:
docstring = "get_buffer_info(arg0: typing_extensions.Buffer) -> pybind11_tests.buffers.buffer_info"
assert m.get_buffer_info.__doc__.strip() == docstring
def test_buffer_docstring(doc, backport_typehints):
assert (
backport_typehints(doc(m.get_buffer_info))
== "get_buffer_info(arg0: collections.abc.Buffer) -> m.buffers.buffer_info"
)


def test_buffer_exception():
Expand Down
13 changes: 7 additions & 6 deletions tests/test_opaque_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ def test_string_list():
assert m.print_opaque_list(cvp.stringList) == "Opaque list: [Element 1, Element 3]"


def test_pointers(msg):
def test_pointers(msg, backport_typehints):
living_before = ConstructorStats.get(UserType).alive()
assert m.get_void_ptr_value(m.return_void_ptr()) == 0x1234
assert m.get_void_ptr_value(UserType()) # Should also work for other C++ types
Expand All @@ -37,14 +37,15 @@ def test_pointers(msg):

with pytest.raises(TypeError) as excinfo:
m.get_void_ptr_value([1, 2, 3]) # This should not work

assert (
msg(excinfo.value)
backport_typehints(msg(excinfo.value))
== """
get_void_ptr_value(): incompatible function arguments. The following argument types are supported:
1. (arg0: types.CapsuleType) -> int
get_void_ptr_value(): incompatible function arguments. The following argument types are supported:
1. (arg0: types.CapsuleType) -> int

Invoked with: [1, 2, 3]
"""
Invoked with: [1, 2, 3]
"""
)

assert m.return_null_str() is None
Expand Down
2 changes: 1 addition & 1 deletion tests/test_pytypes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ namespace detail {

template <>
struct type_caster<RealNumber> {
PYBIND11_TYPE_CASTER(RealNumber, io_name("typing.Union[float, int]", "float"));
PYBIND11_TYPE_CASTER(RealNumber, io_name("float | int", "float"));

static handle cast(const RealNumber &number, return_value_policy, handle) {
return py::float_(number.value).release();
Expand Down
Loading