Merge pull request #3731 from pypeclub/feature/module_interfaces_cleanup

General: Module interfaces cleanup
This commit is contained in:
Jakub Trllo 2022-08-26 11:33:00 +02:00 committed by GitHub
commit d0245ee23f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 111 additions and 123 deletions

View file

@ -950,6 +950,63 @@ class ApplicationLaunchContext:
) )
self.kwargs["env"] = value self.kwargs["env"] = value
def _collect_addons_launch_hook_paths(self):
"""Helper to collect application launch hooks from addons.
Module have to have implemented 'get_launch_hook_paths' method which
can expect appliction as argument or nothing.
Returns:
List[str]: Paths to launch hook directories.
"""
expected_types = (list, tuple, set)
output = []
for module in self.modules_manager.get_enabled_modules():
# Skip module if does not have implemented 'get_launch_hook_paths'
func = getattr(module, "get_launch_hook_paths", None)
if func is None:
continue
func = module.get_launch_hook_paths
if hasattr(inspect, "signature"):
sig = inspect.signature(func)
expect_args = len(sig.parameters) > 0
else:
expect_args = len(inspect.getargspec(func)[0]) > 0
# Pass application argument if method expect it.
try:
if expect_args:
hook_paths = func(self.application)
else:
hook_paths = func()
except Exception:
self.log.warning(
"Failed to call 'get_launch_hook_paths'",
exc_info=True
)
continue
if not hook_paths:
continue
# Convert string to list
if isinstance(hook_paths, six.string_types):
hook_paths = [hook_paths]
# Skip invalid types
if not isinstance(hook_paths, expected_types):
self.log.warning((
"Result of `get_launch_hook_paths`"
" has invalid type {}. Expected {}"
).format(type(hook_paths), expected_types))
continue
output.extend(hook_paths)
return output
def paths_to_launch_hooks(self): def paths_to_launch_hooks(self):
"""Directory paths where to look for launch hooks.""" """Directory paths where to look for launch hooks."""
# This method has potential to be part of application manager (maybe). # This method has potential to be part of application manager (maybe).
@ -983,9 +1040,7 @@ class ApplicationLaunchContext:
paths.append(path) paths.append(path)
# Load modules paths # Load modules paths
paths.extend( paths.extend(self._collect_addons_launch_hook_paths())
self.modules_manager.collect_launch_hook_paths(self.application)
)
return paths return paths

View file

@ -2,7 +2,6 @@
from .base import ( from .base import (
OpenPypeModule, OpenPypeModule,
OpenPypeAddOn, OpenPypeAddOn,
OpenPypeInterface,
load_modules, load_modules,
@ -20,7 +19,6 @@ from .base import (
__all__ = ( __all__ = (
"OpenPypeModule", "OpenPypeModule",
"OpenPypeAddOn", "OpenPypeAddOn",
"OpenPypeInterface",
"load_modules", "load_modules",

View file

@ -32,6 +32,14 @@ from openpype.lib import (
import_module_from_dirpath import_module_from_dirpath
) )
from .interfaces import (
OpenPypeInterface,
IPluginPaths,
IHostModule,
ITrayModule,
ITrayService
)
# Files that will be always ignored on modules import # Files that will be always ignored on modules import
IGNORED_FILENAMES = ( IGNORED_FILENAMES = (
"__pycache__", "__pycache__",
@ -389,29 +397,7 @@ def _load_modules():
log.error(msg, exc_info=True) log.error(msg, exc_info=True)
class _OpenPypeInterfaceMeta(ABCMeta):
"""OpenPypeInterface meta class to print proper string."""
def __str__(self):
return "<'OpenPypeInterface.{}'>".format(self.__name__)
def __repr__(self):
return str(self)
@six.add_metaclass(_OpenPypeInterfaceMeta)
class OpenPypeInterface:
"""Base class of Interface that can be used as Mixin with abstract parts.
This is way how OpenPype module or addon can tell that has implementation
for specific part or for other module/addon.
Child classes of OpenPypeInterface may be used as mixin in different
OpenPype modules which means they have to have implemented methods defined
in the interface. By default interface does not have any abstract parts.
"""
pass
@six.add_metaclass(ABCMeta) @six.add_metaclass(ABCMeta)
@ -747,8 +733,6 @@ class ModulesManager:
and "actions" each containing list of paths. and "actions" each containing list of paths.
""" """
# Output structure # Output structure
from openpype_interfaces import IPluginPaths
output = { output = {
"publish": [], "publish": [],
"create": [], "create": [],
@ -805,8 +789,6 @@ class ModulesManager:
list: List of creator plugin paths. list: List of creator plugin paths.
""" """
# Output structure # Output structure
from openpype_interfaces import IPluginPaths
output = [] output = []
for module in self.get_enabled_modules(): for module in self.get_enabled_modules():
# Skip module that do not inherit from `IPluginPaths` # Skip module that do not inherit from `IPluginPaths`
@ -821,68 +803,6 @@ class ModulesManager:
output.extend(paths) output.extend(paths)
return output return output
def collect_launch_hook_paths(self, app):
"""Helper to collect application launch hooks.
It used to be based on 'ILaunchHookPaths' which is not true anymore.
Module just have to have implemented 'get_launch_hook_paths' method.
Args:
app (Application): Application object which can be used for
filtering of which launch hook paths are returned.
Returns:
list: Paths to launch hook directories.
"""
str_type = type("")
expected_types = (list, tuple, set)
output = []
for module in self.get_enabled_modules():
# Skip module if does not have implemented 'get_launch_hook_paths'
func = getattr(module, "get_launch_hook_paths", None)
if func is None:
continue
func = module.get_launch_hook_paths
if hasattr(inspect, "signature"):
sig = inspect.signature(func)
expect_args = len(sig.parameters) > 0
else:
expect_args = len(inspect.getargspec(func)[0]) > 0
# Pass application argument if method expect it.
try:
if expect_args:
hook_paths = func(app)
else:
hook_paths = func()
except Exception:
self.log.warning(
"Failed to call 'get_launch_hook_paths'",
exc_info=True
)
continue
if not hook_paths:
continue
# Convert string to list
if isinstance(hook_paths, str_type):
hook_paths = [hook_paths]
# Skip invalid types
if not isinstance(hook_paths, expected_types):
self.log.warning((
"Result of `get_launch_hook_paths`"
" has invalid type {}. Expected {}"
).format(type(hook_paths), expected_types))
continue
output.extend(hook_paths)
return output
def get_host_module(self, host_name): def get_host_module(self, host_name):
"""Find host module by host name. """Find host module by host name.
@ -895,8 +815,6 @@ class ModulesManager:
host name set to passed 'host_name'. host name set to passed 'host_name'.
""" """
from openpype_interfaces import IHostModule
for module in self.get_enabled_modules(): for module in self.get_enabled_modules():
if ( if (
isinstance(module, IHostModule) isinstance(module, IHostModule)
@ -913,8 +831,6 @@ class ModulesManager:
inheriting 'IHostModule'. inheriting 'IHostModule'.
""" """
from openpype_interfaces import IHostModule
host_names = { host_names = {
module.host_name module.host_name
for module in self.get_enabled_modules() for module in self.get_enabled_modules()
@ -1096,8 +1012,6 @@ class TrayModulesManager(ModulesManager):
self.tray_menu(tray_menu) self.tray_menu(tray_menu)
def get_enabled_tray_modules(self): def get_enabled_tray_modules(self):
from openpype_interfaces import ITrayModule
output = [] output = []
for module in self.modules: for module in self.modules:
if module.enabled and isinstance(module, ITrayModule): if module.enabled and isinstance(module, ITrayModule):
@ -1173,8 +1087,6 @@ class TrayModulesManager(ModulesManager):
self._report["Tray menu"] = report self._report["Tray menu"] = report
def start_modules(self): def start_modules(self):
from openpype_interfaces import ITrayService
report = {} report = {}
time_start = time.time() time_start = time.time()
prev_start_time = time_start prev_start_time = time_start

View file

@ -9,7 +9,6 @@ from openpype.modules import OpenPypeModule
from openpype_interfaces import ( from openpype_interfaces import (
ITrayModule, ITrayModule,
IPluginPaths, IPluginPaths,
ILaunchHookPaths,
ISettingsChangeListener ISettingsChangeListener
) )
from openpype.settings import SaveWarningExc from openpype.settings import SaveWarningExc
@ -21,7 +20,6 @@ class FtrackModule(
OpenPypeModule, OpenPypeModule,
ITrayModule, ITrayModule,
IPluginPaths, IPluginPaths,
ILaunchHookPaths,
ISettingsChangeListener ISettingsChangeListener
): ):
name = "ftrack" name = "ftrack"
@ -85,7 +83,8 @@ class FtrackModule(
} }
def get_launch_hook_paths(self): def get_launch_hook_paths(self):
"""Implementation of `ILaunchHookPaths`.""" """Implementation for applications launch hooks."""
return os.path.join(FTRACK_MODULE_DIR, "launch_hooks") return os.path.join(FTRACK_MODULE_DIR, "launch_hooks")
def modify_application_launch_arguments(self, application, env): def modify_application_launch_arguments(self, application, env):

View file

@ -1,8 +1,33 @@
from abc import abstractmethod, abstractproperty from abc import ABCMeta, abstractmethod, abstractproperty
import six
from openpype import resources from openpype import resources
from openpype.modules import OpenPypeInterface
class _OpenPypeInterfaceMeta(ABCMeta):
"""OpenPypeInterface meta class to print proper string."""
def __str__(self):
return "<'OpenPypeInterface.{}'>".format(self.__name__)
def __repr__(self):
return str(self)
@six.add_metaclass(_OpenPypeInterfaceMeta)
class OpenPypeInterface:
"""Base class of Interface that can be used as Mixin with abstract parts.
This is way how OpenPype module or addon can tell OpenPype that contain
implementation for specific functionality.
Child classes of OpenPypeInterface may be used as mixin in different
OpenPype modules which means they have to have implemented methods defined
in the interface. By default interface does not have any abstract parts.
"""
pass
class IPluginPaths(OpenPypeInterface): class IPluginPaths(OpenPypeInterface):
@ -56,6 +81,13 @@ class ILaunchHookPaths(OpenPypeInterface):
Expected result is list of paths. Expected result is list of paths.
["path/to/launch_hooks_dir"] ["path/to/launch_hooks_dir"]
Deprecated:
This interface is not needed since OpenPype 3.14.*. Addon just have to
implement 'get_launch_hook_paths' which can expect Application object
or nothing as argument.
Interface class will be removed after 3.16.*.
""" """
@abstractmethod @abstractmethod

View file

@ -3,7 +3,6 @@ import os
from openpype_interfaces import ( from openpype_interfaces import (
ITrayModule, ITrayModule,
IPluginPaths, IPluginPaths,
ILaunchHookPaths,
) )
from openpype.modules import OpenPypeModule from openpype.modules import OpenPypeModule
@ -11,9 +10,7 @@ from openpype.modules import OpenPypeModule
SHOTGRID_MODULE_DIR = os.path.dirname(os.path.abspath(__file__)) SHOTGRID_MODULE_DIR = os.path.dirname(os.path.abspath(__file__))
class ShotgridModule( class ShotgridModule(OpenPypeModule, ITrayModule, IPluginPaths):
OpenPypeModule, ITrayModule, IPluginPaths, ILaunchHookPaths
):
leecher_manager_url = None leecher_manager_url = None
name = "shotgrid" name = "shotgrid"
enabled = False enabled = False

View file

@ -1,14 +1,11 @@
import os import os
from openpype.modules import OpenPypeModule from openpype.modules import OpenPypeModule
from openpype_interfaces import ( from openpype.modules.interfaces import IPluginPaths
IPluginPaths,
ILaunchHookPaths
)
SLACK_MODULE_DIR = os.path.dirname(os.path.abspath(__file__)) SLACK_MODULE_DIR = os.path.dirname(os.path.abspath(__file__))
class SlackIntegrationModule(OpenPypeModule, IPluginPaths, ILaunchHookPaths): class SlackIntegrationModule(OpenPypeModule, IPluginPaths):
"""Allows sending notification to Slack channels during publishing.""" """Allows sending notification to Slack channels during publishing."""
name = "slack" name = "slack"
@ -18,7 +15,8 @@ class SlackIntegrationModule(OpenPypeModule, IPluginPaths, ILaunchHookPaths):
self.enabled = slack_settings["enabled"] self.enabled = slack_settings["enabled"]
def get_launch_hook_paths(self): def get_launch_hook_paths(self):
"""Implementation of `ILaunchHookPaths`.""" """Implementation for applications launch hooks."""
return os.path.join(SLACK_MODULE_DIR, "launch_hooks") return os.path.join(SLACK_MODULE_DIR, "launch_hooks")
def get_plugin_paths(self): def get_plugin_paths(self):

View file

@ -6,7 +6,6 @@ from openpype.client import get_asset_by_name
from openpype.modules import OpenPypeModule from openpype.modules import OpenPypeModule
from openpype_interfaces import ( from openpype_interfaces import (
ITrayService, ITrayService,
ILaunchHookPaths,
IPluginPaths IPluginPaths
) )
from openpype.lib.events import register_event_callback from openpype.lib.events import register_event_callback
@ -79,7 +78,6 @@ class ExampleTimersManagerConnector:
class TimersManager( class TimersManager(
OpenPypeModule, OpenPypeModule,
ITrayService, ITrayService,
ILaunchHookPaths,
IPluginPaths IPluginPaths
): ):
""" Handles about Timers. """ Handles about Timers.
@ -185,12 +183,11 @@ class TimersManager(
) )
def get_launch_hook_paths(self): def get_launch_hook_paths(self):
"""Implementation of `ILaunchHookPaths`.""" """Implementation for applications launch hooks."""
return os.path.join( return [
TIMER_MODULE_DIR, os.path.join(TIMER_MODULE_DIR, "launch_hooks")
"launch_hooks" ]
)
def get_plugin_paths(self): def get_plugin_paths(self):
"""Implementation of `IPluginPaths`.""" """Implementation of `IPluginPaths`."""