Skip to content

Commit 22440dd

Browse files
committed
Fix mypy errors surfaced by the new AirflowPlugin TypedDict shapes
1 parent a548e97 commit 22440dd

4 files changed

Lines changed: 39 additions & 22 deletions

File tree

airflow-core/src/airflow/plugins_manager.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -148,11 +148,11 @@ def _get_ui_plugins() -> tuple[list[Any], list[Any]]:
148148

149149
seen_url_routes: dict[str, str | None] = {}
150150

151-
external_views: list[Any] = []
152-
react_apps: list[Any] = []
151+
external_views: list[ExternalViewDict] = []
152+
react_apps: list[ReactAppDict] = []
153153
for plugin in _get_plugins()[0]:
154-
external_views_to_remove = []
155-
react_apps_to_remove = []
154+
external_views_to_remove: list[ExternalViewDict] = []
155+
react_apps_to_remove: list[ReactAppDict] = []
156156
for external_view in plugin.external_views:
157157
if not isinstance(external_view, dict):
158158
log.warning(
@@ -201,10 +201,10 @@ def _get_ui_plugins() -> tuple[list[Any], list[Any]]:
201201
react_apps.append(react_app)
202202
seen_url_routes[url_route] = plugin.name
203203

204-
for item in external_views_to_remove:
205-
plugin.external_views.remove(item)
206-
for item in react_apps_to_remove:
207-
plugin.react_apps.remove(item)
204+
for external_view in external_views_to_remove:
205+
plugin.external_views.remove(external_view)
206+
for react_app in react_apps_to_remove:
207+
plugin.react_apps.remove(react_app)
208208
return external_views, react_apps
209209

210210

airflow-core/tests/unit/plugins/test_plugin.py

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,13 @@ def test(self):
6767
from starlette.middleware.base import BaseHTTPMiddleware
6868

6969
# This is the class you derive to create a plugin
70-
from airflow.plugins_manager import AirflowPlugin
70+
from airflow.plugins_manager import (
71+
AirflowPlugin,
72+
ExternalViewDict,
73+
FastAPIAppDict,
74+
FastAPIRootMiddlewareDict,
75+
ReactAppDict,
76+
)
7177
from airflow.task.priority_strategy import PriorityWeightStrategy
7278
from airflow.timetables.interval import CronDataIntervalTimetable
7379

@@ -107,22 +113,22 @@ def plugin_macro():
107113
app = FastAPI()
108114

109115

110-
app_with_metadata = {"app": app, "url_prefix": "/some_prefix", "name": "Name of the App"}
116+
app_with_metadata: FastAPIAppDict = {"app": app, "url_prefix": "/some_prefix", "name": "Name of the App"}
111117

112118

113119
class DummyMiddleware(BaseHTTPMiddleware):
114120
async def dispatch(self, request, call_next):
115121
return await call_next(request)
116122

117123

118-
middleware_with_metadata = {
124+
middleware_with_metadata: FastAPIRootMiddlewareDict = {
119125
"middleware": DummyMiddleware,
120126
"args": [],
121127
"kwargs": {},
122128
"name": "Name of the Middleware",
123129
}
124130

125-
external_view_with_metadata = {
131+
external_view_with_metadata: ExternalViewDict = {
126132
"name": "Test IFrame Airflow Docs",
127133
"href": "https://airflow.apache.org/",
128134
"icon": "https://raw.githubusercontent.com/lucide-icons/lucide/refs/heads/main/icons/plug.svg",
@@ -131,7 +137,7 @@ async def dispatch(self, request, call_next):
131137
"category": "browse",
132138
}
133139

134-
react_app_with_metadata = {
140+
react_app_with_metadata: ReactAppDict = {
135141
"name": "Test React App",
136142
"bundle_url": "https://example.com/test-plugin-bundle.js",
137143
"icon": "https://raw.githubusercontent.com/lucide-icons/lucide/refs/heads/main/icons/plug.svg",
@@ -203,4 +209,4 @@ def on_load(self, *args, **kwargs):
203209

204210
class AirflowTestPluginInvalid(AirflowPlugin):
205211
name = "test_plugin_invalid"
206-
external_views = [external_view_with_invalid_destination]
212+
external_views = [external_view_with_invalid_destination] # type: ignore[list-item] # Deliberate bad destination — exercises the /plugins runtime validation

airflow-core/tests/unit/plugins/test_plugins_manager.py

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -212,13 +212,16 @@ def test_should_warning_about_conflicting_url_route(self, caplog):
212212
class TestPluginA(AirflowPlugin):
213213
name = "test_plugin_a"
214214

215-
external_views = [{"url_route": "/test_route"}, {"wrong_view": "/no_url_route"}]
215+
external_views = [
216+
{"name": "A-view", "href": "/a", "url_route": "/test_route"},
217+
{"name": "A-no_url_route", "href": "/b"},
218+
]
216219

217220
class TestPluginB(AirflowPlugin):
218221
name = "test_plugin_b"
219222

220-
external_views = [{"url_route": "/test_route"}]
221-
react_apps = [{"url_route": "/test_route"}]
223+
external_views = [{"name": "B-view", "href": "/b", "url_route": "/test_route"}]
224+
react_apps = [{"name": "B-react", "bundle_url": "/b.js", "url_route": "/test_route"}]
222225

223226
with (
224227
mock_plugin_manager(plugins=[TestPluginA(), TestPluginB()]),
@@ -241,8 +244,14 @@ def test_should_warning_about_external_views_or_react_app_wrong_object(self, cap
241244
class TestPluginA(AirflowPlugin):
242245
name = "test_plugin_a"
243246

244-
external_views = [[{"nested_list": "/test_route"}], {"url_route": "/test_route"}]
245-
react_apps = [[{"nested_list": "/test_route"}], {"url_route": "/test_route_react_app"}]
247+
external_views = [
248+
[{"nested_list": "/test_route"}],
249+
{"name": "A-view", "href": "/a", "url_route": "/test_route"},
250+
]
251+
react_apps = [
252+
[{"nested_list": "/test_route"}],
253+
{"name": "A-react", "bundle_url": "/a.js", "url_route": "/test_route_react_app"},
254+
]
246255

247256
with (
248257
mock_plugin_manager(plugins=[TestPluginA()]),
@@ -256,8 +265,10 @@ class TestPluginA(AirflowPlugin):
256265
plugin_a = next(
257266
plugin for plugin in plugins_manager._get_plugins()[0] if plugin.name == "test_plugin_a"
258267
)
259-
assert plugin_a.external_views == [{"url_route": "/test_route"}]
260-
assert plugin_a.react_apps == [{"url_route": "/test_route_react_app"}]
268+
assert plugin_a.external_views == [{"name": "A-view", "href": "/a", "url_route": "/test_route"}]
269+
assert plugin_a.react_apps == [
270+
{"name": "A-react", "bundle_url": "/a.js", "url_route": "/test_route_react_app"}
271+
]
261272
assert len(external_views) == 1
262273
assert len(react_apps) == 1
263274

shared/plugins_manager/src/airflow_shared/plugins_manager/plugins_manager.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
import sys
2929
import types
3030
from pathlib import Path
31-
from typing import TYPE_CHECKING, Any, cast, Literal, TypedDict
31+
from typing import TYPE_CHECKING, Any, Literal, TypedDict, cast
3232

3333
from typing_extensions import NotRequired
3434

0 commit comments

Comments
 (0)