Repository navigation
Open
Description
π€ AI text below π€
Tracking issue: review of Cython workflows we do and don't cover, as a checklist before tagging 1.0. Nothing below is a hard blocker β CYTHON_ARGS is a sufficient escape hatch for almost everything β but a 1.0 implies an API contract, so the main risk is documenting boundaries rather than missing capability.
Likely-intentional boundaries that should be documented
These aren't bugs, but users migrating from setuptools/cythonize will hit them:
# distutils:directives other thanlanguageare silently ignored.cythonizehonors# distutils: sources = ...,libraries = ...,include_dirs = ...,extra_compile_args = ...,define_macros = ...and applies them to the extension; we only parselanguage(_cython_compute_language). This is the right call β CMake owns build config β but it's the No 1 surprise coming from setuptools. One README sentence ("only thelanguagedirective is read; express libraries/includes/flags as CMake target properties") would help.- No
cython_add_module/ one-call helper. We transpile, then the user callspython_add_library. Classic scikit-buildUseCythonhadadd_cython_target. Decide consciously whether 1.0 ships a convenience wrapper or commits to the two-step idiom. - No
cythonize-style behavior generally (parallelism, dependency-graph resolution, directory globbing). We wrap the rawcythonCLI one file at a time. Worth a line.
Real gaps worth considering
- Pure-Python mode (
.pyinput). Cython compiles.pyfiles. Our code is filename-agnostic (NAME_WE->foo.c), so it probably works, but there's no test fixture and the README only mentions.pyx. Add a fixture, or document that it's.pyxonly. language_level. Not set by default, so Cython emits its "directive 'language_level' not set" warning unless the user adds-X language_level=3toCYTHON_ARGS. Worth showing in the README example so the common case is warning-free.--annotate(-a) HTML output isn't declared to CMake. Generated next to the.cbut not tracked (no clean, not inOUTPUT_VARIABLE). Minor.-I/ cimport include dirs only reachable viaCYTHON_ARGS. A dedicated keyword could be nice for multi-package layouts, but depfile + relative cimports cover most cases β probably leave it.
Polish (doc-only)
- Coverage (
-X linetrace=True+CYTHON_TRACEdefine) and profiling β work today viaCYTHON_ARGS+ target compile definitions; pure documentation. --embedstandalone executables β work viaCYTHON_ARGS; a short example would help.
Suggested 1.0 priority
- Document the "only
languagedirective is read" boundary (No 1) and addlanguage_levelto the example (No 5). - Decide on
.pysupport + add a fixture or a "pyx only" note (No 4). - Decide on a one-call helper vs. the two-step idiom (No 2).
Metadata
Metadata
Assignees
Labels
No labels
Activity
henryiii commentedon Jul 9, 2026
Ha, tried to get Fable to make an independent assessment, but it found this issue.
π€ AI text below π€
Good timing β issue #84 already tracks a pre-1.0 coverage checklist (distutils directives, one-call helper, pure-Python
.pymode,language_level, annotate output,-Idirs, coverage/embed docs). Setting those aside, here's what I'd add after readingFindCython.cmakeandUseCython.cmakefresh:Real functional gaps not in #84:
cdef public/cdef apiheaders are undeclared outputs. When a.pyxcontains public or api declarations, Cython writesfoo.h/foo_api.hnext to the.cfile.Cython_transpiledoesn't declare them asBYPRODUCTS/OUTPUT, doesn't return their paths, and other targets can't depend on them cleanly. This is the whole "embed Cython in a larger C/C++ app" workflow β arguably the workflow where CMake users most outnumber setuptools users. AGENERATE_HEADERS-style option (or auto-detection plus anOUTPUT_HEADER_VARIABLE) would cover it.No
MODULE_NAMEβ--module-namemapping. For a.pyxnested in a package, the raw CLI derives the module name from the filename, so__module__, pickling, and error messages get the unqualified name unless the source tree happens to have__init__files that Cython can walk.cythonizecomputes the dotted name for you; here the user has to know to pass--module-name pkg.sub.modviaCYTHON_ARGS. A first-class keyword is cheap and self-documenting. Yourmultiple_packagesfixture would be a natural place to assert on this.Cython::Cythonisn't actually used byUseCython. The transpile command readsCYTHON_EXECUTABLE(or falls back topython -m cython) but never references the imported target, and the custom command has no dependency on the cython binary β upgrading Cython doesn't trigger re-transpilation. Using$<TARGET_FILE:Cython::Cython>in the command (when the target exists) would fix staleness and let users point the target elsewhere. Related:find_package(Cython)isn't required beforeinclude(UseCython), which is flexible, but thepython -m cythonfallback silently bypasses any version check the user did infind_package.Inter-package
.pxdstory. Packages that want others tocimportthem (the NumPy pattern) need to install their.pxdfiles and consumers need the right-I. There's no helper for either direction β no "install these pxd next to the module" convenience, and Coverage review before 1.0: Cython workflows we do/don't supportΒ #84 punts on the-Ikeyword. Even doc-only guidance ("install.pxdviainstall(FILES ...), consume viaCYTHON_ARGS -I") would help; aINCLUDE_DIRECTORIESkeyword would help more, sinceCYTHON_ARGSis a single escape hatch that collides once users need it for two things.Documentation gaps (beyond #84's list):
NumPy. The single most common Cython pattern is
cimport numpyβ needingfind_package(Python COMPONENTS NumPy), linkingPython::NumPy, andNPY_NO_DEPRECATED_API. A README/example fixture showing this would preempt a lot of first-contact friction, and the C++ fixture shows you're already using fixtures as documentation.Free-threaded Python. Cython 3.1+ builds for 3.13t/3.14t; nothing in this package blocks it, but a sentence confirming it works (and a CI job on
3.13t) would be a cheap trust signal, since scikit-build-core users are exactly the crowd trying it.What "no
cythonize" means for compiler directives.cythonizereads[tool.cython]-style defaults and directive stacking; here everything is per-callCYTHON_ARGS. A short "recommended pattern: set theCYTHON_ARGScache variable once at top level" note would make the existing fallback (UseCython.cmake:200) discoverable β it's currently only visible by reading the source.If I were prioritizing for 1.0: items 2 and 3 are small, contained, and API-shaping (better before the contract freezes than after); item 1 is the biggest genuine capability gap; 4β7 can land any time since they're additive. Want me to add any of these to issue #84, or file them separately?
test: add pure-Python mode (.py) fixture (#89)
feat: declare cdef public/api headers as outputs
feat: declare cdef public/api headers as outputs (#93)
docs: note language_level and the distutils directive boundary (#94)