@@ -33,34 +33,18 @@ static ExplainType ExplainTypeFromInteger(int64_t value) {
3333 }
3434}
3535
36- namespace PYBIND11_NAMESPACE {
37- namespace detail {
38-
39- template <>
40- struct type_caster <ExplainType> : public type_caster_base<ExplainType> {
41- using base = type_caster_base<ExplainType>;
42- ExplainType tmp;
43-
44- public:
45- bool load (handle src, bool convert) {
46- if (base::load (src, convert)) {
47- return true ;
48- } else if (py::isinstance<py::str>(src)) {
49- tmp = ExplainTypeFromString (py::str (src));
50- value = &tmp;
51- return true ;
52- } else if (py::isinstance<py::int_>(src)) {
53- tmp = ExplainTypeFromInteger (src.cast <int64_t >());
54- value = &tmp;
55- return true ;
56- }
57- return false ;
36+ // ! Resolve a Python explain-type argument (ExplainType enum, str, or int) to an ExplainType.
37+ // ! NOTE: deliberately NOT a pybind type_caster. A custom caster inheriting type_caster_base shadows the
38+ // ! registered py::enum_ inconsistently across translation units - it ends up accepting str/int XOR the enum
39+ // ! instance, never both, depending on which TU sees the specialization. Explicit dispatch at the call site is
40+ // ! robust regardless of include order.
41+ static ExplainType ExplainTypeFromPython (const py::object &obj) {
42+ if (py::isinstance<py::str>(obj)) {
43+ return ExplainTypeFromString (py::str (obj));
5844 }
59-
60- static handle cast (ExplainType src, return_value_policy policy, handle parent) {
61- return base::cast (src, policy, parent);
45+ if (py::isinstance<py::int_>(obj)) {
46+ return ExplainTypeFromInteger (obj.cast <int64_t >());
6247 }
63- };
64-
65- } // namespace detail
66- } // namespace PYBIND11_NAMESPACE
48+ // Fall through to the registered py::enum_ caster (handles an actual ExplainType, throws otherwise).
49+ return obj.cast <ExplainType>();
50+ }
0 commit comments