Files
computer-playbook/utils/annotations/message.py
T
kevinveenbirkenbachandClaude Opus 4.7 7dc6d6e491 chore(ruff): enable TC + N rules
TC (flake8-type-checking): typing-only imports moved into
``if TYPE_CHECKING:`` blocks across 8 source files via auto-fix
(117 hits). Reduces runtime import surface and rules out circular
imports for type-only deps. Project-wide ``from __future__ import
annotations`` (FA, already enabled) keeps annotations as strings,
so the moved imports never need to load at runtime.

N (pep8-naming): 32 hits handled.

Renames (semantic + lint):
- ``isSmallerThenMaximumBackupSize`` -> ``is_larger_than_maximum_backup_size``
  (snake_case + fixes both the typo "Then -> Than" and the
  semantic mismatch: the function returns ``current > maximum``)
- ``isDirectoryDeletable`` / ``deleteVersion`` /
  ``getAmountOfIteration`` / ``deleteIteration`` -> snake_case
- 8 test functions with ALL-CAPS env-var fragments
  (``test_..._DOMAIN_PRIMARY_...`` etc.) -> snake_case
- Mock alias ``VH`` -> ``vh`` in test_host_vars.py

Exception class renames (``N818`` Error suffix):
- ``TimeLimitExceededException`` -> ``TimeLimitExceededError``
- ``AttemptException`` -> ``AttemptError``
- ``PathNotFound`` -> ``PathNotFoundError`` (propagated across
  4 callers under tests/integration/lookups/config/)
- ``_ExitJson`` / ``_FailJson`` -> ``*_Error``

Refactor:
- ``METADATA = {...}`` local constant in two functions of
  ``plugins/filter/invokable_paths.py`` lifted to module-level
  ``_METADATA_KEYS``.
- ``from typing import TYPE_CHECKING`` in
  ``tasks/utils/migrate_meta_layout/__main__.py`` moved above
  the ``sys.path.insert`` hack to satisfy E402 (collateral from
  the TC auto-fix that landed it after the path mutation).

Per-file ignores in pyproject.toml for the legitimate cases the
rule cannot accept:
- ``pull-specific-host.py`` -> ``N999`` (Ansible-invoked role-level
  script, hyphenated filename pinned by the surrounding stack).
- 4 matrix-deploy files -> ``N806`` for the ``include_R`` /
  ``raw_include_R`` round-index notation; the uppercase ``R``
  here mirrors the formula in the docstring rather than naming
  a constant.
- ``plugins/filter/value_generator.py`` -> ``N806`` for the
  lazy-loaded class binding ``ValueGenerator =
  _value_generator_cls()``, which retains the imported class
  identifier per PEP 8.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 18:57:07 +02:00

104 lines
2.5 KiB
Python

"""GitHub Actions workflow command annotations.
Outputs structured log messages that GitHub Actions renders as annotations
(warning triangles, error markers, etc.) in the job summary and log view.
See: https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/workflow-commands-for-github-actions#setting-a-warning-message
"""
from __future__ import annotations
import os
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from collections.abc import Iterable
def in_github_actions() -> bool:
return os.environ.get("GITHUB_ACTIONS") == "true"
def _emit(
level: str,
message: str,
*,
title: str | None = None,
file: str | None = None,
line: int | None = None,
col: int | None = None,
) -> None:
if in_github_actions():
parts = []
if title:
parts.append(f"title={title}")
if file:
parts.append(f"file={file}")
if line is not None:
parts.append(f"line={line}")
if col is not None:
parts.append(f"col={col}")
props = ",".join(parts)
prefix = f"::{level} {props}::" if props else f"::{level}::"
print(f"{prefix}{message}", flush=True)
else:
label = f"[{level.upper()}]"
if title:
label += f" {title}"
print(f"{label}: {message}", flush=True)
def warning(
message: str,
*,
title: str | None = None,
file: str | None = None,
line: int | None = None,
col: int | None = None,
) -> None:
_emit("warning", message, title=title, file=file, line=line, col=col)
def error(
message: str,
*,
title: str | None = None,
file: str | None = None,
line: int | None = None,
col: int | None = None,
) -> None:
_emit("error", message, title=title, file=file, line=line, col=col)
def notice(
message: str,
*,
title: str | None = None,
file: str | None = None,
line: int | None = None,
col: int | None = None,
) -> None:
_emit("notice", message, title=title, file=file, line=line, col=col)
def warning_each(
items: Iterable[str],
*,
title: str | None = None,
file: str | None = None,
) -> None:
"""Emit one warning annotation per item in *items*."""
for item in items:
warning(item, title=title, file=file)
def error_each(
items: Iterable[str],
*,
title: str | None = None,
file: str | None = None,
) -> None:
"""Emit one error annotation per item in *items*."""
for item in items:
error(item, title=title, file=file)