Skip to content

Commit 77e3c45

Browse files
committed
feat(AIR303): add rules for names moved to fab provider
* import path (module/package) moved * `airflow.api.auth.backend.basic_auth` → `airflow.providers.fab.auth_manager.api.auth.backend.basic_auth` * `airflow.api.auth.backend.kerberos_auth` → `airflow.providers.fab.auth_manager.api.auth.backend.kerberos_auth` * `airflow.auth.managers.fab.api.auth.backend.kerberos_auth` → `airflow.providers.fab.auth_manager.api.auth.backend.kerberos_auth` * `airflow.auth.managers.fab.security_manager.override` → `airflow.providers.fab.auth_manager.security_manager.override` * other names (e.g., functions, classes) moved * `airflow.www.security.FabAirflowSecurityManagerOverride` → `airflow.providers.fab.auth_manager.security_manager.override.FabAirflowSecurityManagerOverride` * `airflow.auth.managers.fab.fab_auth_manager.FabAuthManager` → `airflow.providers.fab.auth_manager.security_manager.FabAuthManager`
1 parent c1837e4 commit 77e3c45

File tree

8 files changed

+283
-0
lines changed

8 files changed

+283
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
from airflow.api.auth.backend import basic_auth, kerberos_auth
2+
from airflow.api.auth.backend.basic_auth import auth_current_user
3+
from airflow.auth.managers.fab.api.auth.backend import (
4+
kerberos_auth as backend_kerberos_auth,
5+
)
6+
from airflow.auth.managers.fab.fab_auth_manager import FabAuthManager
7+
from airflow.auth.managers.fab.security_manager import override as fab_override
8+
from airflow.www.security import FabAirflowSecurityManagerOverride
9+
10+
basic_auth, kerberos_auth
11+
auth_current_user
12+
backend_kerberos_auth
13+
fab_override
14+
15+
FabAuthManager
16+
FabAirflowSecurityManagerOverride

crates/ruff_linter/src/checkers/ast/analyze/expression.rs

+3
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,9 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) {
223223
if checker.enabled(Rule::Airflow3Removal) {
224224
airflow::rules::removed_in_3(checker, expr);
225225
}
226+
if checker.enabled(Rule::Airflow3MovedToProvider) {
227+
airflow::rules::moved_to_provider_in_3(checker, expr);
228+
}
226229

227230
// Ex) List[...]
228231
if checker.any_enabled(&[

crates/ruff_linter/src/codes.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1046,6 +1046,7 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> {
10461046
(Airflow, "001") => (RuleGroup::Stable, rules::airflow::rules::AirflowVariableNameTaskIdMismatch),
10471047
(Airflow, "301") => (RuleGroup::Preview, rules::airflow::rules::AirflowDagNoScheduleArgument),
10481048
(Airflow, "302") => (RuleGroup::Preview, rules::airflow::rules::Airflow3Removal),
1049+
(Airflow, "303") => (RuleGroup::Preview, rules::airflow::rules::Airflow3MovedToProvider),
10491050

10501051
// perflint
10511052
(Perflint, "101") => (RuleGroup::Stable, rules::perflint::rules::UnnecessaryListCast),

crates/ruff_linter/src/rules/airflow/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ mod tests {
1616
#[test_case(Rule::AirflowDagNoScheduleArgument, Path::new("AIR301.py"))]
1717
#[test_case(Rule::Airflow3Removal, Path::new("AIR302_args.py"))]
1818
#[test_case(Rule::Airflow3Removal, Path::new("AIR302_names.py"))]
19+
#[test_case(Rule::Airflow3MovedToProvider, Path::new("AIR303.py"))]
1920
fn rules(rule_code: Rule, path: &Path) -> Result<()> {
2021
let snapshot = format!("{}_{}", rule_code.noqa_code(), path.to_string_lossy());
2122
let diagnostics = test_path(
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
pub(crate) use dag_schedule_argument::*;
2+
pub(crate) use moved_to_provider_in_3::*;
23
pub(crate) use removal_in_3::*;
34
pub(crate) use task_variable_name::*;
45

56
mod dag_schedule_argument;
7+
mod moved_to_provider_in_3;
68
mod removal_in_3;
79
mod task_variable_name;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
use ruff_diagnostics::{Diagnostic, Violation};
2+
use ruff_macros::{derive_message_formats, ViolationMetadata};
3+
use ruff_python_ast::{Expr, ExprAttribute};
4+
use ruff_python_semantic::Modules;
5+
use ruff_text_size::Ranged;
6+
7+
use crate::checkers::ast::Checker;
8+
9+
#[derive(Debug, Eq, PartialEq)]
10+
enum Replacement {
11+
ProviderName {
12+
name: &'static str,
13+
provider: &'static str,
14+
version: &'static str,
15+
},
16+
ImportPathMoved {
17+
original_path: &'static str,
18+
new_path: &'static str,
19+
provider: &'static str,
20+
version: &'static str,
21+
},
22+
}
23+
24+
/// ## What it does
25+
/// Checks for uses of Airflow functions and values that have been moved to it providers.
26+
/// (e.g., apache-airflow-providers-fab)
27+
///
28+
/// ## Why is this bad?
29+
/// Airflow 3.0 moved various deprecated functions, members, and other
30+
/// values to its providers. The user needs to install the corresponding provider and replace
31+
/// the original usage with the one in the provider
32+
///
33+
/// ## Example
34+
/// ```python
35+
/// from airflow.auth.managers.fab.fab_auth_manage import FabAuthManager
36+
/// ```
37+
///
38+
/// Use instead:
39+
/// ```python
40+
/// from airflow.providers.fab.auth_manager.fab_auth_manage import FabAuthManager
41+
/// ```
42+
#[derive(ViolationMetadata)]
43+
pub(crate) struct Airflow3MovedToProvider {
44+
deprecated: String,
45+
replacement: Replacement,
46+
}
47+
48+
impl Violation for Airflow3MovedToProvider {
49+
#[derive_message_formats]
50+
fn message(&self) -> String {
51+
let Airflow3MovedToProvider {
52+
deprecated,
53+
replacement,
54+
} = self;
55+
match replacement {
56+
Replacement::ProviderName {
57+
name: _,
58+
provider,
59+
version: _,
60+
} => {
61+
format!("`{deprecated}` is moved into `{provider}` provider in Airflow 3.0;")
62+
}
63+
Replacement::ImportPathMoved {
64+
original_path,
65+
new_path: _,
66+
provider,
67+
version: _,
68+
} => {
69+
format!("Import path `{original_path}` is moved into `{provider}` provider in Airflow 3.0;")
70+
}
71+
}
72+
}
73+
74+
fn fix_title(&self) -> Option<String> {
75+
let Airflow3MovedToProvider { replacement, .. } = self;
76+
if let Replacement::ProviderName {
77+
name,
78+
provider,
79+
version,
80+
} = replacement
81+
{
82+
Some(
83+
format!("Install `apache-airflow-provider-{provider}>={version}` and use `{name}` instead.")
84+
)
85+
} else if let Replacement::ImportPathMoved {
86+
original_path: _,
87+
new_path,
88+
provider,
89+
version,
90+
} = replacement
91+
{
92+
Some(format!("Install `apache-airflow-provider-{provider}>={version}` and import from `{new_path}` instead."))
93+
} else {
94+
None
95+
}
96+
}
97+
}
98+
99+
fn moved_to_provider(checker: &mut Checker, expr: &Expr, ranged: impl Ranged) {
100+
let result =
101+
checker
102+
.semantic()
103+
.resolve_qualified_name(expr)
104+
.and_then(|qualname| match qualname.segments() {
105+
// apache-airflow-providers-fab
106+
["airflow", "www", "security", "FabAirflowSecurityManagerOverride"] => Some((
107+
qualname.to_string(),
108+
Replacement::ProviderName {
109+
name: "airflow.providers.fab.auth_manager.security_manager.override.FabAirflowSecurityManagerOverride",
110+
provider: "fab",
111+
version: "1.0.0"
112+
},
113+
)),
114+
["airflow","auth","managers","fab","fab_auth_manager", "FabAuthManager"] => Some((
115+
qualname.to_string(),
116+
Replacement::ProviderName{
117+
name: "airflow.providers.fab.auth_manager.security_manager.FabAuthManager",
118+
provider: "fab",
119+
version: "1.0.0"
120+
},
121+
)),
122+
["airflow", "api", "auth", "backend", "basic_auth", ..] => Some((
123+
qualname.to_string(),
124+
Replacement::ImportPathMoved{
125+
original_path: "airflow.api.auth.backend.basic_auth",
126+
new_path: "airflow.providers.fab.auth_manager.api.auth.backend.basic_auth",
127+
provider:"fab",
128+
version: "1.0.0"
129+
},
130+
)),
131+
["airflow", "api","auth","backend","kerberos_auth", ..] => Some((
132+
qualname.to_string(),
133+
Replacement::ImportPathMoved{
134+
original_path:"airflow.api.auth.backend.kerberos_auth",
135+
new_path: "airflow.providers.fab.auth_manager.api.auth.backend.kerberos_auth",
136+
provider: "fab",
137+
version:"1.0.0"
138+
},
139+
)),
140+
["airflow", "auth", "managers", "fab", "api", "auth", "backend", "kerberos_auth", ..] => Some((
141+
qualname.to_string(),
142+
Replacement::ImportPathMoved{
143+
original_path: "airflow.auth_manager.api.auth.backend.kerberos_auth",
144+
new_path: "airflow.providers.fab.auth_manager.api.auth.backend.kerberos_auth",
145+
provider: "fab",
146+
version: "1.0.0"
147+
},
148+
)),
149+
["airflow","auth","managers","fab","security_manager","override", ..] => Some((
150+
qualname.to_string(),
151+
Replacement::ImportPathMoved{
152+
original_path: "airflow.auth.managers.fab.security_manager.override",
153+
new_path: "airflow.providers.fab.auth_manager.security_manager.override",
154+
provider: "fab",
155+
version: "1.0.0"
156+
},
157+
)),
158+
159+
_ => None,
160+
});
161+
if let Some((deprecated, replacement)) = result {
162+
checker.diagnostics.push(Diagnostic::new(
163+
Airflow3MovedToProvider {
164+
deprecated,
165+
replacement,
166+
},
167+
ranged.range(),
168+
));
169+
}
170+
}
171+
172+
/// AIR303
173+
pub(crate) fn moved_to_provider_in_3(checker: &mut Checker, expr: &Expr) {
174+
if !checker.semantic().seen_module(Modules::AIRFLOW) {
175+
return;
176+
}
177+
178+
match expr {
179+
Expr::Attribute(ExprAttribute { attr: ranged, .. }) => {
180+
moved_to_provider(checker, expr, ranged);
181+
}
182+
ranged @ Expr::Name(_) => moved_to_provider(checker, expr, ranged),
183+
_ => {}
184+
}
185+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
---
2+
source: crates/ruff_linter/src/rules/airflow/mod.rs
3+
snapshot_kind: text
4+
---
5+
AIR303.py:10:1: AIR303 Import path `airflow.api.auth.backend.basic_auth` is moved into `fab` provider in Airflow 3.0;
6+
|
7+
8 | from airflow.www.security import FabAirflowSecurityManagerOverride
8+
9 |
9+
10 | basic_auth, kerberos_auth
10+
| ^^^^^^^^^^ AIR303
11+
11 | auth_current_user
12+
12 | backend_kerberos_auth
13+
|
14+
= help: Install `apache-airflow-provider-fab>=1.0.0` and import from `airflow.providers.fab.auth_manager.api.auth.backend.basic_auth` instead.
15+
16+
AIR303.py:10:13: AIR303 Import path `airflow.api.auth.backend.kerberos_auth` is moved into `fab` provider in Airflow 3.0;
17+
|
18+
8 | from airflow.www.security import FabAirflowSecurityManagerOverride
19+
9 |
20+
10 | basic_auth, kerberos_auth
21+
| ^^^^^^^^^^^^^ AIR303
22+
11 | auth_current_user
23+
12 | backend_kerberos_auth
24+
|
25+
= help: Install `apache-airflow-provider-fab>=1.0.0` and import from `airflow.providers.fab.auth_manager.api.auth.backend.kerberos_auth` instead.
26+
27+
AIR303.py:11:1: AIR303 Import path `airflow.api.auth.backend.basic_auth` is moved into `fab` provider in Airflow 3.0;
28+
|
29+
10 | basic_auth, kerberos_auth
30+
11 | auth_current_user
31+
| ^^^^^^^^^^^^^^^^^ AIR303
32+
12 | backend_kerberos_auth
33+
13 | fab_override
34+
|
35+
= help: Install `apache-airflow-provider-fab>=1.0.0` and import from `airflow.providers.fab.auth_manager.api.auth.backend.basic_auth` instead.
36+
37+
AIR303.py:12:1: AIR303 Import path `airflow.auth_manager.api.auth.backend.kerberos_auth` is moved into `fab` provider in Airflow 3.0;
38+
|
39+
10 | basic_auth, kerberos_auth
40+
11 | auth_current_user
41+
12 | backend_kerberos_auth
42+
| ^^^^^^^^^^^^^^^^^^^^^ AIR303
43+
13 | fab_override
44+
|
45+
= help: Install `apache-airflow-provider-fab>=1.0.0` and import from `airflow.providers.fab.auth_manager.api.auth.backend.kerberos_auth` instead.
46+
47+
AIR303.py:13:1: AIR303 Import path `airflow.auth.managers.fab.security_manager.override` is moved into `fab` provider in Airflow 3.0;
48+
|
49+
11 | auth_current_user
50+
12 | backend_kerberos_auth
51+
13 | fab_override
52+
| ^^^^^^^^^^^^ AIR303
53+
14 |
54+
15 | FabAuthManager
55+
|
56+
= help: Install `apache-airflow-provider-fab>=1.0.0` and import from `airflow.providers.fab.auth_manager.security_manager.override` instead.
57+
58+
AIR303.py:15:1: AIR303 `airflow.auth.managers.fab.fab_auth_manager.FabAuthManager` is moved into `fab` provider in Airflow 3.0;
59+
|
60+
13 | fab_override
61+
14 |
62+
15 | FabAuthManager
63+
| ^^^^^^^^^^^^^^ AIR303
64+
16 | FabAirflowSecurityManagerOverride
65+
|
66+
= help: Install `apache-airflow-provider-fab>=1.0.0` and use `airflow.providers.fab.auth_manager.security_manager.FabAuthManager` instead.
67+
68+
AIR303.py:16:1: AIR303 `airflow.www.security.FabAirflowSecurityManagerOverride` is moved into `fab` provider in Airflow 3.0;
69+
|
70+
15 | FabAuthManager
71+
16 | FabAirflowSecurityManagerOverride
72+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ AIR303
73+
|
74+
= help: Install `apache-airflow-provider-fab>=1.0.0` and use `airflow.providers.fab.auth_manager.security_manager.override.FabAirflowSecurityManagerOverride` instead.

ruff.schema.json

+1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)