mirror of
https://github.com/ynput/ayon-core.git
synced 2026-01-01 16:34:53 +01:00
208 lines
6.6 KiB
Python
208 lines
6.6 KiB
Python
import os
|
|
import inspect
|
|
from openpype.lib.python_module_tools import (
|
|
modules_from_path,
|
|
classes_from_module,
|
|
)
|
|
|
|
|
|
class DiscoverResult:
|
|
"""Hold result of publish plugins discovery.
|
|
|
|
Stores discovered plugins duplicated plugins and file paths which
|
|
crashed on execution of file.
|
|
"""
|
|
|
|
def __init__(self):
|
|
self.plugins = []
|
|
self.crashed_file_paths = {}
|
|
self.duplicated_plugins = []
|
|
self.abstract_plugins = []
|
|
self.ignored_plugins = set()
|
|
|
|
def __iter__(self):
|
|
for plugin in self.plugins:
|
|
yield plugin
|
|
|
|
def __getitem__(self, item):
|
|
return self.plugins[item]
|
|
|
|
def __setitem__(self, item, value):
|
|
self.plugins[item] = value
|
|
|
|
|
|
class PluginDiscoverContext(object):
|
|
"""Store and discover registered types nad registered paths to types.
|
|
|
|
Keeps in memory all registered types and their paths. Paths are dynamically
|
|
loaded on discover so different discover calls won't return the same
|
|
class objects even if were loaded from same file.
|
|
"""
|
|
|
|
def __init__(self):
|
|
self._registered_plugins = {}
|
|
self._registered_plugin_paths = {}
|
|
self._last_discovered_plugins = {}
|
|
|
|
def get_last_discovered_plugins(self, superclass):
|
|
return self._last_discovered_plugins.get(superclass)
|
|
|
|
def discover(
|
|
self, superclass, allow_duplicates=True, ignore_classes=None
|
|
):
|
|
"""Find and return subclasses of `superclass`
|
|
|
|
Args:
|
|
superclass (type): Class which determines discovered subclasses.
|
|
allow_duplicates (bool): Validate class name duplications.
|
|
ignore_classes (list): List of classes that will be ignored
|
|
and not added to result.
|
|
|
|
Returns:
|
|
DiscoverResult: Object holding succesfully discovered plugins,
|
|
ignored plugins, plugins with missing abstract implementation
|
|
and duplicated plugin.
|
|
"""
|
|
|
|
if not ignore_classes:
|
|
ignore_classes = []
|
|
|
|
result = DiscoverResult()
|
|
plugin_names = set()
|
|
registered_classes = self._registered_plugins.get(superclass) or []
|
|
registered_paths = self._registered_plugin_paths.get(superclass) or []
|
|
for cls in registered_classes:
|
|
if cls is superclass or cls in ignore_classes:
|
|
result.ignored_plugins.add(cls)
|
|
continue
|
|
|
|
if inspect.isabstract(cls):
|
|
result.abstract_plugins.append(cls)
|
|
continue
|
|
|
|
class_name = cls.__name__
|
|
if class_name in plugin_names:
|
|
result.duplicated_plugins.append(cls)
|
|
continue
|
|
plugin_names.add(class_name)
|
|
result.plugins.append(cls)
|
|
|
|
# Include plug-ins from registered paths
|
|
for path in registered_paths:
|
|
modules, crashed = modules_from_path(path)
|
|
for item in crashed:
|
|
filepath, exc_info = item
|
|
result.crashed_file_paths[filepath] = exc_info
|
|
|
|
for item in modules:
|
|
filepath, module = item
|
|
for cls in classes_from_module(superclass, module):
|
|
if cls is superclass or cls in ignore_classes:
|
|
result.ignored_plugins.add(cls)
|
|
continue
|
|
|
|
if inspect.isabstract(cls):
|
|
result.abstract_plugins.append(cls)
|
|
continue
|
|
|
|
if not allow_duplicates:
|
|
class_name = cls.__name__
|
|
if class_name in plugin_names:
|
|
result.duplicated_plugins.append(cls)
|
|
continue
|
|
plugin_names.add(class_name)
|
|
|
|
result.plugins.append(cls)
|
|
|
|
self._last_discovered_plugins[superclass] = list(
|
|
result.plugins
|
|
)
|
|
return result
|
|
|
|
def register_plugin(self, superclass, cls):
|
|
"""Register an individual `obj` of type `superclass`
|
|
|
|
Arguments:
|
|
superclass (type): Superclass of plug-in
|
|
cls (object): Subclass of `superclass`
|
|
"""
|
|
|
|
if superclass not in self._registered_plugins:
|
|
self._registered_plugins[superclass] = list()
|
|
|
|
if cls not in self._registered_plugins[superclass]:
|
|
self._registered_plugins[superclass].append(cls)
|
|
|
|
def register_plugin_path(self, superclass, path):
|
|
"""Register a directory of one or more plug-ins
|
|
|
|
Arguments:
|
|
superclass (type): Superclass of plug-ins to look for during discovery
|
|
path (str): Absolute path to directory in which to discover plug-ins
|
|
|
|
"""
|
|
|
|
if superclass not in self._registered_plugin_paths:
|
|
self._registered_plugin_paths[superclass] = list()
|
|
|
|
path = os.path.normpath(path)
|
|
if path not in self._registered_plugin_paths[superclass]:
|
|
self._registered_plugin_paths[superclass].append(path)
|
|
|
|
def registered_plugin_paths(self):
|
|
"""Return all currently registered plug-in paths"""
|
|
# Prohibit editing in-place
|
|
duplicate = {
|
|
superclass: paths[:]
|
|
for superclass, paths in self._registered_plugin_paths.items()
|
|
}
|
|
return duplicate
|
|
|
|
def deregister_plugin(self, superclass, plugin):
|
|
"""Oppsite of `register_plugin()`"""
|
|
if superclass in self._registered_plugins:
|
|
self._registered_plugins[superclass].remove(plugin)
|
|
|
|
def deregister_plugin_path(self, superclass, path):
|
|
"""Oppsite of `register_plugin_path()`"""
|
|
self._registered_plugin_paths[superclass].remove(path)
|
|
|
|
|
|
class GlobalDiscover:
|
|
_context = None
|
|
|
|
@classmethod
|
|
def get_context(cls):
|
|
if cls._context is None:
|
|
cls._context = PluginDiscoverContext()
|
|
return cls._context
|
|
|
|
|
|
def discover(superclass, allow_duplicates=True):
|
|
context = GlobalDiscover.get_context()
|
|
return context.discover(superclass, allow_duplicates)
|
|
|
|
|
|
def get_last_discovered_plugins(superclass):
|
|
context = GlobalDiscover.get_context()
|
|
return context.get_last_discovered_plugins(superclass)
|
|
|
|
|
|
def register_plugin(superclass, cls):
|
|
context = GlobalDiscover.get_context()
|
|
context.register_plugin(superclass, cls)
|
|
|
|
|
|
def register_plugin_path(superclass, path):
|
|
context = GlobalDiscover.get_context()
|
|
context.register_plugin_path(superclass, path)
|
|
|
|
|
|
def deregister_plugin(superclass, cls):
|
|
context = GlobalDiscover.get_context()
|
|
context.deregister_plugin(superclass, cls)
|
|
|
|
|
|
def deregister_plugin_path(superclass, path):
|
|
context = GlobalDiscover.get_context()
|
|
context.deregister_plugin_path(superclass, path)
|