blackbull.grpc¶
blackbull.grpc
¶
Pure-Python gRPC server support for BlackBull.
gRPC runs on HTTP/2; BlackBull already ships a complete HTTP/2 implementation, so this package adds only the gRPC-specific pieces:
- :mod:
~blackbull.grpc.codec— Length-Prefixed-Message framing. - :mod:
~blackbull.grpc.compression— gzip message compression (grpc-encoding). - :class:
~blackbull.grpc.registry.GrpcServiceRegistry—/Service/Method→ handler. - :class:
~blackbull.grpc.status.GrpcStatus/ :class:GrpcError— canonical codes. - :func:
~blackbull.grpc.asgi.serve_grpc+ :class:GrpcContext— the ASGI bridge.
Wire it into an app with app.enable_grpc(registry); gRPC requests
(content-type: application/grpc) are then multiplexed onto the same
HTTP/2 port as the app's REST and WebSocket traffic. All four RPC kinds are
served — unary, server-streaming, client-streaming, and bidirectional — where a
response-streaming handler is an async generator and a request-streaming handler
takes an async iterator of request messages (request_iter).
Protobuf is not a dependency: handlers receive and return raw message
bytes, so the application chooses its own serialisation (grpc_tools.protoc
output, protobuf, or hand-rolled).
GrpcContext
¶
Per-call context handed to a gRPC handler.
Exposes request metadata (the HTTP/2 headers), the call deadline and peer,
and lets the handler set the outgoing status, a human-readable message,
leading/trailing metadata, or abort the call outright — the subset of
grpcio's ServicerContext that a raw-bytes transport can honour.
The response-start machinery (_send … _started) is bound by
:func:serve_grpc just before the handler runs; handlers touch it only via
:meth:send_initial_metadata.
abort(status, details='')
¶
Raise :class:GrpcError to end the call with a non-OK status.
invocation_metadata()
¶
Return all request metadata (HTTP/2 headers) as (name, value)
pairs — grpcio's ServicerContext.invocation_metadata. Pseudo-
headers (:method, :path, …) are excluded; they are call routing,
not application metadata.
metadata(name, default=b'')
¶
Return a request header (call metadata) value, or default.
peer()
¶
Return the client address as grpcio formats it (ipv4:host:port /
ipv6:[host]:port), or '' when the transport did not supply one.
send_initial_metadata(metadata)
async
¶
Send response leading metadata now (the initial HTTP/2 HEADERS),
before the first response message — grpcio's
ServicerContext.send_initial_metadata.
Optional: if never called, the response HEADERS are still emitted lazily
(just before the first message, or with the trailers for an empty
response). Calling it flushes them early with metadata attached — used
to hand the client leading metadata (auth challenges, stream ids) up
front. Raises :class:ValueError once the HEADERS have already gone
out (grpcio's "initial metadata no longer allowed").
time_remaining()
¶
Seconds left until the call deadline (never negative), or None
when the client set no grpc-timeout — grpcio's
ServicerContext.time_remaining. Lets a handler shed work it cannot
finish in time.
trailing_metadata()
¶
The trailing metadata set so far (a copy) — lets helpers compose
with, rather than clobber, what the handler already set (e.g.
blackbull-protobuf's abort_with_details appending
grpc-status-details-bin).
GrpcDecodeError
¶
Bases: ValueError
Raised when a DATA buffer is not a valid sequence of Length-Prefixed-Messages (truncated prefix or short body).
GrpcError
¶
Bases: Exception
Raised by a service handler (or context.abort) to end the RPC with
a non-OK status. serve_grpc translates it into grpc-status /
grpc-message trailers.
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.
decode_messages(data)
¶
Parse data into a list of (compressed, payload) messages.
A single DATA buffer may contain zero, one, or many framed messages
(gRPC permits multiple messages per stream and does not align them to
DATA-frame boundaries). Raises :class:GrpcDecodeError on a truncated
prefix or a message body shorter than its declared length.
encode_message(payload, *, compressed=False)
¶
Frame payload as a single gRPC Length-Prefixed-Message.
serve_grpc(registry, conn, receive, send)
async
¶
Serve a single gRPC call (unary or server-streaming) through the ASGI (scope, receive, send) bridge. Never raises for handler/protocol errors: every failure path is reported as a gRPC status.