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 31aad3834427e..f2c45a2606c47 100644 --- a/crates/ruff_linter/resources/test/fixtures/airflow/AIR302_context.py +++ b/crates/ruff_linter/resources/test/fixtures/airflow/AIR302_context.py @@ -1,9 +1,43 @@ +import pendulum from airflow.models import DAG from airflow.operators.dummy import DummyOperator from datetime import datetime from airflow.plugins_manager import AirflowPlugin from airflow.decorators import task, get_current_context from airflow.models.baseoperator import BaseOperator +from airflow.decorators import dag, task +from airflow.providers.standard.operators.python import PythonOperator + + +def access_invalid_key_in_context(**context): + print("access invalid key", context["conf"]) + + +@task +def access_invalid_key_task_out_of_dag(**context): + 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() + + +invalid_dag() @task def print_config(**context): @@ -74,3 +108,8 @@ def execute(self, context): tomorrow_ds = context["tomorrow_ds"] yesterday_ds = context["yesterday_ds"] yesterday_ds_nodash = context["yesterday_ds_nodash"] + +@task +def access_invalid_key_task_out_of_dag(execution_date, **context): + print("execution date", execution_date) + print("access invalid key", context.get("conf")) 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 01007f6523a83..3f9eb019190ad 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 @@ -1,8 +1,10 @@ +use crate::checkers::ast::Checker; use ruff_diagnostics::{Diagnostic, Edit, Fix, FixAvailability, Violation}; use ruff_macros::{derive_message_formats, ViolationMetadata}; +use ruff_python_ast::helpers::map_callable; use ruff_python_ast::{ name::QualifiedName, Arguments, Expr, ExprAttribute, ExprCall, ExprContext, ExprName, - ExprStringLiteral, ExprSubscript, StmtClassDef, + ExprStringLiteral, ExprSubscript, Stmt, StmtClassDef, StmtFunctionDef, }; use ruff_python_semantic::analyze::typing; use ruff_python_semantic::Modules; @@ -10,8 +12,6 @@ use ruff_python_semantic::ScopeKind; use ruff_text_size::Ranged; use ruff_text_size::TextRange; -use crate::checkers::ast::Checker; - /// ## What it does /// Checks for uses of deprecated Airflow functions and values. /// @@ -71,6 +71,21 @@ impl Violation for Airflow3Removal { } } +const REMOVED_CONTEXT_KEYS: [&str; 12] = [ + "conf", + "execution_date", + "next_ds", + "next_ds_nodash", + "next_execution_date", + "prev_ds", + "prev_ds_nodash", + "prev_execution_date", + "prev_execution_date_success", + "tomorrow_ds", + "yesterday_ds", + "yesterday_ds_nodash", +]; + fn extract_name_from_slice(slice: &Expr) -> Option { match slice { Expr::StringLiteral(ExprStringLiteral { value, .. }) => Some(value.to_string()), @@ -79,21 +94,6 @@ fn extract_name_from_slice(slice: &Expr) -> Option { } pub(crate) fn removed_context_variable(checker: &mut Checker, expr: &Expr) { - const REMOVED_CONTEXT_KEYS: [&str; 12] = [ - "conf", - "execution_date", - "next_ds", - "next_ds_nodash", - "next_execution_date", - "prev_ds", - "prev_ds_nodash", - "prev_execution_date", - "prev_execution_date_success", - "tomorrow_ds", - "yesterday_ds", - "yesterday_ds_nodash", - ]; - if let Expr::Subscript(ExprSubscript { value, slice, .. }) = expr { if let Expr::Name(ExprName { id, .. }) = &**value { if id.as_str() == "context" { @@ -144,6 +144,7 @@ pub(crate) fn removed_in_3(checker: &mut Checker, expr: &Expr) { check_call_arguments(checker, &qualname, arguments); }; check_method(checker, call_expr); + check_context_get(checker, call_expr); } Expr::Attribute(attribute_expr @ ExprAttribute { attr, .. }) => { check_name(checker, expr, attr.range()); @@ -307,6 +308,52 @@ fn check_class_attribute(checker: &mut Checker, attribute_expr: &ExprAttribute) } } +/// Check whether a removed context key is access through context.get("key"). +/// +/// ```python +/// from airflow.decorators import task +/// +/// +/// @task +/// def access_invalid_key_task_out_of_dag(**context): +/// print("access invalid key", context.get("conf")) +/// ``` +fn check_context_get(checker: &mut Checker, call_expr: &ExprCall) { + if is_task_context_referenced(checker, &call_expr.func) { + return; + } + + let Expr::Attribute(ExprAttribute { value, attr, .. }) = &*call_expr.func else { + return; + }; + + // Ensure the method called on `context` + if !value + .as_name_expr() + .is_some_and(|name| matches!(name.id.as_str(), "context")) + { + return; + } + + // Ensure the method called on `get` + if attr.as_str() != "get" { + return; + } + + for removed_key in REMOVED_CONTEXT_KEYS { + if let Some(argument) = call_expr.arguments.find_argument_value(removed_key, 0) { + checker.diagnostics.push(Diagnostic::new( + Airflow3Removal { + deprecated: removed_key.to_string(), + replacement: Replacement::None, + }, + argument.range(), + )); + return; + } + } +} + /// Check whether a removed Airflow class method is called. /// /// For example: @@ -909,3 +956,55 @@ fn is_airflow_builtin_or_provider(segments: &[&str], module: &str, symbol_suffix _ => false, } } + +fn is_task_context_referenced(checker: &mut Checker, expr: &Expr) -> bool { + let parents: Vec<_> = checker.semantic().current_statements().collect(); + + for stmt in parents { + if let Stmt::FunctionDef(function_def) = stmt { + if is_task_decorated_function(checker, function_def) { + let arguments = extract_task_function_arguments(function_def); + + for deprecated_arg in REMOVED_CONTEXT_KEYS { + if arguments.contains(&deprecated_arg.to_string()) { + checker.diagnostics.push(Diagnostic::new( + Airflow3Removal { + deprecated: deprecated_arg.to_string(), + replacement: Replacement::None, + }, + expr.range(), + )); + return true; + } + } + } + } + } + + false +} + +fn extract_task_function_arguments(stmt: &StmtFunctionDef) -> Vec { + let mut arguments = Vec::new(); + + for param in &stmt.parameters.args { + arguments.push(param.parameter.name.to_string()); + } + + if let Some(vararg) = &stmt.parameters.kwarg { + arguments.push(format!("**{}", vararg.name)); + } + + arguments +} + +fn is_task_decorated_function(checker: &mut Checker, stmt: &StmtFunctionDef) -> bool { + stmt.decorator_list.iter().any(|decorator| { + checker + .semantic() + .resolve_qualified_name(map_callable(&decorator.expression)) + .is_some_and(|qualified_name| { + matches!(qualified_name.segments(), ["airflow", "decorators", "task"]) + }) + }) +} 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 1403c8e025ef5..2f7fc15db8035 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 @@ -2,377 +2,429 @@ source: crates/ruff_linter/src/rules/airflow/mod.rs snapshot_kind: text --- -AIR302_context.py:14:30: AIR302 `execution_date` is removed in Airflow 3.0 +AIR302_context.py:13:41: AIR302 `conf` is removed in Airflow 3.0 | -13 | # Removed usage - should trigger violations -14 | execution_date = context["execution_date"] +12 | def access_invalid_key_in_context(**context): +13 | print("access invalid key", context["conf"]) + | ^^^^^^ AIR302 + | + +AIR302_context.py:18:45: AIR302 `conf` is removed in Airflow 3.0 + | +16 | @task +17 | def access_invalid_key_task_out_of_dag(**context): +18 | print("access invalid key", context.get("conf")) + | ^^^^^^ AIR302 + | + +AIR302_context.py:30:49: AIR302 `conf` 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( + | + +AIR302_context.py:48:30: AIR302 `execution_date` is removed in Airflow 3.0 + | +47 | # Removed usage - should trigger violations +48 | execution_date = context["execution_date"] | ^^^^^^^^^^^^^^^^ AIR302 -15 | next_ds = context["next_ds"] -16 | next_ds_nodash = context["next_ds_nodash"] +49 | next_ds = context["next_ds"] +50 | next_ds_nodash = context["next_ds_nodash"] | -AIR302_context.py:15:23: AIR302 `next_ds` is removed in Airflow 3.0 +AIR302_context.py:49:23: AIR302 `next_ds` is removed in Airflow 3.0 | -13 | # Removed usage - should trigger violations -14 | execution_date = context["execution_date"] -15 | next_ds = context["next_ds"] +47 | # Removed usage - should trigger violations +48 | execution_date = context["execution_date"] +49 | next_ds = context["next_ds"] | ^^^^^^^^^ AIR302 -16 | next_ds_nodash = context["next_ds_nodash"] -17 | next_execution_date = context["next_execution_date"] +50 | next_ds_nodash = context["next_ds_nodash"] +51 | next_execution_date = context["next_execution_date"] | -AIR302_context.py:16:30: AIR302 `next_ds_nodash` is removed in Airflow 3.0 +AIR302_context.py:50:30: AIR302 `next_ds_nodash` is removed in Airflow 3.0 | -14 | execution_date = context["execution_date"] -15 | next_ds = context["next_ds"] -16 | next_ds_nodash = context["next_ds_nodash"] +48 | execution_date = context["execution_date"] +49 | next_ds = context["next_ds"] +50 | next_ds_nodash = context["next_ds_nodash"] | ^^^^^^^^^^^^^^^^ AIR302 -17 | next_execution_date = context["next_execution_date"] -18 | prev_ds = context["prev_ds"] +51 | next_execution_date = context["next_execution_date"] +52 | prev_ds = context["prev_ds"] | -AIR302_context.py:17:35: AIR302 `next_execution_date` is removed in Airflow 3.0 +AIR302_context.py:51:35: AIR302 `next_execution_date` is removed in Airflow 3.0 | -15 | next_ds = context["next_ds"] -16 | next_ds_nodash = context["next_ds_nodash"] -17 | next_execution_date = context["next_execution_date"] +49 | next_ds = context["next_ds"] +50 | next_ds_nodash = context["next_ds_nodash"] +51 | next_execution_date = context["next_execution_date"] | ^^^^^^^^^^^^^^^^^^^^^ AIR302 -18 | prev_ds = context["prev_ds"] -19 | prev_ds_nodash = context["prev_ds_nodash"] +52 | prev_ds = context["prev_ds"] +53 | prev_ds_nodash = context["prev_ds_nodash"] | -AIR302_context.py:18:23: AIR302 `prev_ds` is removed in Airflow 3.0 +AIR302_context.py:52:23: AIR302 `prev_ds` is removed in Airflow 3.0 | -16 | next_ds_nodash = context["next_ds_nodash"] -17 | next_execution_date = context["next_execution_date"] -18 | prev_ds = context["prev_ds"] +50 | next_ds_nodash = context["next_ds_nodash"] +51 | next_execution_date = context["next_execution_date"] +52 | prev_ds = context["prev_ds"] | ^^^^^^^^^ AIR302 -19 | prev_ds_nodash = context["prev_ds_nodash"] -20 | prev_execution_date = context["prev_execution_date"] +53 | prev_ds_nodash = context["prev_ds_nodash"] +54 | prev_execution_date = context["prev_execution_date"] | -AIR302_context.py:19:30: AIR302 `prev_ds_nodash` is removed in Airflow 3.0 +AIR302_context.py:53:30: AIR302 `prev_ds_nodash` is removed in Airflow 3.0 | -17 | next_execution_date = context["next_execution_date"] -18 | prev_ds = context["prev_ds"] -19 | prev_ds_nodash = context["prev_ds_nodash"] +51 | next_execution_date = context["next_execution_date"] +52 | prev_ds = context["prev_ds"] +53 | prev_ds_nodash = context["prev_ds_nodash"] | ^^^^^^^^^^^^^^^^ AIR302 -20 | prev_execution_date = context["prev_execution_date"] -21 | prev_execution_date_success = context["prev_execution_date_success"] +54 | prev_execution_date = context["prev_execution_date"] +55 | prev_execution_date_success = context["prev_execution_date_success"] | -AIR302_context.py:20:35: AIR302 `prev_execution_date` is removed in Airflow 3.0 +AIR302_context.py:54:35: AIR302 `prev_execution_date` is removed in Airflow 3.0 | -18 | prev_ds = context["prev_ds"] -19 | prev_ds_nodash = context["prev_ds_nodash"] -20 | prev_execution_date = context["prev_execution_date"] +52 | prev_ds = context["prev_ds"] +53 | prev_ds_nodash = context["prev_ds_nodash"] +54 | prev_execution_date = context["prev_execution_date"] | ^^^^^^^^^^^^^^^^^^^^^ AIR302 -21 | prev_execution_date_success = context["prev_execution_date_success"] -22 | tomorrow_ds = context["tomorrow_ds"] +55 | prev_execution_date_success = context["prev_execution_date_success"] +56 | tomorrow_ds = context["tomorrow_ds"] | -AIR302_context.py:21:43: AIR302 `prev_execution_date_success` is removed in Airflow 3.0 +AIR302_context.py:55:43: AIR302 `prev_execution_date_success` is removed in Airflow 3.0 | -19 | prev_ds_nodash = context["prev_ds_nodash"] -20 | prev_execution_date = context["prev_execution_date"] -21 | prev_execution_date_success = context["prev_execution_date_success"] +53 | prev_ds_nodash = context["prev_ds_nodash"] +54 | prev_execution_date = context["prev_execution_date"] +55 | prev_execution_date_success = context["prev_execution_date_success"] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ AIR302 -22 | tomorrow_ds = context["tomorrow_ds"] -23 | yesterday_ds = context["yesterday_ds"] +56 | tomorrow_ds = context["tomorrow_ds"] +57 | yesterday_ds = context["yesterday_ds"] | -AIR302_context.py:22:27: AIR302 `tomorrow_ds` is removed in Airflow 3.0 +AIR302_context.py:56:27: AIR302 `tomorrow_ds` is removed in Airflow 3.0 | -20 | prev_execution_date = context["prev_execution_date"] -21 | prev_execution_date_success = context["prev_execution_date_success"] -22 | tomorrow_ds = context["tomorrow_ds"] +54 | prev_execution_date = context["prev_execution_date"] +55 | prev_execution_date_success = context["prev_execution_date_success"] +56 | tomorrow_ds = context["tomorrow_ds"] | ^^^^^^^^^^^^^ AIR302 -23 | yesterday_ds = context["yesterday_ds"] -24 | yesterday_ds_nodash = context["yesterday_ds_nodash"] +57 | yesterday_ds = context["yesterday_ds"] +58 | yesterday_ds_nodash = context["yesterday_ds_nodash"] | -AIR302_context.py:23:28: AIR302 `yesterday_ds` is removed in Airflow 3.0 +AIR302_context.py:57:28: AIR302 `yesterday_ds` is removed in Airflow 3.0 | -21 | prev_execution_date_success = context["prev_execution_date_success"] -22 | tomorrow_ds = context["tomorrow_ds"] -23 | yesterday_ds = context["yesterday_ds"] +55 | prev_execution_date_success = context["prev_execution_date_success"] +56 | tomorrow_ds = context["tomorrow_ds"] +57 | yesterday_ds = context["yesterday_ds"] | ^^^^^^^^^^^^^^ AIR302 -24 | yesterday_ds_nodash = context["yesterday_ds_nodash"] +58 | yesterday_ds_nodash = context["yesterday_ds_nodash"] | -AIR302_context.py:24:35: AIR302 `yesterday_ds_nodash` is removed in Airflow 3.0 +AIR302_context.py:58:35: AIR302 `yesterday_ds_nodash` is removed in Airflow 3.0 | -22 | tomorrow_ds = context["tomorrow_ds"] -23 | yesterday_ds = context["yesterday_ds"] -24 | yesterday_ds_nodash = context["yesterday_ds_nodash"] +56 | tomorrow_ds = context["tomorrow_ds"] +57 | yesterday_ds = context["yesterday_ds"] +58 | yesterday_ds_nodash = context["yesterday_ds_nodash"] | ^^^^^^^^^^^^^^^^^^^^^ AIR302 -25 | -26 | with DAG( +59 | +60 | with DAG( | -AIR302_context.py:28:5: AIR302 [*] `schedule_interval` is removed in Airflow 3.0 +AIR302_context.py:62:5: AIR302 [*] `schedule_interval` is removed in Airflow 3.0 | -26 | with DAG( -27 | dag_id="example_dag", -28 | schedule_interval="@daily", +60 | with DAG( +61 | dag_id="example_dag", +62 | schedule_interval="@daily", | ^^^^^^^^^^^^^^^^^ AIR302 -29 | start_date=datetime(2023, 1, 1), -30 | template_searchpath=["/templates"], +63 | start_date=datetime(2023, 1, 1), +64 | template_searchpath=["/templates"], | = help: Use `schedule` instead ℹ Safe fix -25 25 | -26 26 | with DAG( -27 27 | dag_id="example_dag", -28 |- schedule_interval="@daily", - 28 |+ schedule="@daily", -29 29 | start_date=datetime(2023, 1, 1), -30 30 | template_searchpath=["/templates"], -31 31 | ) as dag: - -AIR302_context.py:32:13: AIR302 `airflow.operators.dummy.DummyOperator` is removed in Airflow 3.0 - | -30 | template_searchpath=["/templates"], -31 | ) as dag: -32 | task1 = DummyOperator( +59 59 | +60 60 | with DAG( +61 61 | dag_id="example_dag", +62 |- schedule_interval="@daily", + 62 |+ schedule="@daily", +63 63 | start_date=datetime(2023, 1, 1), +64 64 | template_searchpath=["/templates"], +65 65 | ) as dag: + +AIR302_context.py:66:13: AIR302 `airflow.operators.dummy.DummyOperator` is removed in Airflow 3.0 + | +64 | template_searchpath=["/templates"], +65 | ) as dag: +66 | task1 = DummyOperator( | ^^^^^^^^^^^^^ AIR302 -33 | task_id="task1", -34 | params={ +67 | task_id="task1", +68 | params={ | = help: Use `airflow.operators.empty.EmptyOperator` instead -AIR302_context.py:45:57: AIR302 `execution_date` is removed in Airflow 3.0 +AIR302_context.py:79:57: AIR302 `execution_date` is removed in Airflow 3.0 | -43 | name = "custom_macros" -44 | macros = { -45 | "execution_date_macro": lambda context: context["execution_date"], +77 | name = "custom_macros" +78 | macros = { +79 | "execution_date_macro": lambda context: context["execution_date"], | ^^^^^^^^^^^^^^^^ AIR302 -46 | "next_ds_macro": lambda context: context["next_ds"] -47 | } +80 | "next_ds_macro": lambda context: context["next_ds"] +81 | } | -AIR302_context.py:46:50: AIR302 `next_ds` is removed in Airflow 3.0 +AIR302_context.py:80:50: AIR302 `next_ds` is removed in Airflow 3.0 | -44 | macros = { -45 | "execution_date_macro": lambda context: context["execution_date"], -46 | "next_ds_macro": lambda context: context["next_ds"] +78 | macros = { +79 | "execution_date_macro": lambda context: context["execution_date"], +80 | "next_ds_macro": lambda context: context["next_ds"] | ^^^^^^^^^ AIR302 -47 | } +81 | } | -AIR302_context.py:52:30: AIR302 `execution_date` is removed in Airflow 3.0 +AIR302_context.py:86:30: AIR302 `execution_date` is removed in Airflow 3.0 | -50 | def print_config(): -51 | context = get_current_context() -52 | execution_date = context["execution_date"] +84 | def print_config(): +85 | context = get_current_context() +86 | execution_date = context["execution_date"] | ^^^^^^^^^^^^^^^^ AIR302 -53 | next_ds = context["next_ds"] -54 | next_ds_nodash = context["next_ds_nodash"] +87 | next_ds = context["next_ds"] +88 | next_ds_nodash = context["next_ds_nodash"] | -AIR302_context.py:53:23: AIR302 `next_ds` is removed in Airflow 3.0 +AIR302_context.py:87:23: AIR302 `next_ds` is removed in Airflow 3.0 | -51 | context = get_current_context() -52 | execution_date = context["execution_date"] -53 | next_ds = context["next_ds"] +85 | context = get_current_context() +86 | execution_date = context["execution_date"] +87 | next_ds = context["next_ds"] | ^^^^^^^^^ AIR302 -54 | next_ds_nodash = context["next_ds_nodash"] -55 | next_execution_date = context["next_execution_date"] +88 | next_ds_nodash = context["next_ds_nodash"] +89 | next_execution_date = context["next_execution_date"] | -AIR302_context.py:54:30: AIR302 `next_ds_nodash` is removed in Airflow 3.0 +AIR302_context.py:88:30: AIR302 `next_ds_nodash` is removed in Airflow 3.0 | -52 | execution_date = context["execution_date"] -53 | next_ds = context["next_ds"] -54 | next_ds_nodash = context["next_ds_nodash"] +86 | execution_date = context["execution_date"] +87 | next_ds = context["next_ds"] +88 | next_ds_nodash = context["next_ds_nodash"] | ^^^^^^^^^^^^^^^^ AIR302 -55 | next_execution_date = context["next_execution_date"] -56 | prev_ds = context["prev_ds"] +89 | next_execution_date = context["next_execution_date"] +90 | prev_ds = context["prev_ds"] | -AIR302_context.py:55:35: AIR302 `next_execution_date` is removed in Airflow 3.0 +AIR302_context.py:89:35: AIR302 `next_execution_date` is removed in Airflow 3.0 | -53 | next_ds = context["next_ds"] -54 | next_ds_nodash = context["next_ds_nodash"] -55 | next_execution_date = context["next_execution_date"] +87 | next_ds = context["next_ds"] +88 | next_ds_nodash = context["next_ds_nodash"] +89 | next_execution_date = context["next_execution_date"] | ^^^^^^^^^^^^^^^^^^^^^ AIR302 -56 | prev_ds = context["prev_ds"] -57 | prev_ds_nodash = context["prev_ds_nodash"] +90 | prev_ds = context["prev_ds"] +91 | prev_ds_nodash = context["prev_ds_nodash"] | -AIR302_context.py:56:23: AIR302 `prev_ds` is removed in Airflow 3.0 +AIR302_context.py:90:23: AIR302 `prev_ds` is removed in Airflow 3.0 | -54 | next_ds_nodash = context["next_ds_nodash"] -55 | next_execution_date = context["next_execution_date"] -56 | prev_ds = context["prev_ds"] +88 | next_ds_nodash = context["next_ds_nodash"] +89 | next_execution_date = context["next_execution_date"] +90 | prev_ds = context["prev_ds"] | ^^^^^^^^^ AIR302 -57 | prev_ds_nodash = context["prev_ds_nodash"] -58 | prev_execution_date = context["prev_execution_date"] +91 | prev_ds_nodash = context["prev_ds_nodash"] +92 | prev_execution_date = context["prev_execution_date"] | -AIR302_context.py:57:30: AIR302 `prev_ds_nodash` is removed in Airflow 3.0 +AIR302_context.py:91:30: AIR302 `prev_ds_nodash` is removed in Airflow 3.0 | -55 | next_execution_date = context["next_execution_date"] -56 | prev_ds = context["prev_ds"] -57 | prev_ds_nodash = context["prev_ds_nodash"] +89 | next_execution_date = context["next_execution_date"] +90 | prev_ds = context["prev_ds"] +91 | prev_ds_nodash = context["prev_ds_nodash"] | ^^^^^^^^^^^^^^^^ AIR302 -58 | prev_execution_date = context["prev_execution_date"] -59 | prev_execution_date_success = context["prev_execution_date_success"] +92 | prev_execution_date = context["prev_execution_date"] +93 | prev_execution_date_success = context["prev_execution_date_success"] | -AIR302_context.py:58:35: AIR302 `prev_execution_date` is removed in Airflow 3.0 +AIR302_context.py:92:35: AIR302 `prev_execution_date` is removed in Airflow 3.0 | -56 | prev_ds = context["prev_ds"] -57 | prev_ds_nodash = context["prev_ds_nodash"] -58 | prev_execution_date = context["prev_execution_date"] +90 | prev_ds = context["prev_ds"] +91 | prev_ds_nodash = context["prev_ds_nodash"] +92 | prev_execution_date = context["prev_execution_date"] | ^^^^^^^^^^^^^^^^^^^^^ AIR302 -59 | prev_execution_date_success = context["prev_execution_date_success"] -60 | tomorrow_ds = context["tomorrow_ds"] +93 | prev_execution_date_success = context["prev_execution_date_success"] +94 | tomorrow_ds = context["tomorrow_ds"] | -AIR302_context.py:59:43: AIR302 `prev_execution_date_success` is removed in Airflow 3.0 +AIR302_context.py:93:43: AIR302 `prev_execution_date_success` is removed in Airflow 3.0 | -57 | prev_ds_nodash = context["prev_ds_nodash"] -58 | prev_execution_date = context["prev_execution_date"] -59 | prev_execution_date_success = context["prev_execution_date_success"] +91 | prev_ds_nodash = context["prev_ds_nodash"] +92 | prev_execution_date = context["prev_execution_date"] +93 | prev_execution_date_success = context["prev_execution_date_success"] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ AIR302 -60 | tomorrow_ds = context["tomorrow_ds"] -61 | yesterday_ds = context["yesterday_ds"] +94 | tomorrow_ds = context["tomorrow_ds"] +95 | yesterday_ds = context["yesterday_ds"] | -AIR302_context.py:60:27: AIR302 `tomorrow_ds` is removed in Airflow 3.0 +AIR302_context.py:94:27: AIR302 `tomorrow_ds` is removed in Airflow 3.0 | -58 | prev_execution_date = context["prev_execution_date"] -59 | prev_execution_date_success = context["prev_execution_date_success"] -60 | tomorrow_ds = context["tomorrow_ds"] +92 | prev_execution_date = context["prev_execution_date"] +93 | prev_execution_date_success = context["prev_execution_date_success"] +94 | tomorrow_ds = context["tomorrow_ds"] | ^^^^^^^^^^^^^ AIR302 -61 | yesterday_ds = context["yesterday_ds"] -62 | yesterday_ds_nodash = context["yesterday_ds_nodash"] +95 | yesterday_ds = context["yesterday_ds"] +96 | yesterday_ds_nodash = context["yesterday_ds_nodash"] | -AIR302_context.py:61:28: AIR302 `yesterday_ds` is removed in Airflow 3.0 +AIR302_context.py:95:28: AIR302 `yesterday_ds` is removed in Airflow 3.0 | -59 | prev_execution_date_success = context["prev_execution_date_success"] -60 | tomorrow_ds = context["tomorrow_ds"] -61 | yesterday_ds = context["yesterday_ds"] +93 | prev_execution_date_success = context["prev_execution_date_success"] +94 | tomorrow_ds = context["tomorrow_ds"] +95 | yesterday_ds = context["yesterday_ds"] | ^^^^^^^^^^^^^^ AIR302 -62 | yesterday_ds_nodash = context["yesterday_ds_nodash"] +96 | yesterday_ds_nodash = context["yesterday_ds_nodash"] | -AIR302_context.py:62:35: AIR302 `yesterday_ds_nodash` is removed in Airflow 3.0 +AIR302_context.py:96:35: AIR302 `yesterday_ds_nodash` is removed in Airflow 3.0 | -60 | tomorrow_ds = context["tomorrow_ds"] -61 | yesterday_ds = context["yesterday_ds"] -62 | yesterday_ds_nodash = context["yesterday_ds_nodash"] +94 | tomorrow_ds = context["tomorrow_ds"] +95 | yesterday_ds = context["yesterday_ds"] +96 | yesterday_ds_nodash = context["yesterday_ds_nodash"] | ^^^^^^^^^^^^^^^^^^^^^ AIR302 -63 | -64 | class CustomOperator(BaseOperator): - | - -AIR302_context.py:66:34: AIR302 `execution_date` is removed in Airflow 3.0 - | -64 | class CustomOperator(BaseOperator): -65 | def execute(self, context): -66 | execution_date = context["execution_date"] - | ^^^^^^^^^^^^^^^^ AIR302 -67 | next_ds = context["next_ds"] -68 | next_ds_nodash = context["next_ds_nodash"] - | - -AIR302_context.py:67:27: AIR302 `next_ds` is removed in Airflow 3.0 - | -65 | def execute(self, context): -66 | execution_date = context["execution_date"] -67 | next_ds = context["next_ds"] - | ^^^^^^^^^ AIR302 -68 | next_ds_nodash = context["next_ds_nodash"] -69 | next_execution_date = context["next_execution_date"] - | - -AIR302_context.py:68:34: AIR302 `next_ds_nodash` is removed in Airflow 3.0 - | -66 | execution_date = context["execution_date"] -67 | next_ds = context["next_ds"] -68 | next_ds_nodash = context["next_ds_nodash"] - | ^^^^^^^^^^^^^^^^ AIR302 -69 | next_execution_date = context["next_execution_date"] -70 | prev_ds = context["prev_ds"] - | - -AIR302_context.py:69:39: AIR302 `next_execution_date` is removed in Airflow 3.0 - | -67 | next_ds = context["next_ds"] -68 | next_ds_nodash = context["next_ds_nodash"] -69 | next_execution_date = context["next_execution_date"] - | ^^^^^^^^^^^^^^^^^^^^^ AIR302 -70 | prev_ds = context["prev_ds"] -71 | prev_ds_nodash = context["prev_ds_nodash"] - | - -AIR302_context.py:70:27: AIR302 `prev_ds` is removed in Airflow 3.0 - | -68 | next_ds_nodash = context["next_ds_nodash"] -69 | next_execution_date = context["next_execution_date"] -70 | prev_ds = context["prev_ds"] - | ^^^^^^^^^ AIR302 -71 | prev_ds_nodash = context["prev_ds_nodash"] -72 | prev_execution_date = context["prev_execution_date"] - | - -AIR302_context.py:71:34: AIR302 `prev_ds_nodash` is removed in Airflow 3.0 - | -69 | next_execution_date = context["next_execution_date"] -70 | prev_ds = context["prev_ds"] -71 | prev_ds_nodash = context["prev_ds_nodash"] - | ^^^^^^^^^^^^^^^^ AIR302 -72 | prev_execution_date = context["prev_execution_date"] -73 | prev_execution_date_success = context["prev_execution_date_success"] - | - -AIR302_context.py:72:39: AIR302 `prev_execution_date` is removed in Airflow 3.0 - | -70 | prev_ds = context["prev_ds"] -71 | prev_ds_nodash = context["prev_ds_nodash"] -72 | prev_execution_date = context["prev_execution_date"] - | ^^^^^^^^^^^^^^^^^^^^^ AIR302 -73 | prev_execution_date_success = context["prev_execution_date_success"] -74 | tomorrow_ds = context["tomorrow_ds"] - | - -AIR302_context.py:73:47: AIR302 `prev_execution_date_success` is removed in Airflow 3.0 - | -71 | prev_ds_nodash = context["prev_ds_nodash"] -72 | prev_execution_date = context["prev_execution_date"] -73 | prev_execution_date_success = context["prev_execution_date_success"] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ AIR302 -74 | tomorrow_ds = context["tomorrow_ds"] -75 | yesterday_ds = context["yesterday_ds"] - | - -AIR302_context.py:74:31: AIR302 `tomorrow_ds` is removed in Airflow 3.0 - | -72 | prev_execution_date = context["prev_execution_date"] -73 | prev_execution_date_success = context["prev_execution_date_success"] -74 | tomorrow_ds = context["tomorrow_ds"] - | ^^^^^^^^^^^^^ AIR302 -75 | yesterday_ds = context["yesterday_ds"] -76 | yesterday_ds_nodash = context["yesterday_ds_nodash"] - | - -AIR302_context.py:75:32: AIR302 `yesterday_ds` is removed in Airflow 3.0 - | -73 | prev_execution_date_success = context["prev_execution_date_success"] -74 | tomorrow_ds = context["tomorrow_ds"] -75 | yesterday_ds = context["yesterday_ds"] - | ^^^^^^^^^^^^^^ AIR302 -76 | yesterday_ds_nodash = context["yesterday_ds_nodash"] - | - -AIR302_context.py:76:39: AIR302 `yesterday_ds_nodash` is removed in Airflow 3.0 - | -74 | tomorrow_ds = context["tomorrow_ds"] -75 | yesterday_ds = context["yesterday_ds"] -76 | yesterday_ds_nodash = context["yesterday_ds_nodash"] - | ^^^^^^^^^^^^^^^^^^^^^ AIR302 - | +97 | +98 | class CustomOperator(BaseOperator): + | + +AIR302_context.py:100:34: AIR302 `execution_date` is removed in Airflow 3.0 + | + 98 | class CustomOperator(BaseOperator): + 99 | def execute(self, context): +100 | execution_date = context["execution_date"] + | ^^^^^^^^^^^^^^^^ AIR302 +101 | next_ds = context["next_ds"] +102 | next_ds_nodash = context["next_ds_nodash"] + | + +AIR302_context.py:101:27: AIR302 `next_ds` is removed in Airflow 3.0 + | + 99 | def execute(self, context): +100 | execution_date = context["execution_date"] +101 | next_ds = context["next_ds"] + | ^^^^^^^^^ AIR302 +102 | next_ds_nodash = context["next_ds_nodash"] +103 | next_execution_date = context["next_execution_date"] + | + +AIR302_context.py:102:34: AIR302 `next_ds_nodash` is removed in Airflow 3.0 + | +100 | execution_date = context["execution_date"] +101 | next_ds = context["next_ds"] +102 | next_ds_nodash = context["next_ds_nodash"] + | ^^^^^^^^^^^^^^^^ AIR302 +103 | next_execution_date = context["next_execution_date"] +104 | prev_ds = context["prev_ds"] + | + +AIR302_context.py:103:39: AIR302 `next_execution_date` is removed in Airflow 3.0 + | +101 | next_ds = context["next_ds"] +102 | next_ds_nodash = context["next_ds_nodash"] +103 | next_execution_date = context["next_execution_date"] + | ^^^^^^^^^^^^^^^^^^^^^ AIR302 +104 | prev_ds = context["prev_ds"] +105 | prev_ds_nodash = context["prev_ds_nodash"] + | + +AIR302_context.py:104:27: AIR302 `prev_ds` is removed in Airflow 3.0 + | +102 | next_ds_nodash = context["next_ds_nodash"] +103 | next_execution_date = context["next_execution_date"] +104 | prev_ds = context["prev_ds"] + | ^^^^^^^^^ AIR302 +105 | prev_ds_nodash = context["prev_ds_nodash"] +106 | prev_execution_date = context["prev_execution_date"] + | + +AIR302_context.py:105:34: AIR302 `prev_ds_nodash` is removed in Airflow 3.0 + | +103 | next_execution_date = context["next_execution_date"] +104 | prev_ds = context["prev_ds"] +105 | prev_ds_nodash = context["prev_ds_nodash"] + | ^^^^^^^^^^^^^^^^ AIR302 +106 | prev_execution_date = context["prev_execution_date"] +107 | prev_execution_date_success = context["prev_execution_date_success"] + | + +AIR302_context.py:106:39: AIR302 `prev_execution_date` is removed in Airflow 3.0 + | +104 | prev_ds = context["prev_ds"] +105 | prev_ds_nodash = context["prev_ds_nodash"] +106 | prev_execution_date = context["prev_execution_date"] + | ^^^^^^^^^^^^^^^^^^^^^ AIR302 +107 | prev_execution_date_success = context["prev_execution_date_success"] +108 | tomorrow_ds = context["tomorrow_ds"] + | + +AIR302_context.py:107:47: AIR302 `prev_execution_date_success` is removed in Airflow 3.0 + | +105 | prev_ds_nodash = context["prev_ds_nodash"] +106 | prev_execution_date = context["prev_execution_date"] +107 | prev_execution_date_success = context["prev_execution_date_success"] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ AIR302 +108 | tomorrow_ds = context["tomorrow_ds"] +109 | yesterday_ds = context["yesterday_ds"] + | + +AIR302_context.py:108:31: AIR302 `tomorrow_ds` is removed in Airflow 3.0 + | +106 | prev_execution_date = context["prev_execution_date"] +107 | prev_execution_date_success = context["prev_execution_date_success"] +108 | tomorrow_ds = context["tomorrow_ds"] + | ^^^^^^^^^^^^^ AIR302 +109 | yesterday_ds = context["yesterday_ds"] +110 | yesterday_ds_nodash = context["yesterday_ds_nodash"] + | + +AIR302_context.py:109:32: AIR302 `yesterday_ds` is removed in Airflow 3.0 + | +107 | prev_execution_date_success = context["prev_execution_date_success"] +108 | tomorrow_ds = context["tomorrow_ds"] +109 | yesterday_ds = context["yesterday_ds"] + | ^^^^^^^^^^^^^^ AIR302 +110 | yesterday_ds_nodash = context["yesterday_ds_nodash"] + | + +AIR302_context.py:110:39: AIR302 `yesterday_ds_nodash` is removed in Airflow 3.0 + | +108 | tomorrow_ds = context["tomorrow_ds"] +109 | yesterday_ds = context["yesterday_ds"] +110 | yesterday_ds_nodash = context["yesterday_ds_nodash"] + | ^^^^^^^^^^^^^^^^^^^^^ AIR302 +111 | +112 | @task + | + +AIR302_context.py:114:5: AIR302 `execution_date` is removed in Airflow 3.0 + | +112 | @task +113 | def access_invalid_key_task_out_of_dag(execution_date, **context): +114 | print("execution date", execution_date) + | ^^^^^ AIR302 +115 | print("access invalid key", context.get("conf")) + | + +AIR302_context.py:115:5: AIR302 `execution_date` is removed in Airflow 3.0 + | +113 | def access_invalid_key_task_out_of_dag(execution_date, **context): +114 | print("execution date", execution_date) +115 | print("access invalid key", context.get("conf")) + | ^^^^^ AIR302 + | + +AIR302_context.py:115:33: AIR302 `execution_date` is removed in Airflow 3.0 + | +113 | def access_invalid_key_task_out_of_dag(execution_date, **context): +114 | print("execution date", execution_date) +115 | print("access invalid key", context.get("conf")) + | ^^^^^^^^^^^ AIR302 + |