mirror of
https://github.com/pybind/pybind11.git
synced 2025-01-30 23:02:37 +00:00
feat: rework of arg/return type hints to support .noconvert() (#5486)
* Added rework of arg/return typing * Changed `Path` to `pathlib.Path` for compatibility with pybind11-stubgen * Removed old arg/return type hint implementation * Added noconvert support for arg/return type hints * Added commented failing tests for Literals with special characters * Added return_descr/arg_descr for correct typing in typing::Callable * Fixed clang-tidy issues * Changed io_name to have explicit return type (for C++11 support) * style: pre-commit fixes * Added support for nested callables * Fixed missing include * Fixed is_return_value constructor call * Fixed clang-tidy issue * Uncommented test cases for special characters in literals * Moved literal tests to correct test case * Added escaping of special characters in typing::Literal * Readded mistakenly deleted bracket * Moved sanitize_string_literal to correct namespace * Added test for Literal with `!` and changed StringLiteral template param name * Added test for Literal with multiple and repeated special chars * Simplified string literal sanitization function * Added test for `->` in literal * Added test for `->` with io_name * Removed unused parameter name to prevent warning * Added escaping of `-` in literal to prevent processing of `->` * Fixed wrong computation of sanitized string literal length * Added cast to prevent error with MSVC * Simplified special character check --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
This commit is contained in:
parent
15d9dae14b
commit
1b7aa0bb66
@ -61,19 +61,16 @@ type is explicitly allowed.
|
|||||||
|
|
||||||
template <>
|
template <>
|
||||||
struct type_caster<user_space::Point2D> {
|
struct type_caster<user_space::Point2D> {
|
||||||
// This macro inserts a lot of boilerplate code and sets the default type hint to `tuple`
|
// This macro inserts a lot of boilerplate code and sets the type hint.
|
||||||
PYBIND11_TYPE_CASTER(user_space::Point2D, const_name("tuple"));
|
// `io_name` is used to specify different type hints for arguments and return values.
|
||||||
// `arg_name` and `return_name` may optionally be used to specify type hints separately for
|
|
||||||
// arguments and return values.
|
|
||||||
// The signature of our negate function would then look like:
|
// The signature of our negate function would then look like:
|
||||||
// `negate(Sequence[float]) -> tuple[float, float]`
|
// `negate(Sequence[float]) -> tuple[float, float]`
|
||||||
static constexpr auto arg_name = const_name("Sequence[float]");
|
PYBIND11_TYPE_CASTER(user_space::Point2D, io_name("Sequence[float]", "tuple[float, float]"));
|
||||||
static constexpr auto return_name = const_name("tuple[float, float]");
|
|
||||||
|
|
||||||
// C++ -> Python: convert `Point2D` to `tuple[float, float]`. The second and third arguments
|
// C++ -> Python: convert `Point2D` to `tuple[float, float]`. The second and third arguments
|
||||||
// are used to indicate the return value policy and parent object (for
|
// are used to indicate the return value policy and parent object (for
|
||||||
// return_value_policy::reference_internal) and are often ignored by custom casters.
|
// return_value_policy::reference_internal) and are often ignored by custom casters.
|
||||||
// The return value should reflect the type hint specified by `return_name`.
|
// The return value should reflect the type hint specified by the second argument of `io_name`.
|
||||||
static handle
|
static handle
|
||||||
cast(const user_space::Point2D &number, return_value_policy /*policy*/, handle /*parent*/) {
|
cast(const user_space::Point2D &number, return_value_policy /*policy*/, handle /*parent*/) {
|
||||||
return py::make_tuple(number.x, number.y).release();
|
return py::make_tuple(number.x, number.y).release();
|
||||||
@ -81,7 +78,8 @@ type is explicitly allowed.
|
|||||||
|
|
||||||
// Python -> C++: convert a `PyObject` into a `Point2D` and return false upon failure. The
|
// Python -> C++: convert a `PyObject` into a `Point2D` and return false upon failure. The
|
||||||
// second argument indicates whether implicit conversions should be allowed.
|
// second argument indicates whether implicit conversions should be allowed.
|
||||||
// The accepted types should reflect the type hint specified by `arg_name`.
|
// The accepted types should reflect the type hint specified by the first argument of
|
||||||
|
// `io_name`.
|
||||||
bool load(handle src, bool /*convert*/) {
|
bool load(handle src, bool /*convert*/) {
|
||||||
// Check if handle is a Sequence
|
// Check if handle is a Sequence
|
||||||
if (!py::isinstance<py::sequence>(src)) {
|
if (!py::isinstance<py::sequence>(src)) {
|
||||||
|
@ -34,39 +34,6 @@ PYBIND11_WARNING_DISABLE_MSVC(4127)
|
|||||||
|
|
||||||
PYBIND11_NAMESPACE_BEGIN(detail)
|
PYBIND11_NAMESPACE_BEGIN(detail)
|
||||||
|
|
||||||
// Type trait checker for `descr`
|
|
||||||
template <typename>
|
|
||||||
struct is_descr : std::false_type {};
|
|
||||||
|
|
||||||
template <size_t N, typename... Ts>
|
|
||||||
struct is_descr<descr<N, Ts...>> : std::true_type {};
|
|
||||||
|
|
||||||
template <size_t N, typename... Ts>
|
|
||||||
struct is_descr<const descr<N, Ts...>> : std::true_type {};
|
|
||||||
|
|
||||||
// Use arg_name instead of name when available
|
|
||||||
template <typename T, typename SFINAE = void>
|
|
||||||
struct as_arg_type {
|
|
||||||
static constexpr auto name = T::name;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
struct as_arg_type<T, typename std::enable_if<is_descr<decltype(T::arg_name)>::value>::type> {
|
|
||||||
static constexpr auto name = T::arg_name;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Use return_name instead of name when available
|
|
||||||
template <typename T, typename SFINAE = void>
|
|
||||||
struct as_return_type {
|
|
||||||
static constexpr auto name = T::name;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
struct as_return_type<T,
|
|
||||||
typename std::enable_if<is_descr<decltype(T::return_name)>::value>::type> {
|
|
||||||
static constexpr auto name = T::return_name;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename type, typename SFINAE = void>
|
template <typename type, typename SFINAE = void>
|
||||||
class type_caster : public type_caster_base<type> {};
|
class type_caster : public type_caster_base<type> {};
|
||||||
template <typename type>
|
template <typename type>
|
||||||
@ -1113,8 +1080,6 @@ struct pyobject_caster {
|
|||||||
return src.inc_ref();
|
return src.inc_ref();
|
||||||
}
|
}
|
||||||
PYBIND11_TYPE_CASTER(type, handle_type_name<type>::name);
|
PYBIND11_TYPE_CASTER(type, handle_type_name<type>::name);
|
||||||
static constexpr auto arg_name = as_arg_type<handle_type_name<type>>::name;
|
|
||||||
static constexpr auto return_name = as_return_type<handle_type_name<type>>::name;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
@ -1668,7 +1633,7 @@ public:
|
|||||||
"py::args cannot be specified more than once");
|
"py::args cannot be specified more than once");
|
||||||
|
|
||||||
static constexpr auto arg_names
|
static constexpr auto arg_names
|
||||||
= ::pybind11::detail::concat(type_descr(as_arg_type<make_caster<Args>>::name)...);
|
= ::pybind11::detail::concat(type_descr(make_caster<Args>::name)...);
|
||||||
|
|
||||||
bool load_args(function_call &call) { return load_impl_sequence(call, indices{}); }
|
bool load_args(function_call &call) { return load_impl_sequence(call, indices{}); }
|
||||||
|
|
||||||
|
@ -99,6 +99,13 @@ constexpr descr<1, Type> const_name() {
|
|||||||
return {'%'};
|
return {'%'};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Use a different name based on whether the parameter is used as input or output
|
||||||
|
template <size_t N1, size_t N2>
|
||||||
|
constexpr descr<N1 + N2 + 1> io_name(char const (&text1)[N1], char const (&text2)[N2]) {
|
||||||
|
return const_name("@") + const_name(text1) + const_name("@") + const_name(text2)
|
||||||
|
+ const_name("@");
|
||||||
|
}
|
||||||
|
|
||||||
// If "_" is defined as a macro, py::detail::_ cannot be provided.
|
// If "_" is defined as a macro, py::detail::_ cannot be provided.
|
||||||
// It is therefore best to use py::detail::const_name universally.
|
// It is therefore best to use py::detail::const_name universally.
|
||||||
// This block is for backward compatibility only.
|
// This block is for backward compatibility only.
|
||||||
@ -167,5 +174,15 @@ constexpr descr<N + 2, Ts...> type_descr(const descr<N, Ts...> &descr) {
|
|||||||
return const_name("{") + descr + const_name("}");
|
return const_name("{") + descr + const_name("}");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <size_t N, typename... Ts>
|
||||||
|
constexpr descr<N + 4, Ts...> arg_descr(const descr<N, Ts...> &descr) {
|
||||||
|
return const_name("@^") + descr + const_name("@!");
|
||||||
|
}
|
||||||
|
|
||||||
|
template <size_t N, typename... Ts>
|
||||||
|
constexpr descr<N + 4, Ts...> return_descr(const descr<N, Ts...> &descr) {
|
||||||
|
return const_name("@$") + descr + const_name("@!");
|
||||||
|
}
|
||||||
|
|
||||||
PYBIND11_NAMESPACE_END(detail)
|
PYBIND11_NAMESPACE_END(detail)
|
||||||
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)
|
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)
|
||||||
|
@ -22,6 +22,7 @@
|
|||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <new>
|
#include <new>
|
||||||
|
#include <stack>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
@ -336,8 +337,8 @@ protected:
|
|||||||
|
|
||||||
/* Generate a readable signature describing the function's arguments and return
|
/* Generate a readable signature describing the function's arguments and return
|
||||||
value types */
|
value types */
|
||||||
static constexpr auto signature = const_name("(") + cast_in::arg_names
|
static constexpr auto signature
|
||||||
+ const_name(") -> ") + as_return_type<cast_out>::name;
|
= const_name("(") + cast_in::arg_names + const_name(") -> ") + cast_out::name;
|
||||||
PYBIND11_DESCR_CONSTEXPR auto types = decltype(signature)::types();
|
PYBIND11_DESCR_CONSTEXPR auto types = decltype(signature)::types();
|
||||||
|
|
||||||
/* Register the function with Python from generic (non-templated) code */
|
/* Register the function with Python from generic (non-templated) code */
|
||||||
@ -440,6 +441,13 @@ protected:
|
|||||||
std::string signature;
|
std::string signature;
|
||||||
size_t type_index = 0, arg_index = 0;
|
size_t type_index = 0, arg_index = 0;
|
||||||
bool is_starred = false;
|
bool is_starred = false;
|
||||||
|
// `is_return_value.top()` is true if we are currently inside the return type of the
|
||||||
|
// signature. Using `@^`/`@$` we can force types to be arg/return types while `@!` pops
|
||||||
|
// back to the previous state.
|
||||||
|
std::stack<bool> is_return_value({false});
|
||||||
|
// The following characters have special meaning in the signature parsing. Literals
|
||||||
|
// containing these are escaped with `!`.
|
||||||
|
std::string special_chars("!@%{}-");
|
||||||
for (const auto *pc = text; *pc != '\0'; ++pc) {
|
for (const auto *pc = text; *pc != '\0'; ++pc) {
|
||||||
const auto c = *pc;
|
const auto c = *pc;
|
||||||
|
|
||||||
@ -493,7 +501,57 @@ protected:
|
|||||||
} else {
|
} else {
|
||||||
signature += detail::quote_cpp_type_name(detail::clean_type_id(t->name()));
|
signature += detail::quote_cpp_type_name(detail::clean_type_id(t->name()));
|
||||||
}
|
}
|
||||||
|
} else if (c == '!' && special_chars.find(*(pc + 1)) != std::string::npos) {
|
||||||
|
// typing::Literal escapes special characters with !
|
||||||
|
signature += *++pc;
|
||||||
|
} else if (c == '@') {
|
||||||
|
// `@^ ... @!` and `@$ ... @!` are used to force arg/return value type (see
|
||||||
|
// typing::Callable/detail::arg_descr/detail::return_descr)
|
||||||
|
if (*(pc + 1) == '^') {
|
||||||
|
is_return_value.emplace(false);
|
||||||
|
++pc;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (*(pc + 1) == '$') {
|
||||||
|
is_return_value.emplace(true);
|
||||||
|
++pc;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (*(pc + 1) == '!') {
|
||||||
|
is_return_value.pop();
|
||||||
|
++pc;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// Handle types that differ depending on whether they appear
|
||||||
|
// in an argument or a return value position (see io_name<text1, text2>).
|
||||||
|
// For named arguments (py::arg()) with noconvert set, return value type is used.
|
||||||
|
++pc;
|
||||||
|
if (!is_return_value.top()
|
||||||
|
&& !(arg_index < rec->args.size() && !rec->args[arg_index].convert)) {
|
||||||
|
while (*pc != '\0' && *pc != '@') {
|
||||||
|
signature += *pc++;
|
||||||
|
}
|
||||||
|
if (*pc == '@') {
|
||||||
|
++pc;
|
||||||
|
}
|
||||||
|
while (*pc != '\0' && *pc != '@') {
|
||||||
|
++pc;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
|
while (*pc != '\0' && *pc != '@') {
|
||||||
|
++pc;
|
||||||
|
}
|
||||||
|
if (*pc == '@') {
|
||||||
|
++pc;
|
||||||
|
}
|
||||||
|
while (*pc != '\0' && *pc != '@') {
|
||||||
|
signature += *pc++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (c == '-' && *(pc + 1) == '>') {
|
||||||
|
is_return_value.emplace(true);
|
||||||
|
}
|
||||||
signature += c;
|
signature += c;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -106,9 +106,7 @@ public:
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
PYBIND11_TYPE_CASTER(T, const_name("os.PathLike"));
|
PYBIND11_TYPE_CASTER(T, io_name("Union[os.PathLike, str, bytes]", "pathlib.Path"));
|
||||||
static constexpr auto arg_name = const_name("Union[os.PathLike, str, bytes]");
|
|
||||||
static constexpr auto return_name = const_name("Path");
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // PYBIND11_HAS_FILESYSTEM || defined(PYBIND11_HAS_EXPERIMENTAL_FILESYSTEM)
|
#endif // PYBIND11_HAS_FILESYSTEM || defined(PYBIND11_HAS_EXPERIMENTAL_FILESYSTEM)
|
||||||
|
@ -16,6 +16,13 @@
|
|||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
|
#if defined(__cpp_nontype_template_args) && __cpp_nontype_template_args >= 201911L
|
||||||
|
# define PYBIND11_TYPING_H_HAS_STRING_LITERAL
|
||||||
|
# include <numeric>
|
||||||
|
# include <ranges>
|
||||||
|
# include <string_view>
|
||||||
|
#endif
|
||||||
|
|
||||||
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
|
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
|
||||||
PYBIND11_NAMESPACE_BEGIN(typing)
|
PYBIND11_NAMESPACE_BEGIN(typing)
|
||||||
|
|
||||||
@ -112,8 +119,7 @@ class Never : public none {
|
|||||||
using none::none;
|
using none::none;
|
||||||
};
|
};
|
||||||
|
|
||||||
#if defined(__cpp_nontype_template_args) && __cpp_nontype_template_args >= 201911L
|
#if defined(PYBIND11_TYPING_H_HAS_STRING_LITERAL)
|
||||||
# define PYBIND11_TYPING_H_HAS_STRING_LITERAL
|
|
||||||
template <size_t N>
|
template <size_t N>
|
||||||
struct StringLiteral {
|
struct StringLiteral {
|
||||||
constexpr StringLiteral(const char (&str)[N]) { std::copy_n(str, N, name); }
|
constexpr StringLiteral(const char (&str)[N]) { std::copy_n(str, N, name); }
|
||||||
@ -143,13 +149,6 @@ struct handle_type_name<typing::Tuple<Types...>> {
|
|||||||
static constexpr auto name = const_name("tuple[")
|
static constexpr auto name = const_name("tuple[")
|
||||||
+ ::pybind11::detail::concat(make_caster<Types>::name...)
|
+ ::pybind11::detail::concat(make_caster<Types>::name...)
|
||||||
+ const_name("]");
|
+ const_name("]");
|
||||||
static constexpr auto arg_name
|
|
||||||
= const_name("tuple[")
|
|
||||||
+ ::pybind11::detail::concat(as_arg_type<make_caster<Types>>::name...) + const_name("]");
|
|
||||||
static constexpr auto return_name
|
|
||||||
= const_name("tuple[")
|
|
||||||
+ ::pybind11::detail::concat(as_return_type<make_caster<Types>>::name...)
|
|
||||||
+ const_name("]");
|
|
||||||
};
|
};
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
@ -163,58 +162,32 @@ struct handle_type_name<typing::Tuple<T, ellipsis>> {
|
|||||||
// PEP 484 specifies this syntax for a variable-length tuple
|
// PEP 484 specifies this syntax for a variable-length tuple
|
||||||
static constexpr auto name
|
static constexpr auto name
|
||||||
= const_name("tuple[") + make_caster<T>::name + const_name(", ...]");
|
= const_name("tuple[") + make_caster<T>::name + const_name(", ...]");
|
||||||
static constexpr auto arg_name
|
|
||||||
= const_name("tuple[") + as_arg_type<make_caster<T>>::name + const_name(", ...]");
|
|
||||||
static constexpr auto return_name
|
|
||||||
= const_name("tuple[") + as_return_type<make_caster<T>>::name + const_name(", ...]");
|
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename K, typename V>
|
template <typename K, typename V>
|
||||||
struct handle_type_name<typing::Dict<K, V>> {
|
struct handle_type_name<typing::Dict<K, V>> {
|
||||||
static constexpr auto name = const_name("dict[") + make_caster<K>::name + const_name(", ")
|
static constexpr auto name = const_name("dict[") + make_caster<K>::name + const_name(", ")
|
||||||
+ make_caster<V>::name + const_name("]");
|
+ make_caster<V>::name + const_name("]");
|
||||||
static constexpr auto arg_name = const_name("dict[") + as_arg_type<make_caster<K>>::name
|
|
||||||
+ const_name(", ") + as_arg_type<make_caster<V>>::name
|
|
||||||
+ const_name("]");
|
|
||||||
static constexpr auto return_name = const_name("dict[") + as_return_type<make_caster<K>>::name
|
|
||||||
+ const_name(", ") + as_return_type<make_caster<V>>::name
|
|
||||||
+ const_name("]");
|
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
struct handle_type_name<typing::List<T>> {
|
struct handle_type_name<typing::List<T>> {
|
||||||
static constexpr auto name = const_name("list[") + make_caster<T>::name + const_name("]");
|
static constexpr auto name = const_name("list[") + make_caster<T>::name + const_name("]");
|
||||||
static constexpr auto arg_name
|
|
||||||
= const_name("list[") + as_arg_type<make_caster<T>>::name + const_name("]");
|
|
||||||
static constexpr auto return_name
|
|
||||||
= const_name("list[") + as_return_type<make_caster<T>>::name + const_name("]");
|
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
struct handle_type_name<typing::Set<T>> {
|
struct handle_type_name<typing::Set<T>> {
|
||||||
static constexpr auto name = const_name("set[") + make_caster<T>::name + const_name("]");
|
static constexpr auto name = const_name("set[") + make_caster<T>::name + const_name("]");
|
||||||
static constexpr auto arg_name
|
|
||||||
= const_name("set[") + as_arg_type<make_caster<T>>::name + const_name("]");
|
|
||||||
static constexpr auto return_name
|
|
||||||
= const_name("set[") + as_return_type<make_caster<T>>::name + const_name("]");
|
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
struct handle_type_name<typing::Iterable<T>> {
|
struct handle_type_name<typing::Iterable<T>> {
|
||||||
static constexpr auto name = const_name("Iterable[") + make_caster<T>::name + const_name("]");
|
static constexpr auto name = const_name("Iterable[") + make_caster<T>::name + const_name("]");
|
||||||
static constexpr auto arg_name
|
|
||||||
= const_name("Iterable[") + as_arg_type<make_caster<T>>::name + const_name("]");
|
|
||||||
static constexpr auto return_name
|
|
||||||
= const_name("Iterable[") + as_return_type<make_caster<T>>::name + const_name("]");
|
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
struct handle_type_name<typing::Iterator<T>> {
|
struct handle_type_name<typing::Iterator<T>> {
|
||||||
static constexpr auto name = const_name("Iterator[") + make_caster<T>::name + const_name("]");
|
static constexpr auto name = const_name("Iterator[") + make_caster<T>::name + const_name("]");
|
||||||
static constexpr auto arg_name
|
|
||||||
= const_name("Iterator[") + as_arg_type<make_caster<T>>::name + const_name("]");
|
|
||||||
static constexpr auto return_name
|
|
||||||
= const_name("Iterator[") + as_return_type<make_caster<T>>::name + const_name("]");
|
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename Return, typename... Args>
|
template <typename Return, typename... Args>
|
||||||
@ -222,8 +195,9 @@ struct handle_type_name<typing::Callable<Return(Args...)>> {
|
|||||||
using retval_type = conditional_t<std::is_same<Return, void>::value, void_type, Return>;
|
using retval_type = conditional_t<std::is_same<Return, void>::value, void_type, Return>;
|
||||||
static constexpr auto name
|
static constexpr auto name
|
||||||
= const_name("Callable[[")
|
= const_name("Callable[[")
|
||||||
+ ::pybind11::detail::concat(as_arg_type<make_caster<Args>>::name...) + const_name("], ")
|
+ ::pybind11::detail::concat(::pybind11::detail::arg_descr(make_caster<Args>::name)...)
|
||||||
+ as_return_type<make_caster<retval_type>>::name + const_name("]");
|
+ const_name("], ") + ::pybind11::detail::return_descr(make_caster<retval_type>::name)
|
||||||
|
+ const_name("]");
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename Return>
|
template <typename Return>
|
||||||
@ -231,7 +205,7 @@ struct handle_type_name<typing::Callable<Return(ellipsis)>> {
|
|||||||
// PEP 484 specifies this syntax for defining only return types of callables
|
// PEP 484 specifies this syntax for defining only return types of callables
|
||||||
using retval_type = conditional_t<std::is_same<Return, void>::value, void_type, Return>;
|
using retval_type = conditional_t<std::is_same<Return, void>::value, void_type, Return>;
|
||||||
static constexpr auto name = const_name("Callable[..., ")
|
static constexpr auto name = const_name("Callable[..., ")
|
||||||
+ as_return_type<make_caster<retval_type>>::name
|
+ ::pybind11::detail::return_descr(make_caster<retval_type>::name)
|
||||||
+ const_name("]");
|
+ const_name("]");
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -245,22 +219,11 @@ struct handle_type_name<typing::Union<Types...>> {
|
|||||||
static constexpr auto name = const_name("Union[")
|
static constexpr auto name = const_name("Union[")
|
||||||
+ ::pybind11::detail::concat(make_caster<Types>::name...)
|
+ ::pybind11::detail::concat(make_caster<Types>::name...)
|
||||||
+ const_name("]");
|
+ const_name("]");
|
||||||
static constexpr auto arg_name
|
|
||||||
= const_name("Union[")
|
|
||||||
+ ::pybind11::detail::concat(as_arg_type<make_caster<Types>>::name...) + const_name("]");
|
|
||||||
static constexpr auto return_name
|
|
||||||
= const_name("Union[")
|
|
||||||
+ ::pybind11::detail::concat(as_return_type<make_caster<Types>>::name...)
|
|
||||||
+ const_name("]");
|
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
struct handle_type_name<typing::Optional<T>> {
|
struct handle_type_name<typing::Optional<T>> {
|
||||||
static constexpr auto name = const_name("Optional[") + make_caster<T>::name + const_name("]");
|
static constexpr auto name = const_name("Optional[") + make_caster<T>::name + const_name("]");
|
||||||
static constexpr auto arg_name
|
|
||||||
= const_name("Optional[") + as_arg_type<make_caster<T>>::name + const_name("]");
|
|
||||||
static constexpr auto return_name
|
|
||||||
= const_name("Optional[") + as_return_type<make_caster<T>>::name + const_name("]");
|
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
@ -273,19 +236,14 @@ struct handle_type_name<typing::ClassVar<T>> {
|
|||||||
static constexpr auto name = const_name("ClassVar[") + make_caster<T>::name + const_name("]");
|
static constexpr auto name = const_name("ClassVar[") + make_caster<T>::name + const_name("]");
|
||||||
};
|
};
|
||||||
|
|
||||||
// TypeGuard and TypeIs use as_return_type to use the return type if available, which is usually
|
|
||||||
// the narrower type.
|
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
struct handle_type_name<typing::TypeGuard<T>> {
|
struct handle_type_name<typing::TypeGuard<T>> {
|
||||||
static constexpr auto name
|
static constexpr auto name = const_name("TypeGuard[") + make_caster<T>::name + const_name("]");
|
||||||
= const_name("TypeGuard[") + as_return_type<make_caster<T>>::name + const_name("]");
|
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
struct handle_type_name<typing::TypeIs<T>> {
|
struct handle_type_name<typing::TypeIs<T>> {
|
||||||
static constexpr auto name
|
static constexpr auto name = const_name("TypeIs[") + make_caster<T>::name + const_name("]");
|
||||||
= const_name("TypeIs[") + as_return_type<make_caster<T>>::name + const_name("]");
|
|
||||||
};
|
};
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
@ -299,15 +257,35 @@ struct handle_type_name<typing::Never> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
#if defined(PYBIND11_TYPING_H_HAS_STRING_LITERAL)
|
#if defined(PYBIND11_TYPING_H_HAS_STRING_LITERAL)
|
||||||
|
template <typing::StringLiteral StrLit>
|
||||||
|
consteval auto sanitize_string_literal() {
|
||||||
|
constexpr std::string_view v(StrLit.name);
|
||||||
|
constexpr std::string_view special_chars("!@%{}-");
|
||||||
|
constexpr auto num_special_chars = std::accumulate(
|
||||||
|
special_chars.begin(), special_chars.end(), (size_t) 0, [&v](auto acc, const char &c) {
|
||||||
|
return std::move(acc) + std::ranges::count(v, c);
|
||||||
|
});
|
||||||
|
char result[v.size() + num_special_chars + 1];
|
||||||
|
size_t i = 0;
|
||||||
|
for (auto c : StrLit.name) {
|
||||||
|
if (special_chars.find(c) != std::string_view::npos) {
|
||||||
|
result[i++] = '!';
|
||||||
|
}
|
||||||
|
result[i++] = c;
|
||||||
|
}
|
||||||
|
return typing::StringLiteral(result);
|
||||||
|
}
|
||||||
|
|
||||||
template <typing::StringLiteral... Literals>
|
template <typing::StringLiteral... Literals>
|
||||||
struct handle_type_name<typing::Literal<Literals...>> {
|
struct handle_type_name<typing::Literal<Literals...>> {
|
||||||
static constexpr auto name = const_name("Literal[")
|
static constexpr auto name
|
||||||
+ pybind11::detail::concat(const_name(Literals.name)...)
|
= const_name("Literal[")
|
||||||
|
+ pybind11::detail::concat(const_name(sanitize_string_literal<Literals>().name)...)
|
||||||
+ const_name("]");
|
+ const_name("]");
|
||||||
};
|
};
|
||||||
template <typing::StringLiteral StrLit>
|
template <typing::StringLiteral StrLit>
|
||||||
struct handle_type_name<typing::TypeVar<StrLit>> {
|
struct handle_type_name<typing::TypeVar<StrLit>> {
|
||||||
static constexpr auto name = const_name(StrLit.name);
|
static constexpr auto name = const_name(sanitize_string_literal<StrLit>().name);
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -20,19 +20,16 @@ namespace detail {
|
|||||||
|
|
||||||
template <>
|
template <>
|
||||||
struct type_caster<user_space::Point2D> {
|
struct type_caster<user_space::Point2D> {
|
||||||
// This macro inserts a lot of boilerplate code and sets the default type hint to `tuple`
|
// This macro inserts a lot of boilerplate code and sets the type hint.
|
||||||
PYBIND11_TYPE_CASTER(user_space::Point2D, const_name("tuple"));
|
// `io_name` is used to specify different type hints for arguments and return values.
|
||||||
// `arg_name` and `return_name` may optionally be used to specify type hints separately for
|
|
||||||
// arguments and return values.
|
|
||||||
// The signature of our negate function would then look like:
|
// The signature of our negate function would then look like:
|
||||||
// `negate(Sequence[float]) -> tuple[float, float]`
|
// `negate(Sequence[float]) -> tuple[float, float]`
|
||||||
static constexpr auto arg_name = const_name("Sequence[float]");
|
PYBIND11_TYPE_CASTER(user_space::Point2D, io_name("Sequence[float]", "tuple[float, float]"));
|
||||||
static constexpr auto return_name = const_name("tuple[float, float]");
|
|
||||||
|
|
||||||
// C++ -> Python: convert `Point2D` to `tuple[float, float]`. The second and third arguments
|
// C++ -> Python: convert `Point2D` to `tuple[float, float]`. The second and third arguments
|
||||||
// are used to indicate the return value policy and parent object (for
|
// are used to indicate the return value policy and parent object (for
|
||||||
// return_value_policy::reference_internal) and are often ignored by custom casters.
|
// return_value_policy::reference_internal) and are often ignored by custom casters.
|
||||||
// The return value should reflect the type hint specified by `return_name`.
|
// The return value should reflect the type hint specified by the second argument of `io_name`.
|
||||||
static handle
|
static handle
|
||||||
cast(const user_space::Point2D &number, return_value_policy /*policy*/, handle /*parent*/) {
|
cast(const user_space::Point2D &number, return_value_policy /*policy*/, handle /*parent*/) {
|
||||||
return py::make_tuple(number.x, number.y).release();
|
return py::make_tuple(number.x, number.y).release();
|
||||||
@ -40,7 +37,8 @@ struct type_caster<user_space::Point2D> {
|
|||||||
|
|
||||||
// Python -> C++: convert a `PyObject` into a `Point2D` and return false upon failure. The
|
// Python -> C++: convert a `PyObject` into a `Point2D` and return false upon failure. The
|
||||||
// second argument indicates whether implicit conversions should be allowed.
|
// second argument indicates whether implicit conversions should be allowed.
|
||||||
// The accepted types should reflect the type hint specified by `arg_name`.
|
// The accepted types should reflect the type hint specified by the first argument of
|
||||||
|
// `io_name`.
|
||||||
bool load(handle src, bool /*convert*/) {
|
bool load(handle src, bool /*convert*/) {
|
||||||
// Check if handle is a Sequence
|
// Check if handle is a Sequence
|
||||||
if (!py::isinstance<py::sequence>(src)) {
|
if (!py::isinstance<py::sequence>(src)) {
|
||||||
|
@ -142,7 +142,6 @@ typedef py::typing::TypeVar<"V"> TypeVarV;
|
|||||||
// RealNumber:
|
// RealNumber:
|
||||||
// * in arguments -> float | int
|
// * in arguments -> float | int
|
||||||
// * in return -> float
|
// * in return -> float
|
||||||
// * fallback -> complex
|
|
||||||
// The choice of types is not really useful, but just made different for testing purposes.
|
// The choice of types is not really useful, but just made different for testing purposes.
|
||||||
// According to `PEP 484 – Type Hints` annotating with `float` also allows `int`,
|
// According to `PEP 484 – Type Hints` annotating with `float` also allows `int`,
|
||||||
// so using `float | int` could be replaced by just `float`.
|
// so using `float | int` could be replaced by just `float`.
|
||||||
@ -156,15 +155,17 @@ namespace detail {
|
|||||||
|
|
||||||
template <>
|
template <>
|
||||||
struct type_caster<RealNumber> {
|
struct type_caster<RealNumber> {
|
||||||
PYBIND11_TYPE_CASTER(RealNumber, const_name("complex"));
|
PYBIND11_TYPE_CASTER(RealNumber, io_name("Union[float, int]", "float"));
|
||||||
static constexpr auto arg_name = const_name("Union[float, int]");
|
|
||||||
static constexpr auto return_name = const_name("float");
|
|
||||||
|
|
||||||
static handle cast(const RealNumber &number, return_value_policy, handle) {
|
static handle cast(const RealNumber &number, return_value_policy, handle) {
|
||||||
return py::float_(number.value).release();
|
return py::float_(number.value).release();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool load(handle src, bool) {
|
bool load(handle src, bool convert) {
|
||||||
|
// If we're in no-convert mode, only load if given a float
|
||||||
|
if (!convert && !py::isinstance<py::float_>(src)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
if (!py::isinstance<py::float_>(src) && !py::isinstance<py::int_>(src)) {
|
if (!py::isinstance<py::float_>(src) && !py::isinstance<py::int_>(src)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -970,6 +971,19 @@ TEST_SUBMODULE(pytypes, m) {
|
|||||||
.value("BLUE", literals::Color::BLUE);
|
.value("BLUE", literals::Color::BLUE);
|
||||||
|
|
||||||
m.def("annotate_literal", [](literals::LiteralFoo &o) -> py::object { return o; });
|
m.def("annotate_literal", [](literals::LiteralFoo &o) -> py::object { return o; });
|
||||||
|
// Literal with `@`, `%`, `{`, `}`, and `->`
|
||||||
|
m.def("identity_literal_exclamation", [](const py::typing::Literal<"\"!\""> &x) { return x; });
|
||||||
|
m.def("identity_literal_at", [](const py::typing::Literal<"\"@\""> &x) { return x; });
|
||||||
|
m.def("identity_literal_percent", [](const py::typing::Literal<"\"%\""> &x) { return x; });
|
||||||
|
m.def("identity_literal_curly_open", [](const py::typing::Literal<"\"{\""> &x) { return x; });
|
||||||
|
m.def("identity_literal_curly_close", [](const py::typing::Literal<"\"}\""> &x) { return x; });
|
||||||
|
m.def("identity_literal_arrow_with_io_name",
|
||||||
|
[](const py::typing::Literal<"\"->\""> &x, const RealNumber &) { return x; });
|
||||||
|
m.def("identity_literal_arrow_with_callable",
|
||||||
|
[](const py::typing::Callable<RealNumber(const py::typing::Literal<"\"->\""> &,
|
||||||
|
const RealNumber &)> &x) { return x; });
|
||||||
|
m.def("identity_literal_all_special_chars",
|
||||||
|
[](const py::typing::Literal<"\"!@!!->{%}\""> &x) { return x; });
|
||||||
m.def("annotate_generic_containers",
|
m.def("annotate_generic_containers",
|
||||||
[](const py::typing::List<typevar::TypeVarT> &l) -> py::typing::List<typevar::TypeVarV> {
|
[](const py::typing::List<typevar::TypeVarT> &l) -> py::typing::List<typevar::TypeVarV> {
|
||||||
return l;
|
return l;
|
||||||
@ -1070,6 +1084,14 @@ TEST_SUBMODULE(pytypes, m) {
|
|||||||
m.attr("defined___cpp_inline_variables") = false;
|
m.attr("defined___cpp_inline_variables") = false;
|
||||||
#endif
|
#endif
|
||||||
m.def("half_of_number", [](const RealNumber &x) { return RealNumber{x.value / 2}; });
|
m.def("half_of_number", [](const RealNumber &x) { return RealNumber{x.value / 2}; });
|
||||||
|
m.def(
|
||||||
|
"half_of_number_convert",
|
||||||
|
[](const RealNumber &x) { return RealNumber{x.value / 2}; },
|
||||||
|
py::arg("x"));
|
||||||
|
m.def(
|
||||||
|
"half_of_number_noconvert",
|
||||||
|
[](const RealNumber &x) { return RealNumber{x.value / 2}; },
|
||||||
|
py::arg("x").noconvert());
|
||||||
// std::vector<T>
|
// std::vector<T>
|
||||||
m.def("half_of_number_vector", [](const std::vector<RealNumber> &x) {
|
m.def("half_of_number_vector", [](const std::vector<RealNumber> &x) {
|
||||||
std::vector<RealNumber> result;
|
std::vector<RealNumber> result;
|
||||||
@ -1130,6 +1152,16 @@ TEST_SUBMODULE(pytypes, m) {
|
|||||||
m.def("identity_iterable", [](const py::typing::Iterable<RealNumber> &x) { return x; });
|
m.def("identity_iterable", [](const py::typing::Iterable<RealNumber> &x) { return x; });
|
||||||
// Iterator<T>
|
// Iterator<T>
|
||||||
m.def("identity_iterator", [](const py::typing::Iterator<RealNumber> &x) { return x; });
|
m.def("identity_iterator", [](const py::typing::Iterator<RealNumber> &x) { return x; });
|
||||||
|
// Callable<R(A)> identity
|
||||||
|
m.def("identity_callable",
|
||||||
|
[](const py::typing::Callable<RealNumber(const RealNumber &)> &x) { return x; });
|
||||||
|
// Callable<R(...)> identity
|
||||||
|
m.def("identity_callable_ellipsis",
|
||||||
|
[](const py::typing::Callable<RealNumber(py::ellipsis)> &x) { return x; });
|
||||||
|
// Nested Callable<R(A)> identity
|
||||||
|
m.def("identity_nested_callable",
|
||||||
|
[](const py::typing::Callable<py::typing::Callable<RealNumber(const RealNumber &)>(
|
||||||
|
py::typing::Callable<RealNumber(const RealNumber &)>)> &x) { return x; });
|
||||||
// Callable<R(A)>
|
// Callable<R(A)>
|
||||||
m.def("apply_callable",
|
m.def("apply_callable",
|
||||||
[](const RealNumber &x, const py::typing::Callable<RealNumber(const RealNumber &)> &f) {
|
[](const RealNumber &x, const py::typing::Callable<RealNumber(const RealNumber &)> &f) {
|
||||||
|
@ -1044,6 +1044,39 @@ def test_literal(doc):
|
|||||||
doc(m.annotate_literal)
|
doc(m.annotate_literal)
|
||||||
== 'annotate_literal(arg0: Literal[26, 0x1A, "hello world", b"hello world", u"hello world", True, Color.RED, None]) -> object'
|
== 'annotate_literal(arg0: Literal[26, 0x1A, "hello world", b"hello world", u"hello world", True, Color.RED, None]) -> object'
|
||||||
)
|
)
|
||||||
|
# The characters !, @, %, {, } and -> are used in the signature parser as special characters, but Literal should escape those for the parser to work.
|
||||||
|
assert (
|
||||||
|
doc(m.identity_literal_exclamation)
|
||||||
|
== 'identity_literal_exclamation(arg0: Literal["!"]) -> Literal["!"]'
|
||||||
|
)
|
||||||
|
assert (
|
||||||
|
doc(m.identity_literal_at)
|
||||||
|
== 'identity_literal_at(arg0: Literal["@"]) -> Literal["@"]'
|
||||||
|
)
|
||||||
|
assert (
|
||||||
|
doc(m.identity_literal_percent)
|
||||||
|
== 'identity_literal_percent(arg0: Literal["%"]) -> Literal["%"]'
|
||||||
|
)
|
||||||
|
assert (
|
||||||
|
doc(m.identity_literal_curly_open)
|
||||||
|
== 'identity_literal_curly_open(arg0: Literal["{"]) -> Literal["{"]'
|
||||||
|
)
|
||||||
|
assert (
|
||||||
|
doc(m.identity_literal_curly_close)
|
||||||
|
== 'identity_literal_curly_close(arg0: Literal["}"]) -> Literal["}"]'
|
||||||
|
)
|
||||||
|
assert (
|
||||||
|
doc(m.identity_literal_arrow_with_io_name)
|
||||||
|
== 'identity_literal_arrow_with_io_name(arg0: Literal["->"], arg1: Union[float, int]) -> Literal["->"]'
|
||||||
|
)
|
||||||
|
assert (
|
||||||
|
doc(m.identity_literal_arrow_with_callable)
|
||||||
|
== 'identity_literal_arrow_with_callable(arg0: Callable[[Literal["->"], Union[float, int]], float]) -> Callable[[Literal["->"], Union[float, int]], float]'
|
||||||
|
)
|
||||||
|
assert (
|
||||||
|
doc(m.identity_literal_all_special_chars)
|
||||||
|
== 'identity_literal_all_special_chars(arg0: Literal["!@!!->{%}"]) -> Literal["!@!!->{%}"]'
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.skipif(
|
@pytest.mark.skipif(
|
||||||
@ -1195,15 +1228,22 @@ def test_final_annotation() -> None:
|
|||||||
|
|
||||||
def test_arg_return_type_hints(doc):
|
def test_arg_return_type_hints(doc):
|
||||||
assert doc(m.half_of_number) == "half_of_number(arg0: Union[float, int]) -> float"
|
assert doc(m.half_of_number) == "half_of_number(arg0: Union[float, int]) -> float"
|
||||||
|
assert (
|
||||||
|
doc(m.half_of_number_convert)
|
||||||
|
== "half_of_number_convert(x: Union[float, int]) -> float"
|
||||||
|
)
|
||||||
|
assert (
|
||||||
|
doc(m.half_of_number_noconvert) == "half_of_number_noconvert(x: float) -> float"
|
||||||
|
)
|
||||||
assert m.half_of_number(2.0) == 1.0
|
assert m.half_of_number(2.0) == 1.0
|
||||||
assert m.half_of_number(2) == 1.0
|
assert m.half_of_number(2) == 1.0
|
||||||
assert m.half_of_number(0) == 0
|
assert m.half_of_number(0) == 0
|
||||||
assert isinstance(m.half_of_number(0), float)
|
assert isinstance(m.half_of_number(0), float)
|
||||||
assert not isinstance(m.half_of_number(0), int)
|
assert not isinstance(m.half_of_number(0), int)
|
||||||
# std::vector<T> should use fallback type (complex is not really useful but just used for testing)
|
# std::vector<T>
|
||||||
assert (
|
assert (
|
||||||
doc(m.half_of_number_vector)
|
doc(m.half_of_number_vector)
|
||||||
== "half_of_number_vector(arg0: list[complex]) -> list[complex]"
|
== "half_of_number_vector(arg0: list[Union[float, int]]) -> list[float]"
|
||||||
)
|
)
|
||||||
# Tuple<T, T>
|
# Tuple<T, T>
|
||||||
assert (
|
assert (
|
||||||
@ -1245,6 +1285,21 @@ def test_arg_return_type_hints(doc):
|
|||||||
doc(m.identity_iterator)
|
doc(m.identity_iterator)
|
||||||
== "identity_iterator(arg0: Iterator[Union[float, int]]) -> Iterator[float]"
|
== "identity_iterator(arg0: Iterator[Union[float, int]]) -> Iterator[float]"
|
||||||
)
|
)
|
||||||
|
# Callable<R(A)> identity
|
||||||
|
assert (
|
||||||
|
doc(m.identity_callable)
|
||||||
|
== "identity_callable(arg0: Callable[[Union[float, int]], float]) -> Callable[[Union[float, int]], float]"
|
||||||
|
)
|
||||||
|
# Callable<R(...)> identity
|
||||||
|
assert (
|
||||||
|
doc(m.identity_callable_ellipsis)
|
||||||
|
== "identity_callable_ellipsis(arg0: Callable[..., float]) -> Callable[..., float]"
|
||||||
|
)
|
||||||
|
# Nested Callable<R(A)> identity
|
||||||
|
assert (
|
||||||
|
doc(m.identity_nested_callable)
|
||||||
|
== "identity_nested_callable(arg0: Callable[[Callable[[Union[float, int]], float]], Callable[[Union[float, int]], float]]) -> Callable[[Callable[[Union[float, int]], float]], Callable[[Union[float, int]], float]]"
|
||||||
|
)
|
||||||
# Callable<R(A)>
|
# Callable<R(A)>
|
||||||
assert (
|
assert (
|
||||||
doc(m.apply_callable)
|
doc(m.apply_callable)
|
||||||
|
@ -265,19 +265,19 @@ def test_fs_path(doc):
|
|||||||
assert m.parent_path(PseudoBytesPath()) == Path("foo")
|
assert m.parent_path(PseudoBytesPath()) == Path("foo")
|
||||||
assert (
|
assert (
|
||||||
doc(m.parent_path)
|
doc(m.parent_path)
|
||||||
== "parent_path(arg0: Union[os.PathLike, str, bytes]) -> Path"
|
== "parent_path(arg0: Union[os.PathLike, str, bytes]) -> pathlib.Path"
|
||||||
)
|
)
|
||||||
# std::vector should use name (for arg_name/return_name typing classes must be used)
|
# std::vector
|
||||||
assert m.parent_paths(["foo/bar", "foo/baz"]) == [Path("foo"), Path("foo")]
|
assert m.parent_paths(["foo/bar", "foo/baz"]) == [Path("foo"), Path("foo")]
|
||||||
assert (
|
assert (
|
||||||
doc(m.parent_paths)
|
doc(m.parent_paths)
|
||||||
== "parent_paths(arg0: list[os.PathLike]) -> list[os.PathLike]"
|
== "parent_paths(arg0: list[Union[os.PathLike, str, bytes]]) -> list[pathlib.Path]"
|
||||||
)
|
)
|
||||||
# py::typing::List
|
# py::typing::List
|
||||||
assert m.parent_paths_list(["foo/bar", "foo/baz"]) == [Path("foo"), Path("foo")]
|
assert m.parent_paths_list(["foo/bar", "foo/baz"]) == [Path("foo"), Path("foo")]
|
||||||
assert (
|
assert (
|
||||||
doc(m.parent_paths_list)
|
doc(m.parent_paths_list)
|
||||||
== "parent_paths_list(arg0: list[Union[os.PathLike, str, bytes]]) -> list[Path]"
|
== "parent_paths_list(arg0: list[Union[os.PathLike, str, bytes]]) -> list[pathlib.Path]"
|
||||||
)
|
)
|
||||||
# Nested py::typing::List
|
# Nested py::typing::List
|
||||||
assert m.parent_paths_nested_list([["foo/bar"], ["foo/baz", "foo/buzz"]]) == [
|
assert m.parent_paths_nested_list([["foo/bar"], ["foo/baz", "foo/buzz"]]) == [
|
||||||
@ -286,13 +286,13 @@ def test_fs_path(doc):
|
|||||||
]
|
]
|
||||||
assert (
|
assert (
|
||||||
doc(m.parent_paths_nested_list)
|
doc(m.parent_paths_nested_list)
|
||||||
== "parent_paths_nested_list(arg0: list[list[Union[os.PathLike, str, bytes]]]) -> list[list[Path]]"
|
== "parent_paths_nested_list(arg0: list[list[Union[os.PathLike, str, bytes]]]) -> list[list[pathlib.Path]]"
|
||||||
)
|
)
|
||||||
# py::typing::Tuple
|
# py::typing::Tuple
|
||||||
assert m.parent_paths_tuple(("foo/bar", "foo/baz")) == (Path("foo"), Path("foo"))
|
assert m.parent_paths_tuple(("foo/bar", "foo/baz")) == (Path("foo"), Path("foo"))
|
||||||
assert (
|
assert (
|
||||||
doc(m.parent_paths_tuple)
|
doc(m.parent_paths_tuple)
|
||||||
== "parent_paths_tuple(arg0: tuple[Union[os.PathLike, str, bytes], Union[os.PathLike, str, bytes]]) -> tuple[Path, Path]"
|
== "parent_paths_tuple(arg0: tuple[Union[os.PathLike, str, bytes], Union[os.PathLike, str, bytes]]) -> tuple[pathlib.Path, pathlib.Path]"
|
||||||
)
|
)
|
||||||
# py::typing::Dict
|
# py::typing::Dict
|
||||||
assert m.parent_paths_dict(
|
assert m.parent_paths_dict(
|
||||||
@ -308,7 +308,7 @@ def test_fs_path(doc):
|
|||||||
}
|
}
|
||||||
assert (
|
assert (
|
||||||
doc(m.parent_paths_dict)
|
doc(m.parent_paths_dict)
|
||||||
== "parent_paths_dict(arg0: dict[str, Union[os.PathLike, str, bytes]]) -> dict[str, Path]"
|
== "parent_paths_dict(arg0: dict[str, Union[os.PathLike, str, bytes]]) -> dict[str, pathlib.Path]"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user