-
-
Notifications
You must be signed in to change notification settings - Fork 1.8k
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
Import gRPC stubs from the grpc-stubs project #11204
base: main
Are you sure you want to change the base?
Conversation
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
Thanks for the PR! The stubtest checks currently fail: https://github.com/python/typeshed/actions/runs/7343810747/job/19994850116?pr=11204. This is the relevant output, I think:
I have no experience with grpc, so please correct me if my understanding is wrong: While these are the stubs for "gprc", what you actually install is the |
Thanks for getting back to me so quickly. I can see the failure but not sure what to do about it. I think you're right that it boils down to the |
I think just putting the stubs in a |
I haven't thought too deeply about it, but usually our stubtest checks install the package "x" when building the stubs in the "x" directory (the package will be called "types-x"). We could add a field like On a related note: It would make sense to me to also add (dummy) stubs for |
Putting it in |
This comment has been minimized.
This comment has been minimized.
Interesting, it looks like the typings in the Does typeshed play nice when part of a package is typed well and doesn't require stubs but another part of it isn't, and does? |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
I haven't investigated in details. But there's a few easy linting failures to fix. And the added test-cases aren't passing. Stubtest is also whining about apparent mismatches between the stubs and what's found at runtime. stubtest isn't always 100% correct and sometimes we do have to "cheat" a little bit, but those cases still need to be explicitly excluded and explained. |
I wouldn't worry too much about stubtest for this PR. We can simply silence its errors with allowlists (see any file named The test cases (in |
Co-authored-by: Avasam <[email protected]>
Co-authored-by: Avasam <[email protected]>
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
i took a look at the failing tests and made a pr to @shabbyrobe's branch: |
Changes to resolve stubtest errors
This comment has been minimized.
This comment has been minimized.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks! Not a full review, just a few things I noticed below and a general observation: Except for general cases, we generally use from typing import ...
imports instead of namespacing typing imports (typing.TypeVar
etc.)
import grpc.aio | ||
|
||
# Interceptor casts | ||
client_interceptors = [typing.cast(grpc.aio.ClientInterceptor, "interceptor")] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Considering this test is never run, only checked with type checkers, this is clearer:
client_interceptors = [typing.cast(grpc.aio.ClientInterceptor, "interceptor")] | |
client_interceptors: list[grpc.aio.ClientInterceptor] = [] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Other casts can also generally be avoided by making it a function parameter.
For example:
async def metadata(call: grpc.aio.Call) -> None:
metadata = await call.initial_metadata()
instead of
async def metadata() -> None:
metadata = await typing.cast(grpc.aio.Call, None).initial_metadata()
Not sure how important that is.
client_interceptors = [typing.cast(grpc.aio.ClientInterceptor, "interceptor")] | ||
grpc.aio.insecure_channel("target", interceptors=client_interceptors) | ||
|
||
server_interceptors = [typing.cast(grpc.aio.ServerInterceptor[typing.Any, typing.Any], "interceptor")] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's also unusual not to import Any
directly from typing – at least here in typeshed.
server_interceptors = [typing.cast(grpc.aio.ServerInterceptor[typing.Any, typing.Any], "interceptor")] | |
server_interceptors: list[grpc.aio.ServerInterceptor[Any, Any]] = [] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Turns out there's a few places we do that, but I don't really see a good reason. I opened #13761 to standardise.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is now addressed
import typing | ||
|
||
import grpc | ||
|
||
# XXX: don't yet know how to add a stub for google.rpc.status_pb2.Status | ||
# without affecting other stuff; may need to make a stub-only package for | ||
# google.rpc as well. | ||
|
||
# Returns a google.rpc.status.Status message corresponding to a given grpc.Call. | ||
def from_call(call: grpc.Call) -> typing.Any: ... | ||
|
||
# Convert a google.rpc.status.Status message to grpc.Status. | ||
def to_status(status: typing.Any) -> grpc.Status: ... |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This comment leads me to believe that the type here simply isn't complete (would require more work). Rather than being unrepresentable by the type system.
import typing | |
import grpc | |
# XXX: don't yet know how to add a stub for google.rpc.status_pb2.Status | |
# without affecting other stuff; may need to make a stub-only package for | |
# google.rpc as well. | |
# Returns a google.rpc.status.Status message corresponding to a given grpc.Call. | |
def from_call(call: grpc.Call) -> typing.Any: ... | |
# Convert a google.rpc.status.Status message to grpc.Status. | |
def to_status(status: typing.Any) -> grpc.Status: ... | |
from typing import Incomplete | |
import grpc | |
# Returns a google.rpc.status.Status message corresponding to a given grpc.Call. | |
def from_call(call: grpc.Call) -> Incomplete: ... | |
# Convert a google.rpc.status.Status message to grpc.Status. | |
def to_status(status: Incomplete) -> grpc.Status: ... |
Which can then be rewritten as
import typing | |
import grpc | |
# XXX: don't yet know how to add a stub for google.rpc.status_pb2.Status | |
# without affecting other stuff; may need to make a stub-only package for | |
# google.rpc as well. | |
# Returns a google.rpc.status.Status message corresponding to a given grpc.Call. | |
def from_call(call: grpc.Call) -> typing.Any: ... | |
# Convert a google.rpc.status.Status message to grpc.Status. | |
def to_status(status: typing.Any) -> grpc.Status: ... | |
import grpc | |
# Returns a google.rpc.status.Status message corresponding to a given grpc.Call. | |
def from_call(call: grpc.Call): ... | |
# Convert a google.rpc.status.Status message to grpc.Status. | |
def to_status(status) -> grpc.Status: ... |
with an entry in pyrightconfig.stricter.json
And yeah, to complete this, you'd need a stub for google.rpc
, wherever that comes from (if it's https://github.com/googleapis/googleapis/blob/master/google/rpc/status.proto, we have some tooling to generate stubs from proto files)
"grpcio-channelz", | ||
"grpcio-health-checking", | ||
"grpcio-reflection", | ||
"grpcio-status", |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
…er-than-accessing-the-typing-module import typing symbols directly rather than accessing the typing module
Diff from mypy_primer, showing the effect of this PR on open source code: spark (https://github.com/apache/spark)
- python/pyspark/sql/connect/utils.py:72: error: Cannot find implementation or library stub for module named "grpc_status" [import-not-found]
- python/pyspark/sql/connect/client/reattach.py:30: error: Cannot find implementation or library stub for module named "grpc_status" [import-not-found]
- python/pyspark/sql/connect/plan.py:656: error: Unused "type: ignore" comment [unused-ignore]
- python/pyspark/sql/connect/client/core.py:59: error: Cannot find implementation or library stub for module named "grpc_status" [import-not-found]
jax (https://github.com/google/jax)
+ jax/experimental/jax2tf/examples/serving/model_server_request.py:18: error: Unused "type: ignore" comment [unused-ignore]
|
Following on from #10020, it seemed from that discussion that there was both desire from the issue tracker on the grpc-stubs project to submit these types to typeshed, and willingness on the part of typeshed to accept them, so here's my best first effort at making that happen. Happy to sign any CLA or copyright assignment you require.
This PR attempts to massage the stubs into a hopefully acceptable shape. The original types are functional, albeit gappy. I'm not sure what typeshed's current stance is on incomplete types but I do see a fair bit of prior art in there so I presume there's at least some leeway.
Full disclosure, there's an element of handoff here in my motivation as I'm not involved in any way with any project that uses gRPC and haven't been for nearly 5 years. I'm in no position to continue as maintainer of those stubs as it is; I hoped that by submitting to typeshed that might open up the maintenance pool somewhat. If that disqualifies this from inclusion that's fair; I can't quite tell if the original submitter of typings to typeshed is expected to continue to be responsible after inclusion, but I will need to archive and deprecate grpc-stubs regardless of the outcome, as I don't have enough mental context for working with gRPC any more and no proximate opportunity to revive it.
I found while preparing this PR that what was in the grpc-sttubs project was substantially misaligned with the typeshed conventions so I've worked through all of the lints and test failures that came up while migrating. I've also ported the tests over from the approach used in grpc-stubs to the approach used in typeshed.
There were a couple of other seemingly related things I wasn't sure about. The implementation package is called grpcio; should I have bundled up the
grpc*
folders into agrpcio
folder or are they ok where they are? Also, when usingrequires
in myMETADATA.toml
, what is the correct way to refer to the basegrpc
package? Pretty sure I'm doing this wrong as I see I'm getting a third party subtest failure.I am happy to spend a bit more time massaging this into a fit state for inclusion; I'm quite sure there's a fair bit that's still wrong about these (especially as they were intended to evolve alongside a project, but it just didn't pan out that way, and I ceased to be involved soon after their brisk creation, c'est la vie). They're a bit gappy in places and there were some outstanding issues on the original repo that I was not steeped enough in gRPC any more to be able to contextualise, let alone fix.
Please let me know whether it's worth proceeding and if there's anything that needs to be done, or if there's no pathway from this point and I should just archive the repo. Thank you.