| 110 | |
| 111 | |
| 112 | def class_methods(class_node: ast.ClassDef) -> list[str]: |
| 113 | seen: set[str] = set() |
| 114 | out: list[str] = [] |
| 115 | for child in class_node.body: |
| 116 | if is_method(child): |
| 117 | name = child.name # type: ignore[attr-defined] |
| 118 | if not name.startswith("_") or name in {"__init__", "__call__", "__enter__", "__exit__"}: |
| 119 | if name not in seen: |
| 120 | seen.add(name) |
| 121 | out.append(name) |
| 122 | elif isinstance(child, ast.If): |
| 123 | # Flatten version guards: walk both branches. |
| 124 | for sub in child.body + child.orelse: |
| 125 | if is_method(sub): |
| 126 | name = sub.name # type: ignore[attr-defined] |
| 127 | if name not in seen: |
| 128 | seen.add(name) |
| 129 | out.append(name) |
| 130 | return out |
| 131 | |
| 132 | |
| 133 | def base_qns(class_node: ast.ClassDef, module_qn: str) -> list[str]: |