Skip to content

Import gRPC stubs from the grpc-stubs project #11204

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 33 commits into from
Apr 25, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
853cb17
Import raw stubs from grpc-stubs package
shabbyrobe Dec 27, 2023
b3dd7ca
Format grpc stubs
shabbyrobe Dec 27, 2023
775a418
Fix ruff lints
shabbyrobe Dec 27, 2023
241b0e1
Migrate tests from grpc-stubs
shabbyrobe Dec 27, 2023
5eadd4f
Convert docstrings to comments
shabbyrobe Dec 27, 2023
1c09ab4
Default argument warnings, missing generics
shabbyrobe Dec 27, 2023
505d234
Add TypeAlias
shabbyrobe Dec 27, 2023
7a7cf61
Update typing.Callable to collections.abc.Callable
shabbyrobe Dec 27, 2023
0a228f8
Update more typings to collections.abcs
shabbyrobe Dec 27, 2023
1e31e62
Add underscore to typevars
shabbyrobe Dec 27, 2023
18b3d7d
Silence remaining typing failures
shabbyrobe Dec 27, 2023
15811e6
Paper over cracks in supplemental packages
shabbyrobe Dec 27, 2023
59fb630
Add ABC and ABCMeta to classes that declare it in the grpcio source
shabbyrobe Dec 27, 2023
0ac8415
Add check for alternate aio import
shabbyrobe Dec 28, 2023
82d02fb
Move grpc stubs into grpcio folder
shabbyrobe Dec 28, 2023
9993e8d
Add abstractmethod decorator to grpc.Call
shabbyrobe Dec 28, 2023
be40720
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Dec 28, 2023
5b3e698
Fix a bunch of stubtest issues
shabbyrobe Dec 28, 2023
05e2e99
Merge branch 'main' into grpc-stubs
srittau Apr 14, 2024
811304e
Merge branch 'main' into grpc-stubs
JelleZijlstra Oct 2, 2024
8f9ae2a
Merge branch 'main' into grpc-stubs
Avasam Feb 14, 2025
177bd44
Remove unnecessary assertion
shabbyrobe Feb 22, 2025
4678c7c
Remove unnecessary assertion
shabbyrobe Feb 22, 2025
a75a853
Lint fixes
Avasam Feb 22, 2025
1290473
Merge pull request #1 from Avasam/grpc-stubs-lint-fixes
shabbyrobe Feb 24, 2025
bce9ae8
resolve subtest issues
macro1 Mar 28, 2025
a28b817
convert typing-only classes and variables to private
macro1 Mar 31, 2025
5242f27
remove tests on abstract class
macro1 Apr 1, 2025
93032c5
Merge pull request #2 from macro1/grpc-stubs
shabbyrobe Apr 1, 2025
089280a
import typing symbols directly rather than accessing the typing module
Avasam Apr 2, 2025
c513452
Update stubs/grpcio/@tests/test_cases/check_aio_multi_callable.py
Avasam Apr 4, 2025
6a87f03
Merge pull request #3 from Avasam/import-typing-symbols-directly-rath…
shabbyrobe Apr 4, 2025
725f55b
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Apr 4, 2025
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
1 change: 1 addition & 0 deletions pyrightconfig.stricter.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
"stubs/gdb",
"stubs/geopandas",
"stubs/google-cloud-ndb",
"stubs/grpcio/grpc/__init__.pyi",
"stubs/hdbcli/hdbcli/dbapi.pyi",
"stubs/html5lib",
"stubs/httplib2",
Expand Down
11 changes: 11 additions & 0 deletions stubs/grpcio/@tests/stubtest_allowlist.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Error: is not present at runtime
# =============================
# Error class attributes that aren't defined.
grpc.RpcError.code
grpc.RpcError.details
grpc.RpcError.trailing_metadata

# Error: is inconsistent
# =============================
# Stub class is incomplete.
grpc_reflection.v1alpha._base.BaseReflectionServicer.__init__
25 changes: 25 additions & 0 deletions stubs/grpcio/@tests/test_cases/check_aio.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
from __future__ import annotations

from typing import Any, cast
from typing_extensions import assert_type

import grpc.aio

# Interceptor casts
client_interceptors: list[grpc.aio.ClientInterceptor] = []
grpc.aio.insecure_channel("target", interceptors=client_interceptors)

server_interceptors: list[grpc.aio.ServerInterceptor[Any, Any]] = []
grpc.aio.server(interceptors=server_interceptors)


# Metadata
async def metadata() -> None:
metadata = await cast(grpc.aio.Call, None).initial_metadata()
assert_type(metadata["foo"], grpc.aio._MetadataValue)
for k in metadata:
assert_type(k, str)

for k, v in metadata.items():
assert_type(k, str)
assert_type(v, grpc.aio._MetadataValue)
37 changes: 37 additions & 0 deletions stubs/grpcio/@tests/test_cases/check_aio_multi_callable.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
from __future__ import annotations

from typing import Protocol, cast
from typing_extensions import assert_type

import grpc.aio


class DummyRequest:
pass


class DummyReply:
pass


class DummyServiceStub(Protocol):
UnaryUnary: grpc.aio.UnaryUnaryMultiCallable[DummyRequest, DummyReply]
UnaryStream: grpc.aio.UnaryStreamMultiCallable[DummyRequest, DummyReply]
StreamUnary: grpc.aio.StreamUnaryMultiCallable[DummyRequest, DummyReply]
StreamStream: grpc.aio.StreamStreamMultiCallable[DummyRequest, DummyReply]


stub = cast(DummyServiceStub, None)
req = DummyRequest()


async def async_context() -> None:
assert_type(await stub.UnaryUnary(req), DummyReply)

async for resp in stub.UnaryStream(req):
assert_type(resp, DummyReply)

assert_type(await stub.StreamUnary(iter([req])), DummyReply)

async for resp in stub.StreamStream(iter([req])):
assert_type(resp, DummyReply)
46 changes: 46 additions & 0 deletions stubs/grpcio/@tests/test_cases/check_grpc.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
from __future__ import annotations

from typing import Optional, cast
from typing_extensions import assert_type

import grpc

# Channel options:
assert_type(grpc.insecure_channel("target", ()), grpc.Channel)
assert_type(grpc.insecure_channel("target", (("a", "b"),)), grpc.Channel)
assert_type(grpc.insecure_channel("target", (("a", "b"), ("c", "d"))), grpc.Channel)

# Local channel credentials:
creds = grpc.local_channel_credentials(grpc.LocalConnectionType.LOCAL_TCP)
assert_type(creds, grpc.ChannelCredentials)

# Other credential types:
assert_type(grpc.alts_channel_credentials(), grpc.ChannelCredentials)
assert_type(grpc.alts_server_credentials(), grpc.ServerCredentials)
assert_type(grpc.compute_engine_channel_credentials(grpc.CallCredentials("")), grpc.ChannelCredentials)
assert_type(grpc.insecure_server_credentials(), grpc.ServerCredentials)

# XDS credentials:
assert_type(
grpc.xds_channel_credentials(grpc.local_channel_credentials(grpc.LocalConnectionType.LOCAL_TCP)), grpc.ChannelCredentials
)
assert_type(grpc.xds_server_credentials(grpc.insecure_server_credentials()), grpc.ServerCredentials)

# Channel ready future
channel = grpc.insecure_channel("target", ())
assert_type(grpc.channel_ready_future(channel).result(), None)

# Channel options supports list:
assert_type(grpc.insecure_channel("target", []), grpc.Channel)
assert_type(grpc.insecure_channel("target", [("a", "b")]), grpc.Channel)
assert_type(grpc.insecure_channel("target", [("a", "b"), ("c", "d")]), grpc.Channel)

# Client call details optionals:
call_details = grpc.ClientCallDetails()
assert_type(call_details.method, str)
assert_type(call_details.timeout, Optional[float])

# Call iterator
call_iter = cast(grpc._CallIterator[str], None)
for call in call_iter:
assert_type(call, str)
36 changes: 36 additions & 0 deletions stubs/grpcio/@tests/test_cases/check_handler_inheritance.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
from __future__ import annotations

from typing import cast
from typing_extensions import assert_type

import grpc


class Request:
pass


class Response:
pass


def unary_unary_call(rq: Request, ctx: grpc.ServicerContext) -> Response:
assert_type(rq, Request)
return Response()


class ServiceHandler(grpc.ServiceRpcHandler[Request, Response]):
def service_name(self) -> str:
return "hello"

def service(self, handler_call_details: grpc.HandlerCallDetails) -> grpc.RpcMethodHandler[Request, Response] | None:
rpc = grpc.RpcMethodHandler[Request, Response]()
rpc.unary_unary = unary_unary_call
return rpc


h = ServiceHandler()
ctx = cast(grpc.ServicerContext, None)
svc = h.service(grpc.HandlerCallDetails())
if svc is not None and svc.unary_unary is not None:
svc.unary_unary(Request(), ctx)
35 changes: 35 additions & 0 deletions stubs/grpcio/@tests/test_cases/check_multi_callable.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
from __future__ import annotations

from typing import Protocol, cast
from typing_extensions import assert_type

import grpc


class DummyRequest:
pass


class DummyReply:
pass


class DummyServiceStub(Protocol):
UnaryUnary: grpc.UnaryUnaryMultiCallable[DummyRequest, DummyReply]
UnaryStream: grpc.UnaryStreamMultiCallable[DummyRequest, DummyReply]
StreamUnary: grpc.StreamUnaryMultiCallable[DummyRequest, DummyReply]
StreamStream: grpc.StreamStreamMultiCallable[DummyRequest, DummyReply]


stub = cast(DummyServiceStub, None)
req = DummyRequest()

assert_type(stub.UnaryUnary(req), DummyReply)

for resp in stub.UnaryStream(req):
assert_type(resp, DummyReply)

assert_type(stub.StreamUnary(iter([req])), DummyReply)

for resp in stub.StreamStream(iter([req])):
assert_type(resp, DummyReply)
9 changes: 9 additions & 0 deletions stubs/grpcio/@tests/test_cases/check_reflection.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
from __future__ import annotations

from typing import cast

import grpc
from grpc_reflection.v1alpha.reflection import enable_server_reflection

server = cast(grpc.Server, None)
enable_server_reflection(["foo"], server, None)
9 changes: 9 additions & 0 deletions stubs/grpcio/@tests/test_cases/check_reflection_aio.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
from __future__ import annotations

from typing import cast

import grpc.aio
from grpc_reflection.v1alpha.reflection import enable_server_reflection

server = cast(grpc.aio.Server, None)
enable_server_reflection(["foo"], server, None)
14 changes: 14 additions & 0 deletions stubs/grpcio/@tests/test_cases/check_register.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
from __future__ import annotations

from typing import Any

import grpc


@grpc.Call.register
class CallProxy:
def __init__(self, target: grpc.Call) -> None:
self._target = target

def __getattr__(self, name: str) -> Any:
return getattr(self._target, name)
22 changes: 22 additions & 0 deletions stubs/grpcio/@tests/test_cases/check_server_interceptor.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
from __future__ import annotations

from collections.abc import Callable

import grpc


class Request:
pass


class Response:
pass


class NoopInterceptor(grpc.ServerInterceptor[Request, Response]):
def intercept_service(
self,
continuation: Callable[[grpc.HandlerCallDetails], grpc.RpcMethodHandler[Request, Response] | None],
handler_call_details: grpc.HandlerCallDetails,
) -> grpc.RpcMethodHandler[Request, Response] | None:
return continuation(handler_call_details)
8 changes: 8 additions & 0 deletions stubs/grpcio/@tests/test_cases/check_status.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from __future__ import annotations

from grpc import Status
from grpc_status import to_status

# XXX: to_status actually expects a "google.rpc.status.Status",
# but the stubs for that aren't present yet.
status: Status = to_status(None)
15 changes: 15 additions & 0 deletions stubs/grpcio/METADATA.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
version = "1.*"
upstream_repository = "https://github.com/grpc/grpc"
partial_stub = true
requires = [
"types-protobuf",
]

[tool.stubtest]
ignore_missing_stub = true
stubtest_requirements = [
"grpcio-channelz",
"grpcio-health-checking",
"grpcio-reflection",
"grpcio-status",
Comment on lines +11 to +14
Copy link
Collaborator

@Avasam Avasam Apr 2, 2025

Choose a reason for hiding this comment

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

I just realized, but aren't your top-level packages actually separate distributions?
Shouldn't these all be their own stubs rather than all lumped in stubs/grpcio ?

If someone installs grcpio, it doesn't come with, for example, the package grpc_health. You have to specifically install grpcio-health-checking from what I can see.

To avoid delaying this PR further I wouldn't mind splitting it up as a quick fix afterward. Just thought I'd at least mention it while I notice it.

grpc is a bit weird in that the source is a mono-repo and the distributions versions seems to be synchronized.

Copy link

Choose a reason for hiding this comment

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

will we need to add types-grpcio to typeshed-internal/stub_uploader before we can split in that way?

Copy link
Collaborator

@Avasam Avasam Apr 4, 2025

Choose a reason for hiding this comment

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

Stub-uploader automatically accepts dependencies on other typeshed stubs IIRC.

But as I mentioned, I don't mind doing that as a follow-up, and seeing if we hit any roadblocks there. Just so this PR doesn't get dragged on too much longer.

]
Loading