From 4cdcb974b384ec628e83f480a24cdca4ddd1e605 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 1 Apr 2022 18:07:51 +0200 Subject: [PATCH 01/35] removed usage of config callback in maya --- openpype/hosts/maya/api/pipeline.py | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/openpype/hosts/maya/api/pipeline.py b/openpype/hosts/maya/api/pipeline.py index a8834d1ea3..f6f3472eef 100644 --- a/openpype/hosts/maya/api/pipeline.py +++ b/openpype/hosts/maya/api/pipeline.py @@ -9,8 +9,6 @@ import maya.api.OpenMaya as om import pyblish.api import avalon.api -from avalon.lib import find_submodule - import openpype.hosts.maya from openpype.tools.utils import host_tools from openpype.lib import ( @@ -20,7 +18,6 @@ from openpype.lib import ( ) from openpype.lib.path_tools import HostDirmap from openpype.pipeline import ( - LegacyCreator, register_loader_plugin_path, register_inventory_action_path, register_creator_plugin_path, @@ -270,21 +267,8 @@ def ls(): """ container_names = _ls() - - has_metadata_collector = False - config_host = find_submodule(avalon.api.registered_config(), "maya") - if hasattr(config_host, "collect_container_metadata"): - has_metadata_collector = True - for container in sorted(container_names): - data = parse_container(container) - - # Collect custom data if attribute is present - if has_metadata_collector: - metadata = config_host.collect_container_metadata(container) - data.update(metadata) - - yield data + yield parse_container(container) def containerise(name, From b652170492496a7d64caa34b66db334011a1cdde Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 1 Apr 2022 18:09:05 +0200 Subject: [PATCH 02/35] removed usage of avalon config from houdini --- openpype/hosts/houdini/api/pipeline.py | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/openpype/hosts/houdini/api/pipeline.py b/openpype/hosts/houdini/api/pipeline.py index 8e093a89bc..6a69814e2e 100644 --- a/openpype/hosts/houdini/api/pipeline.py +++ b/openpype/hosts/houdini/api/pipeline.py @@ -7,8 +7,6 @@ import hou import hdefereval import pyblish.api -import avalon.api -from avalon.lib import find_submodule from openpype.pipeline import ( register_creator_plugin_path, @@ -215,24 +213,12 @@ def ls(): "pyblish.mindbender.container"): containers += lib.lsattr("id", identifier) - has_metadata_collector = False - config_host = find_submodule(avalon.api.registered_config(), "houdini") - if hasattr(config_host, "collect_container_metadata"): - has_metadata_collector = True - for container in sorted(containers, # Hou 19+ Python 3 hou.ObjNode are not # sortable due to not supporting greater # than comparisons key=lambda node: node.path()): - data = parse_container(container) - - # Collect custom data if attribute is present - if has_metadata_collector: - metadata = config_host.collect_container_metadata(container) - data.update(metadata) - - yield data + yield parse_container(container) def before_save(): From f3f06444ac7ddd193932549d00dc97a2d827d306 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 1 Apr 2022 18:31:48 +0200 Subject: [PATCH 03/35] created process_context with installation functions --- openpype/pipeline/process_context.py | 333 +++++++++++++++++++++++++++ 1 file changed, 333 insertions(+) create mode 100644 openpype/pipeline/process_context.py diff --git a/openpype/pipeline/process_context.py b/openpype/pipeline/process_context.py new file mode 100644 index 0000000000..65e891c100 --- /dev/null +++ b/openpype/pipeline/process_context.py @@ -0,0 +1,333 @@ +"""Core pipeline functionality""" + +import os +import sys +import json +import types +import logging +import inspect +import platform + +import pyblish.api +from pyblish.lib import MessageHandler + +from avalon import io, Session + +import openpype +from openpype.modules import load_modules +from openpype.settings import get_project_settings +from openpype.lib import ( + Anatomy, + register_event_callback, + filter_pyblish_plugins, + change_timer_to_current_context, +) + +from . import ( + register_loader_plugin_path, + register_inventory_action, + register_creator_plugin_path, + deregister_loader_plugin_path, +) + + +_is_installed = False +_registered_root = {"_": ""} +_registered_host = {"_": None} + +log = logging.getLogger(__name__) + +PACKAGE_DIR = os.path.dirname(os.path.abspath(openpype.__file__)) +PLUGINS_DIR = os.path.join(PACKAGE_DIR, "plugins") + +# Global plugin paths +PUBLISH_PATH = os.path.join(PLUGINS_DIR, "publish") +LOAD_PATH = os.path.join(PLUGINS_DIR, "load") + + +def register_root(path): + """Register currently active root""" + log.info("Registering root: %s" % path) + _registered_root["_"] = path + + +def registered_root(): + """Return currently registered root""" + root = _registered_root["_"] + if root: + return root + + root = Session.get("AVALON_PROJECTS") + if root: + return os.path.normpath(root) + return "" + + +def install(host): + """Install `host` into the running Python session. + + Args: + host (module): A Python module containing the Avalon + avalon host-interface. + """ + global _is_installed + + io.install() + + missing = list() + for key in ("AVALON_PROJECT", "AVALON_ASSET"): + if key not in Session: + missing.append(key) + + assert not missing, ( + "%s missing from environment, %s" % ( + ", ".join(missing), + json.dumps(Session, indent=4, sort_keys=True) + )) + + project_name = Session["AVALON_PROJECT"] + log.info("Activating %s.." % project_name) + + # Optional host install function + if hasattr(host, "install"): + host.install() + + register_host(host) + + _is_installed = True + + # Make sure modules are loaded + load_modules() + + def modified_emit(obj, record): + """Method replacing `emit` in Pyblish's MessageHandler.""" + record.msg = record.getMessage() + obj.records.append(record) + + MessageHandler.emit = modified_emit + + log.info("Registering global plug-ins..") + pyblish.api.register_plugin_path(PUBLISH_PATH) + pyblish.api.register_discovery_filter(filter_pyblish_plugins) + register_loader_plugin_path(LOAD_PATH) + + project_name = os.environ.get("AVALON_PROJECT") + + # Register studio specific plugins + if project_name: + anatomy = Anatomy(project_name) + anatomy.set_root_environments() + register_root(anatomy.roots) + + project_settings = get_project_settings(project_name) + platform_name = platform.system().lower() + project_plugins = ( + project_settings + .get("global", {}) + .get("project_plugins", {}) + .get(platform_name) + ) or [] + for path in project_plugins: + try: + path = str(path.format(**os.environ)) + except KeyError: + pass + + if not path or not os.path.exists(path): + continue + + pyblish.api.register_plugin_path(path) + register_loader_plugin_path(path) + register_creator_plugin_path(path) + register_inventory_action(path) + + # apply monkey patched discover to original one + log.info("Patching discovery") + + register_event_callback("taskChanged", _on_task_change) + + +def _on_task_change(): + change_timer_to_current_context() + + +def uninstall(): + """Undo all of what `install()` did""" + host = registered_host() + + try: + host.uninstall() + except AttributeError: + pass + + log.info("Deregistering global plug-ins..") + pyblish.api.deregister_plugin_path(PUBLISH_PATH) + pyblish.api.deregister_discovery_filter(filter_pyblish_plugins) + deregister_loader_plugin_path(LOAD_PATH) + log.info("Global plug-ins unregistred") + + deregister_host() + + io.uninstall() + + log.info("Successfully uninstalled Avalon!") + + +def is_installed(): + """Return state of installation + + Returns: + True if installed, False otherwise + + """ + + return _is_installed + + +def register_host(host): + """Register a new host for the current process + + Arguments: + host (ModuleType): A module implementing the + Host API interface. See the Host API + documentation for details on what is + required, or browse the source code. + + """ + signatures = { + "ls": [] + } + + _validate_signature(host, signatures) + _registered_host["_"] = host + + +def _validate_signature(module, signatures): + # Required signatures for each member + + missing = list() + invalid = list() + success = True + + for member in signatures: + if not hasattr(module, member): + missing.append(member) + success = False + + else: + attr = getattr(module, member) + if sys.version_info.major >= 3: + signature = inspect.getfullargspec(attr)[0] + else: + signature = inspect.getargspec(attr)[0] + required_signature = signatures[member] + + assert isinstance(signature, list) + assert isinstance(required_signature, list) + + if not all(member in signature + for member in required_signature): + invalid.append({ + "member": member, + "signature": ", ".join(signature), + "required": ", ".join(required_signature) + }) + success = False + + if not success: + report = list() + + if missing: + report.append( + "Incomplete interface for module: '%s'\n" + "Missing: %s" % (module, ", ".join( + "'%s'" % member for member in missing)) + ) + + if invalid: + report.append( + "'%s': One or more members were found, but didn't " + "have the right argument signature." % module.__name__ + ) + + for member in invalid: + report.append( + " Found: {member}({signature})".format(**member) + ) + report.append( + " Expected: {member}({required})".format(**member) + ) + + raise ValueError("\n".join(report)) + + +def registered_host(): + """Return currently registered host""" + return _registered_host["_"] + + +def deregister_host(): + _registered_host["_"] = default_host() + + +def default_host(): + """A default host, in place of anything better + + This may be considered as reference for the + interface a host must implement. It also ensures + that the system runs, even when nothing is there + to support it. + + """ + + host = types.ModuleType("defaultHost") + + def ls(): + return list() + + host.__dict__.update({ + "ls": ls + }) + + return host + + +def debug_host(): + """A debug host, useful to debugging features that depend on a host""" + + host = types.ModuleType("debugHost") + + def ls(): + containers = [ + { + "representation": "ee-ft-a-uuid1", + "schema": "openpype:container-1.0", + "name": "Bruce01", + "objectName": "Bruce01_node", + "namespace": "_bruce01_", + "version": 3, + }, + { + "representation": "aa-bc-s-uuid2", + "schema": "openpype:container-1.0", + "name": "Bruce02", + "objectName": "Bruce01_node", + "namespace": "_bruce02_", + "version": 2, + } + ] + + for container in containers: + yield container + + host.__dict__.update({ + "ls": ls, + "open_file": lambda fname: None, + "save_file": lambda fname: None, + "current_file": lambda: os.path.expanduser("~/temp.txt"), + "has_unsaved_changes": lambda: False, + "work_root": lambda: os.path.expanduser("~/temp"), + "file_extensions": lambda: ["txt"], + }) + + return host From c49791258a5b714c84085f465e05b43f72f08266 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 1 Apr 2022 19:36:08 +0200 Subject: [PATCH 04/35] changed function names and separated install in 2 parts --- openpype/pipeline/__init__.py | 33 ++++++++++++++++++++++++++++ openpype/pipeline/process_context.py | 26 ++++++++++++---------- 2 files changed, 47 insertions(+), 12 deletions(-) diff --git a/openpype/pipeline/__init__.py b/openpype/pipeline/__init__.py index 8460d20ef1..914606cc2f 100644 --- a/openpype/pipeline/__init__.py +++ b/openpype/pipeline/__init__.py @@ -68,6 +68,22 @@ from .actions import ( deregister_inventory_action_path, ) +from .process_context import ( + install_openpype_plugins, + install_host, + uninstall_host, + is_installed, + + register_root, + registered_root, + + register_host, + registered_host, + deregister_host, +) +install = install_host +uninstall = uninstall_host + __all__ = ( "AVALON_CONTAINER_ID", @@ -135,4 +151,21 @@ __all__ = ( "register_inventory_action_path", "deregister_inventory_action", "deregister_inventory_action_path", + + # --- Process context --- + "install_openpype_plugins", + "install_host", + "uninstall_host", + "is_installed", + + "register_root", + "registered_root", + + "register_host", + "registered_host", + "deregister_host", + + # Backwards compatible function names + "install", + "uninstall", ) diff --git a/openpype/pipeline/process_context.py b/openpype/pipeline/process_context.py index 65e891c100..1bef260ec9 100644 --- a/openpype/pipeline/process_context.py +++ b/openpype/pipeline/process_context.py @@ -63,7 +63,7 @@ def registered_root(): return "" -def install(host): +def install_host(host): """Install `host` into the running Python session. Args: @@ -72,6 +72,8 @@ def install(host): """ global _is_installed + _is_installed = True + io.install() missing = list() @@ -94,10 +96,7 @@ def install(host): register_host(host) - _is_installed = True - - # Make sure modules are loaded - load_modules() + register_event_callback("taskChanged", _on_task_change) def modified_emit(obj, record): """Method replacing `emit` in Pyblish's MessageHandler.""" @@ -106,12 +105,20 @@ def install(host): MessageHandler.emit = modified_emit + install_openpype_plugins() + + +def install_openpype_plugins(project_name=None): + # Make sure modules are loaded + load_modules() + log.info("Registering global plug-ins..") pyblish.api.register_plugin_path(PUBLISH_PATH) pyblish.api.register_discovery_filter(filter_pyblish_plugins) register_loader_plugin_path(LOAD_PATH) - project_name = os.environ.get("AVALON_PROJECT") + if project_name is None: + project_name = os.environ.get("AVALON_PROJECT") # Register studio specific plugins if project_name: @@ -141,17 +148,12 @@ def install(host): register_creator_plugin_path(path) register_inventory_action(path) - # apply monkey patched discover to original one - log.info("Patching discovery") - - register_event_callback("taskChanged", _on_task_change) - def _on_task_change(): change_timer_to_current_context() -def uninstall(): +def uninstall_host(): """Undo all of what `install()` did""" host = registered_host() From eabe2fe56960a098608b10599e5d0c1cee550f9a Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 1 Apr 2022 19:37:58 +0200 Subject: [PATCH 05/35] changed usage of registered root --- .../vendor/husdoutputprocessors/avalon_uri_processor.py | 3 ++- openpype/lib/usdlib.py | 3 ++- openpype/pipeline/load/utils.py | 4 +++- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/openpype/hosts/houdini/vendor/husdoutputprocessors/avalon_uri_processor.py b/openpype/hosts/houdini/vendor/husdoutputprocessors/avalon_uri_processor.py index 499b733570..8cd51e6641 100644 --- a/openpype/hosts/houdini/vendor/husdoutputprocessors/avalon_uri_processor.py +++ b/openpype/hosts/houdini/vendor/husdoutputprocessors/avalon_uri_processor.py @@ -134,6 +134,7 @@ class AvalonURIOutputProcessor(base.OutputProcessorBase): """ from avalon import api, io + from openpype.pipeline import registered_root PROJECT = api.Session["AVALON_PROJECT"] asset_doc = io.find_one({"name": asset, @@ -141,7 +142,7 @@ class AvalonURIOutputProcessor(base.OutputProcessorBase): if not asset_doc: raise RuntimeError("Invalid asset name: '%s'" % asset) - root = api.registered_root() + root = registered_root() path = self._template.format(**{ "root": root, "project": PROJECT, diff --git a/openpype/lib/usdlib.py b/openpype/lib/usdlib.py index 89021156b4..7b3b7112de 100644 --- a/openpype/lib/usdlib.py +++ b/openpype/lib/usdlib.py @@ -9,6 +9,7 @@ except ImportError: from mvpxr import Usd, UsdGeom, Sdf, Kind from avalon import io, api +from openpype.pipeline import registered_root log = logging.getLogger(__name__) @@ -323,7 +324,7 @@ def get_usd_master_path(asset, subset, representation): path = template.format( **{ - "root": api.registered_root(), + "root": registered_root(), "project": api.Session["AVALON_PROJECT"], "asset": asset_doc["name"], "subset": subset, diff --git a/openpype/pipeline/load/utils.py b/openpype/pipeline/load/utils.py index 53ac6b626d..cb7c76f133 100644 --- a/openpype/pipeline/load/utils.py +++ b/openpype/pipeline/load/utils.py @@ -10,7 +10,7 @@ import six from bson.objectid import ObjectId from avalon import io, schema -from avalon.api import Session, registered_root +from avalon.api import Session from openpype.lib import Anatomy @@ -532,6 +532,8 @@ def get_representation_path(representation, root=None, dbcon=None): dbcon = io if root is None: + from openpype.pipeline import registered_root + root = registered_root() def path_from_represenation(): From 729131738a5ef8d618f3877da2bb4635e0c2d8be Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 1 Apr 2022 19:40:23 +0200 Subject: [PATCH 06/35] changed installation of hosts --- openpype/hosts/aftereffects/api/lib.py | 4 ++-- openpype/hosts/blender/api/pipeline.py | 4 ++-- .../hosts/blender/blender_addon/startup/init.py | 4 ++-- openpype/hosts/celaction/api/cli.py | 10 ++++------ openpype/hosts/flame/startup/openpype_in_flame.py | 6 +++--- openpype/hosts/fusion/scripts/fusion_switch_shot.py | 8 ++++++-- .../fusion/utility_scripts/__OpenPype_Menu__.py | 13 ++++++------- openpype/hosts/fusion/utility_scripts/switch_ui.py | 6 +++--- openpype/hosts/harmony/api/lib.py | 4 ++-- openpype/hosts/hiero/api/pipeline.py | 9 +-------- .../hiero/api/startup/Python/Startup/Startup.py | 4 ++-- .../hosts/houdini/startup/python2.7libs/pythonrc.py | 4 ++-- .../hosts/houdini/startup/python3.7libs/pythonrc.py | 4 ++-- openpype/hosts/maya/startup/userSetup.py | 5 ++--- openpype/hosts/nuke/startup/menu.py | 4 ++-- openpype/hosts/photoshop/api/lib.py | 5 ++--- .../utility_scripts/OpenPype_sync_util_scripts.py | 5 +++-- .../resolve/utility_scripts/__OpenPype__Menu__.py | 9 ++------- .../utility_scripts/tests/test_otio_as_edl.py | 10 +++++----- .../tests/testing_create_timeline_item_from_path.py | 8 +++----- .../tests/testing_load_media_pool_item.py | 8 +++----- openpype/hosts/tvpaint/api/launch_script.py | 4 ++-- openpype/hosts/tvpaint/api/pipeline.py | 11 ++++------- .../integration/Content/Python/init_unreal.py | 10 ++-------- openpype/hosts/webpublisher/api/__init__.py | 1 - openpype/lib/remote_publish.py | 7 ++----- openpype/tests/test_avalon_plugin_presets.py | 12 +++++------- 27 files changed, 74 insertions(+), 105 deletions(-) diff --git a/openpype/hosts/aftereffects/api/lib.py b/openpype/hosts/aftereffects/api/lib.py index dac6b5d28f..ce4cbf09af 100644 --- a/openpype/hosts/aftereffects/api/lib.py +++ b/openpype/hosts/aftereffects/api/lib.py @@ -6,6 +6,7 @@ import logging from Qt import QtWidgets +from openpype.pipeline import install_host from openpype.lib.remote_publish import headless_publish from openpype.tools.utils import host_tools @@ -22,10 +23,9 @@ def safe_excepthook(*args): def main(*subprocess_args): sys.excepthook = safe_excepthook - import avalon.api from openpype.hosts.aftereffects import api - avalon.api.install(api) + install_host(api) os.environ["OPENPYPE_LOG_NO_COLORS"] = "False" app = QtWidgets.QApplication([]) diff --git a/openpype/hosts/blender/api/pipeline.py b/openpype/hosts/blender/api/pipeline.py index b9ec2cfea4..0ea579970e 100644 --- a/openpype/hosts/blender/api/pipeline.py +++ b/openpype/hosts/blender/api/pipeline.py @@ -19,6 +19,7 @@ from openpype.pipeline import ( deregister_loader_plugin_path, deregister_creator_plugin_path, AVALON_CONTAINER_ID, + uninstall_host, ) from openpype.api import Logger from openpype.lib import ( @@ -209,11 +210,10 @@ def reload_pipeline(*args): """ - avalon.api.uninstall() + uninstall_host() for module in ( "avalon.io", - "avalon.lib", "avalon.pipeline", "avalon.api", ): diff --git a/openpype/hosts/blender/blender_addon/startup/init.py b/openpype/hosts/blender/blender_addon/startup/init.py index e43373bc6c..13a4b8a7a1 100644 --- a/openpype/hosts/blender/blender_addon/startup/init.py +++ b/openpype/hosts/blender/blender_addon/startup/init.py @@ -1,4 +1,4 @@ -from avalon import pipeline +from openpype.pipeline import install_host from openpype.hosts.blender import api -pipeline.install(api) +install_host(api) diff --git a/openpype/hosts/celaction/api/cli.py b/openpype/hosts/celaction/api/cli.py index bc1e3eaf89..85e210f21a 100644 --- a/openpype/hosts/celaction/api/cli.py +++ b/openpype/hosts/celaction/api/cli.py @@ -3,8 +3,6 @@ import sys import copy import argparse -from avalon import io - import pyblish.api import pyblish.util @@ -13,6 +11,8 @@ import openpype import openpype.hosts.celaction from openpype.hosts.celaction import api as celaction from openpype.tools.utils import host_tools +from openpype.pipeline.process_context import install_openpype_plugins + log = Logger().get_logger("Celaction_cli_publisher") @@ -21,9 +21,6 @@ publish_host = "celaction" HOST_DIR = os.path.dirname(os.path.abspath(openpype.hosts.celaction.__file__)) PLUGINS_DIR = os.path.join(HOST_DIR, "plugins") PUBLISH_PATH = os.path.join(PLUGINS_DIR, "publish") -LOAD_PATH = os.path.join(PLUGINS_DIR, "load") -CREATE_PATH = os.path.join(PLUGINS_DIR, "create") -INVENTORY_PATH = os.path.join(PLUGINS_DIR, "inventory") def cli(): @@ -74,7 +71,8 @@ def main(): _prepare_publish_environments() # Registers pype's Global pyblish plugins - openpype.install() + # - use fake host + install_openpype_plugins() if os.path.exists(PUBLISH_PATH): log.info(f"Registering path: {PUBLISH_PATH}") diff --git a/openpype/hosts/flame/startup/openpype_in_flame.py b/openpype/hosts/flame/startup/openpype_in_flame.py index 931c5a1b79..7015abc7f4 100644 --- a/openpype/hosts/flame/startup/openpype_in_flame.py +++ b/openpype/hosts/flame/startup/openpype_in_flame.py @@ -3,16 +3,16 @@ import sys from Qt import QtWidgets from pprint import pformat import atexit -import openpype + import avalon import openpype.hosts.flame.api as opfapi +from openpype.pipeline import install_host def openpype_install(): """Registering OpenPype in context """ - openpype.install() - avalon.api.install(opfapi) + install_host(opfapi) print("Avalon registered hosts: {}".format( avalon.api.registered_host())) diff --git a/openpype/hosts/fusion/scripts/fusion_switch_shot.py b/openpype/hosts/fusion/scripts/fusion_switch_shot.py index ca7efb9136..ca8e5c9e37 100644 --- a/openpype/hosts/fusion/scripts/fusion_switch_shot.py +++ b/openpype/hosts/fusion/scripts/fusion_switch_shot.py @@ -7,6 +7,10 @@ import logging import avalon.api from avalon import io +from openpype.pipeline import ( + install_host, + registered_host, +) from openpype.lib import version_up from openpype.hosts.fusion import api from openpype.hosts.fusion.api import lib @@ -218,7 +222,7 @@ def switch(asset_name, filepath=None, new=True): assert current_comp is not None, ( "Fusion could not load '{}'").format(filepath) - host = avalon.api.registered_host() + host = registered_host() containers = list(host.ls()) assert containers, "Nothing to update" @@ -279,7 +283,7 @@ if __name__ == '__main__': args, unknown = parser.parse_args() - avalon.api.install(api) + install_host(api) switch(args.asset_name, args.file_path) sys.exit(0) diff --git a/openpype/hosts/fusion/utility_scripts/__OpenPype_Menu__.py b/openpype/hosts/fusion/utility_scripts/__OpenPype_Menu__.py index 4b5e8f91a0..aa98563785 100644 --- a/openpype/hosts/fusion/utility_scripts/__OpenPype_Menu__.py +++ b/openpype/hosts/fusion/utility_scripts/__OpenPype_Menu__.py @@ -1,24 +1,23 @@ import os import sys -import openpype from openpype.api import Logger +from openpype.pipeline import ( + install_host, + registered_host, +) log = Logger().get_logger(__name__) def main(env): - import avalon.api from openpype.hosts.fusion import api from openpype.hosts.fusion.api import menu - # Registers pype's Global pyblish plugins - openpype.install() - # activate resolve from pype - avalon.api.install(api) + install_host(api) - log.info(f"Avalon registered hosts: {avalon.api.registered_host()}") + log.info(f"Avalon registered hosts: {registered_host()}") menu.launch_openpype_menu() diff --git a/openpype/hosts/fusion/utility_scripts/switch_ui.py b/openpype/hosts/fusion/utility_scripts/switch_ui.py index d9eeae25ea..37306c7a2a 100644 --- a/openpype/hosts/fusion/utility_scripts/switch_ui.py +++ b/openpype/hosts/fusion/utility_scripts/switch_ui.py @@ -1,14 +1,15 @@ import os +import sys import glob import logging from Qt import QtWidgets, QtCore -import avalon.api from avalon import io import qtawesome as qta from openpype import style +from openpype.pipeline import install_host from openpype.hosts.fusion import api from openpype.lib.avalon_context import get_workdir_from_session @@ -181,8 +182,7 @@ class App(QtWidgets.QWidget): if __name__ == '__main__': - import sys - avalon.api.install(api) + install_host(api) app = QtWidgets.QApplication(sys.argv) window = App() diff --git a/openpype/hosts/harmony/api/lib.py b/openpype/hosts/harmony/api/lib.py index 66eeac1e3a..53fd0f07dd 100644 --- a/openpype/hosts/harmony/api/lib.py +++ b/openpype/hosts/harmony/api/lib.py @@ -183,10 +183,10 @@ def launch(application_path, *args): application_path (str): Path to Harmony. """ - from avalon import api + from openpype.pipeline import install_host from openpype.hosts.harmony import api as harmony - api.install(harmony) + install_host(harmony) ProcessContext.port = random.randrange(49152, 65535) os.environ["AVALON_HARMONY_PORT"] = str(ProcessContext.port) diff --git a/openpype/hosts/hiero/api/pipeline.py b/openpype/hosts/hiero/api/pipeline.py index b334102129..616ff53fd8 100644 --- a/openpype/hosts/hiero/api/pipeline.py +++ b/openpype/hosts/hiero/api/pipeline.py @@ -34,14 +34,7 @@ AVALON_CONTAINERS = ":AVALON_CONTAINERS" def install(): - """ - Installing Hiero integration for avalon - - Args: - config (obj): avalon config module `pype` in our case, it is not - used but required by avalon.api.install() - - """ + """Installing Hiero integration.""" # adding all events events.register_events() diff --git a/openpype/hosts/hiero/api/startup/Python/Startup/Startup.py b/openpype/hosts/hiero/api/startup/Python/Startup/Startup.py index 21c21cd7c3..2e638c2088 100644 --- a/openpype/hosts/hiero/api/startup/Python/Startup/Startup.py +++ b/openpype/hosts/hiero/api/startup/Python/Startup/Startup.py @@ -1,9 +1,9 @@ import traceback # activate hiero from pype -import avalon.api +from openpype.pipeline import install_host import openpype.hosts.hiero.api as phiero -avalon.api.install(phiero) +install_host(phiero) try: __import__("openpype.hosts.hiero.api") diff --git a/openpype/hosts/houdini/startup/python2.7libs/pythonrc.py b/openpype/hosts/houdini/startup/python2.7libs/pythonrc.py index eb33b49759..afadbffd3e 100644 --- a/openpype/hosts/houdini/startup/python2.7libs/pythonrc.py +++ b/openpype/hosts/houdini/startup/python2.7libs/pythonrc.py @@ -1,10 +1,10 @@ -import avalon.api +from openpype.pipeline import install_host from openpype.hosts.houdini import api def main(): print("Installing OpenPype ...") - avalon.api.install(api) + install_host(api) main() diff --git a/openpype/hosts/houdini/startup/python3.7libs/pythonrc.py b/openpype/hosts/houdini/startup/python3.7libs/pythonrc.py index eb33b49759..afadbffd3e 100644 --- a/openpype/hosts/houdini/startup/python3.7libs/pythonrc.py +++ b/openpype/hosts/houdini/startup/python3.7libs/pythonrc.py @@ -1,10 +1,10 @@ -import avalon.api +from openpype.pipeline import install_host from openpype.hosts.houdini import api def main(): print("Installing OpenPype ...") - avalon.api.install(api) + install_host(api) main() diff --git a/openpype/hosts/maya/startup/userSetup.py b/openpype/hosts/maya/startup/userSetup.py index b89244817a..a3ab483add 100644 --- a/openpype/hosts/maya/startup/userSetup.py +++ b/openpype/hosts/maya/startup/userSetup.py @@ -1,11 +1,10 @@ import os -import avalon.api from openpype.api import get_project_settings +from openpype.pipeline import install_host from openpype.hosts.maya import api -import openpype.hosts.maya.api.lib as mlib from maya import cmds -avalon.api.install(api) +install_host(api) print("starting OpenPype usersetup") diff --git a/openpype/hosts/nuke/startup/menu.py b/openpype/hosts/nuke/startup/menu.py index 2cac6d09e7..9ed43b2110 100644 --- a/openpype/hosts/nuke/startup/menu.py +++ b/openpype/hosts/nuke/startup/menu.py @@ -1,7 +1,7 @@ import nuke -import avalon.api from openpype.api import Logger +from openpype.pipeline import install_host from openpype.hosts.nuke import api from openpype.hosts.nuke.api.lib import ( on_script_load, @@ -13,7 +13,7 @@ from openpype.hosts.nuke.api.lib import ( log = Logger.get_logger(__name__) -avalon.api.install(api) +install_host(api) # fix ffmpeg settings on script nuke.addOnScriptLoad(on_script_load) diff --git a/openpype/hosts/photoshop/api/lib.py b/openpype/hosts/photoshop/api/lib.py index 6d2a493a94..2f57d64464 100644 --- a/openpype/hosts/photoshop/api/lib.py +++ b/openpype/hosts/photoshop/api/lib.py @@ -5,9 +5,8 @@ import traceback from Qt import QtWidgets -import avalon.api - from openpype.api import Logger +from openpype.pipeline import install_host from openpype.tools.utils import host_tools from openpype.lib.remote_publish import headless_publish from openpype.lib import env_value_to_bool @@ -24,7 +23,7 @@ def safe_excepthook(*args): def main(*subprocess_args): from openpype.hosts.photoshop import api - avalon.api.install(api) + install_host(api) sys.excepthook = safe_excepthook # coloring in StdOutBroker diff --git a/openpype/hosts/resolve/utility_scripts/OpenPype_sync_util_scripts.py b/openpype/hosts/resolve/utility_scripts/OpenPype_sync_util_scripts.py index ac66916b91..3a16b9c966 100644 --- a/openpype/hosts/resolve/utility_scripts/OpenPype_sync_util_scripts.py +++ b/openpype/hosts/resolve/utility_scripts/OpenPype_sync_util_scripts.py @@ -1,13 +1,14 @@ #!/usr/bin/env python import os import sys -import openpype + +from openpype.pipeline import install_host def main(env): import openpype.hosts.resolve as bmdvr # Registers openpype's Global pyblish plugins - openpype.install() + install_host(bmdvr) bmdvr.setup(env) diff --git a/openpype/hosts/resolve/utility_scripts/__OpenPype__Menu__.py b/openpype/hosts/resolve/utility_scripts/__OpenPype__Menu__.py index b0cef1838a..89ade9238b 100644 --- a/openpype/hosts/resolve/utility_scripts/__OpenPype__Menu__.py +++ b/openpype/hosts/resolve/utility_scripts/__OpenPype__Menu__.py @@ -1,8 +1,7 @@ import os import sys -import avalon.api as avalon -import openpype +from openpype.pipeline import install_host from openpype.api import Logger log = Logger().get_logger(__name__) @@ -10,13 +9,9 @@ log = Logger().get_logger(__name__) def main(env): import openpype.hosts.resolve as bmdvr - # Registers openpype's Global pyblish plugins - openpype.install() # activate resolve from openpype - avalon.install(bmdvr) - - log.info(f"Avalon registered hosts: {avalon.registered_host()}") + install_host(bmdvr) bmdvr.launch_pype_menu() diff --git a/openpype/hosts/resolve/utility_scripts/tests/test_otio_as_edl.py b/openpype/hosts/resolve/utility_scripts/tests/test_otio_as_edl.py index 5430ad32df..8433bd9172 100644 --- a/openpype/hosts/resolve/utility_scripts/tests/test_otio_as_edl.py +++ b/openpype/hosts/resolve/utility_scripts/tests/test_otio_as_edl.py @@ -1,9 +1,11 @@ #! python3 import os import sys -import avalon.api as avalon -import openpype + import opentimelineio as otio + +from openpype.pipeline import install_host + from openpype.hosts.resolve import TestGUI import openpype.hosts.resolve as bmdvr from openpype.hosts.resolve.otio import davinci_export as otio_export @@ -14,10 +16,8 @@ class ThisTestGUI(TestGUI): def __init__(self): super(ThisTestGUI, self).__init__() - # Registers openpype's Global pyblish plugins - openpype.install() # activate resolve from openpype - avalon.install(bmdvr) + install_host(bmdvr) def _open_dir_button_pressed(self, event): # selected_path = self.fu.RequestFile(os.path.expanduser("~")) diff --git a/openpype/hosts/resolve/utility_scripts/tests/testing_create_timeline_item_from_path.py b/openpype/hosts/resolve/utility_scripts/tests/testing_create_timeline_item_from_path.py index afa311e0b8..477955d527 100644 --- a/openpype/hosts/resolve/utility_scripts/tests/testing_create_timeline_item_from_path.py +++ b/openpype/hosts/resolve/utility_scripts/tests/testing_create_timeline_item_from_path.py @@ -1,8 +1,8 @@ #! python3 import os import sys -import avalon.api as avalon -import openpype + +from openpype.pipeline import install_host from openpype.hosts.resolve import TestGUI import openpype.hosts.resolve as bmdvr import clique @@ -13,10 +13,8 @@ class ThisTestGUI(TestGUI): def __init__(self): super(ThisTestGUI, self).__init__() - # Registers openpype's Global pyblish plugins - openpype.install() # activate resolve from openpype - avalon.install(bmdvr) + install_host(bmdvr) def _open_dir_button_pressed(self, event): # selected_path = self.fu.RequestFile(os.path.expanduser("~")) diff --git a/openpype/hosts/resolve/utility_scripts/tests/testing_load_media_pool_item.py b/openpype/hosts/resolve/utility_scripts/tests/testing_load_media_pool_item.py index cfdbe890e5..872d620162 100644 --- a/openpype/hosts/resolve/utility_scripts/tests/testing_load_media_pool_item.py +++ b/openpype/hosts/resolve/utility_scripts/tests/testing_load_media_pool_item.py @@ -1,6 +1,5 @@ #! python3 -import avalon.api as avalon -import openpype +from openpype.pipeline import install_host import openpype.hosts.resolve as bmdvr @@ -15,8 +14,7 @@ def file_processing(fpath): if __name__ == "__main__": path = "C:/CODE/__openpype_projects/jtest03dev/shots/sq01/mainsq01sh030/publish/plate/plateMain/v006/jt3d_mainsq01sh030_plateMain_v006.0996.exr" - openpype.install() # activate resolve from openpype - avalon.install(bmdvr) + install_host(bmdvr) - file_processing(path) \ No newline at end of file + file_processing(path) diff --git a/openpype/hosts/tvpaint/api/launch_script.py b/openpype/hosts/tvpaint/api/launch_script.py index e66bf61df6..0b25027fc6 100644 --- a/openpype/hosts/tvpaint/api/launch_script.py +++ b/openpype/hosts/tvpaint/api/launch_script.py @@ -8,8 +8,8 @@ import logging from Qt import QtWidgets, QtCore, QtGui -from avalon import api from openpype import style +from openpype.pipeline import install_host from openpype.hosts.tvpaint.api.communication_server import ( CommunicationWrapper ) @@ -31,7 +31,7 @@ def main(launch_args): qt_app = QtWidgets.QApplication([]) # Execute pipeline installation - api.install(tvpaint_host) + install_host(tvpaint_host) # Create Communicator object and trigger launch # - this must be done before anything is processed diff --git a/openpype/hosts/tvpaint/api/pipeline.py b/openpype/hosts/tvpaint/api/pipeline.py index cafdf0701d..78c10c3dae 100644 --- a/openpype/hosts/tvpaint/api/pipeline.py +++ b/openpype/hosts/tvpaint/api/pipeline.py @@ -67,11 +67,8 @@ instances=2 def install(): - """Install Maya-specific functionality of avalon-core. + """Install TVPaint-specific functionality.""" - This function is called automatically on calling `api.install(maya)`. - - """ log.info("OpenPype - Installing TVPaint integration") io.install() @@ -96,11 +93,11 @@ def install(): def uninstall(): - """Uninstall TVPaint-specific functionality of avalon-core. - - This function is called automatically on calling `api.uninstall()`. + """Uninstall TVPaint-specific functionality. + This function is called automatically on calling `uninstall_host()`. """ + log.info("OpenPype - Uninstalling TVPaint integration") pyblish.api.deregister_host("tvpaint") pyblish.api.deregister_plugin_path(PUBLISH_PATH) diff --git a/openpype/hosts/unreal/integration/Content/Python/init_unreal.py b/openpype/hosts/unreal/integration/Content/Python/init_unreal.py index 2ecd301c25..4bb03b07ed 100644 --- a/openpype/hosts/unreal/integration/Content/Python/init_unreal.py +++ b/openpype/hosts/unreal/integration/Content/Python/init_unreal.py @@ -2,13 +2,7 @@ import unreal openpype_detected = True try: - from avalon import api -except ImportError as exc: - api = None - openpype_detected = False - unreal.log_error("Avalon: cannot load Avalon [ {} ]".format(exc)) - -try: + from openpype.pipeline import install_host from openpype.hosts.unreal import api as openpype_host except ImportError as exc: openpype_host = None @@ -16,7 +10,7 @@ except ImportError as exc: unreal.log_error("OpenPype: cannot load OpenPype [ {} ]".format(exc)) if openpype_detected: - api.install(openpype_host) + install_host(openpype_host) @unreal.uclass() diff --git a/openpype/hosts/webpublisher/api/__init__.py b/openpype/hosts/webpublisher/api/__init__.py index dbeb628073..72bbffd099 100644 --- a/openpype/hosts/webpublisher/api/__init__.py +++ b/openpype/hosts/webpublisher/api/__init__.py @@ -1,7 +1,6 @@ import os import logging -from avalon import api as avalon from avalon import io from pyblish import api as pyblish import openpype.hosts.webpublisher diff --git a/openpype/lib/remote_publish.py b/openpype/lib/remote_publish.py index 9d97671a61..8a42daf4e9 100644 --- a/openpype/lib/remote_publish.py +++ b/openpype/lib/remote_publish.py @@ -1,13 +1,12 @@ import os from datetime import datetime -import sys -from bson.objectid import ObjectId import collections +from bson.objectid import ObjectId + import pyblish.util import pyblish.api -from openpype import uninstall from openpype.lib.mongo import OpenPypeMongoConnection from openpype.lib.plugin_tools import parse_json @@ -81,7 +80,6 @@ def publish(log, close_plugin_name=None): if result["error"]: log.error(error_format.format(**result)) - uninstall() if close_plugin: # close host app explicitly after error context = pyblish.api.Context() close_plugin().process(context) @@ -118,7 +116,6 @@ def publish_and_log(dbcon, _id, log, close_plugin_name=None, batch_id=None): if result["error"]: log.error(error_format.format(**result)) - uninstall() log_lines = [error_format.format(**result)] + log_lines dbcon.update_one( {"_id": _id}, diff --git a/openpype/tests/test_avalon_plugin_presets.py b/openpype/tests/test_avalon_plugin_presets.py index c491be1c05..464c216d6f 100644 --- a/openpype/tests/test_avalon_plugin_presets.py +++ b/openpype/tests/test_avalon_plugin_presets.py @@ -1,6 +1,5 @@ -import avalon.api as api -import openpype from openpype.pipeline import ( + install_host, LegacyCreator, register_creator_plugin, discover_creator_plugins, @@ -23,15 +22,14 @@ class Test: __name__ = "test" ls = len - def __call__(self): - pass + @staticmethod + def install(): + register_creator_plugin(MyTestCreator) def test_avalon_plugin_presets(monkeypatch, printer): + install_host(Test) - openpype.install() - api.register_host(Test()) - register_creator_plugin(MyTestCreator) plugins = discover_creator_plugins() printer("Test if we got our test plugin") assert MyTestCreator in plugins From 331f87bd15c15099eb30d678c871fe3809dba885 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 1 Apr 2022 19:41:18 +0200 Subject: [PATCH 07/35] changed usages of registered host and config --- openpype/hosts/testhost/run_publish.py | 4 ++-- openpype/pype_commands.py | 10 ++++++---- openpype/tools/loader/app.py | 10 ---------- openpype/tools/utils/host_tools.py | 3 ++- openpype/tools/utils/lib.py | 15 ++++++++------- 5 files changed, 18 insertions(+), 24 deletions(-) diff --git a/openpype/hosts/testhost/run_publish.py b/openpype/hosts/testhost/run_publish.py index 44860a30e4..cc80bdc604 100644 --- a/openpype/hosts/testhost/run_publish.py +++ b/openpype/hosts/testhost/run_publish.py @@ -48,8 +48,8 @@ from openpype.tools.publisher.window import PublisherWindow def main(): """Main function for testing purposes.""" - import avalon.api import pyblish.api + from openpype.pipeline import install_host from openpype.modules import ModulesManager from openpype.hosts.testhost import api as testhost @@ -57,7 +57,7 @@ def main(): for plugin_path in manager.collect_plugin_paths()["publish"]: pyblish.api.register_plugin_path(plugin_path) - avalon.api.install(testhost) + install_host(testhost) QtWidgets.QApplication.setAttribute(QtCore.Qt.AA_EnableHighDpiScaling) app = QtWidgets.QApplication([]) diff --git a/openpype/pype_commands.py b/openpype/pype_commands.py index c05eece2be..e0c8847040 100644 --- a/openpype/pype_commands.py +++ b/openpype/pype_commands.py @@ -101,7 +101,8 @@ class PypeCommands: RuntimeError: When there is no path to process. """ from openpype.modules import ModulesManager - from openpype import install, uninstall + from openpype.pipeline import install_openpype_plugins + from openpype.api import Logger from openpype.tools.utils.host_tools import show_publish from openpype.tools.utils.lib import qt_app_context @@ -112,7 +113,7 @@ class PypeCommands: log = Logger.get_logger() - install() + install_openpype_plugins() manager = ModulesManager() @@ -294,7 +295,8 @@ class PypeCommands: # Register target and host import pyblish.api import pyblish.util - import avalon.api + + from openpype.pipeline import install_host from openpype.hosts.webpublisher import api as webpublisher log = PypeLogger.get_logger() @@ -315,7 +317,7 @@ class PypeCommands: for target in targets: pyblish.api.register_target(target) - avalon.api.install(webpublisher) + install_host(webpublisher) log.info("Running publish ...") diff --git a/openpype/tools/loader/app.py b/openpype/tools/loader/app.py index 923a1fabdb..23c0909f2b 100644 --- a/openpype/tools/loader/app.py +++ b/openpype/tools/loader/app.py @@ -608,14 +608,4 @@ def cli(args): # Store settings api.Session["AVALON_PROJECT"] = project - from avalon import pipeline - - # Find the set config - _config = pipeline.find_config() - if hasattr(_config, "install"): - _config.install() - else: - print("Config `%s` has no function `install`" % - _config.__name__) - show() diff --git a/openpype/tools/utils/host_tools.py b/openpype/tools/utils/host_tools.py index 2d9733ec94..b0c30f6dfb 100644 --- a/openpype/tools/utils/host_tools.py +++ b/openpype/tools/utils/host_tools.py @@ -6,6 +6,7 @@ use singleton approach with global functions (using helper anyway). import os import avalon.api import pyblish.api +from openpype.pipeline import registered_host from .lib import qt_app_context @@ -47,7 +48,7 @@ class HostToolsHelper: Window, validate_host_requirements ) # Host validation - host = avalon.api.registered_host() + host = registered_host() validate_host_requirements(host) workfiles_window = Window(parent=parent) diff --git a/openpype/tools/utils/lib.py b/openpype/tools/utils/lib.py index 422d0f5389..12dd637e6a 100644 --- a/openpype/tools/utils/lib.py +++ b/openpype/tools/utils/lib.py @@ -6,16 +6,17 @@ import collections from Qt import QtWidgets, QtCore, QtGui import qtawesome -import avalon.api - -from openpype.style import get_default_entity_icon_color +from openpype.style import ( + get_default_entity_icon_color, + get_objected_colors, +) +from openpype.resources import get_image_path +from openpype.lib import filter_profiles from openpype.api import ( get_project_settings, Logger ) -from openpype.lib import filter_profiles -from openpype.style import get_objected_colors -from openpype.resources import get_image_path +from openpype.pipeline import registered_host log = Logger.get_logger(__name__) @@ -402,7 +403,7 @@ class FamilyConfigCache: self.family_configs.clear() # Skip if we're not in host context - if not avalon.api.registered_host(): + if not registered_host(): return # Update the icons from the project configuration From f9043329b49573a3c3c89406a25bb266e2ca0106 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 1 Apr 2022 19:41:29 +0200 Subject: [PATCH 08/35] removed unused imports --- .../ftrack/event_handlers_user/action_create_folders.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/openpype/modules/ftrack/event_handlers_user/action_create_folders.py b/openpype/modules/ftrack/event_handlers_user/action_create_folders.py index d15a865124..0ed12bd03e 100644 --- a/openpype/modules/ftrack/event_handlers_user/action_create_folders.py +++ b/openpype/modules/ftrack/event_handlers_user/action_create_folders.py @@ -1,11 +1,6 @@ import os from openpype_modules.ftrack.lib import BaseAction, statics_icon -from avalon import lib as avalonlib -from openpype.api import ( - Anatomy, - get_project_settings -) -from openpype.lib import ApplicationManager +from openpype.api import Anatomy class CreateFolders(BaseAction): From adc27e5186d30ba87c41b1f973a69d225f9f1174 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 1 Apr 2022 19:41:46 +0200 Subject: [PATCH 09/35] changed how library loader is shown in tray --- openpype/modules/avalon_apps/avalon_app.py | 47 ++++++++++++---------- openpype/tools/libraryloader/app.py | 10 ----- openpype/tools/libraryloader/lib.py | 21 ---------- 3 files changed, 26 insertions(+), 52 deletions(-) delete mode 100644 openpype/tools/libraryloader/lib.py diff --git a/openpype/modules/avalon_apps/avalon_app.py b/openpype/modules/avalon_apps/avalon_app.py index 51a22323f1..1d21de129b 100644 --- a/openpype/modules/avalon_apps/avalon_app.py +++ b/openpype/modules/avalon_apps/avalon_app.py @@ -1,5 +1,5 @@ import os -import openpype + from openpype.modules import OpenPypeModule from openpype_interfaces import ITrayModule @@ -26,7 +26,8 @@ class AvalonModule(OpenPypeModule, ITrayModule): self.avalon_mongo_timeout = avalon_mongo_timeout # Tray attributes - self.libraryloader = None + self._library_loader_imported = None + self._library_loader_window = None self.rest_api_obj = None def get_global_environments(self): @@ -41,21 +42,11 @@ class AvalonModule(OpenPypeModule, ITrayModule): def tray_init(self): # Add library tool + self._library_loader_imported = False try: - from Qt import QtCore from openpype.tools.libraryloader import LibraryLoaderWindow - libraryloader = LibraryLoaderWindow( - show_projects=True, - show_libraries=True - ) - # Remove always on top flag for tray - window_flags = libraryloader.windowFlags() - if window_flags | QtCore.Qt.WindowStaysOnTopHint: - window_flags ^= QtCore.Qt.WindowStaysOnTopHint - libraryloader.setWindowFlags(window_flags) - self.libraryloader = libraryloader - + self._library_loader_imported = True except Exception: self.log.warning( "Couldn't load Library loader tool for tray.", @@ -64,7 +55,7 @@ class AvalonModule(OpenPypeModule, ITrayModule): # Definition of Tray menu def tray_menu(self, tray_menu): - if self.libraryloader is None: + if not self._library_loader_imported: return from Qt import QtWidgets @@ -84,17 +75,31 @@ class AvalonModule(OpenPypeModule, ITrayModule): return def show_library_loader(self): - if self.libraryloader is None: - return + if self._library_loader_window is None: + from Qt import QtCore + from openpype.tools.libraryloader import LibraryLoaderWindow + from openpype.pipeline import install_openpype_plugins - self.libraryloader.show() + libraryloader = LibraryLoaderWindow( + show_projects=True, + show_libraries=True + ) + # Remove always on top flag for tray + window_flags = libraryloader.windowFlags() + if window_flags | QtCore.Qt.WindowStaysOnTopHint: + window_flags ^= QtCore.Qt.WindowStaysOnTopHint + libraryloader.setWindowFlags(window_flags) + self._library_loader_window = libraryloader + + install_openpype_plugins() + + self._library_loader_window.show() # Raise and activate the window # for MacOS - self.libraryloader.raise_() + self._library_loader_window.raise_() # for Windows - self.libraryloader.activateWindow() - self.libraryloader.refresh() + self._library_loader_window.activateWindow() # Webserver module implementation def webserver_initialization(self, server_manager): diff --git a/openpype/tools/libraryloader/app.py b/openpype/tools/libraryloader/app.py index b73b415128..328e16205c 100644 --- a/openpype/tools/libraryloader/app.py +++ b/openpype/tools/libraryloader/app.py @@ -16,8 +16,6 @@ from openpype.tools.utils.assets_widget import MultiSelectAssetsWidget from openpype.modules import ModulesManager -from . import lib - module = sys.modules[__name__] module.window = None @@ -260,14 +258,6 @@ class LibraryLoaderWindow(QtWidgets.QDialog): self.dbcon.Session["AVALON_PROJECT"] = project_name - _config = lib.find_config() - if hasattr(_config, "install"): - _config.install() - else: - print( - "Config `%s` has no function `install`" % _config.__name__ - ) - self._subsets_widget.on_project_change(project_name) if self._repres_widget: self._repres_widget.on_project_change(project_name) diff --git a/openpype/tools/libraryloader/lib.py b/openpype/tools/libraryloader/lib.py deleted file mode 100644 index 182b48893a..0000000000 --- a/openpype/tools/libraryloader/lib.py +++ /dev/null @@ -1,21 +0,0 @@ -import os -import importlib -import logging - -log = logging.getLogger(__name__) - - -# `find_config` from `pipeline` -def find_config(): - log.info("Finding configuration for project..") - - config = os.environ["AVALON_CONFIG"] - - if not config: - raise EnvironmentError( - "No configuration found in " - "the project nor environment" - ) - - log.info("Found %s, loading.." % config) - return importlib.import_module(config) From 4364cd55c39e686348818ff0ea1b4de49631e396 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 1 Apr 2022 19:42:07 +0200 Subject: [PATCH 10/35] cleaned up openpype init file --- openpype/__init__.py | 97 -------------------------------------------- 1 file changed, 97 deletions(-) diff --git a/openpype/__init__.py b/openpype/__init__.py index 7fc7e63e61..810664707a 100644 --- a/openpype/__init__.py +++ b/openpype/__init__.py @@ -1,102 +1,5 @@ -# -*- coding: utf-8 -*- -"""Pype module.""" import os -import platform -import logging - -from .settings import get_project_settings -from .lib import ( - Anatomy, - filter_pyblish_plugins, - change_timer_to_current_context, - register_event_callback, -) - -log = logging.getLogger(__name__) PACKAGE_DIR = os.path.dirname(os.path.abspath(__file__)) PLUGINS_DIR = os.path.join(PACKAGE_DIR, "plugins") - -# Global plugin paths -PUBLISH_PATH = os.path.join(PLUGINS_DIR, "publish") -LOAD_PATH = os.path.join(PLUGINS_DIR, "load") - - -def install(): - """Install OpenPype to Avalon.""" - import avalon.api - import pyblish.api - from pyblish.lib import MessageHandler - from openpype.modules import load_modules - from openpype.pipeline import ( - register_loader_plugin_path, - register_inventory_action, - register_creator_plugin_path, - ) - - # Make sure modules are loaded - load_modules() - - def modified_emit(obj, record): - """Method replacing `emit` in Pyblish's MessageHandler.""" - record.msg = record.getMessage() - obj.records.append(record) - - MessageHandler.emit = modified_emit - - log.info("Registering global plug-ins..") - pyblish.api.register_plugin_path(PUBLISH_PATH) - pyblish.api.register_discovery_filter(filter_pyblish_plugins) - register_loader_plugin_path(LOAD_PATH) - - project_name = os.environ.get("AVALON_PROJECT") - - # Register studio specific plugins - if project_name: - anatomy = Anatomy(project_name) - anatomy.set_root_environments() - avalon.api.register_root(anatomy.roots) - - project_settings = get_project_settings(project_name) - platform_name = platform.system().lower() - project_plugins = ( - project_settings - .get("global", {}) - .get("project_plugins", {}) - .get(platform_name) - ) or [] - for path in project_plugins: - try: - path = str(path.format(**os.environ)) - except KeyError: - pass - - if not path or not os.path.exists(path): - continue - - pyblish.api.register_plugin_path(path) - register_loader_plugin_path(path) - register_creator_plugin_path(path) - register_inventory_action(path) - - # apply monkey patched discover to original one - log.info("Patching discovery") - - register_event_callback("taskChanged", _on_task_change) - - -def _on_task_change(): - change_timer_to_current_context() - - -def uninstall(): - """Uninstall Pype from Avalon.""" - import pyblish.api - from openpype.pipeline import deregister_loader_plugin_path - - log.info("Deregistering global plug-ins..") - pyblish.api.deregister_plugin_path(PUBLISH_PATH) - pyblish.api.deregister_discovery_filter(filter_pyblish_plugins) - deregister_loader_plugin_path(LOAD_PATH) - log.info("Global plug-ins unregistred") From 8ec4d9c8d4bc203c820910ac3b5bd879cfa4b210 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 4 Apr 2022 10:59:43 +0200 Subject: [PATCH 11/35] remove irrelevant comment --- openpype/hosts/celaction/api/cli.py | 1 - 1 file changed, 1 deletion(-) diff --git a/openpype/hosts/celaction/api/cli.py b/openpype/hosts/celaction/api/cli.py index 85e210f21a..ef73c7457a 100644 --- a/openpype/hosts/celaction/api/cli.py +++ b/openpype/hosts/celaction/api/cli.py @@ -71,7 +71,6 @@ def main(): _prepare_publish_environments() # Registers pype's Global pyblish plugins - # - use fake host install_openpype_plugins() if os.path.exists(PUBLISH_PATH): From c8a886d6ce575f1cedccecd1429876746bbaf0a1 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 4 Apr 2022 17:52:23 +0200 Subject: [PATCH 12/35] added install_openpype_plugins into load cli --- openpype/tools/loader/app.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/openpype/tools/loader/app.py b/openpype/tools/loader/app.py index 23c0909f2b..ab57f63c38 100644 --- a/openpype/tools/loader/app.py +++ b/openpype/tools/loader/app.py @@ -5,6 +5,7 @@ from avalon import api, io from openpype import style from openpype.lib import register_event_callback +from openpype.pipeline import install_openpype_plugins from openpype.tools.utils import ( lib, PlaceholderLineEdit @@ -608,4 +609,6 @@ def cli(args): # Store settings api.Session["AVALON_PROJECT"] = project + install_openpype_plugins() + show() From 578a0469c9a487948494f30a036bc14b8882323d Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 4 Apr 2022 17:57:27 +0200 Subject: [PATCH 13/35] pass project name to plugin install --- openpype/tools/loader/app.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/tools/loader/app.py b/openpype/tools/loader/app.py index ab57f63c38..fad284d82b 100644 --- a/openpype/tools/loader/app.py +++ b/openpype/tools/loader/app.py @@ -609,6 +609,6 @@ def cli(args): # Store settings api.Session["AVALON_PROJECT"] = project - install_openpype_plugins() + install_openpype_plugins(project) show() From 905eccdc4197cdf0db4c2dfdbcca86619736ef02 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 6 Apr 2022 13:58:53 +0200 Subject: [PATCH 14/35] fixed registered host callbacks --- openpype/hosts/flame/startup/openpype_in_flame.py | 9 +++++---- .../hosts/houdini/plugins/publish/collect_inputs.py | 5 +++-- .../houdini/plugins/publish/increment_current_file.py | 4 ++-- openpype/hosts/houdini/plugins/publish/save_scene.py | 5 +++-- openpype/hosts/maya/api/lib.py | 5 +++-- openpype/hosts/photoshop/api/pipeline.py | 4 ++-- openpype/hosts/tvpaint/plugins/load/load_workfile.py | 5 +++-- openpype/lib/avalon_context.py | 3 ++- openpype/pipeline/create/context.py | 2 +- openpype/pipeline/create/legacy_create.py | 3 ++- .../plugins/publish/collect_scene_loaded_versions.py | 5 +++-- openpype/scripts/fusion_switch_shot.py | 3 ++- openpype/tools/mayalookassigner/commands.py | 11 +++++++---- openpype/tools/mayalookassigner/vray_proxies.py | 5 +++-- openpype/tools/publisher/control.py | 8 +++++--- openpype/tools/sceneinventory/model.py | 9 ++++++--- openpype/tools/subsetmanager/model.py | 4 ++-- openpype/tools/subsetmanager/window.py | 9 ++++----- openpype/tools/workfiles/app.py | 3 ++- 19 files changed, 60 insertions(+), 42 deletions(-) diff --git a/openpype/hosts/flame/startup/openpype_in_flame.py b/openpype/hosts/flame/startup/openpype_in_flame.py index 7015abc7f4..779143cfb3 100644 --- a/openpype/hosts/flame/startup/openpype_in_flame.py +++ b/openpype/hosts/flame/startup/openpype_in_flame.py @@ -4,17 +4,18 @@ from Qt import QtWidgets from pprint import pformat import atexit -import avalon import openpype.hosts.flame.api as opfapi -from openpype.pipeline import install_host +from openpype.pipeline import ( + install_host, + registered_host, +) def openpype_install(): """Registering OpenPype in context """ install_host(opfapi) - print("Avalon registered hosts: {}".format( - avalon.api.registered_host())) + print("Avalon registered hosts: {}".format(registered_host())) # Exception handler diff --git a/openpype/hosts/houdini/plugins/publish/collect_inputs.py b/openpype/hosts/houdini/plugins/publish/collect_inputs.py index 39e2737e8c..8c7098c710 100644 --- a/openpype/hosts/houdini/plugins/publish/collect_inputs.py +++ b/openpype/hosts/houdini/plugins/publish/collect_inputs.py @@ -1,6 +1,7 @@ -import avalon.api as api import pyblish.api +from openpype.pipeline import registered_host + def collect_input_containers(nodes): """Collect containers that contain any of the node in `nodes`. @@ -18,7 +19,7 @@ def collect_input_containers(nodes): lookup = frozenset(nodes) containers = [] - host = api.registered_host() + host = registered_host() for container in host.ls(): node = container["node"] diff --git a/openpype/hosts/houdini/plugins/publish/increment_current_file.py b/openpype/hosts/houdini/plugins/publish/increment_current_file.py index 31c2954ee7..c5cacd1880 100644 --- a/openpype/hosts/houdini/plugins/publish/increment_current_file.py +++ b/openpype/hosts/houdini/plugins/publish/increment_current_file.py @@ -1,8 +1,8 @@ import pyblish.api -import avalon.api from openpype.api import version_up from openpype.action import get_errored_plugins_from_data +from openpype.pipeline import registered_host class IncrementCurrentFile(pyblish.api.InstancePlugin): @@ -41,7 +41,7 @@ class IncrementCurrentFile(pyblish.api.InstancePlugin): ) # Filename must not have changed since collecting - host = avalon.api.registered_host() + host = registered_host() current_file = host.current_file() assert ( context.data["currentFile"] == current_file diff --git a/openpype/hosts/houdini/plugins/publish/save_scene.py b/openpype/hosts/houdini/plugins/publish/save_scene.py index fe5962fbd3..6128c7af77 100644 --- a/openpype/hosts/houdini/plugins/publish/save_scene.py +++ b/openpype/hosts/houdini/plugins/publish/save_scene.py @@ -1,5 +1,6 @@ import pyblish.api -import avalon.api + +from openpype.pipeline import registered_host class SaveCurrentScene(pyblish.api.ContextPlugin): @@ -12,7 +13,7 @@ class SaveCurrentScene(pyblish.api.ContextPlugin): def process(self, context): # Filename must not have changed since collecting - host = avalon.api.registered_host() + host = registered_host() current_file = host.current_file() assert context.data['currentFile'] == current_file, ( "Collected filename from current scene name." diff --git a/openpype/hosts/maya/api/lib.py b/openpype/hosts/maya/api/lib.py index 92fc5133a9..3364e75769 100644 --- a/openpype/hosts/maya/api/lib.py +++ b/openpype/hosts/maya/api/lib.py @@ -26,6 +26,7 @@ from openpype.pipeline import ( loaders_from_representation, get_representation_path, load_container, + registered_host, ) from .commands import reset_frame_range @@ -1574,7 +1575,7 @@ def assign_look_by_version(nodes, version_id): "name": "json"}) # See if representation is already loaded, if so reuse it. - host = api.registered_host() + host = registered_host() representation_id = str(look_representation['_id']) for container in host.ls(): if (container['loader'] == "LookLoader" and @@ -2612,7 +2613,7 @@ def get_attr_in_layer(attr, layer): def fix_incompatible_containers(): """Backwards compatibility: old containers to use new ReferenceLoader""" - host = api.registered_host() + host = registered_host() for container in host.ls(): loader = container['loader'] diff --git a/openpype/hosts/photoshop/api/pipeline.py b/openpype/hosts/photoshop/api/pipeline.py index 7fdaa61b40..1f069c2636 100644 --- a/openpype/hosts/photoshop/api/pipeline.py +++ b/openpype/hosts/photoshop/api/pipeline.py @@ -3,7 +3,6 @@ from Qt import QtWidgets from bson.objectid import ObjectId import pyblish.api -import avalon.api from avalon import io from openpype.api import Logger @@ -14,6 +13,7 @@ from openpype.pipeline import ( deregister_loader_plugin_path, deregister_creator_plugin_path, AVALON_CONTAINER_ID, + registered_host, ) import openpype.hosts.photoshop @@ -33,7 +33,7 @@ def check_inventory(): if not lib.any_outdated(): return - host = avalon.api.registered_host() + host = registered_host() outdated_containers = [] for container in host.ls(): representation = container['representation'] diff --git a/openpype/hosts/tvpaint/plugins/load/load_workfile.py b/openpype/hosts/tvpaint/plugins/load/load_workfile.py index d224cfc390..1ce5449065 100644 --- a/openpype/hosts/tvpaint/plugins/load/load_workfile.py +++ b/openpype/hosts/tvpaint/plugins/load/load_workfile.py @@ -1,12 +1,13 @@ import os -from avalon import api, io +from avalon import io from openpype.lib import ( StringTemplate, get_workfile_template_key_from_context, get_workdir_data, get_last_workfile_with_version, ) +from openpype.pipeline import registered_host from openpype.api import Anatomy from openpype.hosts.tvpaint.api import lib, pipeline, plugin @@ -22,7 +23,7 @@ class LoadWorkfile(plugin.Loader): def load(self, context, name, namespace, options): # Load context of current workfile as first thing # - which context and extension has - host = api.registered_host() + host = registered_host() current_file = host.current_file() context = pipeline.get_current_workfile_context() diff --git a/openpype/lib/avalon_context.py b/openpype/lib/avalon_context.py index 0348d88be2..e82dcc558f 100644 --- a/openpype/lib/avalon_context.py +++ b/openpype/lib/avalon_context.py @@ -161,9 +161,10 @@ def is_latest(representation): @with_avalon def any_outdated(): """Return whether the current scene has any outdated content""" + from openpype.pipeline import registered_host checked = set() - host = avalon.api.registered_host() + host = registered_host() for container in host.ls(): representation = container['representation'] if representation in checked: diff --git a/openpype/pipeline/create/context.py b/openpype/pipeline/create/context.py index 3efdb0e5c3..0cc2819172 100644 --- a/openpype/pipeline/create/context.py +++ b/openpype/pipeline/create/context.py @@ -356,7 +356,7 @@ class CreatedInstance: already existing instance. creator(BaseCreator): Creator responsible for instance. host(ModuleType): Host implementation loaded with - `avalon.api.registered_host`. + `openpype.pipeline.registered_host`. new(bool): Is instance new. """ # Keys that can't be changed or removed from data after loading using diff --git a/openpype/pipeline/create/legacy_create.py b/openpype/pipeline/create/legacy_create.py index cf6629047e..46e0e3d663 100644 --- a/openpype/pipeline/create/legacy_create.py +++ b/openpype/pipeline/create/legacy_create.py @@ -142,7 +142,8 @@ def legacy_create(Creator, name, asset, options=None, data=None): Name of instance """ - from avalon.api import registered_host + from openpype.pipeline import registered_host + host = registered_host() plugin = Creator(name, asset, options, data) diff --git a/openpype/plugins/publish/collect_scene_loaded_versions.py b/openpype/plugins/publish/collect_scene_loaded_versions.py index 6746757e5f..e54592abb8 100644 --- a/openpype/plugins/publish/collect_scene_loaded_versions.py +++ b/openpype/plugins/publish/collect_scene_loaded_versions.py @@ -1,7 +1,8 @@ from bson.objectid import ObjectId import pyblish.api -from avalon import api, io +from avalon import io +from openpype.pipeline import registered_host class CollectSceneLoadedVersions(pyblish.api.ContextPlugin): @@ -24,7 +25,7 @@ class CollectSceneLoadedVersions(pyblish.api.ContextPlugin): ] def process(self, context): - host = api.registered_host() + host = registered_host() if host is None: self.log.warn("No registered host.") return diff --git a/openpype/scripts/fusion_switch_shot.py b/openpype/scripts/fusion_switch_shot.py index 6db8ff36a8..85a5821c6e 100644 --- a/openpype/scripts/fusion_switch_shot.py +++ b/openpype/scripts/fusion_switch_shot.py @@ -9,6 +9,7 @@ import avalon.fusion # Config imports import openpype.lib as pype +from openpype.pipeline import registered_host import openpype.hosts.fusion.lib as fusion_lib from openpype.lib.avalon_context import get_workdir_from_session @@ -176,7 +177,7 @@ def switch(asset_name, filepath=None, new=True): current_comp = fusion.LoadComp(filepath, quiet=True) assert current_comp is not None, "Fusion could not load '%s'" % filepath - host = api.registered_host() + host = registered_host() containers = list(host.ls()) assert containers, "Nothing to update" diff --git a/openpype/tools/mayalookassigner/commands.py b/openpype/tools/mayalookassigner/commands.py index 78fd51c7a3..8fd592d347 100644 --- a/openpype/tools/mayalookassigner/commands.py +++ b/openpype/tools/mayalookassigner/commands.py @@ -5,9 +5,12 @@ import os from bson.objectid import ObjectId import maya.cmds as cmds -from avalon import io, api +from avalon import io -from openpype.pipeline import remove_container +from openpype.pipeline import ( + remove_container, + registered_host, +) from openpype.hosts.maya.api import lib from .vray_proxies import get_alembic_ids_cache @@ -79,7 +82,7 @@ def get_all_asset_nodes(): list: list of dictionaries """ - host = api.registered_host() + host = registered_host() nodes = [] for container in host.ls(): @@ -192,7 +195,7 @@ def remove_unused_looks(): """ - host = api.registered_host() + host = registered_host() unused = [] for container in host.ls(): diff --git a/openpype/tools/mayalookassigner/vray_proxies.py b/openpype/tools/mayalookassigner/vray_proxies.py index 25621fc652..c97664f3cb 100644 --- a/openpype/tools/mayalookassigner/vray_proxies.py +++ b/openpype/tools/mayalookassigner/vray_proxies.py @@ -11,13 +11,14 @@ from bson.objectid import ObjectId import alembic.Abc from maya import cmds -from avalon import io, api +from avalon import io from openpype.pipeline import ( load_container, loaders_from_representation, discover_loader_plugins, get_representation_path, + registered_host, ) from openpype.hosts.maya.api import lib @@ -188,7 +189,7 @@ def load_look(version_id): "name": "ma"}) # See if representation is already loaded, if so reuse it. - host = api.registered_host() + host = registered_host() representation_id = str(look_representation['_id']) for container in host.ls(): if (container['loader'] == "LookLoader" and diff --git a/openpype/tools/publisher/control.py b/openpype/tools/publisher/control.py index 6707feac9c..2973d6a5bb 100644 --- a/openpype/tools/publisher/control.py +++ b/openpype/tools/publisher/control.py @@ -11,10 +11,12 @@ try: except Exception: from openpype.lib.python_2_comp import WeakMethod -import avalon.api import pyblish.api -from openpype.pipeline import PublishValidationError +from openpype.pipeline import ( + PublishValidationError, + registered_host, +) from openpype.pipeline.create import CreateContext from Qt import QtCore @@ -353,7 +355,7 @@ class PublisherController: """ def __init__(self, dbcon=None, headless=False): self.log = logging.getLogger("PublisherController") - self.host = avalon.api.registered_host() + self.host = registered_host() self.headless = headless self.create_context = CreateContext( diff --git a/openpype/tools/sceneinventory/model.py b/openpype/tools/sceneinventory/model.py index 091d6ca925..f8fd8a911a 100644 --- a/openpype/tools/sceneinventory/model.py +++ b/openpype/tools/sceneinventory/model.py @@ -7,8 +7,11 @@ from Qt import QtCore, QtGui import qtawesome from bson.objectid import ObjectId -from avalon import api, io, schema -from openpype.pipeline import HeroVersionType +from avalon import io, schema +from openpype.pipeline import ( + HeroVersionType, + registered_host, +) from openpype.style import get_default_entity_icon_color from openpype.tools.utils.models import TreeModel, Item from openpype.modules import ModulesManager @@ -181,7 +184,7 @@ class InventoryModel(TreeModel): def refresh(self, selected=None, items=None): """Refresh the model""" - host = api.registered_host() + host = registered_host() if not items: # for debugging or testing, injecting items from outside items = host.ls() diff --git a/openpype/tools/subsetmanager/model.py b/openpype/tools/subsetmanager/model.py index b76c3c2343..760a167b42 100644 --- a/openpype/tools/subsetmanager/model.py +++ b/openpype/tools/subsetmanager/model.py @@ -2,7 +2,7 @@ import uuid from Qt import QtCore, QtGui -from avalon import api +from openpype.pipeline import registered_host ITEM_ID_ROLE = QtCore.Qt.UserRole + 1 @@ -21,7 +21,7 @@ class InstanceModel(QtGui.QStandardItemModel): self._instances_by_item_id = {} instances = None - host = api.registered_host() + host = registered_host() list_instances = getattr(host, "list_instances", None) if list_instances: instances = list_instances() diff --git a/openpype/tools/subsetmanager/window.py b/openpype/tools/subsetmanager/window.py index a53af52174..6314e67015 100644 --- a/openpype/tools/subsetmanager/window.py +++ b/openpype/tools/subsetmanager/window.py @@ -4,9 +4,8 @@ import sys from Qt import QtWidgets, QtCore import qtawesome -from avalon import api - from openpype import style +from openpype.pipeline import registered_host from openpype.tools.utils import PlaceholderLineEdit from openpype.tools.utils.lib import ( iter_model_rows, @@ -106,7 +105,7 @@ class SubsetManagerWindow(QtWidgets.QDialog): self._details_widget.set_details(container, item_id) def _on_save(self): - host = api.registered_host() + host = registered_host() if not hasattr(host, "save_instances"): print("BUG: Host does not have \"save_instances\" method") return @@ -141,7 +140,7 @@ class SubsetManagerWindow(QtWidgets.QDialog): # Prepare menu menu = QtWidgets.QMenu(self) actions = [] - host = api.registered_host() + host = registered_host() if hasattr(host, "remove_instance"): action = QtWidgets.QAction("Remove instance", menu) action.setData(host.remove_instance) @@ -176,7 +175,7 @@ class SubsetManagerWindow(QtWidgets.QDialog): self._details_widget.set_details(None, None) self._model.refresh() - host = api.registered_host() + host = registered_host() dev_mode = os.environ.get("AVALON_DEVELOP_MODE") or "" editable = False if dev_mode.lower() in ("1", "yes", "true", "on"): diff --git a/openpype/tools/workfiles/app.py b/openpype/tools/workfiles/app.py index f0e7900cf5..38e1911060 100644 --- a/openpype/tools/workfiles/app.py +++ b/openpype/tools/workfiles/app.py @@ -3,6 +3,7 @@ import logging from avalon import api +from openpype.pipeline import registered_host from openpype.tools.utils import qt_app_context from .window import Window @@ -47,7 +48,7 @@ def show(root=None, debug=False, parent=None, use_context=True, save=True): except (AttributeError, RuntimeError): pass - host = api.registered_host() + host = registered_host() validate_host_requirements(host) if debug: From 2cd10be1557ff7600b44417179d4cfed2f2a8ea3 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 6 Apr 2022 14:00:36 +0200 Subject: [PATCH 15/35] fixed registered host in workfiles tool --- openpype/tools/workfiles/files_widget.py | 3 ++- openpype/tools/workfiles/save_as_dialog.py | 5 +++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/openpype/tools/workfiles/files_widget.py b/openpype/tools/workfiles/files_widget.py index 56af7752da..bb2ded3b94 100644 --- a/openpype/tools/workfiles/files_widget.py +++ b/openpype/tools/workfiles/files_widget.py @@ -18,6 +18,7 @@ from openpype.lib.avalon_context import ( update_current_task, compute_session_changes ) +from openpype.pipeline import registered_host from .model import ( WorkAreaFilesModel, PublishFilesModel, @@ -93,7 +94,7 @@ class FilesWidget(QtWidgets.QWidget): # This is not root but workfile directory self._workfiles_root = None self._workdir_path = None - self.host = api.registered_host() + self.host = registered_host() # Whether to automatically select the latest modified # file on a refresh of the files model. diff --git a/openpype/tools/workfiles/save_as_dialog.py b/openpype/tools/workfiles/save_as_dialog.py index f5ae393d0f..0a7c7821ba 100644 --- a/openpype/tools/workfiles/save_as_dialog.py +++ b/openpype/tools/workfiles/save_as_dialog.py @@ -11,6 +11,7 @@ from openpype.lib import ( get_last_workfile_with_version, get_workdir_data, ) +from openpype.pipeline import registered_host from openpype.tools.utils import PlaceholderLineEdit log = logging.getLogger(__name__) @@ -65,7 +66,7 @@ class CommentMatcher(object): return # Create a regex group for extensions - extensions = api.registered_host().file_extensions() + extensions = registered_host().file_extensions() any_extension = "(?:{})".format( "|".join(re.escape(ext[1:]) for ext in extensions) ) @@ -200,7 +201,7 @@ class SaveAsDialog(QtWidgets.QDialog): self.setWindowFlags(self.windowFlags() | QtCore.Qt.FramelessWindowHint) self.result = None - self.host = api.registered_host() + self.host = registered_host() self.root = root self.work_file = None self._extensions = extensions From cadd5d1e5db0f5cc18ac231cb0287a11ee7b4fe5 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 6 Apr 2022 15:02:42 +0200 Subject: [PATCH 16/35] fix registered host in AE host --- openpype/hosts/aftereffects/api/pipeline.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/openpype/hosts/aftereffects/api/pipeline.py b/openpype/hosts/aftereffects/api/pipeline.py index 94bc369856..4953b19ced 100644 --- a/openpype/hosts/aftereffects/api/pipeline.py +++ b/openpype/hosts/aftereffects/api/pipeline.py @@ -15,6 +15,7 @@ from openpype.pipeline import ( deregister_loader_plugin_path, deregister_creator_plugin_path, AVALON_CONTAINER_ID, + registered_host, ) import openpype.hosts.aftereffects from openpype.lib import register_event_callback @@ -37,7 +38,7 @@ def check_inventory(): if not lib.any_outdated(): return - host = pyblish.api.registered_host() + host = registered_host() outdated_containers = [] for container in host.ls(): representation = container['representation'] @@ -54,12 +55,12 @@ def check_inventory(): # Warn about outdated containers. print("Starting new QApplication..") app = QtWidgets.QApplication(sys.argv) - - message_box = QtWidgets.QMessageBox() - message_box.setIcon(QtWidgets.QMessageBox.Warning) - msg = "There are outdated containers in the scene." - message_box.setText(msg) - message_box.exec_() + if outdated_containers: + message_box = QtWidgets.QMessageBox() + message_box.setIcon(QtWidgets.QMessageBox.Warning) + msg = "There are outdated containers in the scene." + message_box.setText(msg) + message_box.exec_() def application_launch(): From 91c461a1f761fa206906fa3531b74f9afd2762be Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 6 Apr 2022 15:03:27 +0200 Subject: [PATCH 17/35] change registering logs --- openpype/hosts/flame/startup/openpype_in_flame.py | 2 +- openpype/hosts/fusion/utility_scripts/__OpenPype_Menu__.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/flame/startup/openpype_in_flame.py b/openpype/hosts/flame/startup/openpype_in_flame.py index 779143cfb3..f2ac23b19e 100644 --- a/openpype/hosts/flame/startup/openpype_in_flame.py +++ b/openpype/hosts/flame/startup/openpype_in_flame.py @@ -15,7 +15,7 @@ def openpype_install(): """Registering OpenPype in context """ install_host(opfapi) - print("Avalon registered hosts: {}".format(registered_host())) + print("Registered host: {}".format(registered_host())) # Exception handler diff --git a/openpype/hosts/fusion/utility_scripts/__OpenPype_Menu__.py b/openpype/hosts/fusion/utility_scripts/__OpenPype_Menu__.py index aa98563785..de8fc4b3b4 100644 --- a/openpype/hosts/fusion/utility_scripts/__OpenPype_Menu__.py +++ b/openpype/hosts/fusion/utility_scripts/__OpenPype_Menu__.py @@ -17,7 +17,7 @@ def main(env): # activate resolve from pype install_host(api) - log.info(f"Avalon registered hosts: {registered_host()}") + log.info(f"Registered host: {registered_host()}") menu.launch_openpype_menu() From a2a2d5e193798783f1be34d38820c16ece48d404 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 6 Apr 2022 15:41:30 +0200 Subject: [PATCH 18/35] removed repeated logic of any_outdata from after effects --- openpype/hosts/aftereffects/api/pipeline.py | 25 +++++---------------- 1 file changed, 5 insertions(+), 20 deletions(-) diff --git a/openpype/hosts/aftereffects/api/pipeline.py b/openpype/hosts/aftereffects/api/pipeline.py index 4953b19ced..3ed2de0e9d 100644 --- a/openpype/hosts/aftereffects/api/pipeline.py +++ b/openpype/hosts/aftereffects/api/pipeline.py @@ -38,29 +38,14 @@ def check_inventory(): if not lib.any_outdated(): return - host = registered_host() - outdated_containers = [] - for container in host.ls(): - representation = container['representation'] - representation_doc = io.find_one( - { - "_id": ObjectId(representation), - "type": "representation" - }, - projection={"parent": True} - ) - if representation_doc and not lib.is_latest(representation_doc): - outdated_containers.append(container) - # Warn about outdated containers. print("Starting new QApplication..") app = QtWidgets.QApplication(sys.argv) - if outdated_containers: - message_box = QtWidgets.QMessageBox() - message_box.setIcon(QtWidgets.QMessageBox.Warning) - msg = "There are outdated containers in the scene." - message_box.setText(msg) - message_box.exec_() + message_box = QtWidgets.QMessageBox() + message_box.setIcon(QtWidgets.QMessageBox.Warning) + msg = "There are outdated containers in the scene." + message_box.setText(msg) + message_box.exec_() def application_launch(): From fe3758581bbbd2f3249ebccdc20e30aa613a071b Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 11 Apr 2022 14:55:59 +0200 Subject: [PATCH 19/35] disable maintain selection on unreal creator plugin --- openpype/hosts/unreal/api/plugin.py | 1 + 1 file changed, 1 insertion(+) diff --git a/openpype/hosts/unreal/api/plugin.py b/openpype/hosts/unreal/api/plugin.py index b24bab831d..d8d2f2420d 100644 --- a/openpype/hosts/unreal/api/plugin.py +++ b/openpype/hosts/unreal/api/plugin.py @@ -10,6 +10,7 @@ from openpype.pipeline import ( class Creator(LegacyCreator): """This serves as skeleton for future OpenPype specific functionality""" defaults = ['Main'] + maintain_selection = False class Loader(LoaderPlugin, ABC): From 9306314a1bd805ce8d64937e70526cfbc8c91d1e Mon Sep 17 00:00:00 2001 From: Toke Jepsen Date: Tue, 12 Apr 2022 17:48:20 +0100 Subject: [PATCH 20/35] Update collect_render.py Better error message for non selected render camera edge case. --- openpype/hosts/maya/plugins/publish/collect_render.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/openpype/hosts/maya/plugins/publish/collect_render.py b/openpype/hosts/maya/plugins/publish/collect_render.py index a525b562f3..7791169791 100644 --- a/openpype/hosts/maya/plugins/publish/collect_render.py +++ b/openpype/hosts/maya/plugins/publish/collect_render.py @@ -194,11 +194,13 @@ class CollectMayaRender(pyblish.api.ContextPlugin): assert render_products, "no render products generated" exp_files = [] multipart = False + render_cameras = [] for product in render_products: if product.multipart: multipart = True product_name = product.productName if product.camera and layer_render_products.has_camera_token(): + render_cameras.append(product.camera) product_name = "{}{}".format( product.camera, "_" + product_name if product_name else "") @@ -208,6 +210,8 @@ class CollectMayaRender(pyblish.api.ContextPlugin): product) }) + assert render_cameras, "No render cameras found." + self.log.info("multipart: {}".format( multipart)) assert exp_files, "no file names were generated, this is bug" From 432f1264066f7286ec1dd4a3d1e3ccfbf2903046 Mon Sep 17 00:00:00 2001 From: Toke Jepsen Date: Tue, 12 Apr 2022 17:51:51 +0100 Subject: [PATCH 21/35] Update collect_render.py Hound --- openpype/hosts/maya/plugins/publish/collect_render.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/maya/plugins/publish/collect_render.py b/openpype/hosts/maya/plugins/publish/collect_render.py index 7791169791..154f0376ae 100644 --- a/openpype/hosts/maya/plugins/publish/collect_render.py +++ b/openpype/hosts/maya/plugins/publish/collect_render.py @@ -211,7 +211,7 @@ class CollectMayaRender(pyblish.api.ContextPlugin): }) assert render_cameras, "No render cameras found." - + self.log.info("multipart: {}".format( multipart)) assert exp_files, "no file names were generated, this is bug" From a5dd9f4e0e1822b2314dbf9c13a0cd2e1abb034b Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 12 Apr 2022 18:55:57 +0200 Subject: [PATCH 22/35] renamed process_context to context_tools --- openpype/hosts/celaction/api/cli.py | 2 +- openpype/pipeline/__init__.py | 2 +- openpype/pipeline/{process_context.py => context_tools.py} | 0 3 files changed, 2 insertions(+), 2 deletions(-) rename openpype/pipeline/{process_context.py => context_tools.py} (100%) diff --git a/openpype/hosts/celaction/api/cli.py b/openpype/hosts/celaction/api/cli.py index ef73c7457a..8c7b3a2e74 100644 --- a/openpype/hosts/celaction/api/cli.py +++ b/openpype/hosts/celaction/api/cli.py @@ -11,7 +11,7 @@ import openpype import openpype.hosts.celaction from openpype.hosts.celaction import api as celaction from openpype.tools.utils import host_tools -from openpype.pipeline.process_context import install_openpype_plugins +from openpype.pipeline import install_openpype_plugins log = Logger().get_logger("Celaction_cli_publisher") diff --git a/openpype/pipeline/__init__.py b/openpype/pipeline/__init__.py index de04548616..308be6da64 100644 --- a/openpype/pipeline/__init__.py +++ b/openpype/pipeline/__init__.py @@ -69,7 +69,7 @@ from .actions import ( deregister_inventory_action_path, ) -from .process_context import ( +from .context_tools import ( install_openpype_plugins, install_host, uninstall_host, diff --git a/openpype/pipeline/process_context.py b/openpype/pipeline/context_tools.py similarity index 100% rename from openpype/pipeline/process_context.py rename to openpype/pipeline/context_tools.py From 4ceaa605c3522dcfadfb8926b6aea31b80a7be62 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 13 Apr 2022 13:50:31 +0200 Subject: [PATCH 23/35] fix tray publisher installation --- openpype/tools/traypublisher/window.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/tools/traypublisher/window.py b/openpype/tools/traypublisher/window.py index d0453c4f23..a550c88ead 100644 --- a/openpype/tools/traypublisher/window.py +++ b/openpype/tools/traypublisher/window.py @@ -8,8 +8,8 @@ publishing plugins. from Qt import QtWidgets, QtCore -import avalon.api from avalon.api import AvalonMongoDB +from openpype.pipeline import install_host from openpype.hosts.traypublisher import ( api as traypublisher ) @@ -163,7 +163,7 @@ class TrayPublishWindow(PublisherWindow): def main(): - avalon.api.install(traypublisher) + install_host(traypublisher) app = QtWidgets.QApplication([]) window = TrayPublishWindow() window.show() From d2533a6a1b22c38a03b7841cf967ed238444d932 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 13 Apr 2022 17:28:35 +0200 Subject: [PATCH 24/35] OP-2958 - add aov matching even for remainder Previously filtering used only for collections. --- .../deadline/plugins/publish/submit_publish_job.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/openpype/modules/deadline/plugins/publish/submit_publish_job.py b/openpype/modules/deadline/plugins/publish/submit_publish_job.py index c444c3aa53..af388dce99 100644 --- a/openpype/modules/deadline/plugins/publish/submit_publish_job.py +++ b/openpype/modules/deadline/plugins/publish/submit_publish_job.py @@ -524,6 +524,7 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin): """ representations = [] + host_name = os.environ.get("AVALON_APP", "") collections, remainders = clique.assemble(exp_files) # create representation for every collected sequento ce @@ -541,7 +542,6 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin): preview = True else: render_file_name = list(collection)[0] - host_name = os.environ.get("AVALON_APP", "") # if filtered aov name is found in filename, toggle it for # preview video rendering preview = match_aov_pattern( @@ -610,7 +610,11 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin): "files": os.path.basename(remainder), "stagingDir": os.path.dirname(remainder), } - if "render" in instance.get("families"): + + preview = match_aov_pattern( + host_name, self.aov_filter, remainder + ) + if preview: rep.update({ "fps": instance.get("fps"), "tags": ["review"] From fe38ff5e230e89722800e95731cc09ef4da7b1db Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 13 Apr 2022 18:21:46 +0200 Subject: [PATCH 25/35] OP-2958 - extended logging for profile filtering Updated logging for collect_ftrack_famiy --- openpype/lib/profiles_filtering.py | 27 ++++++++++--------- .../plugins/publish/collect_ftrack_family.py | 8 ++++++ 2 files changed, 23 insertions(+), 12 deletions(-) diff --git a/openpype/lib/profiles_filtering.py b/openpype/lib/profiles_filtering.py index 0bb901aff8..370703a68b 100644 --- a/openpype/lib/profiles_filtering.py +++ b/openpype/lib/profiles_filtering.py @@ -44,12 +44,6 @@ def _profile_exclusion(matching_profiles, logger): Returns: dict: Most matching profile. """ - - logger.info( - "Search for first most matching profile in match order:" - " Host name -> Task name -> Family." - ) - if not matching_profiles: return None @@ -168,6 +162,15 @@ def filter_profiles(profiles_data, key_values, keys_order=None, logger=None): _keys_order.append(key) keys_order = tuple(_keys_order) + log_parts = " | ".join([ + "{}: \"{}\"".format(*item) + for item in key_values.items() + ]) + + logger.info( + "Looking for matching profile for: {}".format(log_parts) + ) + matching_profiles = None highest_profile_points = -1 # Each profile get 1 point for each matching filter. Profile with most @@ -205,11 +208,6 @@ def filter_profiles(profiles_data, key_values, keys_order=None, logger=None): if profile_points == highest_profile_points: matching_profiles.append((profile, profile_scores)) - log_parts = " | ".join([ - "{}: \"{}\"".format(*item) - for item in key_values.items() - ]) - if not matching_profiles: logger.info( "None of profiles match your setup. {}".format(log_parts) @@ -221,4 +219,9 @@ def filter_profiles(profiles_data, key_values, keys_order=None, logger=None): "More than one profile match your setup. {}".format(log_parts) ) - return _profile_exclusion(matching_profiles, logger) + profile = _profile_exclusion(matching_profiles, logger) + if profile: + logger.info( + "Profile selected: {}".format(profile) + ) + return profile diff --git a/openpype/modules/ftrack/plugins/publish/collect_ftrack_family.py b/openpype/modules/ftrack/plugins/publish/collect_ftrack_family.py index 95987fe42e..5bfe0dea8b 100644 --- a/openpype/modules/ftrack/plugins/publish/collect_ftrack_family.py +++ b/openpype/modules/ftrack/plugins/publish/collect_ftrack_family.py @@ -34,6 +34,7 @@ class CollectFtrackFamily(pyblish.api.InstancePlugin): self.log.warning("No profiles present for adding Ftrack family") return + add_ftrack_family = False task_name = instance.data.get("task", avalon.api.Session["AVALON_TASK"]) host_name = avalon.api.Session["AVALON_APP"] @@ -69,6 +70,13 @@ class CollectFtrackFamily(pyblish.api.InstancePlugin): else: instance.data["families"] = ["ftrack"] + result_str = "Adding" + if not add_ftrack_family: + result_str = "Not adding" + self.log.info("{} 'ftrack' family for instance with '{}'".format( + result_str, family + )) + def _get_add_ftrack_f_from_addit_filters(self, additional_filters, families, From 7a4db3946eda42ce907c647dab9c713569992c3b Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 13 Apr 2022 18:27:51 +0200 Subject: [PATCH 26/35] OP-2958 - updated logging for collect_ftrack_famiy --- .../modules/ftrack/plugins/publish/collect_ftrack_family.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/openpype/modules/ftrack/plugins/publish/collect_ftrack_family.py b/openpype/modules/ftrack/plugins/publish/collect_ftrack_family.py index 5bfe0dea8b..158135c952 100644 --- a/openpype/modules/ftrack/plugins/publish/collect_ftrack_family.py +++ b/openpype/modules/ftrack/plugins/publish/collect_ftrack_family.py @@ -54,6 +54,8 @@ class CollectFtrackFamily(pyblish.api.InstancePlugin): additional_filters = profile.get("advanced_filtering") if additional_filters: + self.log.info("'{}' families used for additional filtering". + format(families)) add_ftrack_family = self._get_add_ftrack_f_from_addit_filters( additional_filters, families, From 537198f32e63e919ed7cd21776d047ca4ebb86d7 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 13 Apr 2022 18:31:59 +0200 Subject: [PATCH 27/35] OP-2958 - fix not using calculated preview --- openpype/modules/deadline/plugins/publish/submit_publish_job.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/modules/deadline/plugins/publish/submit_publish_job.py b/openpype/modules/deadline/plugins/publish/submit_publish_job.py index af388dce99..4f781de62d 100644 --- a/openpype/modules/deadline/plugins/publish/submit_publish_job.py +++ b/openpype/modules/deadline/plugins/publish/submit_publish_job.py @@ -619,7 +619,7 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin): "fps": instance.get("fps"), "tags": ["review"] }) - self._solve_families(instance, True) + self._solve_families(instance, preview) already_there = False for repre in instance.get("representations", []): From 5e41fbeb91e6ba02242449330006f3b9abf940a2 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 14 Apr 2022 11:39:44 +0200 Subject: [PATCH 28/35] Elaborated on task shor name Requested on discord --- website/docs/admin_settings_project_anatomy.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/admin_settings_project_anatomy.md b/website/docs/admin_settings_project_anatomy.md index 98003dc381..1ed877bb8a 100644 --- a/website/docs/admin_settings_project_anatomy.md +++ b/website/docs/admin_settings_project_anatomy.md @@ -59,7 +59,7 @@ We have a few required anatomy templates for OpenPype to work properly, however | `asset` | Name of asset or shot | | `task[name]` | Name of task | | `task[type]` | Type of task | -| `task[short]` | Shortname of task | +| `task[short]` | Short name of task type (eg. 'Modeling' > 'mdl') | | `parent` | Name of hierarchical parent | | `version` | Version number | | `subset` | Subset name | From 18f9fa845099b3a7d21f2c7004cde6b49c10a8fe Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 14 Apr 2022 11:44:33 +0200 Subject: [PATCH 29/35] Added screenshot of tasks configuration --- website/docs/admin_settings_project_anatomy.md | 3 +++ website/docs/assets/settings/anatomy_tasks.png | Bin 0 -> 27116 bytes 2 files changed, 3 insertions(+) create mode 100644 website/docs/assets/settings/anatomy_tasks.png diff --git a/website/docs/admin_settings_project_anatomy.md b/website/docs/admin_settings_project_anatomy.md index 1ed877bb8a..b98819cd8a 100644 --- a/website/docs/admin_settings_project_anatomy.md +++ b/website/docs/admin_settings_project_anatomy.md @@ -105,5 +105,8 @@ We have a few required anatomy templates for OpenPype to work properly, however ## Task Types +Current state of default Task descriptors. + +![tasks](assets/settings/anatomy_tasks.png) ## Colour Management and Formats \ No newline at end of file diff --git a/website/docs/assets/settings/anatomy_tasks.png b/website/docs/assets/settings/anatomy_tasks.png new file mode 100644 index 0000000000000000000000000000000000000000..16265cf8eb6d20a19720c33f734d009e379e3d8c GIT binary patch literal 27116 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

0AoXGxL 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 Date: Thu, 14 Apr 2022 12:39:34 +0200 Subject: [PATCH 30/35] fix host install in fusion script --- openpype/scripts/fusion_switch_shot.py | 27 ++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/openpype/scripts/fusion_switch_shot.py b/openpype/scripts/fusion_switch_shot.py index 85a5821c6e..3ba150902e 100644 --- a/openpype/scripts/fusion_switch_shot.py +++ b/openpype/scripts/fusion_switch_shot.py @@ -4,13 +4,16 @@ import sys import logging # Pipeline imports -from avalon import api, io -import avalon.fusion +from avalon import io +from openpype.hosts.fusion import api +import openpype.hosts.fusion.api.lib as fusion_lib # Config imports -import openpype.lib as pype -from openpype.pipeline import registered_host -import openpype.hosts.fusion.lib as fusion_lib +from openpype.lib import version_up +from openpype.pipeline import ( + install_host, + registered_host, +) from openpype.lib.avalon_context import get_workdir_from_session @@ -80,7 +83,7 @@ def _format_filepath(session): # Create new unqiue filepath if os.path.exists(new_filepath): - new_filepath = pype.version_up(new_filepath) + new_filepath = version_up(new_filepath) return new_filepath @@ -103,7 +106,7 @@ def _update_savers(comp, session): comp.Print("New renders to: %s\n" % renders) - with avalon.fusion.comp_lock_and_undo_chunk(comp): + with api.comp_lock_and_undo_chunk(comp): savers = comp.GetToolList(False, "Saver").values() for saver in savers: filepath = saver.GetAttrs("TOOLST_Clip_Name")[1.0] @@ -165,12 +168,12 @@ def switch(asset_name, filepath=None, new=True): # Get current project self._project = io.find_one({ "type": "project", - "name": api.Session["AVALON_PROJECT"] + "name": io.Session["AVALON_PROJECT"] }) # Go to comp if not filepath: - current_comp = avalon.fusion.get_current_comp() + current_comp = api.get_current_comp() assert current_comp is not None, "Could not find current comp" else: fusion = _get_fusion_instance() @@ -195,7 +198,7 @@ def switch(asset_name, filepath=None, new=True): current_comp.Print(message) # Build the session to switch to - switch_to_session = api.Session.copy() + switch_to_session = io.Session.copy() switch_to_session["AVALON_ASSET"] = asset['name'] if new: @@ -204,7 +207,7 @@ def switch(asset_name, filepath=None, new=True): # Update savers output based on new session _update_savers(current_comp, switch_to_session) else: - comp_path = pype.version_up(filepath) + comp_path = version_up(filepath) current_comp.Print(comp_path) @@ -235,7 +238,7 @@ if __name__ == '__main__': args, unknown = parser.parse_args() - api.install(avalon.fusion) + install_host(api) switch(args.asset_name, args.file_path) sys.exit(0) From 0882589209344b672e03b22c87a80b1a9ae64c2c Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 14 Apr 2022 12:41:27 +0200 Subject: [PATCH 31/35] fixed imports --- .../modules/clockify/launcher_actions/ClockifyStart.py | 8 +++++--- .../modules/clockify/launcher_actions/ClockifySync.py | 9 ++++++--- openpype/scripts/non_python_host_launch.py | 2 +- openpype/tools/stdout_broker/window.py | 8 +++++--- 4 files changed, 17 insertions(+), 10 deletions(-) diff --git a/openpype/modules/clockify/launcher_actions/ClockifyStart.py b/openpype/modules/clockify/launcher_actions/ClockifyStart.py index db51964eb7..6428d5e7aa 100644 --- a/openpype/modules/clockify/launcher_actions/ClockifyStart.py +++ b/openpype/modules/clockify/launcher_actions/ClockifyStart.py @@ -1,12 +1,14 @@ -from avalon import api, io +from avalon import io + from openpype.api import Logger +from openpype.pipeline import LauncherAction from openpype_modules.clockify.clockify_api import ClockifyAPI -log = Logger().get_logger(__name__) +log = Logger.get_logger(__name__) -class ClockifyStart(api.Action): +class ClockifyStart(LauncherAction): name = "clockify_start_timer" label = "Clockify - Start Timer" diff --git a/openpype/modules/clockify/launcher_actions/ClockifySync.py b/openpype/modules/clockify/launcher_actions/ClockifySync.py index 02982d373a..3c81e2766c 100644 --- a/openpype/modules/clockify/launcher_actions/ClockifySync.py +++ b/openpype/modules/clockify/launcher_actions/ClockifySync.py @@ -1,10 +1,13 @@ -from avalon import api, io +from avalon import io + from openpype_modules.clockify.clockify_api import ClockifyAPI from openpype.api import Logger -log = Logger().get_logger(__name__) +from openpype.pipeline import LauncherAction + +log = Logger.get_logger(__name__) -class ClockifySync(api.Action): +class ClockifySync(LauncherAction): name = "sync_to_clockify" label = "Sync to Clockify" diff --git a/openpype/scripts/non_python_host_launch.py b/openpype/scripts/non_python_host_launch.py index 43921f0483..f795af7bb3 100644 --- a/openpype/scripts/non_python_host_launch.py +++ b/openpype/scripts/non_python_host_launch.py @@ -15,7 +15,7 @@ CURRENT_FILE = os.path.abspath(__file__) def show_error_messagebox(title, message, detail_message=None): """Function will show message and process ends after closing it.""" from Qt import QtWidgets, QtCore - from avalon import style + from openpype import style app = QtWidgets.QApplication([]) app.setStyleSheet(style.load_stylesheet()) diff --git a/openpype/tools/stdout_broker/window.py b/openpype/tools/stdout_broker/window.py index a2190e0491..f5720ca05b 100644 --- a/openpype/tools/stdout_broker/window.py +++ b/openpype/tools/stdout_broker/window.py @@ -1,7 +1,9 @@ -from avalon import style -from Qt import QtWidgets, QtCore -import collections import re +import collections + +from Qt import QtWidgets + +from openpype import style class ConsoleDialog(QtWidgets.QDialog): From f8b3d3e9b144e5c527b382da3bd19630523e77e7 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 14 Apr 2022 17:30:22 +0200 Subject: [PATCH 32/35] updated push-protected action in github workflow --- .github/workflows/prerelease.yml | 2 +- .github/workflows/release.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/prerelease.yml b/.github/workflows/prerelease.yml index d9b4d8089c..5acd20007c 100644 --- a/.github/workflows/prerelease.yml +++ b/.github/workflows/prerelease.yml @@ -80,7 +80,7 @@ jobs: git tag -a $tag_name -m "nightly build" - name: Push to protected main branch - uses: CasperWA/push-protected@v2 + uses: CasperWA/push-protected@v2.10.0 with: token: ${{ secrets.ADMIN_TOKEN }} branch: main diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 917e6c884c..85864b4442 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -68,7 +68,7 @@ jobs: - name: 🔏 Push to protected main branch if: steps.version.outputs.release_tag != 'skip' - uses: CasperWA/push-protected@v2 + uses: CasperWA/push-protected@v2.10.0 with: token: ${{ secrets.ADMIN_TOKEN }} branch: main From 88d20b8cfd7c3e8b4c157e2dc1e265364d2b8e4c Mon Sep 17 00:00:00 2001 From: OpenPype Date: Thu, 14 Apr 2022 16:28:00 +0000 Subject: [PATCH 33/35] [Automated] Bump version --- CHANGELOG.md | 47 ++++++++++++++++++++++++++------------------- openpype/version.py | 2 +- pyproject.toml | 2 +- 3 files changed, 29 insertions(+), 22 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c216dd0595..8cca62cca3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,23 +1,45 @@ # Changelog -## [3.9.4-nightly.1](https://github.com/pypeclub/OpenPype/tree/HEAD) +## [3.9.4-nightly.2](https://github.com/pypeclub/OpenPype/tree/HEAD) [Full Changelog](https://github.com/pypeclub/OpenPype/compare/3.9.3...HEAD) ### 📖 Documentation +- Documentation: more info about Tasks [\#3062](https://github.com/pypeclub/OpenPype/pull/3062) - Documentation: Python requirements to 3.7.9 [\#3035](https://github.com/pypeclub/OpenPype/pull/3035) - Website Docs: Remove unused pages [\#2974](https://github.com/pypeclub/OpenPype/pull/2974) +**🆕 New features** + +- General: Local overrides for environment variables [\#3045](https://github.com/pypeclub/OpenPype/pull/3045) +- Flame: Flare integration preparation [\#2928](https://github.com/pypeclub/OpenPype/pull/2928) + **🚀 Enhancements** +- TVPaint: Added init file for worker to triggers missing sound file dialog [\#3053](https://github.com/pypeclub/OpenPype/pull/3053) +- Ftrack: Custom attributes can be filled in slate values [\#3036](https://github.com/pypeclub/OpenPype/pull/3036) - Resolve environment variable in google drive credential path [\#3008](https://github.com/pypeclub/OpenPype/pull/3008) **🐛 Bug fixes** +- GitHub: Updated push-protected action in github workflow [\#3064](https://github.com/pypeclub/OpenPype/pull/3064) +- Nuke: Typos in imports from Nuke implementation [\#3061](https://github.com/pypeclub/OpenPype/pull/3061) +- Hotfix: fixing deadline job publishing [\#3059](https://github.com/pypeclub/OpenPype/pull/3059) +- General: Extract Review handle invalid characters for ffmpeg [\#3050](https://github.com/pypeclub/OpenPype/pull/3050) +- Slate Review: Support to keep format on slate concatenation [\#3049](https://github.com/pypeclub/OpenPype/pull/3049) +- Webpublisher: fix processing of workfile [\#3048](https://github.com/pypeclub/OpenPype/pull/3048) - Ftrack: Integrate ftrack api fix [\#3044](https://github.com/pypeclub/OpenPype/pull/3044) - Webpublisher - removed wrong hardcoded family [\#3043](https://github.com/pypeclub/OpenPype/pull/3043) +- LibraryLoader: Use current project for asset query in families filter [\#3042](https://github.com/pypeclub/OpenPype/pull/3042) +- SiteSync: Providers ignore that site is disabled [\#3041](https://github.com/pypeclub/OpenPype/pull/3041) - Unreal: Creator import fixes [\#3040](https://github.com/pypeclub/OpenPype/pull/3040) +- SiteSync: fix transitive alternate sites, fix dropdown in Local Settings [\#3018](https://github.com/pypeclub/OpenPype/pull/3018) + +**Merged pull requests:** + +- Deadline: reworked pools assignment [\#3051](https://github.com/pypeclub/OpenPype/pull/3051) +- Houdini: Avoid ImportError on `hdefereval` when Houdini runs without UI [\#2987](https://github.com/pypeclub/OpenPype/pull/2987) ## [3.9.3](https://github.com/pypeclub/OpenPype/tree/3.9.3) (2022-04-07) @@ -38,24 +60,23 @@ - Ftrack: Add more options for note text of integrate ftrack note [\#3025](https://github.com/pypeclub/OpenPype/pull/3025) - Console Interpreter: Changed how console splitter size are reused on show [\#3016](https://github.com/pypeclub/OpenPype/pull/3016) - Deadline: Use more suitable name for sequence review logic [\#3015](https://github.com/pypeclub/OpenPype/pull/3015) +- General: default workfile subset name for workfile [\#3011](https://github.com/pypeclub/OpenPype/pull/3011) - Nuke: add concurrency attr to deadline job [\#3005](https://github.com/pypeclub/OpenPype/pull/3005) -- Deadline: priority configurable in Maya jobs [\#2995](https://github.com/pypeclub/OpenPype/pull/2995) - Workfiles tool: Save as published workfiles [\#2937](https://github.com/pypeclub/OpenPype/pull/2937) **🐛 Bug fixes** - Deadline: Fixed default value of use sequence for review [\#3033](https://github.com/pypeclub/OpenPype/pull/3033) - Settings UI: Version column can be extended so version are visible [\#3032](https://github.com/pypeclub/OpenPype/pull/3032) +- General: Fix validate asset docs plug-in filename and class name [\#3029](https://github.com/pypeclub/OpenPype/pull/3029) - General: Fix import after movements [\#3028](https://github.com/pypeclub/OpenPype/pull/3028) - Harmony: Added creating subset name for workfile from template [\#3024](https://github.com/pypeclub/OpenPype/pull/3024) - AfterEffects: Added creating subset name for workfile from template [\#3023](https://github.com/pypeclub/OpenPype/pull/3023) - General: Add example addons to ignored [\#3022](https://github.com/pypeclub/OpenPype/pull/3022) -- SiteSync: fix transitive alternate sites, fix dropdown in Local Settings [\#3018](https://github.com/pypeclub/OpenPype/pull/3018) - Maya: Remove missing import [\#3017](https://github.com/pypeclub/OpenPype/pull/3017) - Ftrack: multiple reviewable componets [\#3012](https://github.com/pypeclub/OpenPype/pull/3012) - Tray publisher: Fixes after code movement [\#3010](https://github.com/pypeclub/OpenPype/pull/3010) - Nuke: fixing unicode type detection in effect loaders [\#3002](https://github.com/pypeclub/OpenPype/pull/3002) -- Fix - remove doubled dot in workfile created from template [\#2998](https://github.com/pypeclub/OpenPype/pull/2998) - Nuke: removing redundant Ftrack asset when farm publishing [\#2996](https://github.com/pypeclub/OpenPype/pull/2996) **Merged pull requests:** @@ -75,12 +96,12 @@ **🆕 New features** - nuke: bypass baking [\#2992](https://github.com/pypeclub/OpenPype/pull/2992) -- Multiverse: Initial Support [\#2908](https://github.com/pypeclub/OpenPype/pull/2908) **🚀 Enhancements** - Photoshop: create image without instance [\#3001](https://github.com/pypeclub/OpenPype/pull/3001) - TVPaint: Render scene family [\#3000](https://github.com/pypeclub/OpenPype/pull/3000) +- Deadline: priority configurable in Maya jobs [\#2995](https://github.com/pypeclub/OpenPype/pull/2995) - Nuke: ReviewDataMov Read RAW attribute [\#2985](https://github.com/pypeclub/OpenPype/pull/2985) - General: `METADATA\_KEYS` constant as `frozenset` for optimal immutable lookup [\#2980](https://github.com/pypeclub/OpenPype/pull/2980) - General: Tools with host filters [\#2975](https://github.com/pypeclub/OpenPype/pull/2975) @@ -89,12 +110,11 @@ - NewPublisher: Prepared implementation of optional pyblish plugin [\#2943](https://github.com/pypeclub/OpenPype/pull/2943) - TVPaint: Extractor to convert PNG into EXR [\#2942](https://github.com/pypeclub/OpenPype/pull/2942) - Workfiles: Open published workfiles [\#2925](https://github.com/pypeclub/OpenPype/pull/2925) -- General: Default modules loaded dynamically [\#2923](https://github.com/pypeclub/OpenPype/pull/2923) -- Nuke: improving readability [\#2903](https://github.com/pypeclub/OpenPype/pull/2903) **🐛 Bug fixes** - Hosts: Remove path existence checks in 'add\_implementation\_envs' [\#3004](https://github.com/pypeclub/OpenPype/pull/3004) +- Fix - remove doubled dot in workfile created from template [\#2998](https://github.com/pypeclub/OpenPype/pull/2998) - PS: fix renaming subset incorrectly in PS [\#2991](https://github.com/pypeclub/OpenPype/pull/2991) - Fix: Disable setuptools auto discovery [\#2990](https://github.com/pypeclub/OpenPype/pull/2990) - AEL: fix opening existing workfile if no scene opened [\#2989](https://github.com/pypeclub/OpenPype/pull/2989) @@ -115,15 +135,12 @@ - Settings UI: Collapsed of collapsible wrapper works as expected [\#2934](https://github.com/pypeclub/OpenPype/pull/2934) - Maya: Do not pass `set` to maya commands \(fixes support for older maya versions\) [\#2932](https://github.com/pypeclub/OpenPype/pull/2932) - General: Don't print log record on OSError [\#2926](https://github.com/pypeclub/OpenPype/pull/2926) -- Flame: centos related debugging [\#2922](https://github.com/pypeclub/OpenPype/pull/2922) **🔀 Refactored code** - General: Move plugins register and discover [\#2935](https://github.com/pypeclub/OpenPype/pull/2935) - General: Move Attribute Definitions from pipeline [\#2931](https://github.com/pypeclub/OpenPype/pull/2931) - General: Removed silo references and terminal splash [\#2927](https://github.com/pypeclub/OpenPype/pull/2927) -- General: Move pipeline constants to OpenPype [\#2918](https://github.com/pypeclub/OpenPype/pull/2918) -- General: Move remaining plugins from avalon [\#2912](https://github.com/pypeclub/OpenPype/pull/2912) **Merged pull requests:** @@ -136,16 +153,6 @@ [Full Changelog](https://github.com/pypeclub/OpenPype/compare/CI/3.9.1-nightly.3...3.9.1) -**🚀 Enhancements** - -- Nuke: Add no-audio Tag [\#2911](https://github.com/pypeclub/OpenPype/pull/2911) -- General: Change how OPENPYPE\_DEBUG value is handled [\#2907](https://github.com/pypeclub/OpenPype/pull/2907) - -**🐛 Bug fixes** - -- General: Fix use of Anatomy roots [\#2904](https://github.com/pypeclub/OpenPype/pull/2904) -- Fixing gap detection in extract review [\#2902](https://github.com/pypeclub/OpenPype/pull/2902) - ## [3.9.0](https://github.com/pypeclub/OpenPype/tree/3.9.0) (2022-03-14) [Full Changelog](https://github.com/pypeclub/OpenPype/compare/CI/3.9.0-nightly.9...3.9.0) diff --git a/openpype/version.py b/openpype/version.py index 08dcbb5aed..5616e95677 100644 --- a/openpype/version.py +++ b/openpype/version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- """Package declaring Pype version.""" -__version__ = "3.9.4-nightly.1" +__version__ = "3.9.4-nightly.2" diff --git a/pyproject.toml b/pyproject.toml index adec7ab158..d7951180ce 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "OpenPype" -version = "3.9.4-nightly.1" # OpenPype +version = "3.9.4-nightly.2" # OpenPype description = "Open VFX and Animation pipeline with support." authors = ["OpenPype Team "] license = "MIT License" From df9d4167362b77e0432fd59412d0bca3dd771183 Mon Sep 17 00:00:00 2001 From: OpenPype Date: Fri, 15 Apr 2022 10:12:41 +0000 Subject: [PATCH 34/35] [Automated] Release --- CHANGELOG.md | 8 ++++---- openpype/version.py | 2 +- pyproject.toml | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8cca62cca3..57a4560667 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,14 +1,13 @@ # Changelog -## [3.9.4-nightly.2](https://github.com/pypeclub/OpenPype/tree/HEAD) +## [3.9.4](https://github.com/pypeclub/OpenPype/tree/3.9.4) (2022-04-15) -[Full Changelog](https://github.com/pypeclub/OpenPype/compare/3.9.3...HEAD) +[Full Changelog](https://github.com/pypeclub/OpenPype/compare/3.9.3...3.9.4) ### 📖 Documentation - Documentation: more info about Tasks [\#3062](https://github.com/pypeclub/OpenPype/pull/3062) - Documentation: Python requirements to 3.7.9 [\#3035](https://github.com/pypeclub/OpenPype/pull/3035) -- Website Docs: Remove unused pages [\#2974](https://github.com/pypeclub/OpenPype/pull/2974) **🆕 New features** @@ -62,6 +61,7 @@ - Deadline: Use more suitable name for sequence review logic [\#3015](https://github.com/pypeclub/OpenPype/pull/3015) - General: default workfile subset name for workfile [\#3011](https://github.com/pypeclub/OpenPype/pull/3011) - Nuke: add concurrency attr to deadline job [\#3005](https://github.com/pypeclub/OpenPype/pull/3005) +- Deadline: priority configurable in Maya jobs [\#2995](https://github.com/pypeclub/OpenPype/pull/2995) - Workfiles tool: Save as published workfiles [\#2937](https://github.com/pypeclub/OpenPype/pull/2937) **🐛 Bug fixes** @@ -91,6 +91,7 @@ ### 📖 Documentation - Documentation: Added mention of adding My Drive as a root [\#2999](https://github.com/pypeclub/OpenPype/pull/2999) +- Website Docs: Remove unused pages [\#2974](https://github.com/pypeclub/OpenPype/pull/2974) - Docs: Added MongoDB requirements [\#2951](https://github.com/pypeclub/OpenPype/pull/2951) **🆕 New features** @@ -101,7 +102,6 @@ - Photoshop: create image without instance [\#3001](https://github.com/pypeclub/OpenPype/pull/3001) - TVPaint: Render scene family [\#3000](https://github.com/pypeclub/OpenPype/pull/3000) -- Deadline: priority configurable in Maya jobs [\#2995](https://github.com/pypeclub/OpenPype/pull/2995) - Nuke: ReviewDataMov Read RAW attribute [\#2985](https://github.com/pypeclub/OpenPype/pull/2985) - General: `METADATA\_KEYS` constant as `frozenset` for optimal immutable lookup [\#2980](https://github.com/pypeclub/OpenPype/pull/2980) - General: Tools with host filters [\#2975](https://github.com/pypeclub/OpenPype/pull/2975) diff --git a/openpype/version.py b/openpype/version.py index 5616e95677..fe68dcfd4a 100644 --- a/openpype/version.py +++ b/openpype/version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- """Package declaring Pype version.""" -__version__ = "3.9.4-nightly.2" +__version__ = "3.9.4" diff --git a/pyproject.toml b/pyproject.toml index d7951180ce..156e17258e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "OpenPype" -version = "3.9.4-nightly.2" # OpenPype +version = "3.9.4" # OpenPype description = "Open VFX and Animation pipeline with support." authors = ["OpenPype Team "] license = "MIT License" From c704e823fe8fb457ffe4ca87df54585a91324932 Mon Sep 17 00:00:00 2001 From: OpenPype Date: Sat, 16 Apr 2022 03:39:27 +0000 Subject: [PATCH 35/35] [Automated] Bump version --- CHANGELOG.md | 14 +++++++++++--- openpype/version.py | 2 +- pyproject.toml | 2 +- 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 57a4560667..8cd45f2cc0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,13 +1,22 @@ # Changelog +## [3.9.5-nightly.1](https://github.com/pypeclub/OpenPype/tree/HEAD) + +[Full Changelog](https://github.com/pypeclub/OpenPype/compare/3.9.4...HEAD) + +**🐛 Bug fixes** + +- Nuke: Add aov matching even for remainder and prerender [\#3060](https://github.com/pypeclub/OpenPype/pull/3060) + ## [3.9.4](https://github.com/pypeclub/OpenPype/tree/3.9.4) (2022-04-15) -[Full Changelog](https://github.com/pypeclub/OpenPype/compare/3.9.3...3.9.4) +[Full Changelog](https://github.com/pypeclub/OpenPype/compare/CI/3.9.4-nightly.2...3.9.4) ### 📖 Documentation - Documentation: more info about Tasks [\#3062](https://github.com/pypeclub/OpenPype/pull/3062) - Documentation: Python requirements to 3.7.9 [\#3035](https://github.com/pypeclub/OpenPype/pull/3035) +- Website Docs: Remove unused pages [\#2974](https://github.com/pypeclub/OpenPype/pull/2974) **🆕 New features** @@ -76,7 +85,6 @@ - Maya: Remove missing import [\#3017](https://github.com/pypeclub/OpenPype/pull/3017) - Ftrack: multiple reviewable componets [\#3012](https://github.com/pypeclub/OpenPype/pull/3012) - Tray publisher: Fixes after code movement [\#3010](https://github.com/pypeclub/OpenPype/pull/3010) -- Nuke: fixing unicode type detection in effect loaders [\#3002](https://github.com/pypeclub/OpenPype/pull/3002) - Nuke: removing redundant Ftrack asset when farm publishing [\#2996](https://github.com/pypeclub/OpenPype/pull/2996) **Merged pull requests:** @@ -91,7 +99,6 @@ ### 📖 Documentation - Documentation: Added mention of adding My Drive as a root [\#2999](https://github.com/pypeclub/OpenPype/pull/2999) -- Website Docs: Remove unused pages [\#2974](https://github.com/pypeclub/OpenPype/pull/2974) - Docs: Added MongoDB requirements [\#2951](https://github.com/pypeclub/OpenPype/pull/2951) **🆕 New features** @@ -114,6 +121,7 @@ **🐛 Bug fixes** - Hosts: Remove path existence checks in 'add\_implementation\_envs' [\#3004](https://github.com/pypeclub/OpenPype/pull/3004) +- Nuke: fixing unicode type detection in effect loaders [\#3002](https://github.com/pypeclub/OpenPype/pull/3002) - Fix - remove doubled dot in workfile created from template [\#2998](https://github.com/pypeclub/OpenPype/pull/2998) - PS: fix renaming subset incorrectly in PS [\#2991](https://github.com/pypeclub/OpenPype/pull/2991) - Fix: Disable setuptools auto discovery [\#2990](https://github.com/pypeclub/OpenPype/pull/2990) diff --git a/openpype/version.py b/openpype/version.py index fe68dcfd4a..a2916925b4 100644 --- a/openpype/version.py +++ b/openpype/version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- """Package declaring Pype version.""" -__version__ = "3.9.4" +__version__ = "3.9.5-nightly.1" diff --git a/pyproject.toml b/pyproject.toml index 156e17258e..0d1d5dc9a3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "OpenPype" -version = "3.9.4" # OpenPype +version = "3.9.5-nightly.1" # OpenPype description = "Open VFX and Animation pipeline with support." authors = ["OpenPype Team "] license = "MIT License"