Python visitor API
Audience: This page is for Python developers who need to inspect or transform a LotusRPC interface definition programmatically.
Overview
LotusRPC parses the definition file into an LrpcDef object and exposes its structure through the visitor pattern. Implement a custom visitor to walk every element of the definition — services, functions, streams, structs, enums, constants — without managing traversal logic yourself.
Common use cases include generating code or documentation, extracting information, and enforcing custom naming conventions.
Loading a definition
Use load_lrpc_def to load a definition file:
from lrpc.utils import load_lrpc_def
lrpc_def = load_lrpc_def("example.lrpc.yaml") # path, YAML string, or file object
For more information on loading a definition file see Python API — Client
Running a visitor
Pass a visitor instance to LrpcDef.accept():
lrpc_def.accept(visitor)
By default, accept also traverses the built-in meta service. To skip it:
lrpc_def.accept(visitor, visit_meta_service=False)
LrpcVisitor
Subclass LrpcVisitor and override the methods you need. Every method has a default no-op implementation, so you only override what you care about:
from lrpc.visitors import LrpcVisitor
class MyVisitor(LrpcVisitor):
def visit_lrpc_service(self, service):
print(f"Service: {service.name()}")
def visit_lrpc_function(self, function):
print(f" function: {function.name()}")
Visit method reference
The table is ordered by traversal sequence — accept calls methods top to bottom. Structs, enums, and constants are always visited before the first service. Functions and streams within a service are visited in definition order.
| Method | Arguments | Called when |
|---|---|---|
visit_lrpc_def |
lrpc_def: LrpcDef |
Before traversal starts |
visit_rpc_settings |
settings: RpcSettings |
Once, after visit_lrpc_def |
visit_lrpc_constants |
— | Before the first constant (only if constants exist) |
visit_lrpc_constant |
constant: LrpcConstant |
Once per constant |
visit_lrpc_constants_end |
— | After the last constant |
visit_lrpc_enum |
enum: LrpcEnum |
Before each enum |
visit_lrpc_enum_field |
enum: LrpcEnum, field: LrpcEnumField |
Once per enum field |
visit_lrpc_enum_end |
enum: LrpcEnum |
After each enum |
visit_lrpc_struct |
struct: LrpcStruct |
Before each struct |
visit_lrpc_struct_field |
struct: LrpcStruct, field: LrpcVar |
Once per struct field |
visit_lrpc_struct_end |
— | After each struct |
visit_lrpc_service |
service: LrpcService |
Before each service |
visit_lrpc_function |
function: LrpcFun |
Before each function |
visit_lrpc_function_return |
ret: LrpcVar |
Once per function return value |
visit_lrpc_function_return_end |
— | After the last function return value |
visit_lrpc_function_param |
param: LrpcVar |
Once per function parameter |
visit_lrpc_function_param_end |
— | After the last function parameter |
visit_lrpc_function_end |
— | After each function |
visit_lrpc_stream |
stream: LrpcStream |
Before each stream |
visit_lrpc_stream_param |
param: LrpcVar |
Once per stream parameter |
visit_lrpc_stream_param_end |
— | After stream parameters |
visit_lrpc_stream_return |
ret: LrpcVar |
Once per stream return value (server streams only) |
visit_lrpc_stream_return_end |
— | After stream return values |
visit_lrpc_stream_end |
— | After each stream |
visit_lrpc_service_end |
— | After each service |
visit_lrpc_user_settings |
user_settings |
Once for all user settings |
visit_lrpc_def_end |
— | After traversal ends |
Stream parameters vs. returns:
For a client stream the params are the message fields; returns is empty. For a server stream, params is [start] (the implicit start/stop boolean) and returns are the message fields. The visitor calls visit_lrpc_stream_param for params and visit_lrpc_stream_return for returns in both cases.
The types passed as arguments to visit methods are documented in the Python definition model reference.
Example
The visitor below prints a summary of every service, function, and stream in the definition:
from lrpc.visitors import LrpcVisitor
from lrpc.utils import load_lrpc_def
from lrpc.core import LrpcStream
class DefinitionSummary(LrpcVisitor):
def visit_lrpc_service(self, service):
print(f"Service: {service.name()} (ID {service.id()})")
def visit_lrpc_function(self, function):
params = ", ".join(f"{p.name()}: {p.base_type()}" for p in function.params())
returns = ", ".join(f"{r.name()}: {r.base_type()}" for r in function.returns())
print(f" fn {function.name()}({params}) -> ({returns})")
def visit_lrpc_stream(self, stream):
direction = "client→server" if stream.origin() == LrpcStream.Origin.CLIENT else "server→client"
finite = ", finite" if stream.is_finite() else ""
print(f" str {stream.name()} [{direction}{finite}]")
lrpc_def = load_lrpc_def("example.lrpc.yaml")
lrpc_def.accept(DefinitionSummary(), visit_meta_service=False)
Output for the example.lrpc.yaml from Getting started:
Service: math (ID 0)
fn add(a: int32_t, b: int32_t) -> (result: int32_t)