utils.annotations package¶
Submodules¶
utils.annotations.message module¶
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.
- utils.annotations.message.error(message: str, *, title: str | None = None, file: str | None = None, line: int | None = None, col: int | None = None) None¶
- utils.annotations.message.error_each(items: Iterable[str], *, title: str | None = None, file: str | None = None) None¶
Emit one error annotation per item in items.
- utils.annotations.message.in_github_actions() bool¶
- utils.annotations.message.notice(message: str, *, title: str | None = None, file: str | None = None, line: int | None = None, col: int | None = None) None¶
- utils.annotations.message.warning(message: str, *, title: str | None = None, file: str | None = None, line: int | None = None, col: int | None = None) None¶
- utils.annotations.message.warning_each(items: Iterable[str], *, title: str | None = None, file: str | None = None) None¶
Emit one warning annotation per item in items.
utils.annotations.summarize module¶
Parse GHA annotation lines from a log file and write a Markdown summary to $GITHUB_STEP_SUMMARY.
- Usage:
python3 -m utils.annotations.summarize <logfile> [<title>]
- Annotation format emitted by utils.annotations.message:
::warning title=<t>,file=<f>,line=<l>,col=<c>::<message> ::error title=<t>,…::<message> ::notice title=<t>,…::<message>
- class utils.annotations.summarize.Annotation(level: 'str', message: 'str', title: 'str | None' = None, file: 'str | None' = None, line: 'int | None' = None, col: 'int | None' = None)¶
Bases:
object- col: int | None = None¶
- file: str | None = None¶
- level: str¶
- line: int | None = None¶
- message: str¶
- title: str | None = None¶
- utils.annotations.summarize.main() None¶
- utils.annotations.summarize.parse_log(path: Path) list[Annotation]¶
- utils.annotations.summarize.parse_props(props_str: str) dict[str, str]¶
- utils.annotations.summarize.render_markdown(annotations: list[Annotation], title: str) str¶
utils.annotations.suppress module¶
Unified suppression markers for infinito-nexus tests.
A single grammar covers every per-line, per-block, and per-file
opt-out across the test suite. See
docs/contributing/actions/testing/suppression.md for the catalog
of valid rule keys, their placement, and the test that consumes each.
Grammar¶
<comment-prefix> (noqa|nocheck): <rule>(s*,s*<rule>)*
Accepted comment prefixes:
#(Python, YAML, shell, conf, INI, …)//(JS, JSONC, …){# … #}(Jinja2)<!-- … -->(HTML, Markdown)
noqa and nocheck are accepted as synonyms (case-insensitive)
by this parser. By repo convention every project rule from
docs/contributing/actions/testing/suppression.md MUST use
nocheck: because # noqa: <code> is also parsed by ruff as a
flake8 directive and triggers invalid-noqa-code warnings on every
project-specific rule key. noqa: is reserved for real flake8 / ruff
codes (E402, F401, …). The direct-yaml lint enforces the
convention by hard-rejecting the noqa: form.
Multiple rules may be combined on one comment, comma-separated:
# nocheck: shared, email # nocheck: url, docker-version
Position semantics are per-rule and listed in the catalog page. This module exposes three resolvers:
is_suppressed_at(): same line, line above, or either.is_suppressed_in_head(): anywhere in the first N lines (file-level opt-outs).is_suppressed_anywhere(): anywhere in the file (used by the run-once schema check).
- utils.annotations.suppress.is_suppressed_anywhere(lines: Sequence[str], rule: str) bool¶
Return True iff rule appears on any line of lines.
Used for whole-file opt-outs whose semantics legitimately apply to the entire file regardless of where the marker sits (e.g.
run-onceon roles that are intentionally executed every play).
- utils.annotations.suppress.is_suppressed_at(lines: Sequence[str], line_no: int, rule: str, *, mode: str = 'same-or-above') bool¶
Check whether the construct at 1-based line_no is suppressed.
modeselects placement semantics:"same-line": marker must be on the construct’s line."line-above": marker must be on the immediately preceding non-empty line. Blank lines between marker and construct break the association."same-or-above"(default): either of the above.
- utils.annotations.suppress.is_suppressed_for_key(lines: Sequence[str], key: str, rule: str) bool¶
Whether the mapping line declaring key carries a marker for rule.
- Parameters:
lines – the file’s lines.
key – the YAML key, matched as
<key>:after stripping indentation.rule – the rule to look for.
- utils.annotations.suppress.is_suppressed_in_head(lines: Sequence[str], rule: str, *, scan_lines: int = 30) bool¶
Return True iff rule appears in the first scan_lines lines.
File-level opt-outs (e.g.
file-size) MUST live near the top of the file so the cost of carrying the exemption stays visible.
- utils.annotations.suppress.line_has_rule(line: str, rule: str) bool¶
Return True iff line carries a marker for rule.
- utils.annotations.suppress.suppressed_line_numbers(lines: Sequence[str], rule: str) set[int]¶
Return the set of 1-based line numbers carrying a marker for rule.
utils.annotations.task_gate module¶
Detect Ansible task-level when: gates that exempt compose-only shell calls from swarm-compat lints.
A task whose when: evaluates compose-only at deploy time (e.g. when: DEPLOYMENT_MODE != ‘swarm’ or when: DEPLOYMENT_MODE == ‘compose’) never reaches a swarm host, so compose <verb> / chdir: directories.instance inside that task body is legitimate. This module gives the lints a heuristic to recognise that gate without parsing YAML.
The detection is line-based: the task block is the span between the nearest - name: (at indent N) above the offending line and the next top-level structural marker (- name: at indent <= N, or end of file). Within that span we look for any when: clause whose textual value matches a compose-only DEPLOYMENT_MODE expression.
- utils.annotations.task_gate.is_file_compose_only_by_header(lines: Sequence[str]) bool¶
True iff the file declares a top-of-file marker
# include-gated: when: DEPLOYMENT_MODE != “swarm”
that documents the entire file is meant to be include_tasks: from a parent that already carries the compose-only when: guard. The marker MUST sit in the first 5 lines.
- utils.annotations.task_gate.is_task_compose_only_gated(lines: Sequence[str], idx: int) bool¶
True iff the task body containing idx — or any enclosing parent - block: / - name: block — carries a when: clause whose textual expression evaluates compose-only at deploy time. Walks upwards to recognise block-level gates: a single when: DEPLOYMENT_MODE != ‘swarm’ on a parent - block: covers every child task underneath.