Move failures to the parent class when ALL inheritors fail. If every concrete subclass that inherits a method from a parent class appears in *failing_tests*, replace those per-subclass entries with a single entry on the parent. This avoids creating redundant super-call overrides in
(
contents: str,
failing_tests: set[tuple[str, str]],
error_messages: dict[tuple[str, str], str] | None = None,
)
| 289 | |
| 290 | |
| 291 | def _consolidate_to_parent( |
| 292 | contents: str, |
| 293 | failing_tests: set[tuple[str, str]], |
| 294 | error_messages: dict[tuple[str, str], str] | None = None, |
| 295 | ) -> tuple[set[tuple[str, str]], dict[tuple[str, str], str] | None]: |
| 296 | """Move failures to the parent class when ALL inheritors fail. |
| 297 | |
| 298 | If every concrete subclass that inherits a method from a parent class |
| 299 | appears in *failing_tests*, replace those per-subclass entries with a |
| 300 | single entry on the parent. This avoids creating redundant super-call |
| 301 | overrides in every child. |
| 302 | |
| 303 | Returns: |
| 304 | (consolidated_failing_tests, consolidated_error_messages) |
| 305 | """ |
| 306 | tree = ast.parse(contents) |
| 307 | class_bases, class_methods = _build_inheritance_info(tree) |
| 308 | |
| 309 | # Group by (defining_parent, method) → set of failing children |
| 310 | from collections import defaultdict |
| 311 | |
| 312 | groups: dict[tuple[str, str], set[str]] = defaultdict(set) |
| 313 | for class_name, method_name in failing_tests: |
| 314 | defining = _find_method_definition( |
| 315 | class_name, method_name, class_bases, class_methods |
| 316 | ) |
| 317 | if defining and defining != class_name: |
| 318 | groups[(defining, method_name)].add(class_name) |
| 319 | |
| 320 | if not groups: |
| 321 | return failing_tests, error_messages |
| 322 | |
| 323 | result = set(failing_tests) |
| 324 | new_error_messages = dict(error_messages) if error_messages else {} |
| 325 | |
| 326 | for (parent, method_name), failing_children in groups.items(): |
| 327 | all_inheritors = _find_all_inheritors( |
| 328 | parent, method_name, class_bases, class_methods |
| 329 | ) |
| 330 | |
| 331 | if all_inheritors and failing_children >= all_inheritors: |
| 332 | # All inheritors fail → mark on parent instead |
| 333 | children_keys = {(child, method_name) for child in failing_children} |
| 334 | result -= children_keys |
| 335 | result.add((parent, method_name)) |
| 336 | # Pick any child's error message for the parent |
| 337 | if new_error_messages: |
| 338 | for child in failing_children: |
| 339 | msg = new_error_messages.pop((child, method_name), "") |
| 340 | if msg: |
| 341 | new_error_messages[(parent, method_name)] = msg |
| 342 | |
| 343 | return result, new_error_messages or error_messages |
| 344 | |
| 345 | |
| 346 | def build_patches( |
no test coverage detected