Skip to main content
Learn how to build, test, debug, and contribute Cython extensions, including OpenMP parallel processing and the Python fallback pattern.

Overview

mySpellChecker uses Cython for performance-critical operations:

Prerequisites

Required Tools

Verify Installation

Project Structure

src/myspellchecker
text
normalize.py
normalize_c.pyx
normalize_c.pxd
algorithms
viterbi.py
viterbi.pyx
distance
edit_distance_c.pyx
data_pipeline
batch_processor.py
batch_processor.pyx

File Types

Building Cython Extensions

Development Build

Build with Debug Symbols

Build Options

The setup.py automatically detects:
  • OpenMP availability (macOS requires brew install libomp)
  • C++ compiler capabilities
  • Platform-specific flags

Writing Cython Code

Basic Pattern

Creating .pxd Files

Cross-Module Imports

Import Pattern

The core normalize.py module imports directly from the Cython extension without pure Python fallbacks:
Note: Unlike some other modules that use try/except ImportError fallbacks, normalize.py requires the Cython extension. For systems without a C++ compiler, install from a pre-built wheel.

OpenMP Integration

For parallel processing (used in batch_processor.pyx):

macOS OpenMP Setup

Testing Cython Code

Unit Tests

Benchmark Tests

Debugging

GDB/LLDB

Memory Profiling

Common Pitfalls

1. Forgetting to Rebuild

After modifying .pyx files, always rebuild:

2. GIL Management

3. Memory Management

4. Type Declarations

Performance Tips

  1. Use cdef for internal functions - Not callable from Python, but faster
  2. Use typed memoryviews - Faster than NumPy arrays in loops
  3. Minimize GIL acquisition - Use nogil where possible
  4. Use cpdef for hybrid - Callable from Python and fast from Cython
  5. Profile before optimizing - Use cython -a to see Python interactions

Annotation Output

Contributing

When contributing Cython code:
  1. Include both .pyx and .py wrapper
  2. Add .pxd file if cross-module imports needed
  3. Write tests that work with both backends (where applicable)
  4. Document performance characteristics
  5. Test on multiple platforms if possible

Cython Integration Patterns

Different modules use different integration patterns depending on whether a pure Python fallback is needed: Pattern 1: Required Cython (no Python fallback) normalize.py imports directly from normalize_c.pyx without a try/except ImportError guard. The Cython extension is required — if it is not compiled, the import fails at runtime. This pattern is used when the Cython code is the only implementation.
Pattern 2: Optional Cython with Python fallback viterbi.py tries to import the Cython extension and falls back to a pure Python implementation if it is not available. The _HAS_CYTHON_VITERBI flag controls which backend runs at runtime.
When writing tests, be aware of this distinction:
  • For required Cython modules (like normalize), tests can import the Cython functions directly. If the extension is not compiled, the test will fail at import time, which is expected.
  • For optional Cython modules (like viterbi), tests should verify both backends work. Check _HAS_CYTHON_VITERBI to report which backend is active, but the tests should pass either way.