Cython API Reference¶
This page documents the Cython-level API in event_engine.capi. These are cdef classes and methods exposed by the .pyx files.
Note
For typical Python usage, see Python API Reference (CAPI). The Cython API is primarily for performance-critical code or extending PyEventEngine in Cython.
Overview¶
The Cython API provides typed access to the underlying C structures with minimal Python overhead. All Cython classes are defined in .pyx files with corresponding .pxd declaration files.
Importing Cython Definitions¶
To use the Cython API in your own Cython code:
# my_module.pyx
from event_engine.capi.c_topic cimport PyTopic, PyTopicPart
from event_engine.capi.c_event cimport PyMessagePayload, EventHook
from event_engine.capi.c_engine cimport EventEngine
Or import everything:
from event_engine.capi cimport *
Including in Your Cython Extension¶
To use PyEventEngine in your own Cython modules, you need to properly configure your build system to include the necessary header files and declarations.
Using get_include() in setup.py¶
Similar to NumPy’s get_include() function, PyEventEngine provides a get_include() function that returns the path to the package’s include directory containing .pxd files and C headers.
Example setup.py:
from setuptools import setup, Extension
from Cython.Build import cythonize
import event_engine
extensions = [
Extension(
name="my_package.my_module",
sources=["my_package/my_module.pyx"],
include_dirs=[event_engine.get_include()],
extra_compile_args=["-O3"]
)
]
setup(
name="my_package",
ext_modules=cythonize(extensions, compiler_directives={'language_level': 3})
)
Complete Project Example:
from setuptools import setup, Extension, find_packages
from Cython.Build import cythonize
import event_engine
extensions = [
Extension(
name="my_package.core.event_handler",
sources=["my_package/core/event_handler.pyx"],
include_dirs=[event_engine.get_include()],
extra_compile_args=["-O3"]
),
Extension(
name="my_package.core.message_processor",
sources=["my_package/core/message_processor.pyx"],
include_dirs=[event_engine.get_include()],
extra_compile_args=["-O3"]
),
]
setup(
name="my_package",
version="1.0.0",
packages=find_packages(),
ext_modules=cythonize(
extensions,
compiler_directives={
'language_level': 3,
'embedsignature': True,
}
),
install_requires=[
'PyEventEngine>=0.4.4',
],
)
Using in your Cython module:
# my_package/core/event_handler.pyx
from event_engine.capi.c_engine cimport EventEngine
from event_engine.capi.c_topic cimport PyTopic
from event_engine.capi.c_event cimport PyMessagePayload
cdef class MyEventHandler:
cdef EventEngine engine
def __init__(self, int capacity=8192):
self.engine = EventEngine(capacity=capacity)
cpdef void process_event(self, str topic_str, object data):
cdef PyTopic topic = PyTopic(topic_str)
self.engine.put(topic, data, block=True)
What get_include() Returns¶
The get_include() function returns the absolute path to the event_engine package directory, which contains:
capi/*.pxd- Cython declaration files for importing C-level definitionscapi/*.h- C header files for the underlying C APIAll compiled extension modules (
.sofiles on Linux)
This allows the Cython compiler to find and import the necessary type definitions and C declarations when building your extension.
Topic Classes (c_topic.pyx)¶
PyTopicType¶
cpdef enum PyTopicType:
TOPIC_PART_EXACT = 0
TOPIC_PART_ANY = 1
TOPIC_PART_RANGE = 2
TOPIC_PART_PATTERN = 3
PyTopicPart¶
Base class for topic parts:
cdef class PyTopicPart:
cdef TopicPart* header
cdef bint owner
cdef PyTopicPart c_next(self)
cpdef PyTopicType ttype(self)
cpdef uint64_t addr(self)
PyTopicPartExact¶
cdef class PyTopicPartExact(PyTopicPart):
@staticmethod
cdef PyTopicPartExact c_from_header(TopicPart* header, bint owner=*)
cpdef str part(self) # Get the literal string
PyTopicPartAny¶
cdef class PyTopicPartAny(PyTopicPart):
cpdef str name(self) # Get the wildcard name
PyTopicPartRange¶
cdef class PyTopicPartRange(PyTopicPart):
cpdef options(self) # Iterator over option strings
PyTopicPartPattern¶
cdef class PyTopicPartPattern(PyTopicPart):
cpdef str pattern(self)
cpdef object regex(self) # Returns re.Pattern
PyTopic¶
Main topic class:
cdef class PyTopic:
cdef Topic* header
cdef bint owner
@staticmethod
cdef PyTopic c_from_header(Topic* header, bint owner=*)
cpdef str value(self)
cpdef bint is_exact(self)
cpdef uint64_t hash_value(self)
cpdef uint64_t addr(self)
cpdef PyTopicPart get_part(self, int index)
cpdef int length(self)
cpdef PyTopic format(self, **kwargs)
cpdef PyTopicMatchResult match(self, PyTopic other)
Example usage:
cdef PyTopic topic = PyTopic("Market.Data.AAPL")
cdef uint64_t topic_hash = topic.hash_value()
# Access parts
cdef int n_parts = topic.length()
cdef PyTopicPart part0 = topic.get_part(0)
Event Classes (c_event.pyx)¶
PyMessagePayload¶
cdef class PyMessagePayload:
cdef MessagePayload* header
cdef bint owner
cdef bint args_owner
cdef bint kwargs_owner
@staticmethod
cdef PyMessagePayload c_from_header(MessagePayload* header,
bint owner=*,
bint args_owner=*,
bint kwargs_owner=*)
cpdef PyTopic topic(self)
cpdef tuple args(self)
cpdef dict kwargs(self)
cpdef uint64_t seq_id(self)
EventHook¶
cdef class EventHook:
cdef EventHandler* handlers_no_topic
cdef EventHandler* handlers_with_topic
cdef public PyTopic topic
cdef public object logger
cdef public bint retry_on_unexpected_topic
cdef void c_safe_call_no_topic(self, EventHandler* handler, tuple args, dict kwargs)
cdef void c_safe_call_with_topic(self, EventHandler* handler, tuple args, dict kwargs)
@staticmethod
cdef inline void c_free_handlers(EventHandler* handlers)
cpdef void trigger(self, PyMessagePayload msg)
cpdef void add_handler(self, object handler, bint deduplicate=*)
cpdef EventHook remove_handler(self, object handler)
cpdef void clear(self)
cpdef list handlers(self)
EventHookEx¶
Extended hook with statistics:
cdef class EventHookEx(EventHook):
cdef ByteMap* stats_map
cpdef void trigger(self, PyMessagePayload msg)
cpdef dict get_stats(self, object py_callable)
cpdef object stats(self) # Returns iterator
Engine Classes (c_engine.pyx)¶
EventEngine¶
cdef class EventEngine:
cdef MessageQueue* queue
cdef ByteMap* exact_topic_hooks
cdef ByteMap* generic_topic_hooks
cdef MemoryAllocator* allocator
cdef public object logger
cdef public bint active
cdef public object engine
cdef int capacity
cdef uint64_t seq_id
cdef int c_publish(self, PyTopic topic, tuple args, dict kwargs,
bint block, int max_spin, double timeout) nogil
cdef PyMessagePayload c_get(self, bint block, int max_spin, double timeout) nogil
cdef void c_trigger(self, PyMessagePayload msg)
cdef void c_loop(self)
cpdef void register_hook(self, EventHook hook)
cpdef EventHook unregister_hook(self, PyTopic topic)
cpdef void register_handler(self, PyTopic topic, object handler, bint deduplicate=*)
cpdef void unregister_handler(self, PyTopic topic, object handler)
nogil Methods¶
Several performance-critical methods release the GIL:
c_publish(): Publishing can happen without GILc_get(): Queue operations don’t need GILc_loop(): Main event loop releases GIL while waiting
This allows other Python threads to run concurrently with the engine.
Performance Tips¶
Avoiding Python Overhead¶
When possible, use typed Cython code:
# Slower (Python)
topic = PyTopic("Market.Data.AAPL")
value = topic.value # Calls Python __get__
# Faster (Cython)
cdef PyTopic topic = PyTopic("Market.Data.AAPL")
cdef str value = topic.value() # Direct C call
Type Declarations¶
Declare types for best performance:
cdef EventEngine engine = EventEngine(capacity=8192)
cdef PyTopic topic = PyTopic("Test.Topic")
cdef PyMessagePayload msg
# Fast loop
cdef int i
for i in range(10000):
engine.put(topic, i) # Minimal overhead
Using Static Methods¶
The c_from_header static methods create Python wrappers around existing C structures efficiently:
cdef Topic* c_topic = topic_parse(b"My.Topic", 8)
cdef PyTopic py_topic = PyTopic.c_from_header(c_topic, owner=True)
Memory Management¶
Owner Flag¶
The owner flag determines memory management:
owner=True: Python object owns C memory, will free on__dealloc__owner=False: Python object is a view, C memory managed elsewhere
cdef PyTopic owned = PyTopic("Test") # owner=True
# Will call topic_free() in __dealloc__
cdef PyTopic view = PyTopic.c_from_header(some_topic, owner=False)
# Will NOT free some_topic
Reference Counting¶
For Python objects in C structures (e.g., args in MessagePayload):
# When setting args
Py_INCREF(args)
payload.header.args = <void*><PyObject*>args
# When clearing/deallocating
if payload.header.args:
Py_XDECREF(<object>payload.header.args)
payload.header.args = NULL
The Cython classes handle this automatically in their __init__/__dealloc__ methods.
Exception Handling¶
Cython methods can raise Python exceptions:
cdef PyTopic topic
try:
topic = PyTopic("Invalid..{") # May raise ValueError
except ValueError as e:
print(f"Parse error: {e}")
For nogil functions, exceptions are deferred:
cdef int result
with nogil:
result = engine.c_publish(topic, args, kwargs, True, 1000, 0.0)
if result != 0:
raise Full("Queue is full")
Building Cython Extensions¶
To build your own Cython extension using PyEventEngine, see the detailed instructions in Including in Your Cython Extension above.
Quick Example:
from setuptools import setup, Extension
from Cython.Build import cythonize
import event_engine
extensions = [
Extension(
"my_module",
sources=["my_module.pyx"],
include_dirs=[event_engine.get_include()],
extra_compile_args=["-O3"]
)
]
setup(
ext_modules=cythonize(extensions, compiler_directives={'language_level': 3})
)
my_module.pyx:
from event_engine.capi.c_topic cimport PyTopic
from event_engine.capi.c_event cimport PyMessagePayload
from event_engine.capi.c_engine cimport EventEngine
cpdef void my_function():
cdef EventEngine engine = EventEngine(capacity=1024)
cdef PyTopic topic = PyTopic("Test.Topic")
engine.start()
engine.put(topic, 123, block=True)
engine.stop()
See Also¶
C API Reference - Low-level C API
Python API Reference (CAPI) - High-level Python API
Cython documentation - General Cython usage
Source:
event_engine/capi/*.pyxandevent_engine/capi/*.pxd