Cython build hooks for hatch
Project description
hatch-cython
| CI/CD | |
| Package | |
| Meta |
Table of Contents
Usage
The build hook plugin name is cython. It can be configured either globally (applies to all build targets) or specifically for the wheel target.
Installation
Add hatch-cython to your build dependencies in pyproject.toml:
[build-system]
requires = ["hatchling", "hatch-cython", "Cython", "setuptools"]
build-backend = "hatchling.build"
Important:
Cythonandsetuptoolsmust be included inbuild-system.requiresbecause the build hook imports Cython modules at load time, before hook-specific dependencies are resolved.
Build System Requirements for Library Includes
When using include_numpy, include_pyarrow, include_pythran, or custom include_{package} options, those packages must also be added to build-system.requires. This is because hatch-cython imports these packages at hook initialization to resolve their include paths.
| Option | Required in build-system.requires |
|---|---|
include_numpy = true |
"numpy" |
include_pyarrow = true |
"pyarrow" |
include_pythran = true |
"pythran" |
include_{pkg} = {...} |
The package specified in pkg |
Example with NumPy:
[build-system]
requires = ["hatchling", "hatch-cython", "Cython", "setuptools", "numpy"]
build-backend = "hatchling.build"
[tool.hatch.build.targets.wheel.hooks.cython]
dependencies = ["hatch-cython"]
[tool.hatch.build.targets.wheel.hooks.cython.options]
include_numpy = true
define_macros = [["NPY_NO_DEPRECATED_API", "NPY_1_7_API_VERSION"]]
Example with PyArrow:
[build-system]
requires = ["hatchling", "hatch-cython", "Cython", "setuptools", "pyarrow"]
build-backend = "hatchling.build"
[tool.hatch.build.targets.wheel.hooks.cython]
dependencies = ["hatch-cython"]
[tool.hatch.build.targets.wheel.hooks.cython.options]
include_pyarrow = true
Hook Configuration Locations
You can define the hook in several locations depending on your needs:
| Location | Scope | File |
|---|---|---|
[tool.hatch.build.hooks.cython] |
Global (all targets) | pyproject.toml |
[tool.hatch.build.targets.wheel.hooks.cython] |
Wheel only | pyproject.toml |
[build.hooks.cython] |
Global (all targets) | hatch.toml |
[build.targets.wheel.hooks.cython] |
Wheel only | hatch.toml |
Basic Example (pyproject.toml)
[build-system]
requires = ["hatchling", "hatch-cython", "Cython", "setuptools"]
build-backend = "hatchling.build"
[tool.hatch.build.targets.wheel.hooks.cython]
dependencies = ["hatch-cython"]
[tool.hatch.build.targets.wheel.hooks.cython.options]
# Include directories for .h or .cpp files
includes = []
# Include headers from common scientific packages
include_numpy = false
include_pyarrow = false
# Custom library integration (see "Custom Library Includes" section)
include_somelib = {
pkg = "somelib", # module name (must be in dependencies)
include = "gets_include", # somelib.gets_include() -> str | list[str]
libraries = "gets_libraries", # somelib.gets_libraries() -> list[str]
library_dirs = "gets_library_dirs", # somelib.gets_library_dirs() -> list[str]
required_call = "some_setup_op" # somelib.some_setup_op() called before build
}
# Cython compiler directives
directives = { boundscheck = false, nonecheck = false, language_level = 3, binding = true }
# Additional keyword arguments passed to setuptools.Extension()
compile_kwargs = { }
Basic Example (hatch.toml)
[build.targets.wheel.hooks.cython]
dependencies = ["hatch-cython"]
[build.targets.wheel.hooks.cython.options]
directives = { boundscheck = false, nonecheck = false, language_level = 3, binding = true }
compile_args = ["-O3"]
includes = []
include_numpy = false
# Example: equivalent to include_pyarrow = true
include_somelib = { pkg = "pyarrow", include = "get_include", libraries = "get_libraries", library_dirs = "get_library_dirs", required_call = "create_library_symlinks" }
define_macros = [
["NPY_NO_DEPRECATED_API", "NPY_1_7_API_VERSION"],
]
Configuration Options
All options are specified under [tool.hatch.build.targets.wheel.hooks.cython.options] (pyproject.toml) or [build.targets.wheel.hooks.cython.options] (hatch.toml).
| Field | Type | Description |
|---|---|---|
src |
str | None |
The package directory name if it differs from the project name. For example, if your project is package-a but the source is in src/package_a_lib/, set src = "package_a_lib". |
directives |
dict |
Cython compiler directives passed to cythonize(). See compiler-directives for available options. |
compile_args |
list[str | PlatformArg] |
Compiler arguments passed to the C/C++ compiler. Can be simple strings or platform-specific objects (see Platform-Specific Arguments). See extensions for available arguments. |
extra_link_args |
list[str | PlatformArg] |
Linker arguments. Same format as compile_args. See extensions for available arguments. |
cythonize_kwargs |
dict |
Additional keyword arguments passed directly to Cython's cythonize() function. Example: { annotate = true, nthreads = 4 } |
env |
list[EnvArg] |
Environment variables to set during compilation. Format: { env = "VAR", arg = "VALUE", platforms = [...], arch = [...] }. For flags like CFLAGS, LDFLAGS, CPPFLAGS, LDSHARED, CCSHARED, ARFLAGS, values are merged with existing env vars (separated by space). Use merges = true to enable merging for custom variables. |
includes |
list[str] |
List of directories to add to the include path for the C/C++ compiler. |
include_{package} |
dict |
Custom library integration. Format: { pkg = str, include = str, libraries = str | None, library_dirs = str | None, required_call = str | None }. Each field (except pkg) is an attribute name on the package that returns str | list[str] or is a callable returning the same. |
include_numpy |
bool |
If true, automatically includes NumPy headers. NumPy must be in your build dependencies. Default: false |
include_pyarrow |
bool |
If true, automatically includes PyArrow headers and libraries. PyArrow must be in your build dependencies. Default: false |
include_pythran |
bool |
If true, automatically includes Pythran headers. Pythran must be in your build dependencies. Default: false |
parallel |
bool |
If true, adds OpenMP headers and flags for parallel compilation. macOS users: Requires Homebrew's LLVM (brew install llvm) instead of Apple's LLVM to use -fopenmp. Default: false |
compiler |
str |
Compiler identifier. If set to "msvc" (Microsoft Visual Studio), uses /openmp instead of -fopenmp when parallel = true. |
compile_py |
bool |
If true, .py files are compiled to Cython extensions alongside .pyx files. This allows you to write standard Python that gets compiled. Use files.exclude to skip specific files. Default: true |
define_macros |
list[list[str]] |
C preprocessor macro definitions. Each entry is a list: ["KEY"] for #define KEY or ["KEY", "VALUE"] for #define KEY VALUE. Example: [["NPY_NO_DEPRECATED_API", "NPY_1_7_API_VERSION"]] |
**kwargs |
any |
Additional keyword arguments are passed directly to setuptools.Extension(). See extensions for available options. |
Platform-Specific Arguments
The compile_args and extra_link_args fields support platform-specific configuration using objects with the following fields:
| Field | Type | Description |
|---|---|---|
arg |
str |
The compiler/linker argument to add. |
platforms |
str | list[str] |
Platform(s) where this arg applies. Values: "darwin", "linux", "windows", "freebsd", "*" (all). Default: "*" |
arch |
str | list[str] |
Architecture(s) where this arg applies. Values: "x86_64", "arm64", "aarch64", "anon" (empty string), "*" (all). Default: "*" |
depends_path |
bool |
If true, the path specified in arg (after any flag prefix like -I or -L) must exist for this argument to be included. Useful for optional library paths that may not be present on all systems. Default: false |
marker |
str |
A PEP 508 environment marker expression. The argument is only included if the marker evaluates to true. |
Note: The
"anon"architecture value matches systems that return an empty string forplatform.machine(), which can occur in some containerized or emulated environments.
PEP 508 Markers
The marker field accepts standard PEP 508 environment markers for conditional compilation. Common variables include:
python_version- Python version (e.g.,"3.10")python_full_version- Full Python version (e.g.,"3.10.12")os_name- OS name ("posix","nt","java")sys_platform- System platform ("linux","darwin","win32")platform_machine- Machine architecture ("x86_64","arm64")platform_system- System name ("Linux","Darwin","Windows")implementation_name- Python implementation ("cpython","pypy")
Examples:
compile_args = [
# Simple string argument (applies to all platforms)
"-v",
# Platform-specific
{ platforms = ["linux", "darwin"], arg = "-Wcpp" },
# Platform and architecture specific
{ platforms = "darwin", arch = "x86_64", arg = "-arch x86_64" },
{ platforms = "darwin", arch = "arm64", arg = "-arch arm64" },
# Only include if path exists (useful for optional dependencies)
{ platforms = "darwin", arg = "-I/opt/homebrew/include", depends_path = true },
# Only for Python 3.10 and earlier
{ arg = "-I/legacy/include", marker = "python_version <= '3.10'" },
# Only for CPython (not PyPy)
{ arg = "-DCPYTHON_ONLY", marker = "implementation_name == 'cpython'" },
# Combine platform, arch, path check, and marker
{ platforms = "darwin", arch = "x86_64", arg = "-I/usr/local/opt/llvm/include", depends_path = true, marker = "python_version < '3.11'" },
# Complex marker expressions
{ arg = "-DOLD_ABI", marker = "python_version < '3.9' or implementation_name == 'pypy'" },
]
Files
The files section controls which files are included or excluded from compilation.
[build.targets.wheel.hooks.cython.options.files]
exclude = [
# Anything matching this pattern is ignored by cython
"*/no_compile/*",
# Note: "*" in patterns is converted to "([^\s]*)" (non-whitespace regex)
# For a literal regex asterisk, use the full regex syntax:
"([^.]\\*).(pyd$|pytempl$)",
# Platform-specific exclusions (exclude on all OTHER platforms)
{ matches = "*/windows", platforms = ["linux", "darwin", "freebsd"] },
{ matches = "*/darwin", platforms = ["linux", "freebsd", "windows"] },
{ matches = "*/linux", platforms = ["darwin", "freebsd", "windows"] },
{ matches = "*/freebsd", platforms = ["linux", "darwin", "windows"] },
]
# Rename modules in the final build
aliases = {"mylib._internal" = "mylib.public_name"}
Explicit Build Targets
By default, hatch-cython compiles all .pyx files (and .py files if compile_py = true). To compile only specific files, use options.files.targets:
[build.targets.wheel.hooks.cython.options.files]
targets = [
# String pattern
"*/compile.py",
# Platform-specific targets
{ matches = "*/windows", platforms = ["windows"] },
{ matches = "*/posix", platforms = ["darwin", "freebsd", "linux"] },
]
When targets is specified, only matching files are compiled. This also implicitly enables compilation of .py, .c, .cpp, and .cc files that match the patterns.
Source Distributions
Source distributions (sdist) work normally with hatch-cython. When building an sdist:
- Hatch automatically installs
hatch-cythonas specified in your build dependencies - Platform-specific compile arguments are evaluated but compilation is skipped
- Template files (
.pyx.in,.pyi.in, etc.) can be processed and included - Generated
.cand.cppfiles can optionally be included in the sdist
Note: If
hatch-cythonruns during a non-wheel build target, the extension compilation is skipped. The generated intermediate files (.c,.cpp) may still be included if desired, though the compile arguments will differ per-platform at install time.
Templating
hatch-cython supports Cython Tempita templates for generating code at build time. Any file with a .in suffix is processed as a template:
Supported template extensions:
.pyx.in→.pyx.pxd.in→.pxd.pyi.in→.pyi.py.in→.py.c.in→.c.cpp.in→.cpp
Template Example
Source file (templated.pyx.in):
{{for typ in supported}}
cpdef {{typ}} add_{{typ}}({{typ}} a, {{typ}} b):
return a + b
{{endfor}}
With configuration:
[build.targets.wheel.hooks.cython.options.templates]
global = { supported = ["int", "float", "double"] }
Generated output (templated.pyx):
cpdef int add_int(int a, int b):
return a + b
cpdef float add_float(float a, float b):
return a + b
cpdef double add_double(double a, double b):
return a + b
Module Aliasing with Templates
Templates can be combined with aliases to rename modules:
[build.targets.wheel.hooks.cython.options.files]
aliases = {"mylib._templated" = "mylib.templated"}
Build process:
- Source:
_templated.pyx.in,templated.pyi.in - After template processing:
_templated.pyx,templated.pyi - Final module:
mylib.templated
Template Arguments
Templates receive keyword arguments based on matching rules. Configure these in the templates section:
[build.targets.wheel.hooks.cython.options.templates]
# Index defines which keyword sets apply to which files
index = [
{ keyword = "global", matches = "*" },
{ keyword = "mac_types", matches = "templated.*.in", platforms = ["darwin"] },
{ keyword = "win_types", matches = "templated.*.in", platforms = ["windows"] },
{ keyword = "win_x64_types", matches = "templated.*.in", platforms = ["windows"], arch = ["x86_64"] },
{ keyword = "py38_compat", matches = "*.in", marker = "python_version == '3.8'" },
]
# Keyword argument sets
global = { supported = ["int"] }
mac_types = { supported = ["int", "float"] }
win_types = { supported = ["int", "float", "complex"] }
win_x64_types = { supported = ["int", "float", "double", "complex"] }
py38_compat = { use_legacy_api = true }
Matching behavior:
globalis always evaluated first and can be overridden by other matches- Multiple matching keywords are merged in FIFO order (first defined takes precedence for conflicts)
- Each index entry supports
platforms,arch, andmarkerfor conditional matching
Example merge:
# Given matches: global, mac_types
# global = { supported = ["int"], extra = "value" }
# mac_types = { supported = ["int", "float"] }
# Result: { supported = ["int", "float"], extra = "value" }
See the test_libraries/src_structure directory for complete working examples.
Notes
macOS
-
Users with Homebrew installed will automatically have
brew --prefixlibrary and include paths added during compilation -
GitHub Actions runners now use Apple Silicon (M1) for macOS. If using M1 runners, disable
macos-max-compat:# hatch.toml [build.targets.wheel] macos-max-compat = false
OpenMP on macOS
To use parallel = true on macOS, you need Homebrew's LLVM instead of Apple's Clang:
brew install llvm libomp
The plugin automatically detects Homebrew's LLVM and adds the appropriate include/library paths.
Development
Requirements
- C/C++ compiler (gcc, clang, or MSVC)
- Python 3.8 - 3.13
- Mise
Development Scripts
| Command | Description |
|---|---|
mise install |
Setup project environment |
hatch run cov |
Run tests with coverage |
task example |
Test with src_structure example |
task simple-structure |
Test with simple_structure example |
task precommit |
Run pre-commit hooks |
License
hatch-cython is distributed under the terms of the MIT license.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file hatch_cython-0.6.0.tar.gz.
File metadata
- Download URL: hatch_cython-0.6.0.tar.gz
- Upload date:
- Size: 39.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b6480b417b919eb7138e1665e119db570585b486facc623ea62cb3026418ac4c
|
|
| MD5 |
e6fc7eca359c98a0dcbfa7e8f5e26db6
|
|
| BLAKE2b-256 |
d232d13b765c225535d853ea1a9a94a2aa020bf4313707e359e4ffda09f00288
|
Provenance
The following attestation bundles were made for hatch_cython-0.6.0.tar.gz:
Publisher:
build.yaml on joshua-auchincloss/hatch-cython
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
hatch_cython-0.6.0.tar.gz -
Subject digest:
b6480b417b919eb7138e1665e119db570585b486facc623ea62cb3026418ac4c - Sigstore transparency entry: 771732267
- Sigstore integration time:
-
Permalink:
joshua-auchincloss/hatch-cython@dba354076872d2b5c1c55ae540f6b521b2efbdad -
Branch / Tag:
refs/tags/v0.6.0 - Owner: https://github.com/joshua-auchincloss
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
build.yaml@dba354076872d2b5c1c55ae540f6b521b2efbdad -
Trigger Event:
push
-
Statement type:
File details
Details for the file hatch_cython-0.6.0-py3-none-any.whl.
File metadata
- Download URL: hatch_cython-0.6.0-py3-none-any.whl
- Upload date:
- Size: 25.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ca465d132626534dc24260f191005808bac3ff00519f0085d8b78f77d8587037
|
|
| MD5 |
d3158286b844f0a201eb50632eef8feb
|
|
| BLAKE2b-256 |
c63979044cffc446a361165043764106d00ff1348e7692cb2d02cca64ec185aa
|
Provenance
The following attestation bundles were made for hatch_cython-0.6.0-py3-none-any.whl:
Publisher:
build.yaml on joshua-auchincloss/hatch-cython
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
hatch_cython-0.6.0-py3-none-any.whl -
Subject digest:
ca465d132626534dc24260f191005808bac3ff00519f0085d8b78f77d8587037 - Sigstore transparency entry: 771732268
- Sigstore integration time:
-
Permalink:
joshua-auchincloss/hatch-cython@dba354076872d2b5c1c55ae540f6b521b2efbdad -
Branch / Tag:
refs/tags/v0.6.0 - Owner: https://github.com/joshua-auchincloss
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
build.yaml@dba354076872d2b5c1c55ae540f6b521b2efbdad -
Trigger Event:
push
-
Statement type: