Skip to main content

pyzerocopy

Zero-copy structured buffers for Python.

You describe a binary layout as a class. pyzerocopy then reads and writes its fields directly in memory you already have: a bytearray, an mmap, shared memory, a NumPy array. Nothing is copied when you create a view, index into it, or slice it.

from pyzerocopy import Struct, Endian, float32, uint32_t


class Particle(Struct, endian=Endian.LITTLE):
    x: float32
    y: float32
    id: uint32_t


data = bytearray(Particle.sizeof() * 1000)
particles = Particle.view(data)  # no copy
particles[0].x = 10.5  # writes into `data`
subset = particles[10:100]  # still the same memory
blob = subset.to_bytes()  # copies the bytes

Install

pip install pyzerocopy

Python 3.10 or newer. No dependencies.

Why

struct.unpack gives you a copy of every record you touch. NumPy structured arrays avoid that but pull in a big dependency and don't give you attribute-style records. pyzerocopy is a small pure-Python layer that does one thing: named, typed access to bytes where they already live.

Declaring layouts

Fields are class annotations.

Kind Examples
Integers uint8_t, uint16_t, uint32_t, uint64_t, int8_t ... int64_t
Floats float32, float64
Fixed arrays uint8_t * 4, Array(4, uint16_t), Annotated[list[int], Array(4, uint16_t)]
Nested structs another Struct subclass used as the annotation

Class options:

  • endian= one of Endian.LITTLE (default), BIG, NATIVE, NETWORK. Nested structs keep their own byte order.
  • packed= True (default) puts fields back to back. False follows natural C alignment, with padding.
class Header(Struct, endian=Endian.BIG, packed=False):
    version: uint8_t
    length: uint32_t  # lands at offset 4 because packed=False
    tags: uint8_t * 4

Working with views

view = Particle.view(buf)  # whole buffer
view = Particle.view(mm, offset=16, count=100)  # skip a header, take 100 records

len(view)  # number of records
view[3]  # a record proxy, not a copy
view[-1]  # negative indexes work
view[10:100]  # another View over the same memory
for rec in view:
    ...
view.memory  # the underlying memoryview

A record is a small proxy holding the memoryview and an offset. Reading rec.x unpacks one value from the buffer and returns a normal Python number. Array fields and nested structs return views, not copies:

rec.tags[2] = 7  # writes in place
rec.tags = [1, 2, 3, 4]  # element-wise assignment, length must match
rec.inner.b = 5  # nested struct, still in place

Errors

Situation Exception
Value out of range or wrong type PackingError (a ValueError)
Write through read-only memory (bytes, read-only mmap) ReadOnlyError (a TypeError)
Index out of range IndexError
Bad offset/count, or length not a whole number of records ValueError

All library errors inherit from PyZeroCopyError.

Memory and lifetime

  • A view keeps its buffer alive. While a view exists, a bytearray can't be resized, which is what stops records from pointing at freed memory.
  • If you release the memoryview yourself, later access raises ValueError instead of crashing.
  • Don't mutate a buffer from several threads at once without your own locking.

Performance

Benchmark results

Run make bench to reproduce this chart on your machine (it rewrites docs/benchmark.png; N= sets the record count). The chart above is one run on Python 3.14 with 500k records.

  • Reading or writing a field through a record costs about 500 ns, roughly 3 to 4x raw struct calls with hand-computed offsets. That is the price of named fields.
  • Iterating is faster than indexing (about 340 ns per record) because it skips the index checks.
  • Against structed, which copies and decodes the whole record on each access, pyzerocopy is about 5x faster per field read.
  • Creating a view or a slice takes microseconds regardless of buffer size.

Treat the figures as ballpark and measure on your own hardware.

Limits

  • No strided slices (view[::2]).
  • No string or variable-length fields yet.
  • Assigning a whole array is not atomic: if one element is invalid, earlier ones are already written.

Development

git clone https://github.com/Zikithezikit/pyzerocopy
cd pyzerocopy
make venv    # create .venv and install dev tools
make check   # lint, type check, tests (what CI runs)
make help    # all commands

See CONTRIBUTING.md for more.

License

MIT, see LICENSE.

Metadata

Release files for pyzerocopy 0.1.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 pyzerocopy 0.1.0
File Size Uploaded Provenance Actions
pyzerocopy-0.1.0.tar.gz 13.2 kB Without provenance Details for pyzerocopy-0.1.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for pyzerocopy 0.1.0
File Interpreter ABI Platform Actions
pyzerocopy-0.1.0-py3-none-any.whl Python 3 none any Details for pyzerocopy-0.1.0-py3-none-any.whl

Total release size: 23.6 kB

Release files / pyzerocopy-0.1.0.tar.gz

Download URL pyzerocopy-0.1.0.tar.gz
Size 13.2 kB
Tags Source
SHA-256 checksum
How to use checksums
82122a7d92278fab21b7a4149c56a9e11c62adb2af55e3b4e36dec2b63cf7104
BLAKE2b-256 checksum
How to use checksums
824e965926970caf60a3941445209594cd3ad1a43ce063c327eeb8e22a3e3d6b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.13.6 {"installer":{"name":"uv","version":"0.13.6","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / pyzerocopy-0.1.0-py3-none-any.whl

Download URL pyzerocopy-0.1.0-py3-none-any.whl
Size 10.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
bfcde0cfbef073277684d2b903efc7ff5ddbc99f5a65030fc73ae75a63b5d046
BLAKE2b-256 checksum
How to use checksums
d9e438ceb6d917b8c80975d4e367e2c164251c56289a691818ad4d65b2e3a7fb
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.13.6 {"installer":{"name":"uv","version":"0.13.6","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Security for pyzerocopy 0.1.0

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.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files No provenance

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