At least for GCC enum is just a simple numeric type. It can be 8-, 16-, 32-, 64-bit or whatever (I have tested it with 64-bit values) as well as signed or unsigned. I guess it cannot exceed long long int, but practically you should check the range of your enums and choose something like c_uint.

Here is an example. The C program:

enum batch_op {
    OP1 = 2,
    OP2 = 3,
    OP3 = -1,
};

struct attrl {
    char *name;
    struct attrl *next;
    enum batch_op op;
};

void f(struct attrl *x) {
    x->op = OP3;
}

and the Python one:

from ctypes import (Structure, c_char_p, c_uint, c_int,
    POINTER, CDLL)

class AttrList(Structure): pass
AttrList._fields_ = [
    ('name', c_char_p),
    ('next', POINTER(AttrList)),
    ('op', c_int),
]

(OP1, OP2, OP3) = (2, 3, -1)

enum = CDLL('./libenum.so')
enum.f.argtypes = [POINTER(AttrList)]
enum.f.restype = None

a = AttrList(name=None, next=None, op=OP2)
assert a.op == OP2
enum.f(a)
assert a.op == OP3
Answer from Andrey Vlasovskikh on Stack Overflow
🌐
Chriskrycho
v4.chriskrycho.com › 2015 › ctypes-structures-and-dll-exports.html
Python Enums, ctypes.Structures, and DLL exports · Chris Krycho
May 28, 2015 - Now, let’s say we want to apply this pattern to more than a single IntEnum class, because our C code defines more than one enumeration. Extracting it to be common functionality is simple enough: just create a class that implements the class method, and inherit from it. class CtypesEnum(IntEnum): """A ctypes-compatible IntEnum superclass.""" @classmethod def from_param(cls, obj): return int(obj) class MyEnum(CtypesEnum): ZERO = 0 ONE = 1 TWO = 2
🌐
GitHub
github.com › vpelletier › python-enum
GitHub - vpelletier/python-enum: C enum for python (useful for ctype wrappers)
Typical usage: when translating C headers into ctypes wrappers. Example: C code: enum foo { F_FOO, F_BAR = 10, F_BAZ, LAST_FOO = F_BAZ }; Python equivalent: >>> foo = Enum([ ... ('F_FOO', None), ... ('F_BAR', 10), ...
Author: vpelletier
🌐
Bytes
bytes.com › home › forum › topic › python
How to use C enum in Python CTypes? - Post.Byes
August 7, 2007 - You can do it easily yourself, using c_int as the base class." I would just define constants: (OLSS_AD, OLSS_DA, OLSS_DIN, OLSS_DOUT, OLSS_SRL, OLSS_CT) = map(ctypes.c_in t, xrange(6)) Ciao, Marc 'BlackJack' Rintsch ... Re: How to use C enum in Python CTypes?
🌐
Python
python-list.python.narkive.com › ZRUWI0zS › how-to-use-c-enum-in-python-ctypes
How to use C enum in Python CTypes?
Yes, that's how C's and C++'s enums work, unless an override is given (which I know you can do in C++ for sure). Otherwise, they are just numbers starting at 0. The size of the intergers (byte, 2 bytes, 4 bytes, etc..) is implemenation defined I believe, but an int normally works. ... Permalink I am using Python's CTypes module to import functions from a DLL into Python.
🌐
Readthedocs
pyclibrary.readthedocs.io › en › latest › _modules › pyclibrary › backends › ctypes.html
pyclibrary.backends.ctypes — PyCLibrary 0.2.0.dev documentation
For functions, a callable object is returned that handles automatic type conversion for arguments and return values. For structs, types, and enums, a ctypes class is returned matching the type specified. lib.name: searches in order through values, functions, types, structs, unions, and enums ...
🌐
Stack Overflow
stackoverflow.com › questions › 36877391 › using-an-intenum-as-the-type-in-a-ctypes-structure-fields)
python - Using an IntEnum as the type in a ctypes.Structure._fields_ - Stack Overflow
April 26, 2016 - You can subclass ctypes.c_int to create an enum class that overrides __new__ to restrict instantiation and implements the class methods _check_retval_ and from_param to restrict the value for a given result or argument.
🌐
DevGenius
blog.devgenius.io › python-use-external-c-library-ctypes-70c34c3845cd
Python use external C library — Ctypes | by Pietro Colombo | Dev Genius
February 19, 2023 - import ctypes as ct from enum import auto, IntEnum libc = ct.cdll.LoadLibrary("name.dll") #typedef void* ESCONROL; class ESCONROL(ct.c_void_p): pass #ES_API ESCONROL _CreateControl(); libc._CreateControl.argtypes = None libc._CreateControl.restype = ESCONROL es_ctrl = libc._CreateControl() # ES_API bool _OpenConnection(ESCONROL pCtrl, const char* IpAddr = "192.168.137.100", unsigned int usPort = 12345); libc._OpenConnection.argtypes = [ESCONROL, ct.c_char_p, ct.c_uint] libc._OpenConnection.restype = ct.c_bool ip = "192.168.137.100" port = 12345 res = libc._OpenConnection(es_ctrl, ct.c_char_p(ip.encode('utf-8')), port)
Find elsewhere
🌐
YouTube
youtube.com › watch
Accessing enum in Structure with ctypes in Python - YouTube
Learn how to interface with C++ libraries using Python's ctypes to access enums in structures, initialize GSM modules, and handle event callbacks effectively...
Published: April 5, 2025
Views: 3
Top answer
1 of 1
1

I made a small demo of some C code that looks similar to your API below, and show how to handle an enum and callback nicely:

test.c

#include <memory.h>

#ifdef _WIN32
#   define API __declspec(dllexport)
#else
#   define API
#endif

typedef enum _gsmEvent_e {
    GSM_NO_SIGNAL = 0,
    GSM_RING_VOICE,
    GSM_RING_DATA,
    GSM_NEW_SMS,
    GSM_CALL_RELEASED,
    GSM_RECEIVED_DATA,
    GSM_FAILURE,
    GSM_COVERAGE,
} gsmEvent_e;

#define EVENTS_BUFFER_SIZE 1024

typedef struct _gsmEvents_s {
   gsmEvent_e gsmEventType;
   char evBuffer[ EVENTS_BUFFER_SIZE];
   int evHandled;
}gsmEvents_s;

typedef struct _TGSM_MODULE_CONFIGURATION {
   unsigned char wCode[9];
   unsigned char wMECode[9];
   void (*gsm_action)(gsmEvents_s*);
} TGSM_MODULE_CONFIGURATION;

TGSM_MODULE_CONFIGURATION* g_conf;

API int GSM_Initialize(TGSM_MODULE_CONFIGURATION* pgsmconf) {
    // Save off the configuration so the callback can be called by start.
    g_conf = pgsmconf;
    return 1;
}

API void GSM_Start() {
    // Hard-code some event response and call the callback.
    gsmEvents_s event;
    event.gsmEventType = GSM_FAILURE;
    memset(event.evBuffer, 'A', EVENTS_BUFFER_SIZE);
    event.evHandled = 1;
    g_conf->gsm_action(&event);
}

test.py

import ctypes as ct
from enum import Enum,auto

class gsmEvent_e(Enum):
    GSM_NO_SIGNAL = 0
    GSM_RING_VOICE = auto()
    GSM_RING_DATA = auto()
    GSM_NEW_SMS = auto()
    GSM_CALL_RELEASED = auto()
    GSM_RECEIVED_DATA = auto()
    GSM_FAILURE = auto()
    GSM_COVERAGE = auto()

    @classmethod
    def from_param(obj): # allows enum to be used in .argtypes
        if not isinstance(cls, obj):
            raise TypeError('not a gsmEvent_e enumeration')
        return obj.value

class gsmEvents_s(ct.Structure):

    _fields_ = (("_gsmEventType",  ct.c_int),
                ("evBuffer", ct.c_char * 1024),
                ("evHandled", ct.c_int))

    @property
    def gsmEventType(self): # wrapper to return the enum when referenced.
        return gsmEvent_e(self._gsmEventType)

    # allows structure to print itself
    def __repr__(self):
        return f'gsm_Events_s(type={self.gsmEventType.name}, handled={self.evHandled})'

# callback type
GSM_ACTION = ct.CFUNCTYPE(None, ct.POINTER(gsmEvents_s))

class TGSM_MODULE_CONFIGURATION(ct.Structure):
    _fields_ = (("wCode", ct.c_char * 9),
                ("wMECode", ct.c_char * 9),
                ("gsm_action", GSM_ACTION))

# To define a Python callback function, decorate with the callback type
@GSM_ACTION
def gsm_action_callback(gsm_event):
    # callback pointer is dereferenced with .contents
    # __repr__ defined above allows event to print itself
    print(gsm_event.contents)

dll = ct.CDLL('./test')
dll.GSM_Initialize.argtypes = ct.POINTER(TGSM_MODULE_CONFIGURATION),
dll.GSM_Initialize.restype = ct.c_int
dll.GSM_Start.argtypes = ()
dll.GSM_Start.restype = None

gsmconf = TGSM_MODULE_CONFIGURATION(b'code', b'mecode', gsm_action_callback)
dll.GSM_Initialize(gsmconf)
dll.GSM_Start()

Output:

gsm_Events_s(type=GSM_FAILURE, handled=1)
🌐
GitHub
gist.github.com › christoph2 › 9c390e5c094796903097
Add missing enum feature to ctypes Structures. · GitHub
Add missing enum feature to ctypes Structures. GitHub Gist: instantly share code, notes, and snippets.
Top answer
1 of 3
12

The Enumeration class suggested by Raj Kumar was broken in that it required the __init__ to be run to set a new value in a variable, and thus unusable if the value was changed on C side. Here is a fixed version thereof:

class EnumerationType(type(c_uint)):
    def __new__(metacls, name, bases, dict):
        if not "_members_" in dict:
            _members_ = {}
            for key, value in dict.items():
                if not key.startswith("_"):
                    _members_[key] = value

            dict["_members_"] = _members_
        else:
            _members_ = dict["_members_"]

        dict["_reverse_map_"] = { v: k for k, v in _members_.items() }
        cls = type(c_uint).__new__(metacls, name, bases, dict)
        for key,value in cls._members_.items():
            globals()[key] = value
        return cls

    def __repr__(self):
        return "<Enumeration %s>" % self.__name__

class CEnumeration(c_uint):
    __metaclass__ = EnumerationType
    _members_     = {}

    def __repr__(self):
        value = self.value
        return "<%s.%s: %d>" % (
            self.__class__.__name__,
            self._reverse_map_.get(value, '(unknown)'),
            value
        )

    def __eq__(self, other):
        if isinstance(other, (int, long)):
            return self.value == other

        return type(self) == type(other) and self.value == other.value

Now one can declare a CEnumeration:

class EBoolean(CEnumeration):
    FALSE = 0
    TRUE = 1

and use it:

class HeaderStruct(Structure):
    _fields_ = [("param1", EBoolean), 
                ("param2", c_uint)]

Examples:

>>> header = HeaderStruct()
>>> header.param1
<EBoolean.FALSE: 0>
>>> memmove(addressof(header), b'\x01', 1)  # write LSB 0x01 in the boolean
>>> header.param1
<EBoolean.TRUE: 1>
>>> header.param1 == EBoolean.TRUE
True
>>> header.param1 == 1   # as a special case compare against ints
True
>>> header.param1.value
1L
2 of 3
3

Antti Haapala did a fantastic job answering! I, however, did run into some minor issues when using it with Python 3.2.2 that I believe are worth noting. Instead of:

class CEnumeration(c_uint):
    __metaclass__ = EnumerationType
    _members_     = {}

You need to do:

class CEnumeration(c_uint, metaclass = EnumerationType):
    _members_     = {}

Also, int and long have been unified in Python 3 so:

def __eq__(self, other):
        if isinstance(other, (int, long)):
            return self.value == other

        return type(self) == type(other) and self.value == other.value

Becomes:

def __eq__(self, other):
        if isinstance(other, int):
            return self.value == other

        return type(self) == type(other) and self.value == other.value
🌐
Stack Overflow
stackoverflow.com › revisions › 68826175 › 5
Revision - Stack Overflow
August 18, 2021 - This was my solution for the python part. I read 1 and 0 respectively. import ctypes from ctypes import * test = ctypes.WinDLL('Project.dll') if (test == 0): print( " Could not open DLL") class myEnum(c_int): off = 0 on = 1 getParam = test.get_param getParam.argtype = myEnum() getParam.restype= ctypes.c_int result = test.get_param(myEnum.on) print(result) result = test.get_param(myEnum.off) print(result)
🌐
Stack Overflow
stackoverflow.com › questions › 68826175 › ctypes-passing-pointer-to-enum-receiving-uint-32
python - Ctypes, Passing pointer to enum, receiving uint_32 - Stack Overflow
import ctypes from enum import IntEnum test = ctypes.WinDLL('Project.dll') if (test == 0): print( " Could not open DLL") class CtypesEnum(IntEnum): @classmethod def from_param(cls, obj): return int(obj) class myEnum(CtypesEnum): off = 0 on = 1 getParam = test.get_param getParam.argtypes = [ctypes.POINTER(ctypes.c_int)] getParam.restype= ctypes.c_uint getParam(myEnum.on) The error I get now is Traceback (most recent call last): File "<pyshell#5>", line 1, in <module> getParam(myEnum.on) ctypes.ArgumentError: argument 1: <class 'TypeError'>: expected LP_c_long instance instead of myEnum What's the correct way of passing an enum pointer using ctypes.I couldn't find an example and I'm kinda new to python :/ [1]: https://v4.chriskrycho.com/2015/ctypes-structures-and-dll-exports.html
🌐
OCaml
discuss.ocaml.org › learning
Ctypes enum, how to make it work - Learning - OCaml
June 22, 2017 - I try to bind C enums with Ctypes but I am not able to make it work. Here is an example : In the GLib-2 library, threre is this enumeration : typedef enum { G_BOOKMARK_FILE_ERROR_INVALID_URI, G_BOOKMARK_FILE_ERROR_INVALID_VALUE, G_BOOKMARK_FILE_ERROR_APP_NOT_REGISTERED, G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND, G_BOOKMARK_FILE_ERROR_READ, G_BOOKMARK_FILE_ERROR_UNKNOWN_ENCODING, G_BOOKMARK_FILE_ERROR_WRITE, G_BOOKMARK_FILE_ERROR_FILE_NOT_FOUND } GBookmarkFileError; So I create in a ...
🌐
Grokbase
grokbase.com › t › python › python-list › 07876dv5ev › how-to-use-c-enum-in-python-ctypes
[Python] How to use C enum in Python CTypes? - Grokbase
August 7, 2007 - I am fairly new to Python, coming in from C++ (of which I am utterly sick!) ... On Tue, 07 Aug 2007 04:57:19 +0000, rozniy wrote: typedef enum olss_tag { OLSS_AD, OLSS_DA, OLSS_DIN, OLSS_DOUT, OLSS_SRL, OLSS_CT } OLSS; I managed to fudge the HDEV, UINT and PHDASS types as CTypes c_long(), but I am not sure how translate a C enum into Python...