This is an automated email from the ASF dual-hosted git repository.
potiuk pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/airflow.git
The following commit(s) were added to refs/heads/main by this push:
new 805a37cc03c Clarify template field validator guidance (#71210)
805a37cc03c is described below
commit 805a37cc03c5e4063f3c4f07d097eeae272b700d
Author: Mihir Duvedi <[email protected]>
AuthorDate: Fri Aug 7 22:20:15 2026 +0530
Clarify template field validator guidance (#71210)
The existing diagnostic obscures the documented provision-check exception
and has led contributors toward incorrect fixes. Surface the distinction at the
point of failure.
Co-authored-by: Mihir Duvedi
<[email protected]>
---
scripts/ci/prek/validate_operators_init.py | 8 +++++---
scripts/tests/ci/prek/test_validate_operators_init.py | 13 +++++++++++++
2 files changed, 18 insertions(+), 3 deletions(-)
diff --git a/scripts/ci/prek/validate_operators_init.py
b/scripts/ci/prek/validate_operators_init.py
index 664d3e4e471..86d25180508 100755
--- a/scripts/ci/prek/validate_operators_init.py
+++ b/scripts/ci/prek/validate_operators_init.py
@@ -403,9 +403,11 @@ def _check_constructor_field_logic(
if findings:
console.print(
f"{class_node.name}'s constructor applies logic to template
fields. Template fields "
- f"are rendered after the constructor runs, so validation or
transformation here acts "
- f"on the un-rendered Jinja expression and should move to execute()
"
- f"(see contributing-docs/05_pull_requests.rst):"
+ f"are rendered after the constructor runs, so value-dependent
validation or transformation "
+ f"here acts on the un-rendered Jinja expression and should run in
execute() or another method "
+ f"called after rendering. Checks that only ask whether an argument
was passed belong in "
+ f"__init__ and should use explicit is None / is not None
comparisons rather than truthiness. "
+ f"See
https://github.com/apache/airflow/issues/70296#false-positives"
)
for lineno in sorted(findings):
source = source_lines[lineno - 1].strip() if lineno <=
len(source_lines) else ""
diff --git a/scripts/tests/ci/prek/test_validate_operators_init.py
b/scripts/tests/ci/prek/test_validate_operators_init.py
index 4546a173ca7..45302516bb1 100644
--- a/scripts/tests/ci/prek/test_validate_operators_init.py
+++ b/scripts/tests/ci/prek/test_validate_operators_init.py
@@ -23,6 +23,7 @@ from pathlib import Path
import pytest
import validate_operators_init
+from rich.text import Text
from validate_operators_init import (
_check_constructor_field_logic,
_check_constructor_template_fields,
@@ -131,6 +132,18 @@ class TestConstructorFieldLogic:
def test_flags_logic_but_not_sanctioned_patterns(self, ctor_body: str,
expected: int):
assert _logic_findings(_operator_code(ctor_body), ["foo"]) == expected
+ def test_failure_message_explains_provision_check_exception(self):
+ code = _operator_code("self._validate(foo)\nself.foo = foo")
+ with validate_operators_init.console.capture() as capture:
+ assert _logic_findings(code, ["foo"]) == 1
+
+ output = " ".join(Text.from_ansi(capture.get()).plain.split())
+ assert "value-dependent validation or transformation" in output
+ assert "whether an argument was passed" in output
+ assert "is None" in output
+ assert "is not None" in output
+ assert
"https://github.com/apache/airflow/issues/70296#false-positives" in output
+
def test_name_in_parameter_default_is_not_the_field(self):
# A field named like a module (e.g. "conf") used in a parameter
default evaluates at
# class-definition scope and must not be flagged.