Python example: mrpt_containers_example.py
Parses, queries and emits YAML/JSON documents with mrpt.containers.YAML.
Modules: mrpt.containers
1#!/usr/bin/env python3
2"""
3Parses, queries and emits YAML/JSON documents with mrpt.containers.YAML.
4
5Demonstrates the MRPT YAML class and shows side-by-side equivalents for
6users migrating from yaml-cpp (pyyaml) or PyYAML.
7
8 mrpt.containers.YAML vs PyYAML / ruamel-yaml
9 ─────────────────────────────────────────────
10 Both parse YAML and JSON. Key differences:
11
12 • mrpt.YAML is a C++ object; child access via [] returns another YAML node,
13 not a native Python dict/list. Call .as_str() / .as_int() / .as_float() /
14 .as_bool() on a leaf to get a Python scalar.
15 (PyYAML returns plain Python dicts/lists/scalars directly.)
16
17 • mrpt.YAML also supports getOrDefault("key", fallback) for safe access.
18
19 • mrpt.YAML integrates with MRPT's CConfigFileBase system, allowing the
20 same YAML files to drive C++ algorithms from Python.
21
22 • Sequence nodes must be created via from_string("[]") before push_back;
23 there is no integer index operator.
24"""
25
26from mrpt.containers import YAML
27
28YAML_SRC = """\
29robot:
30 name: R2D2
31 speed: 3.14
32 active: true
33 sensors:
34 - lidar
35 - camera
36 pose:
37 x: 1.0
38 y: 2.5
39 theta: 0.785
40"""
41
42# ── Parsing ──────────────────────────────────────────────────────────────────
43print("── Parsing ─────────────────────────────────")
44doc = YAML.from_string(YAML_SRC)
45
46# PyYAML equivalent:
47# import yaml
48# doc = yaml.safe_load(YAML_SRC) # returns a plain Python dict
49#
50# mrpt.YAML returns a YAML node (not a plain dict):
51print(f"type: {type(doc)}") # mrpt.containers._bindings.YAML
52print(f"isMap: {doc.isMap()}") # True
53print(f"keys: {doc.keys()}") # ['robot']
54
55# ── Accessing nested values ───────────────────────────────────────────────────
56print("\n── Accessing values ────────────────────────")
57robot = doc["robot"]
58
59# PyYAML: name = doc["robot"]["name"] -> plain str
60# mrpt: name = doc["robot"]["name"].as_str() -> str via explicit cast
61name = robot["name"].as_str()
62speed = robot["speed"].as_float()
63active = robot["active"].as_bool()
64
65print(f"name: {name!r}") # 'R2D2'
66print(f"speed: {speed}") # 3.14
67print(f"active: {active}") # True
68
69assert name == "R2D2"
70assert abs(speed - 3.14) < 1e-9
71assert active is True
72
73# ── Safe access with defaults ─────────────────────────────────────────────────
74print("\n── Safe access (getOrDefault) ───────────────")
75# PyYAML: doc["robot"].get("battery", 100) -> Python dict .get()
76# mrpt: robot.get_int("battery", 100) -> typed helper
77battery = robot.get_int("battery", 100)
78print(f"battery (default 100): {battery}") # 100
79assert battery == 100
80
81# ── Membership test ───────────────────────────────────────────────────────────
82print("\n── 'in' operator ────────────────────────────")
83# PyYAML: "name" in doc["robot"]
84# mrpt: "name" in robot (same syntax, supported via __contains__)
85print(f"'name' in robot: {'name' in robot}") # True
86print(f"'battery' in robot: {'battery' in robot}") # False
87
88# ── Iteration over map keys ───────────────────────────────────────────────────
89print("\n── Iteration ────────────────────────────────")
90# PyYAML: for k, v in doc["robot"].items(): ...
91# mrpt: for k in robot: ... (yields keys; access values with robot[k])
92pose = robot["pose"]
93print("pose fields:")
94for key in pose:
95 print(f" {key}: {pose[key].as_float():.3f}")
96
97# ── Sequences ────────────────────────────────────────────────────────────────
98print("\n── Sequences ────────────────────────────────")
99sensors = robot["sensors"]
100print(f"isSequence: {sensors.isSequence()}")
101print(f"size: {sensors.size()}")
102# mrpt has no integer index operator; inspect via to_string or size:
103print(f"sensors:\n{sensors.to_string().strip()}")
104
105# Building a sequence from scratch — must start from "[]":
106# PyYAML: lst = [1.0, 2.0, 3.0] -> plain Python list
107# mrpt: start from an empty sequence node, then push_back
108nums = YAML.from_string("[]")
109nums.push_back(1.0)
110nums.push_back(2.0)
111nums.push_back(3.0)
112print(f"built numeric sequence size: {nums.size()}") # 3
113assert nums.size() == 3
114
115strs = YAML.from_string("[]")
116strs.push_back_str("alpha")
117strs.push_back_str("beta")
118print(f"string sequence: {strs.to_string().strip()}")
119
120# ── Building a map programmatically ──────────────────────────────────────────
121print("\n── Building maps ────────────────────────────")
122# PyYAML: d = {"x": 1.0, "y": 2.0}; yaml.dump(d)
123# mrpt: use __setitem__ with YAML scalar nodes
124cfg = YAML()
125cfg["x"] = YAML.from_string("1.0")
126cfg["y"] = YAML.from_string("2.0")
127cfg["label"] = YAML.from_string("origin")
128print(f"cfg keys: {cfg.keys()}")
129print(f"cfg['label']: {cfg['label'].as_str()}")
130assert cfg["label"].as_str() == "origin"
131
132# ── Serialization ─────────────────────────────────────────────────────────────
133print("\n── Serialization ────────────────────────────")
134# PyYAML: yaml.dump(doc)
135# mrpt: doc.to_string()
136txt = robot["pose"].to_string()
137print(f"pose as YAML:\n{txt}")
138assert "x" in txt and "y" in txt
139
140# save / load round-trip
141import tempfile, os
142with tempfile.NamedTemporaryFile(suffix=".yaml", delete=False) as f:
143 fname = f.name
144try:
145 doc.save_to_file(fname)
146 doc2 = YAML.from_file(fname)
147 assert doc2["robot"]["name"].as_str() == "R2D2"
148 print(f"round-trip OK (saved to {os.path.basename(fname)})")
149finally:
150 os.unlink(fname)
151
152# ── JSON is also accepted ─────────────────────────────────────────────────────
153print("\n── JSON input (auto-detected) ───────────────")
154# PyYAML does NOT parse JSON by default; mrpt.YAML auto-detects it:
155jdoc = YAML.from_string('{"width": 640, "height": 480}')
156print(f"width: {jdoc.get_int('width')}, height: {jdoc.get_int('height')}")
157assert jdoc.get_int("width") == 640
158
159print("\nAll checks ✓")