Overview
Yarpc is a code generator for D-Bus services and clients. The interfaces are defined using YAML files.
Features
Un-(marshalling) of structs
Enum support
Reusable definitions
Why not use the XML introspection data format?
Many languages already come with tooling to generate D-Bus client or service code from the XML introspection data.
The XML format does have some disadvantages
it does not support enums, just uints with some documentation
XML is not the most human-readable format, which can be important when the definitions serve as a single source of truth
it does not support named structs. Depending on the tooling used, there may be annotations to define the corresponding types for a certain language, but this usually doesn’t include unmarshalling or the generation of those types. This feature may also not be available in tooling for all languages.
Usage
Assuming we have two python apps calc_service and calc_client
for which we want to generate some interface code:
Create a folder with interface definitions
foo/example.yamlobjects: - name: Example kind: interface docs: Minimum example interface members: - name: CalculateAverage kind: method docs: calculates the average value of the provided numbers args: - name: numbers type: array<double> docs: the numbers to calculate the average for returns: type: double docs: the arithmetic average or 0, if the list is empty - name: Ping kind: signal docs: emitted every second outputs: - name: ExampleService language: py location: calc_service/src/calc_service/gen busName: com.example.calcservice services: - className: CalcService definition: Example objectPath: /com/example/calcservice interfaceName: com.example.calcservice.calculations - name: ExampleClient language: py location: calc_client/src/calc_client/gen clients: - className: CalcClient definition: Example busName: com.example.calcservice objectPath: /com/example/calcservice interfaceName: com.example.calcservice.calculations
More examples can be found under tests/definitions of this repo
Run Yarpc via
yarpc footo generate the codeUse the generated code
calc_service/src/calc_service/main.pyimport asyncio from calc_service.gen.connection import Connection from calc_service.gen.calc_service import CalcService async def main(): service = CalcService() async def calculate_average(values: list) -> float: return sum(values)/len(values) if values else 0 service.on_CalculateAverage(calculate_average) async def do_ping(): while True: await asyncio.sleep(1) service.Ping() await asyncio.gather( Connection.run(service), do_ping() ) if __name__ == "__main__": asyncio.run(main())
calc_client/src/calc_client/main.pyimport asyncio import random from calc_client.gen.calc_client import CalcClient async def main(): client = CalcClient() def on_ping(): print("Ping") client.on_Ping(on_ping) async def do_stuff(): for _ in range(3): await asyncio.sleep(1) random_numbers = [random.random()*100 for _ in range(5)] result = await client.CalculateAverage(random_numbers) print(f"Numbers: {random_numbers}\nAvg: {result}") client.disconnect() await asyncio.sleep(2) await asyncio.gather( client.connect(), do_stuff(), ) if __name__ == "__main__": asyncio.run(main())
Yarpc can also be used to check the consistency of the generated code with the definitions
via yarpc foo --check. This can be useful for CI jobs to ensure running yarpc after changing
definitions is not forgotten.