Skip to content

Commit dd9df35

Browse files
authored
Add back missing [sources] link in generated documentation's includes (apache#49978)
* Add back missing `[sources]` link in generated documentation's includes The exampleinclude of ours used to have generated "sources" link to link to sources of the examples. This was however gone after recent doc build refactoring. The problem was the example directive include import and lack of tests folder available on the python path. Both issues fixed in this PR. Fixes: apache#49893 * fixup! Add back missing `[sources]` link in generated documentation's includes
1 parent 5f4bcc1 commit dd9df35

File tree

55 files changed

+341
-97
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

55 files changed

+341
-97
lines changed

Dockerfile.ci

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -949,6 +949,12 @@ function environment_initialization() {
949949

950950
cd "${AIRFLOW_SOURCES}"
951951

952+
# Temporarily add /opt/airflow/providers/standard/tests to PYTHONPATH in order to see example dags
953+
# in the UI when testing in Breeze. This might be solved differently in the future
954+
if [[ -d /opt/airflow/providers/standard/tests ]]; then
955+
export PYTHONPATH=${PYTHONPATH=}:/opt/airflow/providers/standard/tests
956+
fi
957+
952958
if [[ ${START_AIRFLOW:="false"} == "true" || ${START_AIRFLOW} == "True" ]]; then
953959
export AIRFLOW__CORE__LOAD_EXAMPLES=${LOAD_EXAMPLES}
954960
wait_for_asset_compilation

airflow-core/docs/tutorial/taskflow.rst

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -269,7 +269,7 @@ system-level packages. TaskFlow supports multiple execution environments to isol
269269
Creates a temporary virtualenv at task runtime. Great for experimental or dynamic tasks, but may have cold start
270270
overhead.
271271

272-
.. exampleinclude:: /../src/airflow/example_dags/example_python_decorator.py
272+
.. exampleinclude:: /../../providers/standard/tests/system/standard/example_python_decorator.py
273273
:language: python
274274
:dedent: 4
275275
:start-after: [START howto_operator_python_venv]
@@ -283,7 +283,7 @@ overhead.
283283

284284
Executes the task using a pre-installed Python interpreter — ideal for consistent environments or shared virtualenvs.
285285

286-
.. exampleinclude:: /../src/airflow/example_dags/example_python_decorator.py
286+
.. exampleinclude:: /../../providers/standard/tests/system/standard/example_python_decorator.py
287287
:language: python
288288
:dedent: 4
289289
:start-after: [START howto_operator_external_python]
@@ -333,7 +333,7 @@ Using Sensors
333333
Use ``@task.sensor`` to build lightweight, reusable sensors using Python functions. These support both poke and reschedule
334334
modes.
335335

336-
.. exampleinclude:: /../src/airflow/example_dags/example_sensor_decorator.py
336+
.. exampleinclude:: /../../providers/standard/tests/system/standard/example_sensor_decorator.py
337337
:language: python
338338
:start-after: [START tutorial]
339339
:end-before: [END tutorial]

airflow-core/src/airflow/dag_processing/bundles/manager.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
# under the License.
1717
from __future__ import annotations
1818

19+
import os
1920
from typing import TYPE_CHECKING
2021

2122
from airflow.configuration import conf
@@ -34,6 +35,7 @@
3435
from airflow.dag_processing.bundles.base import BaseDagBundle
3536

3637
_example_dag_bundle_name = "example_dags"
38+
_example_standard_dag_bundle_name = "example_standard_dags"
3739

3840

3941
def _bundle_item_exc(msg):
@@ -80,6 +82,25 @@ def _add_example_dag_bundle(config_list):
8082
)
8183

8284

85+
def _add_example_standard_dag_bundle(config_list):
86+
# TODO(potiuk): make it more generic - for now we only add standard example_dags if they are locally available
87+
try:
88+
from system import standard
89+
except ImportError:
90+
return
91+
92+
example_dag_folder = next(iter(standard.__path__))
93+
config_list.append(
94+
{
95+
"name": _example_standard_dag_bundle_name,
96+
"classpath": "airflow.dag_processing.bundles.local.LocalDagBundle",
97+
"kwargs": {
98+
"path": example_dag_folder,
99+
},
100+
}
101+
)
102+
103+
83104
class DagBundlesManager(LoggingMixin):
84105
"""Manager for DAG bundles."""
85106

@@ -112,6 +133,11 @@ def parse_config(self) -> None:
112133
_validate_bundle_config(config_list)
113134
if conf.getboolean("core", "LOAD_EXAMPLES"):
114135
_add_example_dag_bundle(config_list)
136+
if (
137+
os.environ.get("BREEZE", "").lower() == "true"
138+
or os.environ.get("_IN_UNIT_TESTS", "").lower() == "true"
139+
):
140+
_add_example_standard_dag_bundle(config_list)
115141

116142
for cfg in config_list:
117143
name = cfg["name"]

airflow-core/src/airflow/models/dagbag.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -586,6 +586,14 @@ def collect_dags(
586586
example_dag_folder = next(iter(example_dags.__path__))
587587

588588
files_to_parse.extend(list_py_file_paths(example_dag_folder, safe_mode=safe_mode))
589+
try:
590+
from system import standard
591+
592+
example_dag_folder_standard = next(iter(standard.__path__))
593+
files_to_parse.extend(list_py_file_paths(example_dag_folder_standard, safe_mode=safe_mode))
594+
except ImportError:
595+
# Nothing happens - this should only work during tests
596+
pass
589597

590598
for filepath in files_to_parse:
591599
try:

airflow-core/src/airflow/utils/cli.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -270,7 +270,6 @@ def get_dag(bundle_names: list | None, dag_id: str, from_db: bool = False) -> DA
270270

271271
bundle_names = bundle_names or []
272272
dag: DAG | None = None
273-
274273
if from_db:
275274
dagbag = DagBag(read_dags_from_db=True)
276275
dag = dagbag.get_dag(dag_id) # get_dag loads from the DB as requested

airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_dag_parsing.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,8 @@
2828

2929
pytestmark = pytest.mark.db_test
3030

31-
EXAMPLE_DAG_FILE = AIRFLOW_CORE_SOURCES_PATH / "airflow" / "example_dags" / "example_bash_operator.py"
32-
TEST_DAG_ID = "example_bash_operator"
31+
EXAMPLE_DAG_FILE = AIRFLOW_CORE_SOURCES_PATH / "airflow" / "example_dags" / "example_simplest_dag.py"
32+
TEST_DAG_ID = "example_simplest_dag"
3333
NOT_READABLE_DAG_ID = "latest_only_with_trigger"
3434
TEST_MULTIPLE_DAGS_ID = "asset_produces_1"
3535

airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_dag_report.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323

2424
from airflow.utils.file import list_py_file_paths
2525

26+
import system.standard
2627
from tests_common.test_utils.config import conf_vars
2728
from tests_common.test_utils.db import clear_db_dags, parse_and_sync_to_db
2829

@@ -37,8 +38,10 @@
3738
def get_corresponding_dag_file_count(dir: str, include_examples: bool = True) -> int:
3839
from airflow import example_dags
3940

40-
return len(list_py_file_paths(directory=dir)) + (
41-
len(list_py_file_paths(next(iter(example_dags.__path__)))) if include_examples else 0
41+
return (
42+
len(list_py_file_paths(directory=dir))
43+
+ (len(list_py_file_paths(next(iter(example_dags.__path__)))) if include_examples else 0)
44+
+ (len(list_py_file_paths(next(iter(system.standard.__path__)))) if include_examples else 0)
4245
)
4346

4447

airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_dag_sources.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,9 @@
3939

4040
# Example bash operator located here: airflow/example_dags/example_bash_operator.py
4141
EXAMPLE_DAG_FILE = (
42-
AIRFLOW_REPO_ROOT_PATH / "airflow-core" / "src" / "airflow" / "example_dags" / "example_bash_operator.py"
42+
AIRFLOW_REPO_ROOT_PATH / "airflow-core" / "src" / "airflow" / "example_dags" / "example_simplest_dag.py"
4343
)
44-
TEST_DAG_ID = "example_bash_operator"
44+
TEST_DAG_ID = "example_simplest_dag"
4545

4646

4747
@pytest.fixture

airflow-core/tests/unit/cli/commands/test_task_command.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
from airflow.utils.state import State, TaskInstanceState
4848
from airflow.utils.types import DagRunTriggeredByType, DagRunType
4949

50+
from tests_common.test_utils.config import conf_vars
5051
from tests_common.test_utils.db import clear_db_runs, parse_and_sync_to_db
5152

5253
pytestmark = pytest.mark.db_test
@@ -74,7 +75,6 @@ def move_back(old_path, new_path):
7475
shutil.move(new_path, old_path)
7576

7677

77-
# TODO: Check if tests needs side effects - locally there's missing DAG
7878
class TestCliTasks:
7979
run_id = "TEST_RUN_ID"
8080
dag_id = "example_python_operator"
@@ -91,7 +91,7 @@ def setup_class(cls):
9191
cls.parser = cli_parser.get_parser()
9292
clear_db_runs()
9393

94-
cls.dagbag = DagBag(read_dags_from_db=True)
94+
cls.dagbag = DagBag(read_dags_from_db=True, include_examples=True)
9595
cls.dag = cls.dagbag.get_dag(cls.dag_id)
9696
data_interval = cls.dag.timetable.infer_manual_data_interval(run_after=DEFAULT_DATE)
9797
cls.dag_run = cls.dag.create_dagrun(
@@ -108,6 +108,7 @@ def setup_class(cls):
108108
def teardown_class(cls) -> None:
109109
clear_db_runs()
110110

111+
@conf_vars({("core", "load_examples"): "true"})
111112
@pytest.mark.execution_timeout(120)
112113
def test_cli_list_tasks(self):
113114
for dag_id in self.dagbag.dags:

airflow-core/tests/unit/jobs/test_scheduler_job.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@
3737
from sqlalchemy import func, select, update
3838
from sqlalchemy.orm import joinedload
3939

40-
import airflow.example_dags
4140
from airflow import settings
4241
from airflow.assets.manager import AssetManager
4342
from airflow.callbacks.callback_requests import DagCallbackRequest, TaskCallbackRequest
@@ -75,6 +74,7 @@
7574
from airflow.utils.thread_safe_dict import ThreadSafeDict
7675
from airflow.utils.types import DagRunTriggeredByType, DagRunType
7776

77+
from system import standard
7878
from tests_common.test_utils.asserts import assert_queries_count
7979
from tests_common.test_utils.config import conf_vars, env_vars
8080
from tests_common.test_utils.db import (
@@ -104,7 +104,7 @@
104104
ELASTIC_DAG_FILE = os.path.join(PERF_DAGS_FOLDER, "elastic_dag.py")
105105

106106
TEST_DAG_FOLDER = os.environ["AIRFLOW__CORE__DAGS_FOLDER"]
107-
EXAMPLE_DAGS_FOLDER = airflow.example_dags.__path__[0]
107+
EXAMPLE_STANDARD_DAGS_FOLDER = standard.__path__[0]
108108
DEFAULT_DATE = timezone.datetime(2016, 1, 1)
109109
DEFAULT_LOGICAL_DATE = timezone.coerce_datetime(DEFAULT_DATE)
110110
TRY_NUMBER = 1
@@ -5707,7 +5707,7 @@ def test_find_and_purge_task_instances_without_heartbeats_nothing(self):
57075707

57085708
@pytest.mark.usefixtures("testing_dag_bundle")
57095709
def test_find_and_purge_task_instances_without_heartbeats(self, session, create_dagrun):
5710-
dagfile = os.path.join(EXAMPLE_DAGS_FOLDER, "example_branch_operator.py")
5710+
dagfile = os.path.join(EXAMPLE_STANDARD_DAGS_FOLDER, "example_branch_operator.py")
57115711
dagbag = DagBag(dagfile)
57125712
dag = dagbag.get_dag("example_branch_operator")
57135713
dm = LazyDeserializedDAG(data=SerializedDAG.to_dict(dag))
@@ -5773,7 +5773,7 @@ def test_task_instance_heartbeat_timeout_message(self, session, create_dagrun):
57735773
"""
57745774
Check that the task instance heartbeat timeout message comes out as expected
57755775
"""
5776-
dagfile = os.path.join(EXAMPLE_DAGS_FOLDER, "example_branch_operator.py")
5776+
dagfile = os.path.join(EXAMPLE_STANDARD_DAGS_FOLDER, "example_branch_operator.py")
57775777
dagbag = DagBag(dagfile)
57785778
dag = dagbag.get_dag("example_branch_operator")
57795779
dm = LazyDeserializedDAG(data=SerializedDAG.to_dict(dag))

0 commit comments

Comments
 (0)