From c161e4fb12dcb67fdea253181d87af9342bfda6a Mon Sep 17 00:00:00 2001 From: Wei Lee Date: Mon, 27 Jan 2025 23:18:18 +0800 Subject: [PATCH] [`airflow`] Extend airflow context parameter check for `BaseOperator.execute` (`AIR302`) (#15713) ## Summary * feat * add is_execute_method_inherits_from_airflow_operator for checking the removed context key in the execute method * refactor: rename * is_airflow_task as is_airflow_task_function_def * in_airflow_task as in_airflow_task_function_def * removed_in_3 as airflow_3_removal_expr * removed_in_3_function_def as airflow_3_removal_function_def * test: * reorganize test cases ## Test Plan a test fixture has been updated --------- Co-authored-by: Dhruv Manilawala --- .../test/fixtures/airflow/AIR302_context.py | 133 +++-- .../src/checkers/ast/analyze/expression.rs | 10 +- .../src/checkers/ast/analyze/statement.rs | 2 +- .../src/rules/airflow/rules/removal_in_3.rs | 53 +- ...flow__tests__AIR302_AIR302_context.py.snap | 529 +++++++++--------- 5 files changed, 404 insertions(+), 323 deletions(-) diff --git a/crates/ruff_linter/resources/test/fixtures/airflow/AIR302_context.py b/crates/ruff_linter/resources/test/fixtures/airflow/AIR302_context.py index 2d2764f5dd..2714eb84bc 100644 --- a/crates/ruff_linter/resources/test/fixtures/airflow/AIR302_context.py +++ b/crates/ruff_linter/resources/test/fixtures/airflow/AIR302_context.py @@ -1,7 +1,8 @@ +from __future__ import annotations + from datetime import datetime import pendulum - from airflow.decorators import dag, task from airflow.models import DAG from airflow.models.baseoperator import BaseOperator @@ -13,30 +14,22 @@ from airflow.utils.context import get_current_context def access_invalid_key_in_context(**context): print("access invalid key", context["conf"]) + print("access invalid key", context.get("conf")) + @task def access_invalid_key_task_out_of_dag(**context): + print("access invalid key", context["conf"]) print("access invalid key", context.get("conf")) -@dag( - schedule=None, - start_date=pendulum.datetime(2021, 1, 1, tz="UTC"), - catchup=False, - tags=[""], -) -def invalid_dag(): - @task() - def access_invalid_key_task(**context): - print("access invalid key", context.get("conf")) - task1 = PythonOperator( - task_id="task1", - python_callable=access_invalid_key_in_context, - ) - access_invalid_key_task() >> task1 - access_invalid_key_task_out_of_dag() +@task +def access_invalid_argument_task_out_of_dag( + execution_date, tomorrow_ds, logical_date, **context +): + print("execution date", execution_date) + print("access invalid key", context.get("conf")) -invalid_dag() @task def print_config(**context): @@ -56,31 +49,9 @@ def print_config(**context): yesterday_ds = context["yesterday_ds"] yesterday_ds_nodash = context["yesterday_ds_nodash"] -with DAG( - dag_id="example_dag", - schedule_interval="@daily", - start_date=datetime(2023, 1, 1), - template_searchpath=["/templates"], -) as dag: - task1 = DummyOperator( - task_id="task1", - params={ - # Removed variables in template - "execution_date": "{{ execution_date }}", - "next_ds": "{{ next_ds }}", - "prev_ds": "{{ prev_ds }}" - }, - ) - -class CustomMacrosPlugin(AirflowPlugin): - name = "custom_macros" - macros = { - "execution_date_macro": lambda context: context["execution_date"], - "next_ds_macro": lambda context: context["next_ds"] - } @task -def print_config(): +def print_config_with_get_current_context(): context = get_current_context() execution_date = context["execution_date"] next_ds = context["next_ds"] @@ -94,8 +65,74 @@ def print_config(): yesterday_ds = context["yesterday_ds"] yesterday_ds_nodash = context["yesterday_ds_nodash"] + +@task(task_id="print_the_context") +def print_context(ds=None, **kwargs): + """Print the Airflow context and ds variable from the context.""" + print(ds) + print(kwargs.get("tomorrow_ds")) + c = get_current_context() + c.get("execution_date") + + +@dag( + schedule=None, + start_date=pendulum.datetime(2021, 1, 1, tz="UTC"), + catchup=False, + tags=[""], +) +def invalid_dag(): + @task() + def access_invalid_key_task(**context): + print("access invalid key", context.get("conf")) + + @task() + def access_invalid_key_explicit_task(execution_date): + print(execution_date) + + task1 = PythonOperator( + task_id="task1", + python_callable=access_invalid_key_in_context, + ) + + access_invalid_key_task() >> task1 + access_invalid_key_explicit_task() + access_invalid_argument_task_out_of_dag() + access_invalid_key_task_out_of_dag() + print_config() + print_config_with_get_current_context() + print_context() + + +invalid_dag() + +with DAG( + dag_id="example_dag", + schedule_interval="@daily", + start_date=datetime(2023, 1, 1), + template_searchpath=["/templates"], +) as dag: + task1 = DummyOperator( + task_id="task1", + params={ + # Removed variables in template + "execution_date": "{{ execution_date }}", + "next_ds": "{{ next_ds }}", + "prev_ds": "{{ prev_ds }}", + }, + ) + + +class CustomMacrosPlugin(AirflowPlugin): + name = "custom_macros" + macros = { + "execution_date_macro": lambda context: context["execution_date"], + "next_ds_macro": lambda context: context["next_ds"], + } + + class CustomOperator(BaseOperator): - def execute(self, context): + def execute(self, next_ds, context): execution_date = context["execution_date"] next_ds = context["next_ds"] next_ds_nodash = context["next_ds_nodash"] @@ -108,18 +145,6 @@ class CustomOperator(BaseOperator): yesterday_ds = context["yesterday_ds"] yesterday_ds_nodash = context["yesterday_ds_nodash"] -@task -def access_invalid_argument_task_out_of_dag(execution_date, tomorrow_ds, logical_date, **context): - print("execution date", execution_date) - print("access invalid key", context.get("conf")) - -@task(task_id="print_the_context") -def print_context(ds=None, **kwargs): - """Print the Airflow context and ds variable from the context.""" - print(ds) - print(kwargs.get("tomorrow_ds")) - c = get_current_context() - c.get("execution_date") class CustomOperatorNew(BaseOperator): def execute(self, context): diff --git a/crates/ruff_linter/src/checkers/ast/analyze/expression.rs b/crates/ruff_linter/src/checkers/ast/analyze/expression.rs index 74d877f91b..8601165cb7 100644 --- a/crates/ruff_linter/src/checkers/ast/analyze/expression.rs +++ b/crates/ruff_linter/src/checkers/ast/analyze/expression.rs @@ -176,7 +176,7 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) { pyupgrade::rules::use_pep646_unpack(checker, subscript); } if checker.enabled(Rule::Airflow3Removal) { - airflow::rules::removed_in_3(checker, expr); + airflow::rules::airflow_3_removal_expr(checker, expr); } pandas_vet::rules::subscript(checker, value, expr); } @@ -227,7 +227,7 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) { refurb::rules::regex_flag_alias(checker, expr); } if checker.enabled(Rule::Airflow3Removal) { - airflow::rules::removed_in_3(checker, expr); + airflow::rules::airflow_3_removal_expr(checker, expr); } if checker.enabled(Rule::Airflow3MovedToProvider) { airflow::rules::moved_to_provider_in_3(checker, expr); @@ -311,7 +311,7 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) { } } if checker.enabled(Rule::Airflow3Removal) { - airflow::rules::removed_in_3(checker, expr); + airflow::rules::airflow_3_removal_expr(checker, expr); } if checker.enabled(Rule::MixedCaseVariableInGlobalScope) { if matches!(checker.semantic.current_scope().kind, ScopeKind::Module) { @@ -449,7 +449,7 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) { flake8_pyi::rules::bytestring_attribute(checker, expr); } if checker.enabled(Rule::Airflow3Removal) { - airflow::rules::removed_in_3(checker, expr); + airflow::rules::airflow_3_removal_expr(checker, expr); } } Expr::Call( @@ -1150,7 +1150,7 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) { ruff::rules::unnecessary_regular_expression(checker, call); } if checker.enabled(Rule::Airflow3Removal) { - airflow::rules::removed_in_3(checker, expr); + airflow::rules::airflow_3_removal_expr(checker, expr); } if checker.enabled(Rule::UnnecessaryCastToInt) { ruff::rules::unnecessary_cast_to_int(checker, call); diff --git a/crates/ruff_linter/src/checkers/ast/analyze/statement.rs b/crates/ruff_linter/src/checkers/ast/analyze/statement.rs index dc8f69a9c5..bc9a0cd297 100644 --- a/crates/ruff_linter/src/checkers/ast/analyze/statement.rs +++ b/crates/ruff_linter/src/checkers/ast/analyze/statement.rs @@ -377,7 +377,7 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) { flake8_pytest_style::rules::parameter_with_default_argument(checker, function_def); } if checker.enabled(Rule::Airflow3Removal) { - airflow::rules::removed_in_3_function_def(checker, function_def); + airflow::rules::airflow_3_removal_function_def(checker, function_def); } if checker.enabled(Rule::NonPEP695GenericFunction) { pyupgrade::rules::non_pep695_generic_function(checker, function_def); diff --git a/crates/ruff_linter/src/rules/airflow/rules/removal_in_3.rs b/crates/ruff_linter/src/rules/airflow/rules/removal_in_3.rs index 45f2b2f069..7e0e11d065 100644 --- a/crates/ruff_linter/src/rules/airflow/rules/removal_in_3.rs +++ b/crates/ruff_linter/src/rules/airflow/rules/removal_in_3.rs @@ -80,7 +80,7 @@ enum Replacement { } /// AIR302 -pub(crate) fn removed_in_3(checker: &mut Checker, expr: &Expr) { +pub(crate) fn airflow_3_removal_expr(checker: &mut Checker, expr: &Expr) { if !checker.semantic().seen_module(Modules::AIRFLOW) { return; } @@ -117,7 +117,10 @@ pub(crate) fn removed_in_3(checker: &mut Checker, expr: &Expr) { } /// AIR302 -pub(crate) fn removed_in_3_function_def(checker: &mut Checker, function_def: &StmtFunctionDef) { +pub(crate) fn airflow_3_removal_function_def( + checker: &mut Checker, + function_def: &StmtFunctionDef, +) { if !checker.semantic().seen_module(Modules::AIRFLOW) { return; } @@ -154,7 +157,9 @@ const REMOVED_CONTEXT_KEYS: [&str; 12] = [ /// pass /// ``` fn check_function_parameters(checker: &mut Checker, function_def: &StmtFunctionDef) { - if !is_airflow_task(function_def, checker.semantic()) { + if !is_airflow_task_function_def(function_def, checker.semantic()) + && !is_execute_method_inherits_from_airflow_operator(function_def, checker.semantic()) + { return; } @@ -346,7 +351,7 @@ fn check_class_attribute(checker: &mut Checker, attribute_expr: &ExprAttribute) /// context.get("conf") # 'conf' is removed in Airflow 3.0 /// ``` fn check_context_key_usage_in_call(checker: &mut Checker, call_expr: &ExprCall) { - if !in_airflow_task_function(checker.semantic()) { + if !in_airflow_task_function_def(checker.semantic()) { return; } @@ -395,7 +400,7 @@ fn check_context_key_usage_in_call(checker: &mut Checker, call_expr: &ExprCall) /// Check if a subscript expression accesses a removed Airflow context variable. /// If a removed key is found, push a corresponding diagnostic. fn check_context_key_usage_in_subscript(checker: &mut Checker, subscript: &ExprSubscript) { - if !in_airflow_task_function(checker.semantic()) { + if !in_airflow_task_function_def(checker.semantic()) { return; } @@ -1059,15 +1064,15 @@ fn is_airflow_builtin_or_provider(segments: &[&str], module: &str, symbol_suffix /// Returns `true` if the current statement hierarchy has a function that's decorated with /// `@airflow.decorators.task`. -fn in_airflow_task_function(semantic: &SemanticModel) -> bool { +fn in_airflow_task_function_def(semantic: &SemanticModel) -> bool { semantic .current_statements() .find_map(|stmt| stmt.as_function_def_stmt()) - .is_some_and(|function_def| is_airflow_task(function_def, semantic)) + .is_some_and(|function_def| is_airflow_task_function_def(function_def, semantic)) } /// Returns `true` if the given function is decorated with `@airflow.decorators.task`. -fn is_airflow_task(function_def: &StmtFunctionDef, semantic: &SemanticModel) -> bool { +fn is_airflow_task_function_def(function_def: &StmtFunctionDef, semantic: &SemanticModel) -> bool { function_def.decorator_list.iter().any(|decorator| { semantic .resolve_qualified_name(map_callable(&decorator.expression)) @@ -1076,3 +1081,35 @@ fn is_airflow_task(function_def: &StmtFunctionDef, semantic: &SemanticModel) -> }) }) } + +/// Check it's "execute" method inherits from Airflow base operator +/// +/// For example: +/// +/// ```python +/// from airflow.models.baseoperator import BaseOperator +/// +/// class CustomOperator(BaseOperator): +/// def execute(self): +/// pass +/// ``` +fn is_execute_method_inherits_from_airflow_operator( + function_def: &StmtFunctionDef, + semantic: &SemanticModel, +) -> bool { + if function_def.name.as_str() != "execute" { + return false; + } + + let ScopeKind::Class(class_def) = semantic.current_scope().kind else { + return false; + }; + + class_def.bases().iter().any(|class_base| { + semantic + .resolve_qualified_name(class_base) + .is_some_and(|qualified_name| { + matches!(qualified_name.segments(), ["airflow", .., "BaseOperator"]) + }) + }) +} diff --git a/crates/ruff_linter/src/rules/airflow/snapshots/ruff_linter__rules__airflow__tests__AIR302_AIR302_context.py.snap b/crates/ruff_linter/src/rules/airflow/snapshots/ruff_linter__rules__airflow__tests__AIR302_AIR302_context.py.snap index e410de404e..84332e02b0 100644 --- a/crates/ruff_linter/src/rules/airflow/snapshots/ruff_linter__rules__airflow__tests__AIR302_AIR302_context.py.snap +++ b/crates/ruff_linter/src/rules/airflow/snapshots/ruff_linter__rules__airflow__tests__AIR302_AIR302_context.py.snap @@ -1,319 +1,338 @@ --- source: crates/ruff_linter/src/rules/airflow/mod.rs +snapshot_kind: text --- -AIR302_context.py:19:45: AIR302 `conf` is removed in Airflow 3.0 +AIR302_context.py:22:41: AIR302 `conf` is removed in Airflow 3.0 | -17 | @task -18 | def access_invalid_key_task_out_of_dag(**context): -19 | print("access invalid key", context.get("conf")) +20 | @task +21 | def access_invalid_key_task_out_of_dag(**context): +22 | print("access invalid key", context["conf"]) + | ^^^^^^ AIR302 +23 | print("access invalid key", context.get("conf")) + | + +AIR302_context.py:23:45: AIR302 `conf` is removed in Airflow 3.0 + | +21 | def access_invalid_key_task_out_of_dag(**context): +22 | print("access invalid key", context["conf"]) +23 | print("access invalid key", context.get("conf")) | ^^^^^^ AIR302 -20 | -21 | @dag( | -AIR302_context.py:30:49: AIR302 `conf` is removed in Airflow 3.0 +AIR302_context.py:28:5: AIR302 `execution_date` is removed in Airflow 3.0 | -28 | @task() -29 | def access_invalid_key_task(**context): -30 | print("access invalid key", context.get("conf")) - | ^^^^^^ AIR302 -31 | -32 | task1 = PythonOperator( +26 | @task +27 | def access_invalid_argument_task_out_of_dag( +28 | execution_date, tomorrow_ds, logical_date, **context + | ^^^^^^^^^^^^^^ AIR302 +29 | ): +30 | print("execution date", execution_date) | -AIR302_context.py:47:30: AIR302 `execution_date` is removed in Airflow 3.0 +AIR302_context.py:28:21: AIR302 `tomorrow_ds` is removed in Airflow 3.0 | -46 | # Removed usage - should trigger violations -47 | execution_date = context["execution_date"] +26 | @task +27 | def access_invalid_argument_task_out_of_dag( +28 | execution_date, tomorrow_ds, logical_date, **context + | ^^^^^^^^^^^ AIR302 +29 | ): +30 | print("execution date", execution_date) + | + +AIR302_context.py:31:45: AIR302 `conf` is removed in Airflow 3.0 + | +29 | ): +30 | print("execution date", execution_date) +31 | print("access invalid key", context.get("conf")) + | ^^^^^^ AIR302 + | + +AIR302_context.py:40:30: AIR302 `execution_date` is removed in Airflow 3.0 + | +39 | # Removed usage - should trigger violations +40 | execution_date = context["execution_date"] | ^^^^^^^^^^^^^^^^ AIR302 -48 | next_ds = context["next_ds"] -49 | next_ds_nodash = context["next_ds_nodash"] +41 | next_ds = context["next_ds"] +42 | next_ds_nodash = context["next_ds_nodash"] | -AIR302_context.py:48:23: AIR302 `next_ds` is removed in Airflow 3.0 +AIR302_context.py:41:23: AIR302 `next_ds` is removed in Airflow 3.0 | -46 | # Removed usage - should trigger violations -47 | execution_date = context["execution_date"] -48 | next_ds = context["next_ds"] +39 | # Removed usage - should trigger violations +40 | execution_date = context["execution_date"] +41 | next_ds = context["next_ds"] | ^^^^^^^^^ AIR302 -49 | next_ds_nodash = context["next_ds_nodash"] -50 | next_execution_date = context["next_execution_date"] +42 | next_ds_nodash = context["next_ds_nodash"] +43 | next_execution_date = context["next_execution_date"] | -AIR302_context.py:49:30: AIR302 `next_ds_nodash` is removed in Airflow 3.0 +AIR302_context.py:42:30: AIR302 `next_ds_nodash` is removed in Airflow 3.0 | -47 | execution_date = context["execution_date"] -48 | next_ds = context["next_ds"] -49 | next_ds_nodash = context["next_ds_nodash"] +40 | execution_date = context["execution_date"] +41 | next_ds = context["next_ds"] +42 | next_ds_nodash = context["next_ds_nodash"] | ^^^^^^^^^^^^^^^^ AIR302 -50 | next_execution_date = context["next_execution_date"] -51 | prev_ds = context["prev_ds"] +43 | next_execution_date = context["next_execution_date"] +44 | prev_ds = context["prev_ds"] | -AIR302_context.py:50:35: AIR302 `next_execution_date` is removed in Airflow 3.0 +AIR302_context.py:43:35: AIR302 `next_execution_date` is removed in Airflow 3.0 | -48 | next_ds = context["next_ds"] -49 | next_ds_nodash = context["next_ds_nodash"] -50 | next_execution_date = context["next_execution_date"] +41 | next_ds = context["next_ds"] +42 | next_ds_nodash = context["next_ds_nodash"] +43 | next_execution_date = context["next_execution_date"] | ^^^^^^^^^^^^^^^^^^^^^ AIR302 -51 | prev_ds = context["prev_ds"] -52 | prev_ds_nodash = context["prev_ds_nodash"] +44 | prev_ds = context["prev_ds"] +45 | prev_ds_nodash = context["prev_ds_nodash"] | -AIR302_context.py:51:23: AIR302 `prev_ds` is removed in Airflow 3.0 +AIR302_context.py:44:23: AIR302 `prev_ds` is removed in Airflow 3.0 | -49 | next_ds_nodash = context["next_ds_nodash"] -50 | next_execution_date = context["next_execution_date"] -51 | prev_ds = context["prev_ds"] +42 | next_ds_nodash = context["next_ds_nodash"] +43 | next_execution_date = context["next_execution_date"] +44 | prev_ds = context["prev_ds"] | ^^^^^^^^^ AIR302 -52 | prev_ds_nodash = context["prev_ds_nodash"] -53 | prev_execution_date = context["prev_execution_date"] +45 | prev_ds_nodash = context["prev_ds_nodash"] +46 | prev_execution_date = context["prev_execution_date"] | -AIR302_context.py:52:30: AIR302 `prev_ds_nodash` is removed in Airflow 3.0 +AIR302_context.py:45:30: AIR302 `prev_ds_nodash` is removed in Airflow 3.0 | -50 | next_execution_date = context["next_execution_date"] -51 | prev_ds = context["prev_ds"] -52 | prev_ds_nodash = context["prev_ds_nodash"] +43 | next_execution_date = context["next_execution_date"] +44 | prev_ds = context["prev_ds"] +45 | prev_ds_nodash = context["prev_ds_nodash"] | ^^^^^^^^^^^^^^^^ AIR302 -53 | prev_execution_date = context["prev_execution_date"] -54 | prev_execution_date_success = context["prev_execution_date_success"] +46 | prev_execution_date = context["prev_execution_date"] +47 | prev_execution_date_success = context["prev_execution_date_success"] | -AIR302_context.py:53:35: AIR302 `prev_execution_date` is removed in Airflow 3.0 +AIR302_context.py:46:35: AIR302 `prev_execution_date` is removed in Airflow 3.0 | -51 | prev_ds = context["prev_ds"] -52 | prev_ds_nodash = context["prev_ds_nodash"] -53 | prev_execution_date = context["prev_execution_date"] +44 | prev_ds = context["prev_ds"] +45 | prev_ds_nodash = context["prev_ds_nodash"] +46 | prev_execution_date = context["prev_execution_date"] | ^^^^^^^^^^^^^^^^^^^^^ AIR302 -54 | prev_execution_date_success = context["prev_execution_date_success"] -55 | tomorrow_ds = context["tomorrow_ds"] +47 | prev_execution_date_success = context["prev_execution_date_success"] +48 | tomorrow_ds = context["tomorrow_ds"] | -AIR302_context.py:54:43: AIR302 `prev_execution_date_success` is removed in Airflow 3.0 +AIR302_context.py:47:43: AIR302 `prev_execution_date_success` is removed in Airflow 3.0 | -52 | prev_ds_nodash = context["prev_ds_nodash"] -53 | prev_execution_date = context["prev_execution_date"] -54 | prev_execution_date_success = context["prev_execution_date_success"] +45 | prev_ds_nodash = context["prev_ds_nodash"] +46 | prev_execution_date = context["prev_execution_date"] +47 | prev_execution_date_success = context["prev_execution_date_success"] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ AIR302 -55 | tomorrow_ds = context["tomorrow_ds"] -56 | yesterday_ds = context["yesterday_ds"] +48 | tomorrow_ds = context["tomorrow_ds"] +49 | yesterday_ds = context["yesterday_ds"] | -AIR302_context.py:55:27: AIR302 `tomorrow_ds` is removed in Airflow 3.0 +AIR302_context.py:48:27: AIR302 `tomorrow_ds` is removed in Airflow 3.0 | -53 | prev_execution_date = context["prev_execution_date"] -54 | prev_execution_date_success = context["prev_execution_date_success"] -55 | tomorrow_ds = context["tomorrow_ds"] +46 | prev_execution_date = context["prev_execution_date"] +47 | prev_execution_date_success = context["prev_execution_date_success"] +48 | tomorrow_ds = context["tomorrow_ds"] | ^^^^^^^^^^^^^ AIR302 -56 | yesterday_ds = context["yesterday_ds"] -57 | yesterday_ds_nodash = context["yesterday_ds_nodash"] +49 | yesterday_ds = context["yesterday_ds"] +50 | yesterday_ds_nodash = context["yesterday_ds_nodash"] | -AIR302_context.py:56:28: AIR302 `yesterday_ds` is removed in Airflow 3.0 +AIR302_context.py:49:28: AIR302 `yesterday_ds` is removed in Airflow 3.0 | -54 | prev_execution_date_success = context["prev_execution_date_success"] -55 | tomorrow_ds = context["tomorrow_ds"] -56 | yesterday_ds = context["yesterday_ds"] +47 | prev_execution_date_success = context["prev_execution_date_success"] +48 | tomorrow_ds = context["tomorrow_ds"] +49 | yesterday_ds = context["yesterday_ds"] | ^^^^^^^^^^^^^^ AIR302 -57 | yesterday_ds_nodash = context["yesterday_ds_nodash"] +50 | yesterday_ds_nodash = context["yesterday_ds_nodash"] | -AIR302_context.py:57:35: AIR302 `yesterday_ds_nodash` is removed in Airflow 3.0 +AIR302_context.py:50:35: AIR302 `yesterday_ds_nodash` is removed in Airflow 3.0 | -55 | tomorrow_ds = context["tomorrow_ds"] -56 | yesterday_ds = context["yesterday_ds"] -57 | yesterday_ds_nodash = context["yesterday_ds_nodash"] +48 | tomorrow_ds = context["tomorrow_ds"] +49 | yesterday_ds = context["yesterday_ds"] +50 | yesterday_ds_nodash = context["yesterday_ds_nodash"] | ^^^^^^^^^^^^^^^^^^^^^ AIR302 -58 | -59 | with DAG( | -AIR302_context.py:61:5: AIR302 [*] `schedule_interval` is removed in Airflow 3.0 +AIR302_context.py:56:30: AIR302 `execution_date` is removed in Airflow 3.0 | -59 | with DAG( -60 | dag_id="example_dag", -61 | schedule_interval="@daily", - | ^^^^^^^^^^^^^^^^^ AIR302 -62 | start_date=datetime(2023, 1, 1), -63 | template_searchpath=["/templates"], +54 | def print_config_with_get_current_context(): +55 | context = get_current_context() +56 | execution_date = context["execution_date"] + | ^^^^^^^^^^^^^^^^ AIR302 +57 | next_ds = context["next_ds"] +58 | next_ds_nodash = context["next_ds_nodash"] | - = help: Use `schedule` instead + +AIR302_context.py:57:23: AIR302 `next_ds` is removed in Airflow 3.0 + | +55 | context = get_current_context() +56 | execution_date = context["execution_date"] +57 | next_ds = context["next_ds"] + | ^^^^^^^^^ AIR302 +58 | next_ds_nodash = context["next_ds_nodash"] +59 | next_execution_date = context["next_execution_date"] + | + +AIR302_context.py:58:30: AIR302 `next_ds_nodash` is removed in Airflow 3.0 + | +56 | execution_date = context["execution_date"] +57 | next_ds = context["next_ds"] +58 | next_ds_nodash = context["next_ds_nodash"] + | ^^^^^^^^^^^^^^^^ AIR302 +59 | next_execution_date = context["next_execution_date"] +60 | prev_ds = context["prev_ds"] + | + +AIR302_context.py:59:35: AIR302 `next_execution_date` is removed in Airflow 3.0 + | +57 | next_ds = context["next_ds"] +58 | next_ds_nodash = context["next_ds_nodash"] +59 | next_execution_date = context["next_execution_date"] + | ^^^^^^^^^^^^^^^^^^^^^ AIR302 +60 | prev_ds = context["prev_ds"] +61 | prev_ds_nodash = context["prev_ds_nodash"] + | + +AIR302_context.py:60:23: AIR302 `prev_ds` is removed in Airflow 3.0 + | +58 | next_ds_nodash = context["next_ds_nodash"] +59 | next_execution_date = context["next_execution_date"] +60 | prev_ds = context["prev_ds"] + | ^^^^^^^^^ AIR302 +61 | prev_ds_nodash = context["prev_ds_nodash"] +62 | prev_execution_date = context["prev_execution_date"] + | + +AIR302_context.py:61:30: AIR302 `prev_ds_nodash` is removed in Airflow 3.0 + | +59 | next_execution_date = context["next_execution_date"] +60 | prev_ds = context["prev_ds"] +61 | prev_ds_nodash = context["prev_ds_nodash"] + | ^^^^^^^^^^^^^^^^ AIR302 +62 | prev_execution_date = context["prev_execution_date"] +63 | prev_execution_date_success = context["prev_execution_date_success"] + | + +AIR302_context.py:62:35: AIR302 `prev_execution_date` is removed in Airflow 3.0 + | +60 | prev_ds = context["prev_ds"] +61 | prev_ds_nodash = context["prev_ds_nodash"] +62 | prev_execution_date = context["prev_execution_date"] + | ^^^^^^^^^^^^^^^^^^^^^ AIR302 +63 | prev_execution_date_success = context["prev_execution_date_success"] +64 | tomorrow_ds = context["tomorrow_ds"] + | + +AIR302_context.py:63:43: AIR302 `prev_execution_date_success` is removed in Airflow 3.0 + | +61 | prev_ds_nodash = context["prev_ds_nodash"] +62 | prev_execution_date = context["prev_execution_date"] +63 | prev_execution_date_success = context["prev_execution_date_success"] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ AIR302 +64 | tomorrow_ds = context["tomorrow_ds"] +65 | yesterday_ds = context["yesterday_ds"] + | + +AIR302_context.py:64:27: AIR302 `tomorrow_ds` is removed in Airflow 3.0 + | +62 | prev_execution_date = context["prev_execution_date"] +63 | prev_execution_date_success = context["prev_execution_date_success"] +64 | tomorrow_ds = context["tomorrow_ds"] + | ^^^^^^^^^^^^^ AIR302 +65 | yesterday_ds = context["yesterday_ds"] +66 | yesterday_ds_nodash = context["yesterday_ds_nodash"] + | + +AIR302_context.py:65:28: AIR302 `yesterday_ds` is removed in Airflow 3.0 + | +63 | prev_execution_date_success = context["prev_execution_date_success"] +64 | tomorrow_ds = context["tomorrow_ds"] +65 | yesterday_ds = context["yesterday_ds"] + | ^^^^^^^^^^^^^^ AIR302 +66 | yesterday_ds_nodash = context["yesterday_ds_nodash"] + | + +AIR302_context.py:66:35: AIR302 `yesterday_ds_nodash` is removed in Airflow 3.0 + | +64 | tomorrow_ds = context["tomorrow_ds"] +65 | yesterday_ds = context["yesterday_ds"] +66 | yesterday_ds_nodash = context["yesterday_ds_nodash"] + | ^^^^^^^^^^^^^^^^^^^^^ AIR302 + | + +AIR302_context.py:73:22: AIR302 `tomorrow_ds` is removed in Airflow 3.0 + | +71 | """Print the Airflow context and ds variable from the context.""" +72 | print(ds) +73 | print(kwargs.get("tomorrow_ds")) + | ^^^^^^^^^^^^^ AIR302 +74 | c = get_current_context() +75 | c.get("execution_date") + | + +AIR302_context.py:75:11: AIR302 `execution_date` is removed in Airflow 3.0 + | +73 | print(kwargs.get("tomorrow_ds")) +74 | c = get_current_context() +75 | c.get("execution_date") + | ^^^^^^^^^^^^^^^^ AIR302 + | + +AIR302_context.py:87:49: AIR302 `conf` is removed in Airflow 3.0 + | +85 | @task() +86 | def access_invalid_key_task(**context): +87 | print("access invalid key", context.get("conf")) + | ^^^^^^ AIR302 +88 | +89 | @task() + | + +AIR302_context.py:90:42: AIR302 `execution_date` is removed in Airflow 3.0 + | +89 | @task() +90 | def access_invalid_key_explicit_task(execution_date): + | ^^^^^^^^^^^^^^ AIR302 +91 | print(execution_date) + | + +AIR302_context.py:111:5: AIR302 [*] `schedule_interval` is removed in Airflow 3.0 + | +109 | with DAG( +110 | dag_id="example_dag", +111 | schedule_interval="@daily", + | ^^^^^^^^^^^^^^^^^ AIR302 +112 | start_date=datetime(2023, 1, 1), +113 | template_searchpath=["/templates"], + | + = help: Use `schedule` instead ℹ Safe fix -58 58 | -59 59 | with DAG( -60 60 | dag_id="example_dag", -61 |- schedule_interval="@daily", - 61 |+ schedule="@daily", -62 62 | start_date=datetime(2023, 1, 1), -63 63 | template_searchpath=["/templates"], -64 64 | ) as dag: +108 108 | +109 109 | with DAG( +110 110 | dag_id="example_dag", +111 |- schedule_interval="@daily", + 111 |+ schedule="@daily", +112 112 | start_date=datetime(2023, 1, 1), +113 113 | template_searchpath=["/templates"], +114 114 | ) as dag: -AIR302_context.py:65:13: AIR302 `airflow.operators.dummy.DummyOperator` is removed in Airflow 3.0 - | -63 | template_searchpath=["/templates"], -64 | ) as dag: -65 | task1 = DummyOperator( - | ^^^^^^^^^^^^^ AIR302 -66 | task_id="task1", -67 | params={ - | - = help: Use `airflow.operators.empty.EmptyOperator` instead - -AIR302_context.py:85:30: AIR302 `execution_date` is removed in Airflow 3.0 - | -83 | def print_config(): -84 | context = get_current_context() -85 | execution_date = context["execution_date"] - | ^^^^^^^^^^^^^^^^ AIR302 -86 | next_ds = context["next_ds"] -87 | next_ds_nodash = context["next_ds_nodash"] - | - -AIR302_context.py:86:23: AIR302 `next_ds` is removed in Airflow 3.0 - | -84 | context = get_current_context() -85 | execution_date = context["execution_date"] -86 | next_ds = context["next_ds"] - | ^^^^^^^^^ AIR302 -87 | next_ds_nodash = context["next_ds_nodash"] -88 | next_execution_date = context["next_execution_date"] - | - -AIR302_context.py:87:30: AIR302 `next_ds_nodash` is removed in Airflow 3.0 - | -85 | execution_date = context["execution_date"] -86 | next_ds = context["next_ds"] -87 | next_ds_nodash = context["next_ds_nodash"] - | ^^^^^^^^^^^^^^^^ AIR302 -88 | next_execution_date = context["next_execution_date"] -89 | prev_ds = context["prev_ds"] - | - -AIR302_context.py:88:35: AIR302 `next_execution_date` is removed in Airflow 3.0 - | -86 | next_ds = context["next_ds"] -87 | next_ds_nodash = context["next_ds_nodash"] -88 | next_execution_date = context["next_execution_date"] - | ^^^^^^^^^^^^^^^^^^^^^ AIR302 -89 | prev_ds = context["prev_ds"] -90 | prev_ds_nodash = context["prev_ds_nodash"] - | - -AIR302_context.py:89:23: AIR302 `prev_ds` is removed in Airflow 3.0 - | -87 | next_ds_nodash = context["next_ds_nodash"] -88 | next_execution_date = context["next_execution_date"] -89 | prev_ds = context["prev_ds"] - | ^^^^^^^^^ AIR302 -90 | prev_ds_nodash = context["prev_ds_nodash"] -91 | prev_execution_date = context["prev_execution_date"] - | - -AIR302_context.py:90:30: AIR302 `prev_ds_nodash` is removed in Airflow 3.0 - | -88 | next_execution_date = context["next_execution_date"] -89 | prev_ds = context["prev_ds"] -90 | prev_ds_nodash = context["prev_ds_nodash"] - | ^^^^^^^^^^^^^^^^ AIR302 -91 | prev_execution_date = context["prev_execution_date"] -92 | prev_execution_date_success = context["prev_execution_date_success"] - | - -AIR302_context.py:91:35: AIR302 `prev_execution_date` is removed in Airflow 3.0 - | -89 | prev_ds = context["prev_ds"] -90 | prev_ds_nodash = context["prev_ds_nodash"] -91 | prev_execution_date = context["prev_execution_date"] - | ^^^^^^^^^^^^^^^^^^^^^ AIR302 -92 | prev_execution_date_success = context["prev_execution_date_success"] -93 | tomorrow_ds = context["tomorrow_ds"] - | - -AIR302_context.py:92:43: AIR302 `prev_execution_date_success` is removed in Airflow 3.0 - | -90 | prev_ds_nodash = context["prev_ds_nodash"] -91 | prev_execution_date = context["prev_execution_date"] -92 | prev_execution_date_success = context["prev_execution_date_success"] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ AIR302 -93 | tomorrow_ds = context["tomorrow_ds"] -94 | yesterday_ds = context["yesterday_ds"] - | - -AIR302_context.py:93:27: AIR302 `tomorrow_ds` is removed in Airflow 3.0 - | -91 | prev_execution_date = context["prev_execution_date"] -92 | prev_execution_date_success = context["prev_execution_date_success"] -93 | tomorrow_ds = context["tomorrow_ds"] - | ^^^^^^^^^^^^^ AIR302 -94 | yesterday_ds = context["yesterday_ds"] -95 | yesterday_ds_nodash = context["yesterday_ds_nodash"] - | - -AIR302_context.py:94:28: AIR302 `yesterday_ds` is removed in Airflow 3.0 - | -92 | prev_execution_date_success = context["prev_execution_date_success"] -93 | tomorrow_ds = context["tomorrow_ds"] -94 | yesterday_ds = context["yesterday_ds"] - | ^^^^^^^^^^^^^^ AIR302 -95 | yesterday_ds_nodash = context["yesterday_ds_nodash"] - | - -AIR302_context.py:95:35: AIR302 `yesterday_ds_nodash` is removed in Airflow 3.0 - | -93 | tomorrow_ds = context["tomorrow_ds"] -94 | yesterday_ds = context["yesterday_ds"] -95 | yesterday_ds_nodash = context["yesterday_ds_nodash"] - | ^^^^^^^^^^^^^^^^^^^^^ AIR302 -96 | -97 | class CustomOperator(BaseOperator): - | - -AIR302_context.py:112:45: AIR302 `execution_date` is removed in Airflow 3.0 +AIR302_context.py:115:13: AIR302 `airflow.operators.dummy.DummyOperator` is removed in Airflow 3.0 | -111 | @task -112 | def access_invalid_argument_task_out_of_dag(execution_date, tomorrow_ds, logical_date, **context): - | ^^^^^^^^^^^^^^ AIR302 -113 | print("execution date", execution_date) -114 | print("access invalid key", context.get("conf")) +113 | template_searchpath=["/templates"], +114 | ) as dag: +115 | task1 = DummyOperator( + | ^^^^^^^^^^^^^ AIR302 +116 | task_id="task1", +117 | params={ | + = help: Use `airflow.operators.empty.EmptyOperator` instead -AIR302_context.py:112:61: AIR302 `tomorrow_ds` is removed in Airflow 3.0 +AIR302_context.py:135:23: AIR302 `next_ds` is removed in Airflow 3.0 | -111 | @task -112 | def access_invalid_argument_task_out_of_dag(execution_date, tomorrow_ds, logical_date, **context): - | ^^^^^^^^^^^ AIR302 -113 | print("execution date", execution_date) -114 | print("access invalid key", context.get("conf")) - | - -AIR302_context.py:114:45: AIR302 `conf` is removed in Airflow 3.0 - | -112 | def access_invalid_argument_task_out_of_dag(execution_date, tomorrow_ds, logical_date, **context): -113 | print("execution date", execution_date) -114 | print("access invalid key", context.get("conf")) - | ^^^^^^ AIR302 -115 | -116 | @task(task_id="print_the_context") - | - -AIR302_context.py:120:22: AIR302 `tomorrow_ds` is removed in Airflow 3.0 - | -118 | """Print the Airflow context and ds variable from the context.""" -119 | print(ds) -120 | print(kwargs.get("tomorrow_ds")) - | ^^^^^^^^^^^^^ AIR302 -121 | c = get_current_context() -122 | c.get("execution_date") - | - -AIR302_context.py:122:11: AIR302 `execution_date` is removed in Airflow 3.0 - | -120 | print(kwargs.get("tomorrow_ds")) -121 | c = get_current_context() -122 | c.get("execution_date") - | ^^^^^^^^^^^^^^^^ AIR302 -123 | -124 | class CustomOperatorNew(BaseOperator): +134 | class CustomOperator(BaseOperator): +135 | def execute(self, next_ds, context): + | ^^^^^^^ AIR302 +136 | execution_date = context["execution_date"] +137 | next_ds = context["next_ds"] |