mrkeyoor.com_
Sat 19 Sept 21:41 UTC
PyPIAI / MLupdated 19 Sept 2026

safetensors review

safetensors 0.8.0 writes named tensors as a bounded JSON header followed by contiguous little-endian data. The format cannot carry Python objects or executable pickle instructions, and safe_open() can inspect names or read selected tensors and slices without materializing a full checkpoint. Framework adapters cover PyTorch, NumPy, TensorFlow, JAX/Flax, Paddle, and MLX over a Rust extension. Version 0.8.0 adds direct Apple Silicon MPS loading, a pread backend, ellipsis and stepped slices, MUSA devices, two AMD FNUZ FP8 dtypes, GIL-free serialization, and new Windows ARM64 and RISC-V wheels. It also requires Python 3.10 and changes the low-level serialize input to TensorSpec.

Verdict

safetensors 0.8.0 installed in 0.3 seconds and used 2 MB in our sandbox, with a 0.01-second import, typed-package metadata, and 0 audit findings; it is the better default for shared model weights that do not need pickle objects. Keep training state elsewhere, use save_model() for tied weights, and remember that a non-executable file format does not certify the model inside it.

We installed it

Lab card: what happened when we installed safetensorsScreenshot of safetensors documentation
Install✓ · 0.3s1 package on disk · 2 MB
Importimport safetensors in 0.01s · compiled extensions · py.typed · requires Python >=3.10
Known vulns0(pip-audit)

Answers from our run

Does safetensors install cleanly?

Yes. In a fresh container with an empty cache, pip install safetensors finished in 0.3s, leaving 1 package and 2 MB on disk. pip-audit reported no known vulnerabilities.

What does safetensors need to run?

Python >=3.10, and a platform wheel with compiled extensions. In our run import safetensors succeeded in 0.01s, and the package ships py.typed for type checkers.

safetensors or torch: which should you use?

torch: Use torch.save for a trusted, Python-specific training checkpoint containing optimizer and other non-tensor state. safetensors 0.8.0 installed in 0.3 seconds and used 2 MB in our sandbox, with a 0.01-second import, typed-package metadata, and 0 audit findings; it is the better default for shared model weights that do not need pickle objects.

When should you not use safetensors?

One artifact must preserve optimizer objects, scheduler state, RNG state, custom classes, and nested training metadata. The format accepts tensors plus string-to-string metadata.

API stability4/5The compact on-disk layout and high-level safe_open, load_file, save_file, save_model, and load_model calls remain recognizable in 0.8.0. This release does break direct callers of serialize and serialize_file by replacing mapping-shaped descriptions with TensorSpec, and it drops Python 3.9. New backends, devices, slicing rules, and sub-byte dtypes are still arriving before 1.0, so pin the package when those paths matter.
Docs4/5The README specifies the 8-byte header length, JSON header, tensor offsets, little-endian C-order data, duplicate-key ban, full-buffer coverage, string-only metadata, and lack of NaN or infinity validation. The official documentation returns HTTP 200 and covers framework adapters, shared PyTorch tensors, lazy slicing, and conversion. Backend selection and complete checkpoint design still require reading several API and concept pages rather than one deployment guide.
Maintenance5/5Version 0.8.0 shipped on June 9, 2026 with new wheel targets, devices, dtypes, read backends, slicing behavior, GIL-free writes, and security-audit CI. GitHub shows an unarchived repository pushed on August 26, 2026, 3,876 stars, and 77 open issues and pull requests. The project has also joined the PyTorch Foundation. The pace is active enough that low-level consumers should read every release note before upgrading.
Ecosystem5/5PyPI Stats counted 24,733,148 downloads in the latest week, while GitHub reports 3,876 stars. Framework modules cover PyTorch, NumPy, TensorFlow, JAX/Flax, Paddle, and MLX, and the documented byte layout supports readers outside Python. Hugging Face model distribution makes the extension familiar across Transformers and Diffusers workflows. That reach concerns weight interchange; it does not turn the format into a general training-state container.

Use it if

  • Model weights cross a trust boundary and the loader must avoid pickle's ability to construct Python objects.
  • A Transformers, Diffusers, or Hub workflow already publishes named .safetensors files or shards.
  • Distributed startup should inspect a header and read only the tensor names or row slices assigned to one rank.
  • Apple Silicon PyTorch inference can use version 0.8.0's pread path to load directly into shared MPS buffers.
Skip it if

Setup reality

Our safetensors 0.8.0 install completed in 0.3 seconds in a fresh Python 3.12 Bookworm container. It left one package and 2 MB on disk; pip-audit found 0 known vulnerabilities. Package metadata recorded 36 direct dependencies, Python 3.10 or newer, and the Apache Software License. import safetensors worked in 0.01 seconds. The wheel contains compiled .so code and a py.typed marker. This base check did not install PyTorch or another tensor framework.

Choose the adapter that owns your tensors, such as safetensors.torch or safetensors.numpy. Those framework packages are separate requirements. Building from source needs Rust and Python build tooling; version 0.8.0 publishes additional Windows ARM64 and RISC-V wheels, but an unsupported target can still reach that native build path. The file carries named tensors plus optional string-to-string metadata. Numbers or nested metadata need an encoding your readers agree on.

PyTorch save_file() accepts dense, contiguous tensors and refuses overlapping storage. Tied embeddings therefore need safetensors.torch.save_model(), which selects one stored name and records how omitted names relate; load_model() restores values into the model. Do not treat a weights file as a full training checkpoint. Keep optimizer, scheduler, RNG, data-loader, and scalar step state in a separate explicit artifact. A crash-consistent publication flow should write and verify the file before replacing the advertised checkpoint.

safe_open() defaults to mmap and can return one tensor or a get_slice() view. Version 0.8.0 adds backend='pread', plus ellipsis and step handling such as [:, ::8] wherever the library performs slicing. The MPS pread path can fill shared Metal buffers directly; CUDA still requires data movement to device memory. Low-level serialize() and serialize_file() now take TensorSpec objects and release the GIL. Do not mutate their source buffers while another thread is writing them.

Patterns

Write a PyTorch tensor dictionary save-pytorch-tensors

import torch
from safetensors.torch import save_file

weights = {
    'encoder.weight': torch.zeros((1024, 1024)),
    'encoder.bias': torch.zeros(1024),
}
save_file(weights, 'model.safetensors')

save_file() requires dense contiguous tensors without overlapping storage. Use save_model() when a model ties parameter storage.

Load every tensor onto the CPU load-pytorch-file

from safetensors.torch import load_file

weights = load_file('model.safetensors', device='cpu')
print(weights['encoder.weight'].shape)

load_file() returns the full name-to-tensor dictionary. Use safe_open() when only a few entries from a large file are needed.

Read one named tensor lazily lazy-load-one-tensor

from safetensors import safe_open

with safe_open(
    'model.safetensors', framework='pt', device='cpu'
) as handle:
    names = list(handle.keys())
    embedding = handle.get_tensor('model.embed_tokens.weight')

The file stays mapped inside the context. Match framework='pt' with PyTorch; other adapters use their documented framework value.

Load selected rows and columns read-tensor-slice

from safetensors import safe_open

with safe_open('model.safetensors', framework='pt') as handle:
    view = handle.get_slice('model.embed_tokens.weight')
    print(view.get_shape())
    shard = view[1000:2000, ::8]

Version 0.8.0 honors slice steps where safetensors performs the read. get_slice() avoids materializing the complete tensor first.

Read tensors with pread instead of mmap use-pread-backend

from safetensors.torch import load_file

weights = load_file(
    'model.safetensors',
    device='cpu',
    backend='pread',
)

Version 0.8.0 adds backend='pread' alongside the default mmap backend. Test both on the actual filesystem and access pattern.

Use the Apple Silicon MPS path load-directly-to-mps

from safetensors import safe_open

with safe_open(
    'model.safetensors',
    framework='pt',
    device='mps',
    backend='pread',
) as handle:
    state_dict = handle.get_tensors()

The 0.8.0 pread path can fill shared Metal buffers and pass them to PyTorch through DLPack. This path is specific to Apple Silicon MPS.

Store a small metadata map attach-string-metadata

from safetensors.torch import save_file

save_file(
    weights,
    'model.safetensors',
    metadata={'framework': 'pt', 'training_step': '12000'},
)

from safetensors import safe_open
with safe_open('model.safetensors', framework='pt') as handle:
    print(handle.metadata())

Metadata keys and values must be strings. Encode numeric or nested state yourself, and do not use this map as an optimizer checkpoint.

Handle shared PyTorch parameters save-model-with-tied-weights

from safetensors.torch import save_model, load_model

save_model(model, 'model.safetensors')

restored = MyModel()
missing, unexpected = load_model(
    restored, 'model.safetensors', strict=False
)
print(missing, unexpected)

save_model() removes duplicate shared storage and records which names point to the kept tensor. Raw save_file() rejects that overlap.

Exchange ordinary NumPy arrays roundtrip-numpy-arrays

import numpy as np
from safetensors.numpy import save_file, load_file

arrays = {
    'embedding': np.zeros((100, 768), dtype=np.float32)
}
save_file(arrays, 'embedding.safetensors')
restored = load_file('embedding.safetensors')

The bytes are framework-neutral when both sides support the dtype. The NumPy adapter still requires dense contiguous arrays.

List names, shapes, and dtypes inspect-header-only

from safetensors import safe_open

with safe_open('model.safetensors', framework='pt') as handle:
    for name in handle.keys():
        view = handle.get_slice(name)
        print(name, view.get_shape(), view.get_dtype())

Shape and dtype come from the header, so this loop does not allocate every tensor. Values such as NaN and infinity are not validated there.

Create an in-memory safetensors blob serialize-to-bytes

import torch
from safetensors.torch import save, load

blob = save({'weight': torch.ones(4)})
restored = load(blob)
print(restored['weight'])

The bytes API materializes the complete serialized artifact in process memory. Prefer file APIs for checkpoints too large to duplicate in RAM.

Convert a trusted PyTorch weights file convert-pickle-weights

import torch
from safetensors.torch import save_file

state = torch.load(
    'pytorch_model.bin',
    map_location='cpu',
    weights_only=True,
)
contiguous = {name: value.contiguous() for name, value in state.items()}
save_file(contiguous, 'model.safetensors')

Conversion must open the original pickle-based file. Run untrusted conversion in an isolated environment even with weights_only=True.

Alternatives

PackageRegistryPick it when
torchPyPIUse torch.save for a trusted, Python-specific training checkpoint containing optimizer and other non-tensor state.
numpyPyPIUse .npy or .npz for general NumPy arrays when framework-neutral model conventions and newer ML dtypes are unnecessary.
h5pyPyPIUse HDF5 when hierarchical groups, chunking, compression, and partial scientific-array access matter more than a small weights format.

More ai / ml guides

openai · mcp · huggingface-hub · @modelcontextprotocol/sdk · scikit-learn · tiktoken · the whole shelf →

How this guide is made: grounded in the library's documentation, release notes, changelog, and issue history, on a fixed rubric — not a hands-on install of every release. The 50 most-downloaded entries are additionally install-verified in clean containers. Corrections: contact the desk.