(source_path: Path, output_dir: Path, *, expect_report: bool = True, report_name: str = "FRAMEWORK.md")
| 1362 | |
| 1363 | |
| 1364 | def run_self_check(source_path: Path, output_dir: Path, *, expect_report: bool = True, report_name: str = "FRAMEWORK.md") -> bool: |
| 1365 | print("\n[SELF-CHECK] Running post-split validation...") |
| 1366 | ok = True |
| 1367 | py_files = _iter_python_files(output_dir) |
| 1368 | package_name = output_dir.name |
| 1369 | |
| 1370 | try: |
| 1371 | source_names = _collect_top_level_names(source_path) |
| 1372 | output_names: Set[str] = set() |
| 1373 | for path in py_files: |
| 1374 | if path.name == CodeGenerator.SOURCE_BRIDGE_MODULE: |
| 1375 | continue |
| 1376 | output_names.update(_collect_top_level_names(path)) |
| 1377 | missing = sorted(source_names - output_names) |
| 1378 | extra = sorted(output_names - source_names) |
| 1379 | if missing or extra: |
| 1380 | ok = False |
| 1381 | print(f" [FAIL] top-level symbol coverage mismatch: missing={len(missing)} extra={len(extra)}") |
| 1382 | if missing: |
| 1383 | print(f" missing sample: {missing[:12]}") |
| 1384 | if extra: |
| 1385 | print(f" extra sample: {extra[:12]}") |
| 1386 | else: |
| 1387 | print(f" [OK] top-level symbols match ({len(source_names)} names)") |
| 1388 | |
| 1389 | compile_errors: List[str] = [] |
| 1390 | for path in py_files: |
| 1391 | try: |
| 1392 | py_compile.compile(str(path), doraise=True) |
| 1393 | except py_compile.PyCompileError as exc: |
| 1394 | compile_errors.append(f"{path.relative_to(output_dir).as_posix()}: {exc.msg}") |
| 1395 | except Exception as exc: |
| 1396 | compile_errors.append(f"{path.relative_to(output_dir).as_posix()}: {exc}") |
| 1397 | if compile_errors: |
| 1398 | ok = False |
| 1399 | print(f" [FAIL] py_compile failed for {len(compile_errors)} files") |
| 1400 | print(f" first error: {compile_errors[0]}") |
| 1401 | else: |
| 1402 | print(f" [OK] py_compile passed for {len(py_files)} files") |
| 1403 | |
| 1404 | manifest = SplitManifest.load(output_dir) |
| 1405 | if manifest: |
| 1406 | expected_non_init = { |
| 1407 | entry.target_module |
| 1408 | for entry in manifest.nodes |
| 1409 | if entry.target_module not in {"_imports", "__init__.py"} |
| 1410 | and not entry.target_module.endswith("/__init__.py") |
| 1411 | } |
| 1412 | expected_non_init.add(CodeGenerator.SOURCE_BRIDGE_MODULE) |
| 1413 | stale_generated = [ |
| 1414 | path.relative_to(output_dir).as_posix() |
| 1415 | for path in py_files |
| 1416 | if path.name != "__init__.py" |
| 1417 | and path.relative_to(output_dir).as_posix() not in expected_non_init |
| 1418 | and _is_split_coder_generated_python_file(path) |
| 1419 | ] |
| 1420 | if stale_generated: |
| 1421 | ok = False |
no test coverage detected