Centralize version-aware EmptyOperator imports in a compatibility module#2758
Conversation
da79f39 to
3cbe83f
Compare
There was a problem hiding this comment.
Pull request overview
Centralizes the EmptyOperator import path into a version-aware EMPTY_OPERATOR_CLASS constant to avoid emitting DeprecatedImportWarning on Airflow 3.
Changes:
- Add
EMPTY_OPERATOR_CLASSconstant incosmos/constants.pyselecting the appropriate path based on Airflow version. - Replace the hardcoded legacy path in
cosmos/airflow/graph.pywith the new constant. - Update the corresponding test parametrization to use the constant.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
| cosmos/constants.py | Introduces version-aware EMPTY_OPERATOR_CLASS constant. |
| cosmos/airflow/graph.py | Uses the new constant in source-without-freshness rendering. |
| tests/airflow/test_graph.py | Updates expected operator class in parametrized test. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #2758 +/- ##
=======================================
Coverage 98.28% 98.28%
=======================================
Files 106 107 +1
Lines 7911 7915 +4
=======================================
+ Hits 7775 7779 +4
Misses 136 136 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
tatiana
left a comment
There was a problem hiding this comment.
Hi @pankajkoti ,
I feel uneasy about using a variable to represent a well-known class. This tends to make the human maintenance of the code harder.
That said, I agree we should unify how we handle different import paths (AF2/AF3) across our codebase.
Please, could you take a look at this library - it may be a better solution:
https://airflow.apache.org/docs/apache-airflow-providers-common-compat/stable/index.html
If it is not, I suggest we create a module in cosmos/airflow/compatibility.py to handle the import, keeping the class name as EmptyOperator.
|
Thanks @tatiana for the quick review.
I checked I went with your second suggestion: a new Changes in the latest push:
The module is intentionally generic so we can fold other Airflow 2/3 import splits into it over time. |
Add a centralized EMPTY_OPERATOR_CLASS constant that resolves to airflow.providers.standard.operators.empty.EmptyOperator on Airflow 3 and the legacy airflow.operators.empty.EmptyOperator on Airflow 2. Use it for the source-without-freshness rendering path, which previously hardcoded the legacy path and emitted a DeprecatedImportWarning on Airflow 3.
Add cosmos/airflow/compatibility.py, a lazily resolved (PEP 562) module that exports EmptyOperator under its real name on both Airflow 2 and 3, picking the standard-provider path on Airflow 3 to avoid the DeprecatedImportWarning emitted by the legacy airflow.operators.empty path. Use sites now reference the operator by name instead of a hardcoded path: - watcher_kubernetes.py and _watcher/base.py import EmptyOperator from the module rather than duplicating try/except import blocks. - graph.py derives the dotted operator_class string via get_version_aware_operator_class_path(EmptyOperator). Remove the EMPTY_OPERATOR_CLASS constant from constants.py.
305b89b to
fe4c60d
Compare
Select the EmptyOperator module via AIRFLOW_VERSION.major < _AIRFLOW3_MAJOR_VERSION
instead of comparing against Version("3.0"), so Airflow 3 pre-releases (e.g.
3.0.0rc1) are treated as Airflow 3 and the standard-provider path is chosen.
Cache each resolved symbol in the module namespace from __getattr__ so repeated
attribute access skips the lazy lookup.
tatiana
left a comment
There was a problem hiding this comment.
@pankajkoti approving, so you can move forward with the other tasks. Even though we have different views on the approach, this should not block your progress.
Replace the lazy PEP 562 compatibility module with a plain version-gated import of EmptyOperator and an EMPTY_OPERATOR_CLASS_PATH constant derived from the imported class. The class is a real importable name, so type checkers resolve it natively and graph.py serializes the operator path via the constant instead of a runtime helper.
Thanks @tatiana. I've pushed the simpler version: a direct version-gated import in cosmos/airflow/compatibility.py plus an I checked the full apache-airflow-providers-common-compat provider. The only operators it ships are For a single lightweight symbol like
So the intent was to make this the one place that owns version-aware imports, and during the housekeeping activities fold the scattered The eager, class-derived |
Introduces
cosmos/airflow/compatibility.py, a small module that centralizes the version-specific import of Airflow objects whose path changed between Airflow 2 and 3. The first such object isEmptyOperator, which moved to the standard provider in Airflow 3. The legacyairflow.operators.emptypath still resolves on Airflow 3 but emits aDeprecatedImportWarning, so the module imports from the standard provider on Airflow 3 and from the legacy path on Airflow 2. The selection compares on the Airflow major version, so pre-releases such as3.0.0rc1are treated as Airflow 3.The module also exposes
EMPTY_OPERATOR_CLASS_PATH, the dotted import path for the version-appropriateEmptyOperator, derived from the imported class so it always stays in sync.Call sites reference the operator by name or via the constant instead of duplicating the version check:
watcher_kubernetes.pyand_watcher/base.pyimportEmptyOperatorfrom the module, replacing their duplicated try/except import blocks.graph.pyserializes theoperator_classstring viaEMPTY_OPERATOR_CLASS_PATH.🤖 Generated with Claude Code
Co-authored-by: Tatiana Al-Chueyr tatiana.alchueyr@gmail.com