You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@airflow.apache.org by as...@apache.org on 2022/09/09 08:41:30 UTC

[airflow] branch main updated: Fix 'from airflow import version' lazy import (#26239)

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

ash 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 b7a603cf89 Fix 'from airflow import version' lazy import (#26239)
b7a603cf89 is described below

commit b7a603cf89728e02a187409c83983d58cc554457
Author: Tzu-ping Chung <ur...@gmail.com>
AuthorDate: Fri Sep 9 16:41:03 2022 +0800

    Fix 'from airflow import version' lazy import (#26239)
    
    This incorrectly accesses the version *attribute* in airflow.version
    instead of the airflow.version module itself, breaking compatibility.
    
    I actually rewrote the entire __getattr__ hook; much of the logic seems
    to be quite redundant.
---
 airflow/__init__.py | 44 ++++++++++++++++++++------------------------
 1 file changed, 20 insertions(+), 24 deletions(-)

diff --git a/airflow/__init__.py b/airflow/__init__.py
index 24fea0d574..c7e020dc5a 100644
--- a/airflow/__init__.py
+++ b/airflow/__init__.py
@@ -26,12 +26,13 @@ in their PYTHONPATH. airflow_login should be based off the
 isort:skip_file
 """
 
-
 # flake8: noqa: F401
 
+from __future__ import annotations
+
 import os
 import sys
-from typing import Callable, Optional
+from typing import Callable
 
 from airflow import settings
 
@@ -47,7 +48,7 @@ __path__ = __import__("pkgutil").extend_path(__path__, __name__)  # type: ignore
 if not os.environ.get("_AIRFLOW__AS_LIBRARY", None):
     settings.initialize()
 
-login: Optional[Callable] = None
+login: Callable | None = None
 
 PY36 = sys.version_info >= (3, 6)
 PY37 = sys.version_info >= (3, 7)
@@ -55,36 +56,31 @@ PY38 = sys.version_info >= (3, 8)
 PY39 = sys.version_info >= (3, 9)
 PY310 = sys.version_info >= (3, 10)
 
-# Things to lazy import in form 'name': 'path.to.module'
-__lazy_imports = {
-    'DAG': 'airflow.models.dag',
-    'Dataset': 'airflow.datasets',
-    'XComArg': 'airflow.models.xcom_arg',
-    'AirflowException': 'airflow.exceptions',
-    'version': 'airflow.version',
+# Things to lazy import in form {local_name: ('target_module', 'target_name')}
+__lazy_imports: dict[str, tuple[str, str]] = {
+    'DAG': ('.models.dag', 'DAG'),
+    'Dataset': ('.datasets', 'Dataset'),
+    'XComArg': ('.models.xcom_arg', 'XComArg'),
+    'AirflowException': ('.exceptions', 'AirflowException'),
+    'version': ('.version', ''),
+    '__version__': ('.version', 'version'),
 }
 
 
 def __getattr__(name: str):
     # PEP-562: Lazy loaded attributes on python modules
-    module_attr = name.rsplit('.', 1)[-1]
-    path: Optional[str]
-    if name == '__version__':
-        module_attr = 'version'
-        path = 'airflow.version'
-    else:
-        path = __lazy_imports.get(name)
-    if not path:
+    module_path, attr_name = __lazy_imports.get(name, ('', ''))
+    if not module_path:
         raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
 
-    import operator
+    import importlib
 
-    # Strip off the "airflow." prefix because of how `__import__` works (it always returns the top level
-    # module)
-    without_prefix = path.split('.', 1)[-1]
+    mod = importlib.import_module(module_path, __name__)
+    if attr_name:
+        val = getattr(mod, attr_name)
+    else:
+        val = mod
 
-    getter = operator.attrgetter(f'{without_prefix}.{module_attr}')
-    val = getter(__import__(path))
     # Store for next time
     globals()[name] = val
     return val