Don't ignore ImportErrors raised when loading a plugin. Fixes #4805

This commit is contained in:
Glenn Matthews 2020-07-01 15:23:38 -04:00
parent 43d610405f
commit f807d3a024
3 changed files with 66 additions and 32 deletions

View File

@ -1,12 +1,13 @@
import collections
import importlib
import inspect
import sys
from packaging import version
from django.apps import AppConfig
from django.conf import settings
from django.core.exceptions import ImproperlyConfigured
from django.template.loader import get_template
from django.utils.module_loading import import_string
from extras.registry import registry
from utilities.choices import ButtonColorChoices
@ -60,18 +61,26 @@ class PluginConfig(AppConfig):
def ready(self):
# Register template content
try:
template_extensions = import_string(f"{self.__module__}.{self.template_extensions}")
register_template_extensions(template_extensions)
except ImportError:
pass
module, attr = f"{self.__module__}.{self.template_extensions}".rsplit('.', 1)
spec = importlib.util.find_spec(module)
if spec is not None:
template_content = importlib.util.module_from_spec(spec)
sys.modules[module] = template_content
spec.loader.exec_module(template_content)
if hasattr(template_content, attr):
template_extensions = getattr(template_content, attr)
register_template_extensions(template_extensions)
# Register navigation menu items (if defined)
try:
menu_items = import_string(f"{self.__module__}.{self.menu_items}")
register_menu_items(self.verbose_name, menu_items)
except ImportError:
pass
module, attr = f"{self.__module__}.{self.menu_items}".rsplit('.', 1)
spec = importlib.util.find_spec(module)
if spec is not None:
navigation = importlib.util.module_from_spec(spec)
sys.modules[module] = navigation
spec.loader.exec_module(navigation)
if hasattr(navigation, attr):
menu_items = getattr(navigation, attr)
register_menu_items(self.verbose_name, menu_items)
@classmethod
def validate(cls, user_config):

View File

@ -1,9 +1,11 @@
import importlib
import sys
from django.apps import apps
from django.conf import settings
from django.conf.urls import include
from django.contrib.admin.views.decorators import staff_member_required
from django.urls import path
from django.utils.module_loading import import_string
from . import views
@ -24,19 +26,29 @@ for plugin_path in settings.PLUGINS:
base_url = getattr(app, 'base_url') or app.label
# Check if the plugin specifies any base URLs
try:
urlpatterns = import_string(f"{plugin_path}.urls.urlpatterns")
plugin_patterns.append(
path(f"{base_url}/", include((urlpatterns, app.label)))
)
except ImportError:
pass
spec = importlib.util.find_spec(f"{plugin_path}.urls")
if spec is not None:
# The plugin has a .urls module - import it
urls = importlib.util.module_from_spec(spec)
sys.modules[f"{plugin_path}.urls"] = urls
spec.loader.exec_module(urls)
if hasattr(urls, "urlpatterns"):
urlpatterns = urls.urlpatterns
plugin_patterns.append(
path(f"{base_url}/", include((urlpatterns, app.label)))
)
# Check if the plugin specifies any API URLs
try:
urlpatterns = import_string(f"{plugin_path}.api.urls.urlpatterns")
plugin_api_patterns.append(
path(f"{base_url}/", include((urlpatterns, f"{app.label}-api")))
)
except ImportError:
pass
spec = importlib.util.find_spec(f"{plugin_path}.api")
if spec is not None:
spec = importlib.util.find_spec(f"{plugin_path}.api.urls")
if spec is not None:
# The plugin has a .api.urls module - import it
api_urls = importlib.util.module_from_spec(spec)
sys.modules[f"{plugin_path}.api.urls"] = api_urls
spec.loader.exec_module(api_urls)
if hasattr(api_urls, "urlpatterns"):
urlpatterns = api_urls.urlpatterns
plugin_api_patterns.append(
path(f"{base_url}/", include((urlpatterns, f"{app.label}-api")))
)

View File

@ -1,10 +1,11 @@
from collections import OrderedDict
import importlib
import sys
from django.apps import apps
from django.conf import settings
from django.shortcuts import render
from django.urls.exceptions import NoReverseMatch
from django.utils.module_loading import import_string
from django.views.generic import View
from rest_framework import permissions
from rest_framework.response import Response
@ -60,11 +61,23 @@ class PluginsAPIRootView(APIView):
@staticmethod
def _get_plugin_entry(plugin, app_config, request, format):
try:
api_app_name = import_string(f"{plugin}.api.urls.app_name")
except (ImportError, ModuleNotFoundError):
# Plugin does not expose an API
# Check if the plugin specifies any API URLs
spec = importlib.util.find_spec(f"{plugin}.api")
if spec is None:
# There is no plugin.api module
return None
spec = importlib.util.find_spec(f"{plugin}.api.urls")
if spec is None:
# There is no plugin.api.urls module
return None
# The plugin has a .api.urls module - import it
api_urls = importlib.util.module_from_spec(spec)
sys.modules[f"{plugin}.api.urls"] = api_urls
spec.loader.exec_module(api_urls)
if not hasattr(api_urls, "app_name"):
# The plugin api.urls does not declare an app_name string
return None
api_app_name = api_urls.app_name
try:
entry = (getattr(app_config, 'base_url', app_config.label), reverse(
@ -73,7 +86,7 @@ class PluginsAPIRootView(APIView):
format=format
))
except NoReverseMatch:
# The plugin does not include an api-root
# The plugin does not include an api-root url
entry = None
return entry