Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions MAINTAINERS.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@ Connect implementations:
- [connect-swift][swift],
- [connect-kotlin][kotlin],
- [connect-dart][dart], and
- [connect-python][python].
- [connect-py][python].

[go]: https://github.com/connectrpc/connect-go/blob/main/MAINTAINERS.md
[es]: https://github.com/connectrpc/connect-es/blob/main/MAINTAINERS.md
[swift]: https://github.com/connectrpc/connect-swift/blob/main/MAINTAINERS.md
[kotlin]: https://github.com/connectrpc/connect-kotlin/blob/main/MAINTAINERS.md
[dart]: https://github.com/connectrpc/connect-dart/blob/main/MAINTAINERS.md
[python]: https://github.com/connectrpc/connect-python/blob/main/MAINTAINERS.md
[python]: https://github.com/connectrpc/connect-py/blob/main/MAINTAINERS.md
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ specification][protocol].
* [connect-swift]: Swift clients
* [connect-kotlin]: Kotlin clients
* [connect-dart]: Kotlin clients
* [connect-python]: Python servers and clients
* [connect-py]: Python servers and clients
* [conformance]: Connect, gRPC, and gRPC-Web interoperability tests
* [examples-go]: Go server powering demo.connectrpc.com
* and [much more][github-org].
Expand All @@ -36,7 +36,7 @@ Offered under the [Apache 2 license][license].
[connect-swift]: https://github.com/connectrpc/connect-swift
[connect-kotlin]: https://github.com/connectrpc/connect-kotlin
[connect-dart]: https://github.com/connectrpc/connect-dart
[connect-python]: https://github.com/connectrpc/connect-python
[connect-py]: https://github.com/connectrpc/connect-py
[examples-go]: https://github.com/connectrpc/examples-go
[license]: LICENSE
[protocol]: https://connectrpc.com/docs/protocol
Expand Down
4 changes: 2 additions & 2 deletions src/content/docs/docs/introduction.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ guide][kotlin-getting-started].

## Python

[`connect-python`][connect-python] is available in beta.
[Connect for Python][connect-py] is available in beta.
You can get started with our [Python guide][python-getting-started].

## What's next?
Expand All @@ -89,7 +89,7 @@ like to bring Connect to more languages and frameworks. Our current roadmap is
always pinned to the top of our [GitHub discussions][announcement-discussions],
and we gauge interest in new languages with [GitHub polls][poll-discussions].

[connect-python]: https://github.com/connectrpc/connect-python
[connect-py]: https://github.com/connectrpc/connect-py
[python-getting-started]: /docs/python/getting-started
[connect-conformance]: https://github.com/connectrpc/conformance
[connect-go]: https://github.com/connectrpc/connect-go
Expand Down
2 changes: 1 addition & 1 deletion src/content/docs/docs/python/_version-warning.mdx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
:::note[Beta software]

`connect-python` is in beta. 1.0 will include a new Protobuf implementation built from scratch by [Buf](https://buf.build/home), which may introduce breaking changes.
Connect for Python is in beta.
Join us on [Slack](https://buf.build/links/slack) if you have questions or feedback.
:::
4 changes: 2 additions & 2 deletions src/content/docs/docs/python/deployment.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,9 @@ with bidirectional streaming are:
- [granian](https://github.com/emmett-framework/granian)
- [hypercorn](https://hypercorn.readthedocs.io/en/latest/)

Connect has an extensive test suite to verify compatibility of connect-python with the Connect protocol.
Connect has an extensive test suite to verify compatibility with the ConnectRPC protocol.
Unfortunately, **only pyvoy reliably passes conformance tests**: other servers occasionally
have hung requests or stream ordering issues. pyvoy was built with connect-python in mind, but is
have hung requests or stream ordering issues. pyvoy was built with Connect in mind, but is
very new and needs more time with real-world applications to verify stability.

Keep the above in mind when picking an HTTP/2 server and let us know how it goes if you give any a try.
Expand Down
29 changes: 1 addition & 28 deletions src/content/docs/docs/python/errors.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ Errors can include strongly-typed details using Protobuf messages:
from connectrpc.code import Code
from connectrpc.errors import ConnectError
from connectrpc.request import RequestContext
from google.protobuf.struct_pb2 import Struct, Value
from protobuf.wkt import Struct, Value

async def create_user(self, request: CreateUserRequest, ctx: RequestContext) -> CreateUserResponse:
if not request.email:
Expand Down Expand Up @@ -150,33 +150,6 @@ except ConnectError as e:
print(f"Error detail: {unpacked}")
```

### Standard error detail types

With `googleapis-common-protos` installed, you can use standard types like:

- `BadRequest`: Field violations in a request
- `RetryInfo`: When to retry
- `Help`: Links to documentation
- `QuotaFailure`: Quota violations
- `ErrorInfo`: Structured error metadata

Example:

```python
from google.rpc.error_details_pb2 import BadRequest

bad_request = BadRequest()
violation = bad_request.field_violations.add()
violation.field = "email"
violation.description = "Must be a valid email address"

raise ConnectError(
Code.INVALID_ARGUMENT,
"Invalid email format",
details=[bad_request]
)
```

## HTTP representation

In the Connect protocol, errors are always JSON:
Expand Down
41 changes: 29 additions & 12 deletions src/content/docs/docs/python/getting-started.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -110,9 +110,7 @@ $ touch buf.gen.yaml
```yaml
version: v2
plugins:
- remote: buf.build/protocolbuffers/python
out: .
- remote: buf.build/protocolbuffers/pyi
- remote: buf.build/bufbuild/py
out: .
- remote: buf.build/connectrpc/python
out: .
Expand All @@ -131,18 +129,37 @@ In the `greet` package, you should now see some generated Python:
greet
└── v1
├── greet_connect.py
└── greet_pb2.py
└── greet_pb2.pyi
└── greet_pb.py
```

The package `greet/v1` contains `greet_pb2.py` and `greet_pb2.pyi` which were generated by
the [protocolbuffers/python](https://buf.build/protocolbuffers/python) and
[protocolbuffers/pyi](https://buf.build/protocolbuffers/pyi) and contain `GreetRequest`
The package `greet/v1` contains `greet_pb.py` which was generated by
the [bufbuild/py](https://buf.build/bufbuild/py) plugin and contains `GreetRequest`
and `GreetResponse` structs and the associated marshaling code. `greet_connect.py` was
generated by [connectrpc/python](https://buf.build/connectrpc/python) and contains the
WSGI and ASGI service interfaces and client code to access a Connect server. Feel free to
poke around if you're interested - `greet_connect.py` is standard Python code.

### google.protobuf compatibility

Connect for Python defaults to targeting [protobuf-py](https://protobufpy.com) as the Protocol Buffers
implementation, but Google's Protocol Buffers for Python are also fully supported. Pass `protobuf=google`
to the codegen plugin to use it.

```yaml
version: v2
plugins:
- remote: buf.build/protocolbuffers/python
out: .
- remote: buf.build/protocolbuffers/pyi
out: .
- remote: buf.build/connectrpc/python
out: .
opt: protobuf=google
```

If configuring a client for JSON codec, make sure to pass `connectrpc.compat.google_protobuf_json_codec`
instead of `connectrpc.codec.proto_json_codec`.

## Implement service

The code we've generated takes care of the boring boilerplate, but we still need to implement our greeting logic.
Expand All @@ -157,7 +174,7 @@ in one Python file. `touch server.py` and add:
from connectrpc.request import RequestContext

from greet.v1.greet_connect import GreetService, GreetServiceASGIApplication
from greet.v1.greet_pb2 import GreetRequest, GreetResponse
from greet.v1.greet_pb import GreetRequest, GreetResponse

class Greeter(GreetService):
async def greet(self, request: GreetRequest, ctx: RequestContext[GreetRequest, GreetResponse]) -> GreetResponse:
Expand All @@ -174,7 +191,7 @@ in one Python file. `touch server.py` and add:

```python
from greet.v1.greet_connect import GreetServiceSync, GreetServiceWSGIApplication
from greet.v1.greet_pb2 import GreetResponse
from greet.v1.greet_pb import GreetResponse

class Greeter(GreetServiceSync):
def greet(self, request, ctx):
Expand Down Expand Up @@ -237,7 +254,7 @@ We can also make requests using Connect's generated client. `touch client.py` an
import asyncio

from greet.v1.greet_connect import GreetServiceClient
from greet.v1.greet_pb2 import GreetRequest
from greet.v1.greet_pb import GreetRequest

async def main():
client = GreetServiceClient("http://localhost:8000")
Expand All @@ -253,7 +270,7 @@ We can also make requests using Connect's generated client. `touch client.py` an

```python
from greet.v1.greet_connect import GreetServiceClientSync
from greet.v1.greet_pb2 import GreetRequest
from greet.v1.greet_pb import GreetRequest

def main():
client = GreetServiceClientSync("http://localhost:8000")
Expand Down
8 changes: 4 additions & 4 deletions src/content/docs/docs/python/grpc-compatibility.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,15 @@ In Python, this typically means using [pyvoy](https://pyvoy.dev/), the same serv
If using a server with trailers support, there is no other setup required to support gRPC. If you
run in a server that doesn't support it, you will get a runtime error when making a request.

Connect-Python does not currently provide implementations of gRPC standard endpoints like reflection
Connect does not currently provide implementations of gRPC standard endpoints like reflection
and health check, but likely will in the future.

## Clients

Clients default to using the Connect protocol. To use the gRPC protocol, pass
`protocol=ProtocolType.GRPC` during client construction. If the gRPC server is using TLS, Connect
clients work with no further configuration. If the gRPC server is using HTTP/2 without TLS, you'll
need to configure the transport to explicitly use HTTP/2. Connect-Python uses
need to configure the transport to explicitly use HTTP/2. Connect uses
[pyqwest](https://pyqwest.dev/) as its transport for its support of HTTP/2 trailers and bidirectional
streaming. Configure it for HTTP/2 like this:

Expand All @@ -39,7 +39,7 @@ streaming. Configure it for HTTP/2 like this:
from pyqwest import Client, HTTPTransport, HTTPVersion

from greet.v1.greet_connect import GreetServiceClient
from greet.v1.greet_pb2 import GreetRequest
from greet.v1.greet_pb import GreetRequest

async with (
HTTPTransport(http_version=HTTPVersion.HTTP2) as transport,
Expand All @@ -56,7 +56,7 @@ streaming. Configure it for HTTP/2 like this:
from pyqwest import HTTPVersion, SyncClient, SyncHTTPTransport

from greet.v1.greet_connect import GreetServiceClientSync
from greet.v1.greet_pb2 import GreetRequest
from greet.v1.greet_pb import GreetRequest

with (
SyncHTTPTransport(http_version=HTTPVersion.HTTP2) as transport,
Expand Down
4 changes: 2 additions & 2 deletions src/content/docs/docs/python/streaming.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ After running `buf generate` to update our generated code, we can amend our serv

```python
from greet.v1.greet_connect import GreetService, GreetServiceASGIApplication
from greet.v1.greet_pb2 import GreetResponse
from greet.v1.greet_pb import GreetResponse

class Greeter(GreetService):
async def greet(self, request, ctx):
Expand All @@ -125,7 +125,7 @@ After running `buf generate` to update our generated code, we can amend our serv

```python
from greet.v1.greet_connect import GreetServiceSync, GreetServiceWSGIApplication
from greet.v1.greet_pb2 import GreetResponse
from greet.v1.greet_pb import GreetResponse

class Greeter(GreetServiceSync):
def greet(self, request, ctx):
Expand Down
30 changes: 15 additions & 15 deletions src/content/docs/docs/python/testing.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,15 @@ import VersionWarning from './_version-warning.mdx';

<VersionWarning />

This guide covers testing Connect-Python services and clients.
This guide covers testing Connect services and clients.

## Setup

For [pytest](https://docs.pytest.org/en/stable/) examples in this guide, you'll need pytest and [pytest-asyncio](https://pypi.org/project/pytest-asyncio/). unittest requires no additional dependencies.

## Recommended approach: In-memory testing

The recommended approach is **in-memory testing** using pyqwest's ASGI/WSGI transports (provided by pyqwest, not Connect-Python). This tests your full application stack (routing, serialization, error handling, interceptors) while remaining fast and isolated - no network overhead or port conflicts.
The recommended approach is **in-memory testing** using pyqwest's ASGI/WSGI transports (provided by pyqwest, not Connect). This tests your full application stack (routing, serialization, error handling, interceptors) while remaining fast and isolated - no network overhead or port conflicts.

Here's a minimal example without any test framework:

Expand All @@ -27,7 +27,7 @@ Here's a minimal example without any test framework:
from pyqwest import Client
from pyqwest.testing import ASGITransport
from greet.v1.greet_connect import GreetServiceASGIApplication, GreetServiceClient
from greet.v1.greet_pb2 import GreetRequest
from greet.v1.greet_pb import GreetRequest
from server import Greeter # Your service implementation

# Create ASGI app with your service
Expand All @@ -51,7 +51,7 @@ Here's a minimal example without any test framework:
from pyqwest import SyncClient
from pyqwest.testing import WSGITransport
from greet.v1.greet_connect import GreetServiceWSGIApplication, GreetServiceClientSync
from greet.v1.greet_pb2 import GreetRequest
from greet.v1.greet_pb import GreetRequest
from server import GreeterSync # Your service implementation

# Create WSGI app with your service
Expand Down Expand Up @@ -86,7 +86,7 @@ Testing the service we created in the [Getting Started](/docs/python/getting-sta
from pyqwest import Client
from pyqwest.testing import ASGITransport
from greet.v1.greet_connect import GreetServiceASGIApplication, GreetServiceClient
from greet.v1.greet_pb2 import GreetRequest
from greet.v1.greet_pb import GreetRequest
from server import Greeter # Import your actual service implementation

@pytest.mark.asyncio
Expand All @@ -110,7 +110,7 @@ Testing the service we created in the [Getting Started](/docs/python/getting-sta
from pyqwest import SyncClient
from pyqwest.testing import WSGITransport
from greet.v1.greet_connect import GreetServiceWSGIApplication, GreetServiceClientSync
from greet.v1.greet_pb2 import GreetRequest
from greet.v1.greet_pb import GreetRequest
from server import GreeterSync # Import your actual service implementation

def test_greet():
Expand Down Expand Up @@ -143,7 +143,7 @@ The same in-memory testing approach works with unittest:
from pyqwest import Client
from pyqwest.testing import ASGITransport
from greet.v1.greet_connect import GreetServiceASGIApplication, GreetServiceClient
from greet.v1.greet_pb2 import GreetRequest
from greet.v1.greet_pb import GreetRequest
from server import Greeter

class TestGreet(unittest.TestCase):
Expand All @@ -168,7 +168,7 @@ The same in-memory testing approach works with unittest:
from pyqwest import SyncClient
from pyqwest.testing import WSGITransport
from greet.v1.greet_connect import GreetServiceWSGIApplication, GreetServiceClientSync
from greet.v1.greet_pb2 import GreetRequest
from greet.v1.greet_pb import GreetRequest
from server import GreeterSync

class TestGreet(unittest.TestCase):
Expand Down Expand Up @@ -207,7 +207,7 @@ For cleaner tests, use pytest fixtures to set up clients and services:
import pytest
import pytest_asyncio
from greet.v1.greet_connect import GreetServiceASGIApplication, GreetServiceClient
from greet.v1.greet_pb2 import GreetRequest
from greet.v1.greet_pb import GreetRequest
from server import Greeter

@pytest_asyncio.fixture
Expand Down Expand Up @@ -237,7 +237,7 @@ For cleaner tests, use pytest fixtures to set up clients and services:
from pyqwest.testing import WSGITransport
import pytest
from greet.v1.greet_connect import GreetServiceWSGIApplication, GreetServiceClientSync
from greet.v1.greet_pb2 import GreetRequest
from greet.v1.greet_pb import GreetRequest
from server import GreeterSync

@pytest.fixture
Expand Down Expand Up @@ -265,7 +265,7 @@ This pattern:
- Reduces code duplication across multiple tests
- Makes tests more readable and focused on behavior
- Follows pytest best practices
- Matches the pattern used in Connect-Python's own test suite
- Matches the pattern used in Connect's own test suite

With your test client setup, you can use any Connect code for interacting with the service under test including [streaming](/docs/python/streaming/), reading [headers and trailers](/docs/python/headers-and-trailers/), or checking [errors](/docs/python/errors/). For example, to test error handling:

Expand Down Expand Up @@ -294,7 +294,7 @@ For testing client code that calls Connect services, use the same in-memory test
from connectrpc.code import Code
from connectrpc.errors import ConnectError
from greet.v1.greet_connect import GreetService, GreetServiceASGIApplication, GreetServiceClient
from greet.v1.greet_pb2 import GreetRequest, GreetResponse
from greet.v1.greet_pb import GreetRequest, GreetResponse

async def fetch_user_greeting(user_id: str, client: GreetServiceClient):
"""Client code that handles errors."""
Expand Down Expand Up @@ -340,7 +340,7 @@ For testing client code that calls Connect services, use the same in-memory test
from connectrpc.code import Code
from connectrpc.errors import ConnectError
from greet.v1.greet_connect import GreetServiceSync, GreetServiceWSGIApplication, GreetServiceClientSync
from greet.v1.greet_pb2 import GreetRequest, GreetResponse
from greet.v1.greet_pb import GreetRequest, GreetResponse

def fetch_user_greeting(user_id: str, client: GreetServiceClientSync):
"""Client code that handles errors."""
Expand Down Expand Up @@ -393,7 +393,7 @@ Test interceptors as part of your full application stack. For example, testing t
from connectrpc.code import Code
from connectrpc.errors import ConnectError
from greet.v1.greet_connect import GreetServiceASGIApplication, GreetServiceClient
from greet.v1.greet_pb2 import GreetRequest
from greet.v1.greet_pb import GreetRequest
from interceptors import ServerAuthInterceptor
from server import Greeter

Expand Down Expand Up @@ -444,7 +444,7 @@ Test interceptors as part of your full application stack. For example, testing t
from connectrpc.code import Code
from connectrpc.errors import ConnectError
from greet.v1.greet_connect import GreetServiceWSGIApplication, GreetServiceClientSync
from greet.v1.greet_pb2 import GreetRequest
from greet.v1.greet_pb import GreetRequest
from interceptors import ServerAuthInterceptor
from server import GreeterSync

Expand Down
Loading