Skip to content
Merged
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 Connect 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
38 changes: 5 additions & 33 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 All @@ -136,47 +136,19 @@ async def create_user(self, request: CreateUserRequest, ctx: RequestContext) ->

### Reading error details on the client

Error details are `google.protobuf.Any` messages that can be unpacked to their original types:
Error details are `connectrpc.errors.ErrorDetail` classes that can be unpacked into
the original message.

```python
try:
response = await client.some_method(request)
except ConnectError as e:
for detail in e.details:
# Check the type before unpacking
if detail.Is(Struct.DESCRIPTOR):
unpacked = Struct()
detail.Unpack(unpacked)
# Returns None if type does not match
if unpacked := detail.value(Struct):
print(f"Error detail: {unpacked}")
```

### Standard error detail types

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

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

might be worth adding a replacement for these at some point, since they can still structurally be the same (without needing the same package)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup - I think the best we can do for now is delete this section but we'll need to see how to deal with it in the long term.

Personally, I've never considered using these protos ;)


- `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
Loading