Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
7cebda5
Add missing PyYAML dependency to Task SDK
uranusjr Oct 12, 2026
07503aa
Implement file-loading syntax $f
uranusjr Oct 12, 2026
45c27ba
Implement the YAML importer shell
uranusjr Oct 12, 2026
83663b0
Fix double-rendering
uranusjr Oct 12, 2026
ebc8b35
Protect file read using the same logic as Python templates
uranusjr Oct 12, 2026
14a1264
Re-export to reduce import levels
uranusjr Oct 12, 2026
91224e1
Correctly import in an import context
uranusjr Oct 12, 2026
19978d9
Refactor DagBuilder interface
uranusjr Oct 12, 2026
8be499e
Stricter top-level and with argument checks
uranusjr Oct 12, 2026
7857b24
Tighten template field patching
uranusjr Oct 12, 2026
b4742e7
Tighten literal handling in run
uranusjr Oct 12, 2026
b39b0e7
Consolidate import-time errors from a YAML file
uranusjr Oct 12, 2026
d83a145
Reject duplicate dag_id in a file
uranusjr Oct 12, 2026
1d39630
Tighten might_contain_dag heuristic
uranusjr Oct 12, 2026
8ae0b9c
Coerce YAML input temporal values to datetimes
uranusjr Oct 12, 2026
73f6c96
Rely on stdlib graphlib for ordering
uranusjr Oct 12, 2026
2cdc517
Move unnecessary local import up
uranusjr Oct 12, 2026
1868773
Tighten tests
uranusjr Oct 12, 2026
0d4479d
Try to smartly convert datetimes
uranusjr Oct 12, 2026
88318c6
Tighten
uranusjr Oct 12, 2026
86e348f
Fix leaked rst in schema
uranusjr Oct 12, 2026
0d19512
Clean up in-test comments
uranusjr Oct 12, 2026
d9cd661
Improve temporal coersion
uranusjr Oct 12, 2026
7915e61
Harden
uranusjr Oct 12, 2026
79c433f
Fix zip import function arg name
uranusjr Oct 12, 2026
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
5 changes: 3 additions & 2 deletions task-sdk/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -54,14 +54,15 @@ dependencies = [
"babel>=2.17.0",
"cadwyn>=6.1.1",
"fsspec>=2023.10.0",
"greenback>=1.2.1",
"httpx>=0.27.0",
"jinja2>=3.1.5",
"methodtools>=0.4.7",
"msgspec>=0.19.0",
"python-dateutil>=2.7.0",
"psutil>=6.1.0",
"python-dateutil>=2.7.0",
"pyyaml>=6.0.3",
"structlog>=26.1.0",
"greenback>=1.2.1",
"tenacity>=8.3.0",
# Start of shared timezones dependencies
"pendulum>=3.1.0",
Expand Down
31 changes: 30 additions & 1 deletion task-sdk/src/airflow/sdk/importers/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,17 @@

if TYPE_CHECKING:
from collections.abc import Generator, Iterable, Iterator
from typing import Self
from typing import Protocol, Self

from airflow.dag_processing.bundles.base import BaseDagBundle # noqa: SDK002
from airflow.sdk import DAG

class _SiblingResource(Protocol):
"""A readable handle to a resource, e.g. a :class:`pathlib.Path` or :class:`zipfile.Path`."""

def read_bytes(self) -> bytes: ...


log = logging.getLogger(__name__)

DefT = TypeVar("DefT", bound="DagDefinition")
Expand All @@ -64,6 +70,22 @@ def read_text(self, encoding: str = "utf-8") -> str:
"""Read and return the content of the resource as a string."""
return self.read_bytes().decode(encoding)

def sibling(self, relpath: str) -> _SiblingResource:
"""
Return a readable handle to a resource stored next to this definition.

The handle supports ``read_bytes()``. Resolution is relative to the
directory of the definition's own file and raises if *relpath* escapes
it. This is close to a Python Dag's default Jinja search path, but
stricter: a path escaping via a symlink is rejected (Jinja's
``FileSystemLoader`` would allow it), and a Dag-level
``template_searchpath`` is not consulted.

The base cannot locate siblings; a definition type that can hold them
overrides this.
"""
raise NotImplementedError(f"{type(self).__name__} cannot resolve sibling resources")

@abstractmethod
def as_file(self) -> contextlib.AbstractContextManager[Path]:
"""
Expand Down Expand Up @@ -133,6 +155,13 @@ def suffix(self) -> str:
def read_bytes(self) -> bytes:
return self.path.read_bytes()

def sibling(self, relpath: str) -> Path:
base = self.path.parent.resolve()
target = base.joinpath(relpath).resolve()
if not target.is_relative_to(base):
raise ValueError(f"{relpath!r} resolves outside the Dag file's directory {str(base)!r}")
return target

@contextlib.contextmanager
def as_file(self) -> Generator[Path, None, None]:
yield self.path
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,15 @@

from __future__ import annotations

from airflow.sdk.importers.yaml_importer.builder import YamlDagBuildError
from airflow.sdk.importers.yaml_importer.importer import YamlDagImporter
from airflow.sdk.importers.yaml_importer.models import DagDocument
from airflow.sdk.importers.yaml_importer.parser import (
YamlDagParseError,
parse_documents,
)
from airflow.sdk.importers.yaml_importer.parser import YamlDagParseError, parse_documents

__all__ = [
"DagDocument",
"YamlDagBuildError",
"YamlDagImporter",
"YamlDagParseError",
"parse_documents",
]
Loading