ci: replace custom noqa markers with guard ignores (#39679)

This commit is contained in:
Escape0707 2026-07-28 17:52:12 +09:00 committed by GitHub
parent ae0b66311d
commit 04158ac8ea
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
16 changed files with 63 additions and 27 deletions

View File

@ -52,7 +52,7 @@ def with_session[T, **P, R](
session.commit()
return result
except Exception:
session.rollback() # noqa: no-new-controller-sqlalchemy decorator owns transaction rollback
session.rollback() # guard-ignore: no-new-controller-sqlalchemy -- decorator owns rollback
raise
with session_factory.create_session() as session:

View File

@ -317,7 +317,7 @@ class _WorkflowResponseSource:
self._session = session
def __getattr__(self, name: str) -> object:
return getattr(self._workflow, name) # noqa: no-new-getattr response adapter delegates model fields
return getattr(self._workflow, name) # guard-ignore: no-new-getattr -- delegates model fields
@property
def created_by_account(self) -> Account | None:

View File

@ -218,7 +218,7 @@ class _DatasetQueryResponseSource:
return self.query.get_queries(session=self.session)
def __getattr__(self, name: str) -> Any:
return getattr(self.query, name) # noqa: no-new-getattr response adapter delegates model fields
return getattr(self.query, name) # guard-ignore: no-new-getattr -- delegates model fields
class DatasetQueryListResponse(ResponseModel):
@ -257,7 +257,7 @@ class _RelatedAppResponseSource:
return self.app.mode_compatible_with_agent_with_session(session=self.session)
def __getattr__(self, name: str) -> Any:
return getattr(self.app, name) # noqa: no-new-getattr response adapter delegates model fields
return getattr(self.app, name) # guard-ignore: no-new-getattr -- delegates model fields
class RelatedAppListResponse(ResponseModel):

View File

@ -106,7 +106,7 @@ class ExternalKnowledgeApiResponseSource:
return self.external_knowledge_api.get_dataset_bindings(session=self.session)
def __getattr__(self, name: str) -> Any:
return getattr(self.external_knowledge_api, name) # noqa: no-new-getattr response adapter delegates model fields
return getattr(self.external_knowledge_api, name) # guard-ignore: no-new-getattr -- delegates model fields
def external_knowledge_api_response(

View File

@ -404,7 +404,7 @@ class TrialWorkflowResponseSource:
return self.workflow.get_tool_published(session=self.session)
def __getattr__(self, name: str) -> Any:
return getattr(self.workflow, name) # noqa: no-new-getattr response adapter delegates model fields
return getattr(self.workflow, name) # guard-ignore: no-new-getattr -- delegates model fields
register_schema_models(

View File

@ -34,7 +34,7 @@ class _SessionResponseSource[SourceT]:
self._session = session
def __getattr__(self, name: str) -> object:
return getattr(self._source, name) # noqa: no-new-getattr response adapter delegates model fields
return getattr(self._source, name) # guard-ignore: no-new-getattr -- delegates model fields
class _FeedbackResponseSource(_SessionResponseSource[MessageFeedback]):

View File

@ -227,7 +227,7 @@ class DatasetDetailResponseSource:
return self.dataset.get_total_available_documents(session=self.session)
def __getattr__(self, name: str) -> Any:
return getattr(self.dataset, name) # noqa: no-new-getattr response adapter delegates model fields
return getattr(self.dataset, name) # guard-ignore: no-new-getattr -- delegates model fields
def dataset_detail_response_source(dataset: Any, *, session: Session) -> DatasetDetailResponseSource:

View File

@ -90,7 +90,7 @@ class DocumentWithSession:
return self.document.get_doc_metadata_details(session=self.session)
def __getattr__(self, name: str) -> Any:
return getattr(self.document, name) # noqa: no-new-getattr response adapter delegates model fields
return getattr(self.document, name) # guard-ignore: no-new-getattr -- delegates model fields
def document_response(document: Document, *, session: Session) -> DocumentResponse:

View File

@ -114,7 +114,7 @@ class AppModelConfigResponseView:
self._session = session
def __getattr__(self, name: str) -> Any:
return getattr(self._app_model_config, name) # noqa: no-new-getattr response adapter delegates model fields
return getattr(self._app_model_config, name) # guard-ignore: no-new-getattr -- delegates model fields
@property
def annotation_reply_dict(self) -> Any:
@ -129,7 +129,7 @@ class AppResponseView:
self._session = session
def __getattr__(self, name: str) -> Any:
return getattr(self._app, name) # noqa: no-new-getattr response adapter delegates model fields
return getattr(self._app, name) # guard-ignore: no-new-getattr -- delegates model fields
@property
def desc_or_prompt(self) -> str:

View File

@ -85,6 +85,42 @@ def main_branch_rev(repo: Path) -> str:
return git(repo, "rev-parse", "main")
@pytest.mark.parametrize(
("source_line", "rule_id"),
[
(
"value = getattr(module, name) # guard-ignore: no-new-getattr -- lazy export proxy",
"no-new-getattr",
),
(
"session.rollback() # guard-ignore: no-new-controller-sqlalchemy -- decorator owns rollback",
"no-new-controller-sqlalchemy",
),
],
)
def test_has_reasoned_guard_ignore_accepts_custom_rules(source_line: str, rule_id: str) -> None:
module = load_guard_module()
assert module.has_reasoned_guard_ignore(source_line, rule_id)
@pytest.mark.parametrize(
("source_line", "rule_id"),
[
("value = getattr(module, name) # noqa: no-new-getattr legacy marker", "no-new-getattr"),
("value = getattr(module, name) # guard-ignore: no-new-getattr", "no-new-getattr"),
(
"value = getattr(module, name) # guard-ignore: another-rule -- wrong rule",
"no-new-getattr",
),
],
)
def test_has_reasoned_guard_ignore_rejects_invalid_markers(source_line: str, rule_id: str) -> None:
module = load_guard_module()
assert not module.has_reasoned_guard_ignore(source_line, rule_id)
def test_resolve_ast_grep_command_prefers_ast_grep(monkeypatch: pytest.MonkeyPatch) -> None:
module = load_guard_module()
monkeypatch.setattr(
@ -776,7 +812,7 @@ def test_modified_hunk_with_increased_getattr_count_fails(tmp_path: Path) -> Non
assert "net-new getattr" in result.stderr
def test_inline_noqa_suppression_with_explanatory_text_skips_added_getattr(tmp_path: Path) -> None:
def test_inline_guard_ignore_with_explanatory_text_skips_added_getattr(tmp_path: Path) -> None:
init_repo(tmp_path)
write_repo_file(
tmp_path,
@ -795,20 +831,20 @@ def test_inline_noqa_suppression_with_explanatory_text_skips_added_getattr(tmp_p
"pkg/existing.py",
"""
def read_value(obj):
return getattr(obj, "dynamic_name", None) # noqa: no-new-getattr needed for plugin-defined attributes
return getattr(obj, "dynamic_name", None) # guard-ignore: no-new-getattr -- plugin-defined attributes
""",
)
commit_all(tmp_path, "add suppressed getattr")
result = run_script(tmp_path, "--base-rev", base_rev)
assert "no-new-getattr needed for plugin-defined attributes" in (tmp_path / "pkg/existing.py").read_text(
assert "guard-ignore: no-new-getattr -- plugin-defined attributes" in (tmp_path / "pkg/existing.py").read_text(
encoding="utf-8"
)
assert result.returncode == 0, stderr_lines(result)
def test_inline_noqa_without_explanatory_text_is_not_sufficient(tmp_path: Path) -> None:
def test_inline_guard_ignore_without_explanatory_text_is_not_sufficient(tmp_path: Path) -> None:
init_repo(tmp_path)
write_repo_file(
tmp_path,
@ -827,10 +863,10 @@ def test_inline_noqa_without_explanatory_text_is_not_sufficient(tmp_path: Path)
"pkg/existing.py",
"""
def read_value(obj):
return getattr(obj, "dynamic_name", None) # noqa: no-new-getattr
return getattr(obj, "dynamic_name", None) # guard-ignore: no-new-getattr
""",
)
commit_all(tmp_path, "add bare noqa getattr")
commit_all(tmp_path, "add bare guard ignore getattr")
result = run_script(tmp_path, "--base-rev", base_rev)

View File

@ -115,7 +115,7 @@ def __getattr__(name: str) -> Any:
if name not in _EXPORTS:
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
module = import_module(_EXPORTS[name])
value = getattr(module, name) # noqa: no-new-getattr lazy export proxy
value = getattr(module, name) # guard-ignore: no-new-getattr -- lazy export proxy
globals()[name] = value
return value

View File

@ -173,7 +173,7 @@ def __getattr__(name: str) -> Any:
if name not in _EXPORTS:
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
module = import_module(_EXPORTS[name])
value = getattr(module, name) # noqa: no-new-getattr lazy export proxy
value = getattr(module, name) # guard-ignore: no-new-getattr -- lazy export proxy
globals()[name] = value
return value

View File

@ -245,13 +245,13 @@ def extract_meta_variables(raw_match: dict[str, Any]) -> dict[str, str]:
return result
def has_reasoned_noqa(source_line: str, rule_id: str) -> bool:
pattern = re.compile(rf"# noqa: {re.escape(rule_id)}(?:\s+(?P<reason>\S.*))?\s*$")
def has_reasoned_guard_ignore(source_line: str, rule_id: str) -> bool:
pattern = re.compile(rf"# guard-ignore: {re.escape(rule_id)} -- (?P<reason>\S.*)\s*$")
match = pattern.search(source_line)
if not match:
return False
reason = match.group("reason")
return reason is not None and bool(reason.strip())
return bool(reason.strip())
def collect_hunk_violations(

View File

@ -10,7 +10,7 @@ from __future__ import annotations
import re
from pathlib import Path
from ast_grep_guard import Match, has_reasoned_noqa, rule_path, run_guard
from ast_grep_guard import Match, has_reasoned_guard_ignore, rule_path, run_guard
RULE_ID = "no-new-controller-sqlalchemy"
@ -40,7 +40,7 @@ def is_flask_session_get(match: Match) -> bool:
def is_suppressed(match: Match) -> bool:
return has_reasoned_noqa(match.source_line, RULE_ID)
return has_reasoned_guard_ignore(match.source_line, RULE_ID)
def is_reportable_match(match: Match) -> bool:

View File

@ -3,7 +3,7 @@
from __future__ import annotations
from ast_grep_guard import Match, has_reasoned_noqa, is_python_source_path, rule_path, run_guard
from ast_grep_guard import Match, has_reasoned_guard_ignore, is_python_source_path, rule_path, run_guard
RULE_ID = "no-new-getattr"
@ -12,7 +12,7 @@ VIOLATION_MESSAGE = "no-new-getattr net-new getattr() in added code"
def is_reportable_match(match: Match) -> bool:
return not has_reasoned_noqa(match.source_line, RULE_ID)
return not has_reasoned_guard_ignore(match.source_line, RULE_ID)
def main() -> int:

View File

@ -44,7 +44,7 @@ def parse_args() -> argparse.Namespace:
help="Files or directories to scan. Defaults to api/controllers.",
)
parser.add_argument("--include-allowed", action="store_true", help="Print allowed flush()/commit() findings.")
parser.add_argument("--include-suppressed", action="store_true", help="Print reasoned noqa suppressions.")
parser.add_argument("--include-suppressed", action="store_true", help="Print reasoned guard suppressions.")
parser.add_argument("--summary-only", action="store_true", help="Print counts without per-finding details.")
parser.add_argument("--json", action="store_true", help="Emit machine-readable JSON.")
parser.add_argument(