edri.api.grpc package¶
Submodules¶
edri.api.grpc.directives module¶
ResponseDirective -> grpc.StatusCode mapping.
The 4th instance of the pattern REST (HTTPHandler._directive_handlers), HTML (inherits HTTPHandler's) and WS (WebsocketHandler._directive_handlers) already use: business logic raises transport-agnostic ResponseDirective instances, and each transport maps the ones it understands to its own status/metadata shape, logging and ignoring (never raising on) anything it doesn't.
Unlike the other three, gRPC has exactly one handler shape (there's no per-event handler subclass to hang a _directive_handlers class attribute and an MRO-walking directive_handlers() off — see BaseHandler for that version), so this is a flat module-level table and function instead.
edri.api.grpc.export module¶
Render edri.api.grpc.schema's generated FileDescriptorProto to .proto3 source text, for consumers who want a static .proto file to run through their own language's codegen instead of pointing it at the reflection service. Pure text generation over the FileDescriptorProto's own fields - no DescriptorPool/message_factory/live message classes involved (unlike edri/api/grpc/runtime.py), so this direction doesn't touch anything beyond the plain (proto2-defined) descriptor_pb2 shape schema.py already builds.
Usage: python -m edri.api.grpc.export [output_path] - writes to
output_path if given, otherwise to stdout.
edri.api.grpc.listener module¶
GRPCListener: EDRI's gRPC front end.
A Process analogous to Listener (edri/api/listener.py), but running its own grpc.aio.server() rather than an ASGI app - see the design spec (docs/superpowers/specs/2026-09-12-grpc-api-design.md) for why this is a separate process rather than multiplexed into the ASGI Listener. Shares the same api_broker_queue Listener uses to register clients with Broker and exchange Events over a pipe, via the same ClientRegistry/recv_connection/ build_event helpers Listener itself now delegates to.
Import-safety note: see edri/api/grpc/__init__.py - import grpc here resolves to the third-party grpc package, not this one.
edri.api.grpc.runtime module¶
Turns a Schema (edri/api/grpc/schema.py) into live protobuf message classes, and encodes/decodes values between those messages and the dict[str, Any] shape edri.api.event_builder.build_event()/Response.as_dict() already use - so the gRPC listener never needs its own copy of "what does an Event's fields look like".
No protoc invocation, no generated _pb2.py stub files: message classes come from google.protobuf.descriptor_pool + message_factory against the FileDescriptorProto schema.py already built.
edri.api.grpc.schema module¶
FileDescriptorProto generator for EDRI's gRPC API.
Walks event_type_extensions (via edri.api.event_registry.ensure_populated()) and, for every Event with ApiType.GRPC not in its exclude set, builds:
a <EventName>Request / <EventName>Response message pair
a unary rpc <EventName>(<EventName>Request) returns (<EventName>Response)
one oneof member each in the shared ClientMessage/ServerMessage streaming envelope messages (see edri/api/grpc/listener.py for the bidi stream that serves them)
a Connect(stream ClientMessage) returns (stream ServerMessage) bidi rpc
on one shared EdriService. Events are processed in a name-sorted order so message/method declaration order is stable across restarts. Each event's oneof field number (the value actually written to the wire - WhichOneof() on the receiving end resolves purely from this number, never from a name) is independent of that order too: see _field_number_for()'s docstring for why positional numbering doesn't work here.
A field whose declared type type_mapping.map_field() can't represent (bare Any, a non-dataclass custom object, an Injection-validated field - Injection resolution isn't implemented yet, unions beyond X | None, ...) skips that whole event from GRPC schema generation with a logged warning, the same "best effort, don't crash startup over one bad event" posture _offers() already takes for HTML.
edri.api.grpc.type_mapping module¶
Python type <-> proto3 field mapping.
Pure and protobuf-runtime-free beyond the FieldDescriptorProto.Type/Label enum constants: no DescriptorPool, no message classes. schema.py uses this to decide what to put in a FileDescriptorProto; runtime.py uses the same FieldMapping to encode/decode values against the live message classes built from that descriptor. Keeping the decision in one place means the two can never disagree about how a given field is represented.
Module contents¶
gRPC API front end for EDRI.
Import-safety note: this package is named grpc, same as the third-party
grpc PyPI package. Python 3's absolute-import default means a plain
import grpc written anywhere inside edri/api/grpc/*.py resolves to the
third-party package, never to this one — there is no ambiguity and no
special handling needed. Left as a comment (here and in each module that
does import grpc) so nobody "fixes" it into a relative import later.
Everything under this package is optional: it is only usable when the
grpc extra (grpcio, grpcio-reflection, protobuf) is
installed, and nothing elsewhere in edri imports this package eagerly.