euspinolia
A small, educational CSV/table library with its engine in Zig and a thin
ctypes layer in Python. It parses a CSV into typed columns, reduces,
filters and groups them, and writes them back out — faster than pandas for
most of that, with no dependencies and a shared library that links nothing,
not even libc.
The name comes from Euspinolia, the genus of the velvet ant known as the "panda ant".
>>> import euspinolia
>>> df = euspinolia.read_csv("people.csv")
>>> df
name age score city
0 ada 36 91.5 London
1 grace 45 88.0 New York
2 Doe, John 29 73.25 Paris
[3 rows x 4 columns]
>>> df["age"].to_list()
[36, 45, 29]
>>> df[df["age"] > 30]
name age score city
0 ada 36 91.5 London
1 grace 45 88.0 New York
[2 rows x 4 columns]
>>> df.groupby("city").agg({"score": "mean"})
city score
0 London 91.5
1 New York 88.0
2 Paris 73.25
[3 rows x 2 columns]
Install
pip install euspinolia
Wheels are published for Linux (x86_64, aarch64, armv7l), macOS (x86_64,
arm64) and Windows (x64, arm64), for any Python 3.9 or newer. On anything else,
pip builds from source; that needs no Zig install either, since the build
pulls it from the ziglang package.
To work from a checkout instead, see docs/development.md.
What it does
import euspinolia
df = euspinolia.read_csv("people.csv") # parse a file
df = euspinolia.parse_csv(csv_text) # parse text already in memory
df.shape # (3, 4) — (rows, columns)
df.columns # ('name', 'age', 'score', 'city')
df.dtypes # (string, int, float, string)
df.head(2) # the first two rows as tuples
column = df["age"] # by name; df[1] works too
column[0] # 36 — read straight out of the Zig buffer
column.to_list() # [36, 45, 29]
column.sum() # 110 — exact, reduced in Zig
column.mean() # 36.666666666666664
column.min() # 29
column.max() # 45
df[df["age"] > 30] # a new DataFrame with the matching rows
df[(df["age"] > 30) & (df["score"] < 90)] # conditions chain with &
df.filter("city", "==", "Paris") # the same thing, as a call
df.groupby("city").agg({"score": "mean", "age": "max"}) # one row per city
df.groupby("city").sum() # every numeric column
df.groupby("city").count() # rows per group
df.to_csv("out.csv") # write it back out; reads back as the same frame
df.to_csv() # or as a string
with euspinolia.read_csv("people.csv") as df:
... # Zig-side memory freed on the way out; df.close() does the same
Every column has one type — int, float or string — inferred from
its values. Reductions keep that type, so an integer column sums exactly.
A filtered or grouped frame owns its own memory and outlives its source.
Errors are ordinary Python exceptions: FileNotFoundError, ParseError,
TypeError for text where a number was needed, OverflowError for a sum
that leaves 64 bits.
The full reference is in docs/api.md.
Performance
500,000 rows, 5 columns, 18 MB; best of five on one laptop, against pandas
3.0 and the standard csv module:
| euspinolia | pandas | csv module + Python | |
|---|---|---|---|
| read + parse | 114 ms | 183 ms | 335 ms |
write back out (to_csv) |
63 ms | 478 ms | 294 ms |
| sum an int column | 0.2 ms | 0.2 ms | 10.1 ms |
| filter, keeping half the rows | 10.5 ms | 8.7 ms | 13.8 ms |
| groupby (5 groups), mean | 7.5 ms | 23.6 ms | 47.8 ms |
Parsing, writing and grouping are ahead of pandas; reductions are a wash, as native code against native code should be; filtering is the one loss, because the result copies its strings rather than sharing them. What each number means, and how to run the benchmark yourself, is in docs/benchmarks.md.
How it works
[Python] df = euspinolia.read_csv("data.csv")
│ ctypes call
▼
[Zig] CSV scanner → type inference → one typed array per column
│
├─▶ sum / mean / min / max → one value
├─▶ filter, groupby → a new frame
└─▶ to_csv → bytes
│ opaque handle + borrowed column pointers
▼
[Python] DataFrame / Column wrap the handle; df["age"][0] reads the
Zig buffer through ctypes, no copy
Columns are struct-of-arrays: int and float are flat []i64 / []f64,
strings are one packed byte buffer plus offsets. A filter or an aggregate
walks one contiguous array; a groupby hashes each key into a dense group id
and folds into a flat accumulator; Python reads numeric columns in place.
Module by module: docs/internals.md.
Scope
This is a teaching project, and the scope was fixed at the start: one delimiter, one header row, three column types, and the operations above. Multi-index, date/time types, NaN semantics, join/merge and pivot tables are out, on purpose. The goal is not a real table engine but a subset that genuinely works, is fast for real reasons, and can be read end to end in an afternoon — about 3,000 lines of Zig, tests included, and 900 of Python.
Documentation
- API reference — every function, method, argument and exception
- Internals — the Zig side, module by module, and the ABI
- Benchmarks — the numbers above, what they measure and why they come out that way
- Development — building, testing, cross-compiling wheels, releasing
- Changelog
License
MIT.
Metadata
Release files for euspinolia 0.1.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| euspinolia-0.1.1.tar.gz | 58.6 kB | Details |
Built distributions (wheels)
| File | Reset | |||
|---|---|---|---|---|
| euspinolia-0.1.1-py3-none-win_arm64.whl | Python 3 | none | Windows ARM64 | Details |
| euspinolia-0.1.1-py3-none-win_amd64.whl | Python 3 | none | Windows x86-64 | Details |
| euspinolia-0.1.1-py3-none-manylinux2014_x86_64.musllinux_1_1_x86_64.whl | Python 3 | none | Linux glibc 2.17+ x86-64, Linux musl 1.1+ x86-64 | Details |
| euspinolia-0.1.1-py3-none-manylinux2014_armv7l.musllinux_1_1_armv7l.whl | Python 3 | none | Linux glibc 2.17+ ARMv7l, Linux musl 1.1+ ARMv7l | Details |
| euspinolia-0.1.1-py3-none-manylinux2014_aarch64.musllinux_1_1_aarch64.whl | Python 3 | none | Linux glibc 2.17+ ARM64, Linux musl 1.1+ ARM64 | Details |
| euspinolia-0.1.1-py3-none-macosx_11_0_x86_64.whl | Python 3 | none | macOS 11.0+ x86-64 | Details |
| euspinolia-0.1.1-py3-none-macosx_11_0_arm64.whl | Python 3 | none | macOS 11.0+ ARM64 | Details |
Total release size: 1.2 MB
Release files / euspinolia-0.1.1.tar.gz
| Download URL | euspinolia-0.1.1.tar.gz |
|---|---|
| Size | 58.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
710bce862a2a9c3cac0e4a7d626aa14d15ead70176cd023d309f887d76ae979c
|
|
BLAKE2b-256 checksum How to use checksums |
e2894e72e4d2bf45e59fca5e50d83176189767064dbfed20d0fd9147e3bc0d2e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 19, 2026.
Transparency logRelease files / euspinolia-0.1.1-py3-none-win_arm64.whl
| Download URL | euspinolia-0.1.1-py3-none-win_arm64.whl |
|---|---|
| Size | 173.3 kB |
| Tags | Python 3 Windows ARM64 |
|
SHA-256 checksum How to use checksums |
4498011db1443d59a7db5e68d41f9b3b9956c6c70f9c2a566fdefcaec24efa5d
|
|
BLAKE2b-256 checksum How to use checksums |
46390f1d78c73332cb333e12b35cd1343c50f266e7623268fe2ee48a1bdf8f5e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 19, 2026.
Transparency logRelease files / euspinolia-0.1.1-py3-none-win_amd64.whl
| Download URL | euspinolia-0.1.1-py3-none-win_amd64.whl |
|---|---|
| Size | 211.4 kB |
| Tags | Python 3 Windows x86-64 |
|
SHA-256 checksum How to use checksums |
b9c9c3dedb5af73ffb2ecfbda97c3469b2839aaa9acf9e399d1718b2ff912e7f
|
|
BLAKE2b-256 checksum How to use checksums |
83078d21247298c6109edfe890f5a3aac534744b9b5b8166bbce4d5d34d1c567
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 19, 2026.
Transparency logRelease files / euspinolia-0.1.1-py3-none-manylinux2014_x86_64.musllinux_1_1_x86_64.whl
| Download URL | euspinolia-0.1.1-py3-none-manylinux2014_x86_64.musllinux_1_1_x86_64.whl |
|---|---|
| Size | 153.9 kB |
| Tags | Linux glibc 2.17+ x86-64 Linux musl 1.1+ x86-64 Python 3 |
|
SHA-256 checksum How to use checksums |
6b750469a2fa8f94f882c422ffc6f320575ef6ccf51e73d9bd157d917842747d
|
|
BLAKE2b-256 checksum How to use checksums |
e2527f117913eab953d405c72a2f485893b2e8267bc3977c5c1b38e1c6382b1c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 19, 2026.
Transparency logRelease files / euspinolia-0.1.1-py3-none-manylinux2014_armv7l.musllinux_1_1_armv7l.whl
| Download URL | euspinolia-0.1.1-py3-none-manylinux2014_armv7l.musllinux_1_1_armv7l.whl |
|---|---|
| Size | 160.6 kB |
| Tags | Linux glibc 2.17+ ARMv7l Linux musl 1.1+ ARMv7l Python 3 |
|
SHA-256 checksum How to use checksums |
e0dc53ba438e6e7d1f8565950cc768472fa903b8c176b91276d904118c3a2ef7
|
|
BLAKE2b-256 checksum How to use checksums |
691799203970d38ddd4c7c52427421067348da7f8db58cafdd162e1883698837
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 19, 2026.
Transparency logRelease files / euspinolia-0.1.1-py3-none-manylinux2014_aarch64.musllinux_1_1_aarch64.whl
| Download URL | euspinolia-0.1.1-py3-none-manylinux2014_aarch64.musllinux_1_1_aarch64.whl |
|---|---|
| Size | 156.2 kB |
| Tags | Linux glibc 2.17+ ARM64 Linux musl 1.1+ ARM64 Python 3 |
|
SHA-256 checksum How to use checksums |
77136ac9c434cb76a536746257f446e7d919163f4ad0486d3e5a231c9363b531
|
|
BLAKE2b-256 checksum How to use checksums |
77dd0b0fff1e059257104c254f571c26402291345572089712640e0ac3445718
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 19, 2026.
Transparency logRelease files / euspinolia-0.1.1-py3-none-macosx_11_0_x86_64.whl
| Download URL | euspinolia-0.1.1-py3-none-macosx_11_0_x86_64.whl |
|---|---|
| Size | 157.1 kB |
| Tags | Python 3 macOS 11.0+ x86-64 |
|
SHA-256 checksum How to use checksums |
320077e6be81f94bb0c239f88ef7df879e8ff719a9564ec942820b2aebcdc4f8
|
|
BLAKE2b-256 checksum How to use checksums |
9a742df1c74d127f1488b9f160e70d65c5c198f0ed73ab0ce9b906eaf90bfb37
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 19, 2026.
Transparency logRelease files / euspinolia-0.1.1-py3-none-macosx_11_0_arm64.whl
| Download URL | euspinolia-0.1.1-py3-none-macosx_11_0_arm64.whl |
|---|---|
| Size | 141.7 kB |
| Tags | Python 3 macOS 11.0+ ARM64 |
|
SHA-256 checksum How to use checksums |
2703396a5ff46b8bae6ed5c0c4f09de9bf365e534655f6245ed5dd8b112eb104
|
|
BLAKE2b-256 checksum How to use checksums |
6735ca92be53d6f026d2e888bc2b8c4bcfc4d3515e980cc15220249f00055d3d
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 19, 2026.
Transparency log