Skip to content

Instantly share code, notes, and snippets.

@tritao
Created March 23, 2025 19:05
Show Gist options
  • Save tritao/1951ce1fb58dcc641b995f85ee475e05 to your computer and use it in GitHub Desktop.
Save tritao/1951ce1fb58dcc641b995f85ee475e05 to your computer and use it in GitHub Desktop.
static PyObject* createMyConstantsEnum()
{
// Import the Python 'enum' module.
PyObject* enumModule = PyImport_ImportModule("enum");
if (!enumModule) {
PyErr_SetString(PyExc_ImportError, "Failed to import the enum module");
return nullptr;
}
// Try to get StrEnum from the enum module (Python 3.11 and above).
PyObject* strEnumType = PyObject_GetAttrString(enumModule, "StrEnum");
PyObject* bases = nullptr;
if (strEnumType) {
// If StrEnum is available, use it as the sole base.
bases = PyTuple_New(1);
if (!bases) {
Py_DECREF(strEnumType);
Py_DECREF(enumModule);
return nullptr;
}
// PyTuple_SET_ITEM steals the reference to strEnumType.
PyTuple_SET_ITEM(bases, 0, strEnumType);
} else {
// Fallback for Python 3.10: clear the error and use (str, Enum) as bases.
PyErr_Clear();
// Get Enum from the enum module.
PyObject* enumType = PyObject_GetAttrString(enumModule, "Enum");
if (!enumType) {
Py_DECREF(enumModule);
PyErr_SetString(PyExc_AttributeError, "Failed to get 'Enum' from the enum module");
return nullptr;
}
// Get built-in str type.
PyObject* strType = (PyObject*)&PyUnicode_Type;
Py_INCREF(strType);
// Create a tuple with two bases: (str, Enum). Order is important.
bases = PyTuple_New(2);
if (!bases) {
Py_DECREF(enumType);
Py_DECREF(strType);
Py_DECREF(enumModule);
return nullptr;
}
PyTuple_SET_ITEM(bases, 0, strType); // steals reference to strType.
PyTuple_SET_ITEM(bases, 1, enumType); // steals reference to enumType.
}
Py_DECREF(enumModule);
// Create a dictionary for the enum members.
PyObject* enumDict = PyDict_New();
if (!enumDict) {
Py_DECREF(bases);
return nullptr;
}
// Define constant members:
// CONSTANT_ONE = "one"
// CONSTANT_TWO = "two"
// CONSTANT_THREE = "three"
PyObject* one = PyUnicode_FromString("one");
PyObject* two = PyUnicode_FromString("two");
PyObject* three = PyUnicode_FromString("three");
if (!one || !two || !three) {
Py_XDECREF(one);
Py_XDECREF(two);
Py_XDECREF(three);
Py_DECREF(enumDict);
Py_DECREF(bases);
return nullptr;
}
PyDict_SetItemString(enumDict, "CONSTANT_ONE", one);
PyDict_SetItemString(enumDict, "CONSTANT_TWO", two);
PyDict_SetItemString(enumDict, "CONSTANT_THREE", three);
Py_DECREF(one);
Py_DECREF(two);
Py_DECREF(three);
// Create the name for the new enum type.
PyObject* name = PyUnicode_FromString("MyConstants");
if (!name) {
Py_DECREF(bases);
Py_DECREF(enumDict);
return nullptr;
}
// Create the new type by calling the metatype (PyType_Type).
// The arguments are: name, bases tuple, and the dictionary of attributes.
PyObject* newEnumType = PyObject_CallFunctionObjArgs(
(PyObject*)&PyType_Type, // the metatype 'type'
name,
bases,
enumDict,
nullptr
);
Py_DECREF(name);
Py_DECREF(bases);
Py_DECREF(enumDict);
return newEnumType; // This new type is either based on enum.StrEnum or (str, Enum).
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment