-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add AIR001: task variable name should be same as task_id arg (#4687)
- Loading branch information
Showing
11 changed files
with
194 additions
and
7 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
from airflow.operators import PythonOperator | ||
|
||
|
||
def my_callable(): | ||
pass | ||
|
||
|
||
my_task = PythonOperator(task_id="my_task", callable=my_callable) | ||
my_task_2 = PythonOperator(callable=my_callable, task_id="my_task_2") | ||
|
||
incorrect_name = PythonOperator(task_id="my_task") | ||
incorrect_name_2 = PythonOperator(callable=my_callable, task_id="my_task_2") | ||
|
||
from my_module import MyClass | ||
|
||
incorrect_name = MyClass(task_id="my_task") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
//! Airflow-specific rules. | ||
pub(crate) mod rules; | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
use std::path::Path; | ||
|
||
use anyhow::Result; | ||
use test_case::test_case; | ||
|
||
use crate::registry::Rule; | ||
use crate::test::test_path; | ||
use crate::{assert_messages, settings}; | ||
|
||
#[test_case(Rule::AirflowVariableNameTaskIdMismatch, Path::new("AIR001.py"); "AIR001")] | ||
fn rules(rule_code: Rule, path: &Path) -> Result<()> { | ||
let snapshot = format!("{}_{}", rule_code.noqa_code(), path.to_string_lossy()); | ||
let diagnostics = test_path( | ||
Path::new("airflow").join(path).as_path(), | ||
&settings::Settings::for_rule(rule_code), | ||
)?; | ||
assert_messages!(snapshot, diagnostics); | ||
Ok(()) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
mod task_variable_name; | ||
|
||
pub(crate) use task_variable_name::{variable_name_task_id, AirflowVariableNameTaskIdMismatch}; |
102 changes: 102 additions & 0 deletions
102
crates/ruff/src/rules/airflow/rules/task_variable_name.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
use rustpython_parser::ast; | ||
use rustpython_parser::ast::{Expr, Ranged}; | ||
|
||
use ruff_diagnostics::{Diagnostic, Violation}; | ||
use ruff_macros::{derive_message_formats, violation}; | ||
use ruff_python_ast::prelude::Constant; | ||
|
||
use crate::checkers::ast::Checker; | ||
|
||
/// ## What it does | ||
/// Checks that the task variable name matches the `task_id` value for | ||
/// Airflow Operators. | ||
/// | ||
/// ## Why is this bad? | ||
/// When initializing an Airflow Operator, for consistency, the variable | ||
/// name should match the `task_id` value. This makes it easier to | ||
/// follow the flow of the DAG. | ||
/// | ||
/// ## Example | ||
/// ```python | ||
/// from airflow.operators import PythonOperator | ||
/// | ||
/// | ||
/// incorrect_name = PythonOperator(task_id="my_task") | ||
/// ``` | ||
/// | ||
/// Use instead: | ||
/// ```python | ||
/// from airflow.operators import PythonOperator | ||
/// | ||
/// | ||
/// my_task = PythonOperator(task_id="my_task") | ||
/// ``` | ||
#[violation] | ||
pub struct AirflowVariableNameTaskIdMismatch { | ||
task_id: String, | ||
} | ||
|
||
impl Violation for AirflowVariableNameTaskIdMismatch { | ||
#[derive_message_formats] | ||
fn message(&self) -> String { | ||
let AirflowVariableNameTaskIdMismatch { task_id } = self; | ||
format!("Task variable name should match the `task_id`: \"{task_id}\"") | ||
} | ||
} | ||
|
||
/// AIR001 | ||
pub(crate) fn variable_name_task_id( | ||
checker: &mut Checker, | ||
targets: &[Expr], | ||
value: &Expr, | ||
) -> Option<Diagnostic> { | ||
// If we have more than one target, we can't do anything. | ||
if targets.len() != 1 { | ||
return None; | ||
} | ||
|
||
let target = &targets[0]; | ||
let Expr::Name(ast::ExprName { id, .. }) = target else { | ||
return None; | ||
}; | ||
|
||
// If the value is not a call, we can't do anything. | ||
let Expr::Call(ast::ExprCall { func, keywords, .. }) = value else { | ||
return None; | ||
}; | ||
|
||
// If the function doesn't come from Airflow, we can't do anything. | ||
if !checker | ||
.semantic_model() | ||
.resolve_call_path(func) | ||
.map_or(false, |call_path| matches!(call_path[0], "airflow")) | ||
{ | ||
return None; | ||
} | ||
|
||
// If the call doesn't have a `task_id` keyword argument, we can't do anything. | ||
let keyword = keywords | ||
.iter() | ||
.find(|keyword| keyword.arg.as_ref().map_or(false, |arg| arg == "task_id"))?; | ||
|
||
// If the keyword argument is not a string, we can't do anything. | ||
let task_id = match &keyword.value { | ||
Expr::Constant(constant) => match &constant.value { | ||
Constant::Str(value) => value, | ||
_ => return None, | ||
}, | ||
_ => return None, | ||
}; | ||
|
||
// If the target name is the same as the task_id, no violation. | ||
if id == task_id { | ||
return None; | ||
} | ||
|
||
Some(Diagnostic::new( | ||
AirflowVariableNameTaskIdMismatch { | ||
task_id: task_id.to_string(), | ||
}, | ||
target.range(), | ||
)) | ||
} |
22 changes: 22 additions & 0 deletions
22
crates/ruff/src/rules/airflow/snapshots/ruff__rules__airflow__tests__AIR001_AIR001.py.snap
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
--- | ||
source: crates/ruff/src/rules/airflow/mod.rs | ||
--- | ||
AIR001.py:11:1: AIR001 Task variable name should match the `task_id`: "my_task" | ||
| | ||
11 | my_task_2 = PythonOperator(callable=my_callable, task_id="my_task_2") | ||
12 | | ||
13 | incorrect_name = PythonOperator(task_id="my_task") | ||
| ^^^^^^^^^^^^^^ AIR001 | ||
14 | incorrect_name_2 = PythonOperator(callable=my_callable, task_id="my_task_2") | ||
| | ||
|
||
AIR001.py:12:1: AIR001 Task variable name should match the `task_id`: "my_task_2" | ||
| | ||
12 | incorrect_name = PythonOperator(task_id="my_task") | ||
13 | incorrect_name_2 = PythonOperator(callable=my_callable, task_id="my_task_2") | ||
| ^^^^^^^^^^^^^^^^ AIR001 | ||
14 | | ||
15 | from my_module import MyClass | ||
| | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.