Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 11 additions & 3 deletions astro-airflow-mcp/src/astro_airflow_mcp/_astro_session.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,18 @@ def _config_path() -> Path:


def _read_yaml(path: Path) -> dict[str, Any]:
"""Read and parse the astro config. Empty/missing → {}."""
"""Read and parse the astro config. Empty/missing/non-file → {}.

Treats "missing or not-a-regular-file" as "no astro session" (path is
absent, ASTRO_HOME points at /dev/null or a directory, etc). Other
OSError shapes (PermissionError, IOError) are deliberately not
swallowed — those mean a real config exists but isn't readable, and
surfacing the error is more useful than a misleading "run astro
login" downstream.
"""
try:
text = path.read_text()
except FileNotFoundError:
except (FileNotFoundError, NotADirectoryError, IsADirectoryError):
return {}
try:
return yaml.safe_load(text) or {}
Expand All @@ -62,7 +70,7 @@ def _read_yaml(path: Path) -> dict[str, Any]:
time.sleep(0.05)
try:
return yaml.safe_load(path.read_text()) or {}
except (FileNotFoundError, yaml.YAMLError):
except (FileNotFoundError, NotADirectoryError, IsADirectoryError, yaml.YAMLError):
return {}


Expand Down
15 changes: 14 additions & 1 deletion astro-airflow-mcp/tests/test_astro_pat.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@

from __future__ import annotations

import os
import threading
import time
from datetime import datetime, timedelta, timezone
from pathlib import Path # noqa: TC003 — used as runtime fixture parameter type
from pathlib import Path

import httpx
import pytest
Expand Down Expand Up @@ -577,3 +578,15 @@ def test_unparseable_yaml_treated_as_no_session(self, astro_home: Path):
resolver = AstroPATResolver(env={})
with pytest.raises(AstroNotLoggedInError):
resolver.get_token()

def test_astro_home_at_dev_null_treated_as_no_session(self, monkeypatch):
# ASTRO_HOME=os.devnull is a sentinel some wrappers use to "neutralize"
# global astro state. Reading <devnull>/config.yaml raises
# NotADirectoryError (not FileNotFoundError); the resolver should
# still surface "no session" cleanly.
if not Path(os.devnull).exists() or Path(os.devnull).is_file():
pytest.skip("os.devnull is not a non-regular file on this platform")
monkeypatch.setenv("ASTRO_HOME", os.devnull)
resolver = AstroPATResolver(env={})
with pytest.raises(AstroNotLoggedInError):
resolver.get_token()