Releases: connectrpc/connect-py
v0.11.0
This is a large release - in fact it is so large we have renamed the repo to connect-py! There are major breaking changes so read these notes in detail. Sorry for the churn, but we hope for this to be the last big bump on the road to a stable release.
This release targets the long-awaited protobuf-py, a new Protocol Buffers runtime built from scratch to provide a complete, idiomatic, and performant experience for Python. protobuf-py is now the default, but google.protobuf is also completely supported via a compatibility layer - read through the Breaking Changes section for how to enable it.
☢️ Breaking changes
Enabling the google.protobuf compatibility shim (Existing users read this!)
Starting with this release, protoc-gen-connectrpc will default to generating stubs that target protobuf-py messages. Existing users should add the protobuf=google option to their plugin settings to instead generate stubs that target existing google.protobuf messages. There are some significant changes to the usage of messages with protobuf-py to match Python semantics better, so we recommend existing users to first enable the shim while updating connectrpc and later consider migrating to protobuf-py independently.
Before:
version: v2
plugins:
- remote: buf.build/protocolbuffers/python
out: src
- remote: buf.build/protocolbuffers/pyi
out: src
- remote: buf.build/connectrpc/python
out: srcAfter:
version: v2
plugins:
- remote: buf.build/protocolbuffers/python
out: src
- remote: buf.build/protocolbuffers/pyi
out: src
- remote: buf.build/connectrpc/python
out: src
opt: protobuf=googleThe shim is simply an implementation of Connect's Codec, which is provided by default by generated code when enabling that option. If you set a different codec in your code, for example to use JSON, you will need to update to the new compat codec google_protobuf_json_codec.
Before:
from connectrpc.codec import proto_json_codec
client = ElizaServiceClient("http://localhost:8081", codec=proto_json_codec())After:
from connectrpc.compat import google_protobuf_json_codec
client = ElizaServiceClient("http://localhost:8081", codec=google_protobuf_json_codec())ErrorDetails exposed as class instead of Any
Previously ConnectError exposed details with the google.protobuf type Any directly. We have replaced this with a dedicated ErrorDetail class that provides the same type_name and message_bytes data for manual unpacking, but also has a convenience value method to convert into a protobuf-py message.
To convert into a google.protobuf message, copy the contents to its Any before unpacking.
Before:
try:
resp = client.say(SayMessage(sentence="Hello!"))
except ConnectError as e:
detail = Struct()
e.details[0].Unpack(detail)
print(detail)After:
from google.protobuf.any_pb2 import Any
try:
resp = client.say(SayMessage(sentence="Hello!"))
except ConnectError as e:
detail_any = Any(
type_url=f"type.googleapis.com/{e.details[0].type_name}",
value=e.details[0].message_bytes)
detail = Struct()
detail_any.Unpack(detail)
print(detail)RequestContext and ResponseMetadata expose properties instead of getters
This will effect many interceptor implementations. If you've written them, you've probably found them to simple accessors as getter methods, which especially looks poor for accessing headers. This has been a long-time mistake in the API and we are using this large release to fix it with this significant breaking change. Attributes are now all properties instead of getter methods.
Before:
authorization = ctx.headers()["authorization"]
with ResponseMetadata() as meta:
resp = client.say(SayMessage(sentence="Hello!"))
print(meta.headers()["x-generated-entity"]After:
authorization = ctx.headers["authorization"]
with ResponseMetadata() as meta:
resp = client.say(SayMessage(sentence="Hello!"))
print(meta.headers["x-generated-entity"])protoc-gen-connectrpc is now written in Python
protoc-gen-connectrpc has been rewritten in Python using protobuf-py's plugin framework. This improves the output of the generated code substantially, but also means we cannot provide prebuilt binaries or a go run mechanism anymore. If needing them, consider wasilibs which runs the same plugin code via WASM.
async protoc option renamed to io enum
Previously we used a tri-state bool for generating all or one of async or sync code. Now we use an io enum set to async or sync, defaulting to generating both.
Before:
version: v2
plugins:
- remote: buf.build/connectrpc/python
out: src
opt: async=true # or async=falseAfter:
version: v2
plugins:
- remote: buf.build/connectrpc/python
out: src
opt: io=async # or io=sync📈 Enhancements
New Contributors
Full Changelog: v0.10.1...v0.11.0
v0.10.1
This is a small release with a bugfix to content-type matching, and reordering of parameters for GET requests to improve performance of HTTP caches.
📈 Enhancements
- Update unary-get query parameter to match spec ordering by @oliversun9 in #250
🛠️ Bug fixes
New Contributors
- @oliversun9 made their first contribution in #250
Full Changelog: v0.10.0...v0.10.1
connectrpc-otel/v0.1.1
This is a small update to connectrpc-otel to add a missing py.typed file to reflect it is already fully type annotated. There are no other changes.
New Contributors
- @jeffsawatzky made their first contribution in #240
Full Changelog: v0.10.0...connectrpc-otel/v0.1.1
v0.10.0
☢️ Breaking changes
Pass a Codec instead of proto_json=True to use JSON encoding
The proto_json bool has been replaced by a codec parameter. If you were using the default (proto_json=False), no change is needed — binary protobuf remains the default. If you were opting into JSON encoding:
Before:
async with GreeterClient("http://localhost", proto_json=True) as client:
...After:
from connectrpc.codec import proto_json_codec
async with GreeterClient("http://localhost", codec=proto_json_codec()) as client:
...The new codec parameter accepts any object implementing the Codec protocol, enabling custom encoding strategies — for example, a ProtoJSONCodec subclass with custom marshaling options.
What's Changed
- Allow customization of server and client codecs by @anuraaga in #192
- Allow generating only async or sync code by @anuraaga in #214
- Surface non-Connect handler exceptions to user by @anuraaga in #219
Full Changelog: v0.9.0...v0.10.0
connectrpc-otel v0.1.0
This is the first release of connectrpc-otel, a package with OpenTelemetry instrumentation for Connect-Python.
Users of OpenTelemetry auto-instrumentation can just add the package as a dependency and will have instrumentation enabled automatically. For more usage instructions, check out the documentation.
v0.9.0
BREAKING CHANGE: PyPI package renamed to connectrpc
This is the last version published as the connect-python package - we have migrated to connectrpc as part of this release to match the same code you write with it and will only publish future versions to connectrpc. Make sure to update your dependency specifications to connectrpc to continue to receive updates. Sorry for the churn as we get closer to a stable state.
This release includes a significant rework to compression handling - now, it is possible to implement custom compression methods or configure defaults with custom parameters. This release also allows configuring the supported compression methods for a server rather than automatically inspecting from application dependencies. Defaults now match other Connect implementations, only enabling gzip by default.
We have also added some other small enhancements like gRPC-Web support and improved debugging.
☢️ Breaking changes
Pass compressions rather than strings when configuring the client
Before:
GreeterClient("http://localhost", accept_compressions=["zstd", "br", "gzip"])After:
from connectrpc.compression.brotli import BrotliCompression
from connectrpc.compression.gzip import GzipCompression
from connectrpc.compression.zstd import ZstdCompression
GreeterClient("http://localhost", accept_compressions=[
ZstdCompression(), BrotliCompression(), GzipCompression()
])To configure a client to use gRPC protocol, pass protocol=ProtocolType.GRPC instead of grpc=True
Before:
GreeterClient("http://localhost", grpc=True)After:
from connectrpc.protocol import ProtocolType
GreeterClient("http://localhost", protocol=ProtocolType.GRPC)Metadata interceptors now accept Error | None in on_end
Before:
class MyInterceptor:
def on_end(self, token, ctx):
...After:
class MyInterceptor:
def on_end(self, token, ctx, error):
...📈 Enhancements
- Allow setting supported compressions in server by @anuraaga in #98
- Make Compression protocol public and default to gzip-only by @anuraaga in #103
- Mark codegen plugin as supporting edition 2024 by @stefanvanburen in #125
- Add support for debug in error details by @stefanvanburen in #147
- Add exception to metadata interceptors on_end by @anuraaga in #149
- Add support for gRPC-web by @anuraaga in #161
🛠️ Bug fixes
- Process GET params for empty request messages by @Zaczero in #121
- Fix server streaming handler not cancelled on client disconnect by @stefanvanburen in #175
New Contributors
Full Changelog: v0.8.1...v0.9.0
v0.8.1
This hotfix fixes the default compression level used with gzip, which previously was set too high causing performance issues.
🛠️ Bug fixes
🙇 Thank you
This release was possible thanks to the following contributors who shared their brilliant ideas
Full Changelog: v0.8.0...v0.8.1
v0.8.0
This is a big release - the client now supports bidirectional streaming and the gRPC protocol. With this, connect-python is now a full-featured Connect implementation!
This is enabled by switching our HTTP client transport to pyqwest, a Python interface to the Rust HTTP client reqwest. This client supports all features of HTTP, allowing us to also support bidirectional streaming and gRPC (via HTTP trailers) in turn.
Keep in mind though that this is a very new project, built specifically to push connect-python forward. The underlying reqwest library has been in use for some time and should be battle-tested, but the Python wrapper is new - let us know if you run into any issues so we can fix them.
☢️ Breaking changes
- The HTTP transport for clients has been changed to pyqwest. If you created connect clients with default parameters, this will not need any further changes, but if passing a custom session, you will now need to pass a pyqwest client.
Before:
ctx = ssl.create_default_context(cafile="ca.path")
async with (
httpx.AsyncClient(verify=ctx, http2=True) as session,
GreeterClient("http://localhost", session=session) as client:
)After:
async with (
pyqwest.HTTPTransport(
tls_ca_cert=Path("ca.path").read(),
http_version=pyqwest.HTTPVersion.HTTP2
) as transport,
GreeterClient("http://localhost", http_client=pyqwest.Client(transport)
)If you have unit tests using httpx.ASGITransport / httpx.WSGITransport, you also will need to migrate to pyqwest.testing.ASGITransport / pyqwest.testing.WSGITransport.
📈 Enhancements
🛠️ Bug fixes
- Avoid yielding messages on GeneratorExit in sync server by @anuraaga in #90
- Drain request body in all error cases in WSGI by @anuraaga in #94
Full Changelog: v0.7.1...v0.8.0
v0.7.1
This release fixes a conflict preventing using this library along with grpc-python, possibly from dependencies.
🛠️ Bug fixes
🙇 Thank you
This release was possible thanks to the following contributors who shared their brilliant ideas
Full Changelog: v0.7.0...v0.7.1
v0.7.0
This release includes initial support for the gRPC protocol in connect-python. It is currently only supported on the server side and requires using pyvoy for its support for HTTP/2 trailers.
📈 Enhancements
🛠️ Bug fixes
- Set default accept-encoding to compression algorithms supported by the client by @achille-roussel in #71
🙇 Thank you
This release was possible thanks to the following contributors who shared their brilliant ideas and pull requests
Full Changelog: v0.6.0...v0.7.0