From a31b2d9d77f834441d65733c8ee91c6c3ca5b623 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Mon, 7 Aug 2023 18:48:27 +0200 Subject: [PATCH] SyncServer: Existence of module is optional (#5413) * 'get_repre_icons' have optional sync server * local settings have optional sync server * sync server is optional in sceneinventory * sync server is optional in loader tool * sync server is optional in library loader * sync server is optional in host dirmap * sync server is optional in nuke cache * sync server is optional in integrate plugin * added "sync_server" back to ignored modules for openpype package * fix missing variable * mark syncserver command as deprecated * define 'SYNC_SERVER_ROOT' * added method to receive icon paths * use sync server module to receive icons * fix scene inventory --- openpype/cli.py | 17 ++++++-- openpype/host/dirmap.py | 35 ++++++++++------ openpype/hosts/nuke/api/lib.py | 7 +++- .../modules/sync_server/sync_server_module.py | 30 ++++++++++---- openpype/modules/sync_server/utils.py | 3 ++ openpype/plugins/publish/integrate.py | 17 +++++--- openpype/pype_commands.py | 28 ------------- openpype/tools/libraryloader/app.py | 7 ++-- openpype/tools/loader/model.py | 40 ++++++++++++++----- openpype/tools/sceneinventory/lib.py | 22 ---------- openpype/tools/sceneinventory/model.py | 16 ++++---- openpype/tools/sceneinventory/view.py | 7 +++- .../local_settings/projects_widget.py | 18 ++++----- openpype/tools/utils/lib.py | 9 +++-- server_addon/create_ayon_addons.py | 3 +- 15 files changed, 142 insertions(+), 117 deletions(-) diff --git a/openpype/cli.py b/openpype/cli.py index 6d6a34b0fb..22ad16e937 100644 --- a/openpype/cli.py +++ b/openpype/cli.py @@ -338,12 +338,18 @@ def runtests(folder, mark, pyargs, test_data_folder, persist, app_variant, persist, app_variant, timeout, setup_only) -@main.command() +@main.command(help="DEPRECATED - run sync server") +@click.pass_context @click.option("-a", "--active_site", required=True, - help="Name of active stie") -def syncserver(active_site): + help="Name of active site") +def syncserver(ctx, active_site): """Run sync site server in background. + Deprecated: + This command is deprecated and will be removed in future versions. + Use '~/openpype_console module sync_server syncservice' instead. + + Details: Some Site Sync use cases need to expose site to another one. For example if majority of artists work in studio, they are not using SS at all, but if you want to expose published assets to 'studio' site @@ -359,7 +365,10 @@ def syncserver(active_site): if AYON_SERVER_ENABLED: raise RuntimeError("AYON does not support 'syncserver' command.") - PypeCommands().syncserver(active_site) + + from openpype.modules.sync_server.sync_server_module import ( + syncservice) + ctx.invoke(syncservice, active_site=active_site) @main.command() diff --git a/openpype/host/dirmap.py b/openpype/host/dirmap.py index e77f06e9d6..96a98e808e 100644 --- a/openpype/host/dirmap.py +++ b/openpype/host/dirmap.py @@ -32,19 +32,26 @@ class HostDirmap(object): """ def __init__( - self, host_name, project_name, project_settings=None, sync_module=None + self, + host_name, + project_name, + project_settings=None, + sync_module=None ): self.host_name = host_name self.project_name = project_name self._project_settings = project_settings - self._sync_module = sync_module # to limit reinit of Modules + self._sync_module = sync_module + # to limit reinit of Modules + self._sync_module_discovered = sync_module is not None self._log = None @property def sync_module(self): - if self._sync_module is None: + if not self._sync_module_discovered: + self._sync_module_discovered = True manager = ModulesManager() - self._sync_module = manager["sync_server"] + self._sync_module = manager.get("sync_server") return self._sync_module @property @@ -151,21 +158,25 @@ class HostDirmap(object): """ project_name = self.project_name + sync_module = self.sync_module mapping = {} - if (not self.sync_module.enabled or - project_name not in self.sync_module.get_enabled_projects()): + if ( + sync_module is None + or not sync_module.enabled + or project_name not in sync_module.get_enabled_projects() + ): return mapping - active_site = self.sync_module.get_local_normalized_site( - self.sync_module.get_active_site(project_name)) - remote_site = self.sync_module.get_local_normalized_site( - self.sync_module.get_remote_site(project_name)) + active_site = sync_module.get_local_normalized_site( + sync_module.get_active_site(project_name)) + remote_site = sync_module.get_local_normalized_site( + sync_module.get_remote_site(project_name)) self.log.debug( "active {} - remote {}".format(active_site, remote_site) ) if active_site == "local" and active_site != remote_site: - sync_settings = self.sync_module.get_sync_project_setting( + sync_settings = sync_module.get_sync_project_setting( project_name, exclude_locals=False, cached=False) @@ -179,7 +190,7 @@ class HostDirmap(object): self.log.debug("remote overrides {}".format(remote_overrides)) current_platform = platform.system().lower() - remote_provider = self.sync_module.get_provider_for_site( + remote_provider = sync_module.get_provider_for_site( project_name, remote_site ) # dirmap has sense only with regular disk provider, in the workfile diff --git a/openpype/hosts/nuke/api/lib.py b/openpype/hosts/nuke/api/lib.py index 9942229155..fcf162c84f 100644 --- a/openpype/hosts/nuke/api/lib.py +++ b/openpype/hosts/nuke/api/lib.py @@ -2955,6 +2955,7 @@ class DirmapCache: """Caching class to get settings and sync_module easily and only once.""" _project_name = None _project_settings = None + _sync_module_discovered = False _sync_module = None _mapping = None @@ -2972,8 +2973,10 @@ class DirmapCache: @classmethod def sync_module(cls): - if cls._sync_module is None: - cls._sync_module = ModulesManager().modules_by_name["sync_server"] + if not cls._sync_module_discovered: + cls._sync_module_discovered = True + cls._sync_module = ModulesManager().modules_by_name.get( + "sync_server") return cls._sync_module @classmethod diff --git a/openpype/modules/sync_server/sync_server_module.py b/openpype/modules/sync_server/sync_server_module.py index 67856f0d8e..8a92697920 100644 --- a/openpype/modules/sync_server/sync_server_module.py +++ b/openpype/modules/sync_server/sync_server_module.py @@ -34,7 +34,12 @@ from openpype.settings.constants import ( from .providers.local_drive import LocalDriveHandler from .providers import lib -from .utils import time_function, SyncStatus, SiteAlreadyPresentError +from .utils import ( + time_function, + SyncStatus, + SiteAlreadyPresentError, + SYNC_SERVER_ROOT, +) log = Logger.get_logger("SyncServer") @@ -138,9 +143,23 @@ class SyncServerModule(OpenPypeModule, ITrayModule, IPluginPaths): def get_plugin_paths(self): """Deadline plugin paths.""" - current_dir = os.path.dirname(os.path.abspath(__file__)) return { - "load": [os.path.join(current_dir, "plugins", "load")] + "load": [os.path.join(SYNC_SERVER_ROOT, "plugins", "load")] + } + + def get_site_icons(self): + """Icons for sites. + + Returns: + dict[str, str]: Path to icon by site. + """ + + resource_path = os.path.join( + SYNC_SERVER_ROOT, "providers", "resources" + ) + return { + provider: "{}/{}.png".format(resource_path, provider) + for provider in ["studio", "local_drive", "gdrive"] } """ Start of Public API """ @@ -904,10 +923,7 @@ class SyncServerModule(OpenPypeModule, ITrayModule, IPluginPaths): (str): full absolut path to directory with hooks for the module """ - return os.path.join( - os.path.dirname(os.path.abspath(__file__)), - "launch_hooks" - ) + return os.path.join(SYNC_SERVER_ROOT, "launch_hooks") # Needs to be refactored after Settings are updated # # Methods for Settings to get appriate values to fill forms diff --git a/openpype/modules/sync_server/utils.py b/openpype/modules/sync_server/utils.py index 4caa01e9d7..b2f855539f 100644 --- a/openpype/modules/sync_server/utils.py +++ b/openpype/modules/sync_server/utils.py @@ -1,9 +1,12 @@ +import os import time from openpype.lib import Logger log = Logger.get_logger("SyncServer") +SYNC_SERVER_ROOT = os.path.dirname(os.path.abspath(__file__)) + class ResumableError(Exception): """Error which could be temporary, skip current loop, try next time""" diff --git a/openpype/plugins/publish/integrate.py b/openpype/plugins/publish/integrate.py index ffb9acf4a7..be07cffe72 100644 --- a/openpype/plugins/publish/integrate.py +++ b/openpype/plugins/publish/integrate.py @@ -2,9 +2,10 @@ import os import logging import sys import copy +import datetime + import clique import six - from bson.objectid import ObjectId import pyblish.api @@ -320,10 +321,16 @@ class IntegrateAsset(pyblish.api.InstancePlugin): # Get the accessible sites for Site Sync modules_by_name = instance.context.data["openPypeModules"] - sync_server_module = modules_by_name["sync_server"] - sites = sync_server_module.compute_resource_sync_sites( - project_name=instance.data["projectEntity"]["name"] - ) + sync_server_module = modules_by_name.get("sync_server") + if sync_server_module is None: + sites = [{ + "name": "studio", + "created_dt": datetime.datetime.now() + }] + else: + sites = sync_server_module.compute_resource_sync_sites( + project_name=instance.data["projectEntity"]["name"] + ) self.log.debug("Sync Server Sites: {}".format(sites)) # Compute the resource file infos once (files belonging to the diff --git a/openpype/pype_commands.py b/openpype/pype_commands.py index 4cb4b97707..57bbb0bbe3 100644 --- a/openpype/pype_commands.py +++ b/openpype/pype_commands.py @@ -336,34 +336,6 @@ class PypeCommands: import pytest pytest.main(args) - def syncserver(self, active_site): - """Start running sync_server in background. - - This functionality is available in directly in module cli commands. - `~/openpype_console module sync_server syncservice` - """ - - os.environ["OPENPYPE_LOCAL_ID"] = active_site - - def signal_handler(sig, frame): - print("You pressed Ctrl+C. Process ended.") - sync_server_module.server_exit() - sys.exit(0) - - signal.signal(signal.SIGINT, signal_handler) - signal.signal(signal.SIGTERM, signal_handler) - - from openpype.modules import ModulesManager - - manager = ModulesManager() - sync_server_module = manager.modules_by_name["sync_server"] - - sync_server_module.server_init() - sync_server_module.server_start() - - while True: - time.sleep(1.0) - def repack_version(self, directory): """Repacking OpenPype version.""" from openpype.tools.repack_version import VersionRepacker diff --git a/openpype/tools/libraryloader/app.py b/openpype/tools/libraryloader/app.py index bd10595333..e68e9a5931 100644 --- a/openpype/tools/libraryloader/app.py +++ b/openpype/tools/libraryloader/app.py @@ -114,9 +114,10 @@ class LibraryLoaderWindow(QtWidgets.QDialog): manager = ModulesManager() sync_server = manager.modules_by_name.get("sync_server") - sync_server_enabled = False - if sync_server is not None: - sync_server_enabled = sync_server.enabled + sync_server_enabled = ( + sync_server is not None + and sync_server.enabled + ) repres_widget = None if sync_server_enabled: diff --git a/openpype/tools/loader/model.py b/openpype/tools/loader/model.py index 5115f39a69..69b7e593b1 100644 --- a/openpype/tools/loader/model.py +++ b/openpype/tools/loader/model.py @@ -64,6 +64,7 @@ class BaseRepresentationModel(object): """Sets/Resets sync server vars after every change (refresh.)""" repre_icons = {} sync_server = None + sync_server_enabled = False active_site = active_provider = None remote_site = remote_provider = None @@ -75,6 +76,7 @@ class BaseRepresentationModel(object): if not project_name: self.repre_icons = repre_icons self.sync_server = sync_server + self.sync_server_enabled = sync_server_enabled self.active_site = active_site self.active_provider = active_provider self.remote_site = remote_site @@ -100,8 +102,13 @@ class BaseRepresentationModel(object): self._modules_manager = ModulesManager() self._last_manager_cache = now_time - sync_server = self._modules_manager.modules_by_name["sync_server"] - if sync_server.is_project_enabled(project_name, single=True): + sync_server = self._modules_manager.modules_by_name.get("sync_server") + if ( + sync_server is not None + and sync_server.enabled + and sync_server.is_project_enabled(project_name, single=True) + ): + sync_server_enabled = True active_site = sync_server.get_active_site(project_name) active_provider = sync_server.get_provider_for_site( project_name, active_site) @@ -118,6 +125,7 @@ class BaseRepresentationModel(object): self.repre_icons = repre_icons self.sync_server = sync_server + self.sync_server_enabled = sync_server_enabled self.active_site = active_site self.active_provider = active_provider self.remote_site = remote_site @@ -213,6 +221,7 @@ class SubsetsModel(BaseRepresentationModel, TreeModel): self.repre_icons = {} self.sync_server = None + self.sync_server_enabled = False self.active_site = self.active_provider = None self.columns_index = dict( @@ -282,7 +291,7 @@ class SubsetsModel(BaseRepresentationModel, TreeModel): ) # update availability on active site when version changes - if self.sync_server.enabled and version_doc: + if self.sync_server_enabled and version_doc: repres_info = list( self.sync_server.get_repre_info_for_versions( project_name, @@ -507,7 +516,7 @@ class SubsetsModel(BaseRepresentationModel, TreeModel): return repre_info_by_version_id = {} - if self.sync_server.enabled: + if self.sync_server_enabled: versions_by_id = {} for _subset_id, doc in last_versions_by_subset_id.items(): versions_by_id[doc["_id"]] = doc @@ -1033,12 +1042,16 @@ class RepresentationModel(TreeModel, BaseRepresentationModel): self._version_ids = [] manager = ModulesManager() - sync_server = active_site = remote_site = None + active_site = remote_site = None active_provider = remote_provider = None + sync_server = manager.modules_by_name.get("sync_server") + sync_server_enabled = ( + sync_server is not None + and sync_server.enabled + ) project_name = dbcon.current_project() - if project_name: - sync_server = manager.modules_by_name["sync_server"] + if sync_server_enabled and project_name: active_site = sync_server.get_active_site(project_name) remote_site = sync_server.get_remote_site(project_name) @@ -1057,6 +1070,7 @@ class RepresentationModel(TreeModel, BaseRepresentationModel): remote_provider = 'studio' self.sync_server = sync_server + self.sync_server_enabled = sync_server_enabled self.active_site = active_site self.active_provider = active_provider self.remote_site = remote_site @@ -1174,9 +1188,15 @@ class RepresentationModel(TreeModel, BaseRepresentationModel): repre_groups_items[doc["name"]] = 0 group = group_item - progress = self.sync_server.get_progress_for_repre( - doc, - self.active_site, self.remote_site) + progress = { + self.active_site: 0, + self.remote_site: 0, + } + if self.sync_server_enabled: + progress = self.sync_server.get_progress_for_repre( + doc, + self.active_site, + self.remote_site) active_site_icon = self._icons.get(self.active_provider) remote_site_icon = self._icons.get(self.remote_provider) diff --git a/openpype/tools/sceneinventory/lib.py b/openpype/tools/sceneinventory/lib.py index 4b1860342a..0ac7622d65 100644 --- a/openpype/tools/sceneinventory/lib.py +++ b/openpype/tools/sceneinventory/lib.py @@ -1,9 +1,3 @@ -import os -from openpype_modules import sync_server - -from qtpy import QtGui - - def walk_hierarchy(node): """Recursively yield group node.""" for child in node.children(): @@ -12,19 +6,3 @@ def walk_hierarchy(node): for _child in walk_hierarchy(child): yield _child - - -def get_site_icons(): - resource_path = os.path.join( - os.path.dirname(sync_server.sync_server_module.__file__), - "providers", - "resources" - ) - icons = {} - # TODO get from sync module - for provider in ["studio", "local_drive", "gdrive"]: - pix_url = "{}/{}.png".format(resource_path, provider) - icons[provider] = QtGui.QIcon(pix_url) - - return icons - diff --git a/openpype/tools/sceneinventory/model.py b/openpype/tools/sceneinventory/model.py index 1cfcd0d8c0..64c439712c 100644 --- a/openpype/tools/sceneinventory/model.py +++ b/openpype/tools/sceneinventory/model.py @@ -24,10 +24,7 @@ from openpype.style import get_default_entity_icon_color from openpype.tools.utils.models import TreeModel, Item from openpype.modules import ModulesManager -from .lib import ( - get_site_icons, - walk_hierarchy, -) +from .lib import walk_hierarchy class InventoryModel(TreeModel): @@ -53,8 +50,10 @@ class InventoryModel(TreeModel): self._default_icon_color = get_default_entity_icon_color() manager = ModulesManager() - sync_server = manager.modules_by_name["sync_server"] - self.sync_enabled = sync_server.enabled + sync_server = manager.modules_by_name.get("sync_server") + self.sync_enabled = ( + sync_server is not None and sync_server.enabled + ) self._site_icons = {} self.active_site = self.remote_site = None self.active_provider = self.remote_provider = None @@ -84,7 +83,10 @@ class InventoryModel(TreeModel): self.active_provider = active_provider self.remote_site = remote_site self.remote_provider = remote_provider - self._site_icons = get_site_icons() + self._site_icons = { + provider: QtGui.QIcon(icon_path) + for provider, icon_path in self.get_site_icons().items() + } if "active_site" not in self.Columns: self.Columns.append("active_site") if "remote_site" not in self.Columns: diff --git a/openpype/tools/sceneinventory/view.py b/openpype/tools/sceneinventory/view.py index d22b2bdd0f..816e396c08 100644 --- a/openpype/tools/sceneinventory/view.py +++ b/openpype/tools/sceneinventory/view.py @@ -54,8 +54,11 @@ class SceneInventoryView(QtWidgets.QTreeView): self._selected = None manager = ModulesManager() - self.sync_server = manager.modules_by_name["sync_server"] - self.sync_enabled = self.sync_server.enabled + sync_server = manager.modules_by_name.get("sync_server") + sync_enabled = sync_server is not None and self.sync_server.enabled + + self.sync_server = sync_server + self.sync_enabled = sync_enabled def _set_hierarchy_view(self, enabled): if enabled == self._hierarchy_view: diff --git a/openpype/tools/settings/local_settings/projects_widget.py b/openpype/tools/settings/local_settings/projects_widget.py index 4a4148d7cd..68e144f87b 100644 --- a/openpype/tools/settings/local_settings/projects_widget.py +++ b/openpype/tools/settings/local_settings/projects_widget.py @@ -267,19 +267,20 @@ class SitesWidget(QtWidgets.QWidget): self.input_objects = {} def _get_sites_inputs(self): - sync_server_module = ( - self.modules_manager.modules_by_name["sync_server"] - ) + output = [] + if self._project_name is None: + return output + + sync_server_module = self.modules_manager.modules_by_name.get( + "sync_server") + if sync_server_module is None or not sync_server_module.enabled: + return output site_configs = sync_server_module.get_all_site_configs( self._project_name, local_editable_only=True) - roots_entity = ( - self.project_settings[PROJECT_ANATOMY_KEY][LOCAL_ROOTS_KEY] - ) site_names = [self.active_site_widget.current_text(), self.remote_site_widget.current_text()] - output = [] for site_name in site_names: if not site_name: continue @@ -350,9 +351,6 @@ class SitesWidget(QtWidgets.QWidget): def refresh(self): self._clear_widgets() - if self._project_name is None: - return - # Site label for site_name, site_inputs in self._get_sites_inputs(): site_widget = QtWidgets.QWidget(self.content_widget) diff --git a/openpype/tools/utils/lib.py b/openpype/tools/utils/lib.py index 82ca23c848..2df46c1eae 100644 --- a/openpype/tools/utils/lib.py +++ b/openpype/tools/utils/lib.py @@ -760,20 +760,23 @@ def create_qthread(func, *args, **kwargs): def get_repre_icons(): """Returns a dict {'provider_name': QIcon}""" + icons = {} try: from openpype_modules import sync_server except Exception: # Backwards compatibility - from openpype.modules import sync_server + try: + from openpype.modules import sync_server + except Exception: + return icons resource_path = os.path.join( os.path.dirname(sync_server.sync_server_module.__file__), "providers", "resources" ) - icons = {} if not os.path.exists(resource_path): print("No icons for Site Sync found") - return {} + return icons for file_name in os.listdir(resource_path): if file_name and not file_name.endswith("png"): diff --git a/server_addon/create_ayon_addons.py b/server_addon/create_ayon_addons.py index 8be9baa983..61dbd5c8d9 100644 --- a/server_addon/create_ayon_addons.py +++ b/server_addon/create_ayon_addons.py @@ -203,8 +203,7 @@ def create_openpype_package( ignored_modules = [ "ftrack", "shotgrid", - # Sync server is still expected at multiple places - # "sync_server", + "sync_server", "example_addons", "slack" ]