Skip to main content

euspinolia logo: a velvet ant forming the letter E

euspinolia

CI PyPI Python License: MIT

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, sorts 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.sort_values("score", ascending=False)[["name", "score"]]
        name  score
0        ada   91.5
1      grace   88.0
2  Doe, John  73.25

[3 rows x 2 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.

Then take a first look at a file without writing any Python:

$ euspinolia stats data.csv
data.csv: 500,000 rows x 5 columns, 18.0 MB, parsed in 108 ms

column  type      min     max        mean  distinct
------  ------  -----  ------  ----------  --------
id      int         0  499999    249999.5   500,000
name    string                              500,000
dept    string                                    5
salary  int     40000  199999  119926.142   152,970
score   float     0.0   100.0      49.973    10,001

euspinolia head data.csv -n 20 prints the first rows, and both take --delimiter (';', '\t'). python -m euspinolia works too.

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.read_csv("people.tsv", delimiter="\t")    # or TSV, or ';'
df = euspinolia.parse_csv(csv_text)                       # parse text already in memory
df = euspinolia.from_dict({"name": ["ada"], "age": [36]})  # or build it from Python

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[["name", "age"]]                         # a new DataFrame with those columns
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.sort_values("age", ascending=False)      # rows ordered by one column, stably

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
df.to_dict()           # or as {"name": [...], "age": [...]}

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 selected, filtered, sorted 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
sort by an int column 40.7 ms 69.8 ms 110.6 ms
groupby (5 groups), mean 7.5 ms 23.6 ms 47.8 ms

Parsing, writing, sorting and grouping are ahead of pandas — sorting because numeric keys go through a radix sort. 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
              ├─▶ select, filter, sort,
              │   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 per file, 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,700 lines of Zig, tests included, and 1,250 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.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for euspinolia 0.2.0
File Size Uploaded
euspinolia-0.2.0.tar.gz 74.4 kB Details

Built distributions (wheels)

Table of built distributions (wheels) for euspinolia 0.2.0
File
euspinolia-0.2.0-py3-none-win_arm64.whl Python 3 none Windows ARM64 Details
euspinolia-0.2.0-py3-none-win_amd64.whl Python 3 none Windows x86-64 Details
euspinolia-0.2.0-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.2.0-py3-none-manylinux2014_armv7l.musllinux_1_1_armv7l.whl Python 3 none Linux musl 1.1+ ARMv7l, Linux glibc 2.17+ ARMv7l Details
euspinolia-0.2.0-py3-none-manylinux2014_aarch64.musllinux_1_1_aarch64.whl Python 3 none Linux musl 1.1+ ARM64, Linux glibc 2.17+ ARM64 Details
euspinolia-0.2.0-py3-none-macosx_11_0_x86_64.whl Python 3 none macOS 11.0+ x86-64 Details
euspinolia-0.2.0-py3-none-macosx_11_0_arm64.whl Python 3 none macOS 11.0+ ARM64 Details

Total release size: 1.4 MB

Release files / euspinolia-0.2.0.tar.gz

Download URL euspinolia-0.2.0.tar.gz
Size 74.4 kB
Tags Source
SHA-256 checksum
How to use checksums
0960a5bbb16cad0bee8fc788085bec383bca06a988b2030a1ca79065364e7988
BLAKE2b-256 checksum
How to use checksums
30baaa117a77da8ddf8475eebeb8d1655c061fd2f79a87fc3280936edf8d85e7
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 24, 2026.

Transparency log

Release files / euspinolia-0.2.0-py3-none-win_arm64.whl

Download URL euspinolia-0.2.0-py3-none-win_arm64.whl
Size 191.6 kB
Tags Python 3 Windows ARM64
SHA-256 checksum
How to use checksums
e2833bc78cbb28d308f6ed985e0f1edf11ec18e5bed13d1821702f2991257857
BLAKE2b-256 checksum
How to use checksums
fd95cfa0160ae461ddb5a069c2f439f6b24d1149c8cfaaa7c771d126dffd3789
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 24, 2026.

Transparency log

Release files / euspinolia-0.2.0-py3-none-win_amd64.whl

Download URL euspinolia-0.2.0-py3-none-win_amd64.whl
Size 230.4 kB
Tags Python 3 Windows x86-64
SHA-256 checksum
How to use checksums
77eb6848849ad11c58c018c4146971a562680e4935f2938a6a012de2af7d7a98
BLAKE2b-256 checksum
How to use checksums
6e7c490c7a50022cdc324d05b34301bad71bcfd28be61d35124225abcd3a695c
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 24, 2026.

Transparency log

Release files / euspinolia-0.2.0-py3-none-manylinux2014_x86_64.musllinux_1_1_x86_64.whl

Download URL euspinolia-0.2.0-py3-none-manylinux2014_x86_64.musllinux_1_1_x86_64.whl
Size 174.0 kB
Tags Linux glibc 2.17+ x86-64 Linux musl 1.1+ x86-64 Python 3
SHA-256 checksum
How to use checksums
2605677765653b946786d623d6b9ae3d2082f6208f30a19f1fa376cbb767ca94
BLAKE2b-256 checksum
How to use checksums
f88622e811ec516c027b8b9c92560cd78735ca0870ea57e876f64245bdc07ccb
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 24, 2026.

Transparency log

Release files / euspinolia-0.2.0-py3-none-manylinux2014_armv7l.musllinux_1_1_armv7l.whl

Download URL euspinolia-0.2.0-py3-none-manylinux2014_armv7l.musllinux_1_1_armv7l.whl
Size 179.4 kB
Tags Linux glibc 2.17+ ARMv7l Linux musl 1.1+ ARMv7l Python 3
SHA-256 checksum
How to use checksums
07dda5acfc9418c5eabd3537ae9499e6977a5e15e95a8a1a6e306ac7c52f94c9
BLAKE2b-256 checksum
How to use checksums
1cf41d2774167b45f961a219d4453e37c0daa25c1a5c060710926d2c17c85c15
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 24, 2026.

Transparency log

Release files / euspinolia-0.2.0-py3-none-manylinux2014_aarch64.musllinux_1_1_aarch64.whl

Download URL euspinolia-0.2.0-py3-none-manylinux2014_aarch64.musllinux_1_1_aarch64.whl
Size 174.3 kB
Tags Linux glibc 2.17+ ARM64 Linux musl 1.1+ ARM64 Python 3
SHA-256 checksum
How to use checksums
dfe10b2293c1f4c93d12e6cada4393198ea82153eb103c1b4ac45f112867ef9c
BLAKE2b-256 checksum
How to use checksums
00f55cf6d39e2c6bc29c9dcaaa1276d42cd9a88bd2e4d1b248cdbfe99db00acb
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 24, 2026.

Transparency log

Release files / euspinolia-0.2.0-py3-none-macosx_11_0_x86_64.whl

Download URL euspinolia-0.2.0-py3-none-macosx_11_0_x86_64.whl
Size 178.2 kB
Tags Python 3 macOS 11.0+ x86-64
SHA-256 checksum
How to use checksums
9f9c96080aca667673725f383d7efbd013dc255ee344387f5059035a5572dfee
BLAKE2b-256 checksum
How to use checksums
df071d1433c8447ac0d1cfa7a7c29797d62340aa2b5a841759430bc0975ac6bd
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 24, 2026.

Transparency log

Release files / euspinolia-0.2.0-py3-none-macosx_11_0_arm64.whl

Download URL euspinolia-0.2.0-py3-none-macosx_11_0_arm64.whl
Size 160.6 kB
Tags Python 3 macOS 11.0+ ARM64
SHA-256 checksum
How to use checksums
8bcae4630e95f9bfe78fca66410ca50691fb6da107fc206c81fd5a5f06cbb7b9
BLAKE2b-256 checksum
How to use checksums
ff69c01533c21d47d0613cfb84ad174d13ed0063f8781d8d1c6a7b95d1981021
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 24, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.2.0 This release

8 release files

0.1.1

8 release files

0.1.0

7 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page