Python#
The BSR exposes a PEP 503 simple-repository at https://buf.build/gen/python, so any Python package manager that speaks PEP 503 can install BSR-generated SDKs.
A first install:
For an end-to-end walkthrough, see the generated SDKs quickstart.
Configure pip#
Pass --extra-index-url https://buf.build/gen/python to every pip invocation, or configure pip to include it by default:
Authenticate for private modules#
pip reads ~/.netrc for private repositories.
The recommended way to populate it is buf registry login, which writes a machine <host> login <username> password <token> entry for the BSR host:
Hand-editing ~/.netrc with the same machine/login/password shape also works.
For full credential setup, see Authentication.
Install an SDK#
To install a generated SDK, run pip install with the SDK name.
For example, to install connectrpc/eliza code generated by protocolbuffers/python:
The generated Python code mirrors the source Protobuf package structure, so the import path follows the same shape:
Package names#
The BSR Python repository names every SDK as:
For example, connectrpc-eliza-protocolbuffers-python contains code for the connectrpc/eliza module produced by the protocolbuffers/python plugin.
Versions#
{pluginVersion}.{pluginRevision}.{commitTimestamp}+{commitShortName}
# Example
25.0.0.3.20231106214313+d8fbf2620c60
| Part | Example | Source |
|---|---|---|
| Plugin version | 25.0.0 |
The plugin’s own version. |
| Plugin revision | 3 |
BSR-side rebuild count for that plugin version. |
| Commit timestamp | 20231106214313 |
Module commit timestamp (YYYYMMDDHHMMSS). |
| Commit short name | d8fbf2620c60 |
First 12 characters of the module commit ID. |
Commits pushed to non-default labels carry .dev in place of a numeric timestamp, so default-label versions sort above in-development commits.
The BSR supports commits on labels for non-default labels.
To pin a specific plugin version, use the buf registry sdk version CLI command.
Pin dependencies to a label commit#
The BSR provides a Python-specific endpoint that returns the flattened dependency graph for an SDK at a specific version, so package managers can resolve a single SDK’s dependencies without walking each subindex. This is especially useful when you want every transitive dependency to come from the same module commit (rather than the latest available default-label commit per package, which is the default).
https://your-bsr-instance.example.com/gen/python/deps/{moduleOwner}-{moduleName}-{pluginOwner}-{pluginName}/{version}
The endpoint follows the name and version conventions above.
$ curl https://buf.build/gen/python/deps/acme-petapis-grpc-python/1.72.1.1.20220907172654+7abdb7802c8f
A successful response:
{
"packages": [
{
"name": "acme-petapis-grpc-python",
"version": "1.69.0.1.20250511171428+1269324e55bf"
},
{
"name": "acme-petapis-protocolbuffers-python",
"version": "29.2.0.1.20250511171428+1269324e55bf"
},
{
"name": "acme-petapis-protocolbuffers-pyi",
"version": "29.2.0.1.20250511171428+1269324e55bf"
},
{
"name": "acme-paymentapis-grpc-python",
"version": "1.69.0.1.20250511171427+1beaca87b579"
},
{
"name": "googleapis-googleapis-grpc-python",
"version": "1.69.0.1.20250511171426+6310aaead7ba"
},
{
"name": "acme-paymentapis-protocolbuffers-python",
"version": "29.2.0.1.20250511171427+1beaca87b579"
},
{
"name": "googleapis-googleapis-protocolbuffers-python",
"version": "29.2.0.1.20250511171426+6310aaead7ba"
},
{
"name": "acme-paymentapis-protocolbuffers-pyi",
"version": "29.2.0.1.20250511171427+1beaca87b579"
},
{
"name": "googleapis-googleapis-protocolbuffers-pyi",
"version": "29.2.0.1.20250511171426+6310aaead7ba"
}
]
}
For Poetry, pipe the response through jq to get a flattened list ready to drop into the tool.poetry.dependencies section of pyproject.toml:
$ curl -s https://buf.build/gen/python/deps/acme-petapis-grpc-python/1.72.1.1.20220907172654+7abdb7802c8f \
| jq -r '.packages[] | "\(.name) = {version = \"~\(.version)\", source = \"buf\"}"'
The output drops into pyproject.toml:
acme-petapis-grpc-python = {version = "~1.72.1.1.20220907172654+7abdb7802c8f", source = "buf"}
acme-petapis-protocolbuffers-python = {version = "~31.1.0.1.20220907172654+7abdb7802c8f", source = "buf"}
acme-petapis-protocolbuffers-pyi = {version = "~31.1.0.1.20220907172654+7abdb7802c8f", source = "buf"}
acme-paymentapis-grpc-python = {version = "~1.72.1.1.20220907172603+9a877cf260e1", source = "buf"}
googleapis-googleapis-grpc-python = {version = "~1.72.1.1.20220906171522+62f35d8aed11", source = "buf"}
acme-paymentapis-protocolbuffers-python = {version = "~31.1.0.1.20220907172603+9a877cf260e1", source = "buf"}
googleapis-googleapis-protocolbuffers-python = {version = "~31.1.0.1.20220906171522+62f35d8aed11", source = "buf"}
acme-paymentapis-protocolbuffers-pyi = {version = "~31.1.0.1.20220907172603+9a877cf260e1", source = "buf"}
googleapis-googleapis-protocolbuffers-pyi = {version = "~31.1.0.1.20220906171522+62f35d8aed11", source = "buf"}
Compatible-release version constraints#
Generated Python SDKs declare their dependencies with the ~= (compatible release) version comparator, not ==.
Pinning every dependency with == produces unresolvable graphs in common cases: if module C depends on A and B, and A depends on googleapis@commit1 while B depends on googleapis@commit2, an == pin on both is contradictory.
With ~=, pip chooses the latest version that satisfies each constraint.
Combined with the .dev placeholder on non-default-label commits, that resolution prefers the latest default-label version over any in-development version on a non-default label.
To pin a graph to one specific module commit anyway, use the deps endpoint above.
Other package managers#
The BSR Python repository works with pip, uv, and Poetry today.
Available plugins#
The full list of supported Python plugins lives on the BSR plugins page (filter for Python).
For how those plugins are packaged, see the bufbuild/plugins repository; to request a new plugin, file an issue.
Troubleshooting#
422 status when installing an SDK#
A 422 Unprocessable Entity response means the BSR’s plugin failed to run for that module commit and plugin version.
Open the wheel URL pip reported in the error (a URL ending in .whl) in curl or a browser to read the raw failure details.