Defining a Python enum in a C extension - am I doing this right?

Sean DiZazzo sean.dizazzo at gmail.com
Tue Aug 3 22:24:41 EDT 2021


On Tuesday, August 3, 2021 at 3:04:19 AM UTC-7, Bartosz Golaszewski wrote:
> On Sat, Jul 31, 2021 at 3:01 PM Bartosz Golaszewski <br... at bgdev.pl> wrote: 
> > 
> > On Fri, Jul 30, 2021 at 2:41 PM Serhiy Storchaka <stor... at gmail.com> wrote: 
> > > 
> > > 23.07.21 11:20, Bartosz Golaszewski пише: 
> > > > I'm working on a Python C extension and I would like to expose a 
> > > > custom enum (as in: a class inheriting from enum.Enum) that would be 
> > > > entirely defined in C. 
> > > 
> > > I think that it would be much easier to define it in Python, and then 
> > > either import a Python module in your C code, or exec a Python code as a 
> > > string. 
> > > 
> > 
> > You mean: evaluate a string like this: 
> > 
> > ''' 
> > import enum 
> > 
> > class FooBar(enum.Enum): 
> > FOO = 1 
> > BAR = 2 
> > BAZ = 3 
> > ''' 
> > 
> > And then pull in the FooBar type from the resulting dictionary into my 
> > C code? Sounds good actually. I think I'll be able to add the FooBar 
> > type to another type's tp_dict too - because some enums I want to 
> > create will be nested in other classes. 
> > 
> > Bart
> Just a follow-up: this is how I did it eventually: 
> 
> ``` 
> #include <Python.h> 
> 
> typedef struct { 
> PyObject_HEAD; 
> } dummy_object; 
> 
> PyDoc_STRVAR(dummy_type_doc, "Dummy type in which the enum will be nested."); 
> 
> static PyTypeObject dummy_type = { 
> PyVarObject_HEAD_INIT(NULL, 0) 
> .tp_name = "pycenum.DummyType", 
> .tp_basicsize = sizeof(dummy_object), 
> .tp_flags = Py_TPFLAGS_DEFAULT, 
> .tp_doc = dummy_type_doc, 
> .tp_new = PyType_GenericNew, 
> .tp_dealloc = (destructor)PyObject_Del, 
> };
> PyDoc_STRVAR(module_doc, 
> "C extension module defining a class inheriting from enum.Enum."); 
> 
> static PyModuleDef module_def = { 
> PyModuleDef_HEAD_INIT, 
> .m_name = "pycenum", 
> .m_doc = module_doc, 
> .m_size = -1, 
> };
> static int add_foobar_enum(PyObject *module) 
> { 
> static const char *foobar_src = 
> "class FooBar(enum.Enum):\n" 
> " FOO = 1\n" 
> " BAR = 2\n" 
> " BAZ = 3\n"; 
> 
> PyObject *main_mod, *main_dict, *enum_mod, *result, *foobar_type; 
> int ret; 
> 
> main_mod = PyImport_AddModule("__main__"); 
> if (!main_mod) 
> return -1; 
> 
> main_dict = PyModule_GetDict(main_mod); 
> if (!main_dict) { 
> Py_DECREF(main_mod); 
> return -1;
> } 
> 
> enum_mod = PyImport_ImportModule("enum");
> if (!enum_mod) { 
> Py_DECREF(main_mod); 
> return -1; 
> } 
> 
> ret = PyDict_SetItemString(main_dict, "enum", enum_mod); 
> Py_DECREF(enum_mod); 
> if (ret) { 
> Py_DECREF(main_mod); 
> return -1; 
> } 
> 
> result = PyRun_String(foobar_src, Py_single_input, 
> main_dict, main_dict); 
> if (!result) { 
> Py_DECREF(main_mod); 
> return -1; 
> } 
> 
> foobar_type = PyDict_GetItemString(main_dict, "FooBar"); 
> if (!foobar_type) { 
> Py_DECREF(main_mod); 
> return -1; 
> } 
> 
> ret = PyDict_SetItemString(dummy_type.tp_dict, "FooBar", foobar_type); 
> Py_DECREF(foobar_type); 
> Py_DECREF(main_mod); 
> if (ret) 
> return -1; 
> 
> PyType_Modified(&dummy_type); 
> 
> return ret; 
> } 
> 
> PyMODINIT_FUNC PyInit_pycenum(void) 
> { 
> PyObject *module;
> int ret; 
> 
> module = PyModule_Create(&module_def); 
> if (!module) 
> return NULL; 
> 
> ret = PyModule_AddStringConstant(module, "__version__", "0.0.1");
> if (ret) { 
> Py_DECREF(module); 
> return NULL; 
> } 
> 
> ret = PyType_Ready(&dummy_type); 
> if (ret) { 
> Py_DECREF(module); 
> return NULL; 
> } 
> 
> ret = add_foobar_enum(module); 
> if (ret) { 
> Py_DECREF(module); 
> return NULL; 
> } 
> 
> Py_INCREF(&dummy_type); 
> ret = PyModule_AddObject(module, "DummyType", (PyObject *)&dummy_type); 
> if (ret) { 
> Py_DECREF(&dummy_type);
> Py_DECREF(module); 
> return NULL; 
> } 
> 
> return module; 
> }
> ``` 
> 
> Bart
No


More information about the Python-list mailing list