You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Add first-class Python gRPC support (servicer binding + client detection)
enola already produced gRPC server routes from .proto files (language-agnostic)
and detected the client side + bound server routes to handlers for Go and
TypeScript. Python had neither: a servicer was a plain class and a stub.Method()
call produced no route, so Python dropped out of cross-repo gRPC graphs, impact
analysis, and unused-route detection. This brings Python to parity.
Detection works from hand-written source + the .proto, not from generated
*_pb2_grpc.py stubs — real repos (vosk-server, airflow) build those at build time
and never commit them, so scanning generated stubs finds nothing.
Client side:
- New pythonextractor/grpcpy.go detects `stub = mod._pb2_grpc.FooStub(channel)`
bindings and `stub.Method()` calls, emitting client-role routes. Binding is
positional (interleaved by source offset), so a stub var rebound to a second
service — the vosk pattern — resolves each call to the right service.
- Python source only knows the short service name, so routes are emitted with a
provisional short Name; a new engine pass resolvePyGRPCClientRoutes rewrites
them to the fully-qualified wire path (from the proto's server routes) BEFORE
linkCrossRepo, which matches routes by Name. Remove-then-add keeps the store's
name index consistent. Unresolved (no proto / ambiguous short name) routes are
left provisional.
Server side:
- Generalize bindGRPCHandlers/implShortName to bind a proto server route to a
Python servicer method via the `class X(mod._pb2_grpc.FooServicer)` convention
(in addition to Go's Unimplemented<Service>Server embed), indexing Python
classes alongside Go structs. The existing per-repo scoping and ambiguity guard
are unchanged — two impls of one service in a repo (vosk grpc/ + grpc-wav2vec/)
correctly stay unbound.
Also: classify python-grpc-client in crossrepo handWrittenClientSources; bump
cacheVersion v87 -> v88 (Python extractor now emits new facts); register cachecov
coverage for v88 and for v87 (the resolveCall fix, whose registration was missing).
Tests: unit tests for client detection (incl. positional rebinding + adversarial
GrpcHook/no-import negatives) and for servicer binding + FQ resolution; a golden
py_grpc_multirepo fixture (trimmed from real vosk-server) exercising the full
pipeline — proto server routes, handler binding, client FQ resolution, cross-repo
link, and an unmatched RPC. Verified live via MCP against real vosk-server and
against airflow (no regression).
0 commit comments