Commit 2a3f2745 authored by Bolea Sanchez, Vicente Adolfo's avatar Bolea Sanchez, Vicente Adolfo
Browse files

Merge branch 'upstream-pybind11' into macosCI

# By PyBind11 Upstream
* upstream-pybind11:
  pybind11 2024-07-16 (43de8014)
parent e0a535a6
Loading
Loading
Loading
Loading
+24 −8
Original line number Diff line number Diff line
@@ -10,6 +10,7 @@

#pragma once

#include "detail/common.h"
#include "cast.h"

#include <functional>
@@ -25,6 +26,9 @@ struct is_method {
    explicit is_method(const handle &c) : class_(c) {}
};

/// Annotation for setters
struct is_setter {};

/// Annotation for operators
struct is_operator {};

@@ -187,8 +191,8 @@ struct argument_record {
struct function_record {
    function_record()
        : is_constructor(false), is_new_style_constructor(false), is_stateless(false),
          is_operator(false), is_method(false), has_args(false), has_kwargs(false),
          prepend(false) {}
          is_operator(false), is_method(false), is_setter(false), has_args(false),
          has_kwargs(false), prepend(false) {}

    /// Function name
    char *name = nullptr; /* why no C++ strings? They generate heavier code.. */
@@ -229,6 +233,9 @@ struct function_record {
    /// True if this is a method
    bool is_method : 1;

    /// True if this is a setter
    bool is_setter : 1;

    /// True if the function has a '*args' argument
    bool has_args : 1;

@@ -344,9 +351,11 @@ struct type_record {

        bases.append((PyObject *) base_info->type);

        if (base_info->type->tp_dictoffset != 0) {
            dynamic_attr = true;
        }
#if PY_VERSION_HEX < 0x030B0000
        dynamic_attr |= base_info->type->tp_dictoffset != 0;
#else
        dynamic_attr |= (base_info->type->tp_flags & Py_TPFLAGS_MANAGED_DICT) != 0;
#endif

        if (caster) {
            base_info->implicit_casts.emplace_back(type, caster);
@@ -396,7 +405,7 @@ struct process_attribute<doc> : process_attribute_default<doc> {
template <>
struct process_attribute<const char *> : process_attribute_default<const char *> {
    static void init(const char *d, function_record *r) { r->doc = const_cast<char *>(d); }
    static void init(const char *d, type_record *r) { r->doc = const_cast<char *>(d); }
    static void init(const char *d, type_record *r) { r->doc = d; }
};
template <>
struct process_attribute<char *> : process_attribute<const char *> {};
@@ -423,6 +432,12 @@ struct process_attribute<is_method> : process_attribute_default<is_method> {
    }
};

/// Process an attribute which indicates that this function is a setter
template <>
struct process_attribute<is_setter> : process_attribute_default<is_setter> {
    static void init(const is_setter &, function_record *r) { r->is_setter = true; }
};

/// Process an attribute which indicates the parent scope of a method
template <>
struct process_attribute<scope> : process_attribute_default<scope> {
@@ -477,7 +492,7 @@ struct process_attribute<arg_v> : process_attribute_default<arg_v> {
        }

        if (!a.value) {
#if !defined(NDEBUG)
#if defined(PYBIND11_DETAILED_ERROR_MESSAGES)
            std::string descr("'");
            if (a.name) {
                descr += std::string(a.name) + ": ";
@@ -498,7 +513,8 @@ struct process_attribute<arg_v> : process_attribute_default<arg_v> {
#else
            pybind11_fail("arg(): could not convert default argument "
                          "into a Python object (type not registered yet?). "
                          "Compile in debug mode for more information.");
                          "#define PYBIND11_DETAILED_ERROR_MESSAGES or compile in debug mode for "
                          "more information.");
#endif
        }
        r->args.emplace_back(a.name, a.descr, a.value.inc_ref(), !a.flag_noconvert, a.flag_none);
+30 −15
Original line number Diff line number Diff line
@@ -37,6 +37,9 @@ inline std::vector<ssize_t> f_strides(const std::vector<ssize_t> &shape, ssize_t
    return strides;
}

template <typename T, typename SFINAE = void>
struct compare_buffer_info;

PYBIND11_NAMESPACE_END(detail)

/// Information record describing a Python buffer object
@@ -150,6 +153,17 @@ struct buffer_info {
    Py_buffer *view() const { return m_view; }
    Py_buffer *&view() { return m_view; }

    /* True if the buffer item type is equivalent to `T`. */
    // To define "equivalent" by example:
    // `buffer_info::item_type_is_equivalent_to<int>(b)` and
    // `buffer_info::item_type_is_equivalent_to<long>(b)` may both be true
    // on some platforms, but `int` and `unsigned` will never be equivalent.
    // For the ground truth, please inspect `detail::compare_buffer_info<>`.
    template <typename T>
    bool item_type_is_equivalent_to() const {
        return detail::compare_buffer_info<T>::compare(*this);
    }

private:
    struct private_ctr_tag {};

@@ -170,9 +184,10 @@ private:

PYBIND11_NAMESPACE_BEGIN(detail)

template <typename T, typename SFINAE = void>
template <typename T, typename SFINAE>
struct compare_buffer_info {
    static bool compare(const buffer_info &b) {
        // NOLINTNEXTLINE(bugprone-sizeof-expression) Needed for `PyObject *`
        return b.format == format_descriptor<T>::format() && b.itemsize == (ssize_t) sizeof(T);
    }
};
+284 −69

File changed.

Preview size limit exceeded, changes collapsed.

+112 −90
Original line number Diff line number Diff line
@@ -55,6 +55,9 @@ extern "C" inline int pybind11_static_set(PyObject *self, PyObject *obj, PyObjec
    return PyProperty_Type.tp_descr_set(self, cls, value);
}

// Forward declaration to use in `make_static_property_type()`
inline void enable_dynamic_attributes(PyHeapTypeObject *heap_type);

/** A `static_property` is the same as a `property` but the `__get__()` and `__set__()`
    methods are modified to always use the object type instead of a concrete instance.
    Return value: New reference. */
@@ -83,6 +86,12 @@ inline PyTypeObject *make_static_property_type() {
    type->tp_descr_get = pybind11_static_get;
    type->tp_descr_set = pybind11_static_set;

#    if PY_VERSION_HEX >= 0x030C0000
    // Since Python-3.12 property-derived types are required to
    // have dynamic attributes (to set `__doc__`)
    enable_dynamic_attributes(heap_type);
#    endif

    if (PyType_Ready(type) < 0) {
        pybind11_fail("make_static_property_type(): failure in PyType_Ready()!");
    }
@@ -179,12 +188,10 @@ extern "C" inline PyObject *pybind11_meta_call(PyObject *type, PyObject *args, P
        return nullptr;
    }

    // This must be a pybind11 instance
    auto *instance = reinterpret_cast<detail::instance *>(self);

    // Ensure that the base __init__ function(s) were called
    for (const auto &vh : values_and_holders(instance)) {
        if (!vh.holder_constructed()) {
    values_and_holders vhs(self);
    for (const auto &vh : vhs) {
        if (!vh.holder_constructed() && !vhs.is_redundant_value_and_holder(vh)) {
            PyErr_Format(PyExc_TypeError,
                         "%.200s.__init__() must be called when overriding __init__",
                         get_fully_qualified_tp_name(vh.type->type).c_str());
@@ -198,8 +205,8 @@ extern "C" inline PyObject *pybind11_meta_call(PyObject *type, PyObject *args, P

/// Cleanup the type-info for a pybind11-registered type.
extern "C" inline void pybind11_meta_dealloc(PyObject *obj) {
    with_internals([obj](internals &internals) {
        auto *type = (PyTypeObject *) obj;
    auto &internals = get_internals();

        // A pybind11-registered type will:
        // 1) be found in internals.registered_types_py
@@ -231,6 +238,7 @@ extern "C" inline void pybind11_meta_dealloc(PyObject *obj) {

            delete tinfo;
        }
    });

    PyType_Type.tp_dealloc(obj);
}
@@ -303,19 +311,20 @@ inline void traverse_offset_bases(void *valueptr,
}

inline bool register_instance_impl(void *ptr, instance *self) {
    get_internals().registered_instances.emplace(ptr, self);
    with_instance_map(ptr, [&](instance_map &instances) { instances.emplace(ptr, self); });
    return true; // unused, but gives the same signature as the deregister func
}
inline bool deregister_instance_impl(void *ptr, instance *self) {
    auto &registered_instances = get_internals().registered_instances;
    auto range = registered_instances.equal_range(ptr);
    return with_instance_map(ptr, [&](instance_map &instances) {
        auto range = instances.equal_range(ptr);
        for (auto it = range.first; it != range.second; ++it) {
            if (self == it->second) {
            registered_instances.erase(it);
                instances.erase(it);
                return true;
            }
        }
        return false;
    });
}

inline void register_instance(instance *self, void *valptr, const type_info *tinfo) {
@@ -365,28 +374,37 @@ extern "C" inline PyObject *pybind11_object_new(PyTypeObject *type, PyObject *,
extern "C" inline int pybind11_object_init(PyObject *self, PyObject *, PyObject *) {
    PyTypeObject *type = Py_TYPE(self);
    std::string msg = get_fully_qualified_tp_name(type) + ": No constructor defined!";
    PyErr_SetString(PyExc_TypeError, msg.c_str());
    set_error(PyExc_TypeError, msg.c_str());
    return -1;
}

inline void add_patient(PyObject *nurse, PyObject *patient) {
    auto &internals = get_internals();
    auto *instance = reinterpret_cast<detail::instance *>(nurse);
    instance->has_patients = true;
    Py_INCREF(patient);
    internals.patients[nurse].push_back(patient);

    with_internals([&](internals &internals) { internals.patients[nurse].push_back(patient); });
}

inline void clear_patients(PyObject *self) {
    auto *instance = reinterpret_cast<detail::instance *>(self);
    auto &internals = get_internals();
    std::vector<PyObject *> patients;

    with_internals([&](internals &internals) {
        auto pos = internals.patients.find(self);
    assert(pos != internals.patients.end());

        if (pos == internals.patients.end()) {
            pybind11_fail(
                "FATAL: Internal consistency check failed: Invalid clear_patients() call.");
        }

        // Clearing the patients can cause more Python code to run, which
        // can invalidate the iterator. Extract the vector of patients
        // from the unordered_map first.
    auto patients = std::move(pos->second);
        patients = std::move(pos->second);
        internals.patients.erase(pos);
    });

    instance->has_patients = false;
    for (PyObject *&patient : patients) {
        Py_CLEAR(patient);
@@ -435,9 +453,17 @@ inline void clear_instance(PyObject *self) {
/// Instance destructor function for all pybind11 types. It calls `type_info.dealloc`
/// to destroy the C++ object itself, while the rest is Python bookkeeping.
extern "C" inline void pybind11_object_dealloc(PyObject *self) {
    auto *type = Py_TYPE(self);

    // If this is a GC tracked object, untrack it first
    // Note that the track call is implicitly done by the
    // default tp_alloc, which we never override.
    if (PyType_HasFeature(type, Py_TPFLAGS_HAVE_GC) != 0) {
        PyObject_GC_UnTrack(self);
    }

    clear_instance(self);

    auto *type = Py_TYPE(self);
    type->tp_free(self);

#if PY_VERSION_HEX < 0x03080000
@@ -455,6 +481,8 @@ extern "C" inline void pybind11_object_dealloc(PyObject *self) {
#endif
}

std::string error_string();

/** Create the type which can be used as a common base for all classes.  This is
    needed in order to satisfy Python's requirements for multiple inheritance.
    Return value: New reference. */
@@ -500,42 +528,29 @@ inline PyObject *make_object_base_type(PyTypeObject *metaclass) {
    return (PyObject *) heap_type;
}

/// dynamic_attr: Support for `d = instance.__dict__`.
extern "C" inline PyObject *pybind11_get_dict(PyObject *self, void *) {
    PyObject *&dict = *_PyObject_GetDictPtr(self);
    if (!dict) {
        dict = PyDict_New();
    }
    Py_XINCREF(dict);
    return dict;
}

/// dynamic_attr: Support for `instance.__dict__ = dict()`.
extern "C" inline int pybind11_set_dict(PyObject *self, PyObject *new_dict, void *) {
    if (!PyDict_Check(new_dict)) {
        PyErr_Format(PyExc_TypeError,
                     "__dict__ must be set to a dictionary, not a '%.200s'",
                     get_fully_qualified_tp_name(Py_TYPE(new_dict)).c_str());
        return -1;
    }
    PyObject *&dict = *_PyObject_GetDictPtr(self);
    Py_INCREF(new_dict);
    Py_CLEAR(dict);
    dict = new_dict;
    return 0;
}

/// dynamic_attr: Allow the garbage collector to traverse the internal instance `__dict__`.
extern "C" inline int pybind11_traverse(PyObject *self, visitproc visit, void *arg) {
#if PY_VERSION_HEX >= 0x030D0000
    PyObject_VisitManagedDict(self, visit, arg);
#else
    PyObject *&dict = *_PyObject_GetDictPtr(self);
    Py_VISIT(dict);
#endif
// https://docs.python.org/3/c-api/typeobj.html#c.PyTypeObject.tp_traverse
#if PY_VERSION_HEX >= 0x03090000
    Py_VISIT(Py_TYPE(self));
#endif
    return 0;
}

/// dynamic_attr: Allow the GC to clear the dictionary.
extern "C" inline int pybind11_clear(PyObject *self) {
#if PY_VERSION_HEX >= 0x030D0000
    PyObject_ClearManagedDict(self);
#else
    PyObject *&dict = *_PyObject_GetDictPtr(self);
    Py_CLEAR(dict);
#endif
    return 0;
}

@@ -543,13 +558,17 @@ extern "C" inline int pybind11_clear(PyObject *self) {
inline void enable_dynamic_attributes(PyHeapTypeObject *heap_type) {
    auto *type = &heap_type->ht_type;
    type->tp_flags |= Py_TPFLAGS_HAVE_GC;
#if PY_VERSION_HEX < 0x030B0000
    type->tp_dictoffset = type->tp_basicsize;           // place dict at the end
    type->tp_basicsize += (ssize_t) sizeof(PyObject *); // and allocate enough space for it
#else
    type->tp_flags |= Py_TPFLAGS_MANAGED_DICT;
#endif
    type->tp_traverse = pybind11_traverse;
    type->tp_clear = pybind11_clear;

    static PyGetSetDef getset[] = {
        {const_cast<char *>("__dict__"), pybind11_get_dict, pybind11_set_dict, nullptr, nullptr},
    static PyGetSetDef getset[]
        = {{"__dict__", PyObject_GenericGetDict, PyObject_GenericSetDict, nullptr, nullptr},
           {nullptr, nullptr, nullptr, nullptr, nullptr}};
    type->tp_getset = getset;
}
@@ -568,7 +587,7 @@ extern "C" inline int pybind11_getbuffer(PyObject *obj, Py_buffer *view, int fla
        if (view) {
            view->obj = nullptr;
        }
        PyErr_SetString(PyExc_BufferError, "pybind11_getbuffer(): Internal error");
        set_error(PyExc_BufferError, "pybind11_getbuffer(): Internal error");
        return -1;
    }
    std::memset(view, 0, sizeof(Py_buffer));
@@ -576,7 +595,7 @@ extern "C" inline int pybind11_getbuffer(PyObject *obj, Py_buffer *view, int fla
    if ((flags & PyBUF_WRITABLE) == PyBUF_WRITABLE && info->readonly) {
        delete info;
        // view->obj = nullptr;  // Was just memset to 0, so not necessary
        PyErr_SetString(PyExc_BufferError, "Writable buffer requested for readonly storage");
        set_error(PyExc_BufferError, "Writable buffer requested for readonly storage");
        return -1;
    }
    view->obj = obj;
@@ -642,10 +661,13 @@ inline PyObject *make_new_python_type(const type_record &rec) {

    char *tp_doc = nullptr;
    if (rec.doc && options::show_user_defined_docstrings()) {
        /* Allocate memory for docstring (using PyObject_MALLOC, since
           Python will free this later on) */
        /* Allocate memory for docstring (Python will free this later on) */
        size_t size = std::strlen(rec.doc) + 1;
#if PY_VERSION_HEX >= 0x030D0000
        tp_doc = (char *) PyMem_MALLOC(size);
#else
        tp_doc = (char *) PyObject_MALLOC(size);
#endif
        std::memcpy((void *) tp_doc, rec.doc, size);
    }

@@ -707,7 +729,7 @@ inline PyObject *make_new_python_type(const type_record &rec) {
    }

    if (PyType_Ready(type) < 0) {
        pybind11_fail(std::string(rec.name) + ": PyType_Ready failed (" + error_string() + ")!");
        pybind11_fail(std::string(rec.name) + ": PyType_Ready failed: " + error_string());
    }

    assert(!rec.dynamic_attr || PyType_HasFeature(type, Py_TPFLAGS_HAVE_GC));
+162 −53

File changed.

Preview size limit exceeded, changes collapsed.

Loading