diff --git a/avalon/fusion/pipeline.py b/avalon/fusion/pipeline.py index 1dd9da084..c8c3c4ebe 100644 --- a/avalon/fusion/pipeline.py +++ b/avalon/fusion/pipeline.py @@ -2,10 +2,12 @@ import contextlib import importlib import logging -from pyblish import api as pyblish -from avalon import api as avalon +import pyblish.api + +from .. import api from ..pipeline import AVALON_CONTAINER_ID +from ..lib import find_submodule class CompLogHandler(logging.Handler): @@ -23,32 +25,30 @@ def ls(): assets on disk, it lists assets already loaded in Fusion; once loaded they are called 'containers' + Yields: + dict: container + """ comp = get_current_comp() tools = comp.GetToolList(False, "Loader").values() + + has_metadata_collector = False + config_host = find_submodule(api.registered_config(), "fusion") + if hasattr(config_host, "collect_container_metadata"): + has_metadata_collector = True + for tool in tools: container = parse_container(tool) if container: - # Collect custom data if attribute is present - config = find_host_config(avalon.registered_config()) - if hasattr(config, "collect_container_metadata"): - metadata = config.collect_container_metadata(container) + + if has_metadata_collector: + metadata = config_host.collect_container_metadata(container) container.update(metadata) yield container -def find_host_config(config): - config_name = config.__name__ - try: - config = importlib.import_module(config_name + ".fusion") - except ImportError: - pass - - return config - - def install(config): """Install Fusion-specific functionality of avalon-core. @@ -60,7 +60,7 @@ def install(config): # TODO: Set project # TODO: Install Fusion menu (this is done with config .fu script actually) - pyblish.register_host("fusion") + pyblish.api.register_host("fusion") # Remove all handlers associated with the root logger object, because # that one sometimes logs as "warnings" incorrectly. @@ -75,10 +75,18 @@ def install(config): logger.addHandler(handler) logger.setLevel(logging.DEBUG) - # Trigger install on the config's "fusion" package - config = find_host_config(config) - if hasattr(config, "install"): - config.install() + +def uninstall(config): + """Uninstall Fusion-specific functionality of avalon-core. + + This function is called automatically on calling `api.uninstall()`. + + Args: + config: configuration module + + """ + + pyblish.api.deregister_host("fusion") def imprint_container(tool, diff --git a/avalon/houdini/pipeline.py b/avalon/houdini/pipeline.py index a94df88a4..f2657f978 100644 --- a/avalon/houdini/pipeline.py +++ b/avalon/houdini/pipeline.py @@ -10,8 +10,8 @@ # Local libraries from . import lib -from ..lib import logger -from avalon import api, schema +from ..lib import logger, find_submodule +from .. import api from ..pipeline import AVALON_CONTAINER_ID @@ -39,10 +39,6 @@ def install(config): self._has_been_setup = True - config = find_host_config(config) - if hasattr(config, "install"): - config.install() - def uninstall(config): """Uninstall Houdini-specific functionality of avalon-core. @@ -54,27 +50,11 @@ def uninstall(config): """ - config = find_host_config(config) - if hasattr(config, "uninstall"): - config.uninstall() - pyblish.api.deregister_host("hython") pyblish.api.deregister_host("hpython") pyblish.api.deregister_host("houdini") -def find_host_config(config): - config_name = config.__name__ - try: - config = importlib.import_module(config_name + ".houdini") - except ImportError as exc: - if str(exc) != "No module name {}".format(config_name + ".houdini"): - raise - config = None - - return config - - def get_main_window(): """Acquire Houdini's main window""" if self._parent is None: @@ -89,8 +69,6 @@ def reload_pipeline(*args): """ - import importlib - api.uninstall() for module in ("avalon.io", @@ -236,13 +214,17 @@ def ls(): "pyblish.mindbender.container"): containers += lib.lsattr("id", identifier) + has_metadata_collector = False + config_host = find_submodule(api.registered_config(), "houdini") + if hasattr(config_host, "collect_container_metadata"): + has_metadata_collector = True + for container in sorted(containers): data = parse_container(container) # Collect custom data if attribute is present - config = find_host_config(api.registered_config()) - if hasattr(config, "collect_container_metadata"): - metadata = config.collect_container_metadata(container) + if has_metadata_collector: + metadata = config_host.collect_container_metadata(container) data.update(metadata) yield data diff --git a/avalon/lib.py b/avalon/lib.py index 11c9dff4a..139fce223 100644 --- a/avalon/lib.py +++ b/avalon/lib.py @@ -5,6 +5,7 @@ import json import logging import datetime +import importlib import subprocess import types @@ -236,7 +237,7 @@ def modules_from_path(path): """Get python scripts as modules from a path. Arguments: - path (str): Path to python scrips. + path (str): Path to folder containing python scripts. Returns: List of modules. @@ -275,9 +276,30 @@ def modules_from_path(path): sys.modules[mod_name] = module except Exception as err: - print("Skipped: \"%s\" (%s)", mod_name, err) + print("Skipped: \"{0}\" ({1})".format(mod_name, err)) continue modules.append(module) return modules + + +def find_submodule(module, submodule): + """Find and return submodule of the module. + + Args: + module (types.ModuleType): The module to search in. + submodule (str): The submodule name to find. + + Returns: + types.ModuleType or None: The module, if found. + + """ + name = "{0}.{1}".format(module.__name__, submodule) + try: + return importlib.import_module(name) + except ImportError as exc: + if str(exc) != "No module name {}".format(name): + log_.warning("Could not find '%s' in module: %s", + submodule, + module) diff --git a/avalon/maya/pipeline.py b/avalon/maya/pipeline.py index 55c4bb490..c04baf862 100644 --- a/avalon/maya/pipeline.py +++ b/avalon/maya/pipeline.py @@ -10,8 +10,8 @@ from pyblish import api as pyblish from . import lib, compat -from ..lib import logger -from .. import api, schema +from ..lib import logger, find_submodule +from .. import api from ..tools import workfiles from ..vendor.Qt import QtCore, QtWidgets @@ -58,10 +58,6 @@ def install(config): pyblish.register_host("mayapy") pyblish.register_host("maya") - config = find_host_config(config) - if hasattr(config, "install"): - config.install() - def _set_project(): """Sets the maya project to the current Session's work directory. @@ -84,17 +80,6 @@ def _set_project(): cmds.workspace(workdir, openWorkspace=True) -def find_host_config(config): - try: - config = importlib.import_module(config.__name__ + ".maya") - except ImportError as exc: - if str(exc) != "No module name {}".format(config.__name__ + ".maya"): - raise - config = None - - return config - - def get_main_window(): """Acquire Maya's main window""" if self._parent is None: @@ -111,9 +96,6 @@ def uninstall(config): This function is called automatically on calling `api.uninstall()`. """ - config = find_host_config(config) - if hasattr(config, "uninstall"): - config.uninstall() _uninstall_menu() @@ -230,8 +212,6 @@ def reload_pipeline(*args): """ - import importlib - api.uninstall() for module in ("avalon.io", @@ -272,13 +252,13 @@ def reload_pipeline(*args): def _uninstall_menu(): - + # In Maya 2020+ don't use the QApplication.instance() # during startup (userSetup.py) as it will return a # QtCore.QCoreApplication instance which does not have # the allWidgets method. As such, we call the staticmethod. all_widgets = QtWidgets.QApplication.allWidgets() - + widgets = dict((w.objectName(), w) for w in all_widgets) menu = widgets.get(self._menu) @@ -517,8 +497,8 @@ def ls(): container_names = _ls() has_metadata_collector = False - config = find_host_config(api.registered_config()) - if hasattr(config, "collect_container_metadata"): + config_host = find_submodule(api.registered_config(), "maya") + if hasattr(config_host, "collect_container_metadata"): has_metadata_collector = True for container in sorted(container_names): @@ -526,7 +506,7 @@ def ls(): # Collect custom data if attribute is present if has_metadata_collector: - metadata = config.collect_container_metadata(container) + metadata = config_host.collect_container_metadata(container) data.update(metadata) yield data diff --git a/avalon/nuke/pipeline.py b/avalon/nuke/pipeline.py index 31ac2295e..2b7738a02 100644 --- a/avalon/nuke/pipeline.py +++ b/avalon/nuke/pipeline.py @@ -28,8 +28,6 @@ def reload_pipeline(): """ - import importlib - api.uninstall() _uninstall_menu() @@ -227,26 +225,10 @@ def install(config): _register_events() pyblish.register_host("nuke") - # Trigger install on the config's "nuke" package - config = find_host_config(config) - - if hasattr(config, "install"): - config.install() log.info("config.nuke installed") -def find_host_config(config): - try: - config = importlib.import_module(config.__name__ + ".nuke") - except ImportError as exc: - if str(exc) != "No module name {}".format(config.__name__ + ".nuke"): - raise - config = None - - return config - - def get_main_window(): """Acquire Nuke's main window""" if self._parent is None: @@ -270,9 +252,6 @@ def uninstall(config): modifying the menu or registered families. """ - config = find_host_config(config) - if hasattr(config, "uninstall"): - config.uninstall() _uninstall_menu() diff --git a/avalon/pipeline.py b/avalon/pipeline.py index a310908ec..51cbb35b0 100644 --- a/avalon/pipeline.py +++ b/avalon/pipeline.py @@ -78,6 +78,12 @@ def install(host): if hasattr(host, "install"): host.install(config) + # Optional config.host.install() + host_name = host.__name__.rsplit(".", 1)[-1] + config_host = lib.find_submodule(config, host_name) + if hasattr(config_host, "install"): + config_host.install() + register_host(host) register_config(config) @@ -104,9 +110,16 @@ def find_config(): def uninstall(): """Undo all of what `install()` did""" config = registered_config() + host = registered_host() + + # Optional config.host.uninstall() + host_name = host.__name__.rsplit(".", 1)[-1] + config_host = lib.find_submodule(config, host_name) + if hasattr(config_host, "uninstall"): + config_host.uninstall() try: - registered_host().uninstall(config) + host.uninstall(config) except AttributeError: pass