From 380ae52e56f38013a69d159c7058179bfac9afe9 Mon Sep 17 00:00:00 2001 From: Wei Lee Date: Fri, 9 May 2025 17:16:20 +0800 Subject: [PATCH 1/8] fix(AIR311): fix `airflow.models.baseoperatorlink.BaseOperatorLink` to `airflow.sdk.BaseOperatorLink` rule --- .../airflow/rules/suggested_to_update_3_0.rs | 15 +++++++-------- ...s__airflow__tests__AIR311_AIR311_names.py.snap | 10 ++++++++++ 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/crates/ruff_linter/src/rules/airflow/rules/suggested_to_update_3_0.rs b/crates/ruff_linter/src/rules/airflow/rules/suggested_to_update_3_0.rs index 80c5bdca0e0e6..ad9ab909ef05a 100644 --- a/crates/ruff_linter/src/rules/airflow/rules/suggested_to_update_3_0.rs +++ b/crates/ruff_linter/src/rules/airflow/rules/suggested_to_update_3_0.rs @@ -248,16 +248,15 @@ fn check_name(checker: &Checker, expr: &Expr, range: TextRange) { } // airflow.models.baseoperator - ["airflow", "models", "baseoperator", rest] => match *rest { - "chain" | "chain_linear" | "cross_downstream" => Replacement::SourceModuleMoved { + ["airflow", "models", "baseoperator", rest @ ("chain" | "chain_linear" | "cross_downstream")] => { + Replacement::SourceModuleMoved { module: "airflow.sdk", name: (*rest).to_string(), - }, - "BaseOperatorLink" => Replacement::AutoImport { - module: "airflow.sdk.definitions.baseoperatorlink", - name: "BaseOperatorLink", - }, - _ => return, + } + } + ["airflow", "models", "baseoperatorlink", "BaseOperatorLink"] => Replacement::AutoImport { + module: "airflow.sdk.definitions.baseoperatorlink", + name: "BaseOperatorLink", }, // airflow.model..DAG ["airflow", "models", .., "DAG"] => Replacement::SourceModuleMoved { diff --git a/crates/ruff_linter/src/rules/airflow/snapshots/ruff_linter__rules__airflow__tests__AIR311_AIR311_names.py.snap b/crates/ruff_linter/src/rules/airflow/snapshots/ruff_linter__rules__airflow__tests__AIR311_AIR311_names.py.snap index 8828f7b1164ac..6c6e72b7a1b3a 100644 --- a/crates/ruff_linter/src/rules/airflow/snapshots/ruff_linter__rules__airflow__tests__AIR311_AIR311_names.py.snap +++ b/crates/ruff_linter/src/rules/airflow/snapshots/ruff_linter__rules__airflow__tests__AIR311_AIR311_names.py.snap @@ -328,6 +328,16 @@ AIR311_names.py:56:1: AIR311 `airflow.models.baseoperator.cross_downstream` is r | = help: Use `airflow.sdk.cross_downstream` instead +AIR311_names.py:59:1: AIR311 `airflow.models.baseoperatorlink.BaseOperatorLink` is removed in Airflow 3.0; It still works in Airflow 3.0 but is expected to be removed in a future version. + | +58 | # airflow.models.baseoperatolinker +59 | BaseOperatorLink() + | ^^^^^^^^^^^^^^^^ AIR311 +60 | +61 | # airflow.models.dag + | + = help: Use `airflow.sdk.definitions.baseoperatorlink.BaseOperatorLink` instead + AIR311_names.py:62:1: AIR311 [*] `airflow.models.dag.DAG` is removed in Airflow 3.0; It still works in Airflow 3.0 but is expected to be removed in a future version. | 61 | # airflow.models.dag From 5baa3393ca1e9de504b3d4900fdd8c2875bfe893 Mon Sep 17 00:00:00 2001 From: Wei Lee Date: Fri, 9 May 2025 19:02:34 +0800 Subject: [PATCH 2/8] fix(AIR301): update rules based on the latest Airflow 3.0 implementation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Remove the following rules * name * `airflow.auth.managers.base_auth_manager.is_authorized_dataset` → `airflow.api_fastapi.auth.managers.base_auth_manager.is_authorized_asset` * `airflow.providers.fab.auth_manager.fab_auth_manager.is_authorized_dataset` → `airflow.providers.fab.auth_manager.fab_auth_manager.is_authorized_asset` * Update the following rules * name * `airflow.api_connexion.security.requires_access` → "Use `airflow.api_fastapi.core_api.security.requires_access_*` instead`" * `airflow.api_connexion.security.requires_access_dataset`→ `airflow.api_fastapi.core_api.security.requires_access_asset` * `airflow.notifications.basenotifier.BaseNotifier` → `airflow.sdk.bases.notifier.BaseNotifier` * `airflow.www.auth.has_access` → None * `airflow.www.auth.has_access_dataset` → None * `airflow.www.utils.get_sensitive_variables_fields`→ None * `airflow.www.utils.should_hide_value_for_key`→ None * Add the following rules * class attribute * `airflow.auth.managers.base_auth_manager.BaseAuthManager` | `airflow.providers.fab.auth_manager.fab_auth_manager.FabAuthManager` * `is_authorized_dataset` → `is_authorized_asset` --- .../airflow/AIR301_class_attribute.py | 2 +- .../test/fixtures/airflow/AIR301_names.py | 4 + .../test/fixtures/airflow/AIR301_names_fix.py | 4 +- .../airflow/AIR301_provider_names_fix.py | 8 +- .../src/rules/airflow/rules/removal_in_3.rs | 102 +++++++----------- .../airflow/rules/suggested_to_update_3_0.rs | 15 +-- ...sts__AIR301_AIR301_class_attribute.py.snap | 2 +- ...irflow__tests__AIR301_AIR301_names.py.snap | 32 +++--- ...ow__tests__AIR301_AIR301_names_fix.py.snap | 53 +++------ ...__AIR301_AIR301_provider_names_fix.py.snap | 64 +++-------- 10 files changed, 105 insertions(+), 181 deletions(-) diff --git a/crates/ruff_linter/resources/test/fixtures/airflow/AIR301_class_attribute.py b/crates/ruff_linter/resources/test/fixtures/airflow/AIR301_class_attribute.py index c12a39e9f1507..f2ac5a1ab8f0e 100644 --- a/crates/ruff_linter/resources/test/fixtures/airflow/AIR301_class_attribute.py +++ b/crates/ruff_linter/resources/test/fixtures/airflow/AIR301_class_attribute.py @@ -10,7 +10,7 @@ ) from airflow.datasets.manager import DatasetManager from airflow.lineage.hook import DatasetLineageInfo, HookLineageCollector -from airflow.providers.amazon.auth_manager.aws_auth_manager import AwsAuthManager +from airflow.providers.amazon.aws.auth_manager.aws_auth_manager import AwsAuthManager from airflow.providers.apache.beam.hooks import BeamHook, NotAir302HookError from airflow.providers.google.cloud.secrets.secret_manager import ( CloudSecretManagerBackend, diff --git a/crates/ruff_linter/resources/test/fixtures/airflow/AIR301_names.py b/crates/ruff_linter/resources/test/fixtures/airflow/AIR301_names.py index 73884a5ae6888..10c02b6a1041e 100644 --- a/crates/ruff_linter/resources/test/fixtures/airflow/AIR301_names.py +++ b/crates/ruff_linter/resources/test/fixtures/airflow/AIR301_names.py @@ -159,3 +159,7 @@ from airflow.providers.trino.datasets.trino import sanitize_uri sanitize_uri + +# airflow.notifications.basenotifier +from airflow.notifications.basenotifier import BaseNotifier +BaseNotifier() diff --git a/crates/ruff_linter/resources/test/fixtures/airflow/AIR301_names_fix.py b/crates/ruff_linter/resources/test/fixtures/airflow/AIR301_names_fix.py index d7c9414c5a75c..ac4f45eb42811 100644 --- a/crates/ruff_linter/resources/test/fixtures/airflow/AIR301_names_fix.py +++ b/crates/ruff_linter/resources/test/fixtures/airflow/AIR301_names_fix.py @@ -3,7 +3,7 @@ from airflow.api_connexion.security import requires_access_dataset from airflow.auth.managers.models.resource_details import ( DatasetDetails, - is_authorized_dataset, + ) from airflow.datasets.manager import ( DatasetManager, @@ -19,7 +19,7 @@ requires_access_dataset() DatasetDetails() -is_authorized_dataset() + DatasetManager() dataset_manager() diff --git a/crates/ruff_linter/resources/test/fixtures/airflow/AIR301_provider_names_fix.py b/crates/ruff_linter/resources/test/fixtures/airflow/AIR301_provider_names_fix.py index 99b77a7ebe181..f5a22b6c169ae 100644 --- a/crates/ruff_linter/resources/test/fixtures/airflow/AIR301_provider_names_fix.py +++ b/crates/ruff_linter/resources/test/fixtures/airflow/AIR301_provider_names_fix.py @@ -1,6 +1,6 @@ from __future__ import annotations -from airflow.providers.amazon.aws.auth_manager.avp.entities.AvpEntities import DATASET +from airflow.providers.amazon.aws.auth_manager.avp.entities import AvpEntities from airflow.providers.amazon.aws.datasets.s3 import ( convert_dataset_to_openlineage as s3_convert_dataset_to_openlineage, ) @@ -9,7 +9,7 @@ convert_dataset_to_openlineage as io_convert_dataset_to_openlineage, ) from airflow.providers.common.io.dataset.file import create_dataset as io_create_dataset -from airflow.providers.fab.auth_manager.fab_auth_manager import is_authorized_dataset as fab_is_authorized_dataset + from airflow.providers.google.datasets.bigquery import ( create_dataset as bigquery_create_dataset, ) @@ -22,7 +22,7 @@ translate_airflow_dataset, ) -DATASET +AvpEntities.DATASET s3_create_dataset() s3_convert_dataset_to_openlineage() @@ -30,7 +30,7 @@ io_create_dataset() io_convert_dataset_to_openlineage() -fab_is_authorized_dataset() + # airflow.providers.google.datasets.bigquery bigquery_create_dataset() 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 0787bd038fa5f..2d0b6ebcc1a47 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 @@ -100,8 +100,9 @@ pub(crate) fn airflow_3_removal_expr(checker: &Checker, expr: &Expr) { check_method(checker, call_expr); check_context_key_usage_in_call(checker, call_expr); } - Expr::Attribute(attribute_expr @ ExprAttribute { attr, .. }) => { - check_name(checker, expr, attr.range()); + Expr::Attribute(attribute_expr @ ExprAttribute { range, .. }) => { + // check_name(checker, expr, attr.range()); + check_name(checker, expr, *range); check_class_attribute(checker, attribute_expr); } Expr::Name(ExprName { id, ctx, range }) => { @@ -496,17 +497,6 @@ fn check_method(checker: &Checker, call_expr: &ExprCall) { "collected_datasets" => Replacement::AttrName("collected_assets"), _ => return, }, - [ - "airflow", - "providers", - "amazon", - "auth_manager", - "aws_auth_manager", - "AwsAuthManager", - ] => match attr.as_str() { - "is_authorized_dataset" => Replacement::AttrName("is_authorized_asset"), - _ => return, - }, ["airflow", "providers_manager", "ProvidersManager"] => match attr.as_str() { "initialize_providers_dataset_uri_resources" => { Replacement::AttrName("initialize_providers_asset_uri_resources") @@ -539,6 +529,12 @@ fn check_method(checker: &Checker, call_expr: &ExprCall) { "get_connections" => Replacement::AttrName("get_connection"), _ => return, } + } else if is_airflow_auth_manager(segments) { + if attr.as_str() == "is_authorized_dataset" { + Replacement::AttrName("is_authorized_asset") + } else { + return; + } } else { return; } @@ -596,16 +592,16 @@ fn check_name(checker: &Checker, expr: &Expr, range: TextRange) { ] => Replacement::Message("Use `sys.version_info` instead"), // airflow.api_connexion.security - ["airflow", "api_connexion", "security", "requires_access"] => { - Replacement::Message("Use `airflow.api_connexion.security.requires_access_*` instead") - } + ["airflow", "api_connexion", "security", "requires_access"] => Replacement::Message( + "Use `airflow.api_fastapi.core_api.security.requires_access_*` instead", + ), [ "airflow", "api_connexion", "security", "requires_access_dataset", ] => Replacement::AutoImport { - module: "airflow.api_connexion.security", + module: "airflow.api_fastapi.core_api.security", name: "requires_access_asset", }, @@ -621,26 +617,17 @@ fn check_name(checker: &Checker, expr: &Expr, range: TextRange) { module: "airflow.api_fastapi.auth.managers.models.resource_details", name: "AssetDetails", }, - [ - "airflow", - "auth", - "managers", - "base_auth_manager", - "is_authorized_dataset", - ] => Replacement::AutoImport { - module: "airflow.api_fastapi.auth.managers.base_auth_manager", - name: "is_authorized_asset", - }, // airflow.configuration + // TODO: check whether we could improve it [ "airflow", "configuration", rest @ ("as_dict" | "get" | "getboolean" | "getfloat" | "getint" | "has_option" | "remove_option" | "set"), ] => Replacement::SourceModuleMoved { - module: "airflow.configuration.conf", - name: (*rest).to_string(), + module: "airflow.configuration", + name: format!("conf.{rest}"), }, // airflow.contrib.* @@ -708,7 +695,7 @@ fn check_name(checker: &Checker, expr: &Expr, range: TextRange) { // airflow.notifications ["airflow", "notifications", "basenotifier", "BaseNotifier"] => Replacement::AutoImport { - module: "airflow.sdk", + module: "airflow.sdk.bases.notifier", name: "BaseNotifier", }, @@ -818,22 +805,18 @@ fn check_name(checker: &Checker, expr: &Expr, range: TextRange) { }, // airflow.www - // TODO: www has been removed - ["airflow", "www", "auth", "has_access"] => { - Replacement::Message("Use `airflow.www.auth.has_access_*` instead") - } - ["airflow", "www", "auth", "has_access_dataset"] => Replacement::AutoImport { - module: "airflow.www.auth", - name: "has_access_asset", - }, - ["airflow", "www", "utils", "get_sensitive_variables_fields"] => Replacement::AutoImport { - module: "airflow.utils.log.secrets_masker", - name: "get_sensitive_variables_fields", - }, - ["airflow", "www", "utils", "should_hide_value_for_key"] => Replacement::AutoImport { - module: "airflow.utils.log.secrets_masker", - name: "should_hide_value_for_key", - }, + [ + "airflow", + "www", + "auth", + "has_access" | "has_access_dataset", + ] => Replacement::None, + [ + "airflow", + "www", + "utils", + "get_sensitive_variables_fields" | "should_hide_value_for_key", + ] => Replacement::None, // airflow.providers.amazon [ @@ -870,8 +853,8 @@ fn check_name(checker: &Checker, expr: &Expr, range: TextRange) { "AvpEntities", "DATASET", ] => Replacement::AutoImport { - module: "airflow.providers.amazon.aws.auth_manager.avp.entities.AvpEntities", - name: "ASSET", + module: "airflow.providers.amazon.aws.auth_manager.avp.entities", + name: "AvpEntities.ASSET", }, // airflow.providers.common.io @@ -900,19 +883,6 @@ fn check_name(checker: &Checker, expr: &Expr, range: TextRange) { _ => return, }, - // airflow.providers.fab - [ - "airflow", - "providers", - "fab", - "auth_manager", - "fab_auth_manager", - "is_authorized_dataset", - ] => Replacement::AutoImport { - module: "airflow.providers.fab.auth_manager.fab_auth_manager", - name: "is_authorized_asset", - }, - // airflow.providers.google // airflow.providers.google.datasets ["airflow", "providers", "google", "datasets", rest @ ..] => match &rest { @@ -1016,13 +986,19 @@ fn check_name(checker: &Checker, expr: &Expr, range: TextRange) { if is_guarded_by_try_except(expr, module, name, semantic) { return; } + + let import_target = if expr.is_attribute_expr() && name.contains(".") { + name.split(".").next().unwrap() + } else { + name + }; diagnostic.try_set_fix(|| { let (import_edit, binding) = checker.importer().get_or_import_symbol( - &ImportRequest::import_from(module, name), + &ImportRequest::import_from(module, import_target), expr.start(), checker.semantic(), )?; - let replacement_edit = Edit::range_replacement(binding, range); + let replacement_edit = Edit::range_replacement(name.to_string(), range); Ok(Fix::safe_edits(import_edit, [replacement_edit])) }); } diff --git a/crates/ruff_linter/src/rules/airflow/rules/suggested_to_update_3_0.rs b/crates/ruff_linter/src/rules/airflow/rules/suggested_to_update_3_0.rs index ad9ab909ef05a..46dddee24508b 100644 --- a/crates/ruff_linter/src/rules/airflow/rules/suggested_to_update_3_0.rs +++ b/crates/ruff_linter/src/rules/airflow/rules/suggested_to_update_3_0.rs @@ -248,12 +248,15 @@ fn check_name(checker: &Checker, expr: &Expr, range: TextRange) { } // airflow.models.baseoperator - ["airflow", "models", "baseoperator", rest @ ("chain" | "chain_linear" | "cross_downstream")] => { - Replacement::SourceModuleMoved { - module: "airflow.sdk", - name: (*rest).to_string(), - } - } + [ + "airflow", + "models", + "baseoperator", + rest @ ("chain" | "chain_linear" | "cross_downstream"), + ] => Replacement::SourceModuleMoved { + module: "airflow.sdk", + name: (*rest).to_string(), + }, ["airflow", "models", "baseoperatorlink", "BaseOperatorLink"] => Replacement::AutoImport { module: "airflow.sdk.definitions.baseoperatorlink", name: "BaseOperatorLink", diff --git a/crates/ruff_linter/src/rules/airflow/snapshots/ruff_linter__rules__airflow__tests__AIR301_AIR301_class_attribute.py.snap b/crates/ruff_linter/src/rules/airflow/snapshots/ruff_linter__rules__airflow__tests__AIR301_AIR301_class_attribute.py.snap index 0ae63a796b112..bc8b673a00a0c 100644 --- a/crates/ruff_linter/src/rules/airflow/snapshots/ruff_linter__rules__airflow__tests__AIR301_AIR301_class_attribute.py.snap +++ b/crates/ruff_linter/src/rules/airflow/snapshots/ruff_linter__rules__airflow__tests__AIR301_AIR301_class_attribute.py.snap @@ -310,7 +310,7 @@ AIR301_class_attribute.py:50:11: AIR301 [*] `airflow.lineage.hook.DatasetLineage 11 11 | from airflow.datasets.manager import DatasetManager 12 |-from airflow.lineage.hook import DatasetLineageInfo, HookLineageCollector 12 |+from airflow.lineage.hook import DatasetLineageInfo, HookLineageCollector, AssetLineageInfo -13 13 | from airflow.providers.amazon.auth_manager.aws_auth_manager import AwsAuthManager +13 13 | from airflow.providers.amazon.aws.auth_manager.aws_auth_manager import AwsAuthManager 14 14 | from airflow.providers.apache.beam.hooks import BeamHook, NotAir302HookError 15 15 | from airflow.providers.google.cloud.secrets.secret_manager import ( -------------------------------------------------------------------------------- diff --git a/crates/ruff_linter/src/rules/airflow/snapshots/ruff_linter__rules__airflow__tests__AIR301_AIR301_names.py.snap b/crates/ruff_linter/src/rules/airflow/snapshots/ruff_linter__rules__airflow__tests__AIR301_AIR301_names.py.snap index f64e07f81555d..73b170eb14bc8 100644 --- a/crates/ruff_linter/src/rules/airflow/snapshots/ruff_linter__rules__airflow__tests__AIR301_AIR301_names.py.snap +++ b/crates/ruff_linter/src/rules/airflow/snapshots/ruff_linter__rules__airflow__tests__AIR301_AIR301_names.py.snap @@ -77,7 +77,7 @@ AIR301_names.py:56:1: AIR301 `airflow.api_connexion.security.requires_access` is 56 | requires_access | ^^^^^^^^^^^^^^^ AIR301 | - = help: Use `airflow.api_connexion.security.requires_access_*` instead + = help: Use `airflow.api_fastapi.core_api.security.requires_access_*` instead AIR301_names.py:60:1: AIR301 `airflow.configuration.get` is removed in Airflow 3.0 | @@ -191,20 +191,20 @@ AIR301_names.py:89:1: AIR301 `airflow.triggers.external_task.TaskStateTrigger` i 91 | # airflow.utils.date | -AIR301_names.py:92:7: AIR301 `airflow.utils.dates.date_range` is removed in Airflow 3.0 +AIR301_names.py:92:1: AIR301 `airflow.utils.dates.date_range` is removed in Airflow 3.0 | 91 | # airflow.utils.date 92 | dates.date_range - | ^^^^^^^^^^ AIR301 + | ^^^^^^^^^^^^^^^^ AIR301 93 | dates.days_ago | -AIR301_names.py:93:7: AIR301 `airflow.utils.dates.days_ago` is removed in Airflow 3.0 +AIR301_names.py:93:1: AIR301 `airflow.utils.dates.days_ago` is removed in Airflow 3.0 | 91 | # airflow.utils.date 92 | dates.date_range 93 | dates.days_ago - | ^^^^^^^^ AIR301 + | ^^^^^^^^^^^^^^ AIR301 94 | 95 | date_range | @@ -399,20 +399,20 @@ AIR301_names.py:129:1: AIR301 `airflow.utils.state.terminating_states` is remove 131 | # airflow.utils.trigger_rule | -AIR301_names.py:132:13: AIR301 `airflow.utils.trigger_rule.TriggerRule.DUMMY` is removed in Airflow 3.0 +AIR301_names.py:132:1: AIR301 `airflow.utils.trigger_rule.TriggerRule.DUMMY` is removed in Airflow 3.0 | 131 | # airflow.utils.trigger_rule 132 | TriggerRule.DUMMY - | ^^^^^ AIR301 + | ^^^^^^^^^^^^^^^^^ AIR301 133 | TriggerRule.NONE_FAILED_OR_SKIPPED | -AIR301_names.py:133:13: AIR301 `airflow.utils.trigger_rule.TriggerRule.NONE_FAILED_OR_SKIPPED` is removed in Airflow 3.0 +AIR301_names.py:133:1: AIR301 `airflow.utils.trigger_rule.TriggerRule.NONE_FAILED_OR_SKIPPED` is removed in Airflow 3.0 | 131 | # airflow.utils.trigger_rule 132 | TriggerRule.DUMMY 133 | TriggerRule.NONE_FAILED_OR_SKIPPED - | ^^^^^^^^^^^^^^^^^^^^^^ AIR301 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ AIR301 | AIR301_names.py:137:1: AIR301 `airflow.www.auth.has_access` is removed in Airflow 3.0 @@ -423,7 +423,6 @@ AIR301_names.py:137:1: AIR301 `airflow.www.auth.has_access` is removed in Airflo 138 | 139 | # airflow.www.utils | - = help: Use `airflow.www.auth.has_access_*` instead AIR301_names.py:140:1: AIR301 `airflow.www.utils.get_sensitive_variables_fields` is removed in Airflow 3.0 | @@ -432,7 +431,6 @@ AIR301_names.py:140:1: AIR301 `airflow.www.utils.get_sensitive_variables_fields` | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ AIR301 141 | should_hide_value_for_key | - = help: Use `airflow.utils.log.secrets_masker.get_sensitive_variables_fields` instead AIR301_names.py:141:1: AIR301 `airflow.www.utils.should_hide_value_for_key` is removed in Airflow 3.0 | @@ -443,7 +441,6 @@ AIR301_names.py:141:1: AIR301 `airflow.www.utils.should_hide_value_for_key` is r 142 | 143 | # airflow.operators.python | - = help: Use `airflow.utils.log.secrets_masker.should_hide_value_for_key` instead AIR301_names.py:146:1: AIR301 `airflow.operators.python.get_current_context` is removed in Airflow 3.0 | @@ -484,5 +481,16 @@ AIR301_names.py:161:1: AIR301 `airflow.providers.trino.datasets.trino.sanitize_u 160 | 161 | sanitize_uri | ^^^^^^^^^^^^ AIR301 +162 | +163 | # airflow.notifications.basenotifier | = help: Use `airflow.providers.trino.assets.trino.sanitize_uri` instead + +AIR301_names.py:165:1: AIR301 `airflow.notifications.basenotifier.BaseNotifier` is removed in Airflow 3.0 + | +163 | # airflow.notifications.basenotifier +164 | from airflow.notifications.basenotifier import BaseNotifier +165 | BaseNotifier() + | ^^^^^^^^^^^^ AIR301 + | + = help: Use `airflow.sdk.bases.notifier.BaseNotifier` instead diff --git a/crates/ruff_linter/src/rules/airflow/snapshots/ruff_linter__rules__airflow__tests__AIR301_AIR301_names_fix.py.snap b/crates/ruff_linter/src/rules/airflow/snapshots/ruff_linter__rules__airflow__tests__AIR301_AIR301_names_fix.py.snap index ff6982d49ad41..b2f65f18e5aba 100644 --- a/crates/ruff_linter/src/rules/airflow/snapshots/ruff_linter__rules__airflow__tests__AIR301_AIR301_names_fix.py.snap +++ b/crates/ruff_linter/src/rules/airflow/snapshots/ruff_linter__rules__airflow__tests__AIR301_AIR301_names_fix.py.snap @@ -10,25 +10,19 @@ AIR301_names_fix.py:19:1: AIR301 [*] `airflow.api_connexion.security.requires_ac 20 | 21 | DatasetDetails() | - = help: Use `airflow.api_connexion.security.requires_access_asset` instead + = help: Use `airflow.api_fastapi.core_api.security.requires_access_asset` instead ℹ Safe fix -1 1 | from __future__ import annotations -2 2 | -3 |-from airflow.api_connexion.security import requires_access_dataset - 3 |+from airflow.api_connexion.security import requires_access_dataset, requires_access_asset -4 4 | from airflow.auth.managers.models.resource_details import ( -5 5 | DatasetDetails, -6 6 | is_authorized_dataset, --------------------------------------------------------------------------------- +15 15 | from airflow.secrets.local_filesystm import load_connections 16 16 | from airflow.security.permissions import RESOURCE_DATASET 17 17 | from airflow.www.auth import has_access_dataset -18 18 | + 18 |+from airflow.api_fastapi.core_api.security import requires_access_asset +18 19 | 19 |-requires_access_dataset() - 19 |+requires_access_asset() -20 20 | -21 21 | DatasetDetails() -22 22 | is_authorized_dataset() + 20 |+requires_access_asset() +20 21 | +21 22 | DatasetDetails() +22 23 | AIR301_names_fix.py:21:1: AIR301 [*] `airflow.auth.managers.models.resource_details.DatasetDetails` is removed in Airflow 3.0 | @@ -36,7 +30,6 @@ AIR301_names_fix.py:21:1: AIR301 [*] `airflow.auth.managers.models.resource_deta 20 | 21 | DatasetDetails() | ^^^^^^^^^^^^^^ AIR301 -22 | is_authorized_dataset() | = help: Use `airflow.api_fastapi.auth.managers.models.resource_details.AssetDetails` instead @@ -50,14 +43,12 @@ AIR301_names_fix.py:21:1: AIR301 [*] `airflow.auth.managers.models.resource_deta 20 21 | 21 |-DatasetDetails() 22 |+AssetDetails() -22 23 | is_authorized_dataset() +22 23 | 23 24 | 24 25 | DatasetManager() AIR301_names_fix.py:24:1: AIR301 [*] `airflow.datasets.manager.DatasetManager` is removed in Airflow 3.0 | -22 | is_authorized_dataset() -23 | 24 | DatasetManager() | ^^^^^^^^^^^^^^ AIR301 25 | dataset_manager() @@ -74,7 +65,7 @@ AIR301_names_fix.py:24:1: AIR301 [*] `airflow.datasets.manager.DatasetManager` i 19 20 | requires_access_dataset() 20 21 | 21 22 | DatasetDetails() -22 23 | is_authorized_dataset() +22 23 | 23 24 | 24 |-DatasetManager() 25 |+AssetManager() @@ -100,7 +91,7 @@ AIR301_names_fix.py:25:1: AIR301 [*] `airflow.datasets.manager.dataset_manager` 19 20 | requires_access_dataset() 20 21 | -------------------------------------------------------------------------------- -22 23 | is_authorized_dataset() +22 23 | 23 24 | 24 25 | DatasetManager() 25 |-dataset_manager() @@ -256,7 +247,7 @@ AIR301_names_fix.py:35:1: AIR301 [*] `airflow.security.permissions.RESOURCE_DATA 37 37 | has_access_dataset() 38 38 | -AIR301_names_fix.py:37:1: AIR301 [*] `airflow.www.auth.has_access_dataset` is removed in Airflow 3.0 +AIR301_names_fix.py:37:1: AIR301 `airflow.www.auth.has_access_dataset` is removed in Airflow 3.0 | 35 | RESOURCE_DATASET 36 | @@ -265,26 +256,6 @@ AIR301_names_fix.py:37:1: AIR301 [*] `airflow.www.auth.has_access_dataset` is re 38 | 39 | from airflow.listeners.spec.dataset import ( | - = help: Use `airflow.www.auth.has_access_asset` instead - -ℹ Safe fix -14 14 | from airflow.metrics.validators import AllowListValidator, BlockListValidator -15 15 | from airflow.secrets.local_filesystm import load_connections -16 16 | from airflow.security.permissions import RESOURCE_DATASET -17 |-from airflow.www.auth import has_access_dataset - 17 |+from airflow.www.auth import has_access_dataset, has_access_asset -18 18 | -19 19 | requires_access_dataset() -20 20 | --------------------------------------------------------------------------------- -34 34 | -35 35 | RESOURCE_DATASET -36 36 | -37 |-has_access_dataset() - 37 |+has_access_asset() -38 38 | -39 39 | from airflow.listeners.spec.dataset import ( -40 40 | on_dataset_changed, AIR301_names_fix.py:44:1: AIR301 [*] `airflow.listeners.spec.dataset.on_dataset_created` is removed in Airflow 3.0 | diff --git a/crates/ruff_linter/src/rules/airflow/snapshots/ruff_linter__rules__airflow__tests__AIR301_AIR301_provider_names_fix.py.snap b/crates/ruff_linter/src/rules/airflow/snapshots/ruff_linter__rules__airflow__tests__AIR301_AIR301_provider_names_fix.py.snap index 6b986b413fd04..e1ac3ba9c1d7e 100644 --- a/crates/ruff_linter/src/rules/airflow/snapshots/ruff_linter__rules__airflow__tests__AIR301_AIR301_provider_names_fix.py.snap +++ b/crates/ruff_linter/src/rules/airflow/snapshots/ruff_linter__rules__airflow__tests__AIR301_AIR301_provider_names_fix.py.snap @@ -5,34 +5,26 @@ AIR301_provider_names_fix.py:25:1: AIR301 [*] `airflow.providers.amazon.aws.auth | 23 | ) 24 | -25 | DATASET - | ^^^^^^^ AIR301 +25 | AvpEntities.DATASET + | ^^^^^^^^^^^^^^^^^^^ AIR301 26 | 27 | s3_create_dataset() | = help: Use `airflow.providers.amazon.aws.auth_manager.avp.entities.AvpEntities.ASSET` instead ℹ Safe fix -1 1 | from __future__ import annotations -2 2 | -3 |-from airflow.providers.amazon.aws.auth_manager.avp.entities.AvpEntities import DATASET - 3 |+from airflow.providers.amazon.aws.auth_manager.avp.entities.AvpEntities import DATASET, ASSET -4 4 | from airflow.providers.amazon.aws.datasets.s3 import ( -5 5 | convert_dataset_to_openlineage as s3_convert_dataset_to_openlineage, -6 6 | ) --------------------------------------------------------------------------------- 22 22 | translate_airflow_dataset, 23 23 | ) 24 24 | -25 |-DATASET - 25 |+ASSET +25 |-AvpEntities.DATASET + 25 |+AvpEntities.ASSET 26 26 | 27 27 | s3_create_dataset() 28 28 | s3_convert_dataset_to_openlineage() AIR301_provider_names_fix.py:27:1: AIR301 [*] `airflow.providers.amazon.aws.datasets.s3.create_dataset` is removed in Airflow 3.0 | -25 | DATASET +25 | AvpEntities.DATASET 26 | 27 | s3_create_dataset() | ^^^^^^^^^^^^^^^^^ AIR301 @@ -46,7 +38,7 @@ AIR301_provider_names_fix.py:27:1: AIR301 [*] `airflow.providers.amazon.aws.data 23 23 | ) 24 |+from airflow.providers.amazon.aws.assets.s3 import create_asset 24 25 | -25 26 | DATASET +25 26 | AvpEntities.DATASET 26 27 | 27 |-s3_create_dataset() 28 |+create_asset() @@ -70,7 +62,7 @@ AIR301_provider_names_fix.py:28:1: AIR301 [*] `airflow.providers.amazon.aws.data 23 23 | ) 24 |+from airflow.providers.amazon.aws.assets.s3 import convert_asset_to_openlineage 24 25 | -25 26 | DATASET +25 26 | AvpEntities.DATASET 26 27 | 27 28 | s3_create_dataset() 28 |-s3_convert_dataset_to_openlineage() @@ -79,36 +71,6 @@ AIR301_provider_names_fix.py:28:1: AIR301 [*] `airflow.providers.amazon.aws.data 30 31 | io_create_dataset() 31 32 | io_convert_dataset_to_openlineage() -AIR301_provider_names_fix.py:33:1: AIR301 [*] `airflow.providers.fab.auth_manager.fab_auth_manager.is_authorized_dataset` is removed in Airflow 3.0 - | -31 | io_convert_dataset_to_openlineage() -32 | -33 | fab_is_authorized_dataset() - | ^^^^^^^^^^^^^^^^^^^^^^^^^ AIR301 -34 | -35 | # airflow.providers.google.datasets.bigquery - | - = help: Use `airflow.providers.fab.auth_manager.fab_auth_manager.is_authorized_asset` instead - -ℹ Safe fix -9 9 | convert_dataset_to_openlineage as io_convert_dataset_to_openlineage, -10 10 | ) -11 11 | from airflow.providers.common.io.dataset.file import create_dataset as io_create_dataset -12 |-from airflow.providers.fab.auth_manager.fab_auth_manager import is_authorized_dataset as fab_is_authorized_dataset - 12 |+from airflow.providers.fab.auth_manager.fab_auth_manager import is_authorized_dataset as fab_is_authorized_dataset, is_authorized_asset -13 13 | from airflow.providers.google.datasets.bigquery import ( -14 14 | create_dataset as bigquery_create_dataset, -15 15 | ) --------------------------------------------------------------------------------- -30 30 | io_create_dataset() -31 31 | io_convert_dataset_to_openlineage() -32 32 | -33 |-fab_is_authorized_dataset() - 33 |+is_authorized_asset() -34 34 | -35 35 | # airflow.providers.google.datasets.bigquery -36 36 | bigquery_create_dataset() - AIR301_provider_names_fix.py:36:1: AIR301 [*] `airflow.providers.google.datasets.bigquery.create_dataset` is removed in Airflow 3.0 | 35 | # airflow.providers.google.datasets.bigquery @@ -125,10 +87,10 @@ AIR301_provider_names_fix.py:36:1: AIR301 [*] `airflow.providers.google.datasets 23 23 | ) 24 |+from airflow.providers.google.assets.bigquery import create_asset 24 25 | -25 26 | DATASET +25 26 | AvpEntities.DATASET 26 27 | -------------------------------------------------------------------------------- -33 34 | fab_is_authorized_dataset() +33 34 | 34 35 | 35 36 | # airflow.providers.google.datasets.bigquery 36 |-bigquery_create_dataset() @@ -154,7 +116,7 @@ AIR301_provider_names_fix.py:38:1: AIR301 [*] `airflow.providers.google.datasets 23 23 | ) 24 |+from airflow.providers.google.assets.gcs import create_asset 24 25 | -25 26 | DATASET +25 26 | AvpEntities.DATASET 26 27 | -------------------------------------------------------------------------------- 35 36 | # airflow.providers.google.datasets.bigquery @@ -183,7 +145,7 @@ AIR301_provider_names_fix.py:39:1: AIR301 [*] `airflow.providers.google.datasets 23 23 | ) 24 |+from airflow.providers.google.assets.gcs import convert_asset_to_openlineage 24 25 | -25 26 | DATASET +25 26 | AvpEntities.DATASET 26 27 | -------------------------------------------------------------------------------- 36 37 | bigquery_create_dataset() @@ -213,7 +175,7 @@ AIR301_provider_names_fix.py:41:1: AIR301 [*] `airflow.providers.openlineage.uti 23 |+AssetInfo, 23 24 | ) 24 25 | -25 26 | DATASET +25 26 | AvpEntities.DATASET -------------------------------------------------------------------------------- 38 39 | gcs_create_dataset() 39 40 | gcs_convert_dataset_to_openlineage() @@ -242,7 +204,7 @@ AIR301_provider_names_fix.py:42:1: AIR301 [*] `airflow.providers.openlineage.uti 23 |+translate_airflow_asset, 23 24 | ) 24 25 | -25 26 | DATASET +25 26 | AvpEntities.DATASET -------------------------------------------------------------------------------- 39 40 | gcs_convert_dataset_to_openlineage() 40 41 | # airflow.providers.openlineage.utils.utils From 6e90147996ac38cae7f7e2afaaacd62311c16dde Mon Sep 17 00:00:00 2001 From: Wei Lee Date: Fri, 9 May 2025 19:06:38 +0800 Subject: [PATCH 3/8] refactor(AIR301): simplify unnecessary match with if else --- .../src/rules/airflow/rules/removal_in_3.rs | 29 ++++++++++--------- 1 file changed, 15 insertions(+), 14 deletions(-) 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 2d0b6ebcc1a47..93ddc333c37fc 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 @@ -497,21 +497,25 @@ fn check_method(checker: &Checker, call_expr: &ExprCall) { "collected_datasets" => Replacement::AttrName("collected_assets"), _ => return, }, - ["airflow", "providers_manager", "ProvidersManager"] => match attr.as_str() { - "initialize_providers_dataset_uri_resources" => { + ["airflow", "providers_manager", "ProvidersManager"] => { + if attr.as_str() == "initialize_providers_dataset_uri_resources" { Replacement::AttrName("initialize_providers_asset_uri_resources") + } else { + return; } - _ => return, - }, + } [ "airflow", "secrets", "local_filesystem", "LocalFilesystemBackend", - ] => match attr.as_str() { - "get_connections" => Replacement::AttrName("get_connection"), - _ => return, - }, + ] => { + if attr.as_str() == "get_connections" { + Replacement::AttrName("get_connection") + } else { + return; + } + } ["airflow", "datasets", ..] | ["airflow", "Dataset"] => match attr.as_str() { "iter_datasets" => Replacement::AttrName("iter_assets"), "iter_dataset_aliases" => Replacement::AttrName("iter_asset_aliases"), @@ -524,17 +528,14 @@ fn check_method(checker: &Checker, call_expr: &ExprCall) { "get_connections" => Replacement::AttrName("get_connection"), _ => return, } - } else if is_airflow_hook(segments) { - match attr.as_str() { - "get_connections" => Replacement::AttrName("get_connection"), - _ => return, - } } else if is_airflow_auth_manager(segments) { if attr.as_str() == "is_authorized_dataset" { Replacement::AttrName("is_authorized_asset") } else { return; } + } else if is_airflow_hook(segments) && attr.as_str() == "get_connections" { + Replacement::AttrName("get_connection") } else { return; } @@ -993,7 +994,7 @@ fn check_name(checker: &Checker, expr: &Expr, range: TextRange) { name }; diagnostic.try_set_fix(|| { - let (import_edit, binding) = checker.importer().get_or_import_symbol( + let (import_edit, _) = checker.importer().get_or_import_symbol( &ImportRequest::import_from(module, import_target), expr.start(), checker.semantic(), From 7c81aa56b91bc452d86d10e3dae7eef7c66f2649 Mon Sep 17 00:00:00 2001 From: Wei Lee Date: Fri, 9 May 2025 22:21:59 +0800 Subject: [PATCH 4/8] fix(AIR301): update rules based on the latest Airflow 3.0 implementation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Update the following rules * class attribute * `airflow..sensors.weekday.DayOfWeekSensor` * `use_task_execution_day` removed * `airflow.providers.amazon.aws.auth_manager.aws_auth_manager.AwsAuthManager` * `is_authorized_dataset` * Add the following rules * name * `airflow.auth.managers.base_auth_manager.BaseAuthManager` → `airflow.api_fastapi.auth.managers.base_auth_manager.BaseAuthManager` --- .../test/fixtures/airflow/AIR301_args.py | 4 +- .../test/fixtures/airflow/AIR301_names.py | 4 + .../src/rules/airflow/rules/removal_in_3.rs | 16 +- ...airflow__tests__AIR301_AIR301_args.py.snap | 347 ++++++++++-------- ...irflow__tests__AIR301_AIR301_names.py.snap | 11 + 5 files changed, 215 insertions(+), 167 deletions(-) diff --git a/crates/ruff_linter/resources/test/fixtures/airflow/AIR301_args.py b/crates/ruff_linter/resources/test/fixtures/airflow/AIR301_args.py index 17839131339ac..4d2f9ba7a6052 100644 --- a/crates/ruff_linter/resources/test/fixtures/airflow/AIR301_args.py +++ b/crates/ruff_linter/resources/test/fixtures/airflow/AIR301_args.py @@ -12,7 +12,8 @@ from airflow.providers.google.cloud.log.gcs_task_handler import GCSTaskHandler from airflow.providers.standard.operators import datetime, trigger_dagrun from airflow.providers.standard.sensors import weekday -from airflow.sensors.weekday import BranchDayOfWeekOperator, DayOfWeekSensor +from airflow.operators.weekday import BranchDayOfWeekOperator +from airflow.sensors.weekday import DayOfWeekSensor from airflow.timetables.simple import NullTimetable DAG(dag_id="class_schedule", schedule="@hourly") @@ -90,3 +91,4 @@ def decorator_deprecated_operator_args(): GCSTaskHandler(filename_template="/tmp/test") FabAuthManager(None) + diff --git a/crates/ruff_linter/resources/test/fixtures/airflow/AIR301_names.py b/crates/ruff_linter/resources/test/fixtures/airflow/AIR301_names.py index 10c02b6a1041e..31710de9b6a1a 100644 --- a/crates/ruff_linter/resources/test/fixtures/airflow/AIR301_names.py +++ b/crates/ruff_linter/resources/test/fixtures/airflow/AIR301_names.py @@ -163,3 +163,7 @@ # airflow.notifications.basenotifier from airflow.notifications.basenotifier import BaseNotifier BaseNotifier() + +# airflow.auth.manager +from airflow.auth.managers.base_auth_manager import BaseAuthManager +BaseAuthManager() \ No newline at end of file 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 93ddc333c37fc..7c34843543026 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 @@ -261,8 +261,9 @@ fn check_call_arguments(checker: &Checker, qualified_name: &QualifiedName, argum .., "operators", "weekday", - "DayOfWeekSensor" | "BranchDayOfWeekOperator", - ] => { + "BranchDayOfWeekOperator", + ] + | ["airflow", .., "sensors", "weekday", "DayOfWeekSensor"] => { checker.report_diagnostics(diagnostic_for_argument( arguments, "use_task_execution_day", @@ -607,6 +608,16 @@ fn check_name(checker: &Checker, expr: &Expr, range: TextRange) { }, // airflow.auth.managers + [ + "airflow", + "auth", + "managers", + "base_auth_manager", + "BaseAuthManager", + ] => Replacement::AutoImport { + module: "airflow.api_fastapi.auth.managers.base_auth_manager", + name: "BaseAuthManager", + }, [ "airflow", "auth", @@ -668,7 +679,6 @@ fn check_name(checker: &Checker, expr: &Expr, range: TextRange) { }, // airflow.listeners.spec - // TODO: this is removed ["airflow", "listeners", "spec", "dataset", rest] => match *rest { "on_dataset_created" => Replacement::AutoImport { module: "airflow.listeners.spec.asset", diff --git a/crates/ruff_linter/src/rules/airflow/snapshots/ruff_linter__rules__airflow__tests__AIR301_AIR301_args.py.snap b/crates/ruff_linter/src/rules/airflow/snapshots/ruff_linter__rules__airflow__tests__AIR301_AIR301_args.py.snap index 17ca7e2196105..7cb8bf7ef9913 100644 --- a/crates/ruff_linter/src/rules/airflow/snapshots/ruff_linter__rules__airflow__tests__AIR301_AIR301_args.py.snap +++ b/crates/ruff_linter/src/rules/airflow/snapshots/ruff_linter__rules__airflow__tests__AIR301_AIR301_args.py.snap @@ -1,268 +1,289 @@ --- source: crates/ruff_linter/src/rules/airflow/mod.rs --- -AIR301_args.py:20:39: AIR301 [*] `schedule_interval` is removed in Airflow 3.0 +AIR301_args.py:21:39: AIR301 [*] `schedule_interval` is removed in Airflow 3.0 | -18 | DAG(dag_id="class_schedule", schedule="@hourly") -19 | -20 | DAG(dag_id="class_schedule_interval", schedule_interval="@hourly") +19 | DAG(dag_id="class_schedule", schedule="@hourly") +20 | +21 | DAG(dag_id="class_schedule_interval", schedule_interval="@hourly") | ^^^^^^^^^^^^^^^^^ AIR301 -21 | -22 | DAG(dag_id="class_timetable", timetable=NullTimetable()) +22 | +23 | DAG(dag_id="class_timetable", timetable=NullTimetable()) | = help: Use `schedule` instead ℹ Safe fix -17 17 | -18 18 | DAG(dag_id="class_schedule", schedule="@hourly") -19 19 | -20 |-DAG(dag_id="class_schedule_interval", schedule_interval="@hourly") - 20 |+DAG(dag_id="class_schedule_interval", schedule="@hourly") -21 21 | -22 22 | DAG(dag_id="class_timetable", timetable=NullTimetable()) -23 23 | +18 18 | +19 19 | DAG(dag_id="class_schedule", schedule="@hourly") +20 20 | +21 |-DAG(dag_id="class_schedule_interval", schedule_interval="@hourly") + 21 |+DAG(dag_id="class_schedule_interval", schedule="@hourly") +22 22 | +23 23 | DAG(dag_id="class_timetable", timetable=NullTimetable()) +24 24 | -AIR301_args.py:22:31: AIR301 [*] `timetable` is removed in Airflow 3.0 +AIR301_args.py:23:31: AIR301 [*] `timetable` is removed in Airflow 3.0 | -20 | DAG(dag_id="class_schedule_interval", schedule_interval="@hourly") -21 | -22 | DAG(dag_id="class_timetable", timetable=NullTimetable()) +21 | DAG(dag_id="class_schedule_interval", schedule_interval="@hourly") +22 | +23 | DAG(dag_id="class_timetable", timetable=NullTimetable()) | ^^^^^^^^^ AIR301 | = help: Use `schedule` instead ℹ Safe fix -19 19 | -20 20 | DAG(dag_id="class_schedule_interval", schedule_interval="@hourly") -21 21 | -22 |-DAG(dag_id="class_timetable", timetable=NullTimetable()) - 22 |+DAG(dag_id="class_timetable", schedule=NullTimetable()) -23 23 | +20 20 | +21 21 | DAG(dag_id="class_schedule_interval", schedule_interval="@hourly") +22 22 | +23 |-DAG(dag_id="class_timetable", timetable=NullTimetable()) + 23 |+DAG(dag_id="class_timetable", schedule=NullTimetable()) 24 24 | -25 25 | DAG(dag_id="class_fail_stop", fail_stop=True) +25 25 | +26 26 | DAG(dag_id="class_fail_stop", fail_stop=True) -AIR301_args.py:25:31: AIR301 [*] `fail_stop` is removed in Airflow 3.0 +AIR301_args.py:26:31: AIR301 [*] `fail_stop` is removed in Airflow 3.0 | -25 | DAG(dag_id="class_fail_stop", fail_stop=True) +26 | DAG(dag_id="class_fail_stop", fail_stop=True) | ^^^^^^^^^ AIR301 -26 | -27 | DAG(dag_id="class_default_view", default_view="dag_default_view") +27 | +28 | DAG(dag_id="class_default_view", default_view="dag_default_view") | = help: Use `fail_fast` instead ℹ Safe fix -22 22 | DAG(dag_id="class_timetable", timetable=NullTimetable()) -23 23 | +23 23 | DAG(dag_id="class_timetable", timetable=NullTimetable()) 24 24 | -25 |-DAG(dag_id="class_fail_stop", fail_stop=True) - 25 |+DAG(dag_id="class_fail_stop", fail_fast=True) -26 26 | -27 27 | DAG(dag_id="class_default_view", default_view="dag_default_view") -28 28 | +25 25 | +26 |-DAG(dag_id="class_fail_stop", fail_stop=True) + 26 |+DAG(dag_id="class_fail_stop", fail_fast=True) +27 27 | +28 28 | DAG(dag_id="class_default_view", default_view="dag_default_view") +29 29 | -AIR301_args.py:27:34: AIR301 `default_view` is removed in Airflow 3.0 +AIR301_args.py:28:34: AIR301 `default_view` is removed in Airflow 3.0 | -25 | DAG(dag_id="class_fail_stop", fail_stop=True) -26 | -27 | DAG(dag_id="class_default_view", default_view="dag_default_view") +26 | DAG(dag_id="class_fail_stop", fail_stop=True) +27 | +28 | DAG(dag_id="class_default_view", default_view="dag_default_view") | ^^^^^^^^^^^^ AIR301 -28 | -29 | DAG(dag_id="class_orientation", orientation="BT") +29 | +30 | DAG(dag_id="class_orientation", orientation="BT") | -AIR301_args.py:29:33: AIR301 `orientation` is removed in Airflow 3.0 +AIR301_args.py:30:33: AIR301 `orientation` is removed in Airflow 3.0 | -27 | DAG(dag_id="class_default_view", default_view="dag_default_view") -28 | -29 | DAG(dag_id="class_orientation", orientation="BT") +28 | DAG(dag_id="class_default_view", default_view="dag_default_view") +29 | +30 | DAG(dag_id="class_orientation", orientation="BT") | ^^^^^^^^^^^ AIR301 -30 | -31 | allow_future_exec_dates_dag = DAG(dag_id="class_allow_future_exec_dates") +31 | +32 | allow_future_exec_dates_dag = DAG(dag_id="class_allow_future_exec_dates") | -AIR301_args.py:40:6: AIR301 [*] `schedule_interval` is removed in Airflow 3.0 +AIR301_args.py:41:6: AIR301 [*] `schedule_interval` is removed in Airflow 3.0 | -40 | @dag(schedule_interval="0 * * * *") +41 | @dag(schedule_interval="0 * * * *") | ^^^^^^^^^^^^^^^^^ AIR301 -41 | def decorator_schedule_interval(): -42 | pass +42 | def decorator_schedule_interval(): +43 | pass | = help: Use `schedule` instead ℹ Safe fix -37 37 | pass -38 38 | +38 38 | pass 39 39 | -40 |-@dag(schedule_interval="0 * * * *") - 40 |+@dag(schedule="0 * * * *") -41 41 | def decorator_schedule_interval(): -42 42 | pass -43 43 | +40 40 | +41 |-@dag(schedule_interval="0 * * * *") + 41 |+@dag(schedule="0 * * * *") +42 42 | def decorator_schedule_interval(): +43 43 | pass +44 44 | -AIR301_args.py:45:6: AIR301 [*] `timetable` is removed in Airflow 3.0 +AIR301_args.py:46:6: AIR301 [*] `timetable` is removed in Airflow 3.0 | -45 | @dag(timetable=NullTimetable()) +46 | @dag(timetable=NullTimetable()) | ^^^^^^^^^ AIR301 -46 | def decorator_timetable(): -47 | pass +47 | def decorator_timetable(): +48 | pass | = help: Use `schedule` instead ℹ Safe fix -42 42 | pass -43 43 | +43 43 | pass 44 44 | -45 |-@dag(timetable=NullTimetable()) - 45 |+@dag(schedule=NullTimetable()) -46 46 | def decorator_timetable(): -47 47 | pass -48 48 | +45 45 | +46 |-@dag(timetable=NullTimetable()) + 46 |+@dag(schedule=NullTimetable()) +47 47 | def decorator_timetable(): +48 48 | pass +49 49 | -AIR301_args.py:53:39: AIR301 [*] `execution_date` is removed in Airflow 3.0 +AIR301_args.py:54:39: AIR301 [*] `execution_date` is removed in Airflow 3.0 | -51 | def decorator_deprecated_operator_args(): -52 | trigger_dagrun_op = trigger_dagrun.TriggerDagRunOperator( -53 | task_id="trigger_dagrun_op1", execution_date="2024-12-04" +52 | def decorator_deprecated_operator_args(): +53 | trigger_dagrun_op = trigger_dagrun.TriggerDagRunOperator( +54 | task_id="trigger_dagrun_op1", execution_date="2024-12-04" | ^^^^^^^^^^^^^^ AIR301 -54 | ) -55 | trigger_dagrun_op2 = TriggerDagRunOperator( +55 | ) +56 | trigger_dagrun_op2 = TriggerDagRunOperator( | = help: Use `logical_date` instead ℹ Safe fix -50 50 | @dag() -51 51 | def decorator_deprecated_operator_args(): -52 52 | trigger_dagrun_op = trigger_dagrun.TriggerDagRunOperator( -53 |- task_id="trigger_dagrun_op1", execution_date="2024-12-04" - 53 |+ task_id="trigger_dagrun_op1", logical_date="2024-12-04" -54 54 | ) -55 55 | trigger_dagrun_op2 = TriggerDagRunOperator( -56 56 | task_id="trigger_dagrun_op2", execution_date="2024-12-04" +51 51 | @dag() +52 52 | def decorator_deprecated_operator_args(): +53 53 | trigger_dagrun_op = trigger_dagrun.TriggerDagRunOperator( +54 |- task_id="trigger_dagrun_op1", execution_date="2024-12-04" + 54 |+ task_id="trigger_dagrun_op1", logical_date="2024-12-04" +55 55 | ) +56 56 | trigger_dagrun_op2 = TriggerDagRunOperator( +57 57 | task_id="trigger_dagrun_op2", execution_date="2024-12-04" -AIR301_args.py:56:39: AIR301 [*] `execution_date` is removed in Airflow 3.0 +AIR301_args.py:57:39: AIR301 [*] `execution_date` is removed in Airflow 3.0 | -54 | ) -55 | trigger_dagrun_op2 = TriggerDagRunOperator( -56 | task_id="trigger_dagrun_op2", execution_date="2024-12-04" +55 | ) +56 | trigger_dagrun_op2 = TriggerDagRunOperator( +57 | task_id="trigger_dagrun_op2", execution_date="2024-12-04" | ^^^^^^^^^^^^^^ AIR301 -57 | ) +58 | ) | = help: Use `logical_date` instead ℹ Safe fix -53 53 | task_id="trigger_dagrun_op1", execution_date="2024-12-04" -54 54 | ) -55 55 | trigger_dagrun_op2 = TriggerDagRunOperator( -56 |- task_id="trigger_dagrun_op2", execution_date="2024-12-04" - 56 |+ task_id="trigger_dagrun_op2", logical_date="2024-12-04" -57 57 | ) -58 58 | -59 59 | branch_dt_op = datetime.BranchDateTimeOperator( +54 54 | task_id="trigger_dagrun_op1", execution_date="2024-12-04" +55 55 | ) +56 56 | trigger_dagrun_op2 = TriggerDagRunOperator( +57 |- task_id="trigger_dagrun_op2", execution_date="2024-12-04" + 57 |+ task_id="trigger_dagrun_op2", logical_date="2024-12-04" +58 58 | ) +59 59 | +60 60 | branch_dt_op = datetime.BranchDateTimeOperator( -AIR301_args.py:60:33: AIR301 [*] `use_task_execution_day` is removed in Airflow 3.0 +AIR301_args.py:61:33: AIR301 [*] `use_task_execution_day` is removed in Airflow 3.0 | -59 | branch_dt_op = datetime.BranchDateTimeOperator( -60 | task_id="branch_dt_op", use_task_execution_day=True, task_concurrency=5 +60 | branch_dt_op = datetime.BranchDateTimeOperator( +61 | task_id="branch_dt_op", use_task_execution_day=True, task_concurrency=5 | ^^^^^^^^^^^^^^^^^^^^^^ AIR301 -61 | ) -62 | branch_dt_op2 = BranchDateTimeOperator( +62 | ) +63 | branch_dt_op2 = BranchDateTimeOperator( | = help: Use `use_task_logical_date` instead ℹ Safe fix -57 57 | ) -58 58 | -59 59 | branch_dt_op = datetime.BranchDateTimeOperator( -60 |- task_id="branch_dt_op", use_task_execution_day=True, task_concurrency=5 - 60 |+ task_id="branch_dt_op", use_task_logical_date=True, task_concurrency=5 -61 61 | ) -62 62 | branch_dt_op2 = BranchDateTimeOperator( -63 63 | task_id="branch_dt_op2", +58 58 | ) +59 59 | +60 60 | branch_dt_op = datetime.BranchDateTimeOperator( +61 |- task_id="branch_dt_op", use_task_execution_day=True, task_concurrency=5 + 61 |+ task_id="branch_dt_op", use_task_logical_date=True, task_concurrency=5 +62 62 | ) +63 63 | branch_dt_op2 = BranchDateTimeOperator( +64 64 | task_id="branch_dt_op2", -AIR301_args.py:60:62: AIR301 [*] `task_concurrency` is removed in Airflow 3.0 +AIR301_args.py:61:62: AIR301 [*] `task_concurrency` is removed in Airflow 3.0 | -59 | branch_dt_op = datetime.BranchDateTimeOperator( -60 | task_id="branch_dt_op", use_task_execution_day=True, task_concurrency=5 +60 | branch_dt_op = datetime.BranchDateTimeOperator( +61 | task_id="branch_dt_op", use_task_execution_day=True, task_concurrency=5 | ^^^^^^^^^^^^^^^^ AIR301 -61 | ) -62 | branch_dt_op2 = BranchDateTimeOperator( +62 | ) +63 | branch_dt_op2 = BranchDateTimeOperator( | = help: Use `max_active_tis_per_dag` instead ℹ Safe fix -57 57 | ) -58 58 | -59 59 | branch_dt_op = datetime.BranchDateTimeOperator( -60 |- task_id="branch_dt_op", use_task_execution_day=True, task_concurrency=5 - 60 |+ task_id="branch_dt_op", use_task_execution_day=True, max_active_tis_per_dag=5 -61 61 | ) -62 62 | branch_dt_op2 = BranchDateTimeOperator( -63 63 | task_id="branch_dt_op2", +58 58 | ) +59 59 | +60 60 | branch_dt_op = datetime.BranchDateTimeOperator( +61 |- task_id="branch_dt_op", use_task_execution_day=True, task_concurrency=5 + 61 |+ task_id="branch_dt_op", use_task_execution_day=True, max_active_tis_per_dag=5 +62 62 | ) +63 63 | branch_dt_op2 = BranchDateTimeOperator( +64 64 | task_id="branch_dt_op2", -AIR301_args.py:64:9: AIR301 [*] `use_task_execution_day` is removed in Airflow 3.0 +AIR301_args.py:65:9: AIR301 [*] `use_task_execution_day` is removed in Airflow 3.0 | -62 | branch_dt_op2 = BranchDateTimeOperator( -63 | task_id="branch_dt_op2", -64 | use_task_execution_day=True, +63 | branch_dt_op2 = BranchDateTimeOperator( +64 | task_id="branch_dt_op2", +65 | use_task_execution_day=True, | ^^^^^^^^^^^^^^^^^^^^^^ AIR301 -65 | sla=timedelta(seconds=10), -66 | ) +66 | sla=timedelta(seconds=10), +67 | ) + | + = help: Use `use_task_logical_date` instead + +ℹ Safe fix +62 62 | ) +63 63 | branch_dt_op2 = BranchDateTimeOperator( +64 64 | task_id="branch_dt_op2", +65 |- use_task_execution_day=True, + 65 |+ use_task_logical_date=True, +66 66 | sla=timedelta(seconds=10), +67 67 | ) +68 68 | + +AIR301_args.py:79:60: AIR301 [*] `use_task_execution_day` is removed in Airflow 3.0 + | +77 | task_id="bdow_op", use_task_execution_day=True +78 | ) +79 | bdow_op2 = BranchDayOfWeekOperator(task_id="bdow_op2", use_task_execution_day=True) + | ^^^^^^^^^^^^^^^^^^^^^^ AIR301 +80 | +81 | trigger_dagrun_op >> trigger_dagrun_op2 | = help: Use `use_task_logical_date` instead ℹ Safe fix -61 61 | ) -62 62 | branch_dt_op2 = BranchDateTimeOperator( -63 63 | task_id="branch_dt_op2", -64 |- use_task_execution_day=True, - 64 |+ use_task_logical_date=True, -65 65 | sla=timedelta(seconds=10), -66 66 | ) -67 67 | +76 76 | bdow_op = weekday.BranchDayOfWeekOperator( +77 77 | task_id="bdow_op", use_task_execution_day=True +78 78 | ) +79 |- bdow_op2 = BranchDayOfWeekOperator(task_id="bdow_op2", use_task_execution_day=True) + 79 |+ bdow_op2 = BranchDayOfWeekOperator(task_id="bdow_op2", use_task_logical_date=True) +80 80 | +81 81 | trigger_dagrun_op >> trigger_dagrun_op2 +82 82 | branch_dt_op >> branch_dt_op2 -AIR301_args.py:87:15: AIR301 `filename_template` is removed in Airflow 3.0 +AIR301_args.py:88:15: AIR301 `filename_template` is removed in Airflow 3.0 | -86 | # deprecated filename_template argument in FileTaskHandler -87 | S3TaskHandler(filename_template="/tmp/test") +87 | # deprecated filename_template argument in FileTaskHandler +88 | S3TaskHandler(filename_template="/tmp/test") | ^^^^^^^^^^^^^^^^^ AIR301 -88 | HdfsTaskHandler(filename_template="/tmp/test") -89 | ElasticsearchTaskHandler(filename_template="/tmp/test") +89 | HdfsTaskHandler(filename_template="/tmp/test") +90 | ElasticsearchTaskHandler(filename_template="/tmp/test") | -AIR301_args.py:88:17: AIR301 `filename_template` is removed in Airflow 3.0 +AIR301_args.py:89:17: AIR301 `filename_template` is removed in Airflow 3.0 | -86 | # deprecated filename_template argument in FileTaskHandler -87 | S3TaskHandler(filename_template="/tmp/test") -88 | HdfsTaskHandler(filename_template="/tmp/test") +87 | # deprecated filename_template argument in FileTaskHandler +88 | S3TaskHandler(filename_template="/tmp/test") +89 | HdfsTaskHandler(filename_template="/tmp/test") | ^^^^^^^^^^^^^^^^^ AIR301 -89 | ElasticsearchTaskHandler(filename_template="/tmp/test") -90 | GCSTaskHandler(filename_template="/tmp/test") +90 | ElasticsearchTaskHandler(filename_template="/tmp/test") +91 | GCSTaskHandler(filename_template="/tmp/test") | -AIR301_args.py:89:26: AIR301 `filename_template` is removed in Airflow 3.0 +AIR301_args.py:90:26: AIR301 `filename_template` is removed in Airflow 3.0 | -87 | S3TaskHandler(filename_template="/tmp/test") -88 | HdfsTaskHandler(filename_template="/tmp/test") -89 | ElasticsearchTaskHandler(filename_template="/tmp/test") +88 | S3TaskHandler(filename_template="/tmp/test") +89 | HdfsTaskHandler(filename_template="/tmp/test") +90 | ElasticsearchTaskHandler(filename_template="/tmp/test") | ^^^^^^^^^^^^^^^^^ AIR301 -90 | GCSTaskHandler(filename_template="/tmp/test") +91 | GCSTaskHandler(filename_template="/tmp/test") | -AIR301_args.py:90:16: AIR301 `filename_template` is removed in Airflow 3.0 +AIR301_args.py:91:16: AIR301 `filename_template` is removed in Airflow 3.0 | -88 | HdfsTaskHandler(filename_template="/tmp/test") -89 | ElasticsearchTaskHandler(filename_template="/tmp/test") -90 | GCSTaskHandler(filename_template="/tmp/test") +89 | HdfsTaskHandler(filename_template="/tmp/test") +90 | ElasticsearchTaskHandler(filename_template="/tmp/test") +91 | GCSTaskHandler(filename_template="/tmp/test") | ^^^^^^^^^^^^^^^^^ AIR301 -91 | -92 | FabAuthManager(None) +92 | +93 | FabAuthManager(None) | -AIR301_args.py:92:15: AIR301 `appbuilder` is removed in Airflow 3.0 +AIR301_args.py:93:15: AIR301 `appbuilder` is removed in Airflow 3.0 | -90 | GCSTaskHandler(filename_template="/tmp/test") -91 | -92 | FabAuthManager(None) +91 | GCSTaskHandler(filename_template="/tmp/test") +92 | +93 | FabAuthManager(None) | ^^^^^^ AIR301 | = help: The constructor takes no parameter now diff --git a/crates/ruff_linter/src/rules/airflow/snapshots/ruff_linter__rules__airflow__tests__AIR301_AIR301_names.py.snap b/crates/ruff_linter/src/rules/airflow/snapshots/ruff_linter__rules__airflow__tests__AIR301_AIR301_names.py.snap index 73b170eb14bc8..134511f4e0c9b 100644 --- a/crates/ruff_linter/src/rules/airflow/snapshots/ruff_linter__rules__airflow__tests__AIR301_AIR301_names.py.snap +++ b/crates/ruff_linter/src/rules/airflow/snapshots/ruff_linter__rules__airflow__tests__AIR301_AIR301_names.py.snap @@ -492,5 +492,16 @@ AIR301_names.py:165:1: AIR301 `airflow.notifications.basenotifier.BaseNotifier` 164 | from airflow.notifications.basenotifier import BaseNotifier 165 | BaseNotifier() | ^^^^^^^^^^^^ AIR301 +166 | +167 | # airflow.auth.manager | = help: Use `airflow.sdk.bases.notifier.BaseNotifier` instead + +AIR301_names.py:169:1: AIR301 `airflow.auth.managers.base_auth_manager.BaseAuthManager` is removed in Airflow 3.0 + | +167 | # airflow.auth.manager +168 | from airflow.auth.managers.base_auth_manager import BaseAuthManager +169 | BaseAuthManager() + | ^^^^^^^^^^^^^^^ AIR301 + | + = help: Use `airflow.api_fastapi.auth.managers.base_auth_manager.BaseAuthManager` instead From 56c95b1e928bb8cb38214ed1781fbf7227dbb638 Mon Sep 17 00:00:00 2001 From: Wei Lee Date: Fri, 9 May 2025 22:22:19 +0800 Subject: [PATCH 5/8] test(AIR301): update test cases --- .../test/fixtures/airflow/AIR301_args.py | 29 ++- .../airflow/AIR301_class_attribute.py | 3 +- .../test/fixtures/airflow/AIR301_names.py | 4 +- ...airflow__tests__AIR301_AIR301_args.py.snap | 137 +++++++------- ...sts__AIR301_AIR301_class_attribute.py.snap | 176 +++++++++--------- ...irflow__tests__AIR301_AIR301_names.py.snap | 18 +- 6 files changed, 190 insertions(+), 177 deletions(-) diff --git a/crates/ruff_linter/resources/test/fixtures/airflow/AIR301_args.py b/crates/ruff_linter/resources/test/fixtures/airflow/AIR301_args.py index 4d2f9ba7a6052..ce35d79338195 100644 --- a/crates/ruff_linter/resources/test/fixtures/airflow/AIR301_args.py +++ b/crates/ruff_linter/resources/test/fixtures/airflow/AIR301_args.py @@ -5,6 +5,7 @@ from airflow import DAG, dag from airflow.operators.datetime import BranchDateTimeOperator from airflow.operators.trigger_dagrun import TriggerDagRunOperator +from airflow.operators.weekday import BranchDayOfWeekOperator from airflow.providers.amazon.aws.log.s3_task_handler import S3TaskHandler from airflow.providers.apache.hdfs.log.hdfs_task_handler import HdfsTaskHandler from airflow.providers.elasticsearch.log.es_task_handler import ElasticsearchTaskHandler @@ -12,7 +13,6 @@ from airflow.providers.google.cloud.log.gcs_task_handler import GCSTaskHandler from airflow.providers.standard.operators import datetime, trigger_dagrun from airflow.providers.standard.sensors import weekday -from airflow.operators.weekday import BranchDayOfWeekOperator from airflow.sensors.weekday import DayOfWeekSensor from airflow.timetables.simple import NullTimetable @@ -51,10 +51,10 @@ def decorator_timetable(): @dag() def decorator_deprecated_operator_args(): trigger_dagrun_op = trigger_dagrun.TriggerDagRunOperator( - task_id="trigger_dagrun_op1", execution_date="2024-12-04" + task_id="trigger_dagrun_op1", trigger_dag_id="test", execution_date="2024-12-04" ) trigger_dagrun_op2 = TriggerDagRunOperator( - task_id="trigger_dagrun_op2", execution_date="2024-12-04" + task_id="trigger_dagrun_op2", trigger_dag_id="test", execution_date="2024-12-04" ) branch_dt_op = datetime.BranchDateTimeOperator( @@ -67,16 +67,30 @@ def decorator_deprecated_operator_args(): ) dof_task_sensor = weekday.DayOfWeekSensor( - task_id="dof_task_sensor", use_task_execution_day=True + task_id="dof_task_sensor", + week_day=1, + use_task_execution_day=True, ) dof_task_sensor2 = DayOfWeekSensor( - task_id="dof_task_sensor2", use_task_execution_day=True + task_id="dof_task_sensor2", + week_day=1, + use_task_execution_day=True, ) bdow_op = weekday.BranchDayOfWeekOperator( - task_id="bdow_op", use_task_execution_day=True + task_id="bdow_op", + follow_task_ids_if_false=None, + follow_task_ids_if_true=None, + week_day=1, + use_task_execution_day=True, + ) + bdow_op2 = BranchDayOfWeekOperator( + task_id="bdow_op2", + follow_task_ids_if_false=None, + follow_task_ids_if_true=None, + week_day=1, + use_task_execution_day=True, ) - bdow_op2 = BranchDayOfWeekOperator(task_id="bdow_op2", use_task_execution_day=True) trigger_dagrun_op >> trigger_dagrun_op2 branch_dt_op >> branch_dt_op2 @@ -91,4 +105,3 @@ def decorator_deprecated_operator_args(): GCSTaskHandler(filename_template="/tmp/test") FabAuthManager(None) - diff --git a/crates/ruff_linter/resources/test/fixtures/airflow/AIR301_class_attribute.py b/crates/ruff_linter/resources/test/fixtures/airflow/AIR301_class_attribute.py index f2ac5a1ab8f0e..605ad5b07e52a 100644 --- a/crates/ruff_linter/resources/test/fixtures/airflow/AIR301_class_attribute.py +++ b/crates/ruff_linter/resources/test/fixtures/airflow/AIR301_class_attribute.py @@ -83,8 +83,7 @@ # airflow.providers_manager pm = ProvidersManager() -pm.initialize_providers_asset_uri_resources() -pm.dataset_factories +pm.initialize_providers_dataset_uri_resources() pm.dataset_factories pm.dataset_uri_handlers pm.dataset_to_openlineage_converters diff --git a/crates/ruff_linter/resources/test/fixtures/airflow/AIR301_names.py b/crates/ruff_linter/resources/test/fixtures/airflow/AIR301_names.py index 31710de9b6a1a..e3654a4c52ba6 100644 --- a/crates/ruff_linter/resources/test/fixtures/airflow/AIR301_names.py +++ b/crates/ruff_linter/resources/test/fixtures/airflow/AIR301_names.py @@ -162,8 +162,10 @@ # airflow.notifications.basenotifier from airflow.notifications.basenotifier import BaseNotifier + BaseNotifier() # airflow.auth.manager from airflow.auth.managers.base_auth_manager import BaseAuthManager -BaseAuthManager() \ No newline at end of file + +BaseAuthManager() diff --git a/crates/ruff_linter/src/rules/airflow/snapshots/ruff_linter__rules__airflow__tests__AIR301_AIR301_args.py.snap b/crates/ruff_linter/src/rules/airflow/snapshots/ruff_linter__rules__airflow__tests__AIR301_AIR301_args.py.snap index 7cb8bf7ef9913..af858937ab620 100644 --- a/crates/ruff_linter/src/rules/airflow/snapshots/ruff_linter__rules__airflow__tests__AIR301_AIR301_args.py.snap +++ b/crates/ruff_linter/src/rules/airflow/snapshots/ruff_linter__rules__airflow__tests__AIR301_AIR301_args.py.snap @@ -118,12 +118,12 @@ AIR301_args.py:46:6: AIR301 [*] `timetable` is removed in Airflow 3.0 48 48 | pass 49 49 | -AIR301_args.py:54:39: AIR301 [*] `execution_date` is removed in Airflow 3.0 +AIR301_args.py:54:62: AIR301 [*] `execution_date` is removed in Airflow 3.0 | 52 | def decorator_deprecated_operator_args(): 53 | trigger_dagrun_op = trigger_dagrun.TriggerDagRunOperator( -54 | task_id="trigger_dagrun_op1", execution_date="2024-12-04" - | ^^^^^^^^^^^^^^ AIR301 +54 | task_id="trigger_dagrun_op1", trigger_dag_id="test", execution_date="2024-12-04" + | ^^^^^^^^^^^^^^ AIR301 55 | ) 56 | trigger_dagrun_op2 = TriggerDagRunOperator( | @@ -133,28 +133,28 @@ AIR301_args.py:54:39: AIR301 [*] `execution_date` is removed in Airflow 3.0 51 51 | @dag() 52 52 | def decorator_deprecated_operator_args(): 53 53 | trigger_dagrun_op = trigger_dagrun.TriggerDagRunOperator( -54 |- task_id="trigger_dagrun_op1", execution_date="2024-12-04" - 54 |+ task_id="trigger_dagrun_op1", logical_date="2024-12-04" +54 |- task_id="trigger_dagrun_op1", trigger_dag_id="test", execution_date="2024-12-04" + 54 |+ task_id="trigger_dagrun_op1", trigger_dag_id="test", logical_date="2024-12-04" 55 55 | ) 56 56 | trigger_dagrun_op2 = TriggerDagRunOperator( -57 57 | task_id="trigger_dagrun_op2", execution_date="2024-12-04" +57 57 | task_id="trigger_dagrun_op2", trigger_dag_id="test", execution_date="2024-12-04" -AIR301_args.py:57:39: AIR301 [*] `execution_date` is removed in Airflow 3.0 +AIR301_args.py:57:62: AIR301 [*] `execution_date` is removed in Airflow 3.0 | 55 | ) 56 | trigger_dagrun_op2 = TriggerDagRunOperator( -57 | task_id="trigger_dagrun_op2", execution_date="2024-12-04" - | ^^^^^^^^^^^^^^ AIR301 +57 | task_id="trigger_dagrun_op2", trigger_dag_id="test", execution_date="2024-12-04" + | ^^^^^^^^^^^^^^ AIR301 58 | ) | = help: Use `logical_date` instead ℹ Safe fix -54 54 | task_id="trigger_dagrun_op1", execution_date="2024-12-04" +54 54 | task_id="trigger_dagrun_op1", trigger_dag_id="test", execution_date="2024-12-04" 55 55 | ) 56 56 | trigger_dagrun_op2 = TriggerDagRunOperator( -57 |- task_id="trigger_dagrun_op2", execution_date="2024-12-04" - 57 |+ task_id="trigger_dagrun_op2", logical_date="2024-12-04" +57 |- task_id="trigger_dagrun_op2", trigger_dag_id="test", execution_date="2024-12-04" + 57 |+ task_id="trigger_dagrun_op2", trigger_dag_id="test", logical_date="2024-12-04" 58 58 | ) 59 59 | 60 60 | branch_dt_op = datetime.BranchDateTimeOperator( @@ -220,70 +220,69 @@ AIR301_args.py:65:9: AIR301 [*] `use_task_execution_day` is removed in Airflow 3 67 67 | ) 68 68 | -AIR301_args.py:79:60: AIR301 [*] `use_task_execution_day` is removed in Airflow 3.0 +AIR301_args.py:92:9: AIR301 [*] `use_task_execution_day` is removed in Airflow 3.0 | -77 | task_id="bdow_op", use_task_execution_day=True -78 | ) -79 | bdow_op2 = BranchDayOfWeekOperator(task_id="bdow_op2", use_task_execution_day=True) - | ^^^^^^^^^^^^^^^^^^^^^^ AIR301 -80 | -81 | trigger_dagrun_op >> trigger_dagrun_op2 +90 | follow_task_ids_if_true=None, +91 | week_day=1, +92 | use_task_execution_day=True, + | ^^^^^^^^^^^^^^^^^^^^^^ AIR301 +93 | ) | = help: Use `use_task_logical_date` instead ℹ Safe fix -76 76 | bdow_op = weekday.BranchDayOfWeekOperator( -77 77 | task_id="bdow_op", use_task_execution_day=True -78 78 | ) -79 |- bdow_op2 = BranchDayOfWeekOperator(task_id="bdow_op2", use_task_execution_day=True) - 79 |+ bdow_op2 = BranchDayOfWeekOperator(task_id="bdow_op2", use_task_logical_date=True) -80 80 | -81 81 | trigger_dagrun_op >> trigger_dagrun_op2 -82 82 | branch_dt_op >> branch_dt_op2 +89 89 | follow_task_ids_if_false=None, +90 90 | follow_task_ids_if_true=None, +91 91 | week_day=1, +92 |- use_task_execution_day=True, + 92 |+ use_task_logical_date=True, +93 93 | ) +94 94 | +95 95 | trigger_dagrun_op >> trigger_dagrun_op2 -AIR301_args.py:88:15: AIR301 `filename_template` is removed in Airflow 3.0 - | -87 | # deprecated filename_template argument in FileTaskHandler -88 | S3TaskHandler(filename_template="/tmp/test") - | ^^^^^^^^^^^^^^^^^ AIR301 -89 | HdfsTaskHandler(filename_template="/tmp/test") -90 | ElasticsearchTaskHandler(filename_template="/tmp/test") - | +AIR301_args.py:102:15: AIR301 `filename_template` is removed in Airflow 3.0 + | +101 | # deprecated filename_template argument in FileTaskHandler +102 | S3TaskHandler(filename_template="/tmp/test") + | ^^^^^^^^^^^^^^^^^ AIR301 +103 | HdfsTaskHandler(filename_template="/tmp/test") +104 | ElasticsearchTaskHandler(filename_template="/tmp/test") + | -AIR301_args.py:89:17: AIR301 `filename_template` is removed in Airflow 3.0 - | -87 | # deprecated filename_template argument in FileTaskHandler -88 | S3TaskHandler(filename_template="/tmp/test") -89 | HdfsTaskHandler(filename_template="/tmp/test") - | ^^^^^^^^^^^^^^^^^ AIR301 -90 | ElasticsearchTaskHandler(filename_template="/tmp/test") -91 | GCSTaskHandler(filename_template="/tmp/test") - | +AIR301_args.py:103:17: AIR301 `filename_template` is removed in Airflow 3.0 + | +101 | # deprecated filename_template argument in FileTaskHandler +102 | S3TaskHandler(filename_template="/tmp/test") +103 | HdfsTaskHandler(filename_template="/tmp/test") + | ^^^^^^^^^^^^^^^^^ AIR301 +104 | ElasticsearchTaskHandler(filename_template="/tmp/test") +105 | GCSTaskHandler(filename_template="/tmp/test") + | -AIR301_args.py:90:26: AIR301 `filename_template` is removed in Airflow 3.0 - | -88 | S3TaskHandler(filename_template="/tmp/test") -89 | HdfsTaskHandler(filename_template="/tmp/test") -90 | ElasticsearchTaskHandler(filename_template="/tmp/test") - | ^^^^^^^^^^^^^^^^^ AIR301 -91 | GCSTaskHandler(filename_template="/tmp/test") - | +AIR301_args.py:104:26: AIR301 `filename_template` is removed in Airflow 3.0 + | +102 | S3TaskHandler(filename_template="/tmp/test") +103 | HdfsTaskHandler(filename_template="/tmp/test") +104 | ElasticsearchTaskHandler(filename_template="/tmp/test") + | ^^^^^^^^^^^^^^^^^ AIR301 +105 | GCSTaskHandler(filename_template="/tmp/test") + | -AIR301_args.py:91:16: AIR301 `filename_template` is removed in Airflow 3.0 - | -89 | HdfsTaskHandler(filename_template="/tmp/test") -90 | ElasticsearchTaskHandler(filename_template="/tmp/test") -91 | GCSTaskHandler(filename_template="/tmp/test") - | ^^^^^^^^^^^^^^^^^ AIR301 -92 | -93 | FabAuthManager(None) - | +AIR301_args.py:105:16: AIR301 `filename_template` is removed in Airflow 3.0 + | +103 | HdfsTaskHandler(filename_template="/tmp/test") +104 | ElasticsearchTaskHandler(filename_template="/tmp/test") +105 | GCSTaskHandler(filename_template="/tmp/test") + | ^^^^^^^^^^^^^^^^^ AIR301 +106 | +107 | FabAuthManager(None) + | -AIR301_args.py:93:15: AIR301 `appbuilder` is removed in Airflow 3.0 - | -91 | GCSTaskHandler(filename_template="/tmp/test") -92 | -93 | FabAuthManager(None) - | ^^^^^^ AIR301 - | - = help: The constructor takes no parameter now +AIR301_args.py:107:15: AIR301 `appbuilder` is removed in Airflow 3.0 + | +105 | GCSTaskHandler(filename_template="/tmp/test") +106 | +107 | FabAuthManager(None) + | ^^^^^^ AIR301 + | + = help: The constructor takes no parameter now diff --git a/crates/ruff_linter/src/rules/airflow/snapshots/ruff_linter__rules__airflow__tests__AIR301_AIR301_class_attribute.py.snap b/crates/ruff_linter/src/rules/airflow/snapshots/ruff_linter__rules__airflow__tests__AIR301_AIR301_class_attribute.py.snap index bc8b673a00a0c..64c7197140a8f 100644 --- a/crates/ruff_linter/src/rules/airflow/snapshots/ruff_linter__rules__airflow__tests__AIR301_AIR301_class_attribute.py.snap +++ b/crates/ruff_linter/src/rules/airflow/snapshots/ruff_linter__rules__airflow__tests__AIR301_AIR301_class_attribute.py.snap @@ -529,142 +529,142 @@ AIR301_class_attribute.py:79:15: AIR301 [*] `get_connections` is removed in Airf 81 81 | not_an_error = NotAir302SecretError() 82 82 | not_an_error.get_conn_uri() -AIR301_class_attribute.py:87:4: AIR301 [*] `dataset_factories` is removed in Airflow 3.0 +AIR301_class_attribute.py:86:4: AIR301 [*] `initialize_providers_dataset_uri_resources` is removed in Airflow 3.0 | +84 | # airflow.providers_manager 85 | pm = ProvidersManager() -86 | pm.initialize_providers_asset_uri_resources() +86 | pm.initialize_providers_dataset_uri_resources() + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ AIR301 87 | pm.dataset_factories - | ^^^^^^^^^^^^^^^^^ AIR301 -88 | pm.dataset_factories -89 | pm.dataset_uri_handlers +88 | pm.dataset_uri_handlers | - = help: Use `asset_factories` instead + = help: Use `initialize_providers_asset_uri_resources` instead ℹ Safe fix +83 83 | 84 84 | # airflow.providers_manager 85 85 | pm = ProvidersManager() -86 86 | pm.initialize_providers_asset_uri_resources() -87 |-pm.dataset_factories - 87 |+pm.asset_factories -88 88 | pm.dataset_factories -89 89 | pm.dataset_uri_handlers -90 90 | pm.dataset_to_openlineage_converters +86 |-pm.initialize_providers_dataset_uri_resources() + 86 |+pm.initialize_providers_asset_uri_resources() +87 87 | pm.dataset_factories +88 88 | pm.dataset_uri_handlers +89 89 | pm.dataset_to_openlineage_converters -AIR301_class_attribute.py:88:4: AIR301 [*] `dataset_factories` is removed in Airflow 3.0 +AIR301_class_attribute.py:87:4: AIR301 [*] `dataset_factories` is removed in Airflow 3.0 | -86 | pm.initialize_providers_asset_uri_resources() +85 | pm = ProvidersManager() +86 | pm.initialize_providers_dataset_uri_resources() 87 | pm.dataset_factories -88 | pm.dataset_factories | ^^^^^^^^^^^^^^^^^ AIR301 -89 | pm.dataset_uri_handlers -90 | pm.dataset_to_openlineage_converters +88 | pm.dataset_uri_handlers +89 | pm.dataset_to_openlineage_converters | = help: Use `asset_factories` instead ℹ Safe fix +84 84 | # airflow.providers_manager 85 85 | pm = ProvidersManager() -86 86 | pm.initialize_providers_asset_uri_resources() -87 87 | pm.dataset_factories -88 |-pm.dataset_factories - 88 |+pm.asset_factories -89 89 | pm.dataset_uri_handlers -90 90 | pm.dataset_to_openlineage_converters -91 91 | +86 86 | pm.initialize_providers_dataset_uri_resources() +87 |-pm.dataset_factories + 87 |+pm.asset_factories +88 88 | pm.dataset_uri_handlers +89 89 | pm.dataset_to_openlineage_converters +90 90 | -AIR301_class_attribute.py:89:4: AIR301 [*] `dataset_uri_handlers` is removed in Airflow 3.0 +AIR301_class_attribute.py:88:4: AIR301 [*] `dataset_uri_handlers` is removed in Airflow 3.0 | +86 | pm.initialize_providers_dataset_uri_resources() 87 | pm.dataset_factories -88 | pm.dataset_factories -89 | pm.dataset_uri_handlers +88 | pm.dataset_uri_handlers | ^^^^^^^^^^^^^^^^^^^^ AIR301 -90 | pm.dataset_to_openlineage_converters +89 | pm.dataset_to_openlineage_converters | = help: Use `asset_uri_handlers` instead ℹ Safe fix -86 86 | pm.initialize_providers_asset_uri_resources() +85 85 | pm = ProvidersManager() +86 86 | pm.initialize_providers_dataset_uri_resources() 87 87 | pm.dataset_factories -88 88 | pm.dataset_factories -89 |-pm.dataset_uri_handlers - 89 |+pm.asset_uri_handlers -90 90 | pm.dataset_to_openlineage_converters -91 91 | -92 92 | # airflow.secrets.base_secrets - -AIR301_class_attribute.py:90:4: AIR301 [*] `dataset_to_openlineage_converters` is removed in Airflow 3.0 - | -88 | pm.dataset_factories -89 | pm.dataset_uri_handlers -90 | pm.dataset_to_openlineage_converters +88 |-pm.dataset_uri_handlers + 88 |+pm.asset_uri_handlers +89 89 | pm.dataset_to_openlineage_converters +90 90 | +91 91 | # airflow.secrets.base_secrets + +AIR301_class_attribute.py:89:4: AIR301 [*] `dataset_to_openlineage_converters` is removed in Airflow 3.0 + | +87 | pm.dataset_factories +88 | pm.dataset_uri_handlers +89 | pm.dataset_to_openlineage_converters | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ AIR301 -91 | -92 | # airflow.secrets.base_secrets +90 | +91 | # airflow.secrets.base_secrets | = help: Use `asset_to_openlineage_converters` instead ℹ Safe fix +86 86 | pm.initialize_providers_dataset_uri_resources() 87 87 | pm.dataset_factories -88 88 | pm.dataset_factories -89 89 | pm.dataset_uri_handlers -90 |-pm.dataset_to_openlineage_converters - 90 |+pm.asset_to_openlineage_converters -91 91 | -92 92 | # airflow.secrets.base_secrets -93 93 | base_secret_backend = BaseSecretsBackend() - -AIR301_class_attribute.py:94:21: AIR301 [*] `get_conn_uri` is removed in Airflow 3.0 - | -92 | # airflow.secrets.base_secrets -93 | base_secret_backend = BaseSecretsBackend() -94 | base_secret_backend.get_conn_uri() +88 88 | pm.dataset_uri_handlers +89 |-pm.dataset_to_openlineage_converters + 89 |+pm.asset_to_openlineage_converters +90 90 | +91 91 | # airflow.secrets.base_secrets +92 92 | base_secret_backend = BaseSecretsBackend() + +AIR301_class_attribute.py:93:21: AIR301 [*] `get_conn_uri` is removed in Airflow 3.0 + | +91 | # airflow.secrets.base_secrets +92 | base_secret_backend = BaseSecretsBackend() +93 | base_secret_backend.get_conn_uri() | ^^^^^^^^^^^^ AIR301 -95 | base_secret_backend.get_connections() +94 | base_secret_backend.get_connections() | = help: Use `get_conn_value` instead ℹ Safe fix -91 91 | -92 92 | # airflow.secrets.base_secrets -93 93 | base_secret_backend = BaseSecretsBackend() -94 |-base_secret_backend.get_conn_uri() - 94 |+base_secret_backend.get_conn_value() -95 95 | base_secret_backend.get_connections() -96 96 | -97 97 | # airflow.secrets.local_filesystem +90 90 | +91 91 | # airflow.secrets.base_secrets +92 92 | base_secret_backend = BaseSecretsBackend() +93 |-base_secret_backend.get_conn_uri() + 93 |+base_secret_backend.get_conn_value() +94 94 | base_secret_backend.get_connections() +95 95 | +96 96 | # airflow.secrets.local_filesystem -AIR301_class_attribute.py:95:21: AIR301 [*] `get_connections` is removed in Airflow 3.0 +AIR301_class_attribute.py:94:21: AIR301 [*] `get_connections` is removed in Airflow 3.0 | -93 | base_secret_backend = BaseSecretsBackend() -94 | base_secret_backend.get_conn_uri() -95 | base_secret_backend.get_connections() +92 | base_secret_backend = BaseSecretsBackend() +93 | base_secret_backend.get_conn_uri() +94 | base_secret_backend.get_connections() | ^^^^^^^^^^^^^^^ AIR301 -96 | -97 | # airflow.secrets.local_filesystem +95 | +96 | # airflow.secrets.local_filesystem | = help: Use `get_connection` instead ℹ Safe fix -92 92 | # airflow.secrets.base_secrets -93 93 | base_secret_backend = BaseSecretsBackend() -94 94 | base_secret_backend.get_conn_uri() -95 |-base_secret_backend.get_connections() - 95 |+base_secret_backend.get_connection() -96 96 | -97 97 | # airflow.secrets.local_filesystem -98 98 | lfb = LocalFilesystemBackend() +91 91 | # airflow.secrets.base_secrets +92 92 | base_secret_backend = BaseSecretsBackend() +93 93 | base_secret_backend.get_conn_uri() +94 |-base_secret_backend.get_connections() + 94 |+base_secret_backend.get_connection() +95 95 | +96 96 | # airflow.secrets.local_filesystem +97 97 | lfb = LocalFilesystemBackend() -AIR301_class_attribute.py:99:5: AIR301 [*] `get_connections` is removed in Airflow 3.0 +AIR301_class_attribute.py:98:5: AIR301 [*] `get_connections` is removed in Airflow 3.0 | -97 | # airflow.secrets.local_filesystem -98 | lfb = LocalFilesystemBackend() -99 | lfb.get_connections() +96 | # airflow.secrets.local_filesystem +97 | lfb = LocalFilesystemBackend() +98 | lfb.get_connections() | ^^^^^^^^^^^^^^^ AIR301 | = help: Use `get_connection` instead ℹ Safe fix -96 96 | -97 97 | # airflow.secrets.local_filesystem -98 98 | lfb = LocalFilesystemBackend() -99 |-lfb.get_connections() - 99 |+lfb.get_connection() +95 95 | +96 96 | # airflow.secrets.local_filesystem +97 97 | lfb = LocalFilesystemBackend() +98 |-lfb.get_connections() + 98 |+lfb.get_connection() diff --git a/crates/ruff_linter/src/rules/airflow/snapshots/ruff_linter__rules__airflow__tests__AIR301_AIR301_names.py.snap b/crates/ruff_linter/src/rules/airflow/snapshots/ruff_linter__rules__airflow__tests__AIR301_AIR301_names.py.snap index 134511f4e0c9b..2fa44fd2dabe9 100644 --- a/crates/ruff_linter/src/rules/airflow/snapshots/ruff_linter__rules__airflow__tests__AIR301_AIR301_names.py.snap +++ b/crates/ruff_linter/src/rules/airflow/snapshots/ruff_linter__rules__airflow__tests__AIR301_AIR301_names.py.snap @@ -486,22 +486,22 @@ AIR301_names.py:161:1: AIR301 `airflow.providers.trino.datasets.trino.sanitize_u | = help: Use `airflow.providers.trino.assets.trino.sanitize_uri` instead -AIR301_names.py:165:1: AIR301 `airflow.notifications.basenotifier.BaseNotifier` is removed in Airflow 3.0 +AIR301_names.py:166:1: AIR301 `airflow.notifications.basenotifier.BaseNotifier` is removed in Airflow 3.0 | -163 | # airflow.notifications.basenotifier 164 | from airflow.notifications.basenotifier import BaseNotifier -165 | BaseNotifier() +165 | +166 | BaseNotifier() | ^^^^^^^^^^^^ AIR301 -166 | -167 | # airflow.auth.manager +167 | +168 | # airflow.auth.manager | = help: Use `airflow.sdk.bases.notifier.BaseNotifier` instead -AIR301_names.py:169:1: AIR301 `airflow.auth.managers.base_auth_manager.BaseAuthManager` is removed in Airflow 3.0 +AIR301_names.py:171:1: AIR301 `airflow.auth.managers.base_auth_manager.BaseAuthManager` is removed in Airflow 3.0 | -167 | # airflow.auth.manager -168 | from airflow.auth.managers.base_auth_manager import BaseAuthManager -169 | BaseAuthManager() +169 | from airflow.auth.managers.base_auth_manager import BaseAuthManager +170 | +171 | BaseAuthManager() | ^^^^^^^^^^^^^^^ AIR301 | = help: Use `airflow.api_fastapi.auth.managers.base_auth_manager.BaseAuthManager` instead From bc0f3e09904d03d46d65dfadbdd64046f7cc27a9 Mon Sep 17 00:00:00 2001 From: Wei Lee Date: Mon, 12 May 2025 23:14:37 +0800 Subject: [PATCH 6/8] style: fix clippy warnings --- .../src/rules/airflow/rules/removal_in_3.rs | 6 +- ...irflow__tests__AIR301_AIR301_names.py.snap | 160 +++++++++++++++++- 2 files changed, 155 insertions(+), 11 deletions(-) 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 7c34843543026..46def07b78a51 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 @@ -101,7 +101,6 @@ pub(crate) fn airflow_3_removal_expr(checker: &Checker, expr: &Expr) { check_context_key_usage_in_call(checker, call_expr); } Expr::Attribute(attribute_expr @ ExprAttribute { range, .. }) => { - // check_name(checker, expr, attr.range()); check_name(checker, expr, *range); check_class_attribute(checker, attribute_expr); } @@ -998,11 +997,12 @@ fn check_name(checker: &Checker, expr: &Expr, range: TextRange) { return; } - let import_target = if expr.is_attribute_expr() && name.contains(".") { - name.split(".").next().unwrap() + let import_target = if name.contains('.') { + name.split('.').next().unwrap() } else { name }; + diagnostic.try_set_fix(|| { let (import_edit, _) = checker.importer().get_or_import_symbol( &ImportRequest::import_from(module, import_target), diff --git a/crates/ruff_linter/src/rules/airflow/snapshots/ruff_linter__rules__airflow__tests__AIR301_AIR301_names.py.snap b/crates/ruff_linter/src/rules/airflow/snapshots/ruff_linter__rules__airflow__tests__AIR301_AIR301_names.py.snap index 2fa44fd2dabe9..3763881c29678 100644 --- a/crates/ruff_linter/src/rules/airflow/snapshots/ruff_linter__rules__airflow__tests__AIR301_AIR301_names.py.snap +++ b/crates/ruff_linter/src/rules/airflow/snapshots/ruff_linter__rules__airflow__tests__AIR301_AIR301_names.py.snap @@ -79,7 +79,7 @@ AIR301_names.py:56:1: AIR301 `airflow.api_connexion.security.requires_access` is | = help: Use `airflow.api_fastapi.core_api.security.requires_access_*` instead -AIR301_names.py:60:1: AIR301 `airflow.configuration.get` is removed in Airflow 3.0 +AIR301_names.py:60:1: AIR301 [*] `airflow.configuration.get` is removed in Airflow 3.0 | 59 | # airflow.configuration 60 | get, getboolean, getfloat, getint, has_option, remove_option, as_dict, set @@ -87,7 +87,25 @@ AIR301_names.py:60:1: AIR301 `airflow.configuration.get` is removed in Airflow 3 | = help: Use `airflow.configuration.conf.get` instead -AIR301_names.py:60:6: AIR301 `airflow.configuration.getboolean` is removed in Airflow 3.0 +ℹ Safe fix +19 19 | has_option, +20 20 | remove_option, +21 21 | set, + 22 |+conf, +22 23 | ) +23 24 | from airflow.contrib.aws_athena_hook import AWSAthenaHook +24 25 | from airflow.datasets import DatasetAliasEvent +-------------------------------------------------------------------------------- +57 58 | +58 59 | +59 60 | # airflow.configuration +60 |-get, getboolean, getfloat, getint, has_option, remove_option, as_dict, set + 61 |+conf.get, getboolean, getfloat, getint, has_option, remove_option, as_dict, set +61 62 | +62 63 | +63 64 | # airflow.contrib.* + +AIR301_names.py:60:6: AIR301 [*] `airflow.configuration.getboolean` is removed in Airflow 3.0 | 59 | # airflow.configuration 60 | get, getboolean, getfloat, getint, has_option, remove_option, as_dict, set @@ -95,7 +113,25 @@ AIR301_names.py:60:6: AIR301 `airflow.configuration.getboolean` is removed in Ai | = help: Use `airflow.configuration.conf.getboolean` instead -AIR301_names.py:60:18: AIR301 `airflow.configuration.getfloat` is removed in Airflow 3.0 +ℹ Safe fix +19 19 | has_option, +20 20 | remove_option, +21 21 | set, + 22 |+conf, +22 23 | ) +23 24 | from airflow.contrib.aws_athena_hook import AWSAthenaHook +24 25 | from airflow.datasets import DatasetAliasEvent +-------------------------------------------------------------------------------- +57 58 | +58 59 | +59 60 | # airflow.configuration +60 |-get, getboolean, getfloat, getint, has_option, remove_option, as_dict, set + 61 |+get, conf.getboolean, getfloat, getint, has_option, remove_option, as_dict, set +61 62 | +62 63 | +63 64 | # airflow.contrib.* + +AIR301_names.py:60:18: AIR301 [*] `airflow.configuration.getfloat` is removed in Airflow 3.0 | 59 | # airflow.configuration 60 | get, getboolean, getfloat, getint, has_option, remove_option, as_dict, set @@ -103,7 +139,25 @@ AIR301_names.py:60:18: AIR301 `airflow.configuration.getfloat` is removed in Air | = help: Use `airflow.configuration.conf.getfloat` instead -AIR301_names.py:60:28: AIR301 `airflow.configuration.getint` is removed in Airflow 3.0 +ℹ Safe fix +19 19 | has_option, +20 20 | remove_option, +21 21 | set, + 22 |+conf, +22 23 | ) +23 24 | from airflow.contrib.aws_athena_hook import AWSAthenaHook +24 25 | from airflow.datasets import DatasetAliasEvent +-------------------------------------------------------------------------------- +57 58 | +58 59 | +59 60 | # airflow.configuration +60 |-get, getboolean, getfloat, getint, has_option, remove_option, as_dict, set + 61 |+get, getboolean, conf.getfloat, getint, has_option, remove_option, as_dict, set +61 62 | +62 63 | +63 64 | # airflow.contrib.* + +AIR301_names.py:60:28: AIR301 [*] `airflow.configuration.getint` is removed in Airflow 3.0 | 59 | # airflow.configuration 60 | get, getboolean, getfloat, getint, has_option, remove_option, as_dict, set @@ -111,7 +165,25 @@ AIR301_names.py:60:28: AIR301 `airflow.configuration.getint` is removed in Airfl | = help: Use `airflow.configuration.conf.getint` instead -AIR301_names.py:60:36: AIR301 `airflow.configuration.has_option` is removed in Airflow 3.0 +ℹ Safe fix +19 19 | has_option, +20 20 | remove_option, +21 21 | set, + 22 |+conf, +22 23 | ) +23 24 | from airflow.contrib.aws_athena_hook import AWSAthenaHook +24 25 | from airflow.datasets import DatasetAliasEvent +-------------------------------------------------------------------------------- +57 58 | +58 59 | +59 60 | # airflow.configuration +60 |-get, getboolean, getfloat, getint, has_option, remove_option, as_dict, set + 61 |+get, getboolean, getfloat, conf.getint, has_option, remove_option, as_dict, set +61 62 | +62 63 | +63 64 | # airflow.contrib.* + +AIR301_names.py:60:36: AIR301 [*] `airflow.configuration.has_option` is removed in Airflow 3.0 | 59 | # airflow.configuration 60 | get, getboolean, getfloat, getint, has_option, remove_option, as_dict, set @@ -119,7 +191,25 @@ AIR301_names.py:60:36: AIR301 `airflow.configuration.has_option` is removed in A | = help: Use `airflow.configuration.conf.has_option` instead -AIR301_names.py:60:48: AIR301 `airflow.configuration.remove_option` is removed in Airflow 3.0 +ℹ Safe fix +19 19 | has_option, +20 20 | remove_option, +21 21 | set, + 22 |+conf, +22 23 | ) +23 24 | from airflow.contrib.aws_athena_hook import AWSAthenaHook +24 25 | from airflow.datasets import DatasetAliasEvent +-------------------------------------------------------------------------------- +57 58 | +58 59 | +59 60 | # airflow.configuration +60 |-get, getboolean, getfloat, getint, has_option, remove_option, as_dict, set + 61 |+get, getboolean, getfloat, getint, conf.has_option, remove_option, as_dict, set +61 62 | +62 63 | +63 64 | # airflow.contrib.* + +AIR301_names.py:60:48: AIR301 [*] `airflow.configuration.remove_option` is removed in Airflow 3.0 | 59 | # airflow.configuration 60 | get, getboolean, getfloat, getint, has_option, remove_option, as_dict, set @@ -127,7 +217,25 @@ AIR301_names.py:60:48: AIR301 `airflow.configuration.remove_option` is removed i | = help: Use `airflow.configuration.conf.remove_option` instead -AIR301_names.py:60:63: AIR301 `airflow.configuration.as_dict` is removed in Airflow 3.0 +ℹ Safe fix +19 19 | has_option, +20 20 | remove_option, +21 21 | set, + 22 |+conf, +22 23 | ) +23 24 | from airflow.contrib.aws_athena_hook import AWSAthenaHook +24 25 | from airflow.datasets import DatasetAliasEvent +-------------------------------------------------------------------------------- +57 58 | +58 59 | +59 60 | # airflow.configuration +60 |-get, getboolean, getfloat, getint, has_option, remove_option, as_dict, set + 61 |+get, getboolean, getfloat, getint, has_option, conf.remove_option, as_dict, set +61 62 | +62 63 | +63 64 | # airflow.contrib.* + +AIR301_names.py:60:63: AIR301 [*] `airflow.configuration.as_dict` is removed in Airflow 3.0 | 59 | # airflow.configuration 60 | get, getboolean, getfloat, getint, has_option, remove_option, as_dict, set @@ -135,7 +243,25 @@ AIR301_names.py:60:63: AIR301 `airflow.configuration.as_dict` is removed in Airf | = help: Use `airflow.configuration.conf.as_dict` instead -AIR301_names.py:60:72: AIR301 `airflow.configuration.set` is removed in Airflow 3.0 +ℹ Safe fix +19 19 | has_option, +20 20 | remove_option, +21 21 | set, + 22 |+conf, +22 23 | ) +23 24 | from airflow.contrib.aws_athena_hook import AWSAthenaHook +24 25 | from airflow.datasets import DatasetAliasEvent +-------------------------------------------------------------------------------- +57 58 | +58 59 | +59 60 | # airflow.configuration +60 |-get, getboolean, getfloat, getint, has_option, remove_option, as_dict, set + 61 |+get, getboolean, getfloat, getint, has_option, remove_option, conf.as_dict, set +61 62 | +62 63 | +63 64 | # airflow.contrib.* + +AIR301_names.py:60:72: AIR301 [*] `airflow.configuration.set` is removed in Airflow 3.0 | 59 | # airflow.configuration 60 | get, getboolean, getfloat, getint, has_option, remove_option, as_dict, set @@ -143,6 +269,24 @@ AIR301_names.py:60:72: AIR301 `airflow.configuration.set` is removed in Airflow | = help: Use `airflow.configuration.conf.set` instead +ℹ Safe fix +19 19 | has_option, +20 20 | remove_option, +21 21 | set, + 22 |+conf, +22 23 | ) +23 24 | from airflow.contrib.aws_athena_hook import AWSAthenaHook +24 25 | from airflow.datasets import DatasetAliasEvent +-------------------------------------------------------------------------------- +57 58 | +58 59 | +59 60 | # airflow.configuration +60 |-get, getboolean, getfloat, getint, has_option, remove_option, as_dict, set + 61 |+get, getboolean, getfloat, getint, has_option, remove_option, as_dict, conf.set +61 62 | +62 63 | +63 64 | # airflow.contrib.* + AIR301_names.py:64:1: AIR301 `airflow.contrib.aws_athena_hook.AWSAthenaHook` is removed in Airflow 3.0 | 63 | # airflow.contrib.* From 4ec036e422e057c2e9c8a8476d7de3e8b0453a4b Mon Sep 17 00:00:00 2001 From: Wei Lee Date: Fri, 16 May 2025 18:34:06 +0800 Subject: [PATCH 7/8] Revert "refactor(AIR301): simplify unnecessary match with if else" This reverts commit 6c27f80b8ad82366a134267e5e10fd1fa7d0617a. --- .../src/rules/airflow/rules/removal_in_3.rs | 27 +++++++++---------- 1 file changed, 13 insertions(+), 14 deletions(-) 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 46def07b78a51..22c3af56e6cf4 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 @@ -497,25 +497,21 @@ fn check_method(checker: &Checker, call_expr: &ExprCall) { "collected_datasets" => Replacement::AttrName("collected_assets"), _ => return, }, - ["airflow", "providers_manager", "ProvidersManager"] => { - if attr.as_str() == "initialize_providers_dataset_uri_resources" { + ["airflow", "providers_manager", "ProvidersManager"] => match attr.as_str() { + "initialize_providers_dataset_uri_resources" => { Replacement::AttrName("initialize_providers_asset_uri_resources") - } else { - return; } - } + _ => return, + }, [ "airflow", "secrets", "local_filesystem", "LocalFilesystemBackend", - ] => { - if attr.as_str() == "get_connections" { - Replacement::AttrName("get_connection") - } else { - return; - } - } + ] => match attr.as_str() { + "get_connections" => Replacement::AttrName("get_connection"), + _ => return, + }, ["airflow", "datasets", ..] | ["airflow", "Dataset"] => match attr.as_str() { "iter_datasets" => Replacement::AttrName("iter_assets"), "iter_dataset_aliases" => Replacement::AttrName("iter_asset_aliases"), @@ -528,14 +524,17 @@ fn check_method(checker: &Checker, call_expr: &ExprCall) { "get_connections" => Replacement::AttrName("get_connection"), _ => return, } + } else if is_airflow_hook(segments) { + match attr.as_str() { + "get_connections" => Replacement::AttrName("get_connection"), + _ => return, + } } else if is_airflow_auth_manager(segments) { if attr.as_str() == "is_authorized_dataset" { Replacement::AttrName("is_authorized_asset") } else { return; } - } else if is_airflow_hook(segments) && attr.as_str() == "get_connections" { - Replacement::AttrName("get_connection") } else { return; } From d6312924abcb523c3772b767828d1fee49932211 Mon Sep 17 00:00:00 2001 From: Wei Lee Date: Fri, 16 May 2025 21:41:50 +0800 Subject: [PATCH 8/8] refactor(AIR301): simplify import_target handling --- crates/ruff_linter/src/rules/airflow/rules/removal_in_3.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) 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 22c3af56e6cf4..03cd658b04f47 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 @@ -996,11 +996,7 @@ fn check_name(checker: &Checker, expr: &Expr, range: TextRange) { return; } - let import_target = if name.contains('.') { - name.split('.').next().unwrap() - } else { - name - }; + let import_target = name.split('.').next().unwrap_or(name); diagnostic.try_set_fix(|| { let (import_edit, _) = checker.importer().get_or_import_symbol(