Skip to main content

FastProto is fast and efficient protobuf library for Python, built on the top of Rust.

Project description

FastProto

FastProto is a fast, ergonomic Protocol Buffers library for Python. Messages are plain, readable @dataclass types — no generated getters/setters, no Message reflection API to learn — while all encoding and decoding happens in a compiled Rust core.

  • Idiomatic messages. Generated code is a @dataclass, annotated with plain Python types (str, int, list[...], dict[...], | None). Autocomplete, type checkers, and repr() all just work.
  • Rust-powered wire codec. Encoding and decoding are implemented in Rust via PyO3, avoiding the overhead of pure-Python protobuf implementations.
  • Wire-compatible. Bytes produced by FastProto are read correctly by Google's reference protobuf runtime, and vice versa.
  • Drop-in protoc plugin. Reuses the standard .proto toolchain — run protoc with --fastproto_out to generate typed dataclasses.

Installation

pip install fastproto

Generating code from .proto files also requires the protoc compiler and the plugin's protobuf dependency:

pip install "fastproto[plugin]"

protoc itself isn't installed by pip — grab it from your package manager (apt install protobuf-compiler, brew install protobuf, ...) or the official releases.

Quick start

1. Write a .proto file (user.proto):

syntax = "proto3";
package example;

enum Role {
  ROLE_UNSPECIFIED = 0;
  ROLE_ADMIN = 1;
  ROLE_USER = 2;
}

message Address {
  string city = 1;
  string street = 2;
}

message User {
  int64 id = 1;
  string name = 2;
  optional string email = 3;
  Role role = 4;
  repeated string tags = 5;
  Address address = 6;
  map<string, int32> counters = 7;
}

2. Generate the Python module with protoc, using the fastproto plugin:

protoc --proto_path=. --fastproto_out=. user.proto

This produces user_pb.py — a plain, readable dataclass module:

# @generated by fastproto. DO NOT EDIT.
# source: user.proto
from dataclasses import dataclass, field
from enum import IntEnum

from fastproto import Message, Scalar, message


class Role(IntEnum):
    ROLE_UNSPECIFIED = 0
    ROLE_ADMIN = 1
    ROLE_USER = 2


@message(_ADDRESS_DESCRIPTOR)
@dataclass(slots=True)
class Address(Message):
    city: Scalar.String = ""
    street: Scalar.String = ""


@message(_USER_DESCRIPTOR)
@dataclass(slots=True)
class User(Message):
    id: Scalar.Int64 = 0
    name: Scalar.String = ""
    email: Scalar.String | None = None
    role: Role = Role(0)
    tags: list[Scalar.String] = field(default_factory=list)
    address: "Address | None" = None
    counters: dict[Scalar.String, Scalar.Int32] = field(default_factory=dict)

3. Use it like any other Python dataclass:

from user_pb import Address, Role, User

user = User(
    id=42,
    name="Ada",
    email="ada@example.com",
    role=Role.ROLE_ADMIN,
    tags=["vip", "beta"],
    address=Address(city="London", street="Baker St"),
    counters={"logins": 7},
)

# Serialize to protobuf wire bytes.
data = user.to_bytes()

# Deserialize back into a `User` instance.
same_user = User.from_bytes(data)
assert same_user == user

That's it — no SerializeToString() / ParseFromString() ceremony, no ListFields() reflection, just to_bytes() / from_bytes() on a dataclass you can construct, compare, and pretty-print directly.

Field type mapping

Every proto scalar type has a corresponding alias under fastproto.Scalar. Each alias is just the underlying Python type (int, str, ...) tagged with an Annotated[...] marker, so it type-checks exactly as you'd expect while still documenting the precise wire type:

proto type Python annotation
double Scalar.Double
float Scalar.Float
int32 Scalar.Int32
int64 Scalar.Int64
uint32 Scalar.UInt32
uint64 Scalar.UInt64
sint32 Scalar.SInt32
sint64 Scalar.SInt64
fixed32 Scalar.Fixed32
fixed64 Scalar.Fixed64
sfixed32 Scalar.SFixed32
sfixed64 Scalar.SFixed64
bool Scalar.Bool
string Scalar.String
bytes Scalar.Bytes

Composite fields map the way you'd hope:

  • repeated Tlist[T]
  • map<K, V>dict[K, V]
  • optional T / oneof members → T | None
  • nested/enum messages → the generated class or IntEnum, referenced by name

Enums, nested messages, and oneof

Enums become IntEnum subclasses; message fields hold real instances of the generated dataclass (or None when unset); oneof groups are represented as plain optional fields, and FastProto enforces "at most one set" at encode time:

from user_pb import User

# Setting more than one oneof member raises when you try to serialize it.
User(phone="123", telegram="abc").to_bytes()  # raises ValueError: ... oneof ...

Presence semantics

FastProto follows proto3 field presence rules:

  • Plain scalar fields (string, int32, ...) use their zero value as the default and are not nullable — they always round-trip to a concrete value.
  • optional scalar fields, message fields, and oneof members are nullable (T | None) and track explicit presence, matching proto3 semantics exactly (an explicitly-set empty string is distinguishable from an unset field).
from user_pb import User

empty = User()
assert empty.to_bytes() == b""      # all-default messages encode to zero bytes
assert User.from_bytes(b"") == empty
assert empty.email is None           # optional, unset

How linking works

Generated dataclasses reference sibling messages and enums by name (as forward references), since a message can reference a type defined later in the same file, or itself (recursively). FastProto resolves these references lazily, on first to_bytes() / from_bytes() call, by looking them up in the generated module's namespace — you never need to call anything yourself.

Development

The project is a mixed Rust/Python codebase built with maturin, managed with uv.

git clone https://github.com/pkozhem/fastproto
cd fastproto
uv sync                        # installs dev dependencies and builds the extension
uv run maturin develop         # rebuild the Rust extension in-place after changes
uv run pytest                  # run the test suite
uv run ruff check .            # lint
uv run ty check                # type-check

Test fixtures under tests/generated/ are themselves @generated output, committed so the plugin's golden tests can diff against them. After editing a .proto file under tests/protos/ or the plugin itself, regenerate them with:

uv run python scripts/regen.py

License

MIT

Project details


Download files

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

Source Distribution

fastproto-0.1.2.tar.gz (41.7 kB view details)

Uploaded Source

Built Distributions

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

fastproto-0.1.2-cp312-abi3-win_amd64.whl (167.8 kB view details)

Uploaded CPython 3.12+Windows x86-64

fastproto-0.1.2-cp312-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (310.5 kB view details)

Uploaded CPython 3.12+manylinux: glibc 2.17+ x86-64

fastproto-0.1.2-cp312-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (307.3 kB view details)

Uploaded CPython 3.12+manylinux: glibc 2.17+ ARM64

fastproto-0.1.2-cp312-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl (532.8 kB view details)

Uploaded CPython 3.12+macOS 10.12+ universal2 (ARM64, x86-64)macOS 10.12+ x86-64macOS 11.0+ ARM64

File details

Details for the file fastproto-0.1.2.tar.gz.

File metadata

  • Download URL: fastproto-0.1.2.tar.gz
  • Upload date:
  • Size: 41.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for fastproto-0.1.2.tar.gz
Algorithm Hash digest
SHA256 96be4bdb60be723af0f2366fb009aadd23ca1a57f2a2aa2d2db4a3bb4e114eb6
MD5 649d00038e2271f5b18e9b77871117e5
BLAKE2b-256 734904d8be1af29466552f14389655458ee0bb5bc87d151fe115a2c4d4646508

See more details on using hashes here.

Provenance

The following attestation bundles were made for fastproto-0.1.2.tar.gz:

Publisher: release.yml on pkozhem/fastproto

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

File details

Details for the file fastproto-0.1.2-cp312-abi3-win_amd64.whl.

File metadata

  • Download URL: fastproto-0.1.2-cp312-abi3-win_amd64.whl
  • Upload date:
  • Size: 167.8 kB
  • Tags: CPython 3.12+, Windows x86-64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for fastproto-0.1.2-cp312-abi3-win_amd64.whl
Algorithm Hash digest
SHA256 06b112b164a2983369179a454ea168870e94f1f31d7e9ffdd1187e6c4e98df54
MD5 58f55da307670ab2ff58aa6a6bd6ccdd
BLAKE2b-256 4df0b27c8a6329661d248cb742288823384c10ad419e448bfbbff9a52650ac1e

See more details on using hashes here.

Provenance

The following attestation bundles were made for fastproto-0.1.2-cp312-abi3-win_amd64.whl:

Publisher: release.yml on pkozhem/fastproto

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

File details

Details for the file fastproto-0.1.2-cp312-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for fastproto-0.1.2-cp312-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 b6c86750b0f749367b3306bf80fe434b15581afe956d5100ef1b3f7f49ea6de9
MD5 2a318735b12daa4931d78648f1e476b3
BLAKE2b-256 4040815b8a46a7b15665bde4a0603d3459515fd85d2a9da57e5984ef205b0c7b

See more details on using hashes here.

Provenance

The following attestation bundles were made for fastproto-0.1.2-cp312-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl:

Publisher: release.yml on pkozhem/fastproto

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

File details

Details for the file fastproto-0.1.2-cp312-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.

File metadata

File hashes

Hashes for fastproto-0.1.2-cp312-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 2f9a231ef53ada0011ea1f434011dfc44f5acda818c8103c3ba3477562d09433
MD5 30f6e51232470e01e0b055a3f582c3eb
BLAKE2b-256 f2d0b32712f0950065496fcc194c577ad401d71717f3465f8713d878c55eeebc

See more details on using hashes here.

Provenance

The following attestation bundles were made for fastproto-0.1.2-cp312-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl:

Publisher: release.yml on pkozhem/fastproto

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

File details

Details for the file fastproto-0.1.2-cp312-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl.

File metadata

File hashes

Hashes for fastproto-0.1.2-cp312-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl
Algorithm Hash digest
SHA256 e2cb07a24a5f1020b486fea794abb855b8a28c5c9c0e041d1bc43a14e5ab3fe4
MD5 b0a9c90439ae253ca8f2e530dbd3977a
BLAKE2b-256 46df79bb6f69731f267e432c989e96fc0b2ac39d96c74c6be0eb5c0fe7d51922

See more details on using hashes here.

Provenance

The following attestation bundles were made for fastproto-0.1.2-cp312-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl:

Publisher: release.yml on pkozhem/fastproto

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

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page