This is an automated email from the ASF dual-hosted git repository.
uranusjr 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 fce3a58334 Implement context accessor for DatasetEvent extra (#38481)
fce3a58334 is described below
commit fce3a583348162f655282d032eca654dcb67b497
Author: Tzu-ping Chung <[email protected]>
AuthorDate: Fri Mar 29 10:06:02 2024 +0800
Implement context accessor for DatasetEvent extra (#38481)
---
.pre-commit-config.yaml | 5 +
airflow/datasets/__init__.py | 11 +-
airflow/models/taskinstance.py | 14 ++-
airflow/utils/context.py | 34 ++++++
airflow/utils/context.pyi | 12 ++-
contributing-docs/08_static_code_checks.rst | 2 +
dev/breeze/doc/images/output_static-checks.svg | 114 +++++++++++----------
dev/breeze/doc/images/output_static-checks.txt | 2 +-
dev/breeze/src/airflow_breeze/pre_commit_ids.py | 1 +
.../authoring-and-scheduling/datasets.rst | 31 +++++-
docs/apache-airflow/templates-ref.rst | 2 +
.../pre_commit_template_context_key_sync.py | 0
tests/models/test_taskinstance.py | 67 +++++++++++-
tests/operators/test_python.py | 1 +
14 files changed, 228 insertions(+), 68 deletions(-)
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 1829f9b200..2f347b1c88 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -629,6 +629,11 @@ repos:
entry: ./scripts/ci/pre_commit/pre_commit_sync_init_decorator.py
pass_filenames: false
files:
^airflow/models/dag\.py$|^airflow/(?:decorators|utils)/task_group\.py$
+ - id: check-template-context-variable-in-sync
+ name: Check all template context variable references are in sync
+ language: python
+ entry: ./scripts/ci/pre_commit/pre_commit_template_context_key_sync.py
+ files:
^airflow/models/taskinstance\.py$|^airflow/utils/context\.pyi?$|^docs/apache-airflow/templates-ref\.rst$
- id: check-base-operator-usage
language: pygrep
name: Check BaseOperator core imports
diff --git a/airflow/datasets/__init__.py b/airflow/datasets/__init__.py
index 2507c69d01..d20d3b578e 100644
--- a/airflow/datasets/__init__.py
+++ b/airflow/datasets/__init__.py
@@ -42,7 +42,14 @@ def _get_uri_normalizer(scheme: str) ->
Callable[[SplitResult], SplitResult] | N
return ProvidersManager().dataset_uri_handlers.get(scheme)
-def _sanitize_uri(uri: str) -> str:
+def sanitize_uri(uri: str) -> str:
+ """Sanitize a dataset URI.
+
+ This checks for URI validity, and normalizes the URI if needed. A fully
+ normalized URI is returned.
+
+ :meta private:
+ """
if not uri:
raise ValueError("Dataset URI cannot be empty")
if uri.isspace():
@@ -110,7 +117,7 @@ class Dataset(os.PathLike, BaseDatasetEventInput):
"""A representation of data dependencies between workflows."""
uri: str = attr.field(
- converter=_sanitize_uri,
+ converter=sanitize_uri,
validator=[attr.validators.min_len(1), attr.validators.max_len(3000)],
)
extra: dict[str, Any] | None = None
diff --git a/airflow/models/taskinstance.py b/airflow/models/taskinstance.py
index 8bb9947327..c9bd2ce617 100644
--- a/airflow/models/taskinstance.py
+++ b/airflow/models/taskinstance.py
@@ -104,7 +104,13 @@ from airflow.templates import SandboxedEnvironment
from airflow.ti_deps.dep_context import DepContext
from airflow.ti_deps.dependencies_deps import REQUEUEABLE_DEPS, RUNNING_DEPS
from airflow.utils import timezone
-from airflow.utils.context import ConnectionAccessor, Context,
VariableAccessor, context_merge
+from airflow.utils.context import (
+ ConnectionAccessor,
+ Context,
+ DatasetEventAccessors,
+ VariableAccessor,
+ context_merge,
+)
from airflow.utils.email import send_email
from airflow.utils.helpers import prune_dict, render_template_to_string
from airflow.utils.log.logging_mixin import LoggingMixin
@@ -766,6 +772,7 @@ def _get_template_context(
"dag_run": dag_run,
"data_interval_end": timezone.coerce_datetime(data_interval.end),
"data_interval_start": timezone.coerce_datetime(data_interval.start),
+ "dataset_events": DatasetEventAccessors(),
"ds": ds,
"ds_nodash": ds_nodash,
"execution_date": logical_date,
@@ -2569,7 +2576,7 @@ class TaskInstance(Base, LoggingMixin):
session.add(Log(self.state, self))
session.merge(self).task = self.task
if self.state == TaskInstanceState.SUCCESS:
- self._register_dataset_changes(session=session)
+
self._register_dataset_changes(events=context["dataset_events"],
session=session)
session.commit()
if self.state == TaskInstanceState.SUCCESS:
@@ -2579,7 +2586,7 @@ class TaskInstance(Base, LoggingMixin):
return None
- def _register_dataset_changes(self, *, session: Session) -> None:
+ def _register_dataset_changes(self, *, events: DatasetEventAccessors,
session: Session) -> None:
if TYPE_CHECKING:
assert self.task
@@ -2590,6 +2597,7 @@ class TaskInstance(Base, LoggingMixin):
dataset_manager.register_dataset_change(
task_instance=self,
dataset=obj,
+ extra=events[obj].extra,
session=session,
)
diff --git a/airflow/utils/context.py b/airflow/utils/context.py
index 3501ca7dbc..033b7aa39d 100644
--- a/airflow/utils/context.py
+++ b/airflow/utils/context.py
@@ -36,8 +36,10 @@ from typing import (
ValuesView,
)
+import attrs
import lazy_object_proxy
+from airflow.datasets import Dataset, sanitize_uri
from airflow.exceptions import RemovedInAirflow3Warning
from airflow.utils.types import NOTSET
@@ -54,6 +56,7 @@ KNOWN_CONTEXT_KEYS: set[str] = {
"dag_run",
"data_interval_end",
"data_interval_start",
+ "dataset_events",
"ds",
"ds_nodash",
"execution_date",
@@ -146,6 +149,37 @@ class ConnectionAccessor:
return default_conn
[email protected]()
+class DatasetEventAccessor:
+ """Wrapper to access a DatasetEvent instance in template."""
+
+ extra: dict[str, Any]
+
+
+class DatasetEventAccessors(Mapping[str, DatasetEventAccessor]):
+ """Lazy mapping of dataset event accessors."""
+
+ def __init__(self) -> None:
+ self._dict: dict[str, DatasetEventAccessor] = {}
+
+ def __iter__(self) -> Iterator[str]:
+ return iter(self._dict)
+
+ def __len__(self) -> int:
+ return len(self._dict)
+
+ def __getitem__(self, key: str | Dataset) -> DatasetEventAccessor:
+ if isinstance(key, str):
+ uri = sanitize_uri(key)
+ elif isinstance(key, Dataset):
+ uri = key.uri
+ else:
+ return NotImplemented
+ if uri not in self._dict:
+ self._dict[uri] = DatasetEventAccessor({})
+ return self._dict[uri]
+
+
class AirflowContextDeprecationWarning(RemovedInAirflow3Warning):
"""Warn for usage of deprecated context variables in a task."""
diff --git a/airflow/utils/context.pyi b/airflow/utils/context.pyi
index eb08201248..8b5deb4746 100644
--- a/airflow/utils/context.pyi
+++ b/airflow/utils/context.pyi
@@ -26,11 +26,12 @@
# declare "these are defined, but don't error if others are accessed" someday.
from __future__ import annotations
-from typing import Any, Collection, Container, Iterable, Mapping, overload
+from typing import Any, Collection, Container, Iterable, Iterator, Mapping,
overload
from pendulum import DateTime
from airflow.configuration import AirflowConfigParser
+from airflow.datasets import Dataset
from airflow.models.baseoperator import BaseOperator
from airflow.models.dag import DAG
from airflow.models.dagrun import DagRun
@@ -55,6 +56,14 @@ class VariableAccessor:
class ConnectionAccessor:
def get(self, key: str, default_conn: Any = None) -> Any: ...
+class DatasetEventAccessor:
+ extra: dict[str, Any]
+
+class DatasetEventAccessors(Mapping[str, DatasetEventAccessor]):
+ def __iter__(self) -> Iterator[str]: ...
+ def __len__(self) -> int: ...
+ def __getitem__(self, key: str | Dataset) -> DatasetEventAccessor: ...
+
# NOTE: Please keep this in sync with the following:
# * KNOWN_CONTEXT_KEYS in airflow/utils/context.py
# * Table in docs/apache-airflow/templates-ref.rst
@@ -65,6 +74,7 @@ class Context(TypedDict, total=False):
dag_run: DagRun | DagRunPydantic
data_interval_end: DateTime
data_interval_start: DateTime
+ dataset_events: DatasetEventAccessors
ds: str
ds_nodash: str
exception: BaseException | str | None
diff --git a/contributing-docs/08_static_code_checks.rst
b/contributing-docs/08_static_code_checks.rst
index 0b331bf3e9..c7be51b6a7 100644
--- a/contributing-docs/08_static_code_checks.rst
+++ b/contributing-docs/08_static_code_checks.rst
@@ -222,6 +222,8 @@ require Breeze Docker image to be built locally.
+-----------------------------------------------------------+--------------------------------------------------------------+---------+
| check-system-tests-tocs | Check that
system tests is properly added | |
+-----------------------------------------------------------+--------------------------------------------------------------+---------+
+| check-template-context-variable-in-sync | Check all
template context variable references are in sync | |
++-----------------------------------------------------------+--------------------------------------------------------------+---------+
| check-tests-in-the-right-folders | Check if tests
are in the right folders | |
+-----------------------------------------------------------+--------------------------------------------------------------+---------+
| check-tests-unittest-testcase | Check that unit
tests do not inherit from unittest.TestCase | |
diff --git a/dev/breeze/doc/images/output_static-checks.svg
b/dev/breeze/doc/images/output_static-checks.svg
index 679db3dfee..a709f8070c 100644
--- a/dev/breeze/doc/images/output_static-checks.svg
+++ b/dev/breeze/doc/images/output_static-checks.svg
@@ -1,4 +1,4 @@
-<svg class="rich-terminal" viewBox="0 0 1482 2099.6"
xmlns="http://www.w3.org/2000/svg">
+<svg class="rich-terminal" viewBox="0 0 1482 2124.0"
xmlns="http://www.w3.org/2000/svg">
<!-- Generated with Rich https://www.textualize.io -->
<style>
@@ -43,7 +43,7 @@
<defs>
<clipPath id="breeze-static-checks-clip-terminal">
- <rect x="0" y="0" width="1463.0" height="2048.6" />
+ <rect x="0" y="0" width="1463.0" height="2073.0" />
</clipPath>
<clipPath id="breeze-static-checks-line-0">
<rect x="0" y="1.5" width="1464" height="24.65"/>
@@ -294,9 +294,12 @@
<clipPath id="breeze-static-checks-line-82">
<rect x="0" y="2002.3" width="1464" height="24.65"/>
</clipPath>
+<clipPath id="breeze-static-checks-line-83">
+ <rect x="0" y="2026.7" width="1464" height="24.65"/>
+ </clipPath>
</defs>
- <rect fill="#292929" stroke="rgba(255,255,255,0.35)" stroke-width="1"
x="1" y="1" width="1480" height="2097.6" rx="8"/><text
class="breeze-static-checks-title" fill="#c5c8c6" text-anchor="middle" x="740"
y="27">Command: static-checks</text>
+ <rect fill="#292929" stroke="rgba(255,255,255,0.35)" stroke-width="1"
x="1" y="1" width="1480" height="2122" rx="8"/><text
class="breeze-static-checks-title" fill="#c5c8c6" text-anchor="middle" x="740"
y="27">Command: static-checks</text>
<g transform="translate(26,22)">
<circle cx="0" cy="0" r="7" fill="#ff5f57"/>
<circle cx="22" cy="0" r="7" fill="#febc2e"/>
@@ -338,58 +341,59 @@
</text><text class="breeze-static-checks-r5" x="0" y="727.6" textLength="12.2"
clip-path="url(#breeze-static-checks-line-29)">│</text><text
class="breeze-static-checks-r7" x="451.4" y="727.6" textLength="988.2"
clip-path="url(#breeze-static-checks-line-29)">check-pydevd-left-in-code | check-revision-heads-map |                           </t
[...]
</text><text class="breeze-static-checks-r5" x="0" y="752" textLength="12.2"
clip-path="url(#breeze-static-checks-line-30)">│</text><text
class="breeze-static-checks-r7" x="451.4" y="752" textLength="988.2"
clip-path="url(#breeze-static-checks-line-30)">check-safe-filter-usage-in-html | check-sql-dependency-common-data-structure |   </text><text
class="breeze-static-checks-r5" x="1451.8" y="752" textLength="12.2"
clip-path="url(#breeze-static-checks-line-30) [...]
</text><text class="breeze-static-checks-r5" x="0" y="776.4" textLength="12.2"
clip-path="url(#breeze-static-checks-line-31)">│</text><text
class="breeze-static-checks-r7" x="451.4" y="776.4" textLength="988.2"
clip-path="url(#breeze-static-checks-line-31)">check-start-date-not-used-in-defaults | check-system-tests-present |             </text><text
class="breeze-static-checks-r5" x="1451.8" y="776.4" textLen [...]
-</text><text class="breeze-static-checks-r5" x="0" y="800.8" textLength="12.2"
clip-path="url(#breeze-static-checks-line-32)">│</text><text
class="breeze-static-checks-r7" x="451.4" y="800.8" textLength="988.2"
clip-path="url(#breeze-static-checks-line-32)">check-system-tests-tocs | check-tests-in-the-right-folders |                     </text><text
class="breeze-static [...]
-</text><text class="breeze-static-checks-r5" x="0" y="825.2" textLength="12.2"
clip-path="url(#breeze-static-checks-line-33)">│</text><text
class="breeze-static-checks-r7" x="451.4" y="825.2" textLength="988.2"
clip-path="url(#breeze-static-checks-line-33)">check-tests-unittest-testcase | check-urlparse-usage-in-code |                   </text><text
class="breeze-static-checks-r5 [...]
-</text><text class="breeze-static-checks-r5" x="0" y="849.6" textLength="12.2"
clip-path="url(#breeze-static-checks-line-34)">│</text><text
class="breeze-static-checks-r7" x="451.4" y="849.6" textLength="988.2"
clip-path="url(#breeze-static-checks-line-34)">check-usage-of-re2-over-re | check-xml | codespell | compile-www-assets |        </text><text
class="breeze-static-checks-r5" x="1451.8" y="849.6" textLength=" [...]
-</text><text class="breeze-static-checks-r5" x="0" y="874" textLength="12.2"
clip-path="url(#breeze-static-checks-line-35)">│</text><text
class="breeze-static-checks-r7" x="451.4" y="874" textLength="988.2"
clip-path="url(#breeze-static-checks-line-35)">compile-www-assets-dev | create-missing-init-py-files-tests | debug-statements | </text><text
class="breeze-static-checks-r5" x="1451.8" y="874" textLength="12.2"
clip-path="url(#breeze-static-checks-line-35) [...]
-</text><text class="breeze-static-checks-r5" x="0" y="898.4" textLength="12.2"
clip-path="url(#breeze-static-checks-line-36)">│</text><text
class="breeze-static-checks-r7" x="451.4" y="898.4" textLength="988.2"
clip-path="url(#breeze-static-checks-line-36)">detect-private-key | doctoc | end-of-file-fixer | fix-encoding-pragma | flynt |  </text><text
class="breeze-static-checks-r5" x="1451.8" y="898.4" textLength="12.2"
clip-path="url [...]
-</text><text class="breeze-static-checks-r5" x="0" y="922.8" textLength="12.2"
clip-path="url(#breeze-static-checks-line-37)">│</text><text
class="breeze-static-checks-r7" x="451.4" y="922.8" textLength="988.2"
clip-path="url(#breeze-static-checks-line-37)">generate-airflow-diagrams | generate-pypi-readme | identity | insert-license |   </text><text
class="breeze-static-checks-r5" x="1451.8" y="922.8" textLength="12.2"
clip-path="url(#bre [...]
-</text><text class="breeze-static-checks-r5" x="0" y="947.2" textLength="12.2"
clip-path="url(#breeze-static-checks-line-38)">│</text><text
class="breeze-static-checks-r7" x="451.4" y="947.2" textLength="988.2"
clip-path="url(#breeze-static-checks-line-38)">kubeconform | lint-chart-schema | lint-css | lint-dockerfile | lint-helm-chart | </text><text
class="breeze-static-checks-r5" x="1451.8" y="947.2" textLength="12.2"
clip-path="url(#bre [...]
-</text><text class="breeze-static-checks-r5" x="0" y="971.6" textLength="12.2"
clip-path="url(#breeze-static-checks-line-39)">│</text><text
class="breeze-static-checks-r7" x="451.4" y="971.6" textLength="988.2"
clip-path="url(#breeze-static-checks-line-39)">lint-json-schema | lint-markdown | lint-openapi | mixed-line-ending |            </text><text
class="breeze-static-checks-r5" x="1451.8" y= [...]
-</text><text class="breeze-static-checks-r5" x="0" y="996" textLength="12.2"
clip-path="url(#breeze-static-checks-line-40)">│</text><text
class="breeze-static-checks-r7" x="451.4" y="996" textLength="988.2"
clip-path="url(#breeze-static-checks-line-40)">mypy-airflow | mypy-dev | mypy-docs | mypy-providers | pretty-format-json |      </text><text
class="breeze-static-checks-r5" x="1451.8" y="996" textLength="12.2" [...]
-</text><text class="breeze-static-checks-r5" x="0" y="1020.4"
textLength="12.2" clip-path="url(#breeze-static-checks-line-41)">│</text><text
class="breeze-static-checks-r7" x="451.4" y="1020.4" textLength="988.2"
clip-path="url(#breeze-static-checks-line-41)">pylint | python-no-log-warn | replace-bad-characters | rst-backticks | ruff |    </text><text
class="breeze-static-checks-r5" x="1451.8" y="1020.4" textLength="12.2" c [...]
-</text><text class="breeze-static-checks-r5" x="0" y="1044.8"
textLength="12.2" clip-path="url(#breeze-static-checks-line-42)">│</text><text
class="breeze-static-checks-r7" x="451.4" y="1044.8" textLength="988.2"
clip-path="url(#breeze-static-checks-line-42)">ruff-format | shellcheck | trailing-whitespace | ts-compile-format-lint-www |    </text><text
class="breeze-static-checks-r5" x="1451.8" y="1044.8" textLength="12.2"
clip-path=" [...]
-</text><text class="breeze-static-checks-r5" x="0" y="1069.2"
textLength="12.2" clip-path="url(#breeze-static-checks-line-43)">│</text><text
class="breeze-static-checks-r7" x="451.4" y="1069.2" textLength="988.2"
clip-path="url(#breeze-static-checks-line-43)">update-black-version | update-breeze-cmd-output |                            
[...]
-</text><text class="breeze-static-checks-r5" x="0" y="1093.6"
textLength="12.2" clip-path="url(#breeze-static-checks-line-44)">│</text><text
class="breeze-static-checks-r7" x="451.4" y="1093.6" textLength="988.2"
clip-path="url(#breeze-static-checks-line-44)">update-breeze-readme-config-hash | update-build-dependencies |                   </text><text
class="breeze-static-checks- [...]
-</text><text class="breeze-static-checks-r5" x="0" y="1118" textLength="12.2"
clip-path="url(#breeze-static-checks-line-45)">│</text><text
class="breeze-static-checks-r7" x="451.4" y="1118" textLength="988.2"
clip-path="url(#breeze-static-checks-line-45)">update-chart-dependencies | update-common-sql-api-stubs | update-er-diagram |    </text><text
class="breeze-static-checks-r5" x="1451.8" y="1118" textLength="12.2"
clip-path="url(#breeze-stat [...]
-</text><text class="breeze-static-checks-r5" x="0" y="1142.4"
textLength="12.2" clip-path="url(#breeze-static-checks-line-46)">│</text><text
class="breeze-static-checks-r7" x="451.4" y="1142.4" textLength="988.2"
clip-path="url(#breeze-static-checks-line-46)">update-extras | update-in-the-wild-to-be-sorted |                            
[...]
-</text><text class="breeze-static-checks-r5" x="0" y="1166.8"
textLength="12.2" clip-path="url(#breeze-static-checks-line-47)">│</text><text
class="breeze-static-checks-r7" x="451.4" y="1166.8" textLength="988.2"
clip-path="url(#breeze-static-checks-line-47)">update-inlined-dockerfile-scripts | update-installed-providers-to-be-sorted |    </text><text
class="breeze-static-checks-r5" x="1451.8" y="1166.8" textLength="12.2"
clip-path="url(#breeze-static-c [...]
-</text><text class="breeze-static-checks-r5" x="0" y="1191.2"
textLength="12.2" clip-path="url(#breeze-static-checks-line-48)">│</text><text
class="breeze-static-checks-r7" x="451.4" y="1191.2" textLength="988.2"
clip-path="url(#breeze-static-checks-line-48)">update-installers | update-local-yml-file | update-migration-references |        </text><text
class="breeze-static-checks-r5" x="1451.8" y="1191.2" textLength="12.2" c [...]
-</text><text class="breeze-static-checks-r5" x="0" y="1215.6"
textLength="12.2" clip-path="url(#breeze-static-checks-line-49)">│</text><text
class="breeze-static-checks-r7" x="451.4" y="1215.6" textLength="988.2"
clip-path="url(#breeze-static-checks-line-49)">update-providers-dependencies | update-reproducible-source-date-epoch |          </text><text
class="breeze-static-checks-r5" x="1451.8" y="1215.6" textLength="12.2" c [...]
-</text><text class="breeze-static-checks-r5" x="0" y="1240" textLength="12.2"
clip-path="url(#breeze-static-checks-line-50)">│</text><text
class="breeze-static-checks-r7" x="451.4" y="1240" textLength="988.2"
clip-path="url(#breeze-static-checks-line-50)">update-spelling-wordlist-to-be-sorted | update-supported-versions |              </text><text
class="breeze-static-checks-r5" x="1451.8" y="1240" textL [...]
-</text><text class="breeze-static-checks-r5" x="0" y="1264.4"
textLength="12.2" clip-path="url(#breeze-static-checks-line-51)">│</text><text
class="breeze-static-checks-r7" x="451.4" y="1264.4" textLength="988.2"
clip-path="url(#breeze-static-checks-line-51)">update-vendored-in-k8s-json-schema | update-version | validate-operators-init |  </text><text
class="breeze-static-checks-r5" x="1451.8" y="1264.4" textLength="12.2"
clip-path="url(#breeze-static-c [...]
-</text><text class="breeze-static-checks-r5" x="0" y="1288.8"
textLength="12.2" clip-path="url(#breeze-static-checks-line-52)">│</text><text
class="breeze-static-checks-r7" x="451.4" y="1288.8" textLength="988.2"
clip-path="url(#breeze-static-checks-line-52)">yamllint)                                     &
[...]
-</text><text class="breeze-static-checks-r5" x="0" y="1313.2"
textLength="12.2" clip-path="url(#breeze-static-checks-line-53)">│</text><text
class="breeze-static-checks-r4" x="24.4" y="1313.2" textLength="12.2"
clip-path="url(#breeze-static-checks-line-53)">-</text><text
class="breeze-static-checks-r4" x="36.6" y="1313.2" textLength="61"
clip-path="url(#breeze-static-checks-line-53)">-show</text><text
class="breeze-static-checks-r4" x="97.6" y="1313.2" textLength="195.2"
clip-path="url(# [...]
-</text><text class="breeze-static-checks-r5" x="0" y="1337.6"
textLength="12.2" clip-path="url(#breeze-static-checks-line-54)">│</text><text
class="breeze-static-checks-r4" x="24.4" y="1337.6" textLength="12.2"
clip-path="url(#breeze-static-checks-line-54)">-</text><text
class="breeze-static-checks-r4" x="36.6" y="1337.6" textLength="134.2"
clip-path="url(#breeze-static-checks-line-54)">-initialize</text><text
class="breeze-static-checks-r4" x="170.8" y="1337.6" textLength="146.4" clip-p
[...]
-</text><text class="breeze-static-checks-r5" x="0" y="1362" textLength="12.2"
clip-path="url(#breeze-static-checks-line-55)">│</text><text
class="breeze-static-checks-r4" x="24.4" y="1362" textLength="12.2"
clip-path="url(#breeze-static-checks-line-55)">-</text><text
class="breeze-static-checks-r4" x="36.6" y="1362" textLength="48.8"
clip-path="url(#breeze-static-checks-line-55)">-max</text><text
class="breeze-static-checks-r4" x="85.4" y="1362" textLength="292.8"
clip-path="url(#breeze- [...]
-</text><text class="breeze-static-checks-r5" x="0" y="1386.4"
textLength="12.2" clip-path="url(#breeze-static-checks-line-56)">│</text><text
class="breeze-static-checks-r7" x="451.4" y="1386.4" textLength="854"
clip-path="url(#breeze-static-checks-line-56)">(INTEGER RANGE)                                   
[...]
-</text><text class="breeze-static-checks-r5" x="0" y="1410.8"
textLength="12.2" clip-path="url(#breeze-static-checks-line-57)">│</text><text
class="breeze-static-checks-r5" x="451.4" y="1410.8" textLength="854"
clip-path="url(#breeze-static-checks-line-57)">[default: 3; 1<=x<=10]                                
[...]
-</text><text class="breeze-static-checks-r5" x="0" y="1435.2"
textLength="1464"
clip-path="url(#breeze-static-checks-line-58)">╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯</text><text
class="breeze-static-checks-r1" x="1464" y="1435.2" textLength="12.2"
clip-path="url(#breeze-static-checks-line-58)">
-</text><text class="breeze-static-checks-r5" x="0" y="1459.6"
textLength="24.4" clip-path="url(#breeze-static-checks-line-59)">╭─</text><text
class="breeze-static-checks-r5" x="24.4" y="1459.6" textLength="463.6"
clip-path="url(#breeze-static-checks-line-59)"> Selecting files to run the checks on </text><text
class="breeze-static-checks-r5" x="488" y="1459.6" textLength="951.6"
clip-path="url(#breeze-static-checks-line-59)">──────────────────────── [...]
-</text><text class="breeze-static-checks-r5" x="0" y="1484" textLength="12.2"
clip-path="url(#breeze-static-checks-line-60)">│</text><text
class="breeze-static-checks-r4" x="24.4" y="1484" textLength="12.2"
clip-path="url(#breeze-static-checks-line-60)">-</text><text
class="breeze-static-checks-r4" x="36.6" y="1484" textLength="61"
clip-path="url(#breeze-static-checks-line-60)">-file</text><text
class="breeze-static-checks-r6" x="256.2" y="1484" textLength="24.4"
clip-path="url(#breeze-s [...]
-</text><text class="breeze-static-checks-r5" x="0" y="1508.4"
textLength="12.2" clip-path="url(#breeze-static-checks-line-61)">│</text><text
class="breeze-static-checks-r4" x="24.4" y="1508.4" textLength="12.2"
clip-path="url(#breeze-static-checks-line-61)">-</text><text
class="breeze-static-checks-r4" x="36.6" y="1508.4" textLength="48.8"
clip-path="url(#breeze-static-checks-line-61)">-all</text><text
class="breeze-static-checks-r4" x="85.4" y="1508.4" textLength="73.2"
clip-path="url(# [...]
-</text><text class="breeze-static-checks-r5" x="0" y="1532.8"
textLength="12.2" clip-path="url(#breeze-static-checks-line-62)">│</text><text
class="breeze-static-checks-r4" x="24.4" y="1532.8" textLength="12.2"
clip-path="url(#breeze-static-checks-line-62)">-</text><text
class="breeze-static-checks-r4" x="36.6" y="1532.8" textLength="85.4"
clip-path="url(#breeze-static-checks-line-62)">-commit</text><text
class="breeze-static-checks-r4" x="122" y="1532.8" textLength="48.8"
clip-path="url [...]
-</text><text class="breeze-static-checks-r5" x="0" y="1557.2"
textLength="12.2" clip-path="url(#breeze-static-checks-line-63)">│</text><text
class="breeze-static-checks-r1" x="305" y="1557.2" textLength="183"
clip-path="url(#breeze-static-checks-line-63)">exclusive with </text><text
class="breeze-static-checks-r4" x="488" y="1557.2" textLength="12.2"
clip-path="url(#breeze-static-checks-line-63)">-</text><text
class="breeze-static-checks-r4" x="500.2" y="1557.2" textLength="61" [...]
-</text><text class="breeze-static-checks-r5" x="0" y="1581.6"
textLength="12.2" clip-path="url(#breeze-static-checks-line-64)">│</text><text
class="breeze-static-checks-r7" x="305" y="1581.6" textLength="1134.6"
clip-path="url(#breeze-static-checks-line-64)">(TEXT)                                      
[...]
-</text><text class="breeze-static-checks-r5" x="0" y="1606" textLength="12.2"
clip-path="url(#breeze-static-checks-line-65)">│</text><text
class="breeze-static-checks-r4" x="24.4" y="1606" textLength="12.2"
clip-path="url(#breeze-static-checks-line-65)">-</text><text
class="breeze-static-checks-r4" x="36.6" y="1606" textLength="61"
clip-path="url(#breeze-static-checks-line-65)">-last</text><text
class="breeze-static-checks-r4" x="97.6" y="1606" textLength="85.4"
clip-path="url(#breeze-st [...]
-</text><text class="breeze-static-checks-r5" x="0" y="1630.4"
textLength="12.2" clip-path="url(#breeze-static-checks-line-66)">│</text><text
class="breeze-static-checks-r4" x="24.4" y="1630.4" textLength="12.2"
clip-path="url(#breeze-static-checks-line-66)">-</text><text
class="breeze-static-checks-r4" x="36.6" y="1630.4" textLength="61"
clip-path="url(#breeze-static-checks-line-66)">-only</text><text
class="breeze-static-checks-r4" x="97.6" y="1630.4" textLength="134.2"
clip-path="url(# [...]
-</text><text class="breeze-static-checks-r5" x="0" y="1654.8"
textLength="12.2" clip-path="url(#breeze-static-checks-line-67)">│</text><text
class="breeze-static-checks-r1" x="305" y="1654.8" textLength="1134.6"
clip-path="url(#breeze-static-checks-line-67)">branch and HEAD of your branch.                             
[...]
-</text><text class="breeze-static-checks-r5" x="0" y="1679.2"
textLength="1464"
clip-path="url(#breeze-static-checks-line-68)">╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯</text><text
class="breeze-static-checks-r1" x="1464" y="1679.2" textLength="12.2"
clip-path="url(#breeze-static-checks-line-68)">
-</text><text class="breeze-static-checks-r5" x="0" y="1703.6"
textLength="24.4" clip-path="url(#breeze-static-checks-line-69)">╭─</text><text
class="breeze-static-checks-r5" x="24.4" y="1703.6" textLength="463.6"
clip-path="url(#breeze-static-checks-line-69)"> Building image before running checks </text><text
class="breeze-static-checks-r5" x="488" y="1703.6" textLength="951.6"
clip-path="url(#breeze-static-checks-line-69)">──────────────────────────────────
[...]
-</text><text class="breeze-static-checks-r5" x="0" y="1728" textLength="12.2"
clip-path="url(#breeze-static-checks-line-70)">│</text><text
class="breeze-static-checks-r4" x="24.4" y="1728" textLength="12.2"
clip-path="url(#breeze-static-checks-line-70)">-</text><text
class="breeze-static-checks-r4" x="36.6" y="1728" textLength="61"
clip-path="url(#breeze-static-checks-line-70)">-skip</text><text
class="breeze-static-checks-r4" x="97.6" y="1728" textLength="244"
clip-path="url(#breeze-sta [...]
-</text><text class="breeze-static-checks-r5" x="0" y="1752.4"
textLength="12.2" clip-path="url(#breeze-static-checks-line-71)">│</text><text
class="breeze-static-checks-r4" x="24.4" y="1752.4" textLength="12.2"
clip-path="url(#breeze-static-checks-line-71)">-</text><text
class="breeze-static-checks-r4" x="36.6" y="1752.4" textLength="73.2"
clip-path="url(#breeze-static-checks-line-71)">-force</text><text
class="breeze-static-checks-r4" x="109.8" y="1752.4" textLength="73.2"
clip-path="ur [...]
-</text><text class="breeze-static-checks-r5" x="0" y="1776.8"
textLength="12.2" clip-path="url(#breeze-static-checks-line-72)">│</text><text
class="breeze-static-checks-r4" x="24.4" y="1776.8" textLength="12.2"
clip-path="url(#breeze-static-checks-line-72)">-</text><text
class="breeze-static-checks-r4" x="36.6" y="1776.8" textLength="73.2"
clip-path="url(#breeze-static-checks-line-72)">-image</text><text
class="breeze-static-checks-r4" x="109.8" y="1776.8" textLength="48.8"
clip-path="ur [...]
-</text><text class="breeze-static-checks-r5" x="0" y="1801.2"
textLength="12.2" clip-path="url(#breeze-static-checks-line-73)">│</text><text
class="breeze-static-checks-r7" x="414.8" y="1801.2" textLength="963.8"
clip-path="url(#breeze-static-checks-line-73)">(TEXT)                                     
[...]
-</text><text class="breeze-static-checks-r5" x="0" y="1825.6"
textLength="12.2" clip-path="url(#breeze-static-checks-line-74)">│</text><text
class="breeze-static-checks-r5" x="414.8" y="1825.6" textLength="963.8"
clip-path="url(#breeze-static-checks-line-74)">[default: latest]                                   
[...]
-</text><text class="breeze-static-checks-r5" x="0" y="1850" textLength="12.2"
clip-path="url(#breeze-static-checks-line-75)">│</text><text
class="breeze-static-checks-r4" x="24.4" y="1850" textLength="12.2"
clip-path="url(#breeze-static-checks-line-75)">-</text><text
class="breeze-static-checks-r4" x="36.6" y="1850" textLength="85.4"
clip-path="url(#breeze-static-checks-line-75)">-github</text><text
class="breeze-static-checks-r4" x="122" y="1850" textLength="134.2"
clip-path="url(#breez [...]
-</text><text class="breeze-static-checks-r5" x="0" y="1874.4"
textLength="12.2" clip-path="url(#breeze-static-checks-line-76)">│</text><text
class="breeze-static-checks-r4" x="24.4" y="1874.4" textLength="12.2"
clip-path="url(#breeze-static-checks-line-76)">-</text><text
class="breeze-static-checks-r4" x="36.6" y="1874.4" textLength="97.6"
clip-path="url(#breeze-static-checks-line-76)">-builder</text><text
class="breeze-static-checks-r1" x="414.8" y="1874.4" textLength="756.4"
clip-path= [...]
-</text><text class="breeze-static-checks-r5" x="0" y="1898.8"
textLength="12.2" clip-path="url(#breeze-static-checks-line-77)">│</text><text
class="breeze-static-checks-r5" x="414.8" y="1898.8" textLength="756.4"
clip-path="url(#breeze-static-checks-line-77)">[default: autodetect]                                  &#
[...]
-</text><text class="breeze-static-checks-r5" x="0" y="1923.2"
textLength="1464"
clip-path="url(#breeze-static-checks-line-78)">╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯</text><text
class="breeze-static-checks-r1" x="1464" y="1923.2" textLength="12.2"
clip-path="url(#breeze-static-checks-line-78)">
-</text><text class="breeze-static-checks-r5" x="0" y="1947.6"
textLength="24.4" clip-path="url(#breeze-static-checks-line-79)">╭─</text><text
class="breeze-static-checks-r5" x="24.4" y="1947.6" textLength="195.2"
clip-path="url(#breeze-static-checks-line-79)"> Common options </text><text
class="breeze-static-checks-r5" x="219.6" y="1947.6" textLength="1220"
clip-path="url(#breeze-static-checks-line-79)">──────────────────────────────────────────────────────────────────────
[...]
-</text><text class="breeze-static-checks-r5" x="0" y="1972" textLength="12.2"
clip-path="url(#breeze-static-checks-line-80)">│</text><text
class="breeze-static-checks-r4" x="24.4" y="1972" textLength="12.2"
clip-path="url(#breeze-static-checks-line-80)">-</text><text
class="breeze-static-checks-r4" x="36.6" y="1972" textLength="48.8"
clip-path="url(#breeze-static-checks-line-80)">-dry</text><text
class="breeze-static-checks-r4" x="85.4" y="1972" textLength="48.8"
clip-path="url(#breeze-s [...]
-</text><text class="breeze-static-checks-r5" x="0" y="1996.4"
textLength="12.2" clip-path="url(#breeze-static-checks-line-81)">│</text><text
class="breeze-static-checks-r4" x="24.4" y="1996.4" textLength="12.2"
clip-path="url(#breeze-static-checks-line-81)">-</text><text
class="breeze-static-checks-r4" x="36.6" y="1996.4" textLength="97.6"
clip-path="url(#breeze-static-checks-line-81)">-verbose</text><text
class="breeze-static-checks-r6" x="158.6" y="1996.4" textLength="24.4"
clip-path=" [...]
-</text><text class="breeze-static-checks-r5" x="0" y="2020.8"
textLength="12.2" clip-path="url(#breeze-static-checks-line-82)">│</text><text
class="breeze-static-checks-r4" x="24.4" y="2020.8" textLength="12.2"
clip-path="url(#breeze-static-checks-line-82)">-</text><text
class="breeze-static-checks-r4" x="36.6" y="2020.8" textLength="61"
clip-path="url(#breeze-static-checks-line-82)">-help</text><text
class="breeze-static-checks-r6" x="158.6" y="2020.8" textLength="24.4"
clip-path="url(# [...]
-</text><text class="breeze-static-checks-r5" x="0" y="2045.2"
textLength="1464"
clip-path="url(#breeze-static-checks-line-83)">╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯</text><text
class="breeze-static-checks-r1" x="1464" y="2045.2" textLength="12.2"
clip-path="url(#breeze-static-checks-line-83)">
+</text><text class="breeze-static-checks-r5" x="0" y="800.8" textLength="12.2"
clip-path="url(#breeze-static-checks-line-32)">│</text><text
class="breeze-static-checks-r7" x="451.4" y="800.8" textLength="988.2"
clip-path="url(#breeze-static-checks-line-32)">check-system-tests-tocs | check-template-context-variable-in-sync |              </text><text
class="breeze-static-checks-r5" x="1451.8" y="800.8" te [...]
+</text><text class="breeze-static-checks-r5" x="0" y="825.2" textLength="12.2"
clip-path="url(#breeze-static-checks-line-33)">│</text><text
class="breeze-static-checks-r7" x="451.4" y="825.2" textLength="988.2"
clip-path="url(#breeze-static-checks-line-33)">check-tests-in-the-right-folders | check-tests-unittest-testcase |               </text><text
class="breeze-static-checks-r5" x="1451.8" y="825. [...]
+</text><text class="breeze-static-checks-r5" x="0" y="849.6" textLength="12.2"
clip-path="url(#breeze-static-checks-line-34)">│</text><text
class="breeze-static-checks-r7" x="451.4" y="849.6" textLength="988.2"
clip-path="url(#breeze-static-checks-line-34)">check-urlparse-usage-in-code | check-usage-of-re2-over-re | check-xml | codespell</text><text
class="breeze-static-checks-r5" x="1451.8" y="849.6" textLength="12.2"
clip-path="url(#breeze-static-checks-li [...]
+</text><text class="breeze-static-checks-r5" x="0" y="874" textLength="12.2"
clip-path="url(#breeze-static-checks-line-35)">│</text><text
class="breeze-static-checks-r7" x="451.4" y="874" textLength="988.2"
clip-path="url(#breeze-static-checks-line-35)">| compile-www-assets | compile-www-assets-dev |                            
[...]
+</text><text class="breeze-static-checks-r5" x="0" y="898.4" textLength="12.2"
clip-path="url(#breeze-static-checks-line-36)">│</text><text
class="breeze-static-checks-r7" x="451.4" y="898.4" textLength="988.2"
clip-path="url(#breeze-static-checks-line-36)">create-missing-init-py-files-tests | debug-statements | detect-private-key |     </text><text
class="breeze-static-checks-r5" x="1451.8" y="898.4" textLength="12.2"
clip-path="url(#bre [...]
+</text><text class="breeze-static-checks-r5" x="0" y="922.8" textLength="12.2"
clip-path="url(#breeze-static-checks-line-37)">│</text><text
class="breeze-static-checks-r7" x="451.4" y="922.8" textLength="988.2"
clip-path="url(#breeze-static-checks-line-37)">doctoc | end-of-file-fixer | fix-encoding-pragma | flynt |                       </t
[...]
+</text><text class="breeze-static-checks-r5" x="0" y="947.2" textLength="12.2"
clip-path="url(#breeze-static-checks-line-38)">│</text><text
class="breeze-static-checks-r7" x="451.4" y="947.2" textLength="988.2"
clip-path="url(#breeze-static-checks-line-38)">generate-airflow-diagrams | generate-pypi-readme | identity | insert-license |   </text><text
class="breeze-static-checks-r5" x="1451.8" y="947.2" textLength="12.2"
clip-path="url(#bre [...]
+</text><text class="breeze-static-checks-r5" x="0" y="971.6" textLength="12.2"
clip-path="url(#breeze-static-checks-line-39)">│</text><text
class="breeze-static-checks-r7" x="451.4" y="971.6" textLength="988.2"
clip-path="url(#breeze-static-checks-line-39)">kubeconform | lint-chart-schema | lint-css | lint-dockerfile | lint-helm-chart | </text><text
class="breeze-static-checks-r5" x="1451.8" y="971.6" textLength="12.2"
clip-path="url(#bre [...]
+</text><text class="breeze-static-checks-r5" x="0" y="996" textLength="12.2"
clip-path="url(#breeze-static-checks-line-40)">│</text><text
class="breeze-static-checks-r7" x="451.4" y="996" textLength="988.2"
clip-path="url(#breeze-static-checks-line-40)">lint-json-schema | lint-markdown | lint-openapi | mixed-line-ending |            </text><text
class="breeze-static-checks-r5" x="1451.8" y="996 [...]
+</text><text class="breeze-static-checks-r5" x="0" y="1020.4"
textLength="12.2" clip-path="url(#breeze-static-checks-line-41)">│</text><text
class="breeze-static-checks-r7" x="451.4" y="1020.4" textLength="988.2"
clip-path="url(#breeze-static-checks-line-41)">mypy-airflow | mypy-dev | mypy-docs | mypy-providers | pretty-format-json |      </text><text
class="breeze-static-checks-r5" x="1451.8" y="1020.4" textLengt [...]
+</text><text class="breeze-static-checks-r5" x="0" y="1044.8"
textLength="12.2" clip-path="url(#breeze-static-checks-line-42)">│</text><text
class="breeze-static-checks-r7" x="451.4" y="1044.8" textLength="988.2"
clip-path="url(#breeze-static-checks-line-42)">pylint | python-no-log-warn | replace-bad-characters | rst-backticks | ruff |    </text><text
class="breeze-static-checks-r5" x="1451.8" y="1044.8" textLength="12.2" c [...]
+</text><text class="breeze-static-checks-r5" x="0" y="1069.2"
textLength="12.2" clip-path="url(#breeze-static-checks-line-43)">│</text><text
class="breeze-static-checks-r7" x="451.4" y="1069.2" textLength="988.2"
clip-path="url(#breeze-static-checks-line-43)">ruff-format | shellcheck | trailing-whitespace | ts-compile-format-lint-www |    </text><text
class="breeze-static-checks-r5" x="1451.8" y="1069.2" textLength="12.2"
clip-path=" [...]
+</text><text class="breeze-static-checks-r5" x="0" y="1093.6"
textLength="12.2" clip-path="url(#breeze-static-checks-line-44)">│</text><text
class="breeze-static-checks-r7" x="451.4" y="1093.6" textLength="988.2"
clip-path="url(#breeze-static-checks-line-44)">update-black-version | update-breeze-cmd-output |                            
[...]
+</text><text class="breeze-static-checks-r5" x="0" y="1118" textLength="12.2"
clip-path="url(#breeze-static-checks-line-45)">│</text><text
class="breeze-static-checks-r7" x="451.4" y="1118" textLength="988.2"
clip-path="url(#breeze-static-checks-line-45)">update-breeze-readme-config-hash | update-build-dependencies |                   </text><text
class="breeze-static-checks-r5" [...]
+</text><text class="breeze-static-checks-r5" x="0" y="1142.4"
textLength="12.2" clip-path="url(#breeze-static-checks-line-46)">│</text><text
class="breeze-static-checks-r7" x="451.4" y="1142.4" textLength="988.2"
clip-path="url(#breeze-static-checks-line-46)">update-chart-dependencies | update-common-sql-api-stubs | update-er-diagram |    </text><text
class="breeze-static-checks-r5" x="1451.8" y="1142.4" textLength="12.2"
clip-path="url(#breez [...]
+</text><text class="breeze-static-checks-r5" x="0" y="1166.8"
textLength="12.2" clip-path="url(#breeze-static-checks-line-47)">│</text><text
class="breeze-static-checks-r7" x="451.4" y="1166.8" textLength="988.2"
clip-path="url(#breeze-static-checks-line-47)">update-extras | update-in-the-wild-to-be-sorted |                            
[...]
+</text><text class="breeze-static-checks-r5" x="0" y="1191.2"
textLength="12.2" clip-path="url(#breeze-static-checks-line-48)">│</text><text
class="breeze-static-checks-r7" x="451.4" y="1191.2" textLength="988.2"
clip-path="url(#breeze-static-checks-line-48)">update-inlined-dockerfile-scripts | update-installed-providers-to-be-sorted |    </text><text
class="breeze-static-checks-r5" x="1451.8" y="1191.2" textLength="12.2"
clip-path="url(#breeze-static-c [...]
+</text><text class="breeze-static-checks-r5" x="0" y="1215.6"
textLength="12.2" clip-path="url(#breeze-static-checks-line-49)">│</text><text
class="breeze-static-checks-r7" x="451.4" y="1215.6" textLength="988.2"
clip-path="url(#breeze-static-checks-line-49)">update-installers | update-local-yml-file | update-migration-references |        </text><text
class="breeze-static-checks-r5" x="1451.8" y="1215.6" textLength="12.2" c [...]
+</text><text class="breeze-static-checks-r5" x="0" y="1240" textLength="12.2"
clip-path="url(#breeze-static-checks-line-50)">│</text><text
class="breeze-static-checks-r7" x="451.4" y="1240" textLength="988.2"
clip-path="url(#breeze-static-checks-line-50)">update-providers-dependencies | update-reproducible-source-date-epoch |          </text><text
class="breeze-static-checks-r5" x="1451.8" y="1240" textLength="12.2" clip-pa
[...]
+</text><text class="breeze-static-checks-r5" x="0" y="1264.4"
textLength="12.2" clip-path="url(#breeze-static-checks-line-51)">│</text><text
class="breeze-static-checks-r7" x="451.4" y="1264.4" textLength="988.2"
clip-path="url(#breeze-static-checks-line-51)">update-spelling-wordlist-to-be-sorted | update-supported-versions |              </text><text
class="breeze-static-checks-r5" x="1451.8" y="1264.4" [...]
+</text><text class="breeze-static-checks-r5" x="0" y="1288.8"
textLength="12.2" clip-path="url(#breeze-static-checks-line-52)">│</text><text
class="breeze-static-checks-r7" x="451.4" y="1288.8" textLength="988.2"
clip-path="url(#breeze-static-checks-line-52)">update-vendored-in-k8s-json-schema | update-version | validate-operators-init |  </text><text
class="breeze-static-checks-r5" x="1451.8" y="1288.8" textLength="12.2"
clip-path="url(#breeze-static-c [...]
+</text><text class="breeze-static-checks-r5" x="0" y="1313.2"
textLength="12.2" clip-path="url(#breeze-static-checks-line-53)">│</text><text
class="breeze-static-checks-r7" x="451.4" y="1313.2" textLength="988.2"
clip-path="url(#breeze-static-checks-line-53)">yamllint)                                     &
[...]
+</text><text class="breeze-static-checks-r5" x="0" y="1337.6"
textLength="12.2" clip-path="url(#breeze-static-checks-line-54)">│</text><text
class="breeze-static-checks-r4" x="24.4" y="1337.6" textLength="12.2"
clip-path="url(#breeze-static-checks-line-54)">-</text><text
class="breeze-static-checks-r4" x="36.6" y="1337.6" textLength="61"
clip-path="url(#breeze-static-checks-line-54)">-show</text><text
class="breeze-static-checks-r4" x="97.6" y="1337.6" textLength="195.2"
clip-path="url(# [...]
+</text><text class="breeze-static-checks-r5" x="0" y="1362" textLength="12.2"
clip-path="url(#breeze-static-checks-line-55)">│</text><text
class="breeze-static-checks-r4" x="24.4" y="1362" textLength="12.2"
clip-path="url(#breeze-static-checks-line-55)">-</text><text
class="breeze-static-checks-r4" x="36.6" y="1362" textLength="134.2"
clip-path="url(#breeze-static-checks-line-55)">-initialize</text><text
class="breeze-static-checks-r4" x="170.8" y="1362" textLength="146.4"
clip-path="url [...]
+</text><text class="breeze-static-checks-r5" x="0" y="1386.4"
textLength="12.2" clip-path="url(#breeze-static-checks-line-56)">│</text><text
class="breeze-static-checks-r4" x="24.4" y="1386.4" textLength="12.2"
clip-path="url(#breeze-static-checks-line-56)">-</text><text
class="breeze-static-checks-r4" x="36.6" y="1386.4" textLength="48.8"
clip-path="url(#breeze-static-checks-line-56)">-max</text><text
class="breeze-static-checks-r4" x="85.4" y="1386.4" textLength="292.8"
clip-path="url( [...]
+</text><text class="breeze-static-checks-r5" x="0" y="1410.8"
textLength="12.2" clip-path="url(#breeze-static-checks-line-57)">│</text><text
class="breeze-static-checks-r7" x="451.4" y="1410.8" textLength="854"
clip-path="url(#breeze-static-checks-line-57)">(INTEGER RANGE)                                   
[...]
+</text><text class="breeze-static-checks-r5" x="0" y="1435.2"
textLength="12.2" clip-path="url(#breeze-static-checks-line-58)">│</text><text
class="breeze-static-checks-r5" x="451.4" y="1435.2" textLength="854"
clip-path="url(#breeze-static-checks-line-58)">[default: 3; 1<=x<=10]                                
[...]
+</text><text class="breeze-static-checks-r5" x="0" y="1459.6"
textLength="1464"
clip-path="url(#breeze-static-checks-line-59)">╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯</text><text
class="breeze-static-checks-r1" x="1464" y="1459.6" textLength="12.2"
clip-path="url(#breeze-static-checks-line-59)">
+</text><text class="breeze-static-checks-r5" x="0" y="1484" textLength="24.4"
clip-path="url(#breeze-static-checks-line-60)">╭─</text><text
class="breeze-static-checks-r5" x="24.4" y="1484" textLength="463.6"
clip-path="url(#breeze-static-checks-line-60)"> Selecting files to run the checks on </text><text
class="breeze-static-checks-r5" x="488" y="1484" textLength="951.6"
clip-path="url(#breeze-static-checks-line-60)">──────────────────────────────
[...]
+</text><text class="breeze-static-checks-r5" x="0" y="1508.4"
textLength="12.2" clip-path="url(#breeze-static-checks-line-61)">│</text><text
class="breeze-static-checks-r4" x="24.4" y="1508.4" textLength="12.2"
clip-path="url(#breeze-static-checks-line-61)">-</text><text
class="breeze-static-checks-r4" x="36.6" y="1508.4" textLength="61"
clip-path="url(#breeze-static-checks-line-61)">-file</text><text
class="breeze-static-checks-r6" x="256.2" y="1508.4" textLength="24.4"
clip-path="url(# [...]
+</text><text class="breeze-static-checks-r5" x="0" y="1532.8"
textLength="12.2" clip-path="url(#breeze-static-checks-line-62)">│</text><text
class="breeze-static-checks-r4" x="24.4" y="1532.8" textLength="12.2"
clip-path="url(#breeze-static-checks-line-62)">-</text><text
class="breeze-static-checks-r4" x="36.6" y="1532.8" textLength="48.8"
clip-path="url(#breeze-static-checks-line-62)">-all</text><text
class="breeze-static-checks-r4" x="85.4" y="1532.8" textLength="73.2"
clip-path="url(# [...]
+</text><text class="breeze-static-checks-r5" x="0" y="1557.2"
textLength="12.2" clip-path="url(#breeze-static-checks-line-63)">│</text><text
class="breeze-static-checks-r4" x="24.4" y="1557.2" textLength="12.2"
clip-path="url(#breeze-static-checks-line-63)">-</text><text
class="breeze-static-checks-r4" x="36.6" y="1557.2" textLength="85.4"
clip-path="url(#breeze-static-checks-line-63)">-commit</text><text
class="breeze-static-checks-r4" x="122" y="1557.2" textLength="48.8"
clip-path="url [...]
+</text><text class="breeze-static-checks-r5" x="0" y="1581.6"
textLength="12.2" clip-path="url(#breeze-static-checks-line-64)">│</text><text
class="breeze-static-checks-r1" x="305" y="1581.6" textLength="183"
clip-path="url(#breeze-static-checks-line-64)">exclusive with </text><text
class="breeze-static-checks-r4" x="488" y="1581.6" textLength="12.2"
clip-path="url(#breeze-static-checks-line-64)">-</text><text
class="breeze-static-checks-r4" x="500.2" y="1581.6" textLength="61" [...]
+</text><text class="breeze-static-checks-r5" x="0" y="1606" textLength="12.2"
clip-path="url(#breeze-static-checks-line-65)">│</text><text
class="breeze-static-checks-r7" x="305" y="1606" textLength="1134.6"
clip-path="url(#breeze-static-checks-line-65)">(TEXT)                                      
[...]
+</text><text class="breeze-static-checks-r5" x="0" y="1630.4"
textLength="12.2" clip-path="url(#breeze-static-checks-line-66)">│</text><text
class="breeze-static-checks-r4" x="24.4" y="1630.4" textLength="12.2"
clip-path="url(#breeze-static-checks-line-66)">-</text><text
class="breeze-static-checks-r4" x="36.6" y="1630.4" textLength="61"
clip-path="url(#breeze-static-checks-line-66)">-last</text><text
class="breeze-static-checks-r4" x="97.6" y="1630.4" textLength="85.4"
clip-path="url(#b [...]
+</text><text class="breeze-static-checks-r5" x="0" y="1654.8"
textLength="12.2" clip-path="url(#breeze-static-checks-line-67)">│</text><text
class="breeze-static-checks-r4" x="24.4" y="1654.8" textLength="12.2"
clip-path="url(#breeze-static-checks-line-67)">-</text><text
class="breeze-static-checks-r4" x="36.6" y="1654.8" textLength="61"
clip-path="url(#breeze-static-checks-line-67)">-only</text><text
class="breeze-static-checks-r4" x="97.6" y="1654.8" textLength="134.2"
clip-path="url(# [...]
+</text><text class="breeze-static-checks-r5" x="0" y="1679.2"
textLength="12.2" clip-path="url(#breeze-static-checks-line-68)">│</text><text
class="breeze-static-checks-r1" x="305" y="1679.2" textLength="1134.6"
clip-path="url(#breeze-static-checks-line-68)">branch and HEAD of your branch.                             
[...]
+</text><text class="breeze-static-checks-r5" x="0" y="1703.6"
textLength="1464"
clip-path="url(#breeze-static-checks-line-69)">╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯</text><text
class="breeze-static-checks-r1" x="1464" y="1703.6" textLength="12.2"
clip-path="url(#breeze-static-checks-line-69)">
+</text><text class="breeze-static-checks-r5" x="0" y="1728" textLength="24.4"
clip-path="url(#breeze-static-checks-line-70)">╭─</text><text
class="breeze-static-checks-r5" x="24.4" y="1728" textLength="463.6"
clip-path="url(#breeze-static-checks-line-70)"> Building image before running checks </text><text
class="breeze-static-checks-r5" x="488" y="1728" textLength="951.6"
clip-path="url(#breeze-static-checks-line-70)">────────────────────────────────────────
[...]
+</text><text class="breeze-static-checks-r5" x="0" y="1752.4"
textLength="12.2" clip-path="url(#breeze-static-checks-line-71)">│</text><text
class="breeze-static-checks-r4" x="24.4" y="1752.4" textLength="12.2"
clip-path="url(#breeze-static-checks-line-71)">-</text><text
class="breeze-static-checks-r4" x="36.6" y="1752.4" textLength="61"
clip-path="url(#breeze-static-checks-line-71)">-skip</text><text
class="breeze-static-checks-r4" x="97.6" y="1752.4" textLength="244"
clip-path="url(#br [...]
+</text><text class="breeze-static-checks-r5" x="0" y="1776.8"
textLength="12.2" clip-path="url(#breeze-static-checks-line-72)">│</text><text
class="breeze-static-checks-r4" x="24.4" y="1776.8" textLength="12.2"
clip-path="url(#breeze-static-checks-line-72)">-</text><text
class="breeze-static-checks-r4" x="36.6" y="1776.8" textLength="73.2"
clip-path="url(#breeze-static-checks-line-72)">-force</text><text
class="breeze-static-checks-r4" x="109.8" y="1776.8" textLength="73.2"
clip-path="ur [...]
+</text><text class="breeze-static-checks-r5" x="0" y="1801.2"
textLength="12.2" clip-path="url(#breeze-static-checks-line-73)">│</text><text
class="breeze-static-checks-r4" x="24.4" y="1801.2" textLength="12.2"
clip-path="url(#breeze-static-checks-line-73)">-</text><text
class="breeze-static-checks-r4" x="36.6" y="1801.2" textLength="73.2"
clip-path="url(#breeze-static-checks-line-73)">-image</text><text
class="breeze-static-checks-r4" x="109.8" y="1801.2" textLength="48.8"
clip-path="ur [...]
+</text><text class="breeze-static-checks-r5" x="0" y="1825.6"
textLength="12.2" clip-path="url(#breeze-static-checks-line-74)">│</text><text
class="breeze-static-checks-r7" x="414.8" y="1825.6" textLength="963.8"
clip-path="url(#breeze-static-checks-line-74)">(TEXT)                                     
[...]
+</text><text class="breeze-static-checks-r5" x="0" y="1850" textLength="12.2"
clip-path="url(#breeze-static-checks-line-75)">│</text><text
class="breeze-static-checks-r5" x="414.8" y="1850" textLength="963.8"
clip-path="url(#breeze-static-checks-line-75)">[default: latest]                                   
[...]
+</text><text class="breeze-static-checks-r5" x="0" y="1874.4"
textLength="12.2" clip-path="url(#breeze-static-checks-line-76)">│</text><text
class="breeze-static-checks-r4" x="24.4" y="1874.4" textLength="12.2"
clip-path="url(#breeze-static-checks-line-76)">-</text><text
class="breeze-static-checks-r4" x="36.6" y="1874.4" textLength="85.4"
clip-path="url(#breeze-static-checks-line-76)">-github</text><text
class="breeze-static-checks-r4" x="122" y="1874.4" textLength="134.2"
clip-path="ur [...]
+</text><text class="breeze-static-checks-r5" x="0" y="1898.8"
textLength="12.2" clip-path="url(#breeze-static-checks-line-77)">│</text><text
class="breeze-static-checks-r4" x="24.4" y="1898.8" textLength="12.2"
clip-path="url(#breeze-static-checks-line-77)">-</text><text
class="breeze-static-checks-r4" x="36.6" y="1898.8" textLength="97.6"
clip-path="url(#breeze-static-checks-line-77)">-builder</text><text
class="breeze-static-checks-r1" x="414.8" y="1898.8" textLength="756.4"
clip-path= [...]
+</text><text class="breeze-static-checks-r5" x="0" y="1923.2"
textLength="12.2" clip-path="url(#breeze-static-checks-line-78)">│</text><text
class="breeze-static-checks-r5" x="414.8" y="1923.2" textLength="756.4"
clip-path="url(#breeze-static-checks-line-78)">[default: autodetect]                                  &#
[...]
+</text><text class="breeze-static-checks-r5" x="0" y="1947.6"
textLength="1464"
clip-path="url(#breeze-static-checks-line-79)">╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯</text><text
class="breeze-static-checks-r1" x="1464" y="1947.6" textLength="12.2"
clip-path="url(#breeze-static-checks-line-79)">
+</text><text class="breeze-static-checks-r5" x="0" y="1972" textLength="24.4"
clip-path="url(#breeze-static-checks-line-80)">╭─</text><text
class="breeze-static-checks-r5" x="24.4" y="1972" textLength="195.2"
clip-path="url(#breeze-static-checks-line-80)"> Common options </text><text
class="breeze-static-checks-r5" x="219.6" y="1972" textLength="1220"
clip-path="url(#breeze-static-checks-line-80)">────────────────────────────────────────────────────────────────────────────
[...]
+</text><text class="breeze-static-checks-r5" x="0" y="1996.4"
textLength="12.2" clip-path="url(#breeze-static-checks-line-81)">│</text><text
class="breeze-static-checks-r4" x="24.4" y="1996.4" textLength="12.2"
clip-path="url(#breeze-static-checks-line-81)">-</text><text
class="breeze-static-checks-r4" x="36.6" y="1996.4" textLength="48.8"
clip-path="url(#breeze-static-checks-line-81)">-dry</text><text
class="breeze-static-checks-r4" x="85.4" y="1996.4" textLength="48.8"
clip-path="url(# [...]
+</text><text class="breeze-static-checks-r5" x="0" y="2020.8"
textLength="12.2" clip-path="url(#breeze-static-checks-line-82)">│</text><text
class="breeze-static-checks-r4" x="24.4" y="2020.8" textLength="12.2"
clip-path="url(#breeze-static-checks-line-82)">-</text><text
class="breeze-static-checks-r4" x="36.6" y="2020.8" textLength="97.6"
clip-path="url(#breeze-static-checks-line-82)">-verbose</text><text
class="breeze-static-checks-r6" x="158.6" y="2020.8" textLength="24.4"
clip-path=" [...]
+</text><text class="breeze-static-checks-r5" x="0" y="2045.2"
textLength="12.2" clip-path="url(#breeze-static-checks-line-83)">│</text><text
class="breeze-static-checks-r4" x="24.4" y="2045.2" textLength="12.2"
clip-path="url(#breeze-static-checks-line-83)">-</text><text
class="breeze-static-checks-r4" x="36.6" y="2045.2" textLength="61"
clip-path="url(#breeze-static-checks-line-83)">-help</text><text
class="breeze-static-checks-r6" x="158.6" y="2045.2" textLength="24.4"
clip-path="url(# [...]
+</text><text class="breeze-static-checks-r5" x="0" y="2069.6"
textLength="1464"
clip-path="url(#breeze-static-checks-line-84)">╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯</text><text
class="breeze-static-checks-r1" x="1464" y="2069.6" textLength="12.2"
clip-path="url(#breeze-static-checks-line-84)">
</text>
</g>
</g>
diff --git a/dev/breeze/doc/images/output_static-checks.txt
b/dev/breeze/doc/images/output_static-checks.txt
index 66ca569ffb..10228461e0 100644
--- a/dev/breeze/doc/images/output_static-checks.txt
+++ b/dev/breeze/doc/images/output_static-checks.txt
@@ -1 +1 @@
-e8fa3d7a6215d2565dc536cbc50e0465
+4dc5653769bd03c4d07f6d998cdcb679
diff --git a/dev/breeze/src/airflow_breeze/pre_commit_ids.py
b/dev/breeze/src/airflow_breeze/pre_commit_ids.py
index 516fc85c51..867f16e0a9 100644
--- a/dev/breeze/src/airflow_breeze/pre_commit_ids.py
+++ b/dev/breeze/src/airflow_breeze/pre_commit_ids.py
@@ -76,6 +76,7 @@ PRE_COMMIT_LIST = [
"check-start-date-not-used-in-defaults",
"check-system-tests-present",
"check-system-tests-tocs",
+ "check-template-context-variable-in-sync",
"check-tests-in-the-right-folders",
"check-tests-unittest-testcase",
"check-urlparse-usage-in-code",
diff --git a/docs/apache-airflow/authoring-and-scheduling/datasets.rst
b/docs/apache-airflow/authoring-and-scheduling/datasets.rst
index 1102420dd4..5324a11bbc 100644
--- a/docs/apache-airflow/authoring-and-scheduling/datasets.rst
+++ b/docs/apache-airflow/authoring-and-scheduling/datasets.rst
@@ -99,8 +99,8 @@ The identifier does not have to be absolute; it can be a
scheme-less, relative U
Non-absolute identifiers are considered plain strings that do not carry any
semantic meanings to Airflow.
-Extra information
------------------
+Extra information on Dataset
+----------------------------
If needed, an extra dictionary can be included in a Dataset:
@@ -111,7 +111,7 @@ If needed, an extra dictionary can be included in a Dataset:
extra={"team": "trainees"},
)
-This extra information does not affect a dataset's identity. This means a DAG
will be triggered by a dataset with an identical URI, even if the extra dict is
different:
+This can be used to supply custom description to the dataset, such as who has
ownership to the target file, or what the file is for. The extra information
does not affect a dataset's identity. This means a DAG will be triggered by a
dataset with an identical URI, even if the extra dict is different:
.. code-block:: python
@@ -224,6 +224,29 @@ If one dataset is updated multiple times before all
consumed datasets have been
}
+Attaching extra information to an emitting Dataset Event
+--------------------------------------------------------
+
+.. versionadded:: 2.10.0
+
+A task with a dataset outlet can optionally attach extra information before it
emits a dataset event. This is different
+from `Extra information on Dataset`_. Extra information on a dataset
statically describes the entity pointed to by the dataset URI; extra
information on the *dataset event* instead should be used to annotate the
triggering data change, such as how many rows in the database are changed by
the update, or the date range covered by it.
+
+The easiest way to attach extra information to the dataset event is by
accessing ``dataset_events`` in a task's execution context:
+
+.. code-block:: python
+
+ example_s3_dataset = Dataset("s3://dataset/example.csv")
+
+
+ @task(outlets=[example_s3_dataset])
+ def write_to_s3(*, dataset_events):
+ df = ... # Get a Pandas DataFrame to write.
+ # Write df to dataset...
+ dataset_events[example_s3_dataset].extras = {"row_count": len(df)}
+
+This can also be done in classic operators by either subclassing the operator
and overriding ``execute``, or by supplying a pre- or post-execution function.
+
Fetching information from a Triggering Dataset Event
----------------------------------------------------
@@ -234,7 +257,7 @@ Example:
.. code-block:: python
- example_snowflake_dataset = Dataset("snowflake://my_db.my_schema.my_table")
+ example_snowflake_dataset = Dataset("snowflake://my_db/my_schema/my_table")
with DAG(dag_id="load_snowflake_data", schedule="@hourly", ...):
SQLExecuteQueryOperator(
diff --git a/docs/apache-airflow/templates-ref.rst
b/docs/apache-airflow/templates-ref.rst
index dd05fcc831..4d3014268b 100644
--- a/docs/apache-airflow/templates-ref.rst
+++ b/docs/apache-airflow/templates-ref.rst
@@ -74,6 +74,8 @@ Variable Type
Description
``{{ var.value }}`` Airflow
variables. See `Airflow Variables in Templates`_ below.
``{{ var.json }}`` Airflow
variables. See `Airflow Variables in Templates`_ below.
``{{ conn }}`` Airflow
connections. See `Airflow Connections in Templates`_ below.
+``{{ dataset_events }}`` dict[str, ...] | Accessors
to attach information to dataset events that will be emitted by the current
task.
+ | See
:doc:`Datasets <authoring-and-scheduling/datasets>`. Added in version 2.10.
``{{ task_instance_key_str }}`` str | A unique,
human-readable key to the task instance. The format is
|
``{dag_id}__{task_id}__{ds_nodash}``.
``{{ conf }}`` AirflowConfigParser | The full
configuration object representing the content of your
diff --git a/scripts/ci/pre_commit/pre_commit_template_context_key_sync.py
b/scripts/ci/pre_commit/pre_commit_template_context_key_sync.py
old mode 100644
new mode 100755
diff --git a/tests/models/test_taskinstance.py
b/tests/models/test_taskinstance.py
index 120856dbbf..e618731142 100644
--- a/tests/models/test_taskinstance.py
+++ b/tests/models/test_taskinstance.py
@@ -35,6 +35,7 @@ from uuid import uuid4
import pendulum
import pytest
import time_machine
+from sqlalchemy import select
from airflow import settings
from airflow.decorators import task, task_group
@@ -2281,7 +2282,7 @@ class TestTaskInstance:
task_instance.run()
assert task_instance.current_state() ==
TaskInstanceState.SUCCESS
- def test_outlet_datasets_skipped(self, create_task_instance):
+ def test_outlet_datasets_skipped(self):
"""
Verify that when we have an outlet dataset on a task, and the task
is skipped, a DatasetDagRunQueue is not logged, and a DatasetEvent is
@@ -2311,7 +2312,69 @@ class TestTaskInstance:
# check that no dataset events were generated
assert session.query(DatasetEvent).count() == 0
- def test_changing_of_dataset_when_ddrq_is_already_populated(self,
dag_maker, session):
+ def test_outlet_dataset_extra(self, dag_maker, session):
+ from airflow.datasets import Dataset
+
+ with dag_maker(schedule=None, session=session) as dag:
+
+ @task(outlets=Dataset("test_outlet_dataset_extra_1"))
+ def write1(*, dataset_events):
+ dataset_events["test_outlet_dataset_extra_1"].extra = {"foo":
"bar"}
+
+ write1()
+
+ def _write2_post_execute(context, _):
+ context["dataset_events"]["test_outlet_dataset_extra_2"].extra
= {"x": 1}
+
+ BashOperator(
+ task_id="write2",
+ bash_command=":",
+ outlets=Dataset("test_outlet_dataset_extra_2"),
+ post_execute=_write2_post_execute,
+ )
+
+ dr: DagRun = dag_maker.create_dagrun()
+ for ti in dr.get_task_instances(session=session):
+ ti.refresh_from_task(dag.get_task(ti.task_id))
+ ti.run(session=session)
+
+ events = dict(iter(session.execute(select(DatasetEvent.source_task_id,
DatasetEvent))))
+ assert set(events) == {"write1", "write2"}
+
+ assert events["write1"].source_dag_id == dr.dag_id
+ assert events["write1"].source_run_id == dr.run_id
+ assert events["write1"].source_task_id == "write1"
+ assert events["write1"].dataset.uri == "test_outlet_dataset_extra_1"
+ assert events["write1"].extra == {"foo": "bar"}
+
+ assert events["write2"].source_dag_id == dr.dag_id
+ assert events["write2"].source_run_id == dr.run_id
+ assert events["write2"].source_task_id == "write2"
+ assert events["write2"].dataset.uri == "test_outlet_dataset_extra_2"
+ assert events["write2"].extra == {"x": 1}
+
+ def test_outlet_dataset_extra_ignore_different(self, dag_maker, session):
+ from airflow.datasets import Dataset
+
+ with dag_maker(schedule=None, session=session):
+
+ @task(outlets=Dataset("test_outlet_dataset_extra"))
+ def write(*, dataset_events):
+ dataset_events["test_outlet_dataset_extra"].extra = {"one": 1}
+ dataset_events["different_uri"].extra = {"foo": "bar"} # Will
be silently dropped.
+
+ write()
+
+ dr: DagRun = dag_maker.create_dagrun()
+ dr.get_task_instance("write").run(session=session)
+
+ event = session.scalars(select(DatasetEvent)).one()
+ assert event.source_dag_id == dr.dag_id
+ assert event.source_run_id == dr.run_id
+ assert event.source_task_id == "write"
+ assert event.extra == {"one": 1}
+
+ def test_changing_of_dataset_when_ddrq_is_already_populated(self,
dag_maker):
"""
Test that when a task that produces dataset has ran, that changing the
consumer
dag dataset will not cause primary key blank-out
diff --git a/tests/operators/test_python.py b/tests/operators/test_python.py
index 578302a836..b8876f97ec 100644
--- a/tests/operators/test_python.py
+++ b/tests/operators/test_python.py
@@ -834,6 +834,7 @@ class BaseTestPythonVirtualenvOperator(BasePythonTest):
"ti",
"var", # Accessor for Variable; var->json and var->value.
"conn", # Accessor for Connection.
+ "dataset_events", # Accessor for DatasetEvent.
]
ti = create_task_instance(dag_id=self.dag_id, task_id=self.task_id,
schedule=None)