Back to home page

EIC code displayed by LXR

 
 

    


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

0001 // Copyright (c) 2022-2025 The pybind Community.
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 #pragma once
0006 
0007 #include "../pytypes.h"
0008 #include "common.h"
0009 #include "internals.h"
0010 
0011 #include <cassert>
0012 #include <sstream>
0013 #include <string>
0014 #include <typeindex>
0015 
0016 PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
0017 PYBIND11_NAMESPACE_BEGIN(detail)
0018 
0019 // This is a separate function only to enable easy unit testing.
0020 inline std::string
0021 native_enum_missing_finalize_error_message(const std::string &enum_name_encoded) {
0022     return "pybind11::native_enum<...>(\"" + enum_name_encoded + "\", ...): MISSING .finalize()";
0023 }
0024 
0025 class native_enum_data {
0026 public:
0027     native_enum_data(const object &parent_scope,
0028                      const char *enum_name,
0029                      const char *native_type_name,
0030                      const char *class_doc,
0031                      const std::type_index &enum_type_index)
0032         : enum_name_encoded{enum_name}, native_type_name_encoded{native_type_name},
0033           enum_type_index{enum_type_index}, parent_scope(parent_scope), enum_name{enum_name},
0034           native_type_name{native_type_name}, class_doc(class_doc), export_values_flag{false},
0035           finalize_needed{false} {}
0036 
0037     void finalize();
0038 
0039     native_enum_data(const native_enum_data &) = delete;
0040     native_enum_data &operator=(const native_enum_data &) = delete;
0041 
0042 #if !defined(NDEBUG)
0043     // This dtor cannot easily be unit tested because it terminates the process.
0044     ~native_enum_data() {
0045         if (finalize_needed) {
0046             pybind11_fail(native_enum_missing_finalize_error_message(enum_name_encoded));
0047         }
0048     }
0049 #endif
0050 
0051 protected:
0052     void disarm_finalize_check(const char *error_context) {
0053         if (!finalize_needed) {
0054             pybind11_fail("pybind11::native_enum<...>(\"" + enum_name_encoded
0055                           + "\"): " + error_context);
0056         }
0057         finalize_needed = false;
0058     }
0059 
0060     void arm_finalize_check() {
0061         assert(!finalize_needed); // Catch redundant calls.
0062         finalize_needed = true;
0063     }
0064 
0065     std::string enum_name_encoded;
0066     std::string native_type_name_encoded;
0067     std::type_index enum_type_index;
0068 
0069 private:
0070     object parent_scope;
0071     str enum_name;
0072     str native_type_name;
0073     std::string class_doc;
0074 
0075 protected:
0076     list members;
0077     list member_docs;
0078     bool export_values_flag : 1; // Attention: It is best to keep the bools together.
0079 
0080 private:
0081     bool finalize_needed : 1;
0082 };
0083 
0084 inline void global_internals_native_enum_type_map_set_item(const std::type_index &enum_type_index,
0085                                                            PyObject *py_enum) {
0086     with_internals(
0087         [&](internals &internals) { internals.native_enum_type_map[enum_type_index] = py_enum; });
0088 }
0089 
0090 inline handle
0091 global_internals_native_enum_type_map_get_item(const std::type_index &enum_type_index) {
0092     return with_internals([&](internals &internals) {
0093         auto found = internals.native_enum_type_map.find(enum_type_index);
0094         if (found != internals.native_enum_type_map.end()) {
0095             return handle(found->second);
0096         }
0097         return handle();
0098     });
0099 }
0100 
0101 inline bool
0102 global_internals_native_enum_type_map_contains(const std::type_index &enum_type_index) {
0103     return with_internals([&](internals &internals) {
0104         return internals.native_enum_type_map.count(enum_type_index) != 0;
0105     });
0106 }
0107 
0108 inline object import_or_getattr(const std::string &fully_qualified_name,
0109                                 const std::string &append_to_exception_message) {
0110     std::istringstream stream(fully_qualified_name);
0111     std::string part;
0112 
0113     if (!std::getline(stream, part, '.') || part.empty()) {
0114         std::string msg = "Invalid fully-qualified name `";
0115         msg += fully_qualified_name;
0116         msg += "`";
0117         msg += append_to_exception_message;
0118         throw value_error(msg);
0119     }
0120 
0121     auto curr_scope = reinterpret_steal<object>(PyImport_ImportModule(part.c_str()));
0122     if (!curr_scope) {
0123         std::string msg = "Failed to import top-level module `";
0124         msg += part;
0125         msg += "`";
0126         msg += append_to_exception_message;
0127         raise_from(PyExc_ImportError, msg.c_str());
0128         throw error_already_set();
0129     }
0130 
0131     // Now recursively getattr or import remaining parts
0132     std::string curr_path = part;
0133     while (std::getline(stream, part, '.')) {
0134         if (part.empty()) {
0135             std::string msg = "Invalid fully-qualified name `";
0136             msg += fully_qualified_name;
0137             msg += "`";
0138             msg += append_to_exception_message;
0139             throw value_error(msg);
0140         }
0141         std::string next_path = curr_path;
0142         next_path += ".";
0143         next_path += part;
0144         auto next_scope
0145             = reinterpret_steal<object>(PyObject_GetAttrString(curr_scope.ptr(), part.c_str()));
0146         if (!next_scope) {
0147             error_fetch_and_normalize stored_getattr_error("getattr");
0148             // Try importing the next level
0149             next_scope = reinterpret_steal<object>(PyImport_ImportModule(next_path.c_str()));
0150             if (!next_scope) {
0151                 error_fetch_and_normalize stored_import_error("import");
0152                 std::string msg = "Failed to import or getattr `";
0153                 msg += part;
0154                 msg += "` from `";
0155                 msg += curr_path;
0156                 msg += "`";
0157                 msg += append_to_exception_message;
0158                 msg += "\n-------- getattr exception --------\n";
0159                 msg += stored_getattr_error.error_string();
0160                 msg += "\n-------- import exception --------\n";
0161                 msg += stored_import_error.error_string();
0162                 throw import_error(msg.c_str());
0163             }
0164         }
0165         curr_scope = next_scope;
0166         curr_path = next_path;
0167     }
0168     return curr_scope;
0169 }
0170 
0171 inline void native_enum_data::finalize() {
0172     disarm_finalize_check("DOUBLE finalize");
0173     if (hasattr(parent_scope, enum_name)) {
0174         pybind11_fail("pybind11::native_enum<...>(\"" + enum_name_encoded
0175                       + "\"): an object with that name is already defined");
0176     }
0177     auto py_enum_type = import_or_getattr(native_type_name, " (native_type_name)");
0178     auto py_enum = py_enum_type(enum_name, members);
0179     object module_name = get_module_name_if_available(parent_scope);
0180     if (module_name) {
0181         py_enum.attr("__module__") = module_name;
0182     }
0183     if (hasattr(parent_scope, "__qualname__")) {
0184         const auto parent_qualname = parent_scope.attr("__qualname__").cast<std::string>();
0185         py_enum.attr("__qualname__") = str(parent_qualname + "." + enum_name.cast<std::string>());
0186     }
0187     parent_scope.attr(enum_name) = py_enum;
0188     if (export_values_flag) {
0189         for (auto member : members) {
0190             auto member_name = member[int_(0)];
0191             if (hasattr(parent_scope, member_name)) {
0192                 pybind11_fail("pybind11::native_enum<...>(\"" + enum_name_encoded + "\").value(\""
0193                               + member_name.cast<std::string>()
0194                               + "\"): an object with that name is already defined");
0195             }
0196             parent_scope.attr(member_name) = py_enum[member_name];
0197         }
0198     }
0199     if (!class_doc.empty()) {
0200         py_enum.attr("__doc__") = class_doc.c_str();
0201     }
0202     for (auto doc : member_docs) {
0203         py_enum[doc[int_(0)]].attr("__doc__") = doc[int_(1)];
0204     }
0205     global_internals_native_enum_type_map_set_item(enum_type_index, py_enum.release().ptr());
0206 }
0207 
0208 PYBIND11_NAMESPACE_END(detail)
0209 PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)