Skip to content

Commit b5b6b65

Browse files
authored
[airflow] Add unsafe fix for module moved cases (AIR301) (#18367)
<!-- Thank you for contributing to Ruff/ty! To help us out with reviewing, please consider the following: - Does this pull request include a summary of the change? (See below.) - Does this pull request include a descriptive title? (Please prefix with `[ty]` for ty pull requests.) - Does this pull request include references to any relevant issues? --> ## Summary <!-- What's the purpose of the change? What does it do, and why? --> Follow up on #18093 and apply it to AIR301 ## Test Plan <!-- How was it tested? --> The existing test fixtures have been updated
1 parent ad2f667 commit b5b6b65

File tree

7 files changed

+1196
-1043
lines changed

7 files changed

+1196
-1043
lines changed

crates/ruff_linter/resources/test/fixtures/airflow/AIR301_names.py

Lines changed: 3 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -10,22 +10,10 @@
1010
PY312,
1111
)
1212
from airflow.api_connexion.security import requires_access
13-
from airflow.configuration import (
14-
as_dict,
15-
get,
16-
getboolean,
17-
getfloat,
18-
getint,
19-
has_option,
20-
remove_option,
21-
set,
22-
)
2313
from airflow.contrib.aws_athena_hook import AWSAthenaHook
2414
from airflow.datasets import DatasetAliasEvent
25-
from airflow.hooks.base_hook import BaseHook
2615
from airflow.operators.subdag import SubDagOperator
2716
from airflow.secrets.local_filesystem import LocalFilesystemBackend
28-
from airflow.sensors.base_sensor_operator import BaseSensorOperator
2917
from airflow.triggers.external_task import TaskStateTrigger
3018
from airflow.utils import dates
3119
from airflow.utils.dag_cycle_tester import test_cycle
@@ -40,13 +28,10 @@
4028
)
4129
from airflow.utils.db import create_session
4230
from airflow.utils.decorators import apply_defaults
43-
from airflow.utils.file import TemporaryDirectory, mkdirs
44-
from airflow.utils.helpers import chain as helper_chain
45-
from airflow.utils.helpers import cross_downstream as helper_cross_downstream
46-
from airflow.utils.log import secrets_masker
31+
from airflow.utils.file import mkdirs
4732
from airflow.utils.state import SHUTDOWN, terminating_states
4833
from airflow.utils.trigger_rule import TriggerRule
49-
from airflow.www.auth import has_access
34+
from airflow.www.auth import has_access, has_access_dataset
5035
from airflow.www.utils import get_sensitive_variables_fields, should_hide_value_for_key
5136

5237
# airflow root
@@ -55,11 +40,6 @@
5540
# airflow.api_connexion.security
5641
requires_access
5742

58-
59-
# airflow.configuration
60-
get, getboolean, getfloat, getint, has_option, remove_option, as_dict, set
61-
62-
6343
# airflow.contrib.*
6444
AWSAthenaHook()
6545

@@ -68,10 +48,6 @@
6848
DatasetAliasEvent()
6949

7050

71-
# airflow.hooks
72-
BaseHook()
73-
74-
7551
# airflow.operators.subdag.*
7652
SubDagOperator()
7753

@@ -81,10 +57,6 @@
8157
LocalFilesystemBackend()
8258

8359

84-
# airflow.sensors.base_sensor_operator
85-
BaseSensorOperator()
86-
87-
8860
# airflow.triggers.external_task
8961
TaskStateTrigger()
9062

@@ -114,15 +86,8 @@
11486
apply_defaults
11587

11688
# airflow.utils.file
117-
TemporaryDirectory()
11889
mkdirs
11990

120-
# airflow.utils.helpers
121-
helper_chain
122-
helper_cross_downstream
123-
124-
# airflow.utils.log
125-
secrets_masker
12691

12792
# airflow.utils.state
12893
SHUTDOWN
@@ -135,37 +100,8 @@
135100

136101
# airflow.www.auth
137102
has_access
103+
has_access_dataset
138104

139105
# airflow.www.utils
140106
get_sensitive_variables_fields
141107
should_hide_value_for_key
142-
143-
# airflow.operators.python
144-
from airflow.operators.python import get_current_context
145-
146-
get_current_context()
147-
148-
# airflow.providers.mysql
149-
from airflow.providers.mysql.datasets.mysql import sanitize_uri
150-
151-
sanitize_uri
152-
153-
# airflow.providers.postgres
154-
from airflow.providers.postgres.datasets.postgres import sanitize_uri
155-
156-
sanitize_uri
157-
158-
# airflow.providers.trino
159-
from airflow.providers.trino.datasets.trino import sanitize_uri
160-
161-
sanitize_uri
162-
163-
# airflow.notifications.basenotifier
164-
from airflow.notifications.basenotifier import BaseNotifier
165-
166-
BaseNotifier()
167-
168-
# airflow.auth.manager
169-
from airflow.auth.managers.base_auth_manager import BaseAuthManager
170-
171-
BaseAuthManager()

crates/ruff_linter/resources/test/fixtures/airflow/AIR301_names_fix.py

Lines changed: 74 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
from airflow.api_connexion.security import requires_access_dataset
44
from airflow.auth.managers.models.resource_details import (
55
DatasetDetails,
6-
76
)
87
from airflow.datasets.manager import (
98
DatasetManager,
@@ -12,15 +11,13 @@
1211
)
1312
from airflow.lineage.hook import DatasetLineageInfo
1413
from airflow.metrics.validators import AllowListValidator, BlockListValidator
15-
from airflow.secrets.local_filesystm import load_connections
14+
from airflow.secrets.local_filesystem import load_connections
1615
from airflow.security.permissions import RESOURCE_DATASET
17-
from airflow.www.auth import has_access_dataset
1816

1917
requires_access_dataset()
2018

2119
DatasetDetails()
2220

23-
2421
DatasetManager()
2522
dataset_manager()
2623
resolve_dataset_manager()
@@ -34,7 +31,6 @@
3431

3532
RESOURCE_DATASET
3633

37-
has_access_dataset()
3834

3935
from airflow.listeners.spec.dataset import (
4036
on_dataset_changed,
@@ -43,3 +39,76 @@
4339

4440
on_dataset_created()
4541
on_dataset_changed()
42+
43+
44+
# airflow.operators.python
45+
from airflow.operators.python import get_current_context
46+
47+
get_current_context()
48+
49+
# airflow.providers.mysql
50+
from airflow.providers.mysql.datasets.mysql import sanitize_uri
51+
52+
sanitize_uri
53+
54+
# airflow.providers.postgres
55+
from airflow.providers.postgres.datasets.postgres import sanitize_uri
56+
57+
sanitize_uri
58+
59+
# airflow.providers.trino
60+
from airflow.providers.trino.datasets.trino import sanitize_uri
61+
62+
sanitize_uri
63+
64+
# airflow.notifications.basenotifier
65+
from airflow.notifications.basenotifier import BaseNotifier
66+
67+
BaseNotifier()
68+
69+
# airflow.auth.manager
70+
from airflow.auth.managers.base_auth_manager import BaseAuthManager
71+
72+
BaseAuthManager()
73+
74+
75+
from airflow.configuration import (
76+
as_dict,
77+
get,
78+
getboolean,
79+
getfloat,
80+
getint,
81+
has_option,
82+
remove_option,
83+
set,
84+
)
85+
86+
# airflow.configuration
87+
get, getboolean, getfloat, getint, has_option, remove_option, as_dict, set
88+
from airflow.hooks.base_hook import BaseHook
89+
90+
# airflow.hooks
91+
BaseHook()
92+
93+
from airflow.sensors.base_sensor_operator import BaseSensorOperator
94+
95+
# airflow.sensors.base_sensor_operator
96+
BaseSensorOperator()
97+
BaseHook()
98+
99+
from airflow.utils.helpers import chain as helper_chain
100+
from airflow.utils.helpers import cross_downstream as helper_cross_downstream
101+
102+
# airflow.utils.helpers
103+
helper_chain
104+
helper_cross_downstream
105+
106+
# airflow.utils.file
107+
from airflow.utils.file import TemporaryDirectory
108+
109+
TemporaryDirectory()
110+
111+
from airflow.utils.log import secrets_masker
112+
113+
# airflow.utils.log
114+
secrets_masker
Lines changed: 32 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,54 +1,54 @@
11
from __future__ import annotations
22

33
from airflow.providers.amazon.aws.auth_manager.avp.entities import AvpEntities
4-
from airflow.providers.amazon.aws.datasets.s3 import (
5-
convert_dataset_to_openlineage as s3_convert_dataset_to_openlineage,
6-
)
7-
from airflow.providers.amazon.aws.datasets.s3 import create_dataset as s3_create_dataset
8-
from airflow.providers.common.io.dataset.file import (
9-
convert_dataset_to_openlineage as io_convert_dataset_to_openlineage,
10-
)
11-
from airflow.providers.common.io.dataset.file import create_dataset as io_create_dataset
12-
13-
from airflow.providers.google.datasets.bigquery import (
14-
create_dataset as bigquery_create_dataset,
15-
)
16-
from airflow.providers.google.datasets.gcs import (
17-
convert_dataset_to_openlineage as gcs_convert_dataset_to_openlineage,
18-
)
19-
from airflow.providers.google.datasets.gcs import create_dataset as gcs_create_dataset
204
from airflow.providers.openlineage.utils.utils import (
215
DatasetInfo,
226
translate_airflow_dataset,
237
)
8+
from airflow.secrets.local_filesystem import load_connections
9+
from airflow.security.permissions import RESOURCE_DATASET
2410

2511
AvpEntities.DATASET
2612

13+
# airflow.providers.openlineage.utils.utils
14+
DatasetInfo()
15+
translate_airflow_dataset()
16+
17+
# airflow.secrets.local_filesystem
18+
load_connections()
19+
20+
# airflow.security.permissions
21+
RESOURCE_DATASET
22+
23+
from airflow.providers.amazon.aws.datasets.s3 import (
24+
convert_dataset_to_openlineage as s3_convert_dataset_to_openlineage,
25+
)
26+
from airflow.providers.amazon.aws.datasets.s3 import create_dataset as s3_create_dataset
27+
2728
s3_create_dataset()
2829
s3_convert_dataset_to_openlineage()
2930

31+
from airflow.providers.common.io.dataset.file import (
32+
convert_dataset_to_openlineage as io_convert_dataset_to_openlineage,
33+
)
34+
from airflow.providers.common.io.dataset.file import create_dataset as io_create_dataset
35+
3036
io_create_dataset()
3137
io_convert_dataset_to_openlineage()
3238

3339

40+
# # airflow.providers.google.datasets.bigquery
41+
from airflow.providers.google.datasets.bigquery import (
42+
create_dataset as bigquery_create_dataset,
43+
)
3444

35-
# airflow.providers.google.datasets.bigquery
3645
bigquery_create_dataset()
46+
3747
# airflow.providers.google.datasets.gcs
48+
from airflow.providers.google.datasets.gcs import (
49+
convert_dataset_to_openlineage as gcs_convert_dataset_to_openlineage,
50+
)
51+
from airflow.providers.google.datasets.gcs import create_dataset as gcs_create_dataset
52+
3853
gcs_create_dataset()
3954
gcs_convert_dataset_to_openlineage()
40-
# airflow.providers.openlineage.utils.utils
41-
DatasetInfo()
42-
translate_airflow_dataset()
43-
#
44-
# airflow.secrets.local_filesystem
45-
load_connections()
46-
#
47-
# airflow.security.permissions
48-
RESOURCE_DATASET
49-
50-
# airflow.timetables
51-
DatasetTriggeredTimetable()
52-
#
53-
# airflow.www.auth
54-
has_access_dataset

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

Lines changed: 14 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use crate::checkers::ast::Checker;
2-
use crate::importer::ImportRequest;
32
use crate::rules::airflow::helpers::{
4-
Replacement, is_airflow_builtin_or_provider, is_guarded_by_try_except,
3+
Replacement, generate_import_edit, generate_remove_and_runtime_import_edit,
4+
is_airflow_builtin_or_provider, is_guarded_by_try_except,
55
};
66
use crate::{Edit, Fix, FixAvailability, Violation};
77
use ruff_macros::{ViolationMetadata, derive_message_formats};
@@ -614,7 +614,6 @@ fn check_name(checker: &Checker, expr: &Expr, range: TextRange) {
614614
},
615615

616616
// airflow.configuration
617-
// TODO: check whether we could improve it
618617
[
619618
"airflow",
620619
"configuration",
@@ -984,24 +983,19 @@ fn check_name(checker: &Checker, expr: &Expr, range: TextRange) {
984983
}
985984

986985
let import_target = name.split('.').next().unwrap_or(name);
986+
let mut diagnostic = checker.report_diagnostic(
987+
Airflow3Removal {
988+
deprecated: qualified_name.to_string(),
989+
replacement: replacement.clone(),
990+
},
991+
range,
992+
);
987993

988-
checker
989-
.report_diagnostic(
990-
Airflow3Removal {
991-
deprecated: qualified_name.to_string(),
992-
replacement: replacement.clone(),
993-
},
994-
range,
995-
)
996-
.try_set_fix(|| {
997-
let (import_edit, _) = checker.importer().get_or_import_symbol(
998-
&ImportRequest::import_from(module, import_target),
999-
expr.start(),
1000-
checker.semantic(),
1001-
)?;
1002-
let replacement_edit = Edit::range_replacement(name.to_string(), range);
1003-
Ok(Fix::safe_edits(import_edit, [replacement_edit]))
1004-
});
994+
if let Some(fix) = generate_import_edit(expr, checker, module, import_target, range)
995+
.or_else(|| generate_remove_and_runtime_import_edit(expr, checker, module, name))
996+
{
997+
diagnostic.set_fix(fix);
998+
}
1005999
}
10061000

10071001
/// Check whether a customized Airflow plugin contains removed extensions.

0 commit comments

Comments
 (0)