Fix bundle-extensions script to work on recent importlib-resources. (#5050)

Also adds a test case for running the script.
This commit is contained in:
Jannis Leidel
2020-07-16 22:05:22 +02:00
committed by GitHub
parent cb97364771
commit 41a691328a
12 changed files with 100 additions and 51 deletions

View File

@@ -6,8 +6,8 @@ from pathlib import Path
from shutil import copy
from collections import OrderedDict as odict
from importlib_metadata import entry_points
from importlib_resources import contents, is_resource, path
import importlib_metadata
import importlib_resources
# Name of the subdirectory
BUNDLE_DIRECTORY = "bundle"
@@ -25,18 +25,6 @@ if not extensions_directory.exists():
os.environ["EXTENSIONS_DIRECTORY"] = str(extensions_relative_path)
def resource_isdir(module, resource):
"""Whether a given resource is a directory in the given module
https://importlib-resources.readthedocs.io/en/latest/migration.html#pkg-resources-resource-isdir
"""
try:
return resource in contents(module) and not is_resource(module, resource)
except (ImportError, TypeError):
# module isn't a package, so can't have a subdirectory/-package
return False
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")
@@ -77,18 +65,28 @@ def load_bundles():
"""
bundles = odict()
for entry_point in entry_points().get("redash.bundles", []):
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
if not resource_isdir(module, BUNDLE_DIRECTORY):
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 directory "%s" could not be found.', entry_point.name
'Redash bundle module "%s" could not be imported: "%s"',
entry_point.name,
module,
)
continue
with path(module, BUNDLE_DIRECTORY) as bundle_dir:
bundles[entry_point.name] = list(bundle_dir.rglob("*"))
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

View File

@@ -0,0 +1,2 @@
dist
build

View File

@@ -0,0 +1,2 @@
include README.md
recursive-include redash_dummy *.jsx

View File

@@ -1,6 +1,6 @@
Metadata-Version: 1.0
Name: redash-dummy
Version: 0.1
Version: 0.2
Summary: Redash extensions for testing
Home-page: UNKNOWN
Author: Redash authors

View File

@@ -1,8 +1,12 @@
MANIFEST.in
README.md
redash_dummy.py
setup.py
redash_dummy/__init__.py
redash_dummy/extension.py
redash_dummy/jobs.py
redash_dummy.egg-info/PKG-INFO
redash_dummy.egg-info/SOURCES.txt
redash_dummy.egg-info/dependency_links.txt
redash_dummy.egg-info/entry_points.txt
redash_dummy.egg-info/top_level.txt
redash_dummy.egg-info/top_level.txt
redash_dummy/bundle/WideFooter.jsx

View File

@@ -1,10 +1,13 @@
[redash.bundles]
wide_footer = redash_dummy
[redash.extensions]
assertive_extension = redash_dummy:assertive_extension
non_callable_extension = redash_dummy:module_attribute
not_findable_extension = redash_dummy:missing_attribute
assertive_extension = redash_dummy.extension:assertive_extension
non_callable_extension = redash_dummy.extension:module_attribute
not_findable_extension = redash_dummy.extension:missing_attribute
not_importable_extension = missing_extension_module:extension
working_extension = redash_dummy:extension
working_extension = redash_dummy.extension:extension
[redash.periodic_jobs]
dummy_periodic_job = redash_dummy:periodic_job
dummy_periodic_job = redash_dummy.jobs:periodic_job

View File

@@ -0,0 +1,9 @@
import React from "react";
export default function WideFooter() {
return (
<div>
This is a wide footer
</div>
);
}

View File

@@ -0,0 +1,11 @@
module_attribute = "hello!"
def extension(app):
"""This extension will work"""
return "extension loaded"
def assertive_extension(app):
"""This extension won't work"""
assert False

View File

@@ -1,17 +1,5 @@
from datetime import timedelta
module_attribute = "hello!"
def extension(app):
"""This extension will work"""
return "extension loaded"
def assertive_extension(app):
"""This extension won't work"""
assert False
def job_callback():
return "result"

View File

@@ -1,21 +1,25 @@
from setuptools import setup
from setuptools import setup, find_packages
setup(
name="redash-dummy",
version="0.1",
version="0.2",
description="Redash extensions for testing",
author="Redash authors",
license="MIT",
packages=find_packages(),
include_package_data=True,
entry_points={
"redash.extensions": [
"working_extension = redash_dummy:extension",
"non_callable_extension = redash_dummy:module_attribute",
"not_findable_extension = redash_dummy:missing_attribute",
"working_extension = redash_dummy.extension:extension",
"non_callable_extension = redash_dummy.extension:module_attribute",
"not_findable_extension = redash_dummy.extension:missing_attribute",
"not_importable_extension = missing_extension_module:extension",
"assertive_extension = redash_dummy:assertive_extension",
"assertive_extension = redash_dummy.extension:assertive_extension",
],
"redash.periodic_jobs": ["dummy_periodic_job = redash_dummy.jobs:periodic_job"],
"redash.bundles": [
"wide_footer = redash_dummy",
],
"redash.periodic_jobs": ["dummy_periodic_job = redash_dummy:periodic_job"],
},
py_modules=["redash_dummy"],
)

View File

@@ -1,6 +1,8 @@
import logging
import os
import shutil
import subprocess
import sys
from pathlib import Path
from redash import extensions
from redash.tasks import periodic_job_definitions
@@ -8,7 +10,13 @@ from tests import BaseTestCase
logger = logging.getLogger(__name__)
dummy_extension = "redash-dummy"
dummy_path = os.path.join(os.path.dirname(__file__), dummy_extension)
this_dir = Path(__file__).parent.resolve()
app_dir = this_dir.parent.parent
dummy_path = str(this_dir / dummy_extension)
test_bundle = (
app_dir / "client" / "app" / "extensions" / "wide_footer" / "WideFooter.jsx"
)
class TestExtensions(BaseTestCase):
@@ -47,5 +55,25 @@ class TestExtensions(BaseTestCase):
def test_dummy_periodic_task_definitions(self):
jobs = periodic_job_definitions()
from redash_dummy import job_callback
from redash_dummy.jobs import job_callback
self.assertIn(job_callback, [job.get("func", None) for job in jobs])
class TestBundles(BaseTestCase):
@classmethod
def setUpClass(cls):
# Install the redash-dummy package temporarily using pip
# in the user's local site package directory under ~/.local/
subprocess.call(["pip", "install", "--user", dummy_path])
@classmethod
def tearDownClass(cls):
subprocess.call(["pip", "uninstall", "-y", "redash-dummy"])
def test_bundle_extensions(self):
# cleaning up after running bundle-extensions again
self.addCleanup(lambda: shutil.rmtree(test_bundle.parent))
assert not test_bundle.exists()
subprocess.run(str(app_dir / "bin" / "bundle-extensions"), check=True)
assert test_bundle.exists()