File indexing completed on 2026-01-09 10:20:47
0001
0002
0003
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
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
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);
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;
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
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
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)