#!/usr/bin/env python3 """Copy bundle extension files to the client/app/extension directory""" import logging import os from pathlib import Path from shutil import copy from collections import OrderedDict as odict import importlib_metadata import importlib_resources # Name of the subdirectory BUNDLE_DIRECTORY = "bundle" logger = logging.getLogger(__name__) # Make a directory for extensions and set it as an environment variable # to be picked up by webpack. extensions_relative_path = Path("client", "app", "extensions") extensions_directory = Path(__file__).parent.parent / extensions_relative_path if not extensions_directory.exists(): extensions_directory.mkdir() os.environ["EXTENSIONS_DIRECTORY"] = str(extensions_relative_path) def entry_point_module(entry_point): """Returns the dotted module path for the given entry point""" return entry_point.pattern.match(entry_point.value).group("module") def load_bundles(): """"Load bundles as defined in Redash extensions. The bundle entry point can be defined as a dotted path to a module or a callable, but it won't be called but just used as a means to find the files under its file system path. The name of the directory it looks for files in is "bundle". So a Python package with an extension bundle could look like this:: my_extensions/ ├── __init__.py └── wide_footer ├── __init__.py └── bundle ├── extension.js └── styles.css and would then need to register the bundle with an entry point under the "redash.bundles" group, e.g. in your setup.py:: setup( # ... entry_points={ "redash.bundles": [ "wide_footer = my_extensions.wide_footer", ] # ... }, # ... ) """ bundles = odict() for entry_point in importlib_metadata.entry_points().get("redash.bundles", []): logger.info('Loading Redash bundle "%s".', entry_point.name) module = entry_point_module(entry_point) # Try to get a list of bundle files try: bundle_dir = importlib_resources.files(module).joinpath(BUNDLE_DIRECTORY) except (ImportError, TypeError): # Module isn't a package, so can't have a subdirectory/-package logger.error( 'Redash bundle module "%s" could not be imported: "%s"', entry_point.name, module, ) continue if not bundle_dir.is_dir(): logger.error( 'Redash bundle directory "%s" could not be found or is not a directory: "%s"', entry_point.name, bundle_dir, ) continue bundles[entry_point.name] = list(bundle_dir.rglob("*")) return bundles bundles = load_bundles().items() if bundles: print("Number of extension bundles found: {}".format(len(bundles))) else: print("No extension bundles found.") for bundle_name, paths in bundles: # Shortcut in case not paths were found for the bundle if not paths: print('No paths found for bundle "{}".'.format(bundle_name)) continue # The destination for the bundle files with the entry point name as the subdirectory destination = Path(extensions_directory, bundle_name) if not destination.exists(): destination.mkdir() # Copy the bundle directory from the module to its destination. print('Copying "{}" bundle to {}:'.format(bundle_name, destination.resolve())) for src_path in paths: dest_path = destination / src_path.name print(" - {} -> {}".format(src_path, dest_path)) copy(str(src_path), str(dest_path))