c2py23 0.3.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- c2py23-0.3.0/LICENSE +21 -0
- c2py23-0.3.0/MANIFEST.in +40 -0
- c2py23-0.3.0/PKG-INFO +341 -0
- c2py23-0.3.0/README.md +316 -0
- c2py23-0.3.0/c2py23/__init__.py +3 -0
- c2py23-0.3.0/c2py23/c2py_loader.py +126 -0
- c2py23-0.3.0/c2py23/cli.py +277 -0
- c2py23-0.3.0/c2py23/generator.py +2003 -0
- c2py23-0.3.0/c2py23/invariant_checker.py +278 -0
- c2py23-0.3.0/c2py23/parser.py +1457 -0
- c2py23-0.3.0/c2py23/perf.py +265 -0
- c2py23-0.3.0/c2py23/runtime/c2py_amd64.h +49 -0
- c2py23-0.3.0/c2py23/runtime/c2py_arm64.h +34 -0
- c2py23-0.3.0/c2py23/runtime/c2py_ppc64.h +27 -0
- c2py23-0.3.0/c2py23/runtime/c2py_runtime.c +847 -0
- c2py23-0.3.0/c2py23/runtime/c2py_runtime.h +760 -0
- c2py23-0.3.0/c2py23.egg-info/PKG-INFO +341 -0
- c2py23-0.3.0/c2py23.egg-info/SOURCES.txt +65 -0
- c2py23-0.3.0/c2py23.egg-info/dependency_links.txt +1 -0
- c2py23-0.3.0/c2py23.egg-info/entry_points.txt +2 -0
- c2py23-0.3.0/c2py23.egg-info/requires.txt +6 -0
- c2py23-0.3.0/c2py23.egg-info/top_level.txt +1 -0
- c2py23-0.3.0/docs/specification.md +1432 -0
- c2py23-0.3.0/docs/user_guide.md +356 -0
- c2py23-0.3.0/examples/cmake_demo/CMakeLists.txt +48 -0
- c2py23-0.3.0/examples/cmake_demo/arraysum/__init__.py +14 -0
- c2py23-0.3.0/examples/cmake_demo/arraysum.c +10 -0
- c2py23-0.3.0/examples/cmake_demo/arraysum.c2py +14 -0
- c2py23-0.3.0/examples/cmake_demo/build.sh +27 -0
- c2py23-0.3.0/examples/cmake_demo/setup.py +35 -0
- c2py23-0.3.0/examples/kissfft_wrap/example.py +26 -0
- c2py23-0.3.0/examples/kissfft_wrap/kissfft.c2py +38 -0
- c2py23-0.3.0/examples/kissfft_wrap/kissfft_thin.c +19 -0
- c2py23-0.3.0/examples/kissfft_wrap/kissfftmod_wrapper.c +558 -0
- c2py23-0.3.0/examples/lz4_wrap/example.py +29 -0
- c2py23-0.3.0/examples/lz4_wrap/lz4.c2py +35 -0
- c2py23-0.3.0/examples/lz4_wrap/lz4_thin.c +10 -0
- c2py23-0.3.0/examples/lz4_wrap/lz4mod_wrapper.c +555 -0
- c2py23-0.3.0/examples/meson_demo/arraysum/__init__.py +14 -0
- c2py23-0.3.0/examples/meson_demo/arraysum.c +10 -0
- c2py23-0.3.0/examples/meson_demo/arraysum.c2py +14 -0
- c2py23-0.3.0/examples/meson_demo/build.sh +36 -0
- c2py23-0.3.0/examples/meson_demo/setup.py +35 -0
- c2py23-0.3.0/examples/simd_dispatch/CMakeLists.txt +68 -0
- c2py23-0.3.0/examples/simd_dispatch/MANIFEST.in +4 -0
- c2py23-0.3.0/examples/simd_dispatch/Makefile +54 -0
- c2py23-0.3.0/examples/simd_dispatch/_polysimd_wrapper.c +735 -0
- c2py23-0.3.0/examples/simd_dispatch/build.sh +32 -0
- c2py23-0.3.0/examples/simd_dispatch/poly_kernel.c +41 -0
- c2py23-0.3.0/examples/simd_dispatch/polysimd/__init__.py +16 -0
- c2py23-0.3.0/examples/simd_dispatch/polysimd.c2py +29 -0
- c2py23-0.3.0/examples/simd_dispatch/setup.py +37 -0
- c2py23-0.3.0/examples/simd_dispatch/test_polysimd.py +125 -0
- c2py23-0.3.0/examples/threading_bench/Makefile +16 -0
- c2py23-0.3.0/examples/threading_bench/bench_mc_pi.py +177 -0
- c2py23-0.3.0/examples/threading_bench/mc_pi.c +75 -0
- c2py23-0.3.0/examples/threading_bench/mc_pi.c2py +20 -0
- c2py23-0.3.0/examples/threading_bench/mcpimod_wrapper.c +259 -0
- c2py23-0.3.0/examples/wheel_demo/MANIFEST.in +4 -0
- c2py23-0.3.0/examples/wheel_demo/arraysum/__init__.py +17 -0
- c2py23-0.3.0/examples/wheel_demo/arraysum.c +10 -0
- c2py23-0.3.0/examples/wheel_demo/arraysum.c2py +14 -0
- c2py23-0.3.0/examples/wheel_demo/build.sh +100 -0
- c2py23-0.3.0/examples/wheel_demo/setup.py +44 -0
- c2py23-0.3.0/pyproject.toml +36 -0
- c2py23-0.3.0/setup.cfg +4 -0
- c2py23-0.3.0/setup.py +31 -0
c2py23-0.3.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 jonwright and DeepSeek V4 Pro
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
c2py23-0.3.0/MANIFEST.in
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
include LICENSE
|
|
2
|
+
include README.md
|
|
3
|
+
include pyproject.toml
|
|
4
|
+
include setup.py
|
|
5
|
+
|
|
6
|
+
recursive-include c2py23 *.py *.h *.c
|
|
7
|
+
prune c2py23/__pycache__
|
|
8
|
+
|
|
9
|
+
recursive-include docs *.md
|
|
10
|
+
|
|
11
|
+
# wrapper examples (not the full submodule sources)
|
|
12
|
+
recursive-include examples/kissfft_wrap *.py *.c *.c2py *.h
|
|
13
|
+
recursive-include examples/lz4_wrap *.py *.c *.c2py *.h
|
|
14
|
+
recursive-include examples/simd_dispatch *.py *.c *.c2py *.h Makefile CMakeLists.txt build.sh MANIFEST.in setup.py
|
|
15
|
+
recursive-include examples/simd_dispatch/polysimd *.py
|
|
16
|
+
recursive-include examples/threading_bench *.py *.c *.c2py Makefile
|
|
17
|
+
recursive-include examples/wheel_demo *.py *.c *.c2py *.h MANIFEST.in build.sh setup.py
|
|
18
|
+
recursive-include examples/wheel_demo/arraysum *.py
|
|
19
|
+
recursive-include examples/cmake_demo *.py *.c *.c2py *.h CMakeLists.txt build.sh setup.py
|
|
20
|
+
recursive-include examples/cmake_demo/arraysum *.py
|
|
21
|
+
recursive-include examples/meson_demo *.py *.c *.c2py *.h build.sh setup.py
|
|
22
|
+
recursive-include examples/meson_demo/arraysum *.py
|
|
23
|
+
|
|
24
|
+
# build artifacts in examples
|
|
25
|
+
prune examples/*/build
|
|
26
|
+
prune examples/*/builddir
|
|
27
|
+
prune examples/*/*.egg-info
|
|
28
|
+
|
|
29
|
+
# exclude full submodule sources (only wrapper dirs needed)
|
|
30
|
+
prune examples/kissfft
|
|
31
|
+
prune examples/lz4
|
|
32
|
+
|
|
33
|
+
prune audit/20260620_resolved
|
|
34
|
+
prune tests
|
|
35
|
+
prune tests/test_venv_3.12
|
|
36
|
+
prune tests/test_workspace
|
|
37
|
+
|
|
38
|
+
global-exclude *.pyc
|
|
39
|
+
global-exclude __pycache__
|
|
40
|
+
global-exclude .DS_Store
|
c2py23-0.3.0/PKG-INFO
ADDED
|
@@ -0,0 +1,341 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: c2py23
|
|
3
|
+
Version: 0.3.0
|
|
4
|
+
Summary: Wrap C99 code to Python via the buffer protocol
|
|
5
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
6
|
+
Classifier: Programming Language :: Python :: 2
|
|
7
|
+
Classifier: Programming Language :: Python :: 2.7
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: Programming Language :: Python :: 3.6
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.7
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.15
|
|
19
|
+
Requires-Python: >=2.7
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
Requires-Dist: PyYAML>=5.1; python_version >= "3"
|
|
22
|
+
Requires-Dist: PyYAML<6,>=3.10; python_version == "2.7"
|
|
23
|
+
Dynamic: description
|
|
24
|
+
Dynamic: description-content-type
|
|
25
|
+
|
|
26
|
+
# c2py23
|
|
27
|
+
|
|
28
|
+
Wrap a strict subset of C99 code as Python C extensions via the buffer protocol.
|
|
29
|
+
One compiled `.so` works on Python 2.7 through 3.15 with no recompilation.
|
|
30
|
+
Now supports Linux x86_64 (gcc) and Windows x64 (MSVC / MinGW).
|
|
31
|
+
|
|
32
|
+
## Install
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
cd c2py23
|
|
36
|
+
pip install -e .
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Requires PyYAML and a C99 compiler (gcc, clang, or MSVC on Windows).
|
|
40
|
+
|
|
41
|
+
## Quick Start
|
|
42
|
+
|
|
43
|
+
Create a C function:
|
|
44
|
+
|
|
45
|
+
```c
|
|
46
|
+
/* arraysum.c */
|
|
47
|
+
int array_sum(const double *a, const double *b, double *result, int n) {
|
|
48
|
+
for (int i = 0; i < n; i++) result[i] = a[i] + b[i];
|
|
49
|
+
return n;
|
|
50
|
+
}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Write the interface file:
|
|
54
|
+
|
|
55
|
+
```yaml
|
|
56
|
+
# arraysum.c2py
|
|
57
|
+
module: arraysum
|
|
58
|
+
source: [arraysum.c]
|
|
59
|
+
|
|
60
|
+
functions:
|
|
61
|
+
- py_sig: "array_sum(a: buffer, b: buffer, result: buffer) -> int"
|
|
62
|
+
checks:
|
|
63
|
+
- "a.format == 'd'"
|
|
64
|
+
- "b.format == 'd'"
|
|
65
|
+
- "result.format == 'd'"
|
|
66
|
+
- "a.n == b.n"
|
|
67
|
+
- "a.n == result.n"
|
|
68
|
+
c_overloads:
|
|
69
|
+
- sig: "array_sum(const double *a, const double *b, double *result, int n) -> int"
|
|
70
|
+
map: {a: "a.ptr", b: "b.ptr", result: "result.ptr", n: "a.n"}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Build and use:
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
c2py23 build arraysum.c2py # produces arraysum.so
|
|
77
|
+
|
|
78
|
+
python3 -c "
|
|
79
|
+
import ctypes, sys
|
|
80
|
+
sys.path.insert(0, '.')
|
|
81
|
+
import arraysum
|
|
82
|
+
a = (ctypes.c_double * 4)(1, 2, 3, 4)
|
|
83
|
+
b = (ctypes.c_double * 4)(5, 6, 7, 8)
|
|
84
|
+
r = (ctypes.c_double * 4)(0, 0, 0, 0)
|
|
85
|
+
n = arraysum.array_sum(a, b, r)
|
|
86
|
+
print(n, list(r)) # 4 [6.0, 8.0, 10.0, 12.0]
|
|
87
|
+
"
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### Shape Dispatch (AoS vs SoA)
|
|
91
|
+
|
|
92
|
+
The same Python function can dispatch to different C code based on buffer
|
|
93
|
+
shape. Below, a `(n,3)` layout calls `transform_aos` (array-of-structs),
|
|
94
|
+
and a `(3,n)` layout calls `transform_soa` (struct-of-arrays) -- same raw
|
|
95
|
+
pointer, zero copies, different interpretation:
|
|
96
|
+
|
|
97
|
+
```c
|
|
98
|
+
/* transform.c -- AoS vs SoA dispatch */
|
|
99
|
+
#include <stdint.h>
|
|
100
|
+
|
|
101
|
+
void transform_aos(double *points, intptr_t n, double *out) {
|
|
102
|
+
/* points[n][3] -- array of structs */
|
|
103
|
+
int i;
|
|
104
|
+
for (i = 0; i < n; i++) {
|
|
105
|
+
out[i*3+0] = points[i*3+0] * 2.0;
|
|
106
|
+
out[i*3+1] = points[i*3+1] * 2.0;
|
|
107
|
+
out[i*3+2] = points[i*3+2] * 2.0;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
void transform_soa(double *points, intptr_t n, double *out) {
|
|
112
|
+
/* points[3][n] -- struct of arrays */
|
|
113
|
+
int i;
|
|
114
|
+
for (i = 0; i < n; i++) {
|
|
115
|
+
out[0*n + i] = points[0*n + i] * 2.0;
|
|
116
|
+
out[1*n + i] = points[1*n + i] * 2.0;
|
|
117
|
+
out[2*n + i] = points[2*n + i] * 2.0;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
Interface file:
|
|
123
|
+
|
|
124
|
+
```yaml
|
|
125
|
+
# transform.c2py
|
|
126
|
+
module: xfrm
|
|
127
|
+
source: [transform.c]
|
|
128
|
+
timing: true
|
|
129
|
+
|
|
130
|
+
functions:
|
|
131
|
+
- py_sig: "transform(points: buffer, out: buffer) -> void"
|
|
132
|
+
checks:
|
|
133
|
+
- "points.format == 'd'"
|
|
134
|
+
- "out.format == 'd'"
|
|
135
|
+
- "out.n == points.n"
|
|
136
|
+
- "points.ndim == 2"
|
|
137
|
+
- "points.slow_axis == 0"
|
|
138
|
+
c_overloads:
|
|
139
|
+
- sig: "transform_aos(double *points, intptr_t n, double *out)"
|
|
140
|
+
map: {points: "points.ptr", n: "points.shape[0]", out: "out.ptr"}
|
|
141
|
+
when: "points.shape[1] == 3"
|
|
142
|
+
- sig: "transform_soa(double *points, intptr_t n, double *out)"
|
|
143
|
+
map: {points: "points.ptr", n: "points.shape[1]", out: "out.ptr"}
|
|
144
|
+
when: "points.shape[0] == 3"
|
|
145
|
+
default_raise: "ValueError: expected [N,3] or [3,N] buffer"
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
Usage:
|
|
149
|
+
|
|
150
|
+
```python
|
|
151
|
+
import ctypes
|
|
152
|
+
from ctypes import c_double
|
|
153
|
+
|
|
154
|
+
aos = (c_double * 12)(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12)
|
|
155
|
+
out = (c_double * 12)()
|
|
156
|
+
mv_aos = memoryview(aos).cast('B').cast('d', [4, 3])
|
|
157
|
+
mv_out = memoryview(out).cast('B').cast('d', [4, 3])
|
|
158
|
+
xfrm.transform(mv_aos, mv_out) # shape[1]==3 -> transform_aos, n=shape[0]=4
|
|
159
|
+
|
|
160
|
+
soa = (c_double * 12)(1, 4, 7, 10, 2, 5, 8, 11, 3, 6, 9, 12)
|
|
161
|
+
out2 = (c_double * 12)()
|
|
162
|
+
mv_soa = memoryview(soa).cast('B').cast('d', [3, 4])
|
|
163
|
+
mv_out2 = memoryview(out2).cast('B').cast('d', [3, 4])
|
|
164
|
+
xfrm.transform(mv_soa, mv_out2) # shape[0]==3 -> transform_soa, n=shape[1]=4
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
The wrapper never transposes or copies data. `n` is computed from the
|
|
168
|
+
appropriate dimension via `map:`, and `when:` picks the C function.
|
|
169
|
+
|
|
170
|
+
The generated wrapper for this example is committed at
|
|
171
|
+
[`tests/cases/transform/xfrm_wrapper.c`](tests/cases/transform/xfrm_wrapper.c)
|
|
172
|
+
and can be compiled without c2py23 installed:
|
|
173
|
+
|
|
174
|
+
```bash
|
|
175
|
+
gcc -shared -fPIC -I c2py23/runtime \
|
|
176
|
+
tests/cases/transform/xfrm_wrapper.c \
|
|
177
|
+
tests/cases/transform/transform.c \
|
|
178
|
+
c2py23/runtime/c2py_runtime.c \
|
|
179
|
+
-ldl -lm -o xfrm.so
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
A git pre-commit hook (`.githooks/pre-commit`) regenerates the wrapper
|
|
183
|
+
when the generator, parser, runtime, or transform source files change.
|
|
184
|
+
|
|
185
|
+
### Array Dimension Notation in `sig:`
|
|
186
|
+
|
|
187
|
+
C function parameters can use array syntax instead of flat pointers.
|
|
188
|
+
c2py23 auto-generates buffer validation checks from the dimensions:
|
|
189
|
+
|
|
190
|
+
```yaml
|
|
191
|
+
sig: "double sum_rows(const double gv[][3], intptr_t ng)"
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
This produces checks `data.slow_axis == 0`, `data.ndim == 2`, `data.shape[1] == 3`
|
|
195
|
+
for a `map: {gv: "data.ptr"}` mapping. See the [specification](docs/specification.md) for details.
|
|
196
|
+
|
|
197
|
+
### Two-Step Workflow (generate then compile)
|
|
198
|
+
|
|
199
|
+
For build system integration, c2py23 supports a split workflow:
|
|
200
|
+
|
|
201
|
+
```bash
|
|
202
|
+
# Generate C wrapper only (no compilation)
|
|
203
|
+
c2py23 generate mymod.c2py -o mymod_wrapper.c
|
|
204
|
+
|
|
205
|
+
# Compile separately (for meson/cmake/setuptools integration)
|
|
206
|
+
c2py23 compile mymod_wrapper.c -s mymod.c -I include/ -o mymod.so
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
The `build` subcommand also supports `--generate-only` and `--compile-only` flags.
|
|
210
|
+
|
|
211
|
+
## Platforms
|
|
212
|
+
|
|
213
|
+
| OS | Architecture | Compiler | Status |
|
|
214
|
+
|----|-------------|----------|--------|
|
|
215
|
+
| Linux | x86_64 | gcc | Supported |
|
|
216
|
+
| Windows | x64 | MSVC / MinGW | Supported |
|
|
217
|
+
| Linux | aarch64 | gcc cross | Planned |
|
|
218
|
+
| Linux | ppc64le | gcc cross | Planned |
|
|
219
|
+
| macOS | arm64 | clang | Future |
|
|
220
|
+
|
|
221
|
+
On Windows, set `CC=cl` for MSVC (recommended) or `CC=gcc` for MinGW.
|
|
222
|
+
The compiler is auto-detected in `c2py23 build`. Output extension is
|
|
223
|
+
`.pyd` on Windows, `.so` elsewhere.
|
|
224
|
+
|
|
225
|
+
## Supported Types
|
|
226
|
+
|
|
227
|
+
| Python type | C types |
|
|
228
|
+
|-------------|---------|
|
|
229
|
+
| `buffer` | `const T*` / `T*` for any integer or float C type below |
|
|
230
|
+
| `int` | `int` |
|
|
231
|
+
| `float` | `float`, `double` |
|
|
232
|
+
| Return `void` | C `void` function |
|
|
233
|
+
| Return `int` | C `int` return |
|
|
234
|
+
| Return `float` | C `float` or `double` return |
|
|
235
|
+
|
|
236
|
+
**C pointer element types**: `_Bool`, `int8_t`, `uint8_t`, `int16_t`, `uint16_t`, `int32_t`,
|
|
237
|
+
`uint32_t`, `int64_t`, `uint64_t`, `intptr_t`, `size_t`, `int`, `float`, `double`, `char`, `void`
|
|
238
|
+
|
|
239
|
+
The `void*` pointer type maps from Python `int` (pointer-width, casts via `intptr_t`).
|
|
240
|
+
This is for opaque addresses the user manages (GPU memory, custom allocators, etc.).
|
|
241
|
+
The wrapper never dereferences `void*` -- it is a pure address passthrough.
|
|
242
|
+
|
|
243
|
+
**Output scalars** (`outputs:` key): supports `int8_t`, `int16_t`, `int32_t`,
|
|
244
|
+
`uint8_t`, `uint16_t`, `uint32_t`, `int64_t`, `uint64_t`, `float`, `double`,
|
|
245
|
+
`int` as return-by-pointer parameters.
|
|
246
|
+
|
|
247
|
+
## What It Doesn't Do
|
|
248
|
+
|
|
249
|
+
- No structs, enums, nested pointers, or heap allocation -- all memory is flat and owned by Python
|
|
250
|
+
- No `-lpython` link -- one `.so`/`.pyd` works on Python 2.7 through 3.15. Uses nimpy-style `dlopen(NULL)` + `dlsym()` (Linux) or `GetModuleHandle` + `GetProcAddress` (Windows) at load time.
|
|
251
|
+
- No copies or transpositions -- zero-copy, C functions operate on buffers in-place
|
|
252
|
+
- No numpy required -- `ctypes` arrays, `memoryview`, `bytearray`, and anything supporting PEP 3118 works
|
|
253
|
+
|
|
254
|
+
**Warning for Python 3.14t (free-threaded) users:** By default, c2py23 modules
|
|
255
|
+
re-enable the GIL globally on load (safe default). Set `free_threading: true` in
|
|
256
|
+
your `.c2py` file to opt into true free-threading -- but only after verifying
|
|
257
|
+
your C code is thread-safe. See `docs/user_guide.md` for a guide to thread-safe
|
|
258
|
+
C patterns.
|
|
259
|
+
|
|
260
|
+
## Examples
|
|
261
|
+
|
|
262
|
+
| Example | Build System | Description |
|
|
263
|
+
|---------|-------------|-------------|
|
|
264
|
+
| [`kissfft_wrap/`](examples/kissfft_wrap/) | c2py23 build | real + complex FFT over float buffers |
|
|
265
|
+
| [`lz4_wrap/`](examples/lz4_wrap/) | c2py23 build | compress/decompress over byte buffers |
|
|
266
|
+
| [`simd_dispatch/`](examples/simd_dispatch/) | Makefile, Meson, CMake, setuptools | multi-flag compilation + CPU feature dispatch |
|
|
267
|
+
| [`threading_bench/`](examples/threading_bench/) | c2py23 build | GIL release, free-threading, OpenMP |
|
|
268
|
+
| [`wheel_demo/`](examples/wheel_demo/) | setuptools + gcc | minimal `py3-none-any` wheel |
|
|
269
|
+
| [`meson_demo/`](examples/meson_demo/) | meson.build | arraysum wheel built with meson |
|
|
270
|
+
| [`cmake_demo/`](examples/cmake_demo/) | CMakeLists.txt | arraysum wheel built with cmake |
|
|
271
|
+
|
|
272
|
+
The `simd_dispatch/` example demonstrates the full variant API: `default: false`
|
|
273
|
+
for benchmark-only variants, `_variants_*()` for enumeration, `_rebind_*()` for
|
|
274
|
+
runtime selection, and per-variant perf metadata. See `examples/simd_dispatch/Makefile`
|
|
275
|
+
for multi-flag compilation.
|
|
276
|
+
|
|
277
|
+
## Wheel Packaging
|
|
278
|
+
|
|
279
|
+
c2py23 modules can be distributed as multi-platform `py3-none-any` wheels.
|
|
280
|
+
The approach follows the ctypes peer model: ship platform-specific `.so` files,
|
|
281
|
+
load by explicit filename.
|
|
282
|
+
|
|
283
|
+
Filename convention: `_mymodule.c2py23-{os}_{arch}.so`
|
|
284
|
+
|
|
285
|
+
```bash
|
|
286
|
+
# Build inside manylinux2014 (glibc 2.17) for portable binaries
|
|
287
|
+
c2py23 generate mymodule.c2py -o wrapper.c
|
|
288
|
+
gcc -shared -fPIC wrapper.c mymodule.c c2py_runtime.c -ldl -lm \
|
|
289
|
+
-o mymodule/_mymodule.c2py23-linux_x86_64.so
|
|
290
|
+
|
|
291
|
+
# Package as py3-none-any wheel (setuptools bdist_wheel.get_tag() override)
|
|
292
|
+
python3 -m build
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
The `c2py_loader` module (`c2py23/c2py_loader.py`) loads the right `.so` at
|
|
296
|
+
import time by explicit path via `ExtensionFileLoader` (3.x) or
|
|
297
|
+
`imp.load_dynamic` (2.7). No `EXTENSION_SUFFIXES` monkeypatching, no
|
|
298
|
+
`sys.path` hacking. Set `C2PY_TRACE=1` to see which file was loaded.
|
|
299
|
+
|
|
300
|
+
Multiple platform-specific `.so` files can coexist in the same wheel.
|
|
301
|
+
pip installs on any arch; the loader picks the right one.
|
|
302
|
+
|
|
303
|
+
See `examples/wheel_demo/` for a complete working example and `docs/user_guide.md`
|
|
304
|
+
for the full packaging guide.
|
|
305
|
+
|
|
306
|
+
## Testing
|
|
307
|
+
|
|
308
|
+
```bash
|
|
309
|
+
# Test a specific Python version locally
|
|
310
|
+
bash tests/run_tests.sh python3.12
|
|
311
|
+
|
|
312
|
+
# Test across all versions via snakepit containers
|
|
313
|
+
python3 tests/test_all.py
|
|
314
|
+
|
|
315
|
+
# Test the manylinux2014 build-once cross-test strategy
|
|
316
|
+
python3 tests/test_manylinux.py
|
|
317
|
+
|
|
318
|
+
# Valgrind memory leak check
|
|
319
|
+
valgrind --leak-check=full python3 tests/test_leaks.py
|
|
320
|
+
|
|
321
|
+
# ASan build
|
|
322
|
+
c2py23 build --asan module.c2py
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
Requires the snakepit SIF containers at `../snakepit/`.
|
|
326
|
+
|
|
327
|
+
|
|
328
|
+
|
|
329
|
+
## Documentation
|
|
330
|
+
|
|
331
|
+
- `AGENTS.md` -- guidelines for AI agents working on the codebase
|
|
332
|
+
- `docs/specification.md` -- full grammar, worked examples, architecture
|
|
333
|
+
- `docs/user_guide.md` -- thread safety guide and packaging instructions
|
|
334
|
+
|
|
335
|
+
## Limitations
|
|
336
|
+
|
|
337
|
+
- No structs, enums, or nested data types
|
|
338
|
+
- Flat memory only (contiguous buffers)
|
|
339
|
+
- On Free Threading (FT) 3.14t, CPython re-enables the GIL globally when a c2py23 module loads (no `Py_MOD_GIL_NOT_USED`). All Python threads are serialized; parallel C execution requires `gil_release: true` (same as standard CPython). Set `PYTHON_GIL=0` or `-Xgil=0` for true free-threading at your own risk (C code must be thread-safe). See `docs/user_guide.md` for a practical guide and `docs/specification.md` for technical details.
|
|
340
|
+
- Subinterpreters (Python 3.12+ `_xxsubinterpreters`) are not supported. The nimpy-style module init bypasses the multi-phase initialization slot (`Py_mod_multiple_interpreters`) required by subinterpreters. This is not a practical limitation -- `multiprocessing`, `concurrent.futures`, and `asyncio` do not use subinterpreters.
|
|
341
|
+
- 32-bit platforms are not supported. Module import fails at runtime with a clear diagnostic. Only LP64 (64-bit) targets are tested.
|