Skip to content

Commit c2b32b1

Browse files
fix(types): type hints from future python versions (#5693)
* fix future type hints Signed-off-by: Michael Carlstrom <[email protected]> * style: pre-commit fixes * remove unused var Signed-off-by: Michael Carlstrom <[email protected]> * remove union_helper Signed-off-by: Michael Carlstrom <[email protected]> * fix speelling error Signed-off-by: Michael Carlstrom <[email protected]> * base case for union_concat Signed-off-by: Michael Carlstrom <[email protected]> * style: pre-commit fixes * add case for one descr Signed-off-by: Michael Carlstrom <[email protected]> * weakref and final test Signed-off-by: Michael Carlstrom <[email protected]> * Add acrpss_version_type_hint_checker Signed-off-by: Michael Carlstrom <[email protected]> * cleanup Signed-off-by: Michael Carlstrom <[email protected]> * style: pre-commit fixes * remove test.pyi Signed-off-by: Michael Carlstrom <[email protected]> * use new unions and add fixture Signed-off-by: Michael Carlstrom <[email protected]> * timohl suggested cleanup Signed-off-by: Michael Carlstrom <[email protected]> * style: pre-commit fixes * add missing auto Signed-off-by: Michael Carlstrom <[email protected]> * style: pre-commit fixes * move operator| def --------- Signed-off-by: Michael Carlstrom <[email protected]> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
1 parent 21c9dd1 commit c2b32b1

File tree

12 files changed

+181
-109
lines changed

12 files changed

+181
-109
lines changed

include/pybind11/cast.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -404,7 +404,7 @@ class type_caster<void> : public type_caster<void_type> {
404404
template <typename T>
405405
using cast_op_type = void *&;
406406
explicit operator void *&() { return value; }
407-
static constexpr auto name = const_name("types.CapsuleType");
407+
static constexpr auto name = const_name(PYBIND11_CAPSULE_TYPE_TYPE_HINT);
408408

409409
private:
410410
void *value = nullptr;
@@ -1361,7 +1361,7 @@ struct handle_type_name<dict> {
13611361
};
13621362
template <>
13631363
struct handle_type_name<anyset> {
1364-
static constexpr auto name = const_name("typing.Union[set, frozenset]");
1364+
static constexpr auto name = const_name("set | frozenset");
13651365
};
13661366
template <>
13671367
struct handle_type_name<set> {
@@ -1441,15 +1441,15 @@ struct handle_type_name<type> {
14411441
};
14421442
template <>
14431443
struct handle_type_name<capsule> {
1444-
static constexpr auto name = const_name("types.CapsuleType");
1444+
static constexpr auto name = const_name(PYBIND11_CAPSULE_TYPE_TYPE_HINT);
14451445
};
14461446
template <>
14471447
struct handle_type_name<ellipsis> {
14481448
static constexpr auto name = const_name("ellipsis");
14491449
};
14501450
template <>
14511451
struct handle_type_name<weakref> {
1452-
static constexpr auto name = const_name("weakref");
1452+
static constexpr auto name = const_name("weakref.ReferenceType");
14531453
};
14541454
template <>
14551455
struct handle_type_name<args> {

include/pybind11/detail/common.h

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -265,13 +265,36 @@
265265
# endif
266266
#endif
267267

268+
// 3.13 Compatibility
269+
#if 0x030D0000 <= PY_VERSION_HEX
270+
# define PYBIND11_TYPE_IS_TYPE_HINT "typing.TypeIs"
271+
# define PYBIND11_CAPSULE_TYPE_TYPE_HINT "types.CapsuleType"
272+
#else
273+
# define PYBIND11_TYPE_IS_TYPE_HINT "typing_extensions.TypeIs"
274+
# define PYBIND11_CAPSULE_TYPE_TYPE_HINT "typing_extensions.CapsuleType"
275+
#endif
276+
268277
// 3.12 Compatibility
269278
#if 0x030C0000 <= PY_VERSION_HEX
270279
# define PYBIND11_BUFFER_TYPE_HINT "collections.abc.Buffer"
271280
#else
272281
# define PYBIND11_BUFFER_TYPE_HINT "typing_extensions.Buffer"
273282
#endif
274283

284+
// 3.11 Compatibility
285+
#if 0x030B0000 <= PY_VERSION_HEX
286+
# define PYBIND11_NEVER_TYPE_HINT "typing.Never"
287+
#else
288+
# define PYBIND11_NEVER_TYPE_HINT "typing_extensions.Never"
289+
#endif
290+
291+
// 3.10 Compatibility
292+
#if 0x030A0000 <= PY_VERSION_HEX
293+
# define PYBIND11_TYPE_GUARD_TYPE_HINT "typing.TypeGuard"
294+
#else
295+
# define PYBIND11_TYPE_GUARD_TYPE_HINT "typing_extensions.TypeGuard"
296+
#endif
297+
275298
// #define PYBIND11_STR_LEGACY_PERMISSIVE
276299
// If DEFINED, pybind11::str can hold PyUnicodeObject or PyBytesObject
277300
// (probably surprising and never documented, but this was the

include/pybind11/detail/descr.h

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,12 +157,24 @@ constexpr descr<1, Type> _() {
157157
#endif // #ifndef _
158158

159159
constexpr descr<0> concat() { return {}; }
160+
constexpr descr<0> union_concat() { return {}; }
160161

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

167+
template <size_t N, typename... Ts>
168+
constexpr descr<N, Ts...> union_concat(const descr<N, Ts...> &descr) {
169+
return descr;
170+
}
171+
172+
template <size_t N1, size_t N2, typename... Ts1, typename... Ts2>
173+
constexpr descr<N1 + N2 + 3, Ts1..., Ts2...> operator|(const descr<N1, Ts1...> &a,
174+
const descr<N2, Ts2...> &b) {
175+
return a + const_name(" | ") + b;
176+
}
177+
166178
#ifdef __cpp_fold_expressions
167179
template <size_t N1, size_t N2, typename... Ts1, typename... Ts2>
168180
constexpr descr<N1 + N2 + 2, Ts1..., Ts2...> operator,(const descr<N1, Ts1...> &a,
@@ -174,12 +186,25 @@ template <size_t N, typename... Ts, typename... Args>
174186
constexpr auto concat(const descr<N, Ts...> &d, const Args &...args) {
175187
return (d, ..., args);
176188
}
189+
190+
template <size_t N, typename... Ts, typename... Args>
191+
constexpr auto union_concat(const descr<N, Ts...> &d, const Args &...args) {
192+
return (d | ... | args);
193+
}
194+
177195
#else
178196
template <size_t N, typename... Ts, typename... Args>
179197
constexpr auto concat(const descr<N, Ts...> &d, const Args &...args)
180198
-> decltype(std::declval<descr<N + 2, Ts...>>() + concat(args...)) {
181199
return d + const_name(", ") + concat(args...);
182200
}
201+
202+
template <size_t N, typename... Ts, typename... Args>
203+
constexpr auto union_concat(const descr<N, Ts...> &d, const Args &...args)
204+
-> decltype(std::declval<descr<N + 3, Ts...>>() + union_concat(args...)) {
205+
return d + const_name(" | ") + union_concat(args...);
206+
}
207+
183208
#endif
184209

185210
template <size_t N, typename... Ts>

include/pybind11/stl.h

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -557,8 +557,7 @@ struct optional_caster {
557557
return true;
558558
}
559559

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

564563
#if defined(PYBIND11_HAS_OPTIONAL)
@@ -642,10 +641,7 @@ struct variant_caster<V<Ts...>> {
642641
}
643642

644643
using Type = V<Ts...>;
645-
PYBIND11_TYPE_CASTER(Type,
646-
const_name("typing.Union[")
647-
+ ::pybind11::detail::concat(make_caster<Ts>::name...)
648-
+ const_name("]"));
644+
PYBIND11_TYPE_CASTER(Type, ::pybind11::detail::union_concat(make_caster<Ts>::name...));
649645
};
650646

651647
#if defined(PYBIND11_HAS_VARIANT)

include/pybind11/stl/filesystem.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ struct path_caster {
9696
return true;
9797
}
9898

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

102102
#endif // PYBIND11_HAS_FILESYSTEM || defined(PYBIND11_HAS_EXPERIMENTAL_FILESYSTEM)

include/pybind11/typing.h

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -218,15 +218,12 @@ struct handle_type_name<typing::Type<T>> {
218218

219219
template <typename... Types>
220220
struct handle_type_name<typing::Union<Types...>> {
221-
static constexpr auto name = const_name("typing.Union[")
222-
+ ::pybind11::detail::concat(make_caster<Types>::name...)
223-
+ const_name("]");
221+
static constexpr auto name = ::pybind11::detail::union_concat(make_caster<Types>::name...);
224222
};
225223

226224
template <typename T>
227225
struct handle_type_name<typing::Optional<T>> {
228-
static constexpr auto name
229-
= const_name("typing.Optional[") + make_caster<T>::name + const_name("]");
226+
static constexpr auto name = make_caster<T>::name | make_caster<none>::name;
230227
};
231228

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

245242
template <typename T>
246243
struct handle_type_name<typing::TypeGuard<T>> {
247-
static constexpr auto name
248-
= const_name("typing.TypeGuard[") + make_caster<T>::name + const_name("]");
244+
static constexpr auto name = const_name(PYBIND11_TYPE_GUARD_TYPE_HINT) + const_name("[")
245+
+ make_caster<T>::name + const_name("]");
249246
};
250247

251248
template <typename T>
252249
struct handle_type_name<typing::TypeIs<T>> {
253-
static constexpr auto name
254-
= const_name("typing.TypeIs[") + make_caster<T>::name + const_name("]");
250+
static constexpr auto name = const_name(PYBIND11_TYPE_IS_TYPE_HINT) + const_name("[")
251+
+ make_caster<T>::name + const_name("]");
255252
};
256253

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

262259
template <>
263260
struct handle_type_name<typing::Never> {
264-
static constexpr auto name = const_name("typing.Never");
261+
static constexpr auto name = const_name(PYBIND11_NEVER_TYPE_HINT);
265262
};
266263

267264
#if defined(PYBIND11_TYPING_H_HAS_STRING_LITERAL)

tests/conftest.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import sysconfig
1717
import textwrap
1818
import traceback
19+
from typing import Callable
1920

2021
import pytest
2122

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

244245
return lines
246+
247+
248+
@pytest.fixture
249+
def backport_typehints() -> Callable[[SanitizedString], SanitizedString]:
250+
d = {}
251+
if sys.version_info < (3, 13):
252+
d["typing_extensions.TypeIs"] = "typing.TypeIs"
253+
d["typing_extensions.CapsuleType"] = "types.CapsuleType"
254+
if sys.version_info < (3, 12):
255+
d["typing_extensions.Buffer"] = "collections.abc.Buffer"
256+
if sys.version_info < (3, 11):
257+
d["typing_extensions.Never"] = "typing.Never"
258+
if sys.version_info < (3, 10):
259+
d["typing_extensions.TypeGuard"] = "typing.TypeGuard"
260+
261+
def backport(sanatized_string: SanitizedString) -> SanitizedString:
262+
for old, new in d.items():
263+
sanatized_string.string = sanatized_string.string.replace(old, new)
264+
265+
return sanatized_string
266+
267+
return backport

tests/test_buffers.py

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
import ctypes
44
import io
55
import struct
6-
import sys
76

87
import pytest
98

@@ -228,12 +227,11 @@ def test_ctypes_from_buffer():
228227
assert not cinfo.readonly
229228

230229

231-
def test_buffer_docstring():
232-
if sys.version_info >= (3, 12):
233-
docstring = "get_buffer_info(arg0: collections.abc.Buffer) -> pybind11_tests.buffers.buffer_info"
234-
else:
235-
docstring = "get_buffer_info(arg0: typing_extensions.Buffer) -> pybind11_tests.buffers.buffer_info"
236-
assert m.get_buffer_info.__doc__.strip() == docstring
230+
def test_buffer_docstring(doc, backport_typehints):
231+
assert (
232+
backport_typehints(doc(m.get_buffer_info))
233+
== "get_buffer_info(arg0: collections.abc.Buffer) -> m.buffers.buffer_info"
234+
)
237235

238236

239237
def test_buffer_exception():

tests/test_opaque_types.py

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ def test_string_list():
2727
assert m.print_opaque_list(cvp.stringList) == "Opaque list: [Element 1, Element 3]"
2828

2929

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

3838
with pytest.raises(TypeError) as excinfo:
3939
m.get_void_ptr_value([1, 2, 3]) # This should not work
40+
4041
assert (
41-
msg(excinfo.value)
42+
backport_typehints(msg(excinfo.value))
4243
== """
43-
get_void_ptr_value(): incompatible function arguments. The following argument types are supported:
44-
1. (arg0: types.CapsuleType) -> int
44+
get_void_ptr_value(): incompatible function arguments. The following argument types are supported:
45+
1. (arg0: types.CapsuleType) -> int
4546
46-
Invoked with: [1, 2, 3]
47-
"""
47+
Invoked with: [1, 2, 3]
48+
"""
4849
)
4950

5051
assert m.return_null_str() is None

tests/test_pytypes.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,7 @@ namespace detail {
155155

156156
template <>
157157
struct type_caster<RealNumber> {
158-
PYBIND11_TYPE_CASTER(RealNumber, io_name("typing.Union[float, int]", "float"));
158+
PYBIND11_TYPE_CASTER(RealNumber, io_name("float | int", "float"));
159159

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

0 commit comments

Comments
 (0)