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
6 changes: 2 additions & 4 deletions airflow/datasets/manager.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -114,7 +114,7 @@ def register_dataset_change(
session.add(dataset_event)

dags_to_queue_from_dataset = {
ref.dag for ref in dataset_model.consuming_dags if ref.dag.is_active and not ref.dag.is_paused
ref.dag for ref in dataset_model.consuming_dags if not ref.dag.is_paused
}
Comment thread
uranusjr marked this conversation as resolved.
dags_to_queue_from_dataset_alias = set()
if source_alias_names:
Expand All@@ -133,9 +133,7 @@ def register_dataset_change(
session.add(dsa)

dags_to_queue_from_dataset_alias |= {
alias_ref.dag
for alias_ref in dsa.consuming_dags
if alias_ref.dag.is_active and not alias_ref.dag.is_paused
alias_ref.dag for alias_ref in dsa.consuming_dags if not alias_ref.dag.is_paused
}

dags_to_reparse = dags_to_queue_from_dataset_alias - dags_to_queue_from_dataset
Expand Down
43 changes: 38 additions & 5 deletions tests/datasets/test_manager.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -22,7 +22,7 @@
from unittest import mock

import pytest
from sqlalchemy import delete
from sqlalchemy import delete, func, select

from airflow.datasets import Dataset, DatasetAlias
from airflow.datasets.manager import DatasetManager
Expand All@@ -46,7 +46,7 @@
pytest.importorskip("pydantic", minversion="2.0.0")


@pytest.fixture
@pytest.fixture(autouse=True)
def clear_datasets():
from tests.test_utils.db import clear_db_datasets

Expand All@@ -55,6 +55,13 @@ def clear_datasets():
clear_db_datasets()


@pytest.fixture(autouse=True)
def clean_listener_manager():
dataset_listener.clear()
yield
dataset_listener.clear()


@pytest.fixture
def mock_task_instance():
return TaskInstancePydantic(
Expand DownExpand Up@@ -145,7 +152,6 @@ def test_register_dataset_change(self, session, dag_maker, mock_task_instance):
assert session.query(DatasetDagRunQueue).count() == 2

@pytest.mark.skip_if_database_isolation_mode
@pytest.mark.usefixtures("clear_datasets")
def test_register_dataset_change_with_alias(self, session, dag_maker, mock_task_instance):
consumer_dag_1 = DagModel(dag_id="conumser_1", is_active=True, fileloc="dag1.py")
consumer_dag_2 = DagModel(dag_id="conumser_2", is_active=True, fileloc="dag2.py")
Expand DownExpand Up@@ -197,7 +203,6 @@ def test_register_dataset_change_no_downstreams(self, session, mock_task_instanc
@pytest.mark.skip_if_database_isolation_mode
def test_register_dataset_change_notifies_dataset_listener(self, session, mock_task_instance):
dsem = DatasetManager()
dataset_listener.clear()
get_listener_manager().add_listener(dataset_listener)

ds = Dataset(uri="test_dataset_uri_2")
Expand All@@ -218,7 +223,6 @@ def test_register_dataset_change_notifies_dataset_listener(self, session, mock_t
@pytest.mark.skip_if_database_isolation_mode
def test_create_datasets_notifies_dataset_listener(self, session):
dsem = DatasetManager()
dataset_listener.clear()
get_listener_manager().add_listener(dataset_listener)

dsm = DatasetModel(uri="test_dataset_uri_3")
Expand All@@ -228,3 +232,32 @@ def test_create_datasets_notifies_dataset_listener(self, session):
# Ensure the listener was notified
assert len(dataset_listener.created) == 1
assert dataset_listener.created[0].uri == dsm.uri

@pytest.mark.skip_if_database_isolation_mode
def test_register_dataset_change_queues_stale_dag(self, session, mock_task_instance):
dsem = DatasetManager()

dsm = DatasetModel(uri="test_dataset_uri_3")
session.add(dsm)

# Setup a dag that is STALE but NOT PAUSED
# We want stale dags to still receive updates
stale_dag = DagModel(dag_id="stale_dag", is_active=False, is_paused=False)
session.add(stale_dag)

# Link stale dags to the dataset
dsm.consuming_dags = [DagScheduleDatasetReference(dag_id=stale_dag.dag_id)]

session.execute(delete(DatasetDagRunQueue))
session.flush()

dsem.register_dataset_change(
task_instance=mock_task_instance,
dataset=Dataset(dsm.uri),
session=session,
)
session.flush()

# Verify the stale Dag was NOT ignored
assert session.scalar(select(func.count()).select_from(DatasetDagRunQueue)) == 1
assert session.scalar(select(DatasetDagRunQueue.target_dag_id)) == "stale_dag"
5 changes: 4 additions & 1 deletion tests/jobs/test_scheduler_job.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -4479,7 +4479,10 @@ def test_no_create_dag_runs_when_dag_disabled(self, session, dag_maker, disable,
session=session,
)
assert session.scalars(dse_q).one().source_run_id == dr1.run_id
assert session.scalars(ddrq_q).one_or_none() is None
if "is_active" in disable:
assert session.scalars(ddrq_q).one_or_none() is not None
else:
assert session.scalars(ddrq_q).one_or_none() is None

# Simulate the consumer DAG being enabled.
session.execute(update(DagModel).where(DagModel.dag_id == "consumer").values(**enable))
Expand Down
8 changes: 8 additions & 0 deletions tests/listeners/test_dataset_listener.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -24,6 +24,7 @@
from airflow.operators.empty import EmptyOperator
from airflow.utils.session import provide_session
from tests.listeners import dataset_listener
from tests.test_utils.db import clear_db_datasets


@pytest.fixture(autouse=True)
Expand All@@ -37,6 +38,13 @@ def clean_listener_manager():
dataset_listener.clear()


@pytest.fixture(autouse=True)
def clear_datasets():
clear_db_datasets()
yield
clear_db_datasets()


@pytest.mark.skip_if_database_isolation_mode # Test is broken in db isolation mode
@pytest.mark.db_test
@provide_session
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
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
6 changes: 2 additions & 4 deletions airflow/datasets/manager.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -114,7 +114,7 @@ def register_dataset_change(
session.add(dataset_event)

dags_to_queue_from_dataset = {
ref.dag for ref in dataset_model.consuming_dags if ref.dag.is_active and not ref.dag.is_paused
ref.dag for ref in dataset_model.consuming_dags if not ref.dag.is_paused
}
Comment thread
uranusjr marked this conversation as resolved.
dags_to_queue_from_dataset_alias = set()
if source_alias_names:
Expand All@@ -133,9 +133,7 @@ def register_dataset_change(
session.add(dsa)

dags_to_queue_from_dataset_alias |= {
alias_ref.dag
for alias_ref in dsa.consuming_dags
if alias_ref.dag.is_active and not alias_ref.dag.is_paused
alias_ref.dag for alias_ref in dsa.consuming_dags if not alias_ref.dag.is_paused
}

dags_to_reparse = dags_to_queue_from_dataset_alias - dags_to_queue_from_dataset
Expand Down
43 changes: 38 additions & 5 deletions tests/datasets/test_manager.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -22,7 +22,7 @@
from unittest import mock

import pytest
from sqlalchemy import delete
from sqlalchemy import delete, func, select

from airflow.datasets import Dataset, DatasetAlias
from airflow.datasets.manager import DatasetManager
Expand All@@ -46,7 +46,7 @@
pytest.importorskip("pydantic", minversion="2.0.0")


@pytest.fixture
@pytest.fixture(autouse=True)
def clear_datasets():
from tests.test_utils.db import clear_db_datasets

Expand All@@ -55,6 +55,13 @@ def clear_datasets():
clear_db_datasets()


@pytest.fixture(autouse=True)
def clean_listener_manager():
dataset_listener.clear()
yield
dataset_listener.clear()


@pytest.fixture
def mock_task_instance():
return TaskInstancePydantic(
Expand DownExpand Up@@ -145,7 +152,6 @@ def test_register_dataset_change(self, session, dag_maker, mock_task_instance):
assert session.query(DatasetDagRunQueue).count() == 2

@pytest.mark.skip_if_database_isolation_mode
@pytest.mark.usefixtures("clear_datasets")
def test_register_dataset_change_with_alias(self, session, dag_maker, mock_task_instance):
consumer_dag_1 = DagModel(dag_id="conumser_1", is_active=True, fileloc="dag1.py")
consumer_dag_2 = DagModel(dag_id="conumser_2", is_active=True, fileloc="dag2.py")
Expand DownExpand Up@@ -197,7 +203,6 @@ def test_register_dataset_change_no_downstreams(self, session, mock_task_instanc
@pytest.mark.skip_if_database_isolation_mode
def test_register_dataset_change_notifies_dataset_listener(self, session, mock_task_instance):
dsem = DatasetManager()
dataset_listener.clear()
get_listener_manager().add_listener(dataset_listener)

ds = Dataset(uri="test_dataset_uri_2")
Expand All@@ -218,7 +223,6 @@ def test_register_dataset_change_notifies_dataset_listener(self, session, mock_t
@pytest.mark.skip_if_database_isolation_mode
def test_create_datasets_notifies_dataset_listener(self, session):
dsem = DatasetManager()
dataset_listener.clear()
get_listener_manager().add_listener(dataset_listener)

dsm = DatasetModel(uri="test_dataset_uri_3")
Expand All@@ -228,3 +232,32 @@ def test_create_datasets_notifies_dataset_listener(self, session):
# Ensure the listener was notified
assert len(dataset_listener.created) == 1
assert dataset_listener.created[0].uri == dsm.uri

@pytest.mark.skip_if_database_isolation_mode
def test_register_dataset_change_queues_stale_dag(self, session, mock_task_instance):
dsem = DatasetManager()

dsm = DatasetModel(uri="test_dataset_uri_3")
session.add(dsm)

# Setup a dag that is STALE but NOT PAUSED
# We want stale dags to still receive updates
stale_dag = DagModel(dag_id="stale_dag", is_active=False, is_paused=False)
session.add(stale_dag)

# Link stale dags to the dataset
dsm.consuming_dags = [DagScheduleDatasetReference(dag_id=stale_dag.dag_id)]

session.execute(delete(DatasetDagRunQueue))
session.flush()

dsem.register_dataset_change(
task_instance=mock_task_instance,
dataset=Dataset(dsm.uri),
session=session,
)
session.flush()

# Verify the stale Dag was NOT ignored
assert session.scalar(select(func.count()).select_from(DatasetDagRunQueue)) == 1
assert session.scalar(select(DatasetDagRunQueue.target_dag_id)) == "stale_dag"
5 changes: 4 additions & 1 deletion tests/jobs/test_scheduler_job.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -4479,7 +4479,10 @@ def test_no_create_dag_runs_when_dag_disabled(self, session, dag_maker, disable,
session=session,
)
assert session.scalars(dse_q).one().source_run_id == dr1.run_id
assert session.scalars(ddrq_q).one_or_none() is None
if "is_active" in disable:
assert session.scalars(ddrq_q).one_or_none() is not None
else:
assert session.scalars(ddrq_q).one_or_none() is None

# Simulate the consumer DAG being enabled.
session.execute(update(DagModel).where(DagModel.dag_id == "consumer").values(**enable))
Expand Down
8 changes: 8 additions & 0 deletions tests/listeners/test_dataset_listener.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -24,6 +24,7 @@
from airflow.operators.empty import EmptyOperator
from airflow.utils.session import provide_session
from tests.listeners import dataset_listener
from tests.test_utils.db import clear_db_datasets


@pytest.fixture(autouse=True)
Expand All@@ -37,6 +38,13 @@ def clean_listener_manager():
dataset_listener.clear()


@pytest.fixture(autouse=True)
def clear_datasets():
clear_db_datasets()
yield
clear_db_datasets()


@pytest.mark.skip_if_database_isolation_mode # Test is broken in db isolation mode
@pytest.mark.db_test
@provide_session
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
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
6 changes: 2 additions & 4 deletions airflow/datasets/manager.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -114,7 +114,7 @@ def register_dataset_change(
session.add(dataset_event)

dags_to_queue_from_dataset = {
ref.dag for ref in dataset_model.consuming_dags if ref.dag.is_active and not ref.dag.is_paused
ref.dag for ref in dataset_model.consuming_dags if not ref.dag.is_paused
}
Comment thread
uranusjr marked this conversation as resolved.
dags_to_queue_from_dataset_alias = set()
if source_alias_names:
Expand All@@ -133,9 +133,7 @@ def register_dataset_change(
session.add(dsa)

dags_to_queue_from_dataset_alias |= {
alias_ref.dag
for alias_ref in dsa.consuming_dags
if alias_ref.dag.is_active and not alias_ref.dag.is_paused
alias_ref.dag for alias_ref in dsa.consuming_dags if not alias_ref.dag.is_paused
}

dags_to_reparse = dags_to_queue_from_dataset_alias - dags_to_queue_from_dataset
Expand Down
43 changes: 38 additions & 5 deletions tests/datasets/test_manager.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -22,7 +22,7 @@
from unittest import mock

import pytest
from sqlalchemy import delete
from sqlalchemy import delete, func, select

from airflow.datasets import Dataset, DatasetAlias
from airflow.datasets.manager import DatasetManager
Expand All@@ -46,7 +46,7 @@
pytest.importorskip("pydantic", minversion="2.0.0")


@pytest.fixture
@pytest.fixture(autouse=True)
def clear_datasets():
from tests.test_utils.db import clear_db_datasets

Expand All@@ -55,6 +55,13 @@ def clear_datasets():
clear_db_datasets()


@pytest.fixture(autouse=True)
def clean_listener_manager():
dataset_listener.clear()
yield
dataset_listener.clear()


@pytest.fixture
def mock_task_instance():
return TaskInstancePydantic(
Expand DownExpand Up@@ -145,7 +152,6 @@ def test_register_dataset_change(self, session, dag_maker, mock_task_instance):
assert session.query(DatasetDagRunQueue).count() == 2

@pytest.mark.skip_if_database_isolation_mode
@pytest.mark.usefixtures("clear_datasets")
def test_register_dataset_change_with_alias(self, session, dag_maker, mock_task_instance):
consumer_dag_1 = DagModel(dag_id="conumser_1", is_active=True, fileloc="dag1.py")
consumer_dag_2 = DagModel(dag_id="conumser_2", is_active=True, fileloc="dag2.py")
Expand DownExpand Up@@ -197,7 +203,6 @@ def test_register_dataset_change_no_downstreams(self, session, mock_task_instanc
@pytest.mark.skip_if_database_isolation_mode
def test_register_dataset_change_notifies_dataset_listener(self, session, mock_task_instance):
dsem = DatasetManager()
dataset_listener.clear()
get_listener_manager().add_listener(dataset_listener)

ds = Dataset(uri="test_dataset_uri_2")
Expand All@@ -218,7 +223,6 @@ def test_register_dataset_change_notifies_dataset_listener(self, session, mock_t
@pytest.mark.skip_if_database_isolation_mode
def test_create_datasets_notifies_dataset_listener(self, session):
dsem = DatasetManager()
dataset_listener.clear()
get_listener_manager().add_listener(dataset_listener)

dsm = DatasetModel(uri="test_dataset_uri_3")
Expand All@@ -228,3 +232,32 @@ def test_create_datasets_notifies_dataset_listener(self, session):
# Ensure the listener was notified
assert len(dataset_listener.created) == 1
assert dataset_listener.created[0].uri == dsm.uri

@pytest.mark.skip_if_database_isolation_mode
def test_register_dataset_change_queues_stale_dag(self, session, mock_task_instance):
dsem = DatasetManager()

dsm = DatasetModel(uri="test_dataset_uri_3")
session.add(dsm)

# Setup a dag that is STALE but NOT PAUSED
# We want stale dags to still receive updates
stale_dag = DagModel(dag_id="stale_dag", is_active=False, is_paused=False)
session.add(stale_dag)

# Link stale dags to the dataset
dsm.consuming_dags = [DagScheduleDatasetReference(dag_id=stale_dag.dag_id)]

session.execute(delete(DatasetDagRunQueue))
session.flush()

dsem.register_dataset_change(
task_instance=mock_task_instance,
dataset=Dataset(dsm.uri),
session=session,
)
session.flush()

# Verify the stale Dag was NOT ignored
assert session.scalar(select(func.count()).select_from(DatasetDagRunQueue)) == 1
assert session.scalar(select(DatasetDagRunQueue.target_dag_id)) == "stale_dag"
5 changes: 4 additions & 1 deletion tests/jobs/test_scheduler_job.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -4479,7 +4479,10 @@ def test_no_create_dag_runs_when_dag_disabled(self, session, dag_maker, disable,
session=session,
)
assert session.scalars(dse_q).one().source_run_id == dr1.run_id
assert session.scalars(ddrq_q).one_or_none() is None
if "is_active" in disable:
assert session.scalars(ddrq_q).one_or_none() is not None
else:
assert session.scalars(ddrq_q).one_or_none() is None

# Simulate the consumer DAG being enabled.
session.execute(update(DagModel).where(DagModel.dag_id == "consumer").values(**enable))
Expand Down
8 changes: 8 additions & 0 deletions tests/listeners/test_dataset_listener.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -24,6 +24,7 @@
from airflow.operators.empty import EmptyOperator
from airflow.utils.session import provide_session
from tests.listeners import dataset_listener
from tests.test_utils.db import clear_db_datasets


@pytest.fixture(autouse=True)
Expand All@@ -37,6 +38,13 @@ def clean_listener_manager():
dataset_listener.clear()


@pytest.fixture(autouse=True)
def clear_datasets():
clear_db_datasets()
yield
clear_db_datasets()


@pytest.mark.skip_if_database_isolation_mode # Test is broken in db isolation mode
@pytest.mark.db_test
@provide_session
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
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
6 changes: 2 additions & 4 deletions airflow/datasets/manager.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -114,7 +114,7 @@ def register_dataset_change(
session.add(dataset_event)

dags_to_queue_from_dataset = {
ref.dag for ref in dataset_model.consuming_dags if ref.dag.is_active and not ref.dag.is_paused
ref.dag for ref in dataset_model.consuming_dags if not ref.dag.is_paused
}
Comment thread
uranusjr marked this conversation as resolved.
dags_to_queue_from_dataset_alias = set()
if source_alias_names:
Expand All@@ -133,9 +133,7 @@ def register_dataset_change(
session.add(dsa)

dags_to_queue_from_dataset_alias |= {
alias_ref.dag
for alias_ref in dsa.consuming_dags
if alias_ref.dag.is_active and not alias_ref.dag.is_paused
alias_ref.dag for alias_ref in dsa.consuming_dags if not alias_ref.dag.is_paused
}

dags_to_reparse = dags_to_queue_from_dataset_alias - dags_to_queue_from_dataset
Expand Down
43 changes: 38 additions & 5 deletions tests/datasets/test_manager.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -22,7 +22,7 @@
from unittest import mock

import pytest
from sqlalchemy import delete
from sqlalchemy import delete, func, select

from airflow.datasets import Dataset, DatasetAlias
from airflow.datasets.manager import DatasetManager
Expand All@@ -46,7 +46,7 @@
pytest.importorskip("pydantic", minversion="2.0.0")


@pytest.fixture
@pytest.fixture(autouse=True)
def clear_datasets():
from tests.test_utils.db import clear_db_datasets

Expand All@@ -55,6 +55,13 @@ def clear_datasets():
clear_db_datasets()


@pytest.fixture(autouse=True)
def clean_listener_manager():
dataset_listener.clear()
yield
dataset_listener.clear()


@pytest.fixture
def mock_task_instance():
return TaskInstancePydantic(
Expand DownExpand Up@@ -145,7 +152,6 @@ def test_register_dataset_change(self, session, dag_maker, mock_task_instance):
assert session.query(DatasetDagRunQueue).count() == 2

@pytest.mark.skip_if_database_isolation_mode
@pytest.mark.usefixtures("clear_datasets")
def test_register_dataset_change_with_alias(self, session, dag_maker, mock_task_instance):
consumer_dag_1 = DagModel(dag_id="conumser_1", is_active=True, fileloc="dag1.py")
consumer_dag_2 = DagModel(dag_id="conumser_2", is_active=True, fileloc="dag2.py")
Expand DownExpand Up@@ -197,7 +203,6 @@ def test_register_dataset_change_no_downstreams(self, session, mock_task_instanc
@pytest.mark.skip_if_database_isolation_mode
def test_register_dataset_change_notifies_dataset_listener(self, session, mock_task_instance):
dsem = DatasetManager()
dataset_listener.clear()
get_listener_manager().add_listener(dataset_listener)

ds = Dataset(uri="test_dataset_uri_2")
Expand All@@ -218,7 +223,6 @@ def test_register_dataset_change_notifies_dataset_listener(self, session, mock_t
@pytest.mark.skip_if_database_isolation_mode
def test_create_datasets_notifies_dataset_listener(self, session):
dsem = DatasetManager()
dataset_listener.clear()
get_listener_manager().add_listener(dataset_listener)

dsm = DatasetModel(uri="test_dataset_uri_3")
Expand All@@ -228,3 +232,32 @@ def test_create_datasets_notifies_dataset_listener(self, session):
# Ensure the listener was notified
assert len(dataset_listener.created) == 1
assert dataset_listener.created[0].uri == dsm.uri

@pytest.mark.skip_if_database_isolation_mode
def test_register_dataset_change_queues_stale_dag(self, session, mock_task_instance):
dsem = DatasetManager()

dsm = DatasetModel(uri="test_dataset_uri_3")
session.add(dsm)

# Setup a dag that is STALE but NOT PAUSED
# We want stale dags to still receive updates
stale_dag = DagModel(dag_id="stale_dag", is_active=False, is_paused=False)
session.add(stale_dag)

# Link stale dags to the dataset
dsm.consuming_dags = [DagScheduleDatasetReference(dag_id=stale_dag.dag_id)]

session.execute(delete(DatasetDagRunQueue))
session.flush()

dsem.register_dataset_change(
task_instance=mock_task_instance,
dataset=Dataset(dsm.uri),
session=session,
)
session.flush()

# Verify the stale Dag was NOT ignored
assert session.scalar(select(func.count()).select_from(DatasetDagRunQueue)) == 1
assert session.scalar(select(DatasetDagRunQueue.target_dag_id)) == "stale_dag"
5 changes: 4 additions & 1 deletion tests/jobs/test_scheduler_job.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -4479,7 +4479,10 @@ def test_no_create_dag_runs_when_dag_disabled(self, session, dag_maker, disable,
session=session,
)
assert session.scalars(dse_q).one().source_run_id == dr1.run_id
assert session.scalars(ddrq_q).one_or_none() is None
if "is_active" in disable:
assert session.scalars(ddrq_q).one_or_none() is not None
else:
assert session.scalars(ddrq_q).one_or_none() is None

# Simulate the consumer DAG being enabled.
session.execute(update(DagModel).where(DagModel.dag_id == "consumer").values(**enable))
Expand Down
8 changes: 8 additions & 0 deletions tests/listeners/test_dataset_listener.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -24,6 +24,7 @@
from airflow.operators.empty import EmptyOperator
from airflow.utils.session import provide_session
from tests.listeners import dataset_listener
from tests.test_utils.db import clear_db_datasets


@pytest.fixture(autouse=True)
Expand All@@ -37,6 +38,13 @@ def clean_listener_manager():
dataset_listener.clear()


@pytest.fixture(autouse=True)
def clear_datasets():
clear_db_datasets()
yield
clear_db_datasets()


@pytest.mark.skip_if_database_isolation_mode # Test is broken in db isolation mode
@pytest.mark.db_test
@provide_session
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
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
6 changes: 2 additions & 4 deletions airflow/datasets/manager.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -114,7 +114,7 @@ def register_dataset_change(
session.add(dataset_event)

dags_to_queue_from_dataset = {
ref.dag for ref in dataset_model.consuming_dags if ref.dag.is_active and not ref.dag.is_paused
ref.dag for ref in dataset_model.consuming_dags if not ref.dag.is_paused
}
Comment thread
uranusjr marked this conversation as resolved.
dags_to_queue_from_dataset_alias = set()
if source_alias_names:
Expand All@@ -133,9 +133,7 @@ def register_dataset_change(
session.add(dsa)

dags_to_queue_from_dataset_alias |= {
alias_ref.dag
for alias_ref in dsa.consuming_dags
if alias_ref.dag.is_active and not alias_ref.dag.is_paused
alias_ref.dag for alias_ref in dsa.consuming_dags if not alias_ref.dag.is_paused
}

dags_to_reparse = dags_to_queue_from_dataset_alias - dags_to_queue_from_dataset
Expand Down
43 changes: 38 additions & 5 deletions tests/datasets/test_manager.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -22,7 +22,7 @@
from unittest import mock

import pytest
from sqlalchemy import delete
from sqlalchemy import delete, func, select

from airflow.datasets import Dataset, DatasetAlias
from airflow.datasets.manager import DatasetManager
Expand All@@ -46,7 +46,7 @@
pytest.importorskip("pydantic", minversion="2.0.0")


@pytest.fixture
@pytest.fixture(autouse=True)
def clear_datasets():
from tests.test_utils.db import clear_db_datasets

Expand All@@ -55,6 +55,13 @@ def clear_datasets():
clear_db_datasets()


@pytest.fixture(autouse=True)
def clean_listener_manager():
dataset_listener.clear()
yield
dataset_listener.clear()


@pytest.fixture
def mock_task_instance():
return TaskInstancePydantic(
Expand DownExpand Up@@ -145,7 +152,6 @@ def test_register_dataset_change(self, session, dag_maker, mock_task_instance):
assert session.query(DatasetDagRunQueue).count() == 2

@pytest.mark.skip_if_database_isolation_mode
@pytest.mark.usefixtures("clear_datasets")
def test_register_dataset_change_with_alias(self, session, dag_maker, mock_task_instance):
consumer_dag_1 = DagModel(dag_id="conumser_1", is_active=True, fileloc="dag1.py")
consumer_dag_2 = DagModel(dag_id="conumser_2", is_active=True, fileloc="dag2.py")
Expand DownExpand Up@@ -197,7 +203,6 @@ def test_register_dataset_change_no_downstreams(self, session, mock_task_instanc
@pytest.mark.skip_if_database_isolation_mode
def test_register_dataset_change_notifies_dataset_listener(self, session, mock_task_instance):
dsem = DatasetManager()
dataset_listener.clear()
get_listener_manager().add_listener(dataset_listener)

ds = Dataset(uri="test_dataset_uri_2")
Expand All@@ -218,7 +223,6 @@ def test_register_dataset_change_notifies_dataset_listener(self, session, mock_t
@pytest.mark.skip_if_database_isolation_mode
def test_create_datasets_notifies_dataset_listener(self, session):
dsem = DatasetManager()
dataset_listener.clear()
get_listener_manager().add_listener(dataset_listener)

dsm = DatasetModel(uri="test_dataset_uri_3")
Expand All@@ -228,3 +232,32 @@ def test_create_datasets_notifies_dataset_listener(self, session):
# Ensure the listener was notified
assert len(dataset_listener.created) == 1
assert dataset_listener.created[0].uri == dsm.uri

@pytest.mark.skip_if_database_isolation_mode
def test_register_dataset_change_queues_stale_dag(self, session, mock_task_instance):
dsem = DatasetManager()

dsm = DatasetModel(uri="test_dataset_uri_3")
session.add(dsm)

# Setup a dag that is STALE but NOT PAUSED
# We want stale dags to still receive updates
stale_dag = DagModel(dag_id="stale_dag", is_active=False, is_paused=False)
session.add(stale_dag)

# Link stale dags to the dataset
dsm.consuming_dags = [DagScheduleDatasetReference(dag_id=stale_dag.dag_id)]

session.execute(delete(DatasetDagRunQueue))
session.flush()

dsem.register_dataset_change(
task_instance=mock_task_instance,
dataset=Dataset(dsm.uri),
session=session,
)
session.flush()

# Verify the stale Dag was NOT ignored
assert session.scalar(select(func.count()).select_from(DatasetDagRunQueue)) == 1
assert session.scalar(select(DatasetDagRunQueue.target_dag_id)) == "stale_dag"
5 changes: 4 additions & 1 deletion tests/jobs/test_scheduler_job.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -4479,7 +4479,10 @@ def test_no_create_dag_runs_when_dag_disabled(self, session, dag_maker, disable,
session=session,
)
assert session.scalars(dse_q).one().source_run_id == dr1.run_id
assert session.scalars(ddrq_q).one_or_none() is None
if "is_active" in disable:
assert session.scalars(ddrq_q).one_or_none() is not None
else:
assert session.scalars(ddrq_q).one_or_none() is None

# Simulate the consumer DAG being enabled.
session.execute(update(DagModel).where(DagModel.dag_id == "consumer").values(**enable))
Expand Down
8 changes: 8 additions & 0 deletions tests/listeners/test_dataset_listener.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -24,6 +24,7 @@
from airflow.operators.empty import EmptyOperator
from airflow.utils.session import provide_session
from tests.listeners import dataset_listener
from tests.test_utils.db import clear_db_datasets


@pytest.fixture(autouse=True)
Expand All@@ -37,6 +38,13 @@ def clean_listener_manager():
dataset_listener.clear()


@pytest.fixture(autouse=True)
def clear_datasets():
clear_db_datasets()
yield
clear_db_datasets()


@pytest.mark.skip_if_database_isolation_mode # Test is broken in db isolation mode
@pytest.mark.db_test
@provide_session
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
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
6 changes: 2 additions & 4 deletions airflow/datasets/manager.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -114,7 +114,7 @@ def register_dataset_change(
session.add(dataset_event)

dags_to_queue_from_dataset = {
ref.dag for ref in dataset_model.consuming_dags if ref.dag.is_active and not ref.dag.is_paused
ref.dag for ref in dataset_model.consuming_dags if not ref.dag.is_paused
}
Comment thread
uranusjr marked this conversation as resolved.
dags_to_queue_from_dataset_alias = set()
if source_alias_names:
Expand All@@ -133,9 +133,7 @@ def register_dataset_change(
session.add(dsa)

dags_to_queue_from_dataset_alias |= {
alias_ref.dag
for alias_ref in dsa.consuming_dags
if alias_ref.dag.is_active and not alias_ref.dag.is_paused
alias_ref.dag for alias_ref in dsa.consuming_dags if not alias_ref.dag.is_paused
}

dags_to_reparse = dags_to_queue_from_dataset_alias - dags_to_queue_from_dataset
Expand Down
43 changes: 38 additions & 5 deletions tests/datasets/test_manager.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -22,7 +22,7 @@
from unittest import mock

import pytest
from sqlalchemy import delete
from sqlalchemy import delete, func, select

from airflow.datasets import Dataset, DatasetAlias
from airflow.datasets.manager import DatasetManager
Expand All@@ -46,7 +46,7 @@
pytest.importorskip("pydantic", minversion="2.0.0")


@pytest.fixture
@pytest.fixture(autouse=True)
def clear_datasets():
from tests.test_utils.db import clear_db_datasets

Expand All@@ -55,6 +55,13 @@ def clear_datasets():
clear_db_datasets()


@pytest.fixture(autouse=True)
def clean_listener_manager():
dataset_listener.clear()
yield
dataset_listener.clear()


@pytest.fixture
def mock_task_instance():
return TaskInstancePydantic(
Expand DownExpand Up@@ -145,7 +152,6 @@ def test_register_dataset_change(self, session, dag_maker, mock_task_instance):
assert session.query(DatasetDagRunQueue).count() == 2

@pytest.mark.skip_if_database_isolation_mode
@pytest.mark.usefixtures("clear_datasets")
def test_register_dataset_change_with_alias(self, session, dag_maker, mock_task_instance):
consumer_dag_1 = DagModel(dag_id="conumser_1", is_active=True, fileloc="dag1.py")
consumer_dag_2 = DagModel(dag_id="conumser_2", is_active=True, fileloc="dag2.py")
Expand DownExpand Up@@ -197,7 +203,6 @@ def test_register_dataset_change_no_downstreams(self, session, mock_task_instanc
@pytest.mark.skip_if_database_isolation_mode
def test_register_dataset_change_notifies_dataset_listener(self, session, mock_task_instance):
dsem = DatasetManager()
dataset_listener.clear()
get_listener_manager().add_listener(dataset_listener)

ds = Dataset(uri="test_dataset_uri_2")
Expand All@@ -218,7 +223,6 @@ def test_register_dataset_change_notifies_dataset_listener(self, session, mock_t
@pytest.mark.skip_if_database_isolation_mode
def test_create_datasets_notifies_dataset_listener(self, session):
dsem = DatasetManager()
dataset_listener.clear()
get_listener_manager().add_listener(dataset_listener)

dsm = DatasetModel(uri="test_dataset_uri_3")
Expand All@@ -228,3 +232,32 @@ def test_create_datasets_notifies_dataset_listener(self, session):
# Ensure the listener was notified
assert len(dataset_listener.created) == 1
assert dataset_listener.created[0].uri == dsm.uri

@pytest.mark.skip_if_database_isolation_mode
def test_register_dataset_change_queues_stale_dag(self, session, mock_task_instance):
dsem = DatasetManager()

dsm = DatasetModel(uri="test_dataset_uri_3")
session.add(dsm)

# Setup a dag that is STALE but NOT PAUSED
# We want stale dags to still receive updates
stale_dag = DagModel(dag_id="stale_dag", is_active=False, is_paused=False)
session.add(stale_dag)

# Link stale dags to the dataset
dsm.consuming_dags = [DagScheduleDatasetReference(dag_id=stale_dag.dag_id)]

session.execute(delete(DatasetDagRunQueue))
session.flush()

dsem.register_dataset_change(
task_instance=mock_task_instance,
dataset=Dataset(dsm.uri),
session=session,
)
session.flush()

# Verify the stale Dag was NOT ignored
assert session.scalar(select(func.count()).select_from(DatasetDagRunQueue)) == 1
assert session.scalar(select(DatasetDagRunQueue.target_dag_id)) == "stale_dag"
5 changes: 4 additions & 1 deletion tests/jobs/test_scheduler_job.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -4479,7 +4479,10 @@ def test_no_create_dag_runs_when_dag_disabled(self, session, dag_maker, disable,
session=session,
)
assert session.scalars(dse_q).one().source_run_id == dr1.run_id
assert session.scalars(ddrq_q).one_or_none() is None
if "is_active" in disable:
assert session.scalars(ddrq_q).one_or_none() is not None
else:
assert session.scalars(ddrq_q).one_or_none() is None

# Simulate the consumer DAG being enabled.
session.execute(update(DagModel).where(DagModel.dag_id == "consumer").values(**enable))
Expand Down
8 changes: 8 additions & 0 deletions tests/listeners/test_dataset_listener.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -24,6 +24,7 @@
from airflow.operators.empty import EmptyOperator
from airflow.utils.session import provide_session
from tests.listeners import dataset_listener
from tests.test_utils.db import clear_db_datasets


@pytest.fixture(autouse=True)
Expand All@@ -37,6 +38,13 @@ def clean_listener_manager():
dataset_listener.clear()


@pytest.fixture(autouse=True)
def clear_datasets():
clear_db_datasets()
yield
clear_db_datasets()


@pytest.mark.skip_if_database_isolation_mode # Test is broken in db isolation mode
@pytest.mark.db_test
@provide_session
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
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
6 changes: 2 additions & 4 deletions airflow/datasets/manager.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -114,7 +114,7 @@ def register_dataset_change(
session.add(dataset_event)

dags_to_queue_from_dataset = {
ref.dag for ref in dataset_model.consuming_dags if ref.dag.is_active and not ref.dag.is_paused
ref.dag for ref in dataset_model.consuming_dags if not ref.dag.is_paused
}
Comment thread
uranusjr marked this conversation as resolved.
dags_to_queue_from_dataset_alias = set()
if source_alias_names:
Expand All@@ -133,9 +133,7 @@ def register_dataset_change(
session.add(dsa)

dags_to_queue_from_dataset_alias |= {
alias_ref.dag
for alias_ref in dsa.consuming_dags
if alias_ref.dag.is_active and not alias_ref.dag.is_paused
alias_ref.dag for alias_ref in dsa.consuming_dags if not alias_ref.dag.is_paused
}

dags_to_reparse = dags_to_queue_from_dataset_alias - dags_to_queue_from_dataset
Expand Down
43 changes: 38 additions & 5 deletions tests/datasets/test_manager.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -22,7 +22,7 @@
from unittest import mock

import pytest
from sqlalchemy import delete
from sqlalchemy import delete, func, select

from airflow.datasets import Dataset, DatasetAlias
from airflow.datasets.manager import DatasetManager
Expand All@@ -46,7 +46,7 @@
pytest.importorskip("pydantic", minversion="2.0.0")


@pytest.fixture
@pytest.fixture(autouse=True)
def clear_datasets():
from tests.test_utils.db import clear_db_datasets

Expand All@@ -55,6 +55,13 @@ def clear_datasets():
clear_db_datasets()


@pytest.fixture(autouse=True)
def clean_listener_manager():
dataset_listener.clear()
yield
dataset_listener.clear()


@pytest.fixture
def mock_task_instance():
return TaskInstancePydantic(
Expand DownExpand Up@@ -145,7 +152,6 @@ def test_register_dataset_change(self, session, dag_maker, mock_task_instance):
assert session.query(DatasetDagRunQueue).count() == 2

@pytest.mark.skip_if_database_isolation_mode
@pytest.mark.usefixtures("clear_datasets")
def test_register_dataset_change_with_alias(self, session, dag_maker, mock_task_instance):
consumer_dag_1 = DagModel(dag_id="conumser_1", is_active=True, fileloc="dag1.py")
consumer_dag_2 = DagModel(dag_id="conumser_2", is_active=True, fileloc="dag2.py")
Expand DownExpand Up@@ -197,7 +203,6 @@ def test_register_dataset_change_no_downstreams(self, session, mock_task_instanc
@pytest.mark.skip_if_database_isolation_mode
def test_register_dataset_change_notifies_dataset_listener(self, session, mock_task_instance):
dsem = DatasetManager()
dataset_listener.clear()
get_listener_manager().add_listener(dataset_listener)

ds = Dataset(uri="test_dataset_uri_2")
Expand All@@ -218,7 +223,6 @@ def test_register_dataset_change_notifies_dataset_listener(self, session, mock_t
@pytest.mark.skip_if_database_isolation_mode
def test_create_datasets_notifies_dataset_listener(self, session):
dsem = DatasetManager()
dataset_listener.clear()
get_listener_manager().add_listener(dataset_listener)

dsm = DatasetModel(uri="test_dataset_uri_3")
Expand All@@ -228,3 +232,32 @@ def test_create_datasets_notifies_dataset_listener(self, session):
# Ensure the listener was notified
assert len(dataset_listener.created) == 1
assert dataset_listener.created[0].uri == dsm.uri

@pytest.mark.skip_if_database_isolation_mode
def test_register_dataset_change_queues_stale_dag(self, session, mock_task_instance):
dsem = DatasetManager()

dsm = DatasetModel(uri="test_dataset_uri_3")
session.add(dsm)

# Setup a dag that is STALE but NOT PAUSED
# We want stale dags to still receive updates
stale_dag = DagModel(dag_id="stale_dag", is_active=False, is_paused=False)
session.add(stale_dag)

# Link stale dags to the dataset
dsm.consuming_dags = [DagScheduleDatasetReference(dag_id=stale_dag.dag_id)]

session.execute(delete(DatasetDagRunQueue))
session.flush()

dsem.register_dataset_change(
task_instance=mock_task_instance,
dataset=Dataset(dsm.uri),
session=session,
)
session.flush()

# Verify the stale Dag was NOT ignored
assert session.scalar(select(func.count()).select_from(DatasetDagRunQueue)) == 1
assert session.scalar(select(DatasetDagRunQueue.target_dag_id)) == "stale_dag"
5 changes: 4 additions & 1 deletion tests/jobs/test_scheduler_job.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -4479,7 +4479,10 @@ def test_no_create_dag_runs_when_dag_disabled(self, session, dag_maker, disable,
session=session,
)
assert session.scalars(dse_q).one().source_run_id == dr1.run_id
assert session.scalars(ddrq_q).one_or_none() is None
if "is_active" in disable:
assert session.scalars(ddrq_q).one_or_none() is not None
else:
assert session.scalars(ddrq_q).one_or_none() is None

# Simulate the consumer DAG being enabled.
session.execute(update(DagModel).where(DagModel.dag_id == "consumer").values(**enable))
Expand Down
8 changes: 8 additions & 0 deletions tests/listeners/test_dataset_listener.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -24,6 +24,7 @@
from airflow.operators.empty import EmptyOperator
from airflow.utils.session import provide_session
from tests.listeners import dataset_listener
from tests.test_utils.db import clear_db_datasets


@pytest.fixture(autouse=True)
Expand All@@ -37,6 +38,13 @@ def clean_listener_manager():
dataset_listener.clear()


@pytest.fixture(autouse=True)
def clear_datasets():
clear_db_datasets()
yield
clear_db_datasets()


@pytest.mark.skip_if_database_isolation_mode # Test is broken in db isolation mode
@pytest.mark.db_test
@provide_session
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
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
6 changes: 2 additions & 4 deletions airflow/datasets/manager.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -114,7 +114,7 @@ def register_dataset_change(
session.add(dataset_event)

dags_to_queue_from_dataset = {
ref.dag for ref in dataset_model.consuming_dags if ref.dag.is_active and not ref.dag.is_paused
ref.dag for ref in dataset_model.consuming_dags if not ref.dag.is_paused
}
Comment thread
uranusjr marked this conversation as resolved.
dags_to_queue_from_dataset_alias = set()
if source_alias_names:
Expand All@@ -133,9 +133,7 @@ def register_dataset_change(
session.add(dsa)

dags_to_queue_from_dataset_alias |= {
alias_ref.dag
for alias_ref in dsa.consuming_dags
if alias_ref.dag.is_active and not alias_ref.dag.is_paused
alias_ref.dag for alias_ref in dsa.consuming_dags if not alias_ref.dag.is_paused
}

dags_to_reparse = dags_to_queue_from_dataset_alias - dags_to_queue_from_dataset
Expand Down
43 changes: 38 additions & 5 deletions tests/datasets/test_manager.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -22,7 +22,7 @@
from unittest import mock

import pytest
from sqlalchemy import delete
from sqlalchemy import delete, func, select

from airflow.datasets import Dataset, DatasetAlias
from airflow.datasets.manager import DatasetManager
Expand All@@ -46,7 +46,7 @@
pytest.importorskip("pydantic", minversion="2.0.0")


@pytest.fixture
@pytest.fixture(autouse=True)
def clear_datasets():
from tests.test_utils.db import clear_db_datasets

Expand All@@ -55,6 +55,13 @@ def clear_datasets():
clear_db_datasets()


@pytest.fixture(autouse=True)
def clean_listener_manager():
dataset_listener.clear()
yield
dataset_listener.clear()


@pytest.fixture
def mock_task_instance():
return TaskInstancePydantic(
Expand DownExpand Up@@ -145,7 +152,6 @@ def test_register_dataset_change(self, session, dag_maker, mock_task_instance):
assert session.query(DatasetDagRunQueue).count() == 2

@pytest.mark.skip_if_database_isolation_mode
@pytest.mark.usefixtures("clear_datasets")
def test_register_dataset_change_with_alias(self, session, dag_maker, mock_task_instance):
consumer_dag_1 = DagModel(dag_id="conumser_1", is_active=True, fileloc="dag1.py")
consumer_dag_2 = DagModel(dag_id="conumser_2", is_active=True, fileloc="dag2.py")
Expand DownExpand Up@@ -197,7 +203,6 @@ def test_register_dataset_change_no_downstreams(self, session, mock_task_instanc
@pytest.mark.skip_if_database_isolation_mode
def test_register_dataset_change_notifies_dataset_listener(self, session, mock_task_instance):
dsem = DatasetManager()
dataset_listener.clear()
get_listener_manager().add_listener(dataset_listener)

ds = Dataset(uri="test_dataset_uri_2")
Expand All@@ -218,7 +223,6 @@ def test_register_dataset_change_notifies_dataset_listener(self, session, mock_t
@pytest.mark.skip_if_database_isolation_mode
def test_create_datasets_notifies_dataset_listener(self, session):
dsem = DatasetManager()
dataset_listener.clear()
get_listener_manager().add_listener(dataset_listener)

dsm = DatasetModel(uri="test_dataset_uri_3")
Expand All@@ -228,3 +232,32 @@ def test_create_datasets_notifies_dataset_listener(self, session):
# Ensure the listener was notified
assert len(dataset_listener.created) == 1
assert dataset_listener.created[0].uri == dsm.uri

@pytest.mark.skip_if_database_isolation_mode
def test_register_dataset_change_queues_stale_dag(self, session, mock_task_instance):
dsem = DatasetManager()

dsm = DatasetModel(uri="test_dataset_uri_3")
session.add(dsm)

# Setup a dag that is STALE but NOT PAUSED
# We want stale dags to still receive updates
stale_dag = DagModel(dag_id="stale_dag", is_active=False, is_paused=False)
session.add(stale_dag)

# Link stale dags to the dataset
dsm.consuming_dags = [DagScheduleDatasetReference(dag_id=stale_dag.dag_id)]

session.execute(delete(DatasetDagRunQueue))
session.flush()

dsem.register_dataset_change(
task_instance=mock_task_instance,
dataset=Dataset(dsm.uri),
session=session,
)
session.flush()

# Verify the stale Dag was NOT ignored
assert session.scalar(select(func.count()).select_from(DatasetDagRunQueue)) == 1
assert session.scalar(select(DatasetDagRunQueue.target_dag_id)) == "stale_dag"
5 changes: 4 additions & 1 deletion tests/jobs/test_scheduler_job.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -4479,7 +4479,10 @@ def test_no_create_dag_runs_when_dag_disabled(self, session, dag_maker, disable,
session=session,
)
assert session.scalars(dse_q).one().source_run_id == dr1.run_id
assert session.scalars(ddrq_q).one_or_none() is None
if "is_active" in disable:
assert session.scalars(ddrq_q).one_or_none() is not None
else:
assert session.scalars(ddrq_q).one_or_none() is None

# Simulate the consumer DAG being enabled.
session.execute(update(DagModel).where(DagModel.dag_id == "consumer").values(**enable))
Expand Down
8 changes: 8 additions & 0 deletions tests/listeners/test_dataset_listener.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -24,6 +24,7 @@
from airflow.operators.empty import EmptyOperator
from airflow.utils.session import provide_session
from tests.listeners import dataset_listener
from tests.test_utils.db import clear_db_datasets


@pytest.fixture(autouse=True)
Expand All@@ -37,6 +38,13 @@ def clean_listener_manager():
dataset_listener.clear()


@pytest.fixture(autouse=True)
def clear_datasets():
clear_db_datasets()
yield
clear_db_datasets()


@pytest.mark.skip_if_database_isolation_mode # Test is broken in db isolation mode
@pytest.mark.db_test
@provide_session
Expand Down
Loading