You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@superset.apache.org by ru...@apache.org on 2026/09/22 01:25:25 UTC
(superset) branch master updated: fix(reports): replace stale Selenium references in screenshot messages (#44244)
This is an automated email from the ASF dual-hosted git repository.
rusackas pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/superset.git
The following commit(s) were added to refs/heads/master by this push:
new f47fcad3d6e fix(reports): replace stale Selenium references in screenshot messages (#44244)
f47fcad3d6e is described below
commit f47fcad3d6e5ec7740bc5b7dd26d21695580d2b4
Author: Sam Firke <sf...@users.noreply.github.com>
AuthorDate: Mon Sep 21 21:25:18 2026 -0400
fix(reports): replace stale Selenium references in screenshot messages (#44244)
Co-authored-by: Joe Li <jo...@preset.io>
---
docker-compose.yml | 2 +-
superset/config.py | 5 +++--
superset/mcp_service/README.md | 6 ++----
superset/utils/screenshots.py | 3 +--
superset/utils/webdriver.py | 17 ++++++++++++-----
tests/unit_tests/utils/webdriver_test.py | 31 +++++++++++++++++++++++++++++++
6 files changed, 50 insertions(+), 14 deletions(-)
diff --git a/docker-compose.yml b/docker-compose.yml
index 381a1cefca4..2a7d01e86f2 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -257,7 +257,7 @@ services:
- "host.docker.internal:host-gateway"
healthcheck:
test: ["CMD-SHELL", "celery -A superset.tasks.celery_app:app inspect ping -d celery@$$HOSTNAME"]
- # Bump memory limit if processing selenium / thumbnails on superset-worker
+ # Bump memory limit if processing screenshots / thumbnails on superset-worker
# mem_limit: 2038m
# mem_reservation: 128M
diff --git a/superset/config.py b/superset/config.py
index 5afe84e06fa..648e4f9fd0a 100644
--- a/superset/config.py
+++ b/superset/config.py
@@ -1287,8 +1287,8 @@ CACHE_WARMUP_EXECUTORS = [ExecutorType.EDITOR]
# ---------------------------------------------------
# Thumbnail config (behind feature flag)
# ---------------------------------------------------
-# By default, thumbnails are rendered per user, and will fall back to the Selenium
-# user for anonymous users. Similar to Alerts & Reports, thumbnails
+# By default, thumbnails are rendered as the user who requests them. Similar to
+# Alerts & Reports, thumbnails
# can be configured to always be rendered as a fixed user. See
# `superset.tasks.types.ExecutorType` for a full list of executor options.
# To always use a fixed user account (admin in this example, use the following
@@ -1365,6 +1365,7 @@ SUPERSET_CACHE_WARMUP_USER: str | None = None
SCREENSHOT_LOCATE_WAIT = int(timedelta(seconds=10).total_seconds())
# Time before screenshot capture times out while waiting for chart readiness.
SCREENSHOT_LOAD_WAIT = int(timedelta(minutes=1).total_seconds())
+# "SELENIUM" in the next two key names is historical; both apply to Playwright.
# Give the browser an initial headstart, in seconds
SCREENSHOT_SELENIUM_HEADSTART = 3
# Wait for the chart animation, in seconds
diff --git a/superset/mcp_service/README.md b/superset/mcp_service/README.md
index 052b18d681c..2079ba94884 100644
--- a/superset/mcp_service/README.md
+++ b/superset/mcp_service/README.md
@@ -688,9 +688,8 @@ fixed dev-mode identity would defeat the point of requiring real auth. Pick one:
# MCP Service Configuration -- development/testing (no auth)
MCP_DEV_USERNAME = 'admin' # Username for development/testing
-# WebDriver for chart screenshots
+# Headless browser (Playwright + Chromium) settings for chart screenshots
WEBDRIVER_BASEURL = 'http://superset:8088/'
-WEBDRIVER_TYPE = 'chrome'
WEBDRIVER_OPTION_ARGS = ['--headless', '--no-sandbox']
```
@@ -703,9 +702,8 @@ MCP_JWT_PUBLIC_KEY = 'your-public-key' # For JWT token validation
MCP_AUTH_FACTORY = 'your.custom.auth_factory'
MCP_USER_RESOLVER = 'your.custom.user_resolver'
-# WebDriver for chart screenshots
+# Headless browser (Playwright + Chromium) settings for chart screenshots
WEBDRIVER_BASEURL = 'http://superset:8088/'
-WEBDRIVER_TYPE = 'chrome'
WEBDRIVER_OPTION_ARGS = ['--headless', '--no-sandbox']
```
diff --git a/superset/utils/screenshots.py b/superset/utils/screenshots.py
index 1b7a70bea10..cd331a8b3e6 100644
--- a/superset/utils/screenshots.py
+++ b/superset/utils/screenshots.py
@@ -542,7 +542,6 @@ class BaseScreenshot:
else:
self.cache.set(cache_key, cache_payload.to_dict())
image = None
- # Assuming all sorts of things can go wrong with Selenium
try:
logger.info(
"trying to generate screenshot for cache_key=%s", cache_key
@@ -647,7 +646,7 @@ class BaseScreenshot:
context_suffix = f" [{log_context}]" if log_context else ""
thumb_size = thumb_size or cls.thumb_size
img = Image.open(BytesIO(img_bytes))
- logger.debug("Selenium image size: %s%s", str(img.size), context_suffix)
+ logger.debug("Screenshot image size: %s%s", str(img.size), context_suffix)
if crop and img.size[1] != cls.window_size[1]:
desired_ratio = float(cls.window_size[1]) / cls.window_size[0]
desired_width = int(img.size[0] * desired_ratio)
diff --git a/superset/utils/webdriver.py b/superset/utils/webdriver.py
index 3aa464ebc33..d7ae9618d05 100644
--- a/superset/utils/webdriver.py
+++ b/superset/utils/webdriver.py
@@ -66,10 +66,12 @@ from superset.utils.screenshot_utils import (
WindowSize = tuple[int, int]
logger = logging.getLogger(__name__)
-# Installation message for missing Playwright (Cypress doesn't work with DeckGL)
+# Installation hint for the one failure that carries no error of its own: the
+# Playwright import failed, so there is nothing to report but this. Launch
+# failures raise Playwright's own error instead, which already says whether the
+# browser binary is missing and which command installs it.
PLAYWRIGHT_INSTALL_MESSAGE = (
- "To complete the migration from Cypress "
- "and enable WebGL/DeckGL screenshot support, install Playwright with: "
+ "Install Playwright and Chromium with: "
"pip install playwright && playwright install chromium"
)
@@ -944,9 +946,14 @@ class WebDriverPlaywright(WebDriverProxy):
try:
browser = _browser_manager.get_browser(browser_args)
except Exception as ex:
+ logger.exception(
+ "Failed to launch the headless browser with args %s%s",
+ browser_args,
+ context_suffix,
+ )
raise RuntimeError(
- f"Playwright is required for screenshots. "
- f"{PLAYWRIGHT_INSTALL_MESSAGE}{context_suffix}"
+ f"Failed to launch the headless browser for screenshots: "
+ f"{ex}{context_suffix}"
) from ex
pixel_density = app.config["WEBDRIVER_WINDOW"].get("pixel_density", 1)
viewport_height = self._window[1]
diff --git a/tests/unit_tests/utils/webdriver_test.py b/tests/unit_tests/utils/webdriver_test.py
index 5a0068d37aa..e7461f9e3ab 100644
--- a/tests/unit_tests/utils/webdriver_test.py
+++ b/tests/unit_tests/utils/webdriver_test.py
@@ -41,6 +41,7 @@ from superset.utils.webdriver import (
check_playwright_availability,
PLAYWRIGHT_AVAILABLE,
PLAYWRIGHT_INSTALL_MESSAGE,
+ PlaywrightError,
WebDriverPlaywright,
)
@@ -505,6 +506,36 @@ class TestWebDriverPlaywrightFallback:
with pytest.raises(RuntimeError, match="Playwright is required"):
driver.get_screenshot("http://example.com", "test-element", mock_user)
+ @patch("superset.utils.webdriver.PLAYWRIGHT_AVAILABLE", True)
+ @patch("superset.utils.webdriver._browser_manager")
+ @patch("superset.utils.webdriver.app")
+ def test_get_screenshot_surfaces_the_browser_launch_error(
+ self, mock_app, mock_browser_manager
+ ):
+ """Chromium is installed but refuses to start, here because the worker
+ runs as root without --no-sandbox. Every launch failure used to be
+ reported as a missing dependency, sending operators after a reinstall
+ that fixes nothing, so Playwright's own error is passed through instead
+ (#44244). A missing browser binary takes the same path: Playwright names
+ the expected path and the install command in its message, which is more
+ than Superset can say about it."""
+ mock_app.config = {
+ "WEBDRIVER_OPTION_ARGS": [],
+ "SCREENSHOT_LOCATE_WAIT": 10,
+ "SCREENSHOT_LOAD_WAIT": 10,
+ }
+ mock_browser_manager.get_browser.side_effect = PlaywrightError(
+ "BrowserType.launch: Running as root without --no-sandbox is not supported."
+ )
+
+ driver = WebDriverPlaywright("chrome")
+ with pytest.raises(RuntimeError) as excinfo:
+ driver.get_screenshot("http://example.com", "test-element")
+
+ message = str(excinfo.value)
+ assert "Running as root without --no-sandbox" in message
+ assert PLAYWRIGHT_INSTALL_MESSAGE not in message
+
@patch("superset.utils.webdriver.PLAYWRIGHT_AVAILABLE", True)
@patch("superset.utils.webdriver._browser_manager")
@patch("superset.utils.webdriver.app")