| # Licensed to the Apache Software Foundation (ASF) under one |
| # or more contributor license agreements. See the NOTICE file |
| # distributed with this work for additional information |
| # regarding copyright ownership. The ASF licenses this file |
| # to you under the Apache License, Version 2.0 (the |
| # "License"); you may not use this file except in compliance |
| # with the License. You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, |
| # software distributed under the License is distributed on an |
| # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| # KIND, either express or implied. See the License for the |
| # specific language governing permissions and limitations |
| # under the License. |
| from __future__ import annotations |
| |
| import importlib.machinery |
| import importlib.util |
| import logging |
| import os |
| import sys |
| from types import ModuleType |
| from typing import Any, Dict, Union |
| |
| from flask import Flask |
| from werkzeug.utils import import_string |
| |
| from superset.initialization import SupersetAppInitializer |
| from superset.utils.core import is_test |
| |
| logger = logging.getLogger(__name__) |
| |
| |
| def create_app() -> Flask: |
| app = SupersetApp(__name__) |
| |
| try: |
| # Allow user to override our config completely |
| config = init_config() |
| app.config.from_mapping(config) |
| |
| app_initializer = app.config.get("APP_INITIALIZER", SupersetAppInitializer)(app) |
| app_initializer.init_app() |
| |
| return app |
| |
| # Make sure that bootstrap errors ALWAYS get logged |
| except Exception as ex: |
| logger.exception("Failed to create app") |
| raise ex |
| |
| |
| def init_config() -> Dict[Any, Any]: |
| config = convert_to_dict(load_default_config()) |
| override_conf = convert_to_dict(load_override_config()) |
| config.update(override_conf) |
| return config |
| |
| |
| def convert_to_dict(module: Union[ModuleType, Dict[Any, Any]]) -> Dict[Any, Any]: |
| raw_dict = module if isinstance(module, dict) else module.__dict__ |
| return {k: v for k, v in raw_dict.items() if k.isupper() and not k.startswith("_")} |
| |
| |
| def load_default_config() -> ModuleType: |
| config_module = os.environ.get("SUPERSET_CONFIG", "superset.config") |
| config: ModuleType = import_string(config_module) |
| return config |
| |
| |
| def load_override_config() -> Union[Dict[Any, Any], ModuleType]: |
| CONFIG_PATH_ENV_VAR = "SUPERSET_CONFIG_PATH" # pylint: disable=C0103 |
| if CONFIG_PATH_ENV_VAR in os.environ: |
| # Explicitly import config module that is not necessarily in pythonpath; useful |
| # for case where app is being executed via pex. |
| cfg_path = os.environ[CONFIG_PATH_ENV_VAR] |
| try: |
| CONFIG_MODULE_NAME = "superset_config" # pylint: disable=C0103 |
| loader = importlib.machinery.SourceFileLoader(CONFIG_MODULE_NAME, cfg_path) |
| spec = importlib.util.spec_from_loader(CONFIG_MODULE_NAME, loader) |
| override_conf = importlib.util.module_from_spec(spec) |
| sys.modules[CONFIG_MODULE_NAME] = override_conf |
| loader.exec_module(override_conf) |
| |
| print(f"Loaded your LOCAL configuration at [{cfg_path}]") |
| return override_conf |
| except Exception: |
| logger.exception( |
| "Failed to import config for %s=%s", CONFIG_PATH_ENV_VAR, cfg_path |
| ) |
| raise |
| elif importlib.util.find_spec("superset_config") and not is_test(): |
| try: |
| import superset_config # pylint: disable=import-error |
| |
| print(f"Loaded your LOCAL configuration at [{superset_config.__file__}]") |
| return superset_config |
| except Exception: |
| logger.exception("Found but failed to import local superset_config") |
| raise |
| return {} |
| |
| |
| class SupersetApp(Flask): |
| pass |