Solve circular import error in Astro runtime while debugging#2538
Conversation
cosmos.log imported cosmos.settings.rich_logging at module level, which caused a circular import when Airflow plugin discovery triggered the chain: cosmos.plugin → cosmos.telemetry → cosmos.log → cosmos.settings (still initializing). Move the import inside get_logger() so it only runs when the function is first called, by which time cosmos.settings is fully initialized. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
This PR aims to prevent an Airflow plugin-discovery circular import by deferring cosmos.settings.rich_logging access until get_logger() is called, instead of importing it at cosmos.log module import time.
Changes:
- Removed the module-level
from cosmos.settings import rich_loggingimport incosmos.log. - Added a deferred import of
rich_logginginsideget_logger().
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
rich_logging is now imported inside get_logger() from cosmos.settings, so tests must monkeypatch it there instead of on the cosmos.log module. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
from cosmos.settings import rich_logging can still fail if get_logger() is called while cosmos.settings is partially initialized (e.g. cosmos.telemetry calls get_logger() at module level during plugin discovery). Use getattr with a False default instead, which safely returns the default when the attribute hasn't been defined yet. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
💡 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 #2538 +/- ##
=======================================
Coverage 98.08% 98.08%
=======================================
Files 103 103
Lines 7482 7483 +1
=======================================
+ Hits 7339 7340 +1
Misses 143 143 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
Break circular import between cosmos.config and cosmos.profiles by moving the BaseProfileMapping import behind TYPE_CHECKING. The isinstance check in get_profile_type() uses a local import instead. This prevents ImportError when Airflow plugin discovery triggers cosmos.config while cosmos.profiles is still initializing. Guard CosmosRichLogger.handle() against None msg to prevent TypeError when logging None values. Add autouse fixture in conftest.py that demotes CosmosRichLogger instances back to logging.Logger after each test, preventing test_log.py from polluting the logging cache for other tests. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
rich_logging import in cosmos.log to break circular importThe try/except ImportError was masking legitimate import errors. Python returns the partially-initialized module during circular imports, so the bare import always succeeds. getattr with a False default is sufficient to handle the case where rich_logging hasn't been defined yet. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 3 out of 3 changed files in this pull request and generated no new comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Does this happen with Cosmos 1.14.0 on runtime images in Astro? If so, do we need a patch release out? I did not observe this when testing Cosmos 1.14.0a8 with runtime images for both 3.1 and 3.2 alpha.
Either way, I think is a good fix, thanks. I am going ahead with the merge, but would be nice to confirm if we need a patch release out with this fix.
|
@pankajkoti This does not happen when we do It happens both with Cosmos 1.14.0 and 1.13.1 (I asked @pankajastro to try to reproduce yesterday, and he confirmed he observed it as well) (https://astronomer.slack.com/archives/C06RRC8MFT5/p1775591027977759?thread_ts=1775589526.276809&cid=C06RRC8MFT5)) Since it's been there since at least February 2026 and no one has reported it, we're fine waiting for other bug fixes and shipping it as part of 1.14.1. |
Bug Fixes * Fix ``ExecutionMode.WATCHER`` producer retry behaviour by @tatiana in #2559 * Prevent watcher producer skip propagating to downstream tasks via gateway task by @johnhoran and @tatiana in #2597 * Keep watcher sensor polling when producer is still running by @pankajkoti in #2592 * Fix circular import error in Cosmos plugin discovery under Astro Runtime by @tatiana in #2538 * Fix ``CosmosRichLogger`` crash on ``None`` log message by @tatiana in #2540 * Enable inlets and outlets using dbt Fusion on Airflow 3 by @ichirotakami in #2561 * Fix incorrectly skipped source downstream tasks in ``ExecutionMode.WATCHER`` by @pankajastro in #2563 * Fix duplicate logs in ``dbt build`` when source freshness is enabled by @pankajastro in #2564 * Warn and normalize when ``source_rendering_behavior=None`` is passed by @pankajastro in #2570 * Gracefully handle ``Variable.set()`` failures on Astro Remote Execution by @hkc-8010 in #2573 * Skip malformed YAML selectors instead of failing entirely by @YourRoyalLinus in #2577 Docs * Update watcher test behavior docs for Cosmos 1.14.0 by @tatiana in #2549 * Add redirect for moved partial-parsing docs page by @tatiana in #2550 * Document ``ExecutionMode.WATCHER`` and ``depends_on_past`` limitation by @tatiana in #2602 * Restore memory-optimised imports docs for Cosmos < 1.14.0 by @pankajkoti in #2604 Others * Speed up Airflow 3.1+ integration tests by caching InProcessExecutionAPI by @pankajkoti in #2547 * Improve stability of cache hash unit tests by @tatiana in #2539 * Fix mypy 1.20.0 type check failures by @pankajkoti in #2546 * Fix CI failures caused by docs build memory exhaustion by @pankajkoti in #2580 * Fix dbt Fusion broken integration tests by @tatiana in #2581 * Fix flaky ``cosmos_manifest_selectors_example`` DAG in CI by @pankajkoti in #2593 * Reduce pre-commit autoupdate frequency PRs by @tatiana in #2544 * Bump ``reviewdog/action-actionlint`` from 1.71.0 to 1.72.0 by @dependabot in #2542 * Skip watcher gateway test on Airflow 3.0 by @tatiana in #2607 closes: astronomer/oss-integrations-private#381
Problem
When Cosmos is loaded inside Astro Runtime, plugin discovery triggers a circular import that prevents Cosmos from loading:
How to reproduce
Root cause
The import chain during plugin discovery is:
cosmos.telemetryimports bothcosmos.settings(line 15) andcosmos.log(line 16) at the module level. Whencosmos.settingsis imported, it starts executing but hasn't reached therich_logging = ...assignment (line 35) beforecosmos.logtries toimport rich_loggingfrom it.In standard Airflow, this doesn't manifest because the import order completes
cosmos.settingsbeforecosmos.logis needed. In Astro Runtime, there is probably some specific code that triggers plugin discovery in a different order than the one that exposes the cycle.Fix
Move the from
cosmos.settings import rich_loggingimport from module level incosmos/log.pyinto theget_logger()function body.Validation
I validated by cloning and changing:
https://github.com/astronomer/cosmos-demo
So that the
requirements.txtinstalls Cosmos using: