mirror of
https://github.com/apache/superset.git
synced 2026-05-06 16:34:32 +00:00
Compare commits
1 Commits
fix-sqllab
...
sl-1-exten
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
91d018c52d |
@@ -64,9 +64,11 @@ x-superset-volumes: &superset-volumes
|
|||||||
# /app/pythonpath_docker will be appended to the PYTHONPATH in the final container
|
# /app/pythonpath_docker will be appended to the PYTHONPATH in the final container
|
||||||
- ./docker:/app/docker
|
- ./docker:/app/docker
|
||||||
- ./superset:/app/superset
|
- ./superset:/app/superset
|
||||||
|
- ./superset-core:/app/superset-core
|
||||||
- ./superset-frontend:/app/superset-frontend
|
- ./superset-frontend:/app/superset-frontend
|
||||||
- superset_home_light:/app/superset_home
|
- superset_home_light:/app/superset_home
|
||||||
- ./tests:/app/tests
|
- ./tests:/app/tests
|
||||||
|
- ./extensions:/app/extensions
|
||||||
x-common-build: &common-build
|
x-common-build: &common-build
|
||||||
context: .
|
context: .
|
||||||
target: ${SUPERSET_BUILD_TARGET:-dev} # can use `dev` (default) or `lean`
|
target: ${SUPERSET_BUILD_TARGET:-dev} # can use `dev` (default) or `lean`
|
||||||
|
|||||||
@@ -105,7 +105,15 @@ class CeleryConfig:
|
|||||||
|
|
||||||
CELERY_CONFIG = CeleryConfig
|
CELERY_CONFIG = CeleryConfig
|
||||||
|
|
||||||
FEATURE_FLAGS = {"ALERT_REPORTS": True}
|
# Extensions configuration
|
||||||
|
# For local development, point to the extensions directory
|
||||||
|
# Note: If running in Docker, this path needs to be accessible from inside the container
|
||||||
|
EXTENSIONS_PATH = os.getenv("EXTENSIONS_PATH", "/app/extensions")
|
||||||
|
|
||||||
|
FEATURE_FLAGS = {
|
||||||
|
"ALERT_REPORTS": True,
|
||||||
|
"ENABLE_EXTENSIONS": True,
|
||||||
|
}
|
||||||
ALERT_REPORTS_NOTIFICATION_DRY_RUN = True
|
ALERT_REPORTS_NOTIFICATION_DRY_RUN = True
|
||||||
WEBDRIVER_BASEURL = f"http://superset_app{os.environ.get('SUPERSET_APP_ROOT', '/')}/" # When using docker compose baseurl should be http://superset_nginx{ENV{BASEPATH}}/ # noqa: E501
|
WEBDRIVER_BASEURL = f"http://superset_app{os.environ.get('SUPERSET_APP_ROOT', '/')}/" # When using docker compose baseurl should be http://superset_nginx{ENV{BASEPATH}}/ # noqa: E501
|
||||||
# The base URL for the email report hyperlinks.
|
# The base URL for the email report hyperlinks.
|
||||||
|
|||||||
@@ -223,6 +223,34 @@ def build_extension_data(extension: LoadedExtension) -> dict[str, Any]:
|
|||||||
return extension_data
|
return extension_data
|
||||||
|
|
||||||
|
|
||||||
|
def load_extension_backend(extension: LoadedExtension) -> None:
|
||||||
|
"""
|
||||||
|
Load an extension's backend code by installing modules and importing entry points.
|
||||||
|
|
||||||
|
Entry points are module names that get imported. The modules are expected to
|
||||||
|
self-register any capabilities (e.g., semantic layers) when imported.
|
||||||
|
"""
|
||||||
|
# Install backend modules in-memory if present
|
||||||
|
if extension.backend:
|
||||||
|
install_in_memory_importer(
|
||||||
|
extension.backend,
|
||||||
|
source_base_path=extension.source_base_path,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Import entry point modules - they self-register on import
|
||||||
|
manifest = extension.manifest
|
||||||
|
if manifest.backend:
|
||||||
|
for module_name in manifest.backend.entryPoints:
|
||||||
|
try:
|
||||||
|
eager_import(module_name)
|
||||||
|
except Exception:
|
||||||
|
logger.exception(
|
||||||
|
"Failed to load entry point '%s' from extension %s",
|
||||||
|
module_name,
|
||||||
|
extension.name,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def get_extensions() -> dict[str, LoadedExtension]:
|
def get_extensions() -> dict[str, LoadedExtension]:
|
||||||
extensions: dict[str, LoadedExtension] = {}
|
extensions: dict[str, LoadedExtension] = {}
|
||||||
|
|
||||||
@@ -234,6 +262,7 @@ def get_extensions() -> dict[str, LoadedExtension]:
|
|||||||
extension = get_loaded_extension(files, source_base_path=abs_dist_path)
|
extension = get_loaded_extension(files, source_base_path=abs_dist_path)
|
||||||
extension_id = extension.manifest.id
|
extension_id = extension.manifest.id
|
||||||
extensions[extension_id] = extension
|
extensions[extension_id] = extension
|
||||||
|
load_extension_backend(extension)
|
||||||
logger.info(
|
logger.info(
|
||||||
"Loading extension %s (ID: %s) from local filesystem",
|
"Loading extension %s (ID: %s) from local filesystem",
|
||||||
extension.name,
|
extension.name,
|
||||||
@@ -248,6 +277,7 @@ def get_extensions() -> dict[str, LoadedExtension]:
|
|||||||
extension_id = extension.manifest.id
|
extension_id = extension.manifest.id
|
||||||
if extension_id not in extensions: # Don't override LOCAL_EXTENSIONS
|
if extension_id not in extensions: # Don't override LOCAL_EXTENSIONS
|
||||||
extensions[extension_id] = extension
|
extensions[extension_id] = extension
|
||||||
|
load_extension_backend(extension)
|
||||||
logger.info(
|
logger.info(
|
||||||
"Loading extension %s (ID: %s) from discovery path",
|
"Loading extension %s (ID: %s) from discovery path",
|
||||||
extension.name,
|
extension.name,
|
||||||
|
|||||||
@@ -546,37 +546,18 @@ class SupersetAppInitializer: # pylint: disable=too-many-public-methods
|
|||||||
self.init_extensions()
|
self.init_extensions()
|
||||||
|
|
||||||
def init_extensions(self) -> None:
|
def init_extensions(self) -> None:
|
||||||
from superset.extensions.utils import (
|
from superset.extensions.utils import get_extensions
|
||||||
eager_import,
|
|
||||||
get_extensions,
|
|
||||||
install_in_memory_importer,
|
|
||||||
)
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
extensions = get_extensions()
|
# get_extensions() discovers and loads all extensions,
|
||||||
|
# including installing in-memory importers and registering entry points
|
||||||
|
get_extensions()
|
||||||
except Exception: # pylint: disable=broad-except # noqa: S110
|
except Exception: # pylint: disable=broad-except # noqa: S110
|
||||||
# If the db hasn't been initialized yet, an exception will be raised.
|
# If the db hasn't been initialized yet, an exception will be raised.
|
||||||
# It's fine to ignore this, as in this case there are no extensions
|
# It's fine to ignore this, as in this case there are no extensions
|
||||||
# present yet.
|
# present yet.
|
||||||
return
|
return
|
||||||
|
|
||||||
for extension in extensions.values():
|
|
||||||
if backend_files := extension.backend:
|
|
||||||
install_in_memory_importer(
|
|
||||||
backend_files,
|
|
||||||
source_base_path=extension.source_base_path,
|
|
||||||
)
|
|
||||||
|
|
||||||
backend = extension.manifest.backend
|
|
||||||
|
|
||||||
if backend and (entrypoints := backend.entryPoints):
|
|
||||||
for entrypoint in entrypoints:
|
|
||||||
try:
|
|
||||||
eager_import(entrypoint)
|
|
||||||
except Exception as ex: # pylint: disable=broad-except # noqa: S110
|
|
||||||
# Surface exceptions during initialization of extensions
|
|
||||||
print(ex)
|
|
||||||
|
|
||||||
def init_app_in_ctx(self) -> None:
|
def init_app_in_ctx(self) -> None:
|
||||||
"""
|
"""
|
||||||
Runs init logic in the context of the app
|
Runs init logic in the context of the app
|
||||||
|
|||||||
Reference in New Issue
Block a user