Compare commits

...

12 Commits

Author SHA1 Message Date
Dhruv Manilawala
c76e15a45d Check context parameters directly from function definition 2025-01-23 14:13:29 +05:30
Ankit Chaurasia
d0aff2bbff Add find_parameter 2025-01-23 12:19:31 +05:45
Ankit Chaurasia
65db31f0e1 highlights the parameter itself 2025-01-22 12:59:39 +05:45
Wei Lee
c2c37b8052 test: update test fixture 2025-01-22 10:06:15 +08:00
Ankit Chaurasia
51613d9107 Add lint error for removed context variables for get_current_context 2025-01-22 09:57:26 +08:00
Ankit Chaurasia
f20e70cd62 remove use of vectors 2025-01-22 09:57:25 +08:00
Ankit Chaurasia
4737824345 fix the logic for lint error message 2025-01-22 09:57:25 +08:00
Ankit Chaurasia
1961b76d03 Refactor functions to use ExprSubscript 2025-01-22 09:57:25 +08:00
Ankit Chaurasia
62a1e55705 refactor rename functions 2025-01-22 09:57:25 +08:00
Ankit Chaurasia
8a7ec4c0a3 Fix PR comments 2025-01-22 09:57:25 +08:00
Ankit Chaurasia
9b9540c3cd Check arguments and function decorated with @task 2025-01-22 09:57:25 +08:00
Ankit Chaurasia
ccafaf8e30 Add more checks for removed context variables
add lint rule to show error for removed context variables in airflow
2025-01-22 09:57:25 +08:00
6 changed files with 826 additions and 4 deletions

View File

@@ -0,0 +1,119 @@
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.models.baseoperator import BaseOperator
from airflow.decorators import dag, task
from airflow.providers.standard.operators.python import PythonOperator
from airflow.utils.context import get_current_context
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):
# This should not throw an error as logical_date is part of airflow context.
logical_date = context["logical_date"]
# Removed usage - should trigger violations
execution_date = context["execution_date"]
next_ds = context["next_ds"]
next_ds_nodash = context["next_ds_nodash"]
next_execution_date = context["next_execution_date"]
prev_ds = context["prev_ds"]
prev_ds_nodash = context["prev_ds_nodash"]
prev_execution_date = context["prev_execution_date"]
prev_execution_date_success = context["prev_execution_date_success"]
tomorrow_ds = context["tomorrow_ds"]
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():
context = get_current_context()
execution_date = context["execution_date"]
next_ds = context["next_ds"]
next_ds_nodash = context["next_ds_nodash"]
next_execution_date = context["next_execution_date"]
prev_ds = context["prev_ds"]
prev_ds_nodash = context["prev_ds_nodash"]
prev_execution_date = context["prev_execution_date"]
prev_execution_date_success = context["prev_execution_date_success"]
tomorrow_ds = context["tomorrow_ds"]
yesterday_ds = context["yesterday_ds"]
yesterday_ds_nodash = context["yesterday_ds_nodash"]
class CustomOperator(BaseOperator):
def execute(self, context):
execution_date = context["execution_date"]
next_ds = context["next_ds"]
next_ds_nodash = context["next_ds_nodash"]
next_execution_date = context["next_execution_date"]
prev_ds = context["prev_ds"]
prev_ds_nodash = context["prev_ds_nodash"]
prev_execution_date = context["prev_execution_date"]
prev_execution_date_success = context["prev_execution_date_success"]
tomorrow_ds = context["tomorrow_ds"]
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")

View File

@@ -175,7 +175,9 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) {
if checker.enabled(Rule::NonPEP646Unpack) {
pyupgrade::rules::use_pep646_unpack(checker, subscript);
}
if checker.enabled(Rule::Airflow3Removal) {
airflow::rules::removed_in_3(checker, expr);
}
pandas_vet::rules::subscript(checker, value, expr);
}
Expr::Tuple(ast::ExprTuple {

View File

@@ -376,6 +376,9 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
if checker.enabled(Rule::PytestParameterWithDefaultArgument) {
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);
}
}
Stmt::Return(_) => {
if checker.enabled(Rule::ReturnOutsideFunction) {

View File

@@ -18,6 +18,7 @@ mod tests {
#[test_case(Rule::Airflow3Removal, Path::new("AIR302_names.py"))]
#[test_case(Rule::Airflow3Removal, Path::new("AIR302_class_attribute.py"))]
#[test_case(Rule::Airflow3Removal, Path::new("AIR302_airflow_plugin.py"))]
#[test_case(Rule::Airflow3Removal, Path::new("AIR302_context.py"))]
#[test_case(Rule::Airflow3MovedToProvider, Path::new("AIR303.py"))]
fn rules(rule_code: Rule, path: &Path) -> Result<()> {
let snapshot = format!("{}_{}", rule_code.noqa_code(), path.to_string_lossy());

View File

@@ -1,17 +1,19 @@
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::AnyParameterRef;
use ruff_python_ast::{
name::QualifiedName, Arguments, Expr, ExprAttribute, ExprCall, ExprContext, ExprName,
StmtClassDef,
ExprStringLiteral, ExprSubscript, Stmt, StmtClassDef, StmtFunctionDef,
};
use ruff_python_semantic::analyze::typing;
use ruff_python_semantic::Modules;
use ruff_python_semantic::ScopeKind;
use ruff_python_semantic::SemanticModel;
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 +73,64 @@ 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<String> {
match slice {
Expr::StringLiteral(ExprStringLiteral { value, .. }) => Some(value.to_string()),
_ => None,
}
}
/// Check if a subscript expression accesses a removed Airflow context variable.
/// If a removed key is found, push a corresponding diagnostic.
fn removed_context_variable(checker: &mut Checker, subscript: &ExprSubscript) {
let ExprSubscript { value, slice, .. } = subscript;
let is_context_arg = if let Expr::Name(ExprName { id, .. }) = &**value {
id.as_str() == "context" || id.as_str().starts_with("**")
} else {
false
};
let is_current_context =
if let Some(qualname) = typing::resolve_assignment(value, checker.semantic()) {
matches!(
qualname.segments(),
["airflow", "utils", "context", "get_current_context"]
)
} else {
false
};
if is_context_arg || is_current_context {
if let Some(key) = extract_name_from_slice(slice) {
if REMOVED_CONTEXT_KEYS.contains(&key.as_str()) {
checker.diagnostics.push(Diagnostic::new(
Airflow3Removal {
deprecated: key,
replacement: Replacement::None,
},
slice.range(),
));
}
}
}
}
/// AIR302
pub(crate) fn removed_in_3(checker: &mut Checker, expr: &Expr) {
if !checker.semantic().seen_module(Modules::AIRFLOW) {
@@ -87,6 +147,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());
@@ -100,10 +161,43 @@ pub(crate) fn removed_in_3(checker: &mut Checker, expr: &Expr) {
}
}
}
Expr::Subscript(subscript_expr) => {
removed_context_variable(checker, subscript_expr);
}
_ => {}
}
}
/// AIR302
pub(crate) fn removed_in_3_function_def(checker: &mut Checker, function_def: &StmtFunctionDef) {
if !checker.semantic().seen_module(Modules::AIRFLOW) {
return;
}
if !is_airflow_task(function_def, checker.semantic()) {
return;
}
for param in function_def
.parameters
.posonlyargs
.iter()
.chain(function_def.parameters.args.iter())
.chain(function_def.parameters.kwonlyargs.iter())
{
let param_name = param.parameter.name.as_str();
if REMOVED_CONTEXT_KEYS.contains(&param_name) {
checker.diagnostics.push(Diagnostic::new(
Airflow3Removal {
deprecated: param_name.to_string(),
replacement: Replacement::None,
},
param.parameter.name.range(),
));
}
}
}
#[derive(Debug, Eq, PartialEq)]
enum Replacement {
None,
@@ -247,6 +341,143 @@ fn check_class_attribute(checker: &mut Checker, attribute_expr: &ExprAttribute)
}
}
/// Finds the parameter definition for a given name expression in a function.
fn find_parameter<'a>(
semantic: &'a SemanticModel,
name: &'a ExprName,
) -> Option<AnyParameterRef<'a>> {
let binding_id = semantic.only_binding(name)?;
let binding = semantic.binding(binding_id);
let StmtFunctionDef { parameters, .. } = binding.statement(semantic)?.as_function_def_stmt()?;
parameters
.iter()
.find(|parameter| parameter.name().range() == binding.range())
}
/// Checks whether an Airflow 3.0removed context key is used in a function decorated with `@task`.
///
/// Specifically, it flags two scenarios for task decorated function:
/// 1. A removed context variable passed in as a function parameter name (e.g., `execution_date`).
/// 2. A removed context key accessed via `context.get("...")`.
///
/// # Examples
///
/// **Removed key used in `context.get(...)`:**
/// ```python
/// from airflow.decorators import task
///
/// @task
/// def my_task(**context):
/// # 'conf' is removed in Airflow 3.0
/// print(context.get("conf"))
/// ```
///
/// **Removed context variable as a parameter:**
/// ```python
/// from airflow.decorators import task
///
/// @task
/// def another_task(execution_date, **kwargs):
/// # 'execution_date' is removed in Airflow 3.0
/// pass
/// ```
///
/// **Accessing multiple keys:**
/// ```python
/// from airflow.decorators import task
///
/// @task
/// def more_keys(**context):
/// # 'prev_ds' is also removed in Airflow 3.0
/// print(context.get("prev_ds"))
/// ```
fn check_context_get(checker: &mut Checker, call_expr: &ExprCall) {
if !is_taskflow(checker) {
return;
}
let Expr::Attribute(ExprAttribute { value, attr, .. }) = &*call_expr.func else {
return;
};
let is_named_context = if let Expr::Name(name) = &**value {
if let Some(parameter) = find_parameter(checker.semantic(), name) {
matches!(parameter.name().as_str(), "context" | "kwargs")
|| parameter.name().as_str().starts_with("**")
} else {
false
}
} else {
false
};
let is_assigned_from_gcc =
if let Some(qualname) = typing::resolve_assignment(value, checker.semantic()) {
matches!(
qualname.segments(),
["airflow", "utils", "context", "get_current_context"]
)
} else {
false
};
if !(is_named_context || is_assigned_from_gcc) {
return;
}
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) {
if let Expr::StringLiteral(ExprStringLiteral { value, .. }) = argument {
if value == removed_key {
checker.diagnostics.push(Diagnostic::new(
Airflow3Removal {
deprecated: removed_key.to_string(),
replacement: Replacement::None,
},
argument.range(),
));
}
}
}
}
}
/// Check whether the function is decorated by @task
///
///
/// Examples for the above patterns:
/// ```python
/// from airflow.decorators import task
///
///
/// @task
/// def access_invalid_key_task_out_of_dag(**context):
/// print("access invalid key", context.get("conf"))
/// ```
fn is_taskflow(checker: &mut Checker) -> bool {
let mut parents = checker.semantic().current_statements();
if let Some(Stmt::FunctionDef(StmtFunctionDef { decorator_list, .. })) =
parents.find(|stmt| stmt.is_function_def_stmt())
{
for decorator in decorator_list {
if checker
.semantic()
.resolve_qualified_name(map_callable(&decorator.expression))
.is_some_and(|qualified_name| {
matches!(qualified_name.segments(), ["airflow", "decorators", "task"])
})
{
return true;
}
}
}
false
}
/// Check whether a removed Airflow class method is called.
///
/// For example:
@@ -855,3 +1086,14 @@ fn is_airflow_builtin_or_provider(segments: &[&str], module: &str, symbol_suffix
_ => false,
}
}
/// Returns `true` if the given function is decorated with `@airflow.decorators.task`.
fn is_airflow_task(function_def: &StmtFunctionDef, semantic: &SemanticModel) -> bool {
function_def.decorator_list.iter().any(|decorator| {
semantic
.resolve_qualified_name(map_callable(&decorator.expression))
.is_some_and(|qualified_name| {
matches!(qualified_name.segments(), ["airflow", "decorators", "task"])
})
})
}

View File

@@ -0,0 +1,455 @@
---
source: crates/ruff_linter/src/rules/airflow/mod.rs
snapshot_kind: text
---
AIR302_context.py:12:41: AIR302 `conf` is removed in Airflow 3.0
|
11 | def access_invalid_key_in_context(**context):
12 | print("access invalid key", context["conf"])
| ^^^^^^ AIR302
13 |
14 | @task
|
AIR302_context.py:16:45: AIR302 `conf` is removed in Airflow 3.0
|
14 | @task
15 | def access_invalid_key_task_out_of_dag(**context):
16 | print("access invalid key", context.get("conf"))
| ^^^^^^ AIR302
17 |
18 | @dag(
|
AIR302_context.py:27:49: AIR302 `conf` is removed in Airflow 3.0
|
25 | @task()
26 | def access_invalid_key_task(**context):
27 | print("access invalid key", context.get("conf"))
| ^^^^^^ AIR302
28 |
29 | task1 = PythonOperator(
|
AIR302_context.py:44:30: AIR302 `execution_date` is removed in Airflow 3.0
|
43 | # Removed usage - should trigger violations
44 | execution_date = context["execution_date"]
| ^^^^^^^^^^^^^^^^ AIR302
45 | next_ds = context["next_ds"]
46 | next_ds_nodash = context["next_ds_nodash"]
|
AIR302_context.py:45:23: AIR302 `next_ds` is removed in Airflow 3.0
|
43 | # Removed usage - should trigger violations
44 | execution_date = context["execution_date"]
45 | next_ds = context["next_ds"]
| ^^^^^^^^^ AIR302
46 | next_ds_nodash = context["next_ds_nodash"]
47 | next_execution_date = context["next_execution_date"]
|
AIR302_context.py:46:30: AIR302 `next_ds_nodash` is removed in Airflow 3.0
|
44 | execution_date = context["execution_date"]
45 | next_ds = context["next_ds"]
46 | next_ds_nodash = context["next_ds_nodash"]
| ^^^^^^^^^^^^^^^^ AIR302
47 | next_execution_date = context["next_execution_date"]
48 | prev_ds = context["prev_ds"]
|
AIR302_context.py:47:35: AIR302 `next_execution_date` is removed in Airflow 3.0
|
45 | next_ds = context["next_ds"]
46 | next_ds_nodash = context["next_ds_nodash"]
47 | next_execution_date = context["next_execution_date"]
| ^^^^^^^^^^^^^^^^^^^^^ AIR302
48 | prev_ds = context["prev_ds"]
49 | prev_ds_nodash = context["prev_ds_nodash"]
|
AIR302_context.py:48:23: AIR302 `prev_ds` is removed in Airflow 3.0
|
46 | next_ds_nodash = context["next_ds_nodash"]
47 | next_execution_date = context["next_execution_date"]
48 | prev_ds = context["prev_ds"]
| ^^^^^^^^^ AIR302
49 | prev_ds_nodash = context["prev_ds_nodash"]
50 | prev_execution_date = context["prev_execution_date"]
|
AIR302_context.py:49:30: AIR302 `prev_ds_nodash` is removed in Airflow 3.0
|
47 | next_execution_date = context["next_execution_date"]
48 | prev_ds = context["prev_ds"]
49 | prev_ds_nodash = context["prev_ds_nodash"]
| ^^^^^^^^^^^^^^^^ AIR302
50 | prev_execution_date = context["prev_execution_date"]
51 | prev_execution_date_success = context["prev_execution_date_success"]
|
AIR302_context.py:50:35: AIR302 `prev_execution_date` is removed in Airflow 3.0
|
48 | prev_ds = context["prev_ds"]
49 | prev_ds_nodash = context["prev_ds_nodash"]
50 | prev_execution_date = context["prev_execution_date"]
| ^^^^^^^^^^^^^^^^^^^^^ AIR302
51 | prev_execution_date_success = context["prev_execution_date_success"]
52 | tomorrow_ds = context["tomorrow_ds"]
|
AIR302_context.py:51:43: AIR302 `prev_execution_date_success` is removed in Airflow 3.0
|
49 | prev_ds_nodash = context["prev_ds_nodash"]
50 | prev_execution_date = context["prev_execution_date"]
51 | prev_execution_date_success = context["prev_execution_date_success"]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ AIR302
52 | tomorrow_ds = context["tomorrow_ds"]
53 | yesterday_ds = context["yesterday_ds"]
|
AIR302_context.py:52:27: AIR302 `tomorrow_ds` is removed in Airflow 3.0
|
50 | prev_execution_date = context["prev_execution_date"]
51 | prev_execution_date_success = context["prev_execution_date_success"]
52 | tomorrow_ds = context["tomorrow_ds"]
| ^^^^^^^^^^^^^ AIR302
53 | yesterday_ds = context["yesterday_ds"]
54 | yesterday_ds_nodash = context["yesterday_ds_nodash"]
|
AIR302_context.py:53:28: AIR302 `yesterday_ds` is removed in Airflow 3.0
|
51 | prev_execution_date_success = context["prev_execution_date_success"]
52 | tomorrow_ds = context["tomorrow_ds"]
53 | yesterday_ds = context["yesterday_ds"]
| ^^^^^^^^^^^^^^ AIR302
54 | yesterday_ds_nodash = context["yesterday_ds_nodash"]
|
AIR302_context.py:54:35: AIR302 `yesterday_ds_nodash` is removed in Airflow 3.0
|
52 | tomorrow_ds = context["tomorrow_ds"]
53 | yesterday_ds = context["yesterday_ds"]
54 | yesterday_ds_nodash = context["yesterday_ds_nodash"]
| ^^^^^^^^^^^^^^^^^^^^^ AIR302
55 |
56 | with DAG(
|
AIR302_context.py:58:5: AIR302 [*] `schedule_interval` is removed in Airflow 3.0
|
56 | with DAG(
57 | dag_id="example_dag",
58 | schedule_interval="@daily",
| ^^^^^^^^^^^^^^^^^ AIR302
59 | start_date=datetime(2023, 1, 1),
60 | template_searchpath=["/templates"],
|
= help: Use `schedule` instead
Safe fix
55 55 |
56 56 | with DAG(
57 57 | dag_id="example_dag",
58 |- schedule_interval="@daily",
58 |+ schedule="@daily",
59 59 | start_date=datetime(2023, 1, 1),
60 60 | template_searchpath=["/templates"],
61 61 | ) as dag:
AIR302_context.py:62:13: AIR302 `airflow.operators.dummy.DummyOperator` is removed in Airflow 3.0
|
60 | template_searchpath=["/templates"],
61 | ) as dag:
62 | task1 = DummyOperator(
| ^^^^^^^^^^^^^ AIR302
63 | task_id="task1",
64 | params={
|
= help: Use `airflow.operators.empty.EmptyOperator` instead
AIR302_context.py:75:57: AIR302 `execution_date` is removed in Airflow 3.0
|
73 | name = "custom_macros"
74 | macros = {
75 | "execution_date_macro": lambda context: context["execution_date"],
| ^^^^^^^^^^^^^^^^ AIR302
76 | "next_ds_macro": lambda context: context["next_ds"]
77 | }
|
AIR302_context.py:76:50: AIR302 `next_ds` is removed in Airflow 3.0
|
74 | macros = {
75 | "execution_date_macro": lambda context: context["execution_date"],
76 | "next_ds_macro": lambda context: context["next_ds"]
| ^^^^^^^^^ AIR302
77 | }
|
AIR302_context.py:82:30: AIR302 `execution_date` is removed in Airflow 3.0
|
80 | def print_config():
81 | context = get_current_context()
82 | execution_date = context["execution_date"]
| ^^^^^^^^^^^^^^^^ AIR302
83 | next_ds = context["next_ds"]
84 | next_ds_nodash = context["next_ds_nodash"]
|
AIR302_context.py:83:23: AIR302 `next_ds` is removed in Airflow 3.0
|
81 | context = get_current_context()
82 | execution_date = context["execution_date"]
83 | next_ds = context["next_ds"]
| ^^^^^^^^^ AIR302
84 | next_ds_nodash = context["next_ds_nodash"]
85 | next_execution_date = context["next_execution_date"]
|
AIR302_context.py:84:30: AIR302 `next_ds_nodash` is removed in Airflow 3.0
|
82 | execution_date = context["execution_date"]
83 | next_ds = context["next_ds"]
84 | next_ds_nodash = context["next_ds_nodash"]
| ^^^^^^^^^^^^^^^^ AIR302
85 | next_execution_date = context["next_execution_date"]
86 | prev_ds = context["prev_ds"]
|
AIR302_context.py:85:35: AIR302 `next_execution_date` is removed in Airflow 3.0
|
83 | next_ds = context["next_ds"]
84 | next_ds_nodash = context["next_ds_nodash"]
85 | next_execution_date = context["next_execution_date"]
| ^^^^^^^^^^^^^^^^^^^^^ AIR302
86 | prev_ds = context["prev_ds"]
87 | prev_ds_nodash = context["prev_ds_nodash"]
|
AIR302_context.py:86:23: AIR302 `prev_ds` is removed in Airflow 3.0
|
84 | next_ds_nodash = context["next_ds_nodash"]
85 | next_execution_date = context["next_execution_date"]
86 | prev_ds = context["prev_ds"]
| ^^^^^^^^^ AIR302
87 | prev_ds_nodash = context["prev_ds_nodash"]
88 | prev_execution_date = context["prev_execution_date"]
|
AIR302_context.py:87:30: AIR302 `prev_ds_nodash` is removed in Airflow 3.0
|
85 | next_execution_date = context["next_execution_date"]
86 | prev_ds = context["prev_ds"]
87 | prev_ds_nodash = context["prev_ds_nodash"]
| ^^^^^^^^^^^^^^^^ AIR302
88 | prev_execution_date = context["prev_execution_date"]
89 | prev_execution_date_success = context["prev_execution_date_success"]
|
AIR302_context.py:88:35: AIR302 `prev_execution_date` is removed in Airflow 3.0
|
86 | prev_ds = context["prev_ds"]
87 | prev_ds_nodash = context["prev_ds_nodash"]
88 | prev_execution_date = context["prev_execution_date"]
| ^^^^^^^^^^^^^^^^^^^^^ AIR302
89 | prev_execution_date_success = context["prev_execution_date_success"]
90 | tomorrow_ds = context["tomorrow_ds"]
|
AIR302_context.py:89:43: AIR302 `prev_execution_date_success` is removed in Airflow 3.0
|
87 | prev_ds_nodash = context["prev_ds_nodash"]
88 | prev_execution_date = context["prev_execution_date"]
89 | prev_execution_date_success = context["prev_execution_date_success"]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ AIR302
90 | tomorrow_ds = context["tomorrow_ds"]
91 | yesterday_ds = context["yesterday_ds"]
|
AIR302_context.py:90:27: AIR302 `tomorrow_ds` is removed in Airflow 3.0
|
88 | prev_execution_date = context["prev_execution_date"]
89 | prev_execution_date_success = context["prev_execution_date_success"]
90 | tomorrow_ds = context["tomorrow_ds"]
| ^^^^^^^^^^^^^ AIR302
91 | yesterday_ds = context["yesterday_ds"]
92 | yesterday_ds_nodash = context["yesterday_ds_nodash"]
|
AIR302_context.py:91:28: AIR302 `yesterday_ds` is removed in Airflow 3.0
|
89 | prev_execution_date_success = context["prev_execution_date_success"]
90 | tomorrow_ds = context["tomorrow_ds"]
91 | yesterday_ds = context["yesterday_ds"]
| ^^^^^^^^^^^^^^ AIR302
92 | yesterday_ds_nodash = context["yesterday_ds_nodash"]
|
AIR302_context.py:92:35: AIR302 `yesterday_ds_nodash` is removed in Airflow 3.0
|
90 | tomorrow_ds = context["tomorrow_ds"]
91 | yesterday_ds = context["yesterday_ds"]
92 | yesterday_ds_nodash = context["yesterday_ds_nodash"]
| ^^^^^^^^^^^^^^^^^^^^^ AIR302
93 |
94 | class CustomOperator(BaseOperator):
|
AIR302_context.py:96:34: AIR302 `execution_date` is removed in Airflow 3.0
|
94 | class CustomOperator(BaseOperator):
95 | def execute(self, context):
96 | execution_date = context["execution_date"]
| ^^^^^^^^^^^^^^^^ AIR302
97 | next_ds = context["next_ds"]
98 | next_ds_nodash = context["next_ds_nodash"]
|
AIR302_context.py:97:27: AIR302 `next_ds` is removed in Airflow 3.0
|
95 | def execute(self, context):
96 | execution_date = context["execution_date"]
97 | next_ds = context["next_ds"]
| ^^^^^^^^^ AIR302
98 | next_ds_nodash = context["next_ds_nodash"]
99 | next_execution_date = context["next_execution_date"]
|
AIR302_context.py:98:34: AIR302 `next_ds_nodash` is removed in Airflow 3.0
|
96 | execution_date = context["execution_date"]
97 | next_ds = context["next_ds"]
98 | next_ds_nodash = context["next_ds_nodash"]
| ^^^^^^^^^^^^^^^^ AIR302
99 | next_execution_date = context["next_execution_date"]
100 | prev_ds = context["prev_ds"]
|
AIR302_context.py:99:39: AIR302 `next_execution_date` is removed in Airflow 3.0
|
97 | next_ds = context["next_ds"]
98 | next_ds_nodash = context["next_ds_nodash"]
99 | next_execution_date = context["next_execution_date"]
| ^^^^^^^^^^^^^^^^^^^^^ AIR302
100 | prev_ds = context["prev_ds"]
101 | prev_ds_nodash = context["prev_ds_nodash"]
|
AIR302_context.py:100:27: AIR302 `prev_ds` is removed in Airflow 3.0
|
98 | next_ds_nodash = context["next_ds_nodash"]
99 | next_execution_date = context["next_execution_date"]
100 | prev_ds = context["prev_ds"]
| ^^^^^^^^^ AIR302
101 | prev_ds_nodash = context["prev_ds_nodash"]
102 | prev_execution_date = context["prev_execution_date"]
|
AIR302_context.py:101:34: AIR302 `prev_ds_nodash` is removed in Airflow 3.0
|
99 | next_execution_date = context["next_execution_date"]
100 | prev_ds = context["prev_ds"]
101 | prev_ds_nodash = context["prev_ds_nodash"]
| ^^^^^^^^^^^^^^^^ AIR302
102 | prev_execution_date = context["prev_execution_date"]
103 | prev_execution_date_success = context["prev_execution_date_success"]
|
AIR302_context.py:102:39: AIR302 `prev_execution_date` is removed in Airflow 3.0
|
100 | prev_ds = context["prev_ds"]
101 | prev_ds_nodash = context["prev_ds_nodash"]
102 | prev_execution_date = context["prev_execution_date"]
| ^^^^^^^^^^^^^^^^^^^^^ AIR302
103 | prev_execution_date_success = context["prev_execution_date_success"]
104 | tomorrow_ds = context["tomorrow_ds"]
|
AIR302_context.py:103:47: AIR302 `prev_execution_date_success` is removed in Airflow 3.0
|
101 | prev_ds_nodash = context["prev_ds_nodash"]
102 | prev_execution_date = context["prev_execution_date"]
103 | prev_execution_date_success = context["prev_execution_date_success"]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ AIR302
104 | tomorrow_ds = context["tomorrow_ds"]
105 | yesterday_ds = context["yesterday_ds"]
|
AIR302_context.py:104:31: AIR302 `tomorrow_ds` is removed in Airflow 3.0
|
102 | prev_execution_date = context["prev_execution_date"]
103 | prev_execution_date_success = context["prev_execution_date_success"]
104 | tomorrow_ds = context["tomorrow_ds"]
| ^^^^^^^^^^^^^ AIR302
105 | yesterday_ds = context["yesterday_ds"]
106 | yesterday_ds_nodash = context["yesterday_ds_nodash"]
|
AIR302_context.py:105:32: AIR302 `yesterday_ds` is removed in Airflow 3.0
|
103 | prev_execution_date_success = context["prev_execution_date_success"]
104 | tomorrow_ds = context["tomorrow_ds"]
105 | yesterday_ds = context["yesterday_ds"]
| ^^^^^^^^^^^^^^ AIR302
106 | yesterday_ds_nodash = context["yesterday_ds_nodash"]
|
AIR302_context.py:106:39: AIR302 `yesterday_ds_nodash` is removed in Airflow 3.0
|
104 | tomorrow_ds = context["tomorrow_ds"]
105 | yesterday_ds = context["yesterday_ds"]
106 | yesterday_ds_nodash = context["yesterday_ds_nodash"]
| ^^^^^^^^^^^^^^^^^^^^^ AIR302
107 |
108 | @task
|
AIR302_context.py:109:45: AIR302 `execution_date` is removed in Airflow 3.0
|
108 | @task
109 | def access_invalid_argument_task_out_of_dag(execution_date, tomorrow_ds, logical_date, **context):
| ^^^^^^^^^^^^^^ AIR302
110 | print("execution date", execution_date)
111 | print("access invalid key", context.get("conf"))
|
AIR302_context.py:109:61: AIR302 `tomorrow_ds` is removed in Airflow 3.0
|
108 | @task
109 | def access_invalid_argument_task_out_of_dag(execution_date, tomorrow_ds, logical_date, **context):
| ^^^^^^^^^^^ AIR302
110 | print("execution date", execution_date)
111 | print("access invalid key", context.get("conf"))
|
AIR302_context.py:111:45: AIR302 `conf` is removed in Airflow 3.0
|
109 | def access_invalid_argument_task_out_of_dag(execution_date, tomorrow_ds, logical_date, **context):
110 | print("execution date", execution_date)
111 | print("access invalid key", context.get("conf"))
| ^^^^^^ AIR302
112 |
113 | @task(task_id="print_the_context")
|
AIR302_context.py:117:22: AIR302 `tomorrow_ds` is removed in Airflow 3.0
|
115 | """Print the Airflow context and ds variable from the context."""
116 | print(ds)
117 | print(kwargs.get("tomorrow_ds"))
| ^^^^^^^^^^^^^ AIR302
118 | c = get_current_context()
119 | c.get("execution_date")
|
AIR302_context.py:119:11: AIR302 `execution_date` is removed in Airflow 3.0
|
117 | print(kwargs.get("tomorrow_ds"))
118 | c = get_current_context()
119 | c.get("execution_date")
| ^^^^^^^^^^^^^^^^ AIR302
|