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.
Files changed (67) hide show
  1. c2py23-0.3.0/LICENSE +21 -0
  2. c2py23-0.3.0/MANIFEST.in +40 -0
  3. c2py23-0.3.0/PKG-INFO +341 -0
  4. c2py23-0.3.0/README.md +316 -0
  5. c2py23-0.3.0/c2py23/__init__.py +3 -0
  6. c2py23-0.3.0/c2py23/c2py_loader.py +126 -0
  7. c2py23-0.3.0/c2py23/cli.py +277 -0
  8. c2py23-0.3.0/c2py23/generator.py +2003 -0
  9. c2py23-0.3.0/c2py23/invariant_checker.py +278 -0
  10. c2py23-0.3.0/c2py23/parser.py +1457 -0
  11. c2py23-0.3.0/c2py23/perf.py +265 -0
  12. c2py23-0.3.0/c2py23/runtime/c2py_amd64.h +49 -0
  13. c2py23-0.3.0/c2py23/runtime/c2py_arm64.h +34 -0
  14. c2py23-0.3.0/c2py23/runtime/c2py_ppc64.h +27 -0
  15. c2py23-0.3.0/c2py23/runtime/c2py_runtime.c +847 -0
  16. c2py23-0.3.0/c2py23/runtime/c2py_runtime.h +760 -0
  17. c2py23-0.3.0/c2py23.egg-info/PKG-INFO +341 -0
  18. c2py23-0.3.0/c2py23.egg-info/SOURCES.txt +65 -0
  19. c2py23-0.3.0/c2py23.egg-info/dependency_links.txt +1 -0
  20. c2py23-0.3.0/c2py23.egg-info/entry_points.txt +2 -0
  21. c2py23-0.3.0/c2py23.egg-info/requires.txt +6 -0
  22. c2py23-0.3.0/c2py23.egg-info/top_level.txt +1 -0
  23. c2py23-0.3.0/docs/specification.md +1432 -0
  24. c2py23-0.3.0/docs/user_guide.md +356 -0
  25. c2py23-0.3.0/examples/cmake_demo/CMakeLists.txt +48 -0
  26. c2py23-0.3.0/examples/cmake_demo/arraysum/__init__.py +14 -0
  27. c2py23-0.3.0/examples/cmake_demo/arraysum.c +10 -0
  28. c2py23-0.3.0/examples/cmake_demo/arraysum.c2py +14 -0
  29. c2py23-0.3.0/examples/cmake_demo/build.sh +27 -0
  30. c2py23-0.3.0/examples/cmake_demo/setup.py +35 -0
  31. c2py23-0.3.0/examples/kissfft_wrap/example.py +26 -0
  32. c2py23-0.3.0/examples/kissfft_wrap/kissfft.c2py +38 -0
  33. c2py23-0.3.0/examples/kissfft_wrap/kissfft_thin.c +19 -0
  34. c2py23-0.3.0/examples/kissfft_wrap/kissfftmod_wrapper.c +558 -0
  35. c2py23-0.3.0/examples/lz4_wrap/example.py +29 -0
  36. c2py23-0.3.0/examples/lz4_wrap/lz4.c2py +35 -0
  37. c2py23-0.3.0/examples/lz4_wrap/lz4_thin.c +10 -0
  38. c2py23-0.3.0/examples/lz4_wrap/lz4mod_wrapper.c +555 -0
  39. c2py23-0.3.0/examples/meson_demo/arraysum/__init__.py +14 -0
  40. c2py23-0.3.0/examples/meson_demo/arraysum.c +10 -0
  41. c2py23-0.3.0/examples/meson_demo/arraysum.c2py +14 -0
  42. c2py23-0.3.0/examples/meson_demo/build.sh +36 -0
  43. c2py23-0.3.0/examples/meson_demo/setup.py +35 -0
  44. c2py23-0.3.0/examples/simd_dispatch/CMakeLists.txt +68 -0
  45. c2py23-0.3.0/examples/simd_dispatch/MANIFEST.in +4 -0
  46. c2py23-0.3.0/examples/simd_dispatch/Makefile +54 -0
  47. c2py23-0.3.0/examples/simd_dispatch/_polysimd_wrapper.c +735 -0
  48. c2py23-0.3.0/examples/simd_dispatch/build.sh +32 -0
  49. c2py23-0.3.0/examples/simd_dispatch/poly_kernel.c +41 -0
  50. c2py23-0.3.0/examples/simd_dispatch/polysimd/__init__.py +16 -0
  51. c2py23-0.3.0/examples/simd_dispatch/polysimd.c2py +29 -0
  52. c2py23-0.3.0/examples/simd_dispatch/setup.py +37 -0
  53. c2py23-0.3.0/examples/simd_dispatch/test_polysimd.py +125 -0
  54. c2py23-0.3.0/examples/threading_bench/Makefile +16 -0
  55. c2py23-0.3.0/examples/threading_bench/bench_mc_pi.py +177 -0
  56. c2py23-0.3.0/examples/threading_bench/mc_pi.c +75 -0
  57. c2py23-0.3.0/examples/threading_bench/mc_pi.c2py +20 -0
  58. c2py23-0.3.0/examples/threading_bench/mcpimod_wrapper.c +259 -0
  59. c2py23-0.3.0/examples/wheel_demo/MANIFEST.in +4 -0
  60. c2py23-0.3.0/examples/wheel_demo/arraysum/__init__.py +17 -0
  61. c2py23-0.3.0/examples/wheel_demo/arraysum.c +10 -0
  62. c2py23-0.3.0/examples/wheel_demo/arraysum.c2py +14 -0
  63. c2py23-0.3.0/examples/wheel_demo/build.sh +100 -0
  64. c2py23-0.3.0/examples/wheel_demo/setup.py +44 -0
  65. c2py23-0.3.0/pyproject.toml +36 -0
  66. c2py23-0.3.0/setup.cfg +4 -0
  67. 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.
@@ -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.