Post-process parsed data to push inherited properties and functions down into subclasses. This ensures API pages for classes like UMjJointVelSensor actually show their inherited components.
(data: List[Dict[str, Any]])
| 88 | # Step 2 — Emit Markdown |
| 89 | # ────────────────────────────────────────────────────────────────────────────── |
| 90 | def resolve_inheritance(data: List[Dict[str, Any]]): |
| 91 | """ |
| 92 | Post-process parsed data to push inherited properties and functions down into subclasses. |
| 93 | This ensures API pages for classes like UMjJointVelSensor actually show their inherited components. |
| 94 | """ |
| 95 | print("\n─── Step 1.5: Resolving Inheritance ───") |
| 96 | |
| 97 | # 1. Build a lookup dict: class_name -> class object |
| 98 | class_map = {} |
| 99 | for f in data: |
| 100 | for c in f.get('classes', []): |
| 101 | class_map[c['name']] = c |
| 102 | |
| 103 | # 2. Helper to get all inherited members (recursive) |
| 104 | def get_inherited_members(cls_name: str, memo=None) -> tuple: |
| 105 | if memo is None: memo = set() |
| 106 | if cls_name in memo: return [], [] # prevent cyclic loops just in case |
| 107 | memo.add(cls_name) |
| 108 | |
| 109 | cls = class_map.get(cls_name) |
| 110 | if not cls: return [], [] |
| 111 | |
| 112 | props = copy.deepcopy(cls.get('properties', [])) |
| 113 | funcs = copy.deepcopy(cls.get('functions', [])) |
| 114 | |
| 115 | parent_name = cls.get('parent') |
| 116 | if parent_name and parent_name in class_map: |
| 117 | p_props, p_funcs = get_inherited_members(parent_name, memo) |
| 118 | props.extend(p_props) |
| 119 | funcs.extend(p_funcs) |
| 120 | |
| 121 | return props, funcs |
| 122 | |
| 123 | # 3. Apply inherited members to all classes |
| 124 | resolved_count = 0 |
| 125 | for f in data: |
| 126 | for c in f.get('classes', []): |
| 127 | parent_name = c.get('parent') |
| 128 | if parent_name and parent_name in class_map: |
| 129 | p_props, p_funcs = get_inherited_members(parent_name) |
| 130 | |
| 131 | # Filter out ones we already have (overrides) |
| 132 | existing_prop_names = {p['name'] for p in c.get('properties', [])} |
| 133 | existing_func_names = {f_['name'] for f_ in c.get('functions', [])} |
| 134 | |
| 135 | added_props = [p for p in p_props if p['name'] not in existing_prop_names] |
| 136 | added_funcs = [f_ for f_ in p_funcs if f_['name'] not in existing_func_names] |
| 137 | |
| 138 | c.setdefault('properties', []).extend(added_props) |
| 139 | c.setdefault('functions', []).extend(added_funcs) |
| 140 | |
| 141 | if added_props or added_funcs: |
| 142 | resolved_count += 1 |
| 143 | |
| 144 | print(f" ✓ Inherited members resolved for {resolved_count} subclasses") |
| 145 | |
| 146 | |
| 147 | def step_emit(data): |
no test coverage detected