Skip to content

blackbull.grpc.registry

blackbull.grpc.registry

gRPC service registry — maps /package.Service/Method to a handler.

This is the gRPC analogue of :class:blackbull.router.Router: it holds the table of method paths and the coroutine that serves each one.

A unary handler is::

async def handler(request: bytes, context: GrpcContext) -> bytes

A server-streaming handler is an async generator that yields zero or more response messages::

async def handler(request: bytes, context: GrpcContext):
    yield b'...'
    yield b'...'

In both cases request is the (already de-framed) request message body and the yielded / returned value is a response message body. Protobuf (de)serialisation is the handler's responsibility — the framework stays dependency-free. Handlers signal a non-OK result by raising :class:GrpcError or calling context.abort(...).

A client-streaming or bidirectional handler takes an async iterator of request messages instead of a single request::

async def handler(request_iter, context) -> bytes:   # client-streaming
    async for message in request_iter:
        ...

async def handler(request_iter, context):            # bidirectional
    async for message in request_iter:
        yield ...

Response-streaming is detected automatically at registration via :func:inspect.isasyncgenfunction; request-streaming is detected from the first parameter name (request_iter / requests / request_iterator / request_stream). Pass streaming= / client_streaming= explicitly to override when a decorator hides the handler's nature.

GrpcMethod

Bases: NamedTuple

A registered method: its handler and the two streaming axes.

streaming is the response axis (the handler is an async generator that yields response messages); client_streaming is the request axis (the handler takes an async iterator of request messages instead of a single request: bytes). The four combinations are unary, server-streaming, client-streaming, and bidirectional.

handler is annotated as a bare :class:~collections.abc.Callable rather than :data:GrpcHandler: a response-streaming handler is an async generator function (its call returns an async iterator, not an Awaitable), and a plain Callable is the one form that stays runtime-isinstanceable for the NamedTuple field check (see memory beartype-namedtuple-callable-alias).

GrpcServiceRegistry

Holds the path -> GrpcMethod table for gRPC methods.

add_method(path, handler, *, streaming=None, client_streaming=None)

Register handler for the fully-qualified method path (/package.Service/Method or package.Service/Method).

streaming is the response axis — server-streaming (the handler is an async generator that yields response messages). When None (the default) it is auto-detected with :func:inspect.isasyncgenfunction; pass True to force it for a handler whose async-generator nature is hidden behind a wrapper, or False to force a single response. Forcing False on an async-generator function is a contradiction and raises ValueError.

client_streaming is the request axis — the handler takes an async iterator of request messages (first parameter request_iter / requests / request_iterator / request_stream) instead of a single request: bytes. When None it is auto-detected from that first parameter name; pass an explicit bool to override.

add_service(service, methods)

Register every method_name -> handler in methods under the fully-qualified service name (e.g. "helloworld.Greeter").

Each handler's streaming-ness is auto-detected individually.

lookup(path)

Return the handler for path, or None if unregistered.

Backwards-compatible accessor (returns just the callable); use :meth:lookup_method when the streaming flag is needed.

lookup_method(path)

Return the :class:GrpcMethod for path, or None if unregistered.

method(path, *, streaming=None, client_streaming=None)

Decorator form of :meth:add_method.

methods()

Return all registered method paths, in registration order.