1
0
mirror of https://github.com/netbox-community/netbox.git synced 2024-05-10 07:54:54 +00:00

Refactor repeated import code

This commit is contained in:
Glenn Matthews
2020-07-14 17:15:17 -04:00
parent f807d3a024
commit 0fd3c83861
4 changed files with 58 additions and 64 deletions

View File

@ -1,7 +1,5 @@
import collections import collections
import importlib
import inspect import inspect
import sys
from packaging import version from packaging import version
from django.apps import AppConfig from django.apps import AppConfig
@ -12,6 +10,8 @@ from django.template.loader import get_template
from extras.registry import registry from extras.registry import registry
from utilities.choices import ButtonColorChoices from utilities.choices import ButtonColorChoices
from extras.plugins.utils import import_object
# Initialize plugin registry stores # Initialize plugin registry stores
registry['plugin_template_extensions'] = collections.defaultdict(list) registry['plugin_template_extensions'] = collections.defaultdict(list)
@ -61,26 +61,14 @@ class PluginConfig(AppConfig):
def ready(self): def ready(self):
# Register template content # Register template content
module, attr = f"{self.__module__}.{self.template_extensions}".rsplit('.', 1) template_extensions = import_object(f"{self.__module__}.{self.template_extensions}")
spec = importlib.util.find_spec(module) if template_extensions is not None:
if spec is not None: register_template_extensions(template_extensions)
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) # Register navigation menu items (if defined)
module, attr = f"{self.__module__}.{self.menu_items}".rsplit('.', 1) menu_items = import_object(f"{self.__module__}.{self.menu_items}")
spec = importlib.util.find_spec(module) if menu_items is not None:
if spec is not None: register_menu_items(self.verbose_name, menu_items)
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 @classmethod
def validate(cls, user_config): def validate(cls, user_config):

View File

@ -1,12 +1,11 @@
import importlib
import sys
from django.apps import apps from django.apps import apps
from django.conf import settings from django.conf import settings
from django.conf.urls import include from django.conf.urls import include
from django.contrib.admin.views.decorators import staff_member_required from django.contrib.admin.views.decorators import staff_member_required
from django.urls import path from django.urls import path
from extras.plugins.utils import import_object
from . import views from . import views
# Initialize URL base, API, and admin URL patterns for plugins # Initialize URL base, API, and admin URL patterns for plugins
@ -26,29 +25,15 @@ for plugin_path in settings.PLUGINS:
base_url = getattr(app, 'base_url') or app.label base_url = getattr(app, 'base_url') or app.label
# Check if the plugin specifies any base URLs # Check if the plugin specifies any base URLs
spec = importlib.util.find_spec(f"{plugin_path}.urls") urlpatterns = import_object(f"{plugin_path}.urls.urlpatterns")
if spec is not None: if urlpatterns is not None:
# The plugin has a .urls module - import it plugin_patterns.append(
urls = importlib.util.module_from_spec(spec) path(f"{base_url}/", include((urlpatterns, app.label)))
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 # Check if the plugin specifies any API URLs
spec = importlib.util.find_spec(f"{plugin_path}.api") urlpatterns = import_object(f"{plugin_path}.api.urls.urlpatterns")
if spec is not None: if urlpatterns is not None:
spec = importlib.util.find_spec(f"{plugin_path}.api.urls") plugin_api_patterns.append(
if spec is not None: path(f"{base_url}/", include((urlpatterns, f"{app.label}-api")))
# 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

@ -0,0 +1,33 @@
import importlib.util
import sys
def import_object(module_and_object):
"""
Import a specific object from a specific module by name, such as "extras.plugins.utils.import_object".
Returns the imported object, or None if it doesn't exist.
"""
target_module_name, object_name = module_and_object.rsplit('.', 1)
module_hierarchy = target_module_name.split('.')
# Iterate through the module hierarchy, checking for the existence of each successive submodule.
# We have to do this rather than jumping directly to calling find_spec(target_module_name)
# because find_spec will raise a ModuleNotFoundError if any parent module of target_module_name does not exist.
module_name = ""
for module_component in module_hierarchy:
module_name = f"{module_name}.{module_component}" if module_name else module_component
spec = importlib.util.find_spec(module_name)
if spec is None:
# No such module
return None
# Okay, target_module_name exists. Load it if not already loaded
if target_module_name in sys.modules:
module = sys.modules[target_module_name]
else:
module = importlib.util.module_from_spec(spec)
sys.modules[target_module_name] = module
spec.loader.exec_module(module)
return getattr(module, object_name, None)

View File

@ -1,6 +1,4 @@
from collections import OrderedDict from collections import OrderedDict
import importlib
import sys
from django.apps import apps from django.apps import apps
from django.conf import settings from django.conf import settings
@ -12,6 +10,8 @@ from rest_framework.response import Response
from rest_framework.reverse import reverse from rest_framework.reverse import reverse
from rest_framework.views import APIView from rest_framework.views import APIView
from extras.plugins.utils import import_object
class InstalledPluginsAdminView(View): class InstalledPluginsAdminView(View):
""" """
@ -62,22 +62,10 @@ class PluginsAPIRootView(APIView):
@staticmethod @staticmethod
def _get_plugin_entry(plugin, app_config, request, format): def _get_plugin_entry(plugin, app_config, request, format):
# Check if the plugin specifies any API URLs # Check if the plugin specifies any API URLs
spec = importlib.util.find_spec(f"{plugin}.api") api_app_name = import_object(f"{plugin}.api.urls.app_name")
if spec is None: if api_app_name is None:
# There is no plugin.api module # Plugin does not expose an API
return None 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: try:
entry = (getattr(app_config, 'base_url', app_config.label), reverse( entry = (getattr(app_config, 'base_url', app_config.label), reverse(