#!/usr/bin/env python
# -*- coding: UTF-8 -*-
""" Module __init__.py: This module acts to import all files in subdirectories
to sys.modules """
import inspect
import os
import logging
import traceback
import sys
from stkMariTools.lib.ui_utils import MariToolsMenuItem
# Use importlib if detected Python 2.7 or later
if sys.version_info >= (2,7):
_use_legacy_libraries = False
import importlib
else:
_use_legacy_libraries = True
[docs]def import_submodules():
"""
This method will import all submodules from the ./tools folder into
the module namespace. It will also attempt to load and register any
valid Mari Tools plugins that are a subclass of ``MariToolsMenuItem``.
:return: ``None``
"""
logger = logging.getLogger(__name__)
# Grab all modules in subdirectories recursively
modules = []
# Normalize the base path
basePath = os.path.dirname(os.path.abspath(__file__))
logger.debug('Searching directory:\n{0}\nrecursively for modules...\n'
.format(basePath))
for root, dirs, filenames in os.walk(basePath):
for filename in filenames:
# Only grab .py files and not files like __init__.py
if filename.endswith('.py') and not filename.startswith('__'):
# Filter out broken symlinks and directories called 'dir.py/'
if os.path.isfile(os.path.join(root, filename)):
# todo: Check for duplicate filenames and deal with them in the namespace
modules.append((filename, os.path.join(root, filename)))
# todo: check if importing into __all__ is really needed
# Define all module names to be in the package namespace
# __all__ = [os.path.basename(module[0])[:-3] for module in modules]
#
# logger.debug('Successfully set the following modules to {0} namespace: \n{1}\n'
# .format(__name__, __all__))
for module in modules:
try:
logger.info('Inspecting module: {0} ...'.format(module[0]))
# Format module namespaces for absolute import
parent_module_path = os.path.abspath(os.path.dirname(module[1]))
parent_module_namespace = parent_module_path.split('stkMariTools')[-1].split(os.path.sep)
parent_module_namespace[0] = 'stkMariTools'
parent_module_namespace = '.'.join(parent_module_namespace)
module_namespace = module[1].split('stkMariTools')[-1].split(os.path.sep)
module_namespace[0] = 'stkMariTools'
module_namespace[-1] = os.path.splitext(module_namespace[-1])[0]
module_namespace = '.'.join(module_namespace)
logger.debug('Formatted parent namespace as: {0} and module namespace as: {1} ...'
.format(parent_module_namespace, module_namespace))
# Attempt to import the package
# If using older than Python 2.7, use legacy import method to handle
# the absolute import, otherwise use newer importlib module
if _use_legacy_libraries:
try:
# todo: check if using importlib or __import__ is ultimately the best solution here
# Use __import__ over imp.load_source because imp does not
# automatically import parent packages, it only creates the
# parent namespace objects; submodules will then fail to
# import packages that rely on other submodules
loaded_submodule = __import__(module_namespace, fromlist=[''])
except ImportError:
logger.error('### Failed to import module!!!\n{0}'
.format(traceback.print_exc()))
continue
else:
try:
importlib.import_module(
name=parent_module_namespace, package=module[1]
)
loaded_submodule = importlib.import_module(
name=module_namespace, package=module[1]
)
except ImportError:
logger.error('### Failed to import module!!!\n{0}'
.format(traceback.print_exc()))
continue
# Get all class members from imported module in order to check against
class_members = inspect.getmembers(loaded_submodule, inspect.isclass)
# Filter out class members which are not defined within the module itself
class_members = filter(
lambda x:inspect.getmodule(x[1])==loaded_submodule,
class_members
)
# Check if valid type of subclass
for class_member in class_members:
# Check if the class member is a valid subclass and if it is
# actually defined within the module
if not issubclass(class_member[1], MariToolsMenuItem):
# Skip this class member
continue
# Get the Method Resolution Order (shows class inheritance)
base_class = inspect.getmro(class_member[1])[-2]
if base_class == MariToolsMenuItem:
logger.debug('Valid base class for: {0} detected, attempting '
'to register plugin...'
.format(module[0]))
assert isinstance(class_member, tuple), \
'### {0} is an invalid tuple object!!!'
# Create a new instance from the class type
plugin_instance = class_member[1]()
# Call the pre-defined function in order to implement the plugin
try:
plugin_instance.registerMariToolsPlugin()
logger.info('Successfully registered the following menuItem plugin: '
'{0}!\n\n------\n\n'
.format(module[0]))
except AttributeError:
logger.error('### {0} does not have the registerMariToolsPlugin() '
'method implemented!!!\n{1}'
.format(traceback.print_exc()))
except RuntimeWarning:
logger.warning('# Absolute import was handled incorrectly!\n{0}'
.format(traceback.print_exc()))
continue
except ImportError:
logger.error('### Could not find module!!!\n{0}'
.format(traceback.print_exc()))
continue
except AttributeError:
logger.debug('Module does not have required registerMariToolsPlugin() '
'method implemented, skipping...\n{0}\n{1}'
.format(module[1], traceback.print_exc()))
continue
except Exception:
logger.error('### Importing tools modules failed!!!\n{0}'
.format(traceback.print_exc()))
continue