Skip to main content

Säckli

This is a friendly fork of bagz.

Additions so far:

  • Merge some PRs such as S3 support PR by @KefanXIAO and compile fixes.
  • Add access_pattern and cache_policy reader hints:
    • On POSIX filesystems, this can add mmap hints or use pread-based no-cache reads to optimize for random access and larger-than-RAM data.
    • On Linux, support O_DIRECT for even better reading of random access and larger-than-RAM data.
    • On macOS, support F_NOCACHE, MAP_NOCACHE, and madvise-based cache hints on Apple silicon.
  • Add explicit limits warming for coordinated multi-process and network-filesystem workloads.
  • Make it compatible to Python versions past 3.13.
  • Make it compatible with free-threading (nogil) Python.
  • Add macOS support and wheels.
  • Add CI, stress-tests and automatic wheel releases to PyPI for Linux x86_64, Linux aarch64, and macOS arm64 (macOS 14+).

Säckli uses independent semantic versioning and does not track upstream Bagz releases.

Overview

Säckli is a format for storing a sequence of byte-array records. It supports per-record compression and fast index-based lookup. All indexing is zero based.

Installation

The recommended installation on Linux and Mac is via the pre-built wheels on PyPI. Releases include Linux x86_64 and aarch64 wheels, plus macOS arm64 wheels for macOS 14+:

uv pip install sackli

If you want to build locally to work on this, just uv pip install .. However, building can be slow because of GCS and S3 support; to skip both of these dependencies for much faster builds, you can do:

CMAKE_ARGS="-DSACKLI_ENABLE_GCS=OFF -DSACKLI_ENABLE_S3=OFF" uv pip install .

Python API

Python Reader

Reader for reading a single or sharded Säckli file-set.

from collections.abc import Sequence, Iterable

import sackli
import numpy as np

# Säckli Readers support random access. The order of elements within a Säckli
# file is the order in which they are written. Records are returned as `bytes`
# objects.
data = sackli.Reader('/path/to/data.bagz')

# Säckli Readers can be configured like this - here we require that the file was
# written with separate limits. All options can be passed directly as keyword
# arguments, and enum values can be given as (case-insensitive) strings:
data_separate_limits = sackli.Reader('/path/to/data.bagz',
                                     limits_placement='separate')

# The equivalent explicit form:
data_separate_limits = sackli.Reader('/path/to/data.bagz', sackli.Reader.Options(
    limits_placement=sackli.LimitsPlacement.SEPARATE,
))

# Säckli Readers are Sequences and support slicing, iterating, etc.
assert isinstance(data, Sequence)

# Säckli Readers have a length.
assert len(data) > 10

# Can access record by row-index.
fifth_value: bytes = data[5]

# Can slice.
data_from_5: sackli.Reader = data[5:]

# Slices are still Readers.
assert isinstance(data_from_5, sackli.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)

Note that the Sequence methods value in reader, reader.count(value) and reader.index(value) scan the records linearly — on a huge file-set they read (and decompress) everything up to the first match. Use sackli.Index / sackli.MultiIndex if you need repeated record lookups.

Readers hold open file handles (and mmaps) until garbage collected. They can be closed eagerly with reader.close() or by using the reader as a context manager; the underlying files close once the last handle sharing them (the reader, slices made from it, and live iterators) is closed or collected.

with sackli.Reader('/path/to/data.bagz') as data:
  first = data[0]

Warming limits before multi-process reads

Every record has an eight-byte limit entry that maps its index to its byte range. Large datasets can therefore have limits sections measured in gigabytes. Normally limits are read lazily: this avoids startup work, but many worker processes starting together can all first-touch the same limits at once. That synchronized traffic is particularly undesirable when the files are on NFS or another network filesystem.

Reader.warm_limits() synchronously reads every shard's limits, without reading any record payloads. Shards are warmed sequentially so that one warm-up does not itself fan out into a burst of shard reads.

For several processes on one machine, use limits_storage="on_disk" and have one designated process call warm_limits() before the other workers begin:

# Run once per machine, behind a node-local leader election or startup lock.
warmup_reader = sackli.Reader(
    '/path/on/nfs/data@1000.bagz',
    limits_storage='on_disk',
)
warmup_reader.warm_limits()

# Signal the other processes on this machine only after warming completes.

For normal POSIX files, ON_DISK limits use the filesystem page cache, so the warmed pages can be reused by other processes on the same machine. The cache is not shared between machines, and the operating system may evict pages under memory pressure. warm_limits() does not perform leader election or inter-process synchronization; callers must arrange the once-per-machine coordination themselves.

With limits_storage="in_memory", warm_limits() instead forces every shard's lazy private limits cache to be populated immediately. This can make later access latency predictable, but independently launched processes do not share those copies. Each process needs eight bytes per record: two billion records, for example, require about 16 GB (14.9 GiB) per process just for cached limits.

Calling warm_limits() on a sliced reader warms the complete underlying file-set shared by the slice. Repeating it in IN_MEMORY mode does not reload already populated limits; repeating it in ON_DISK mode reads the limits again and can be used to re-warm pages that may have been evicted.

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 = sackli.Reader('/path/to/keys.bag')
# Get the index of the first occurrence of key.
index = sackli.Index(keys)
key_index: int = index[b'example_key']

# Get all occurrences of key.
multi_index = sackli.MultiIndex(keys)
all_indices: list[int] = multi_index[b'example_key']

Python Writer

For writing a single Säckli file.

Example:

import sackli

# Compression is selected based on the file extension:
# `.bagz` will use Zstandard compression with default settings.
# `.bag` will use no compression.
with sackli.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 sackli.Writer(
    '/path/to/data.bagz',
    compression=sackli.CompressionZstd(level=3),
) as writer:
  for d in generate_records():
    writer.write(d)

Options

All options can be given either bundled in an Options object (sackli.Reader(path, sackli.Reader.Options(...))) or directly as keyword arguments (sackli.Reader(path, cache_policy='drop_after_read')); keyword arguments override the corresponding Options field. Enum-valued options accept the case-insensitive name of an enum value (e.g. 'random', 'in_memory'), and compression accepts 'auto', 'none' or 'zstd'.

Reader Options

sackli.Reader.Options has these optional arguments.

  • compression: Can be one of:
    • sackli.CompressionAutoDetect(): Default - Uses extension whether to compress. (.bagz - Compressed (ZStandard), .bag - Uncompressed)
    • sackli.CompressionNone(): Records are not decompressed.
    • sackli.CompressionZstd(): Records are decompressed using Zstandard.
  • limits_placement: Can be one of:
    • sackli.LimitsPlacement.TAIL: Default- Reads limits from a tail of file.
    • sackli.LimitsPlacement.SEPARATE: Reads limits from a separate file.
  • limits_storage: Can be one of:
    • sackli.LimitsStorage.ON_DISK: Default - Reads limits from disk for each read.
    • sackli.LimitsStorage.IN_MEMORY: On the first limits access to each shard, reads that shard's complete limits section into a private in-process cache. Use reader.warm_limits() to populate all shard caches explicitly.
  • access_pattern: Can be one of:
    • sackli.AccessPattern.SYSTEM: Default - no specific hint to the OS.
    • sackli.AccessPattern.RANDOM: Hints that you read entries in random order.
    • sackli.AccessPattern.SEQUENTIAL: Hints that you read entries roughly sequentially.
  • cache_policy: Can be one of:
    • sackli.CachePolicy.SYSTEM: Default - no specific hint to the OS.
    • sackli.CachePolicy.DROP_AFTER_READ: Reads data in such a way that the OS is unlikely to hold any of it in cache. For POSIX filesystems, this means using OS-specific no-cache hints: Linux uses pread with posix_fadvise, while macOS uses MAP_NOCACHE plus madvise for mmap-backed reads and F_NOCACHE for streaming reads. This is more efficient when you read more data than your RAM before doing any repeats (ie when an epoch is larger than RAM).
    • sackli.CachePolicy.DIRECT_IO: Uses O_DIRECT on Linux and F_NOCACHE on macOS to read records. This is the most aggressive os-cache avoidance option and can be best for random reads on huge data with rare re-reads. Linux empirically probes direct-I/O alignment from 512 bytes, caches the result per device, and treats STATX_DIOALIGN only as a hint. If direct I/O cannot be validated, it falls back to pread with cache-dropping advice. For the unaligned tail, it does a one-time standard read at init.
  • max_parallelism: Default number of threads when reading many records.
  • read_ahead_bytes: Byte budget that sizes the record batches the iterators read ahead (default 1 MiB). For compressed files, the budget counts compressed on-disk bytes; decompressed records can require substantially more RAM, especially for high compression ratios or large records. The double-buffered iterator can hold up to two batches in flight, so this is a sizing heuristic rather than a memory bound. The read_ahead argument of read_indices_iter/read_range_iter, which counts records, takes precedence when given.
  • sharding_layout: Can be one of:
    • sackli.ShardingLayout.CONCATENATED: Default - See Sharding
    • sackli.ShardingLayout.INTERLEAVED: See Sharding

access_pattern and cache_policy are currently interpreted only for local POSIX files and influence OS-level behaviour on page cache and cache lines.

For tail-formatted files, non-default POSIX record-cache policies open a second POSIX read handle to the same file so limits metadata reads keep the default cache policy.

Writer Options

sackli.Writer.Options has these optional arguments.

  • compression: Can be one of:
    • sackli.CompressionAutoDetect(): Default - Uses extension whether to compress. (.bagz - Compressed (Zstandard), .bag - Uncompressed)
    • sackli.CompressionNone(): Records are not compressed.
    • sackli.CompressionZstd(level = 3): Records are compressed using Zstandard the level of the compression can be specified.
  • limits_placement: Can be one of:
    • sackli.LimitsPlacement.TAIL: Default - Writes limits to a tail of file.
    • sackli.LimitsPlacement.SEPARATE: Writes limits to a separate file.

Sharding

An ordered collection of Säckli-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:

  1. Concatenated (default). Indexing is equivalent to the records in each Säckli-formatted shard being concatenated into a single sequence of records.

    Example:

    When opening four Säckli-formatted files with sizes [8, 4, 0, 5], the global-index with range [0, 17) (shown as the table entries) maps to shard and shard-index like 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 16
    

    Mappings

    global-index shard shard-index
    0 00000-of-00004 0
    1 00000-of-00004 1
    2 00000-of-00004 2
    ... ... ...
    8 00001-of-00004 0
    9 00001-of-00004 1
    ... ... ...
    15 00003-of-00004 3
    16 00003-of-00004 4
  2. Interleaved where the global-index is interleaved in a round-robin manner across all the shards.

    Example:

    When opening three Säckli-formatted files with sizes [6, 6, 5], the global-index with range [0, 17) (shown as the table entries) maps to shard and shard-index like 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 14
    

    Mappings

    global-index shard shard-index
    0 00000-of-00003 0
    1 00001-of-00003 0
    2 00002-of-00003 0
    ... ... ...
    6 00000-of-00003 2
    7 00001-of-00003 2
    8 00002-of-00003 2
    ... ... ...
    15 00000-of-00003 5
    16 00001-of-00003 5

Apache Beam Support

Säckli also provides Apache Beam connectors for reading and writing Säckli files in Beam pipelines.

Ensure you have Apache Beam installed.

uv pip install apache_beam

Säckli Source

import apache_beam as beam
from sackli.beam import sacklio
import tensorflow as tf

with beam.Pipeline() as pipeline:
  examples = (
      pipeline
      | 'ReadData' >> sacklio.ReadFromSackli('/path/to/your/data@*.bagz')
      | 'Decode' >> beam.Map(tf.train.Example.FromString)
  )
  # Continue your pipeline.

Säckli Sink

from sackli.beam import sacklio
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' >> sacklio.WriteToSackli('/path/to/output/data@*.bagz')
  )

Cloud Storage

Säckli supports POSIX file-systems, Google Cloud Storage (GCS), and Amazon S3. These can be enabled or disabled at compile-time, but the PyPI-deployed wheels have support for both built-in.

GCS authentication

These examples assume you have the gcloud CLI installed.

gcloud config set project your-project-name
gcloud auth application-default login

S3 authentication

Authentication uses the standard AWS credential chain (environment variables, ~/.aws/credentials, IAM roles, etc.).

aws configure

Paths

Use the gs: and s3: file-system prefixes in paths.

import pathlib
import sackli

# This may freeze if you have not configured the GCS project.
gcs_reader = sackli.Reader('gs://your-bucket-name/your-file.bagz')
s3_reader = sackli.Reader('s3://your-bucket-name/your-file.bagz')

# Path supports a leading slash to work well with pathlib.
gcs_bucket = pathlib.Path('/gs://your-bucket-name')
gcs_reader = sackli.Reader(gcs_bucket / 'your-file.bagz')

s3_bucket = pathlib.Path('/s3://your-bucket-name')
s3_reader = sackli.Reader(s3_bucket / 'your-file.bagz')

Säckli/Bagz file format

For now, Säckli still preserves exactly the Bagz file format. However, this is not guaranteed to remain the case.

The Bagz file format has two parts: the records section and the limits section.

  • The records section 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 limits section 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 as tail-limits in one file, where the limits section is appended to the records section, or as separate-limits, where they are stored in separate files.

Tail-limits example

Given a Bagz-formatted file with the following 3 uncompressed records:

Records
abcdef
123
catcat

The raw bytes of the Bagz-formatted 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.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

sackli-0.3.4.tar.gz (116.0 kB view details)

Uploaded Source

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

sackli-0.3.4-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl (12.0 MB view details)

Uploaded CPython 3.14tmanylinux: glibc 2.27+ x86-64manylinux: glibc 2.28+ x86-64

sackli-0.3.4-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl (11.1 MB view details)

Uploaded CPython 3.14tmanylinux: glibc 2.27+ ARM64manylinux: glibc 2.28+ ARM64

sackli-0.3.4-cp314-cp314t-macosx_14_0_arm64.whl (5.8 MB view details)

Uploaded CPython 3.14tmacOS 14.0+ ARM64

sackli-0.3.4-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl (12.0 MB view details)

Uploaded CPython 3.14manylinux: glibc 2.27+ x86-64manylinux: glibc 2.28+ x86-64

sackli-0.3.4-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl (11.1 MB view details)

Uploaded CPython 3.14manylinux: glibc 2.27+ ARM64manylinux: glibc 2.28+ ARM64

sackli-0.3.4-cp314-cp314-macosx_14_0_arm64.whl (5.8 MB view details)

Uploaded CPython 3.14macOS 14.0+ ARM64

sackli-0.3.4-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl (12.0 MB view details)

Uploaded CPython 3.13manylinux: glibc 2.27+ x86-64manylinux: glibc 2.28+ x86-64

sackli-0.3.4-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl (11.1 MB view details)

Uploaded CPython 3.13manylinux: glibc 2.27+ ARM64manylinux: glibc 2.28+ ARM64

sackli-0.3.4-cp313-cp313-macosx_14_0_arm64.whl (5.8 MB view details)

Uploaded CPython 3.13macOS 14.0+ ARM64

sackli-0.3.4-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl (12.0 MB view details)

Uploaded CPython 3.12manylinux: glibc 2.27+ x86-64manylinux: glibc 2.28+ x86-64

sackli-0.3.4-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl (11.1 MB view details)

Uploaded CPython 3.12manylinux: glibc 2.27+ ARM64manylinux: glibc 2.28+ ARM64

sackli-0.3.4-cp312-cp312-macosx_14_0_arm64.whl (5.8 MB view details)

Uploaded CPython 3.12macOS 14.0+ ARM64

File details

Details for the file sackli-0.3.4.tar.gz.

File metadata

  • Download URL: sackli-0.3.4.tar.gz
  • Upload date:
  • Size: 116.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for sackli-0.3.4.tar.gz
Algorithm Hash digest
SHA256 d1fe3eca7fdb2bd0b0c5a2e4c61c32644da8080ae00ad64504a3d2df5940b9ac
MD5 1a9b868e6206b13c4d7194a85253557c
BLAKE2b-256 8e6587ba14e8f0814f9a5751f48f205f9545e9ba814dfb5c41d9bb463df7c79f

See more details on using hashes here.

Provenance

The following attestation bundles were made for sackli-0.3.4.tar.gz:

Publisher: publish.yml on lucasb-eyer/sackli

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file sackli-0.3.4-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for sackli-0.3.4-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 deee0fe5305c4edf2e4b6292c0ddf58711649fc576ed48db42fca86d5b1ecc02
MD5 2b699af6446b33e9a318847db05aa437
BLAKE2b-256 8fc08ae32a84ad7d5f76777d69ad64a625fe51a8e144cc3b5f721d0a3a75f977

See more details on using hashes here.

Provenance

The following attestation bundles were made for sackli-0.3.4-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl:

Publisher: publish.yml on lucasb-eyer/sackli

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file sackli-0.3.4-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl.

File metadata

File hashes

Hashes for sackli-0.3.4-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl
Algorithm Hash digest
SHA256 1556c6a4cc58651f7f286cac1c0b370a30c6db6a5af3a0264038b8bd0efa8f99
MD5 08f42f0f3d2087e8d290ab8286fbcf13
BLAKE2b-256 36edca4d75be6fc79c09650908c1f32f45bc3cdcf83da95309a91f3bed5045e8

See more details on using hashes here.

Provenance

The following attestation bundles were made for sackli-0.3.4-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl:

Publisher: publish.yml on lucasb-eyer/sackli

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file sackli-0.3.4-cp314-cp314t-macosx_14_0_arm64.whl.

File metadata

File hashes

Hashes for sackli-0.3.4-cp314-cp314t-macosx_14_0_arm64.whl
Algorithm Hash digest
SHA256 d98cbd7bbd791eb81d238d075a723b0617206f511515d9ada6f6bc99716b8347
MD5 d3205993ee4c8ff4424f3b913c92b3fa
BLAKE2b-256 95c49378cf8fa896c86c4fd55146bc44fd93f5f7eb41441f6926d85d04efa621

See more details on using hashes here.

Provenance

The following attestation bundles were made for sackli-0.3.4-cp314-cp314t-macosx_14_0_arm64.whl:

Publisher: publish.yml on lucasb-eyer/sackli

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file sackli-0.3.4-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for sackli-0.3.4-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 725af48f4c14860a0af64d978b6c438cec4b23c3cc4b94c5efe70fa534acbc74
MD5 b33fd666f5911cb4e5840bc99feb9056
BLAKE2b-256 387bc2a94a74396cbfc12522a587ea2a5abb2cccd2557ae808a0ded1ae7a124a

See more details on using hashes here.

Provenance

The following attestation bundles were made for sackli-0.3.4-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl:

Publisher: publish.yml on lucasb-eyer/sackli

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file sackli-0.3.4-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl.

File metadata

File hashes

Hashes for sackli-0.3.4-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl
Algorithm Hash digest
SHA256 d973f4ee23eee73bcd1aceb098e87b4af74346d8671b0621be1c0a2358d1ffda
MD5 d1f5f0ed4fa8436ab9c93019f4258495
BLAKE2b-256 854f4b340f66a0c23871572e23707f4b68f3271cdcd5dde1eba6adc9b2d73a37

See more details on using hashes here.

Provenance

The following attestation bundles were made for sackli-0.3.4-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl:

Publisher: publish.yml on lucasb-eyer/sackli

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file sackli-0.3.4-cp314-cp314-macosx_14_0_arm64.whl.

File metadata

File hashes

Hashes for sackli-0.3.4-cp314-cp314-macosx_14_0_arm64.whl
Algorithm Hash digest
SHA256 0bc3590bdfb4235b034cb8d80ddd9d6bb3aa96202fd301c1bfbc18a6a0f26865
MD5 0289d794e96612d9550e58036aaa5922
BLAKE2b-256 9f8fcf9c17d9fa61c8dc59271c9a34f80824d57e536bc10488bf51cba6fbc3fb

See more details on using hashes here.

Provenance

The following attestation bundles were made for sackli-0.3.4-cp314-cp314-macosx_14_0_arm64.whl:

Publisher: publish.yml on lucasb-eyer/sackli

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file sackli-0.3.4-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for sackli-0.3.4-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 48d945abc2f267db2a3cb17e30bab350a889dfa563d1bb072ded14c8057cb9e6
MD5 7e4ec28f80185bb3701d01df87a9e856
BLAKE2b-256 781fd699f874fdb660e4d0276d7e71ca2d735e81e73f07abd4ebd9cea7e1c82e

See more details on using hashes here.

Provenance

The following attestation bundles were made for sackli-0.3.4-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl:

Publisher: publish.yml on lucasb-eyer/sackli

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file sackli-0.3.4-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl.

File metadata

File hashes

Hashes for sackli-0.3.4-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl
Algorithm Hash digest
SHA256 e1364dd24116d5e6cc609f3dc4f050326c4f25ba923bd748ff62c289a1b4f0de
MD5 494b4ac0eeadc45af8b00d5d73b54598
BLAKE2b-256 6515d4eb421dceaf5944e00477bbaf0932af88f31ba3f998630e431760bc95ae

See more details on using hashes here.

Provenance

The following attestation bundles were made for sackli-0.3.4-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl:

Publisher: publish.yml on lucasb-eyer/sackli

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file sackli-0.3.4-cp313-cp313-macosx_14_0_arm64.whl.

File metadata

File hashes

Hashes for sackli-0.3.4-cp313-cp313-macosx_14_0_arm64.whl
Algorithm Hash digest
SHA256 e7e5b086910e32d4d5d7e7b4f1564100d9442cbfb4fa7133524b6295b805bac5
MD5 385e46931e0e82629100d696f784a79c
BLAKE2b-256 d9055a448ab712b97753665d7a49d4a862f23e5541290b18dcbb744fbdad75c1

See more details on using hashes here.

Provenance

The following attestation bundles were made for sackli-0.3.4-cp313-cp313-macosx_14_0_arm64.whl:

Publisher: publish.yml on lucasb-eyer/sackli

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file sackli-0.3.4-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for sackli-0.3.4-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 0a0ac4346a70b3c9851c9907f780f710648ccf68994c7453723628f532ebfb44
MD5 7d9e6dfaff6d71cdb93fd258e6e22c58
BLAKE2b-256 be7d427d06030c96bb9dfbb4d2743c4224a3e6d167c37ce40459669c3a103c39

See more details on using hashes here.

Provenance

The following attestation bundles were made for sackli-0.3.4-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl:

Publisher: publish.yml on lucasb-eyer/sackli

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file sackli-0.3.4-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl.

File metadata

File hashes

Hashes for sackli-0.3.4-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl
Algorithm Hash digest
SHA256 3b180af19d8d21240b1353c13e5533d960d937936898fd364e19fa6c4bcdac31
MD5 36f5725105a61c268e7a7281bd9940e7
BLAKE2b-256 d1809636b8e713eca0daff648b15721d1dde9b168519ebb5d62428aa4e9ece50

See more details on using hashes here.

Provenance

The following attestation bundles were made for sackli-0.3.4-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl:

Publisher: publish.yml on lucasb-eyer/sackli

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file sackli-0.3.4-cp312-cp312-macosx_14_0_arm64.whl.

File metadata

File hashes

Hashes for sackli-0.3.4-cp312-cp312-macosx_14_0_arm64.whl
Algorithm Hash digest
SHA256 d5fc000de90fcbf54a0de61acbe4141739012f2ab31880e4f06f1e70930567be
MD5 5e76e1d0edb512ca5432ade8894824b0
BLAKE2b-256 fb7bbdca0762015916810e209ae9673daedf1481fb208e3693d3718a38282a78

See more details on using hashes here.

Provenance

The following attestation bundles were made for sackli-0.3.4-cp312-cp312-macosx_14_0_arm64.whl:

Publisher: publish.yml on lucasb-eyer/sackli

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

This release

0.3.4 This release

13 files

0.3.3

13 files

0.3.2

13 files

0.3.1

13 files

0.3.0

9 files

0.2.8

9 files

0.2.7

9 files

0.2.6

6 files

0.2.5

5 files

0.2.3

5 files

0.2.2

2 files

0.2.1

2 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