chore(api): Suppress unknown contract checks by default (#36969)

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
This commit is contained in:
chariri 2026-06-09 17:32:34 +09:00 committed by GitHub
parent 0019e6a6f3
commit c88a38b8b5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 45 additions and 6 deletions

View File

@ -7,9 +7,9 @@ or ``Model.model_validate(...).model_dump()`` return.
Raw dictionaries, raw lists, ``None`` responses, streaming helpers, missing
response schemas, and returns with non-literal status codes are classified as
unknown so reviewers can triage them without blocking unrelated work. The one
intentional non-schema mismatch is a known body/schema on a no-body status such
as 204, 205, or 304.
unknown. Unknown details are hidden by default to keep routine output focused;
pass ``--include-unknown`` when triaging them. The one intentional non-schema
mismatch is a known body/schema on a no-body status such as 204, 205, or 304.
"""
from __future__ import annotations
@ -589,7 +589,7 @@ def as_jsonable(check: ContractCheck) -> dict[str, Any]:
return data
def print_text_report(checks: Sequence[ContractCheck], *, include_valid: bool) -> None:
def print_text_report(checks: Sequence[ContractCheck], *, include_unknown: bool, include_valid: bool) -> None:
counts = Counter(check.classification for check in checks)
sys.stdout.write(
"Response contract lint: "
@ -601,6 +601,8 @@ def print_text_report(checks: Sequence[ContractCheck], *, include_valid: bool) -
for classification in ("mismatch", "refactorable", "unknown", "valid"):
filtered = [check for check in checks if check.classification == classification]
if classification == "unknown" and not include_unknown:
continue
if classification == "valid" and not include_valid:
continue
if not filtered:
@ -619,6 +621,7 @@ def parse_args() -> argparse.Namespace:
nargs="*",
help="Files or directories to lint. Defaults to Flask controller directories.",
)
parser.add_argument("--include-unknown", action="store_true", help="Print unknown route methods in output.")
parser.add_argument("--include-valid", action="store_true", help="Print valid route methods in text output.")
parser.add_argument("--json", action="store_true", help="Emit machine-readable JSON.")
parser.add_argument(
@ -650,10 +653,16 @@ def main() -> int:
if args.json:
grouped = defaultdict(list)
for check in checks:
if check.classification == "unknown" and not args.include_unknown:
continue
grouped[check.classification].append(as_jsonable(check))
sys.stdout.write(f"{json.dumps(grouped, indent=2, sort_keys=True)}\n")
else:
print_text_report(checks, include_valid=bool(args.include_valid))
print_text_report(
checks,
include_unknown=bool(args.include_unknown),
include_valid=bool(args.include_valid),
)
has_mismatch = any(check.classification == "mismatch" for check in checks)
has_unknown = any(check.classification == "unknown" for check in checks)

View File

@ -2,6 +2,8 @@ import importlib.util
import sys
from pathlib import Path
import pytest
def _load_lint_response_contracts_module():
api_dir = Path(__file__).parents[3]
@ -115,7 +117,7 @@ class StreamApi(Resource):
assert {actual.model for actual in checks[0].actual} == {"StreamResponse"}
def test_main_is_report_only_by_default_for_mismatches(tmp_path: Path, monkeypatch):
def test_main_is_report_only_by_default_for_mismatches(tmp_path: Path, monkeypatch: pytest.MonkeyPatch):
module = _load_lint_response_contracts_module()
controller_path = tmp_path / "controllers" / "sample.py"
controller_path.parent.mkdir()
@ -137,6 +139,34 @@ class BadDeleteApi(Resource):
assert module.main() == 1
def test_main_hides_unknown_details_by_default(tmp_path: Path, monkeypatch: pytest.MonkeyPatch, capsys):
module = _load_lint_response_contracts_module()
controller_path = tmp_path / "controllers" / "sample.py"
controller_path.parent.mkdir()
controller_path.write_text(
"""
@ns.route("/items")
class ItemApi(Resource):
@ns.response(200, "OK", ns.models[ItemResponse.__name__])
def get(self):
return dump_response(ItemResponse, item), status_code
""",
encoding="utf-8",
)
monkeypatch.setattr(sys, "argv", ["lint_response_contracts.py", str(controller_path)])
assert module.main() == 0
default_output = capsys.readouterr().out
assert "1 unknown" in default_output
assert "UNKNOWN:" not in default_output
monkeypatch.setattr(sys, "argv", ["lint_response_contracts.py", "--include-unknown", str(controller_path)])
assert module.main() == 0
include_unknown_output = capsys.readouterr().out
assert "UNKNOWN:" in include_unknown_output
assert "non-literal or unsupported status" in include_unknown_output
def test_class_level_route_and_response_docs_apply_to_methods(tmp_path: Path):
checks = _checks_for_source(
tmp_path,