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 ✓")