This is an automated email from the ASF dual-hosted git repository.

amoghdesai pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/airflow.git


The following commit(s) were added to refs/heads/main by this push:
     new 7c4520ba62e Update usages of entry_points helpers and deprecate it 
from utils (#60061)
7c4520ba62e is described below

commit 7c4520ba62ec4bb5a35e07fb451dcda276a2a841
Author: Amogh Desai <[email protected]>
AuthorDate: Sat Jan 3 14:43:07 2026 +0530

    Update usages of entry_points helpers and deprecate it from utils (#60061)
---
 airflow-core/src/airflow/plugins_manager.py        |  3 +-
 airflow-core/src/airflow/providers_manager.py      |  3 +-
 airflow-core/src/airflow/utils/__init__.py         | 10 ++++++
 .../src/airflow/utils/deprecation_tools.py         | 17 ++++++++--
 airflow-core/src/airflow/utils/entry_points.py     | 26 ---------------
 .../tests/unit/utils/test_deprecation_tools.py     | 38 ++++++++++++++++++++++
 devel-common/src/tests_common/pytest_plugin.py     |  6 +++-
 7 files changed, 69 insertions(+), 34 deletions(-)

diff --git a/airflow-core/src/airflow/plugins_manager.py 
b/airflow-core/src/airflow/plugins_manager.py
index 8fdb3f06356..40cfe9bd25d 100644
--- a/airflow-core/src/airflow/plugins_manager.py
+++ b/airflow-core/src/airflow/plugins_manager.py
@@ -32,13 +32,12 @@ from pathlib import Path
 from typing import TYPE_CHECKING, Any
 
 from airflow import settings
-from airflow._shared.module_loading import import_string, qualname
+from airflow._shared.module_loading import entry_points_with_dist, 
import_string, qualname
 from airflow.configuration import conf
 from airflow.task.priority_strategy import (
     PriorityWeightStrategy,
     airflow_priority_weight_strategies,
 )
-from airflow.utils.entry_points import entry_points_with_dist
 from airflow.utils.file import find_path_from_directory
 
 if TYPE_CHECKING:
diff --git a/airflow-core/src/airflow/providers_manager.py 
b/airflow-core/src/airflow/providers_manager.py
index 0ddf7188021..227e063d5df 100644
--- a/airflow-core/src/airflow/providers_manager.py
+++ b/airflow-core/src/airflow/providers_manager.py
@@ -35,9 +35,8 @@ from typing import TYPE_CHECKING, Any, NamedTuple, ParamSpec, 
TypeVar
 
 from packaging.utils import canonicalize_name
 
-from airflow._shared.module_loading import import_string
+from airflow._shared.module_loading import entry_points_with_dist, 
import_string
 from airflow.exceptions import AirflowOptionalProviderFeatureException
-from airflow.utils.entry_points import entry_points_with_dist
 from airflow.utils.log.logging_mixin import LoggingMixin
 from airflow.utils.singleton import Singleton
 
diff --git a/airflow-core/src/airflow/utils/__init__.py 
b/airflow-core/src/airflow/utils/__init__.py
index 4b0ad3419a1..9aa833f9e6c 100644
--- a/airflow-core/src/airflow/utils/__init__.py
+++ b/airflow-core/src/airflow/utils/__init__.py
@@ -56,3 +56,13 @@ __deprecated_classes = {
 }
 
 add_deprecated_classes(__deprecated_classes, __name__)
+
+add_deprecated_classes(
+    {
+        "entry_points": {
+            "*": "airflow._shared.module_loading",
+        },
+    },
+    __name__,
+    message="The `{module}.{name}` is deprecated and will be removed in a 
future version.",
+)
diff --git a/airflow-core/src/airflow/utils/deprecation_tools.py 
b/airflow-core/src/airflow/utils/deprecation_tools.py
index dcb2d6f72d6..2253b6b86c9 100644
--- a/airflow-core/src/airflow/utils/deprecation_tools.py
+++ b/airflow-core/src/airflow/utils/deprecation_tools.py
@@ -40,6 +40,7 @@ def getattr_with_deprecation(
     override_deprecated_classes: dict[str, str],
     extra_message: str,
     name: str,
+    message_override: str = "",
 ):
     """
     Retrieve the imported attribute from the redirected module and raises a 
deprecation warning.
@@ -49,6 +50,8 @@ def getattr_with_deprecation(
     :param override_deprecated_classes: override target attributes with 
deprecated ones. If target attribute is
        found in the dictionary, it will be displayed in the warning message.
     :param extra_message: extra message to display in the warning or import 
error message
+    :param message_override: if provided, overrides the default deprecation 
message. Supports placeholders:
+       {module}, {name}, {target} which are substituted with the actual values.
     :param name: attribute name
     :return:
     """
@@ -67,9 +70,12 @@ def getattr_with_deprecation(
     if override_deprecated_classes and name in override_deprecated_classes:
         warning_class_name = override_deprecated_classes[name]
 
-    message = f"The `{module}.{name}` attribute is deprecated. Please use 
`{warning_class_name!r}`."
-    if extra_message:
-        message += f" {extra_message}."
+    if message_override:
+        message = message_override.format(module=module, name=name, 
target=warning_class_name)
+    else:
+        message = f"The `{module}.{name}` attribute is deprecated. Please use 
`{warning_class_name!r}`."
+        if extra_message:
+            message += f" {extra_message}."
     warnings.warn(message, DeprecatedImportWarning, stacklevel=2)
 
     # Import and return the target attribute
@@ -90,6 +96,7 @@ def add_deprecated_classes(
     package: str,
     override_deprecated_classes: dict[str, dict[str, str]] | None = None,
     extra_message: str | None = None,
+    message: str | None = None,
 ):
     """
     Add deprecated attribute PEP-563 imports and warnings modules to the 
package.
@@ -105,6 +112,8 @@ def add_deprecated_classes(
     :param override_deprecated_classes: override target attributes with 
deprecated ones.
         Format: dict[str, dict[str, str]] matching the structure of 
module_imports
     :param extra_message: extra message to display in the warning or import 
error message
+    :param message: if provided, overrides the default deprecation message. 
Supports placeholders:
+        {module}, {name}, {target} which are substituted with the actual 
values.
 
     Examples:
         # Create virtual modules (e.g., for removed .py files)
@@ -175,6 +184,7 @@ def add_deprecated_classes(
                 package,
                 current_override,
                 extra_message or "",
+                message_override=message or "",
             )
 
             # Set the __getattr__ function on the current module
@@ -195,5 +205,6 @@ def add_deprecated_classes(
                 full_module_name,
                 override_deprecated_classes_for_module,
                 extra_message or "",
+                message_override=message or "",
             )
             sys.modules.setdefault(full_module_name, module_type)
diff --git a/airflow-core/src/airflow/utils/entry_points.py 
b/airflow-core/src/airflow/utils/entry_points.py
deleted file mode 100644
index dac8e3b868b..00000000000
--- a/airflow-core/src/airflow/utils/entry_points.py
+++ /dev/null
@@ -1,26 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-"""Re-export entry_points utilities from shared library for backward 
compatibility."""
-
-from __future__ import annotations
-
-from airflow._shared.module_loading import (
-    EPnD as EPnD,
-    _get_grouped_entry_points as _get_grouped_entry_points,
-    entry_points_with_dist as entry_points_with_dist,
-    metadata as metadata,
-)
diff --git a/airflow-core/tests/unit/utils/test_deprecation_tools.py 
b/airflow-core/tests/unit/utils/test_deprecation_tools.py
index 2a6ca7ce74c..c48a59ecac7 100644
--- a/airflow-core/tests/unit/utils/test_deprecation_tools.py
+++ b/airflow-core/tests/unit/utils/test_deprecation_tools.py
@@ -472,6 +472,44 @@ class TestAddDeprecatedClasses:
                 package=nonexistent_module,
             )
 
+    def test_add_deprecated_classes_with_custom_message(self):
+        """Test add_deprecated_classes with custom message parameter."""
+        module_name = get_unique_module_name("custom_msg_module")
+        full_module_name = f"airflow.test.{module_name}"
+
+        # Create a module to modify
+        test_module = ModuleType(full_module_name)
+        sys.modules[full_module_name] = test_module
+
+        with temporary_module(full_module_name):
+            # Mock the target module and attribute
+            mock_target_module = mock.MagicMock()
+            mock_attribute = mock.MagicMock()
+            mock_target_module.deprecated_attr = mock_attribute
+
+            with mock.patch(
+                "airflow.utils.deprecation_tools.importlib.import_module", 
return_value=mock_target_module
+            ):
+                custom_message = "We are just going to remove {module}.{name}. 
Prepare yourselves!"
+                add_deprecated_classes(
+                    {full_module_name: {"deprecated_attr": 
"target.module.deprecated_attr"}},
+                    package=full_module_name,
+                    message=custom_message,
+                )
+
+                with warnings.catch_warnings(record=True) as w:
+                    warnings.simplefilter("always")
+                    result = getattr(test_module, "deprecated_attr")
+
+                    assert result == mock_attribute
+                    assert len(w) == 1
+                    assert issubclass(w[0].category, DeprecatedImportWarning)
+                    expected = (
+                        f"We are just going to remove 
{full_module_name}.deprecated_attr. Prepare yourselves!"
+                    )
+                    assert str(w[0].message) == expected
+                    assert "Please use" not in str(w[0].message)
+
     def test_add_deprecated_classes_preserves_existing_module_attributes(self):
         """Test that add_deprecated_classes preserves existing module 
attributes."""
         module_name = get_unique_module_name("preserve_module")
diff --git a/devel-common/src/tests_common/pytest_plugin.py 
b/devel-common/src/tests_common/pytest_plugin.py
index 5d60aa66d1c..3dc2cec736e 100644
--- a/devel-common/src/tests_common/pytest_plugin.py
+++ b/devel-common/src/tests_common/pytest_plugin.py
@@ -1785,7 +1785,11 @@ def clear_lru_cache():
         yield
         return
 
-    from airflow.utils.entry_points import _get_grouped_entry_points
+    try:
+        from airflow._shared.module_loading import _get_grouped_entry_points
+    except ImportError:
+        # compat for airflow < 3.2
+        from airflow.utils.entry_points import _get_grouped_entry_points
 
     _get_grouped_entry_points.cache_clear()
     try:

Reply via email to