Skip to content

Commit 22b55fb

Browse files
authored
Import gRPC stubs from the grpc-stubs project (#11204)
1 parent 9dd2ea6 commit 22b55fb

33 files changed

+1519
-0
lines changed

pyrightconfig.stricter.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
"stubs/gdb",
4545
"stubs/geopandas",
4646
"stubs/google-cloud-ndb",
47+
"stubs/grpcio/grpc/__init__.pyi",
4748
"stubs/hdbcli/hdbcli/dbapi.pyi",
4849
"stubs/html5lib",
4950
"stubs/httplib2",
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# Error: is not present at runtime
2+
# =============================
3+
# Error class attributes that aren't defined.
4+
grpc.RpcError.code
5+
grpc.RpcError.details
6+
grpc.RpcError.trailing_metadata
7+
8+
# Error: is inconsistent
9+
# =============================
10+
# Stub class is incomplete.
11+
grpc_reflection.v1alpha._base.BaseReflectionServicer.__init__
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
from __future__ import annotations
2+
3+
from typing import Any, cast
4+
from typing_extensions import assert_type
5+
6+
import grpc.aio
7+
8+
# Interceptor casts
9+
client_interceptors: list[grpc.aio.ClientInterceptor] = []
10+
grpc.aio.insecure_channel("target", interceptors=client_interceptors)
11+
12+
server_interceptors: list[grpc.aio.ServerInterceptor[Any, Any]] = []
13+
grpc.aio.server(interceptors=server_interceptors)
14+
15+
16+
# Metadata
17+
async def metadata() -> None:
18+
metadata = await cast(grpc.aio.Call, None).initial_metadata()
19+
assert_type(metadata["foo"], grpc.aio._MetadataValue)
20+
for k in metadata:
21+
assert_type(k, str)
22+
23+
for k, v in metadata.items():
24+
assert_type(k, str)
25+
assert_type(v, grpc.aio._MetadataValue)
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
from __future__ import annotations
2+
3+
from typing import Protocol, cast
4+
from typing_extensions import assert_type
5+
6+
import grpc.aio
7+
8+
9+
class DummyRequest:
10+
pass
11+
12+
13+
class DummyReply:
14+
pass
15+
16+
17+
class DummyServiceStub(Protocol):
18+
UnaryUnary: grpc.aio.UnaryUnaryMultiCallable[DummyRequest, DummyReply]
19+
UnaryStream: grpc.aio.UnaryStreamMultiCallable[DummyRequest, DummyReply]
20+
StreamUnary: grpc.aio.StreamUnaryMultiCallable[DummyRequest, DummyReply]
21+
StreamStream: grpc.aio.StreamStreamMultiCallable[DummyRequest, DummyReply]
22+
23+
24+
stub = cast(DummyServiceStub, None)
25+
req = DummyRequest()
26+
27+
28+
async def async_context() -> None:
29+
assert_type(await stub.UnaryUnary(req), DummyReply)
30+
31+
async for resp in stub.UnaryStream(req):
32+
assert_type(resp, DummyReply)
33+
34+
assert_type(await stub.StreamUnary(iter([req])), DummyReply)
35+
36+
async for resp in stub.StreamStream(iter([req])):
37+
assert_type(resp, DummyReply)
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
from __future__ import annotations
2+
3+
from typing import Optional, cast
4+
from typing_extensions import assert_type
5+
6+
import grpc
7+
8+
# Channel options:
9+
assert_type(grpc.insecure_channel("target", ()), grpc.Channel)
10+
assert_type(grpc.insecure_channel("target", (("a", "b"),)), grpc.Channel)
11+
assert_type(grpc.insecure_channel("target", (("a", "b"), ("c", "d"))), grpc.Channel)
12+
13+
# Local channel credentials:
14+
creds = grpc.local_channel_credentials(grpc.LocalConnectionType.LOCAL_TCP)
15+
assert_type(creds, grpc.ChannelCredentials)
16+
17+
# Other credential types:
18+
assert_type(grpc.alts_channel_credentials(), grpc.ChannelCredentials)
19+
assert_type(grpc.alts_server_credentials(), grpc.ServerCredentials)
20+
assert_type(grpc.compute_engine_channel_credentials(grpc.CallCredentials("")), grpc.ChannelCredentials)
21+
assert_type(grpc.insecure_server_credentials(), grpc.ServerCredentials)
22+
23+
# XDS credentials:
24+
assert_type(
25+
grpc.xds_channel_credentials(grpc.local_channel_credentials(grpc.LocalConnectionType.LOCAL_TCP)), grpc.ChannelCredentials
26+
)
27+
assert_type(grpc.xds_server_credentials(grpc.insecure_server_credentials()), grpc.ServerCredentials)
28+
29+
# Channel ready future
30+
channel = grpc.insecure_channel("target", ())
31+
assert_type(grpc.channel_ready_future(channel).result(), None)
32+
33+
# Channel options supports list:
34+
assert_type(grpc.insecure_channel("target", []), grpc.Channel)
35+
assert_type(grpc.insecure_channel("target", [("a", "b")]), grpc.Channel)
36+
assert_type(grpc.insecure_channel("target", [("a", "b"), ("c", "d")]), grpc.Channel)
37+
38+
# Client call details optionals:
39+
call_details = grpc.ClientCallDetails()
40+
assert_type(call_details.method, str)
41+
assert_type(call_details.timeout, Optional[float])
42+
43+
# Call iterator
44+
call_iter = cast(grpc._CallIterator[str], None)
45+
for call in call_iter:
46+
assert_type(call, str)
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
from __future__ import annotations
2+
3+
from typing import cast
4+
from typing_extensions import assert_type
5+
6+
import grpc
7+
8+
9+
class Request:
10+
pass
11+
12+
13+
class Response:
14+
pass
15+
16+
17+
def unary_unary_call(rq: Request, ctx: grpc.ServicerContext) -> Response:
18+
assert_type(rq, Request)
19+
return Response()
20+
21+
22+
class ServiceHandler(grpc.ServiceRpcHandler[Request, Response]):
23+
def service_name(self) -> str:
24+
return "hello"
25+
26+
def service(self, handler_call_details: grpc.HandlerCallDetails) -> grpc.RpcMethodHandler[Request, Response] | None:
27+
rpc = grpc.RpcMethodHandler[Request, Response]()
28+
rpc.unary_unary = unary_unary_call
29+
return rpc
30+
31+
32+
h = ServiceHandler()
33+
ctx = cast(grpc.ServicerContext, None)
34+
svc = h.service(grpc.HandlerCallDetails())
35+
if svc is not None and svc.unary_unary is not None:
36+
svc.unary_unary(Request(), ctx)
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
from __future__ import annotations
2+
3+
from typing import Protocol, cast
4+
from typing_extensions import assert_type
5+
6+
import grpc
7+
8+
9+
class DummyRequest:
10+
pass
11+
12+
13+
class DummyReply:
14+
pass
15+
16+
17+
class DummyServiceStub(Protocol):
18+
UnaryUnary: grpc.UnaryUnaryMultiCallable[DummyRequest, DummyReply]
19+
UnaryStream: grpc.UnaryStreamMultiCallable[DummyRequest, DummyReply]
20+
StreamUnary: grpc.StreamUnaryMultiCallable[DummyRequest, DummyReply]
21+
StreamStream: grpc.StreamStreamMultiCallable[DummyRequest, DummyReply]
22+
23+
24+
stub = cast(DummyServiceStub, None)
25+
req = DummyRequest()
26+
27+
assert_type(stub.UnaryUnary(req), DummyReply)
28+
29+
for resp in stub.UnaryStream(req):
30+
assert_type(resp, DummyReply)
31+
32+
assert_type(stub.StreamUnary(iter([req])), DummyReply)
33+
34+
for resp in stub.StreamStream(iter([req])):
35+
assert_type(resp, DummyReply)
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
from __future__ import annotations
2+
3+
from typing import cast
4+
5+
import grpc
6+
from grpc_reflection.v1alpha.reflection import enable_server_reflection
7+
8+
server = cast(grpc.Server, None)
9+
enable_server_reflection(["foo"], server, None)
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
from __future__ import annotations
2+
3+
from typing import cast
4+
5+
import grpc.aio
6+
from grpc_reflection.v1alpha.reflection import enable_server_reflection
7+
8+
server = cast(grpc.aio.Server, None)
9+
enable_server_reflection(["foo"], server, None)
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
from __future__ import annotations
2+
3+
from typing import Any
4+
5+
import grpc
6+
7+
8+
@grpc.Call.register
9+
class CallProxy:
10+
def __init__(self, target: grpc.Call) -> None:
11+
self._target = target
12+
13+
def __getattr__(self, name: str) -> Any:
14+
return getattr(self._target, name)
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
from __future__ import annotations
2+
3+
from collections.abc import Callable
4+
5+
import grpc
6+
7+
8+
class Request:
9+
pass
10+
11+
12+
class Response:
13+
pass
14+
15+
16+
class NoopInterceptor(grpc.ServerInterceptor[Request, Response]):
17+
def intercept_service(
18+
self,
19+
continuation: Callable[[grpc.HandlerCallDetails], grpc.RpcMethodHandler[Request, Response] | None],
20+
handler_call_details: grpc.HandlerCallDetails,
21+
) -> grpc.RpcMethodHandler[Request, Response] | None:
22+
return continuation(handler_call_details)
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
from __future__ import annotations
2+
3+
from grpc import Status
4+
from grpc_status import to_status
5+
6+
# XXX: to_status actually expects a "google.rpc.status.Status",
7+
# but the stubs for that aren't present yet.
8+
status: Status = to_status(None)

stubs/grpcio/METADATA.toml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
version = "1.*"
2+
upstream_repository = "https://github.com/grpc/grpc"
3+
partial_stub = true
4+
requires = [
5+
"types-protobuf",
6+
]
7+
8+
[tool.stubtest]
9+
ignore_missing_stub = true
10+
stubtest_requirements = [
11+
"grpcio-channelz",
12+
"grpcio-health-checking",
13+
"grpcio-reflection",
14+
"grpcio-status",
15+
]

0 commit comments

Comments
 (0)