mirror of
https://github.com/pybind/pybind11.git
synced 2025-02-23 17:09:27 +00:00
Merge branch 'pybind:master' into master
This commit is contained in:
commit
85b385d27b
2
.github/workflows/pip.yml
vendored
2
.github/workflows/pip.yml
vendored
@ -103,7 +103,7 @@ jobs:
|
||||
- uses: actions/download-artifact@v4
|
||||
|
||||
- name: Generate artifact attestation for sdist and wheel
|
||||
uses: actions/attest-build-provenance@c4fbc648846ca6f503a13a2281a5e7b98aa57202 # v2.0.1
|
||||
uses: actions/attest-build-provenance@7668571508540a607bdfd90a87a560489fe372eb # v2.1.0
|
||||
with:
|
||||
subject-path: "*/pybind11*"
|
||||
|
||||
|
@ -1366,6 +1366,31 @@ object object_or_cast(T &&o) {
|
||||
return pybind11::cast(std::forward<T>(o));
|
||||
}
|
||||
|
||||
// Declared in pytypes.h:
|
||||
// Implemented here so that make_caster<T> can be used.
|
||||
template <typename D>
|
||||
template <typename T>
|
||||
str_attr_accessor object_api<D>::attr_with_type_hint(const char *key) const {
|
||||
#if !defined(__cpp_inline_variables)
|
||||
static_assert(always_false<T>::value,
|
||||
"C++17 feature __cpp_inline_variables not available: "
|
||||
"https://en.cppreference.com/w/cpp/language/static#Static_data_members");
|
||||
#endif
|
||||
object ann = annotations();
|
||||
if (ann.contains(key)) {
|
||||
throw std::runtime_error("__annotations__[\"" + std::string(key) + "\"] was set already.");
|
||||
}
|
||||
ann[key] = make_caster<T>::name.text;
|
||||
return {derived(), key};
|
||||
}
|
||||
|
||||
template <typename D>
|
||||
template <typename T>
|
||||
obj_attr_accessor object_api<D>::attr_with_type_hint(handle key) const {
|
||||
(void) attr_with_type_hint<T>(key.cast<std::string>().c_str());
|
||||
return {derived(), reinterpret_borrow<object>(key)};
|
||||
}
|
||||
|
||||
// Placeholder type for the unneeded (and dead code) static variable in the
|
||||
// PYBIND11_OVERRIDE_OVERRIDE macro
|
||||
struct override_unused {};
|
||||
|
@ -12,51 +12,32 @@
|
||||
#define PYBIND11_PLATFORM_ABI_ID_STRINGIFY(x) #x
|
||||
#define PYBIND11_PLATFORM_ABI_ID_TOSTRING(x) PYBIND11_PLATFORM_ABI_ID_STRINGIFY(x)
|
||||
|
||||
// On MSVC, debug and release builds are not ABI-compatible!
|
||||
#if defined(_MSC_VER) && defined(_DEBUG)
|
||||
# define PYBIND11_BUILD_TYPE "_debug"
|
||||
#ifdef PYBIND11_COMPILER_TYPE
|
||||
// // To maintain backward compatibility (see PR #5439).
|
||||
# define PYBIND11_COMPILER_TYPE_LEADING_UNDERSCORE ""
|
||||
#else
|
||||
# define PYBIND11_BUILD_TYPE ""
|
||||
#endif
|
||||
|
||||
// Let's assume that different compilers are ABI-incompatible.
|
||||
// A user can manually set this string if they know their
|
||||
// compiler is compatible.
|
||||
#ifndef PYBIND11_COMPILER_TYPE
|
||||
# if defined(_MSC_VER)
|
||||
# define PYBIND11_COMPILER_TYPE "_msvc"
|
||||
# elif defined(__INTEL_COMPILER)
|
||||
# define PYBIND11_COMPILER_TYPE "_icc"
|
||||
# elif defined(__clang__)
|
||||
# define PYBIND11_COMPILER_TYPE "_clang"
|
||||
# elif defined(__PGI)
|
||||
# define PYBIND11_COMPILER_TYPE "_pgi"
|
||||
# elif defined(__MINGW32__)
|
||||
# define PYBIND11_COMPILER_TYPE "_mingw"
|
||||
# define PYBIND11_COMPILER_TYPE_LEADING_UNDERSCORE "_"
|
||||
# if defined(__MINGW32__)
|
||||
# define PYBIND11_COMPILER_TYPE "mingw"
|
||||
# elif defined(__CYGWIN__)
|
||||
# define PYBIND11_COMPILER_TYPE "_gcc_cygwin"
|
||||
# elif defined(__GNUC__)
|
||||
# define PYBIND11_COMPILER_TYPE "_gcc"
|
||||
# define PYBIND11_COMPILER_TYPE "gcc_cygwin"
|
||||
# elif defined(_MSC_VER)
|
||||
# define PYBIND11_COMPILER_TYPE "msvc"
|
||||
# elif defined(__clang__) || defined(__GNUC__)
|
||||
# define PYBIND11_COMPILER_TYPE "system" // Assumed compatible with system compiler.
|
||||
# else
|
||||
# define PYBIND11_COMPILER_TYPE "_unknown"
|
||||
# error "Unknown PYBIND11_COMPILER_TYPE: PLEASE REVISE THIS CODE."
|
||||
# endif
|
||||
#endif
|
||||
|
||||
// Also standard libs
|
||||
// PR #5439 made this macro obsolete. However, there are many manipulations of this macro in the
|
||||
// wild. Therefore, to maintain backward compatibility, it is kept around.
|
||||
#ifndef PYBIND11_STDLIB
|
||||
# if defined(_LIBCPP_VERSION)
|
||||
# define PYBIND11_STDLIB "_libcpp"
|
||||
# elif defined(__GLIBCXX__) || defined(__GLIBCPP__)
|
||||
# define PYBIND11_STDLIB "_libstdcpp"
|
||||
# else
|
||||
# define PYBIND11_STDLIB ""
|
||||
# endif
|
||||
# define PYBIND11_STDLIB ""
|
||||
#endif
|
||||
|
||||
#ifndef PYBIND11_BUILD_ABI
|
||||
# if defined(__GXX_ABI_VERSION) // Linux/OSX.
|
||||
# define PYBIND11_BUILD_ABI "_cxxabi" PYBIND11_PLATFORM_ABI_ID_TOSTRING(__GXX_ABI_VERSION)
|
||||
# elif defined(_MSC_VER) // See PR #4953.
|
||||
# if defined(_MSC_VER) // See PR #4953.
|
||||
# if defined(_MT) && defined(_DLL) // Corresponding to CL command line options /MD or /MDd.
|
||||
# if (_MSC_VER) / 100 == 19
|
||||
# define PYBIND11_BUILD_ABI "_md_mscver19"
|
||||
@ -72,17 +53,35 @@
|
||||
# error "Unknown major version for MSC_VER: PLEASE REVISE THIS CODE."
|
||||
# endif
|
||||
# endif
|
||||
# elif defined(__NVCOMPILER) // NVHPC (PGI-based).
|
||||
# define PYBIND11_BUILD_ABI "" // TODO: What should be here, to prevent UB?
|
||||
# elif defined(_LIBCPP_ABI_VERSION) // https://libcxx.llvm.org/DesignDocs/ABIVersioning.html
|
||||
# define PYBIND11_BUILD_ABI \
|
||||
"_libcpp_abi" PYBIND11_PLATFORM_ABI_ID_TOSTRING(_LIBCPP_ABI_VERSION)
|
||||
# elif defined(_GLIBCXX_USE_CXX11_ABI) // See PR #5439.
|
||||
# if defined(__NVCOMPILER)
|
||||
// // Assume that NVHPC is in the 1xxx ABI family.
|
||||
// // THIS ASSUMPTION IS NOT FUTURE PROOF but apparently the best we can do.
|
||||
// // Please let us know if there is a way to validate the assumption here.
|
||||
# elif !defined(__GXX_ABI_VERSION)
|
||||
# error \
|
||||
"Unknown platform or compiler (_GLIBCXX_USE_CXX11_ABI): PLEASE REVISE THIS CODE."
|
||||
# endif
|
||||
# if defined(__GXX_ABI_VERSION) && __GXX_ABI_VERSION < 1002 || __GXX_ABI_VERSION >= 2000
|
||||
# error "Unknown platform or compiler (__GXX_ABI_VERSION): PLEASE REVISE THIS CODE."
|
||||
# endif
|
||||
# define PYBIND11_BUILD_ABI \
|
||||
"_libstdcpp_gxx_abi_1xxx_use_cxx11_abi_" PYBIND11_PLATFORM_ABI_ID_TOSTRING( \
|
||||
_GLIBCXX_USE_CXX11_ABI)
|
||||
# else
|
||||
# error "Unknown platform or compiler: PLEASE REVISE THIS CODE."
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifndef PYBIND11_INTERNALS_KIND
|
||||
# define PYBIND11_INTERNALS_KIND ""
|
||||
// On MSVC, debug and release builds are not ABI-compatible!
|
||||
#if defined(_MSC_VER) && defined(_DEBUG)
|
||||
# define PYBIND11_BUILD_TYPE "_debug"
|
||||
#else
|
||||
# define PYBIND11_BUILD_TYPE ""
|
||||
#endif
|
||||
|
||||
#define PYBIND11_PLATFORM_ABI_ID \
|
||||
PYBIND11_INTERNALS_KIND PYBIND11_COMPILER_TYPE PYBIND11_STDLIB PYBIND11_BUILD_ABI \
|
||||
PYBIND11_BUILD_TYPE
|
||||
PYBIND11_COMPILER_TYPE PYBIND11_STDLIB PYBIND11_BUILD_ABI PYBIND11_BUILD_TYPE
|
||||
|
@ -627,6 +627,14 @@ struct instance {
|
||||
static_assert(std::is_standard_layout<instance>::value,
|
||||
"Internal error: `pybind11::detail::instance` is not standard layout!");
|
||||
|
||||
// Some older compilers (e.g. gcc 9.4.0) require
|
||||
// static_assert(always_false<T>::value, "...");
|
||||
// instead of
|
||||
// static_assert(false, "...");
|
||||
// to trigger the static_assert() in a template only if it is actually instantiated.
|
||||
template <typename>
|
||||
struct always_false : std::false_type {};
|
||||
|
||||
/// from __cpp_future__ import (convenient aliases from C++14/17)
|
||||
#if defined(PYBIND11_CPP14)
|
||||
using std::conditional_t;
|
||||
|
@ -272,11 +272,11 @@ struct type_info {
|
||||
|
||||
#define PYBIND11_INTERNALS_ID \
|
||||
"__pybind11_internals_v" PYBIND11_TOSTRING(PYBIND11_INTERNALS_VERSION) \
|
||||
PYBIND11_PLATFORM_ABI_ID "__"
|
||||
PYBIND11_COMPILER_TYPE_LEADING_UNDERSCORE PYBIND11_PLATFORM_ABI_ID "__"
|
||||
|
||||
#define PYBIND11_MODULE_LOCAL_ID \
|
||||
"__pybind11_module_local_v" PYBIND11_TOSTRING(PYBIND11_INTERNALS_VERSION) \
|
||||
PYBIND11_PLATFORM_ABI_ID "__"
|
||||
PYBIND11_COMPILER_TYPE_LEADING_UNDERSCORE PYBIND11_PLATFORM_ABI_ID "__"
|
||||
|
||||
/// Each module locally stores a pointer to the `internals` data. The data
|
||||
/// itself is shared among modules with the same `PYBIND11_INTERNALS_ID`.
|
||||
|
@ -113,6 +113,17 @@ public:
|
||||
/// See above (the only difference is that the key is provided as a string literal)
|
||||
str_attr_accessor attr(const char *key) const;
|
||||
|
||||
/** \rst
|
||||
Similar to the above attr functions with the difference that the templated Type
|
||||
is used to set the `__annotations__` dict value to the corresponding key. Worth noting
|
||||
that attr_with_type_hint is implemented in cast.h.
|
||||
\endrst */
|
||||
template <typename T>
|
||||
obj_attr_accessor attr_with_type_hint(handle key) const;
|
||||
/// See above (the only difference is that the key is provided as a string literal)
|
||||
template <typename T>
|
||||
str_attr_accessor attr_with_type_hint(const char *key) const;
|
||||
|
||||
/** \rst
|
||||
Matches * unpacking in Python, e.g. to unpack arguments out of a ``tuple``
|
||||
or ``list`` for a function call. Applying another * to the result yields
|
||||
@ -182,6 +193,9 @@ public:
|
||||
/// Get or set the object's docstring, i.e. ``obj.__doc__``.
|
||||
str_attr_accessor doc() const;
|
||||
|
||||
/// Get or set the object's annotations, i.e. ``obj.__annotations__``.
|
||||
object annotations() const;
|
||||
|
||||
/// Return the object's current reference count
|
||||
ssize_t ref_count() const {
|
||||
#ifdef PYPY_VERSION
|
||||
@ -2558,6 +2572,19 @@ str_attr_accessor object_api<D>::doc() const {
|
||||
return attr("__doc__");
|
||||
}
|
||||
|
||||
template <typename D>
|
||||
object object_api<D>::annotations() const {
|
||||
#if PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION <= 9
|
||||
// https://docs.python.org/3/howto/annotations.html#accessing-the-annotations-dict-of-an-object-in-python-3-9-and-older
|
||||
if (!hasattr(derived(), "__annotations__")) {
|
||||
setattr(derived(), "__annotations__", dict());
|
||||
}
|
||||
return attr("__annotations__");
|
||||
#else
|
||||
return getattr(derived(), "__annotations__", dict());
|
||||
#endif
|
||||
}
|
||||
|
||||
template <typename D>
|
||||
handle object_api<D>::get_type() const {
|
||||
return type::handle_of(derived());
|
||||
|
@ -82,6 +82,18 @@ class Optional : public object {
|
||||
using object::object;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class Final : public object {
|
||||
PYBIND11_OBJECT_DEFAULT(Final, object, PyObject_Type)
|
||||
using object::object;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class ClassVar : public object {
|
||||
PYBIND11_OBJECT_DEFAULT(ClassVar, object, PyObject_Type)
|
||||
using object::object;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class TypeGuard : public bool_ {
|
||||
using bool_::bool_;
|
||||
@ -251,6 +263,16 @@ struct handle_type_name<typing::Optional<T>> {
|
||||
= const_name("Optional[") + as_return_type<make_caster<T>>::name + const_name("]");
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct handle_type_name<typing::Final<T>> {
|
||||
static constexpr auto name = const_name("Final[") + make_caster<T>::name + const_name("]");
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct handle_type_name<typing::ClassVar<T>> {
|
||||
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.
|
||||
|
||||
|
@ -1037,6 +1037,38 @@ TEST_SUBMODULE(pytypes, m) {
|
||||
#else
|
||||
m.attr("defined_PYBIND11_TEST_PYTYPES_HAS_RANGES") = false;
|
||||
#endif
|
||||
|
||||
#if defined(__cpp_inline_variables)
|
||||
// Exercises const char* overload:
|
||||
m.attr_with_type_hint<py::typing::List<int>>("list_int") = py::list();
|
||||
// Exercises py::handle overload:
|
||||
m.attr_with_type_hint<py::typing::Set<py::str>>(py::str("set_str")) = py::set();
|
||||
|
||||
struct Empty {};
|
||||
py::class_<Empty>(m, "EmptyAnnotationClass");
|
||||
|
||||
struct Static {};
|
||||
auto static_class = py::class_<Static>(m, "Static");
|
||||
static_class.def(py::init());
|
||||
static_class.attr_with_type_hint<py::typing::ClassVar<float>>("x") = 1.0;
|
||||
static_class.attr_with_type_hint<py::typing::ClassVar<py::typing::Dict<py::str, int>>>(
|
||||
"dict_str_int")
|
||||
= py::dict();
|
||||
|
||||
struct Instance {};
|
||||
auto instance = py::class_<Instance>(m, "Instance", py::dynamic_attr());
|
||||
instance.def(py::init());
|
||||
instance.attr_with_type_hint<float>("y");
|
||||
|
||||
m.def("attr_with_type_hint_float_x",
|
||||
[](py::handle obj) { obj.attr_with_type_hint<float>("x"); });
|
||||
|
||||
m.attr_with_type_hint<py::typing::Final<int>>("CONST_INT") = 3;
|
||||
|
||||
m.attr("defined___cpp_inline_variables") = true;
|
||||
#else
|
||||
m.attr("defined___cpp_inline_variables") = false;
|
||||
#endif
|
||||
m.def("half_of_number", [](const RealNumber &x) { return RealNumber{x.value / 2}; });
|
||||
// std::vector<T>
|
||||
m.def("half_of_number_vector", [](const std::vector<RealNumber> &x) {
|
||||
|
@ -1103,6 +1103,96 @@ def test_dict_ranges(tested_dict, expected):
|
||||
assert m.transform_dict_plus_one(tested_dict) == expected
|
||||
|
||||
|
||||
# https://docs.python.org/3/howto/annotations.html#accessing-the-annotations-dict-of-an-object-in-python-3-9-and-older
|
||||
def get_annotations_helper(o):
|
||||
if isinstance(o, type):
|
||||
return o.__dict__.get("__annotations__", None)
|
||||
return getattr(o, "__annotations__", None)
|
||||
|
||||
|
||||
@pytest.mark.skipif(
|
||||
not m.defined___cpp_inline_variables,
|
||||
reason="C++17 feature __cpp_inline_variables not available.",
|
||||
)
|
||||
def test_module_attribute_types() -> None:
|
||||
module_annotations = get_annotations_helper(m)
|
||||
|
||||
assert module_annotations["list_int"] == "list[int]"
|
||||
assert module_annotations["set_str"] == "set[str]"
|
||||
|
||||
|
||||
@pytest.mark.skipif(
|
||||
not m.defined___cpp_inline_variables,
|
||||
reason="C++17 feature __cpp_inline_variables not available.",
|
||||
)
|
||||
@pytest.mark.skipif(
|
||||
sys.version_info < (3, 10),
|
||||
reason="get_annotations function does not exist until Python3.10",
|
||||
)
|
||||
def test_get_annotations_compliance() -> None:
|
||||
from inspect import get_annotations
|
||||
|
||||
module_annotations = get_annotations(m)
|
||||
|
||||
assert module_annotations["list_int"] == "list[int]"
|
||||
assert module_annotations["set_str"] == "set[str]"
|
||||
|
||||
|
||||
@pytest.mark.skipif(
|
||||
not m.defined___cpp_inline_variables,
|
||||
reason="C++17 feature __cpp_inline_variables not available.",
|
||||
)
|
||||
def test_class_attribute_types() -> None:
|
||||
empty_annotations = get_annotations_helper(m.EmptyAnnotationClass)
|
||||
static_annotations = get_annotations_helper(m.Static)
|
||||
instance_annotations = get_annotations_helper(m.Instance)
|
||||
|
||||
assert empty_annotations is None
|
||||
assert static_annotations["x"] == "ClassVar[float]"
|
||||
assert static_annotations["dict_str_int"] == "ClassVar[dict[str, int]]"
|
||||
|
||||
assert m.Static.x == 1.0
|
||||
|
||||
m.Static.x = 3.0
|
||||
static = m.Static()
|
||||
assert static.x == 3.0
|
||||
|
||||
static.dict_str_int["hi"] = 3
|
||||
assert m.Static().dict_str_int == {"hi": 3}
|
||||
|
||||
assert instance_annotations["y"] == "float"
|
||||
instance1 = m.Instance()
|
||||
instance1.y = 4.0
|
||||
|
||||
instance2 = m.Instance()
|
||||
instance2.y = 5.0
|
||||
|
||||
assert instance1.y != instance2.y
|
||||
|
||||
|
||||
@pytest.mark.skipif(
|
||||
not m.defined___cpp_inline_variables,
|
||||
reason="C++17 feature __cpp_inline_variables not available.",
|
||||
)
|
||||
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"] == "float"
|
||||
with pytest.raises(
|
||||
RuntimeError, match=r'^__annotations__\["x"\] was set already\.$'
|
||||
):
|
||||
m.attr_with_type_hint_float_x(obj)
|
||||
|
||||
|
||||
@pytest.mark.skipif(
|
||||
not m.defined___cpp_inline_variables,
|
||||
reason="C++17 feature __cpp_inline_variables not available.",
|
||||
)
|
||||
def test_final_annotation() -> None:
|
||||
module_annotations = get_annotations_helper(m)
|
||||
assert module_annotations["CONST_INT"] == "Final[int]"
|
||||
|
||||
|
||||
def test_arg_return_type_hints(doc):
|
||||
assert doc(m.half_of_number) == "half_of_number(arg0: Union[float, int]) -> float"
|
||||
assert m.half_of_number(2.0) == 1.0
|
||||
|
Loading…
Reference in New Issue
Block a user