Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-01-09 10:20:47

0001 // Copyright (c) 2024-2025 The Pybind Development Team.
0002 // All rights reserved. Use of this source code is governed by a
0003 // BSD-style license that can be found in the LICENSE file.
0004 
0005 // For background see the description of PR google/pybind11clif#30099.
0006 
0007 #pragma once
0008 
0009 #include <pybind11/attr.h>
0010 #include <pybind11/conduit/pybind11_platform_abi_id.h>
0011 #include <pybind11/pytypes.h>
0012 
0013 #include "common.h"
0014 
0015 #include <cstring>
0016 #include <utility>
0017 
0018 PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
0019 PYBIND11_NAMESPACE_BEGIN(detail)
0020 
0021 struct function_record_PyObject {
0022     PyObject_HEAD
0023     function_record *cpp_func_rec;
0024 };
0025 
0026 PYBIND11_NAMESPACE_BEGIN(function_record_PyTypeObject_methods)
0027 
0028 PyObject *tp_new_impl(PyTypeObject *type, PyObject *args, PyObject *kwds);
0029 PyObject *tp_alloc_impl(PyTypeObject *type, Py_ssize_t nitems);
0030 int tp_init_impl(PyObject *self, PyObject *args, PyObject *kwds);
0031 void tp_dealloc_impl(PyObject *self);
0032 void tp_free_impl(void *self);
0033 
0034 static PyObject *reduce_ex_impl(PyObject *self, PyObject *, PyObject *);
0035 
0036 static PyMethodDef tp_methods_impl[]
0037     = {{"__reduce_ex__",
0038         // reduce_ex_impl is a PyCFunctionWithKeywords, but PyMethodDef
0039         // requires a PyCFunction. The cast through void* is safe and
0040         // idiomatic with METH_KEYWORDS, and it successfully sidesteps
0041         // unhelpful compiler warnings.
0042         // NOLINTNEXTLINE(bugprone-casting-through-void)
0043         reinterpret_cast<PyCFunction>(reinterpret_cast<void *>(reduce_ex_impl)),
0044         METH_VARARGS | METH_KEYWORDS,
0045         nullptr},
0046        {nullptr, nullptr, 0, nullptr}};
0047 
0048 // Python 3.12+ emits a DeprecationWarning for heap types whose tp_name does
0049 // not contain a dot ('.') and that lack a __module__ attribute. For pybind11's
0050 // internal function_record type, we do not have an actual module object to
0051 // attach, so we cannot use PyType_FromModuleAndSpec (introduced in Python 3.9)
0052 // to set __module__ automatically.
0053 //
0054 // As a workaround, we define a "qualified" type name that includes a dummy
0055 // module name (PYBIND11_DUMMY_MODULE_NAME). This is non‑idiomatic but avoids
0056 // the deprecation warning, and results in reprs like
0057 //
0058 //     <class 'pybind11_builtins.pybind11_detail_function_record_...'>
0059 //
0060 // even though no real pybind11_builtins module exists. If pybind11 gains an
0061 // actual module object in the future, this code should switch to
0062 // PyType_FromModuleAndSpec for Python 3.9+ and drop the dummy module
0063 // workaround.
0064 //
0065 // Note that this name is versioned.
0066 #define PYBIND11_DETAIL_FUNCTION_RECORD_TP_PLAINNAME                                              \
0067     "pybind11_detail_function_record_" PYBIND11_DETAIL_FUNCTION_RECORD_ABI_ID                     \
0068     "_" PYBIND11_PLATFORM_ABI_ID
0069 constexpr char tp_plainname_impl[] = PYBIND11_DETAIL_FUNCTION_RECORD_TP_PLAINNAME;
0070 constexpr char tp_qualname_impl[]
0071     = PYBIND11_DUMMY_MODULE_NAME "." PYBIND11_DETAIL_FUNCTION_RECORD_TP_PLAINNAME;
0072 
0073 PYBIND11_NAMESPACE_END(function_record_PyTypeObject_methods)
0074 
0075 static PyType_Slot function_record_PyType_Slots[] = {
0076     {Py_tp_dealloc,
0077      reinterpret_cast<void *>(function_record_PyTypeObject_methods::tp_dealloc_impl)},
0078     {Py_tp_methods,
0079      reinterpret_cast<void *>(function_record_PyTypeObject_methods::tp_methods_impl)},
0080     {Py_tp_init, reinterpret_cast<void *>(function_record_PyTypeObject_methods::tp_init_impl)},
0081     {Py_tp_alloc, reinterpret_cast<void *>(function_record_PyTypeObject_methods::tp_alloc_impl)},
0082     {Py_tp_new, reinterpret_cast<void *>(function_record_PyTypeObject_methods::tp_new_impl)},
0083     {Py_tp_free, reinterpret_cast<void *>(function_record_PyTypeObject_methods::tp_free_impl)},
0084     {0, nullptr}};
0085 
0086 static PyType_Spec function_record_PyType_Spec
0087     = {function_record_PyTypeObject_methods::tp_qualname_impl,
0088        sizeof(function_record_PyObject),
0089        0,
0090        Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HEAPTYPE,
0091        function_record_PyType_Slots};
0092 
0093 inline PyTypeObject *get_function_record_PyTypeObject() {
0094     PyTypeObject *&py_type_obj = detail::get_local_internals().function_record_py_type;
0095     if (!py_type_obj) {
0096         PyObject *py_obj = PyType_FromSpec(&function_record_PyType_Spec);
0097         if (py_obj == nullptr) {
0098             throw error_already_set();
0099         }
0100         py_type_obj = reinterpret_cast<PyTypeObject *>(py_obj);
0101     }
0102     return py_type_obj;
0103 }
0104 
0105 inline bool is_function_record_PyObject(PyObject *obj) {
0106     if (PyType_Check(obj) != 0) {
0107         return false;
0108     }
0109     PyTypeObject *obj_type = Py_TYPE(obj);
0110 
0111     PyTypeObject *frtype = get_function_record_PyTypeObject();
0112 
0113     // Fast path (pointer comparison).
0114     if (obj_type == frtype) {
0115         return true;
0116     }
0117     // This works across extension modules. Note that tp_name is versioned.
0118     if (strcmp(obj_type->tp_name, function_record_PyTypeObject_methods::tp_qualname_impl) == 0
0119         || strcmp(obj_type->tp_name, function_record_PyTypeObject_methods::tp_plainname_impl)
0120                == 0) {
0121         return true;
0122     }
0123     return false;
0124 }
0125 
0126 inline function_record *function_record_ptr_from_PyObject(PyObject *obj) {
0127     if (is_function_record_PyObject(obj)) {
0128         return ((detail::function_record_PyObject *) obj)->cpp_func_rec;
0129     }
0130     return nullptr;
0131 }
0132 
0133 inline object function_record_PyObject_New() {
0134     auto *py_func_rec = PyObject_New(function_record_PyObject, get_function_record_PyTypeObject());
0135     if (py_func_rec == nullptr) {
0136         throw error_already_set();
0137     }
0138     py_func_rec->cpp_func_rec = nullptr; // For clarity/purity. Redundant in practice.
0139     return reinterpret_steal<object>((PyObject *) py_func_rec);
0140 }
0141 
0142 PYBIND11_NAMESPACE_BEGIN(function_record_PyTypeObject_methods)
0143 
0144 // Guard against accidents & oversights, in particular when porting to future Python versions.
0145 inline PyObject *tp_new_impl(PyTypeObject *, PyObject *, PyObject *) {
0146     pybind11_fail("UNEXPECTED CALL OF function_record_PyTypeObject_methods::tp_new_impl");
0147     // return nullptr; // Unreachable.
0148 }
0149 
0150 inline PyObject *tp_alloc_impl(PyTypeObject *, Py_ssize_t) {
0151     pybind11_fail("UNEXPECTED CALL OF function_record_PyTypeObject_methods::tp_alloc_impl");
0152     // return nullptr; // Unreachable.
0153 }
0154 
0155 inline int tp_init_impl(PyObject *, PyObject *, PyObject *) {
0156     pybind11_fail("UNEXPECTED CALL OF function_record_PyTypeObject_methods::tp_init_impl");
0157     // return -1; // Unreachable.
0158 }
0159 
0160 inline void tp_free_impl(void *) {
0161     pybind11_fail("UNEXPECTED CALL OF function_record_PyTypeObject_methods::tp_free_impl");
0162 }
0163 
0164 inline PyObject *reduce_ex_impl(PyObject *self, PyObject *, PyObject *) {
0165     // Deliberately ignoring the arguments for simplicity (expected is `protocol: int`).
0166     const function_record *rec = function_record_ptr_from_PyObject(self);
0167     if (rec == nullptr) {
0168         pybind11_fail(
0169             "FATAL: function_record_PyTypeObject reduce_ex_impl(): cannot obtain cpp_func_rec.");
0170     }
0171     if (rec->name != nullptr && rec->name[0] != '\0' && rec->scope
0172         && PyModule_Check(rec->scope.ptr()) != 0) {
0173         object scope_module = get_scope_module(rec->scope);
0174         if (scope_module) {
0175             auto builtins = reinterpret_borrow<dict>(PyEval_GetBuiltins());
0176             auto builtins_eval = builtins["eval"];
0177             auto reconstruct_args = make_tuple(str("__import__('importlib').import_module('")
0178                                                + scope_module + str("')"));
0179             return make_tuple(std::move(builtins_eval), std::move(reconstruct_args))
0180                 .release()
0181                 .ptr();
0182         }
0183     }
0184     set_error(PyExc_RuntimeError, repr(self) + str(" is not pickleable."));
0185     return nullptr;
0186 }
0187 
0188 PYBIND11_NAMESPACE_END(function_record_PyTypeObject_methods)
0189 
0190 PYBIND11_NAMESPACE_END(detail)
0191 PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)