Bagz
Overview
Bagz is a format for storing a sequence of byte-array records, typically serialised protocol buffers. It supports per-record compression and fast index-based lookup. All indexing is zero based.
Installation
To install Bagz from source we recommend using uv.
The install will download required dependencies apart from curl
(libcurl-devel) and OpenSSL (openssl-devel). These need to be installed in a
location that cmake's find_package searches.
uv pip install .
To support reading from GCS:
uv pip install '.[GCS]'
On Linux and macOS you can install the latest version from PyPI.
uv pip install bagz
If you want GCS support install bagz-gcs:
uv pip install bagz-gcs
To build wheels locally it is recommended to use cibuildwheel.
On Linux (using podman):
sudo apt install podman
rm -rf dist/
python3 -m build --sdist --outdir dist .
python3 -m build --sdist --outdir dist plugins/gcs
CIBW_CONTAINER_ENGINE="podman" cibuildwheel --platform linux dist/bagz-*.tar.gz
CIBW_CONTAINER_ENGINE="podman" cibuildwheel --platform linux dist/bagz_gcs-*.tar.gz
On macOS:
rm -rf dist/
python3 -m build --sdist --outdir dist .
python3 -m build --sdist --outdir dist plugins/gcs
cibuildwheel --platform macos dist/bagz-*.tar.gz
cibuildwheel --platform macos dist/bagz_gcs-*.tar.gz
Python API
Python Reader
Reader for reading a single or sharded Bagz file-set.
from collections.abc import Sequence, Iterable
import bagz
import numpy as np
# Bagz Readers support random access. The order of elements within a Bagz
# file is the order in which they are written. Records are returned as `bytes`
# objects.
data = bagz.Reader('/path/to/data.bagz')
# Bagz Readers can be configured like this - here we require that the file was
# written with separate limits.
data_separate_limits = bagz.Reader('/path/to/data.bagz', bagz.Reader.Options(
limits_placement=bagz.LimitsPlacement.SEPARATE,
))
# Bagz Readers are Sequences and support slicing, iterating, etc.
assert isinstance(data, Sequence)
# Bagz Readers have a length.
assert len(data) > 10
# Can access record by row-index.
fifth_value: bytes = data[5]
# Can slice.
data_from_5: bagz.Reader = data[5:]
# Slices are still Readers.
assert isinstance(data_from_5, bagz.Reader)
assert data_from_5[0] == fifth_value
# Can access records by multiple row-indices.
fourth, second, tenth = data.read_indices([4, 2, 10])
assert fourth == data[4]
assert second == data[2]
assert tenth == data[10]
# Can iterate records.
for record in data:
do_something_else(record)
# Can read all records. This eager version can be faster than iteration.
all_records = data.read()
# Can iterate sub-range of records.
for record in data[4:9]:
do_something_else(record)
# Can read a sub-range of records. This eager form can be faster than
# iteration.
sub_range = data[4:9].read()
# Can use an infinite iterator as source of indices. (Reads ahead in parallel.)
def my_generator(size: int) -> Iterable[int]:
rng = np.random.default_rng(42)
while True:
yield rng.integers(size).item()
data_iter: Iterable[bytes] = data.read_indices_iter(my_generator(len(data)))
for i in range(10):
random_item: bytes = next(data_iter)
Python Reader - Index and MultiIndex
You can use Index to find the first index of a record and MultiIndex to find
all instances of an item.
keys = bagz.Reader('/path/to/keys.bag')
# Get the index of the first occurrence of key.
index = bagz.Index(keys)
key_index: int = index[b'example_key']
# Get all occurrences of key.
multi_index = bagz.MultiIndex(keys)
all_indices: list[int] = multi_index[b'example_key']
Python Writer
For writing a single Bagz file.
Example:
import bagz
# Compression is selected based on the file extension:
# `.bagz` will use Zstandard compression with default settings.
# `.bag` will use no compression.
with bagz.Writer('/path/to/data.bagz') as writer:
for d in generate_records():
writer.write(d)
# Adjust compression level explicitly.
# Note this will no longer use the extension to detemine whether to compress.
with bagz.Writer(
'/path/to/data.bagz',
bagz.Writer.Options(
compression=bagz.CompressionZstd(level=3)
),
) as writer:
for d in generate_records():
writer.write(d)
Options
Reader Options
bagz.Reader.Options has these optional arguments.
compression: Can be one of:bagz.CompressionAutoDetect(): Default - Uses extension whether to compress. (.bagz- Compressed (ZStandard),.bag- Uncompressed)bagz.CompressionNone(): Records are not decompressed.bagz.CompressionZstd(): Records are decompressed using Zstandard.
limits_placement: Can be one of:bagz.LimitsPlacement.TAIL: Default- Reads limits from a tail of file.bagz.LimitsPlacement.SEPARATE: Reads limits from a separate file.
limits_storage: Can be one of:bagz.LimitsStorage.ON_DISK: Default - Reads limits from disk for each read.bagz.LimitsStorage.IN_MEMORY: Reads all limits from disk in one go.
max_parallelism: Default number of threads when reading many records.sharding_layout: Can be one of:
Writer Options
bagz.Writer.Options has these optional arguments.
compression: Can be one of:bagz.CompressionAutoDetect(): Default - Uses extension whether to compress. (.bagz- Compressed (Zstandard),.bag- Uncompressed)bagz.CompressionNone(): Records are not compressed.bagz.CompressionZstd(level = 3): Records are compressed using Zstandard the level of the compression can be specified.
limits_placement: Can be one of:bagz.LimitsPlacement.TAIL: Default - Writes limits to a tail of file.bagz.LimitsPlacement.SEPARATE: Writes limits to a separate file.
Apache Beam Support
Bagz also provides Apache Beam connectors for reading and writing Bagz files in Beam pipelines.
Ensure you have Apache Beam installed.
uv pip install apache_beam
Bagz Source
import apache_beam as beam
from bagz.beam import bagzio
import tensorflow as tf
with beam.Pipeline() as pipeline:
examples = (
pipeline
| 'ReadData' >> bagzio.ReadFromBagz('/path/to/your/data@*.bagz')
| 'Decode' >> beam.Map(tf.train.Example.FromString)
)
# Continue your pipeline.
Bagz Sink
from bagz.beam import bagzio
import tensorflow as tf
def create_tf_example(data):
# Replace with your actual feature creation logic.
feature = {
'data': tf.train.Feature(bytes_list=tf.train.BytesList(value=[data])),
}
return tf.train.Example(features=tf.train.Features(feature=feature))
with beam.Pipeline() as pipeline:
data = [b'record1', b'record2', b'record3']
examples = (
pipeline
| 'CreateData' >> beam.Create(data)
| 'Encode' >> beam.Map(lambda x: create_tf_example(x).SerializeToString())
| 'WriteData' >> bagzio.WriteToBagz('/path/to/output/data@*.bagz')
)
GCS Support
Bagz supports Posix file-systems and Google Cloud Storage (GCS). If you have
files on GCS you can access them with using the prefix /gs: to the path. These
examples assume you have gcloud CLI installed.
From the shell:
gcloud config set project your-project-name
gcloud auth application-default login
Then use the 'gs:' file-system prefix.
import pathlib
import bagz
# (This may freeze if you have not configured the project.)
reader = bagz.Reader('gs://your-bucket-name/your-file.bagz')
# Path supports a leading slash to work well with pathlib.
bucket = pathlib.Path('/gs://your-bucket-name')
reader = bagz.Reader(bucket / 'your-file.bagz')
Sharding
An ordered collection of Bagz-formatted files ("shards") may be opened together
and indexed via a single global-index. The global-index is mapped to a
shard and an index within that shard (shard-index) in one of two ways:
-
Concatenated (default). Indexing is equivalent to the records in each Bagz-formatted shard being concatenated into a single sequence of records.
Example:
When opening four Bagz-formatted files with sizes
[8, 4, 0, 5], theglobal-indexwith range[0, 17)(shown as the table entries) maps toshardandshard-indexlike this:| shard-index shard | 0 1 2 3 4 5 6 7 -------------- | ----------------------- 00000-of-00004 | 0 1 2 3 4 5 6 7 00001-of-00004 | 8 9 10 11 00002-of-00004 | 00003-of-00004 | 12 13 14 15 16Mappings
global-indexshardshard-index000000-of-000040100000-of-000041200000-of-000042... ... ... 800001-of-000040900001-of-000041... ... ... 1500003-of-0000431600003-of-000044 -
Interleaved where the global-index is interleaved in a round-robin manner across all the shards.
Example:
When opening three Bagz-formatted files with sizes
[6, 6, 5], theglobal-indexwith range[0, 17)(shown as the table entries) maps toshardandshard-indexlike this:| shard-index shard | 0 1 2 3 4 5 -------------- | ----------------- 00000-of-00003 | 0 3 6 9 12 15 00001-of-00003 | 1 4 7 10 13 16 00002-of-00003 | 2 5 8 11 14Mappings
global-indexshardshard-index000000-of-000030100001-of-000030200002-of-000030... ... ... 600000-of-000032700001-of-000032800002-of-000032... ... ... 1500000-of-0000351600001-of-000035
Bagz file format
The Bagz file format has two parts: the records section and the limits
section.
- The
recordssection consists of the concatenation of all (possibly compressed) records. (There are no additional bytes inside or between records, and records are not aligned in any way.) - The
limitssection is a dense array of the end-offsets of each record in order, encoded in little-endian 64-bit unsigned integers.
These can be stored with tail-limits in one file, where the limits sections
is appended to the record section or separate-limits where they are stored
in separate files.
Tail-limits example
Given Bagz file formatted file with the following 3 uncompressed records:
| Records |
|---|
abcdef |
123 |
catcat |
The raw bytes of the Bagz file formated file corresponding to the records above:
0x61 a 0x62 b 0x63 c 0x64 d 0x65 e 0x66 f
0x31 1 0x32 2 0x33 3
0x63 c 0x61 a 0x74 t 0x63 c 0x61 a 0x74 t
0x06 0x00 0x00 0x00 0x00 0x00 0x00 0x00 # 6 byte offset
0x09 0x00 0x00 0x00 0x00 0x00 0x00 0x00 # 9 byte offset
0x0f 0x00 0x00 0x00 0x00 0x00 0x00 0x00 # 15 byte offset
The last 8 bytes represent the end-offset of the last record. This is also the
start of the limits section. Therefore reading the last 8 bytes will directly
tell you the offset of the records/limits boundary.
Disclaimer
This is not an official Google product.
Metadata
Release files for bagz 0.3.8
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| bagz-0.3.8.tar.gz | 75.8 kB | Details |
Built distributions (wheels)
| File | Reset | |||
|---|---|---|---|---|
| bagz-0.3.8-cp312-abi3-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl | CPython 3.12 | abi3 | Linux glibc 2.27+ x86-64, Linux glibc 2.28+ x86-64 | Details |
| bagz-0.3.8-cp312-abi3-macosx_11_0_arm64.whl | CPython 3.12 | abi3 | macOS 11.0+ ARM64 | Details |
| bagz-0.3.8-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl | CPython 3.11 | CPython 3.11 | Linux glibc 2.28+ x86-64, Linux glibc 2.27+ x86-64 | Details |
| bagz-0.3.8-cp311-cp311-macosx_11_0_arm64.whl | CPython 3.11 | CPython 3.11 | macOS 11.0+ ARM64 | Details |
| bagz-0.3.8-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl | CPython 3.10 | CPython 3.10 | Linux glibc 2.28+ x86-64, Linux glibc 2.27+ x86-64 | Details |
| bagz-0.3.8-cp310-cp310-macosx_11_0_arm64.whl | CPython 3.10 | CPython 3.10 | macOS 11.0+ ARM64 | Details |
Total release size: 4.7 MB
Release files / bagz-0.3.8.tar.gz
| Download URL | bagz-0.3.8.tar.gz |
|---|---|
| Size | 75.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
d6032b15cf462afcd9c3cabad0412a9e7d38bcee33d96cab21df78ea7e400763
|
|
BLAKE2b-256 checksum How to use checksums |
f0f9a9c57641a7a6e4278026890d475178f1de7dfff4e908fe6ef827a2d493c2
|
| 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 2, 2026.
Transparency logRelease files / bagz-0.3.8-cp312-abi3-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
| Download URL | bagz-0.3.8-cp312-abi3-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl |
|---|---|
| Size | 898.2 kB |
| Tags | CPython 3.12 Linux glibc 2.27+ x86-64 Linux glibc 2.28+ x86-64 abi3 |
|
SHA-256 checksum How to use checksums |
160b01f5ece74588a9f5fd09676d6303e8b6194979c82ecbeec3d0a4ca7c963e
|
|
BLAKE2b-256 checksum How to use checksums |
b5c1463f79b7d354c287d3ffacf3a5bda05ac0ba4b41f6729e6940747b60cdfa
|
| 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 2, 2026.
Transparency logRelease files / bagz-0.3.8-cp312-abi3-macosx_11_0_arm64.whl
| Download URL | bagz-0.3.8-cp312-abi3-macosx_11_0_arm64.whl |
|---|---|
| Size | 641.6 kB |
| Tags | CPython 3.12 abi3 macOS 11.0+ ARM64 |
|
SHA-256 checksum How to use checksums |
05c7e5dfd705c53eee537d40e067ed17b4641c625752920e949e3c795fb85fb2
|
|
BLAKE2b-256 checksum How to use checksums |
bf3c36eb680e55237eb22905bf7e2d432a31530062bedbf2ab7fc0c0b35e265c
|
| 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 2, 2026.
Transparency logRelease files / bagz-0.3.8-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
| Download URL | bagz-0.3.8-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl |
|---|---|
| Size | 902.7 kB |
| Tags | CPython 3.11 Linux glibc 2.27+ x86-64 Linux glibc 2.28+ x86-64 |
|
SHA-256 checksum How to use checksums |
56ab8ef84ab47a30ae994beb70ea27d0a6e4a1b2e090d01554ba94138c5b1927
|
|
BLAKE2b-256 checksum How to use checksums |
8d9824c7a9b84b8f8f6b65001ecd4fa543c0ec5c1a2a4541547734bec548c5f8
|
| 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 2, 2026.
Transparency logRelease files / bagz-0.3.8-cp311-cp311-macosx_11_0_arm64.whl
| Download URL | bagz-0.3.8-cp311-cp311-macosx_11_0_arm64.whl |
|---|---|
| Size | 643.4 kB |
| Tags | CPython 3.11 macOS 11.0+ ARM64 |
|
SHA-256 checksum How to use checksums |
bc21984645e41143597c23269f046fdb7299a4c052f1833e11524d95bbd71b30
|
|
BLAKE2b-256 checksum How to use checksums |
8031832eca888b0720e1e09c514316ca1c62d8b164b2da77fb6bdf91e1aebd42
|
| 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 2, 2026.
Transparency logRelease files / bagz-0.3.8-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
| Download URL | bagz-0.3.8-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl |
|---|---|
| Size | 902.3 kB |
| Tags | CPython 3.10 Linux glibc 2.27+ x86-64 Linux glibc 2.28+ x86-64 |
|
SHA-256 checksum How to use checksums |
93784b34a059f0345b29ac17a6b207c2110e0fadbc96bc0d308cd9370de0e06c
|
|
BLAKE2b-256 checksum How to use checksums |
c34bbe317135e61e8bdb65fe1c195b145723c298694495661890df0bb96d3838
|
| 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 2, 2026.
Transparency logRelease files / bagz-0.3.8-cp310-cp310-macosx_11_0_arm64.whl
| Download URL | bagz-0.3.8-cp310-cp310-macosx_11_0_arm64.whl |
|---|---|
| Size | 642.9 kB |
| Tags | CPython 3.10 macOS 11.0+ ARM64 |
|
SHA-256 checksum How to use checksums |
40fae1ee77bb2a0226ee8f5fc0c20ef528ae7cf6ed07a83aa705afbf4faed165
|
|
BLAKE2b-256 checksum How to use checksums |
6a98cb937f42955ed10b709eb6e5342dc672a5d4f3e176636cae07188e918221
|
| 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 2, 2026.
Transparency log