Meshroom/meshroom/_plugins/base.py
waaake 02b85cb273 [core] Plugins: Introduction of NodePluginManager
The NodePluginManager provides a way for registering and managing node plugins within Meshroom. This also provides a way for other components to interact with the plugins to understand whether a plugin is available or not.
2024-11-28 09:36:36 +05:30

92 lines
3.2 KiB
Python

""" Base functionality for Plugins.
"""
# Types
from typing import List
# STD
from contextlib import contextmanager
import enum
import importlib
import inspect
import logging
import pkgutil
class Status(enum.IntEnum):
""" Enum describing the state of the plugin.
"""
# UNLOADED or NOT Available - Describing that the plugin is not available in the current set of plugins
UNLOADED = -1
# ERRORED describes that the plugin exists but could not be loaded due to errors with the structure
ERRORED = 0
# LOADED describes that the plugin is currently loaded and is fully functional
LOADED = 1
class Pluginator:
""" The common plugin utilities.
"""
@staticmethod
@contextmanager
def add_to_path(_p):
""" A Context Manager to add the provided path to Python's sys.path temporarily.
"""
import sys # pylint: disable=import-outside-toplevel
old_path = sys.path
sys.path = sys.path[:]
sys.path.insert(0, _p)
try:
yield
finally:
sys.path = old_path
@staticmethod
def get(folder, packageName, classType) -> List:
""" Returns Array of Plugin, each holding the plugin and the module it belongs to.
Args:
folder (str): Path to the Directory.
packageName (str): Name of the package to import.
classType (desc.Node | BaseSubmitter): The base type of plugin which is being imported.
"""
pluginTypes = []
errors = []
# temporarily add folder to python path
with Pluginator.add_to_path(folder):
# import node package
package = importlib.import_module(packageName)
packageName = package.packageName if hasattr(package, 'packageName') else package.__name__
packageVersion = getattr(package, "__version__", None)
for importer, pluginName, ispkg in pkgutil.iter_modules(package.__path__):
pluginModuleName = '.' + pluginName
try:
pluginMod = importlib.import_module(pluginModuleName, package=package.__name__)
plugins = [plugin for name, plugin in inspect.getmembers(pluginMod, inspect.isclass)
if plugin.__module__ == '{}.{}'.format(package.__name__, pluginName)
and issubclass(plugin, classType)]
if not plugins:
logging.warning("No class defined in plugin: {}".format(pluginModuleName))
# Update the package name and version on the plugin
for p in plugins:
p.packageName = packageName
p.packageVersion = packageVersion
# Extend all of the plugins
pluginTypes.extend(plugins)
except Exception as exc:
errors.append(' * {}: {}'.format(pluginName, str(exc)))
if errors:
logging.warning('== The following "{package}" plugins could not be loaded ==\n'
'{errorMsg}\n'
.format(package=packageName, errorMsg='\n'.join(errors)))
return pluginTypes