From aefe8f3b7296d549e306e548d62886c4634139d3 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 10 Dec 2020 09:55:45 +0100 Subject: [PATCH 01/78] PypeModule is python 2 compatible --- pype/modules/base.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/pype/modules/base.py b/pype/modules/base.py index ee90aa4cbb..a37f08869d 100644 --- a/pype/modules/base.py +++ b/pype/modules/base.py @@ -1,11 +1,14 @@ # -*- coding: utf-8 -*- """Base class for Pype Modules.""" from uuid import uuid4 -from abc import ABC, abstractmethod from pype.api import Logger +from abc import ABCMeta, abstractmethod +import six -class PypeModule(ABC): + +@six.add_metaclass(ABCMeta) +class PypeModule: """Base class of pype module. Attributes: From c0feb0841143150271d4a118108e24d3db8cc1d7 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 10 Dec 2020 09:56:47 +0100 Subject: [PATCH 02/78] module's name attribute is abstract --- pype/modules/base.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/pype/modules/base.py b/pype/modules/base.py index a37f08869d..31e75dc92c 100644 --- a/pype/modules/base.py +++ b/pype/modules/base.py @@ -18,8 +18,12 @@ class PypeModule: """ enabled = False - name = None _id = None + @property + @abstractmethod + def name(self): + """Module's name.""" + pass def __init__(self, settings): if self.name is None: From 9e0421238073d744000be1b7374bbab77cc81c45 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 10 Dec 2020 09:59:31 +0100 Subject: [PATCH 03/78] PypeModule also expect managers object --- pype/modules/base.py | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/pype/modules/base.py b/pype/modules/base.py index 31e75dc92c..90a9e12fb5 100644 --- a/pype/modules/base.py +++ b/pype/modules/base.py @@ -1,10 +1,10 @@ # -*- coding: utf-8 -*- """Base class for Pype Modules.""" from uuid import uuid4 -from pype.api import Logger from abc import ABCMeta, abstractmethod import six +from pype.lib import PypeLogger @six.add_metaclass(ABCMeta) @@ -25,15 +25,12 @@ class PypeModule: """Module's name.""" pass - def __init__(self, settings): - if self.name is None: - self.name = self.__class__.__name__ + def __init__(self, manager, settings): + self.manager = manager - self.log = Logger().get_logger(self.name) + self.log = PypeLogger().get_logger(self.name) - self.settings = settings.get(self.name) - self.enabled = settings.get("enabled", False) - self._id = uuid4() + self.initialize(settings) @property def id(self): From c33c8bbc3637e7f88b2007ba7fe966f5a08e480c Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 10 Dec 2020 10:00:08 +0100 Subject: [PATCH 04/78] _id is filled with uuid4 if not set yet --- pype/modules/base.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pype/modules/base.py b/pype/modules/base.py index 90a9e12fb5..18e1dacbbe 100644 --- a/pype/modules/base.py +++ b/pype/modules/base.py @@ -34,6 +34,8 @@ class PypeModule: @property def id(self): + if self._id is None: + self._id = uuid4() return self._id @abstractmethod From df7d45b8d84fb6c9796fd442aca11fa227a05fc5 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 10 Dec 2020 10:00:37 +0100 Subject: [PATCH 05/78] renamed startup_environments with get_global_environments --- pype/modules/base.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/pype/modules/base.py b/pype/modules/base.py index 18e1dacbbe..a6ae89184f 100644 --- a/pype/modules/base.py +++ b/pype/modules/base.py @@ -39,6 +39,9 @@ class PypeModule: return self._id @abstractmethod - def startup_environments(self): - """Get startup environments for module.""" + def get_global_environments(self): + """Get global environments values of module. + + Environment variables that can be get only from system settings. + """ return {} From 66aa85e9215e409b99d2543cedd08cc44c7a9e0f Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 10 Dec 2020 10:01:18 +0100 Subject: [PATCH 06/78] get_global_environments is not abstract and adde initialize and connect_with_modules abstract methods --- pype/modules/base.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/pype/modules/base.py b/pype/modules/base.py index a6ae89184f..c6521cd199 100644 --- a/pype/modules/base.py +++ b/pype/modules/base.py @@ -39,6 +39,19 @@ class PypeModule: return self._id @abstractmethod + def initialize(self, module_settings): + """Initialization of module attributes. + + It is not recommended to override __init__ that's why specific method + was implemented. + """ + pass + + @abstractmethod + def connect_with_modules(self, enabled_modules): + """Connect with other enabled modules.""" + pass + def get_global_environments(self): """Get global environments values of module. From c1a7e36e217ed18520ca978d634a96ecac0ce70a Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 10 Dec 2020 10:03:15 +0100 Subject: [PATCH 07/78] implemented TrayModule interface ITrayModule --- pype/modules/base.py | 50 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/pype/modules/base.py b/pype/modules/base.py index c6521cd199..1a3f1e5818 100644 --- a/pype/modules/base.py +++ b/pype/modules/base.py @@ -58,3 +58,53 @@ class PypeModule: Environment variables that can be get only from system settings. """ return {} + + +@six.add_metaclass(ABCMeta) +class ITrayModule: + """Module has special procedures when used in Pype Tray. + + IMPORTANT: + The module still must be usable if is not used in tray even if + would do nothing. + """ + tray_initialized = False + + def do_tray_init(self, *args, **kwargs): + """Method called by Tray manager. + + Point is to set `tray_initialized` to True after process. + + TODO try to handle this with decorator on `tray_init`. + """ + self.tray_init(*args, **kwargs) + self.tray_initialized = True + + @abstractmethod + def tray_init(self, tray_widget, main_window): + """Initialization part of tray implementation. + + Triggered between `initialization` and `connect_with_modules`. + + This is where GUIs should be loaded or tray specific parts should be + prepared. + """ + pass + + @abstractmethod + def tray_menu(self, tray_menu): + """Add module's action to tray menu.""" + pass + + @abstractmethod + def tray_start(self): + """Start procedure in Pype tray.""" + pass + + @abstractmethod + def tray_exit(self): + """Cleanup method which is executed on tray shutdown. + + This is place where all threads should be shut. + """ + pass From 78e1246e8b3e6bab6cfcf35ec4fac0cda1bdd7ea Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 10 Dec 2020 10:04:36 +0100 Subject: [PATCH 08/78] implemented Tray service interface ITrayService --- pype/modules/base.py | 82 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) diff --git a/pype/modules/base.py b/pype/modules/base.py index 1a3f1e5818..dd7ef2f95c 100644 --- a/pype/modules/base.py +++ b/pype/modules/base.py @@ -5,6 +5,7 @@ from abc import ABCMeta, abstractmethod import six from pype.lib import PypeLogger +from pype import resources @six.add_metaclass(ABCMeta) @@ -108,3 +109,84 @@ class ITrayModule: This is place where all threads should be shut. """ pass + + +class ITrayService(ITrayModule): + menu_action = None + # Class properties + _services_submenu = None + _icon_failed = None + _icon_running = None + _icon_idle = None + + @property + @abstractmethod + def label(self): + """Service label.""" + pass + + # TODO be able to get any sort of information to show/print + # @abstractmethod + # def get_service_info(self): + # pass + + @classmethod + def services_submenu(cls): + return cls._services_submenu + + @classmethod + def _load_service_icons(cls): + from Qt import QtGui + cls._failed_icon = QtGui.QIcon( + resources.get_resource("icons", "circle_red.png") + ) + cls._icon_running = QtGui.QIcon( + resources.get_resource("icons", "circle_green.png") + ) + cls._icon_idle = QtGui.QIcon( + resources.get_resource("icons", "circle_orange.png") + ) + + @classmethod + def get_icon_running(cls): + if cls._icon_running is None: + cls._load_service_icons() + return cls._icon_running + + @classmethod + def get_icon_idle(cls): + if cls._icon_idle is None: + cls._load_service_icons() + return cls._icon_idle + + @classmethod + def get_icon_failed(cls): + if cls._failed_icon is None: + cls._load_service_icons() + return cls._failed_icon + + def tray_menu(self, tray_menu): + from Qt import QtWidgets + services_submenu = self.services_submenu() + if services_submenu is None: + services_submenu = QtWidgets.QMenu("Services", tray_menu) + self.__class__._services_submenu = services_submenu + + action = QtWidgets.QAction(self.label, services_submenu) + services_submenu.addAction(action) + + self.menu_action = action + + self.set_service_running() + + def set_service_running(self): + if self.menu_action: + self.menu_action.setIcon(self.get_icon_running()) + + def set_service_failed(self): + if self.menu_action: + self.menu_action.setIcon(self.get_icon_failed()) + + def set_service_idle(self): + if self.menu_action: + self.menu_action.setIcon(self.get_icon_idle()) From b7620de341c0b2ed6d45bb8c24cf21fc39ac9b74 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 10 Dec 2020 10:05:19 +0100 Subject: [PATCH 09/78] implemented interface IPluginPaths to return plugin paths byt keys "publish", "create", "load" or "actions" --- pype/modules/base.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/pype/modules/base.py b/pype/modules/base.py index dd7ef2f95c..78b0ed1194 100644 --- a/pype/modules/base.py +++ b/pype/modules/base.py @@ -111,6 +111,22 @@ class ITrayModule: pass +@six.add_metaclass(ABCMeta) +class IPluginPaths: + """Module has plugin paths to return. + + Expected result is dictionary with keys "publish", "create", "load" or + "actions" and values as list or string. + { + "publish": ["path/to/publish_plugins"] + } + """ + # TODO validation of an output + @abstractmethod + def get_plugin_paths(self): + pass + + class ITrayService(ITrayModule): menu_action = None # Class properties From 6354fb4d451a5a39c2994ea2ac42f17badebf4da Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 10 Dec 2020 10:06:23 +0100 Subject: [PATCH 10/78] implemented ModulesManager --- pype/modules/base.py | 65 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/pype/modules/base.py b/pype/modules/base.py index 78b0ed1194..4d737e930c 100644 --- a/pype/modules/base.py +++ b/pype/modules/base.py @@ -1,9 +1,13 @@ # -*- coding: utf-8 -*- """Base class for Pype Modules.""" +import inspect +import logging from uuid import uuid4 from abc import ABCMeta, abstractmethod import six +import pype +from pype.settings import get_system_settings from pype.lib import PypeLogger from pype import resources @@ -206,3 +210,64 @@ class ITrayService(ITrayModule): def set_service_idle(self): if self.menu_action: self.menu_action.setIcon(self.get_icon_idle()) + + +class ModulesManager: + def __init__(self): + self.log = logging.getLogger(self.__class__.__name__) + + self.modules = {} + + self.initialize_modules() + self.connect_modules() + + def initialize_modules(self): + self.log.debug("*** Pype modules initialization.") + modules_settings = get_system_settings()["modules"] + for name in dir(pype.modules): + modules_item = getattr(pype.modules, name, None) + if ( + not inspect.isclass(modules_item) + or modules_item is pype.modules.PypeModule + or not issubclass(modules_item, pype.modules.PypeModule) + ): + continue + + if inspect.isabstract(modules_item): + not_implemented = [] + for attr_name in dir(modules_item): + attr = getattr(modules_item, attr_name, None) + if attr and getattr(attr, "__isabstractmethod__", None): + not_implemented.append(attr_name) + + self.log.warning(( + "Skipping abstract Class: {}. Missing implementations: {}" + ).format(name, ", ".join(not_implemented))) + continue + + try: + module = modules_item(self, modules_settings) + self.modules[module.id] = module + enabled_str = " ENABLED" + if not module.enabled: + enabled_str = "DISABLED" + self.log.debug("[{}] {}".format(enabled_str, name)) + + except Exception: + self.log.warning( + "Initialization of module {} failed.".format(name), + exc_info=True + ) + + def connect_modules(self): + enabled_modules = self.get_enabled_modules() + self.log.debug("Has {} enabled modules.".format(len(enabled_modules))) + for module in enabled_modules: + module.connect_with_modules(enabled_modules) + + def get_enabled_modules(self): + return [ + module + for module in self.modules.values() + if module.enabled + ] From 971af9914680bdeae68126b644fab2de56a931d7 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 10 Dec 2020 10:06:44 +0100 Subject: [PATCH 11/78] implemented TrayModulesManager for tray purposes --- pype/modules/base.py | 43 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/pype/modules/base.py b/pype/modules/base.py index 4d737e930c..d4b1508699 100644 --- a/pype/modules/base.py +++ b/pype/modules/base.py @@ -17,13 +17,16 @@ class PypeModule: """Base class of pype module. Attributes: - id (UUID): Module id. + id (UUID): Module's id. enabled (bool): Is module enabled. name (str): Module name. + manager (ModulesManager): Manager that created the module. """ + # Disable by default enabled = False _id = None + @property @abstractmethod def name(self): @@ -271,3 +274,41 @@ class ModulesManager: for module in self.modules.values() if module.enabled ] + + +class TrayModulesManager(ModulesManager): + def __init__(self): + self.log = PypeLogger().get_logger(self.__class__.__name__) + + self.modules = {} + + def initialize(self, tray_widget, main_window): + self.tray_widget = tray_widget + self.main_window = main_window + + self.initialize_modules() + self.tray_init(tray_widget, main_window) + self.connect_modules() + + def get_enabled_tray_modules(self): + output = [] + for module in self.modules.values(): + if module.enabled and isinstance(module, ITrayModule): + output.append(module) + return output + + def tray_init(self, *args, **kwargs): + for module in self.get_enabled_tray_modules(): + module.do_tray_init(*args, **kwargs) + + def tray_menu(self, tray_menu): + for module in self.get_enabled_tray_modules(): + module.tray_menu(tray_menu) + + def start_modules(self): + for module in self.get_enabled_tray_modules(): + module.tray_start() + + def on_exit(self): + for module in self.get_enabled_tray_modules(): + module.tray_exit() From e28c7c93f837ef754db7f07cb8275cb26d367bc2 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 10 Dec 2020 10:17:11 +0100 Subject: [PATCH 12/78] simplified clockify widgets --- pype/modules/clockify/clockify.py | 18 +++++++++--------- pype/modules/clockify/widgets.py | 29 +++++++---------------------- 2 files changed, 16 insertions(+), 31 deletions(-) diff --git a/pype/modules/clockify/clockify.py b/pype/modules/clockify/clockify.py index 4309bff9f2..9658e178e4 100644 --- a/pype/modules/clockify/clockify.py +++ b/pype/modules/clockify/clockify.py @@ -32,7 +32,7 @@ class ClockifyModule: self.main_parent = main_parent self.parent = parent self.message_widget = None - self.widget_settings = ClockifySettings(main_parent, self) + self.widget_settings = ClockifySettings(self.clockapi) self.widget_settings_required = None self.thread_timer_check = None @@ -198,20 +198,20 @@ class ClockifyModule: "Project \"{}\" was not found in Clockify. Timer won't start." ).format(project_name)) + if not self.MessageWidgetClass: + return + msg = ( "Project \"{}\" is not" " in Clockify Workspace \"{}\"." "

Please inform your Project Manager." ).format(project_name, str(self.clockapi.workspace_name)) - if self.MessageWidgetClass: - self.message_widget = self.MessageWidgetClass( - self.main_parent, msg, "Clockify - Info Message" - ) - self.message_widget.closed.connect( - self.on_message_widget_close - ) - self.message_widget.show() + self.message_widget = self.MessageWidgetClass( + msg, "Clockify - Info Message" + ) + self.message_widget.closed.connect(self.on_message_widget_close) + self.message_widget.show() return diff --git a/pype/modules/clockify/widgets.py b/pype/modules/clockify/widgets.py index dc57a48ecb..74b2dede66 100644 --- a/pype/modules/clockify/widgets.py +++ b/pype/modules/clockify/widgets.py @@ -10,18 +10,12 @@ class MessageWidget(QtWidgets.QWidget): closed = QtCore.Signal() - def __init__(self, parent=None, messages=[], title="Message"): - + def __init__(self, messages=[], title="Message"): super(MessageWidget, self).__init__() - self._parent = parent - # Icon - if parent and hasattr(parent, 'icon'): - self.setWindowIcon(parent.icon) - else: - icon = QtGui.QIcon(resources.pype_icon_filepath()) - self.setWindowIcon(icon) + icon = QtGui.QIcon(resources.pype_icon_filepath()) + self.setWindowIcon(icon) self.setWindowFlags( QtCore.Qt.WindowCloseButtonHint | @@ -93,30 +87,21 @@ class MessageWidget(QtWidgets.QWidget): class ClockifySettings(QtWidgets.QWidget): - SIZE_W = 300 SIZE_H = 130 loginSignal = QtCore.Signal(object, object, object) - def __init__(self, main_parent=None, parent=None, optional=True): - + def __init__(self, clockapi, optional=True): super(ClockifySettings, self).__init__() - self.parent = parent - self.main_parent = main_parent - self.clockapi = parent.clockapi + self.clockapi = clockapi self.optional = optional self.validated = False # Icon - if hasattr(parent, 'icon'): - self.setWindowIcon(self.parent.icon) - elif hasattr(parent, 'parent') and hasattr(parent.parent, 'icon'): - self.setWindowIcon(self.parent.parent.icon) - else: - icon = QtGui.QIcon(resources.pype_icon_filepath()) - self.setWindowIcon(icon) + icon = QtGui.QIcon(resources.pype_icon_filepath()) + self.setWindowIcon(icon) self.setWindowFlags( QtCore.Qt.WindowCloseButtonHint | From 8f198ddb55ec724f4f120721b9b6d36e3150c505 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 10 Dec 2020 10:20:52 +0100 Subject: [PATCH 13/78] Clockify module converted to new module --- pype/modules/__init__.py | 2 + pype/modules/clockify/__init__.py | 8 ++- pype/modules/clockify/clockify.py | 89 ++++++++++++++----------------- 3 files changed, 45 insertions(+), 54 deletions(-) diff --git a/pype/modules/__init__.py b/pype/modules/__init__.py index aacd541e18..4f6b5f44c8 100644 --- a/pype/modules/__init__.py +++ b/pype/modules/__init__.py @@ -1,6 +1,8 @@ # -*- coding: utf-8 -*- from .base import PypeModule +from .clockify import ClockifyModule __all__ = ( "PypeModule", + "ClockifyModule", ) diff --git a/pype/modules/clockify/__init__.py b/pype/modules/clockify/__init__.py index 8e11d2f5f4..e4ca2611c4 100644 --- a/pype/modules/clockify/__init__.py +++ b/pype/modules/clockify/__init__.py @@ -1,7 +1,5 @@ from .clockify import ClockifyModule -CLASS_DEFINIION = ClockifyModule - - -def tray_init(tray_widget, main_widget): - return ClockifyModule(main_widget, tray_widget) +__all__ = ( + "ClockifyModule", +) diff --git a/pype/modules/clockify/clockify.py b/pype/modules/clockify/clockify.py index 9658e178e4..03f357b793 100644 --- a/pype/modules/clockify/clockify.py +++ b/pype/modules/clockify/clockify.py @@ -2,35 +2,47 @@ import os import threading import time -from pype.api import Logger from .clockify_api import ClockifyAPI -from .constants import CLOCKIFY_FTRACK_USER_PATH +from .constants import ( + CLOCKIFY_FTRACK_USER_PATH, + CLOCKIFY_FTRACK_SERVER_PATH +) +from pype.modules import ( + PypeModule, + ITrayModule, +) -class ClockifyModule: - workspace_name = None +class ClockifyModule( + PypeModule, + ITrayModule, +): + name = "Clockify" - def __init__(self, main_parent=None, parent=None): - if not self.workspace_name: - raise Exception("Clockify Workspace is not set in config.") + def initialize(self, modules_settings): + clockify_settings = modules_settings[self.name] + self.enabled = clockify_settings["enabled"] + self.workspace_name = clockify_settings["workspace_name"] - os.environ["CLOCKIFY_WORKSPACE"] = self.workspace_name + if self.enabled and not self.workspace_name: + raise Exception("Clockify Workspace is not set in settings.") self.timer_manager = None self.MessageWidgetClass = None + self.message_widget = None self.clockapi = ClockifyAPI(master_parent=self) - self.log = Logger().get_logger(self.__class__.__name__, "PypeTray") - self.tray_init(main_parent, parent) + def get_global_environments(self): + return { + "CLOCKIFY_WORKSPACE": self.workspace_name + } - def tray_init(self, main_parent, parent): + def tray_init(self, tray_widget, main_window): from .widgets import ClockifySettings, MessageWidget self.MessageWidgetClass = MessageWidget - self.main_parent = main_parent - self.parent = parent self.message_widget = None self.widget_settings = ClockifySettings(self.clockapi) self.widget_settings_required = None @@ -56,43 +68,25 @@ class ClockifyModule: self.set_menu_visibility() - def process_modules(self, modules): - if 'FtrackModule' in modules: - current = os.environ.get('FTRACK_ACTIONS_PATH', '') - if current: - current += os.pathsep - os.environ['FTRACK_ACTIONS_PATH'] = ( - current + CLOCKIFY_FTRACK_USER_PATH - ) + def tray_exit(self, *_a, **_kw): + return - if 'AvalonApps' in modules: - actions_path = os.path.join( - os.path.dirname(__file__), - 'launcher_actions' - ) - current = os.environ.get('AVALON_ACTIONS', '') - if current: - current += os.pathsep - os.environ['AVALON_ACTIONS'] = current + actions_path + def get_plugin_paths(self): + """Implementaton of IPluginPaths to get plugin paths.""" + actions_path = os.path.join( + os.path.dirname(os.path.abspath(__file__)), + "launcher_actions" + ) + return { + "actions": [actions_path] + } - if 'TimersManager' in modules: - self.timer_manager = modules['TimersManager'] - self.timer_manager.add_module(self) - def start_timer_manager(self, data): - self.start_timer(data) + def connect_with_modules(self, *_a, **_kw): + return - def stop_timer_manager(self): - self.stop_timer() - - def timer_started(self, data): - if self.timer_manager: - self.timer_manager.start_timers(data) - - def timer_stopped(self): + def clockify_timer_stopped(self): self.bool_timer_run = False - if self.timer_manager: - self.timer_manager.stop_timers() def start_timer_check(self): self.bool_thread_check_running = True @@ -110,7 +104,6 @@ class ClockifyModule: self.thread_timer_check = None def check_running(self): - while self.bool_thread_check_running is True: bool_timer_run = False if self.clockapi.get_in_progress() is not None: @@ -118,7 +111,7 @@ class ClockifyModule: if self.bool_timer_run != bool_timer_run: if self.bool_timer_run is True: - self.timer_stopped() + self.clockify_timer_stopped() elif self.bool_timer_run is False: actual_timer = self.clockapi.get_in_progress() if not actual_timer: @@ -160,8 +153,6 @@ class ClockifyModule: def stop_timer(self): self.clockapi.finish_time_entry() - if self.bool_timer_run: - self.timer_stopped() def signed_in(self): if not self.timer_manager: From 0e12ba88e6e52e2f955180541a2d96d44e4ce0f7 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 10 Dec 2020 10:23:17 +0100 Subject: [PATCH 14/78] converted AvalonModule --- pype/modules/__init__.py | 2 + pype/modules/avalon_apps/__init__.py | 7 +- pype/modules/avalon_apps/avalon_app.py | 169 ++++++++++++++++++++----- 3 files changed, 140 insertions(+), 38 deletions(-) diff --git a/pype/modules/__init__.py b/pype/modules/__init__.py index 4f6b5f44c8..daa5a185d1 100644 --- a/pype/modules/__init__.py +++ b/pype/modules/__init__.py @@ -1,8 +1,10 @@ # -*- coding: utf-8 -*- from .base import PypeModule +from .avalon_apps import AvalonModule from .clockify import ClockifyModule __all__ = ( "PypeModule", + "AvalonModule", "ClockifyModule", ) diff --git a/pype/modules/avalon_apps/__init__.py b/pype/modules/avalon_apps/__init__.py index 845f94a330..baa21cc803 100644 --- a/pype/modules/avalon_apps/__init__.py +++ b/pype/modules/avalon_apps/__init__.py @@ -1,5 +1,6 @@ -from .avalon_app import AvalonApps +from .avalon_app import AvalonModule -def tray_init(tray_widget, main_widget): - return AvalonApps(main_widget, tray_widget) +__all__ = ( + "AvalonModule", +) diff --git a/pype/modules/avalon_apps/avalon_app.py b/pype/modules/avalon_apps/avalon_app.py index de10268304..48e347399d 100644 --- a/pype/modules/avalon_apps/avalon_app.py +++ b/pype/modules/avalon_apps/avalon_app.py @@ -1,61 +1,160 @@ -from pype.api import Logger +import os +import pype +from .. import ( + PypeModule, + ITrayModule, + IPluginPaths, +) -class AvalonApps: - def __init__(self, main_parent=None, parent=None): - self.log = Logger().get_logger(__name__) +class AvalonModule(PypeModule, ITrayModule, IRestApi): + name = "Avalon" - self.tray_init(main_parent, parent) + def initialize(self, modules_settings): + # This module is always enabled + self.enabled = True - def tray_init(self, main_parent, parent): - from avalon.tools.libraryloader import app - from avalon import style - from pype.tools.launcher import LauncherWindow, actions + avalon_settings = modules_settings[self.name] - self.parent = parent - self.main_parent = main_parent + # Check if environment is already set + avalon_mongo_url = os.environ.get("AVALON_MONGO") + if not avalon_mongo_url: + avalon_mongo_url = avalon_settings["AVALON_MONGO"] + # Use pype mongo if Avalon's mongo not defined + if not avalon_mongo_url: + avalon_mongo_url = os.environ["PYPE_MONGO"] - self.app_launcher = LauncherWindow() - self.libraryloader = app.Window( - icon=self.parent.icon, - show_projects=True, - show_libraries=True + thumbnail_root = os.environ.get("AVALON_THUMBNAIL_ROOT") + if not thumbnail_root: + thumbnail_root = avalon_settings["AVALON_THUMBNAIL_ROOT"] + + self.thumbnail_root = thumbnail_root + self.avalon_mongo_url = avalon_mongo_url + self.avalon_settings = avalon_settings + self.schema_path = os.path.join( + os.path.dirname(pype.PACKAGE_DIR), + "schema" ) - self.libraryloader.setStyleSheet(style.load_stylesheet()) - # actions.register_default_actions() - actions.register_config_actions() - actions.register_environment_actions() + # Tray attributes + self.app_launcher = None + self.libraryloader = None + self.rest_api_obj = None def process_modules(self, modules): if "RestApiServer" in modules: + def get_global_environments(self): + """Avalon global environments for pype implementation.""" + mongodb_data_dir = os.environ.get("AVALON_DB_DATA") + if not mongodb_data_dir: + mongodb_data_dir = os.path.join( + os.path.dirname(os.environ["PYPE_ROOT"]), + "mongo_db_data" + ) + return { + # 100% hardcoded + "AVALON_SCHEMA": self.schema_path, + "AVALON_CONFIG": "pype", + "AVALON_LABEL": "Pype", + + # Modifiable by settings + # - mongo ulr for avalon projects + "AVALON_MONGO": self.avalon_mongo_url, + # TODO thumbnails root should be multiplafrom + # - thumbnails root + "AVALON_THUMBNAIL_ROOT": self.thumbnail_root, + + # May be modifiable? + # - mongo database name where projects are stored + "AVALON_DB": "avalon", + # - mongo timeout in ms + "AVALON_TIMEOUT": "1000", + + # Avalon environments not used in code + "AVALON_DEBUG": "1", + "AVALON_EARLY_ADOPTER": "1", + + # Not even connected to Avalon + # TODO remove - pype's variable for local mongo + "AVALON_DB_DATA": mongodb_data_dir + } + + def tray_init(self, tray_widget, main_window): + # Add library tool + try: + from avalon.tools.libraryloader import app + from avalon import style + + self.libraryloader = app.Window( + icon=tray_widget.icon, + show_projects=True, + show_libraries=True + ) + self.libraryloader.setStyleSheet(style.load_stylesheet()) + except Exception: + self.log.warning( + "Couldn't load Library loader tool for tray.", + exc_info=True + ) + + # Add launcher + try: + from pype.tools.launcher import LauncherWindow + self.app_launcher = LauncherWindow() + except Exception: + self.log.warning( + "Couldn't load Launch for tray.", + exc_info=True + ) + + def connect_with_modules(self, enabled_modules): + plugin_paths = [] + for module in enabled_modules: + if not isinstance(module, IPluginPaths): + continue + + paths = (module.get_plugin_paths() or {}).get("actions") + if not paths: + continue + + if not isinstance(paths, (list, tuple, set)): + paths = [paths] + plugin_paths.extend(paths) + + if plugin_paths: + env_paths_str = os.environ.get("AVALON_ACTIONS") or "" + env_paths = env_paths_str.split(os.pathsep) + env_paths.extend(plugin_paths) + os.environ["AVALON_ACTIONS"] = os.pathsep.join(env_paths) + + if self.tray_initialized: + from pype.tools.launcher import actions + # actions.register_default_actions() + actions.register_config_actions() + actions.register_environment_actions() from .rest_api import AvalonRestApi self.rest_api_obj = AvalonRestApi() # Definition of Tray menu - def tray_menu(self, parent_menu=None): + def tray_menu(self, tray_menu): from Qt import QtWidgets # Actions - if parent_menu is None: - if self.parent is None: - self.log.warning('Parent menu is not set') - return - elif self.parent.hasattr('menu'): - parent_menu = self.parent.menu - else: - self.log.warning('Parent menu is not set') - return - - action_launcher = QtWidgets.QAction("Launcher", parent_menu) + action_launcher = QtWidgets.QAction("Launcher", tray_menu) action_library_loader = QtWidgets.QAction( - "Library loader", parent_menu + "Library loader", tray_menu ) action_launcher.triggered.connect(self.show_launcher) action_library_loader.triggered.connect(self.show_library_loader) - parent_menu.addAction(action_launcher) - parent_menu.addAction(action_library_loader) + tray_menu.addAction(action_launcher) + tray_menu.addAction(action_library_loader) + + def tray_start(self, *_a, **_kw): + return + + def tray_exit(self, *_a, **_kw): + return def show_launcher(self): # if app_launcher don't exist create it/otherwise only show main window From 1c139dd90956948303a292b46b7f674f80734348 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 10 Dec 2020 10:24:09 +0100 Subject: [PATCH 15/78] converted idle manager --- pype/modules/__init__.py | 6 + pype/modules/idle_manager/__init__.py | 11 +- pype/modules/idle_manager/idle_manager.py | 134 ++++++++++++++++------ 3 files changed, 112 insertions(+), 39 deletions(-) diff --git a/pype/modules/__init__.py b/pype/modules/__init__.py index daa5a185d1..fad9a37161 100644 --- a/pype/modules/__init__.py +++ b/pype/modules/__init__.py @@ -1,10 +1,16 @@ # -*- coding: utf-8 -*- from .base import PypeModule +from .idle_manager import ( + IdleManager, + IIdleManager +) from .avalon_apps import AvalonModule from .clockify import ClockifyModule __all__ = ( "PypeModule", + "IdleManager", + "IIdleManager", "AvalonModule", "ClockifyModule", ) diff --git a/pype/modules/idle_manager/__init__.py b/pype/modules/idle_manager/__init__.py index f1a87bef41..4bc33c87c1 100644 --- a/pype/modules/idle_manager/__init__.py +++ b/pype/modules/idle_manager/__init__.py @@ -1,5 +1,10 @@ -from .idle_manager import IdleManager +from .idle_manager import ( + IdleManager, + IIdleManager +) -def tray_init(tray_widget, main_widget): - return IdleManager() +__all__ = ( + "IdleManager", + "IIdleManager" +) diff --git a/pype/modules/idle_manager/idle_manager.py b/pype/modules/idle_manager/idle_manager.py index 3a9f9154a9..ca9872b2ed 100644 --- a/pype/modules/idle_manager/idle_manager.py +++ b/pype/modules/idle_manager/idle_manager.py @@ -1,65 +1,131 @@ import time import collections import threading +from abc import ABCMeta, abstractmethod + +import six from pynput import mouse, keyboard -from pype.api import Logger + +from pype.lib import PypeLogger +from pype.modules import PypeModule, ITrayService -class IdleManager(threading.Thread): +@six.add_metaclass(ABCMeta) +class IIdleManager: + """Other modules interface to return callbacks by idle time in seconds. + + Expected output is dictionary with seconds as keys and callback/s + as value, value may be callback of list of callbacks. + EXAMPLE: + ``` + { + 60: self.on_minute_idle + } + ``` + """ + idle_manager = None + + @abstractmethod + def callbacks_by_idle_time(self): + pass + + @property + def idle_time(self): + if self.idle_manager: + return self.idle_manager.idle_time + + +class IdleManager(PypeModule, ITrayService): """ Measure user's idle time in seconds. Idle time resets on keyboard/mouse input. Is able to emit signals at specific time idle. """ - time_callbacks = collections.defaultdict(list) - idle_time = 0 + label = "Idle Service" + name = "Idle Manager" - def __init__(self): - super(IdleManager, self).__init__() - self.log = Logger().get_logger(self.__class__.__name__) - self.qaction = None - self.failed_icon = None - self._is_running = False - self.threads = [] + def initialize(self, module_settings): + idle_man_settings = module_settings[self.name] + self.enabled = idle_man_settings["enabled"] - def set_qaction(self, qaction, failed_icon): - self.qaction = qaction - self.failed_icon = failed_icon + self.time_callbacks = collections.defaultdict(list) + self.idle_thread = None + + def tray_init(self, *_a, **_kw): + return def tray_start(self): - self.start() + self.start_thread() def tray_exit(self): - self.stop() + self.stop_thread() try: self.time_callbacks = {} except Exception: pass - def add_time_callback(self, emit_time, callback): - """If any module want to use IdleManager, need to use this method. + def connect_with_modules(self, enabled_modules): + for module in enabled_modules: + if not isinstance(module, IIdleManager): + continue - Args: - emit_time(int): Time when callback will be triggered. - callback(func): Callback that will be triggered. - """ - self.time_callbacks[emit_time].append(callback) + module.idle_manager = self + callbacks_items = module.callbacks_by_idle_time() or {} + for emit_time, callbacks in callbacks_items.items(): + if not isinstance(callbacks, (tuple, list, set)): + callbacks = [callbacks] + self.time_callbacks[emit_time].extend(callbacks) @property - def is_running(self): - return self._is_running + def idle_time(self): + if self.idle_thread and self.idle_thread.is_running: + return self.idle_thread.idle_time - def _reset_time(self): + def start_thread(self): + if self.idle_thread: + self.idle_thread.stop() + self.idle_thread.join() + self.idle_thread = IdleManagerThread(self) + self.idle_thread.start() + + def stop_thread(self): + if self.idle_thread: + self.idle_thread.stop() + self.idle_thread.join() + + def on_thread_stop(self): + self.set_service_failed() + + +class IdleManagerThread(threading.Thread): + def __init__(self, module, *args, **kwargs): + super(IdleManagerThread, self).__init__(*args, **kwargs) + self.log = PypeLogger().get_logger(self.__class__.__name__) + self.module = module + self.threads = [] + self.is_running = False self.idle_time = 0 def stop(self): - self._is_running = False + self.is_running = False + + def reset_time(self): + self.idle_time = 0 + + @property + def time_callbacks(self): + return self.module.time_callbacks + + def on_stop(self): + self.is_running = False + self.log.info("IdleManagerThread has stopped") + self.module.on_thread_stop() def run(self): - self.log.info('IdleManager has started') - self._is_running = True - thread_mouse = MouseThread(self._reset_time) + self.log.info("IdleManagerThread has started") + self.is_running = True + thread_mouse = MouseThread(self.reset_time) thread_mouse.start() - thread_keyboard = KeyboardThread(self._reset_time) + thread_keyboard = KeyboardThread(self.reset_time) thread_keyboard.start() try: while self.is_running: @@ -82,9 +148,6 @@ class IdleManager(threading.Thread): 'Idle Manager service has failed', exc_info=True ) - if self.qaction and self.failed_icon: - self.qaction.setIcon(self.failed_icon) - # Threads don't have their attrs when Qt application already finished try: thread_mouse.stop() @@ -98,8 +161,7 @@ class IdleManager(threading.Thread): except AttributeError: pass - self._is_running = False - self.log.info('IdleManager has stopped') + self.on_stop() class MouseThread(mouse.Listener): From eff22772299f4dcebc4f983982407d705e992d5e Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 10 Dec 2020 10:25:46 +0100 Subject: [PATCH 16/78] converted rest api module --- pype/modules/__init__.py | 7 +++ pype/modules/rest_api/__init__.py | 30 ++++++++-- pype/modules/rest_api/rest_api.py | 97 ++++++++++++++++++++----------- 3 files changed, 95 insertions(+), 39 deletions(-) diff --git a/pype/modules/__init__.py b/pype/modules/__init__.py index fad9a37161..7417bca16b 100644 --- a/pype/modules/__init__.py +++ b/pype/modules/__init__.py @@ -1,5 +1,9 @@ # -*- coding: utf-8 -*- from .base import PypeModule +from .rest_api import ( + RestApiModule, + IRestApi +) from .idle_manager import ( IdleManager, IIdleManager @@ -11,6 +15,9 @@ __all__ = ( "PypeModule", "IdleManager", "IIdleManager", + "RestApiModule", + "IRestApi", + "AvalonModule", "ClockifyModule", ) diff --git a/pype/modules/rest_api/__init__.py b/pype/modules/rest_api/__init__.py index 55253bc58b..b3312e8d31 100644 --- a/pype/modules/rest_api/__init__.py +++ b/pype/modules/rest_api/__init__.py @@ -1,9 +1,27 @@ -from .rest_api import RestApiServer -from .base_class import RestApi, abort, route, register_statics -from .lib import RestMethods, CallbackResult +from .rest_api import ( + RestApiModule, + IRestApi +) +from .base_class import ( + RestApi, + abort, + route, + register_statics +) +from .lib import ( + RestMethods, + CallbackResult +) -CLASS_DEFINIION = RestApiServer +__all__ = ( + "RestApiModule", + "IRestApi", + "RestApi", + "abort", + "route", + "register_statics", -def tray_init(tray_widget, main_widget): - return RestApiServer() + "RestMethods", + "CallbackResult" +) diff --git a/pype/modules/rest_api/rest_api.py b/pype/modules/rest_api/rest_api.py index 3e0c646560..8cc6a213df 100644 --- a/pype/modules/rest_api/rest_api.py +++ b/pype/modules/rest_api/rest_api.py @@ -1,21 +1,31 @@ -import os import socket import threading - +from abc import ABCMeta, abstractmethod from socketserver import ThreadingMixIn from http.server import HTTPServer + +import six + +from pype.lib import PypeLogger +from pype import resources + from .lib import RestApiFactory, Handler from .base_class import route, register_statics -from pype.api import Logger - -log = Logger().get_logger("RestApiServer") +from .. import PypeModule, ITrayService -class ThreadingSimpleServer(ThreadingMixIn, HTTPServer): - pass +@six.add_metaclass(ABCMeta) +class IRestApi: + """Other modules interface to return paths to ftrack event handlers. + + Expected output is dictionary with "server" and "user" keys. + """ + @abstractmethod + def rest_api_initialization(self, rest_api_module): + pass -class RestApiServer: +class RestApiModule(PypeModule, ITrayService): """Rest Api allows to access statics or callbacks with http requests. To register statics use `register_statics`. @@ -85,30 +95,32 @@ class RestApiServer: Callback may return many types. For more information read docstring of `_handle_callback_result` defined in handler. """ - default_port = 8011 - exclude_ports = [] + label = "Rest API Service" + name = "Rest Api" - def __init__(self): - self.qaction = None - self.failed_icon = None - self._is_running = False + def initialize(self, modules_settings): + rest_api_settings = modules_settings[self.name] + self.enabled = True + self.default_port = rest_api_settings["default_port"] + self.exclude_ports = rest_api_settings["exclude_ports"] + + self._thread_initialized = False + + self.rest_api_url = None + self.rest_api_thread = None + self.resources_url = None + + def initialize_thread(self): + if self._thread_initialized: + return port = self.find_port() + self.rest_api_url = "http://localhost:{}".format(port) self.rest_api_thread = RestApiThread(self, port) + self.register_statics("/res", resources.RESOURCES_DIR) + self.resources_url = "{}/res".format(self.rest_api_url) - statics_dir = os.path.join( - os.environ["PYPE_MODULE_ROOT"], - "pype", - "resources" - ) - self.register_statics("/res", statics_dir) - os.environ["PYPE_STATICS_SERVER"] = "{}/res".format( - os.environ["PYPE_REST_API_URL"] - ) - - def set_qaction(self, qaction, failed_icon): - self.qaction = qaction - self.failed_icon = failed_icon + self._thread_initialized = True def register_callback( self, path, callback, url_prefix="", methods=[], strict_match=False @@ -123,6 +135,13 @@ class RestApiServer: def register_obj(self, obj): RestApiFactory.register_obj(obj) + def connect_with_modules(self, enabled_modules): + for module in enabled_modules: + if not isinstance(module, IRestApi): + continue + + module.rest_api_initialization(self) + def find_port(self): start_port = self.default_port exclude_ports = self.exclude_ports @@ -139,15 +158,22 @@ class RestApiServer: break if found_port is None: return None - os.environ["PYPE_REST_API_URL"] = "http://localhost:{}".format( - found_port - ) return found_port + def get_global_environments(self): + self.initialize_thread() + return { + "PYPE_REST_API_URL": self.rest_api_url, + "PYPE_STATICS_SERVER": self.resources_url + } + + def tray_init(self, *_a, **_kw): + self.initialize_thread() + def tray_start(self): RestApiFactory.prepare_registered() if not RestApiFactory.has_handlers(): - log.debug("There are not registered any handlers for RestApi") + self.log.debug("There are not registered any handlers for RestApi") return self.rest_api_thread.start() @@ -163,6 +189,10 @@ class RestApiServer: self.rest_api_thread.join() +class ThreadingSimpleServer(ThreadingMixIn, HTTPServer): + pass + + class RestApiThread(threading.Thread): """ Listener for REST requests. @@ -176,6 +206,7 @@ class RestApiThread(threading.Thread): self.module = module self.port = port self.httpd = None + self.log = PypeLogger().get_logger("RestApiThread") def stop(self): self.is_running = False @@ -186,7 +217,7 @@ class RestApiThread(threading.Thread): self.is_running = True try: - log.debug( + self.log.debug( "Running Rest Api server on URL:" " \"http://localhost:{}\"".format(self.port) ) @@ -197,7 +228,7 @@ class RestApiThread(threading.Thread): httpd.handle_request() except Exception: - log.warning( + self.log.warning( "Rest Api Server service has failed", exc_info=True ) From 32fc16a34ff88918ce9b7dd1472e9799d5372084 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 10 Dec 2020 10:26:25 +0100 Subject: [PATCH 17/78] converted timers manager module --- pype/modules/__init__.py | 8 + pype/modules/timers_manager/__init__.py | 14 +- pype/modules/timers_manager/timers_manager.py | 214 +++++++++--------- 3 files changed, 118 insertions(+), 118 deletions(-) diff --git a/pype/modules/__init__.py b/pype/modules/__init__.py index 7417bca16b..17eef13485 100644 --- a/pype/modules/__init__.py +++ b/pype/modules/__init__.py @@ -8,6 +8,10 @@ from .idle_manager import ( IdleManager, IIdleManager ) +from .timers_manager import ( + TimersManager, + ITimersManager +) from .avalon_apps import AvalonModule from .clockify import ClockifyModule @@ -15,6 +19,10 @@ __all__ = ( "PypeModule", "IdleManager", "IIdleManager", + + "TimersManager", + "ITimersManager", + "RestApiModule", "IRestApi", diff --git a/pype/modules/timers_manager/__init__.py b/pype/modules/timers_manager/__init__.py index 9de205f088..1b565cc59a 100644 --- a/pype/modules/timers_manager/__init__.py +++ b/pype/modules/timers_manager/__init__.py @@ -1,7 +1,9 @@ -from .timers_manager import TimersManager +from .timers_manager import ( + ITimersManager, + TimersManager +) -CLASS_DEFINIION = TimersManager - - -def tray_init(tray_widget, main_widget): - return TimersManager(tray_widget, main_widget) +__all__ = ( + "ITimersManager", + "TimersManager" +) diff --git a/pype/modules/timers_manager/timers_manager.py b/pype/modules/timers_manager/timers_manager.py index 62767c24f1..e31342b9b0 100644 --- a/pype/modules/timers_manager/timers_manager.py +++ b/pype/modules/timers_manager/timers_manager.py @@ -1,118 +1,136 @@ -from pype.api import Logger +from abc import ABCMeta, abstractmethod +import six +from .. import PypeModule, ITrayService, IIdleManager -class TimersManager: +@six.add_metaclass(ABCMeta) +class ITimersManager: + timer_manager_module = None + + @abstractmethod + def stop_timer(self): + pass + + @abstractmethod + def start_timer(self, data): + pass + + def timer_started(self, data): + if not self.timer_manager_module: + return + + self.timer_manager_module.timer_started(self.id, data) + + def timer_stopped(self): + if not self.timer_manager_module: + return + + self.timer_manager_module.timer_stopped(self.id) + + +class TimersManager(PypeModule, ITrayService, IIdleManager): """ Handles about Timers. Should be able to start/stop all timers at once. If IdleManager is imported then is able to handle about stop timers when user idles for a long time (set in presets). """ + name = "Timers Manager" - # Presetable attributes - # - when timer will stop if idle manager is running (minutes) - full_time = 15 - # - how many minutes before the timer is stopped will popup the message - message_time = 0.5 + def initialize(self, modules_settings): + timers_settings = modules_settings[self.name] - def __init__(self, tray_widget, main_widget): - self.log = Logger().get_logger(self.__class__.__name__) + self.enabled = timers_settings["enabled"] + # When timer will stop if idle manager is running (minutes) + full_time = int(timers_settings["full_time"] * 60) + # How many minutes before the timer is stopped will popup the message + message_time = int(timers_settings["message_time"] * 60) + + self.time_show_message = full_time - message_time + self.time_stop_timer = full_time - self.modules = [] self.is_running = False self.last_task = None - self.tray_widget = tray_widget - self.main_widget = main_widget - - self.idle_man = None + # Tray attributes + self.signal_handler = None + self.widget_user_idle = None self.signal_handler = None - self.trat_init(tray_widget, main_widget) + self.modules = [] - def trat_init(self, tray_widget, main_widget): + def tray_init(self, tray_widget, main_widget): from .widget_user_idle import WidgetUserIdle, SignalHandler self.widget_user_idle = WidgetUserIdle(self, tray_widget) self.signal_handler = SignalHandler(self) - def set_signal_times(self): - try: - full_time = int(self.full_time * 60) - message_time = int(self.message_time * 60) - self.time_show_message = full_time - message_time - self.time_stop_timer = full_time - return True - except Exception: - self.log.error("Couldn't set timer signals.", exc_info=True) + def tray_start(self, *_a, **_kw): + return - def add_module(self, module): - """ Adds module to context + def tray_exit(self): + """Nothing special for TimersManager.""" + return - Module must have implemented methods: - - ``start_timer_manager(data)`` - - ``stop_timer_manager()`` - """ - self.modules.append(module) - - def start_timers(self, data): - ''' - :param data: basic information needed to start any timer - :type data: dict - ..note:: - Dictionary "data" should contain: - - project_name(str) - Name of Project - - hierarchy(list/tuple) - list of parents(except project) - - task_type(str) - - task_name(str) - - Example: - - to run timers for task in - 'C001_BackToPast/assets/characters/villian/Lookdev BG' - - input data should contain: - .. code-block:: Python - data = { - 'project_name': 'C001_BackToPast', - 'hierarchy': ['assets', 'characters', 'villian'], - 'task_type': 'lookdev', - 'task_name': 'Lookdev BG' - } - ''' - if len(data['hierarchy']) < 1: - self.log.error(( - 'Not allowed action in Pype!!' - ' Timer has been launched on task which is child of Project.' - )) - return + def timer_started(self, source_id, data): + for module in self.modules: + if module.id != source_id: + module.start_timer(data) self.last_task = data - - for module in self.modules: - module.start_timer_manager(data) self.is_running = True + def timer_stopped(self, source_id): + for module in self.modules: + if module.id != source_id: + module.stop_timer() + def restart_timers(self): if self.last_task is not None: - self.start_timers(self.last_task) + self.timer_started(None, self.last_task) def stop_timers(self): if self.is_running is False: return + self.widget_user_idle.bool_not_stopped = False self.widget_user_idle.refresh_context() - for module in self.modules: - module.stop_timer_manager() self.is_running = False - def process_modules(self, modules): - """ Gives ability to connect with imported modules from TrayManager. + for module in self.modules: + module.stop_timer() - :param modules: All imported modules from TrayManager - :type modules: dict - """ + def connect_with_modules(self, enabled_modules): + for module in enabled_modules: + if not isinstance(module, ITimersManager): + continue + module.timer_manager_module = self + self.modules.append(module) - if 'IdleManager' in modules: - if self.set_signal_times() is True: - self.register_to_idle_manager(modules['IdleManager']) + def callbacks_by_idle_time(self): + """Implementation of IIdleManager interface.""" + # Time when message is shown + callbacks = { + self.time_show_message: lambda: self.time_callback(0) + } + + # Times when idle is between show widget and stop timers + show_to_stop_range = range( + self.time_show_message - 1, self.time_stop_timer + ) + for num in show_to_stop_range: + callbacks[num] = lambda: self.time_callback(1) + + # Times when widget is already shown and user restart idle + shown_and_moved_range = range( + self.time_stop_timer - self.time_show_message + ) + for num in shown_and_moved_range: + callbacks[num] = lambda: self.time_callback(1) + + # Time when timers are stopped + callbacks[self.time_stop_timer] = lambda: self.time_callback(2) + + return callbacks def time_callback(self, int_def): if not self.signal_handler: @@ -125,51 +143,23 @@ class TimersManager: elif int_def == 2: self.signal_handler.signal_stop_timers.emit() - def register_to_idle_manager(self, man_obj): - self.idle_man = man_obj - - # Time when message is shown - self.idle_man.add_time_callback( - self.time_show_message, - lambda: self.time_callback(0) - ) - - # Times when idle is between show widget and stop timers - show_to_stop_range = range( - self.time_show_message - 1, self.time_stop_timer - ) - for num in show_to_stop_range: - self.idle_man.add_time_callback( - num, lambda: self.time_callback(1) - ) - # Times when widget is already shown and user restart idle - shown_and_moved_range = range( - self.time_stop_timer - self.time_show_message - ) - for num in shown_and_moved_range: - self.idle_man.add_time_callback( - num, lambda: self.time_callback(1) - ) - - # Time when timers are stopped - self.idle_man.add_time_callback( - self.time_stop_timer, - lambda: self.time_callback(2) - ) - def change_label(self): if self.is_running is False: return - if not self.idle_man or self.widget_user_idle.bool_is_showed is False: + + if ( + not self.idle_manager + or self.widget_user_idle.bool_is_showed is False + ): return - if self.idle_man.idle_time > self.time_show_message: - value = self.time_stop_timer - self.idle_man.idle_time + if self.idle_manager.idle_time > self.time_show_message: + value = self.time_stop_timer - self.idle_manager.idle_time else: value = 1 + ( self.time_stop_timer - self.time_show_message - - self.idle_man.idle_time + self.idle_manager.idle_time ) self.widget_user_idle.change_count_widget(value) From cf0cb2a8ca69e3241e5a9469517e32710b4dd117 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 10 Dec 2020 10:28:45 +0100 Subject: [PATCH 18/78] converted websocket server module --- pype/modules/__init__.py | 4 + pype/modules/websocket_server/__init__.py | 11 ++- .../websocket_server/websocket_server.py | 81 ++++++++++++++----- 3 files changed, 74 insertions(+), 22 deletions(-) diff --git a/pype/modules/__init__.py b/pype/modules/__init__.py index 17eef13485..419c0a3633 100644 --- a/pype/modules/__init__.py +++ b/pype/modules/__init__.py @@ -14,6 +14,8 @@ from .timers_manager import ( ) from .avalon_apps import AvalonModule from .clockify import ClockifyModule +from .websocket_server import WebsocketModule + __all__ = ( "PypeModule", @@ -28,4 +30,6 @@ __all__ = ( "AvalonModule", "ClockifyModule", + + "WebsocketModule" ) diff --git a/pype/modules/websocket_server/__init__.py b/pype/modules/websocket_server/__init__.py index eb5a0d9f27..0f6888585f 100644 --- a/pype/modules/websocket_server/__init__.py +++ b/pype/modules/websocket_server/__init__.py @@ -1,5 +1,10 @@ -from .websocket_server import WebSocketServer +from .websocket_server import ( + WebsocketModule, + WebSocketServer +) -def tray_init(tray_widget, main_widget): - return WebSocketServer() +__all__ = ( + "WebsocketModule", + "WebSocketServer" +) diff --git a/pype/modules/websocket_server/websocket_server.py b/pype/modules/websocket_server/websocket_server.py index daf4b03103..e99265f604 100644 --- a/pype/modules/websocket_server/websocket_server.py +++ b/pype/modules/websocket_server/websocket_server.py @@ -1,17 +1,53 @@ -from pype.api import Logger - -import threading -from aiohttp import web -import asyncio -from wsrpc_aiohttp import STATIC_DIR, WebSocketAsync - import os import sys import pyclbr import importlib import urllib +import threading -log = Logger().get_logger("WebsocketServer") +import six +from pype.lib import PypeLogger +from .. import PypeModule, ITrayService + +if six.PY2: + web = asyncio = STATIC_DIR = WebSocketAsync = None +else: + from aiohttp import web + import asyncio + from wsrpc_aiohttp import STATIC_DIR, WebSocketAsync + +log = PypeLogger().get_logger("WebsocketServer") + + +class WebsocketModule(PypeModule, ITrayService): + name = "Websocket server" + label = "Websocket server" + + def initialize(self, module_settings): + if asyncio is None: + raise AssertionError( + "WebSocketServer module requires Python 3.5 or higher." + ) + + self.enabled = True + self.websocket_server = None + + def connect_with_modules(self, *_a, **kw): + return + + def tray_init(self, *_a, **kw): + self.websocket_server = WebSocketServer() + self.websocket_server.on_stop_callbacks.append( + self.set_service_failed + ) + + def tray_start(self): + if self.websocket_server: + self.websocket_server.module_start() + + def tray_exit(self): + if self.websocket_server: + self.websocket_server.module_stop() class WebSocketServer(): @@ -24,12 +60,11 @@ class WebSocketServer(): _instance = None def __init__(self): - self.qaction = None - self.failed_icon = None - self._is_running = False WebSocketServer._instance = self + self.client = None self.handlers = {} + self.on_stop_callbacks = [] port = None websocket_url = os.getenv("WEBSOCKET_URL") @@ -51,6 +86,14 @@ class WebSocketServer(): self.websocket_thread = WebsocketServerThread(self, port) + def module_start(self): + if self.websocket_thread: + self.websocket_thread.start() + + def module_stop(self): + if self.websocket_thread: + self.websocket_thread.stop() + def add_routes_for_directories(self, directories_with_routes): """ Loops through selected directories to find all modules and in them all classes implementing 'WebSocketRoute' that could be @@ -106,14 +149,7 @@ class WebSocketServer(): WebSocketServer() return WebSocketServer._instance - def tray_start(self): - self.websocket_thread.start() - - def tray_exit(self): - self.stop() - def stop_websocket_server(self): - self.stop() @property @@ -134,7 +170,8 @@ class WebSocketServer(): ) def thread_stopped(self): - self._is_running = False + for callback in self.on_stop_callbacks: + callback() class WebsocketServerThread(threading.Thread): @@ -145,7 +182,13 @@ class WebsocketServerThread(threading.Thread): it creates separate thread and separate asyncio event loop """ def __init__(self, module, port): + if asyncio is None: + raise AssertionError( + "WebSocketServer module requires Python 3.5 or higher." + ) + super(WebsocketServerThread, self).__init__() + self.is_running = False self.port = port self.module = module From 1b2e8029c8a799a5f8f5b491f37187e3384fffce Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 10 Dec 2020 10:29:10 +0100 Subject: [PATCH 19/78] converted user module --- pype/modules/__init__.py | 7 +++ pype/modules/user/__init__.py | 11 +++-- pype/modules/user/user_module.py | 82 +++++++++++++++++++++----------- 3 files changed, 69 insertions(+), 31 deletions(-) diff --git a/pype/modules/__init__.py b/pype/modules/__init__.py index 419c0a3633..01a7b2870c 100644 --- a/pype/modules/__init__.py +++ b/pype/modules/__init__.py @@ -4,6 +4,10 @@ from .rest_api import ( RestApiModule, IRestApi ) +from .user import ( + UserModule, + IUserModule +) from .idle_manager import ( IdleManager, IIdleManager @@ -19,6 +23,9 @@ from .websocket_server import WebsocketModule __all__ = ( "PypeModule", + "UserModule", + "IUserModule", + "IdleManager", "IIdleManager", diff --git a/pype/modules/user/__init__.py b/pype/modules/user/__init__.py index 04fe392c2c..a97ac0eef6 100644 --- a/pype/modules/user/__init__.py +++ b/pype/modules/user/__init__.py @@ -1,5 +1,10 @@ -from .user_module import UserModule +from .user_module import ( + UserModule, + IUserModule +) -def tray_init(tray_widget, main_widget): - return UserModule(main_widget, tray_widget) +__all__ = ( + "UserModule", + "IUserModule" +) diff --git a/pype/modules/user/user_module.py b/pype/modules/user/user_module.py index dc57fe4a63..d60dfed353 100644 --- a/pype/modules/user/user_module.py +++ b/pype/modules/user/user_module.py @@ -2,29 +2,46 @@ import os import json import getpass +from abc import ABCMeta, abstractmethod + +import six import appdirs -from pype.api import Logger +from .. import PypeModule, ITrayModule, IRestApi -class UserModule: +@six.add_metaclass(ABCMeta) +class IUserModule: + """Interface for other modules to use user change callbacks.""" + + @abstractmethod + def on_pype_user_change(self, username): + """What should happen on Pype user change.""" + pass + + +class UserModule(PypeModule, ITrayModule, IRestApi): cred_folder_path = os.path.normpath( appdirs.user_data_dir('pype-app', 'pype') ) cred_filename = 'user_info.json' env_name = "PYPE_USERNAME" - log = Logger().get_logger("UserModule", "user") + name = "User setting" - def __init__(self, main_parent=None, parent=None): - self._callbacks_on_user_change = [] + def initialize(self, modules_settings): + user_settings = modules_settings[self.name] + self.enabled = user_settings["enabled"] + + self.callbacks_on_user_change = [] self.cred = {} self.cred_path = os.path.normpath(os.path.join( self.cred_folder_path, self.cred_filename )) - self.widget_login = None - self.tray_init(main_parent, parent) + # Tray attributes + self.widget_login = None + self.action_show_widget = None def tray_init(self, main_parent=None, parent=None): from .widget_user import UserWidget @@ -33,7 +50,7 @@ class UserModule: self.load_credentials() def register_callback_on_user_change(self, callback): - self._callbacks_on_user_change.append(callback) + self.callbacks_on_user_change.append(callback) def tray_start(self): """Store credentials to env and preset them to widget""" @@ -44,29 +61,34 @@ class UserModule: os.environ[self.env_name] = username self.widget_login.set_user(username) + def tray_exit(self): + """Nothing special for User.""" + return + def get_user(self): return self.cred.get("username") or getpass.getuser() - def process_modules(self, modules): - """ Gives ability to connect with imported modules from TrayManager. + def rest_api_initialization(self, rest_api_module): + def api_get_username(): + return self.cred - :param modules: All imported modules from TrayManager - :type modules: dict - """ + rest_api_module.register_callback( + "user/username", api_get_username, "get" + ) - if "RestApiServer" in modules: - def api_get_username(): - return self.cred + def api_show_widget(): + self.action_show_widget.trigger() - def api_show_widget(): - self.action_show_widget.trigger() + rest_api_module.register_callback( + "user/show_widget", api_show_widget, "post" + ) - modules["RestApiServer"].register_callback( - "user/username", api_get_username, "get" - ) - modules["RestApiServer"].register_callback( - "user/show_widget", api_show_widget, "post" - ) + def connect_with_modules(self, enabled_modules): + for module in enabled_modules: + if isinstance(module, IUserModule): + self.callbacks_on_user_change.append( + module.on_pype_user_change + ) # Definition of Tray menu def tray_menu(self, parent_menu): @@ -108,12 +130,14 @@ class UserModule: def change_credentials(self, username): self.save_credentials(username) - for callback in self._callbacks_on_user_change: + for callback in self.callbacks_on_user_change: try: - callback() + callback(username) except Exception: self.log.warning( - "Failed to execute callback \"{}\".".format(str(callback)), + "Failed to execute callback \"{}\".".format( + str(callback) + ), exc_info=True ) @@ -135,7 +159,9 @@ class UserModule: self.log.debug("Username \"{}\" stored".format(username)) except Exception: self.log.error( - "Could not store username to file \"{}\"".format(self.cred_path), + "Could not store username to file \"{}\"".format( + self.cred_path + ), exc_info=True ) From ba17ca17d51fddbb8264bb59ef110dfb31181430 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 10 Dec 2020 10:32:53 +0100 Subject: [PATCH 20/78] converted muster module --- pype/modules/__init__.py | 2 + pype/modules/muster/__init__.py | 5 +- pype/modules/muster/muster.py | 77 +++++++++++++++++------------ pype/modules/muster/widget_login.py | 18 +++---- 4 files changed, 56 insertions(+), 46 deletions(-) diff --git a/pype/modules/__init__.py b/pype/modules/__init__.py index 01a7b2870c..d832563ca5 100644 --- a/pype/modules/__init__.py +++ b/pype/modules/__init__.py @@ -18,6 +18,7 @@ from .timers_manager import ( ) from .avalon_apps import AvalonModule from .clockify import ClockifyModule +from .muster import MusterModule from .websocket_server import WebsocketModule @@ -37,6 +38,7 @@ __all__ = ( "AvalonModule", "ClockifyModule", + "MusterModule", "WebsocketModule" ) diff --git a/pype/modules/muster/__init__.py b/pype/modules/muster/__init__.py index 9429cbe561..d194f8f3c2 100644 --- a/pype/modules/muster/__init__.py +++ b/pype/modules/muster/__init__.py @@ -1,5 +1,6 @@ from .muster import MusterModule -def tray_init(tray_widget, main_widget): - return MusterModule(main_widget, tray_widget) +__all__ = ( + "MusterModule", +) diff --git a/pype/modules/muster/muster.py b/pype/modules/muster/muster.py index beb30690ac..5ea02f7541 100644 --- a/pype/modules/muster/muster.py +++ b/pype/modules/muster/muster.py @@ -2,9 +2,10 @@ import os import json import appdirs import requests +from .. import PypeModule, ITrayModule, IRestApi -class MusterModule: +class MusterModule(PypeModule, ITrayModule, IRestApi): """ Module handling Muster Render credentials. This will display dialog asking for user credentials for Muster if not already specified. @@ -14,38 +15,42 @@ class MusterModule: ) cred_filename = 'muster_cred.json' - def __init__(self, main_parent=None, parent=None): + name = "muster" + + def initialize(self, modules_settings): + muster_settings = modules_settings[self.name] + self.enabled = muster_settings["enabled"] + self.muster_url = muster_settings["MUSTER_REST_URL"] + self.cred_path = os.path.join( self.cred_folder_path, self.cred_filename ) - self.tray_init(main_parent, parent) + # Tray attributes + self.widget_login = None + self.action_show_login = None - def tray_init(self, main_parent, parent): + def get_global_environments(self): + return { + "MUSTER_REST_URL": self.muster_url + } + + def tray_init(self, tray_widget, main_window): from .widget_login import MusterLogin - - self.main_parent = main_parent - self.parent = parent - self.widget_login = MusterLogin(main_parent, self) + self.widget_login = MusterLogin(self) def tray_start(self): - """ - Show login dialog if credentials not found. - """ + """Show login dialog if credentials not found.""" # This should be start of module in tray cred = self.load_credentials() if not cred: self.show_login() - else: - # nothing to do - pass - def process_modules(self, modules): - if "RestApiServer" in modules: - def api_show_login(): - self.aShowLogin.trigger() - modules["RestApiServer"].register_callback( - "/show_login", api_show_login, "muster", "post" - ) + def tray_exit(self): + """Nothing special for Muster.""" + return + + def connect_with_modules(self, *_a, **_kw): + return # Definition of Tray menu def tray_menu(self, parent): @@ -53,18 +58,26 @@ class MusterModule: from Qt import QtWidgets # Menu for Tray App - self.menu = QtWidgets.QMenu('Muster', parent) - self.menu.setProperty('submenu', 'on') + menu = QtWidgets.QMenu('Muster', parent) + menu.setProperty('submenu', 'on') # Actions - self.aShowLogin = QtWidgets.QAction( - "Change login", self.menu + self.action_show_login = QtWidgets.QAction( + "Change login", menu ) - self.menu.addAction(self.aShowLogin) - self.aShowLogin.triggered.connect(self.show_login) + menu.addAction(self.action_show_login) + self.action_show_login.triggered.connect(self.show_login) - parent.addMenu(self.menu) + parent.addMenu(menu) + + def rest_api_initialization(self, rest_api_module): + """Implementation of IRestApi interface.""" + def api_show_login(): + self.action_show_login.trigger() + rest_api_module.register_callback( + "/show_login", api_show_login, "muster", "post" + ) def load_credentials(self): """ @@ -84,8 +97,7 @@ class MusterModule: """ Authenticate user with Muster and get authToken from server. """ - MUSTER_REST_URL = os.environ.get("MUSTER_REST_URL") - if not MUSTER_REST_URL: + if not self.muster_url: raise AttributeError("Muster REST API url not set") params = { 'username': username, @@ -93,7 +105,7 @@ class MusterModule: } api_entry = '/api/login' response = self._requests_post( - MUSTER_REST_URL + api_entry, params=params) + self.muster_url + api_entry, params=params) if response.status_code != 200: self.log.error( 'Cannot log into Muster: {}'.format(response.status_code)) @@ -123,7 +135,8 @@ class MusterModule: """ Show dialog to enter credentials """ - self.widget_login.show() + if self.widget_login: + self.widget_login.show() def _requests_post(self, *args, **kwargs): """ Wrapper for requests, disabling SSL certificate validation if diff --git a/pype/modules/muster/widget_login.py b/pype/modules/muster/widget_login.py index f446c13325..af10f8df83 100644 --- a/pype/modules/muster/widget_login.py +++ b/pype/modules/muster/widget_login.py @@ -11,21 +11,15 @@ class MusterLogin(QtWidgets.QWidget): loginSignal = QtCore.Signal(object, object, object) - def __init__(self, main_parent=None, parent=None): + def __init__(self, module, parent=None): - super(MusterLogin, self).__init__() + super(MusterLogin, self).__init__(parent) - self.parent_widget = parent - self.main_parent = main_parent + self.module = module # Icon - if hasattr(parent, 'icon'): - self.setWindowIcon(parent.icon) - elif hasattr(parent, 'parent') and hasattr(parent.parent, 'icon'): - self.setWindowIcon(parent.parent.icon) - else: - icon = QtGui.QIcon(resources.pype_icon_filepath()) - self.setWindowIcon(icon) + icon = QtGui.QIcon(resources.pype_icon_filepath()) + self.setWindowIcon(icon) self.setWindowFlags( QtCore.Qt.WindowCloseButtonHint | @@ -153,7 +147,7 @@ class MusterLogin(QtWidgets.QWidget): self._close_widget() def save_credentials(self, username, password): - self.parent_widget.get_auth_token(username, password) + self.module.get_auth_token(username, password) def closeEvent(self, event): event.ignore() From 5732eb8f7c66ba31945985a2e1d11eda38f3a42a Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 10 Dec 2020 10:33:14 +0100 Subject: [PATCH 21/78] converted standalone publisher --- .../standalonepublish_module.py | 46 ++++++++++++------- 1 file changed, 30 insertions(+), 16 deletions(-) diff --git a/pype/modules/standalonepublish/standalonepublish_module.py b/pype/modules/standalonepublish/standalonepublish_module.py index f8bc0c6f24..ddd58b27d2 100644 --- a/pype/modules/standalonepublish/standalonepublish_module.py +++ b/pype/modules/standalonepublish/standalonepublish_module.py @@ -2,36 +2,50 @@ import os import sys import subprocess import pype +from .. import PypeModule, ITrayModule, IPluginPaths -class StandAlonePublishModule: - def __init__(self, main_parent=None, parent=None): - self.main_parent = main_parent - self.parent_widget = parent +class StandAlonePublishModule(PypeModule, ITrayModule): + menu_label = "Publish" + name = "Standalone Publish" + + def initialize(self, modules_settings): + self.enabled = modules_settings[self.name]["enabled"] self.publish_paths = [ os.path.join( pype.PLUGINS_DIR, "standalonepublisher", "publish" ) ] + def tray_init(self, *_a, **_kw): + return + + def tray_start(self, *_a, **_kw): + return + + def tray_exit(self, *_a, **_kw): + return + def tray_menu(self, parent_menu): from Qt import QtWidgets - self.run_action = QtWidgets.QAction( - "Publish", parent_menu - ) - self.run_action.triggered.connect(self.show) - parent_menu.addAction(self.run_action) + run_action = QtWidgets.QAction(self.menu_label, parent_menu) + run_action.triggered.connect(self.run_standalone_publisher) + parent_menu.addAction(run_action) - def process_modules(self, modules): - if "FtrackModule" in modules: - self.publish_paths.append(os.path.join( - pype.PLUGINS_DIR, "ftrack", "publish" - )) + def connect_with_modules(self, enabled_modules): + """Collect publish paths from other modules.""" + for module in enabled_modules: + if isinstance(module, IPluginPaths): + plugin_paths = module.get_plugin_paths() or {} + publish_paths = plugin_paths.get("publish") or [] + if not isinstance(publish_paths, (list, tuple, set)): + publish_paths = [publish_paths] + self.publish_paths.extend(publish_paths) - def show(self): + def run_standalone_publisher(self): from pype import tools standalone_publisher_tool_path = os.path.join( - os.path.dirname(tools.__file__), + os.path.dirname(os.path.abspath(tools.__file__)), "standalonepublish" ) subprocess.Popen([ From b9a8d356b2b39ae80fd932306809a8d0f26894db Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 10 Dec 2020 10:34:51 +0100 Subject: [PATCH 22/78] converted logging module --- pype/modules/logging/__init__.py | 6 ++++ .../logging/{tray => }/logging_module.py | 31 +++++++++++-------- pype/modules/logging/tray/__init__.py | 5 --- pype/modules/logging/tray/{gui => }/app.py | 0 pype/modules/logging/tray/gui/__init__.py | 0 pype/modules/logging/tray/{gui => }/models.py | 0 .../modules/logging/tray/{gui => }/widgets.py | 0 7 files changed, 24 insertions(+), 18 deletions(-) create mode 100644 pype/modules/logging/__init__.py rename pype/modules/logging/{tray => }/logging_module.py (53%) rename pype/modules/logging/tray/{gui => }/app.py (100%) delete mode 100644 pype/modules/logging/tray/gui/__init__.py rename pype/modules/logging/tray/{gui => }/models.py (100%) rename pype/modules/logging/tray/{gui => }/widgets.py (100%) diff --git a/pype/modules/logging/__init__.py b/pype/modules/logging/__init__.py new file mode 100644 index 0000000000..48128a0f7e --- /dev/null +++ b/pype/modules/logging/__init__.py @@ -0,0 +1,6 @@ +from .logging_module import LoggingModule + + +__all__ = ( + "LoggingModule" +) diff --git a/pype/modules/logging/tray/logging_module.py b/pype/modules/logging/logging_module.py similarity index 53% rename from pype/modules/logging/tray/logging_module.py rename to pype/modules/logging/logging_module.py index 84b40f68e1..a4400a610a 100644 --- a/pype/modules/logging/tray/logging_module.py +++ b/pype/modules/logging/logging_module.py @@ -1,41 +1,46 @@ from pype.api import Logger +from .. import PypeModule, ITrayModule -class LoggingModule: - def __init__(self, main_parent=None, parent=None): - self.parent = parent - self.log = Logger().get_logger(self.__class__.__name__, "logging") +class LoggingModule(PypeModule, ITrayModule): + name = "Logging" + def initialize(self, modules_settings): + logging_settings = modules_settings[self.name] + self.enabled = logging_settings["enabled"] + + # Tray attributes self.window = None - self.tray_init(main_parent, parent) - def tray_init(self, main_parent, parent): try: - from .gui.app import LogsWindow + from .tray.app import LogsWindow self.window = LogsWindow() - self.tray_menu = self._tray_menu except Exception: self.log.warning( "Couldn't set Logging GUI due to error.", exc_info=True ) # Definition of Tray menu - def _tray_menu(self, parent_menu): + def tray_menu(self, tray_menu): from Qt import QtWidgets # Menu for Tray App - menu = QtWidgets.QMenu('Logging', parent_menu) + menu = QtWidgets.QMenu('Logging', tray_menu) show_action = QtWidgets.QAction("Show Logs", menu) show_action.triggered.connect(self._show_logs_gui) menu.addAction(show_action) - parent_menu.addMenu(menu) + tray_menu.addMenu(menu) def tray_start(self): - pass + return - def process_modules(self, modules): + def tray_exit(self): + return + + def connect_with_modules(self, _enabled_modules): + """Nothing special.""" return def _show_logs_gui(self): diff --git a/pype/modules/logging/tray/__init__.py b/pype/modules/logging/tray/__init__.py index a2586155e7..e69de29bb2 100644 --- a/pype/modules/logging/tray/__init__.py +++ b/pype/modules/logging/tray/__init__.py @@ -1,5 +0,0 @@ -from .logging_module import LoggingModule - - -def tray_init(tray_widget, main_widget): - return LoggingModule(main_widget, tray_widget) diff --git a/pype/modules/logging/tray/gui/app.py b/pype/modules/logging/tray/app.py similarity index 100% rename from pype/modules/logging/tray/gui/app.py rename to pype/modules/logging/tray/app.py diff --git a/pype/modules/logging/tray/gui/__init__.py b/pype/modules/logging/tray/gui/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/pype/modules/logging/tray/gui/models.py b/pype/modules/logging/tray/models.py similarity index 100% rename from pype/modules/logging/tray/gui/models.py rename to pype/modules/logging/tray/models.py diff --git a/pype/modules/logging/tray/gui/widgets.py b/pype/modules/logging/tray/widgets.py similarity index 100% rename from pype/modules/logging/tray/gui/widgets.py rename to pype/modules/logging/tray/widgets.py From 8d7c181b547590a4ce4acc02ad38f106732c2141 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 10 Dec 2020 10:38:21 +0100 Subject: [PATCH 23/78] converted ftrack module --- pype/modules/__init__.py | 29 ++++- pype/modules/avalon_apps/avalon_app.py | 6 +- pype/modules/clockify/clockify.py | 18 +++- pype/modules/ftrack/__init__.py | 15 ++- pype/modules/ftrack/ftrack_module.py | 101 ++++++++++++++++++ .../ftrack_server/custom_db_connector.py | 2 +- pype/modules/ftrack/ftrack_server/lib.py | 5 +- pype/modules/ftrack/tray/__init__.py | 7 +- .../tray/{ftrack_module.py => ftrack_tray.py} | 43 ++------ 9 files changed, 176 insertions(+), 50 deletions(-) create mode 100644 pype/modules/ftrack/ftrack_module.py rename pype/modules/ftrack/tray/{ftrack_module.py => ftrack_tray.py} (93%) diff --git a/pype/modules/__init__.py b/pype/modules/__init__.py index d832563ca5..11157f24b1 100644 --- a/pype/modules/__init__.py +++ b/pype/modules/__init__.py @@ -1,5 +1,13 @@ # -*- coding: utf-8 -*- -from .base import PypeModule +from .base import ( + PypeModule, + ITrayModule, + ITrayService, + IPluginPaths, + ModulesManager, + TrayModulesManager +) + from .rest_api import ( RestApiModule, IRestApi @@ -17,13 +25,25 @@ from .timers_manager import ( ITimersManager ) from .avalon_apps import AvalonModule +from .ftrack import ( + FtrackModule, + IFtrackEventHandlerPaths +) from .clockify import ClockifyModule +from .logging import LoggingModule from .muster import MusterModule +from .standalonepublish import StandAlonePublishModule from .websocket_server import WebsocketModule __all__ = ( "PypeModule", + "ITrayModule", + "ITrayService", + "IPluginPaths", + "ModulesManager", + "TrayModulesManager", + "UserModule", "IUserModule", @@ -37,8 +57,15 @@ __all__ = ( "IRestApi", "AvalonModule", + + "FtrackModule", + "IFtrackEventHandlerPaths", + "ClockifyModule", + "IdleManager", + "LoggingModule", "MusterModule", + "StandAlonePublishModule", "WebsocketModule" ) diff --git a/pype/modules/avalon_apps/avalon_app.py b/pype/modules/avalon_apps/avalon_app.py index 48e347399d..b80930a88a 100644 --- a/pype/modules/avalon_apps/avalon_app.py +++ b/pype/modules/avalon_apps/avalon_app.py @@ -4,6 +4,7 @@ from .. import ( PypeModule, ITrayModule, IPluginPaths, + IRestApi ) @@ -41,8 +42,6 @@ class AvalonModule(PypeModule, ITrayModule, IRestApi): self.libraryloader = None self.rest_api_obj = None - def process_modules(self, modules): - if "RestApiServer" in modules: def get_global_environments(self): """Avalon global environments for pype implementation.""" mongodb_data_dir = os.environ.get("AVALON_DB_DATA") @@ -132,6 +131,9 @@ class AvalonModule(PypeModule, ITrayModule, IRestApi): # actions.register_default_actions() actions.register_config_actions() actions.register_environment_actions() + + def rest_api_initialization(self, rest_api_module): + if self.tray_initialized: from .rest_api import AvalonRestApi self.rest_api_obj = AvalonRestApi() diff --git a/pype/modules/clockify/clockify.py b/pype/modules/clockify/clockify.py index 03f357b793..77a30761c9 100644 --- a/pype/modules/clockify/clockify.py +++ b/pype/modules/clockify/clockify.py @@ -10,12 +10,18 @@ from .constants import ( from pype.modules import ( PypeModule, ITrayModule, + IPluginPaths, + IFtrackEventHandlerPaths, + ITimersManager ) class ClockifyModule( PypeModule, ITrayModule, + IPluginPaths, + IFtrackEventHandlerPaths, + ITimersManager ): name = "Clockify" @@ -81,12 +87,20 @@ class ClockifyModule( "actions": [actions_path] } + def get_event_handler_paths(self): + """Implementaton of IFtrackEventHandlerPaths to get plugin paths.""" + return { + "user": [CLOCKIFY_FTRACK_USER_PATH], + "server": [CLOCKIFY_FTRACK_SERVER_PATH] + } def connect_with_modules(self, *_a, **_kw): return def clockify_timer_stopped(self): self.bool_timer_run = False + # Call `ITimersManager` method + self.timer_stopped() def start_timer_check(self): self.bool_thread_check_running = True @@ -144,7 +158,7 @@ class ClockifyModule( "project_name": project_name, "task_type": task_type } - + # Call `ITimersManager` method self.timer_started(data) self.bool_timer_run = bool_timer_run @@ -152,6 +166,7 @@ class ClockifyModule( time.sleep(5) def stop_timer(self): + """Implementation of ITimersManager.""" self.clockapi.finish_time_entry() def signed_in(self): @@ -165,6 +180,7 @@ class ClockifyModule( self.start_timer_manager(self.timer_manager.last_task) def start_timer(self, input_data): + """Implementation of ITimersManager.""" # If not api key is not entered then skip if not self.clockapi.get_api_key(): return diff --git a/pype/modules/ftrack/__init__.py b/pype/modules/ftrack/__init__.py index cd3bee216f..c02b0fca19 100644 --- a/pype/modules/ftrack/__init__.py +++ b/pype/modules/ftrack/__init__.py @@ -1,16 +1,15 @@ -import os - +from .ftrack_module import ( + FtrackModule, + IFtrackEventHandlerPaths +) from . import ftrack_server from .ftrack_server import FtrackServer, check_ftrack_url from .lib import BaseHandler, BaseEvent, BaseAction, ServerAction -from pype.api import get_system_settings - -# TODO: set in ftrack module -os.environ["FTRACK_SERVER"] = ( - get_system_settings()["modules"]["Ftrack"]["ftrack_server"] -) __all__ = ( + "FtrackModule", + "IFtrackEventHandlerPaths", + "ftrack_server", "FtrackServer", "check_ftrack_url", diff --git a/pype/modules/ftrack/ftrack_module.py b/pype/modules/ftrack/ftrack_module.py new file mode 100644 index 0000000000..01a41559d4 --- /dev/null +++ b/pype/modules/ftrack/ftrack_module.py @@ -0,0 +1,101 @@ +import os +from abc import ABCMeta, abstractmethod +import six +import pype +from pype.modules import ( + PypeModule, ITrayModule, IPluginPaths, ITimersManager, IUserModule +) + + +@six.add_metaclass(ABCMeta) +class IFtrackEventHandlerPaths: + """Other modules interface to return paths to ftrack event handlers. + + Expected output is dictionary with "server" and "user" keys. + """ + @abstractmethod + def get_event_handler_paths(self): + pass + + +class FtrackModule( + PypeModule, ITrayModule, IPluginPaths, ITimersManager, IUserModule +): + name = "Ftrack" + def initialize(self, settings): + ftrack_settings = settings[self.name] + + self.enabled = ftrack_settings["enabled"] + self.ftrack_url = ftrack_settings["ftrack_server"] + + # TODO load from settings + self.server_event_handlers_paths = [] + self.user_event_handlers_paths = [] + + # Prepare attribute + self.tray_module = None + + def get_global_environments(self): + """Ftrack's global environments.""" + return { + "FTRACK_SERVER": self.ftrack_url + } + + def get_plugin_paths(self): + """Ftrack plugin paths.""" + return { + "publish": [os.path.join(pype.PLUGINS_DIR, "ftrack", "publish")] + } + + def connect_with_modules(self, enabled_modules): + for module in enabled_modules: + if not isinstance(module, IFtrackEventHandlerPaths): + continue + paths_by_type = module.get_event_handler_paths() or {} + for key, value in paths_by_type.items(): + if not value: + continue + + if key not in ("server", "user"): + self.log.warning( + "Unknown event handlers key \"{}\" skipping.".format( + key + ) + ) + continue + + if not isinstance(value, (list, tuple, set)): + value = [value] + + if key == "server": + self.server_event_handlers_paths.extend(value) + elif key == "user": + self.user_event_handlers_paths.extend(value) + + def start_timer(self, data): + """Implementation of ITimersManager interface.""" + if self.tray_module: + self.tray_module.start_timer_manager(data) + + def stop_timer(self): + """Implementation of ITimersManager interface.""" + if self.tray_module: + self.tray_module.stop_timer_manager() + + def on_pype_user_change(self, username): + """Implementation of IUserModule interface.""" + if self.tray_module: + self.tray_module.changed_user() + + def tray_init(self, tray_widget, main_window): + from .tray import FtrackTrayWrapper + self.tray_module = FtrackTrayWrapper(self, tray_widget, main_window) + + def tray_menu(self, parent_menu): + return self.tray_module.tray_menu(parent_menu) + + def tray_start(self): + return self.tray_module.validate() + + def tray_exit(self): + return self.tray_module.stop_action_server() diff --git a/pype/modules/ftrack/ftrack_server/custom_db_connector.py b/pype/modules/ftrack/ftrack_server/custom_db_connector.py index 8a8ba4ccbb..f435086e8a 100644 --- a/pype/modules/ftrack/ftrack_server/custom_db_connector.py +++ b/pype/modules/ftrack/ftrack_server/custom_db_connector.py @@ -52,11 +52,11 @@ def check_active_collection(func): class CustomDbConnector: log = logging.getLogger(__name__) - timeout = int(os.environ["AVALON_TIMEOUT"]) def __init__( self, uri, database_name, port=None, collection_name=None ): + self.timeout = int(os.environ["AVALON_TIMEOUT"]) self._mongo_client = None self._sentry_client = None self._sentry_logging_handler = None diff --git a/pype/modules/ftrack/ftrack_server/lib.py b/pype/modules/ftrack/ftrack_server/lib.py index 436e15f497..08c77d89a2 100644 --- a/pype/modules/ftrack/ftrack_server/lib.py +++ b/pype/modules/ftrack/ftrack_server/lib.py @@ -127,14 +127,15 @@ class StorerEventHub(SocketBaseEventHub): class ProcessEventHub(SocketBaseEventHub): - hearbeat_msg = b"processor" - uri, port, database, collection_name = get_ftrack_event_mongo_info() is_collection_created = False pypelog = Logger().get_logger("Session Processor") def __init__(self, *args, **kwargs): + self.uri, self.port, self.database, self.collection_name = ( + get_ftrack_event_mongo_info() + ) self.dbcon = CustomDbConnector( self.uri, self.database, diff --git a/pype/modules/ftrack/tray/__init__.py b/pype/modules/ftrack/tray/__init__.py index bca0784c7f..b7159197e1 100644 --- a/pype/modules/ftrack/tray/__init__.py +++ b/pype/modules/ftrack/tray/__init__.py @@ -1,5 +1,6 @@ -from .ftrack_module import FtrackModule +from .ftrack_tray import FtrackTrayWrapper -def tray_init(tray_widget, main_widget): - return FtrackModule(main_widget, tray_widget) +__all__ = ( + "FtrackTrayWrapper", +) diff --git a/pype/modules/ftrack/tray/ftrack_module.py b/pype/modules/ftrack/tray/ftrack_tray.py similarity index 93% rename from pype/modules/ftrack/tray/ftrack_module.py rename to pype/modules/ftrack/tray/ftrack_tray.py index 36ce1eec9f..3e2291ce28 100644 --- a/pype/modules/ftrack/tray/ftrack_module.py +++ b/pype/modules/ftrack/tray/ftrack_tray.py @@ -10,15 +10,17 @@ from ..ftrack_server import socket_thread from ..lib import credentials from . import login_dialog -from pype.api import Logger, resources, get_system_settings +from pype.api import Logger, resources log = Logger().get_logger("FtrackModule", "ftrack") -class FtrackModule: - def __init__(self, main_parent=None, parent=None): - self.parent = parent +class FtrackTrayWrapper: + def __init__(self, module, tray_widget, main_window): + self.module = module + self.tray_widget = tray_widget + self.parent = main_window self.thread_action_server = None self.thread_socket_server = None @@ -29,13 +31,12 @@ class FtrackModule: self.bool_action_thread_running = False self.bool_timer_event = False - self.load_ftrack_url() - self.widget_login = login_dialog.CredentialsDialog() self.widget_login.login_changed.connect(self.on_login_change) self.widget_login.logout_signal.connect(self.on_logout) self.action_credentials = None + self.tray_server_menu = None self.icon_logged = QtGui.QIcon( resources.get_resource("icons", "circle_green.png") ) @@ -118,7 +119,8 @@ class FtrackModule: self.bool_action_server_running = True self.bool_action_thread_running = False - ftrack_url = os.environ['FTRACK_SERVER'] + ftrack_url = self.module.ftrack_url + os.environ["FTRACK_SERVER"] = ftrack_url parent_file_path = os.path.dirname( os.path.dirname(os.path.realpath(__file__)) @@ -294,15 +296,6 @@ class FtrackModule: def tray_exit(self): self.stop_action_server() - def load_ftrack_url(self): - ftrack_url = ( - get_system_settings() - ["modules"] - ["Ftrack"] - ["ftrack_server"] - ) - os.environ["FTRACK_SERVER"] = ftrack_url - # Definition of visibility of each menu actions def set_menu_visibility(self): self.tray_server_menu.menuAction().setVisible(self.bool_logged) @@ -348,18 +341,6 @@ class FtrackModule: credentials.set_env() self.validate() - def process_modules(self, modules): - if 'TimersManager' in modules: - self.timer_manager = modules['TimersManager'] - self.timer_manager.add_module(self) - - if "UserModule" in modules: - credentials.USER_GETTER = modules["UserModule"].get_user - modules["UserModule"].register_callback_on_user_change( - self.changed_user - ) - - def start_timer_manager(self, data): if self.thread_timer is not None: self.thread_timer.ftrack_start_timer(data) @@ -369,12 +350,10 @@ class FtrackModule: self.thread_timer.ftrack_stop_timer() def timer_started(self, data): - if hasattr(self, 'timer_manager'): - self.timer_manager.start_timers(data) + self.module.timer_started(data) def timer_stopped(self): - if hasattr(self, 'timer_manager'): - self.timer_manager.stop_timers() + self.module.timer_stopped() class FtrackEventsThread(QtCore.QThread): From bd059db9b63a4cc42af2777dc5bdfe3ae4b40428 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 10 Dec 2020 10:38:33 +0100 Subject: [PATCH 24/78] removed previous modules_manager.py --- pype/modules_manager.py | 102 ---------------------------------------- 1 file changed, 102 deletions(-) delete mode 100644 pype/modules_manager.py diff --git a/pype/modules_manager.py b/pype/modules_manager.py deleted file mode 100644 index 72023500e4..0000000000 --- a/pype/modules_manager.py +++ /dev/null @@ -1,102 +0,0 @@ -import os -import inspect - -import pype.modules -from pype.modules import PypeModule -from pype.settings import get_system_settings -from pype.api import Logger - - -class PypeModuleManager: - skip_module_names = ("__pycache__", ) - - def __init__(self): - self.log = Logger().get_logger( - "{}.{}".format(__name__, self.__class__.__name__) - ) - - self.pype_modules = self.find_pype_modules() - - def modules_environments(self): - environments = {} - for pype_module in self.pype_modules.values(): - environments.update(pype_module.startup_environments()) - return environments - - def find_pype_modules(self): - settings = get_system_settings() - modules = [] - dirpath = os.path.dirname(pype.modules.__file__) - for module_name in os.listdir(dirpath): - # Check if path lead to a folder - full_path = os.path.join(dirpath, module_name) - if not os.path.isdir(full_path): - continue - - # Skip known invalid names - if module_name in self.skip_module_names: - continue - - import_name = "pype.modules.{}".format(module_name) - try: - modules.append( - __import__(import_name, fromlist=[""]) - ) - - except Exception: - self.log.warning( - "Couldn't import {}".format(import_name), exc_info=True - ) - - pype_module_classes = [] - for module in modules: - try: - pype_module_classes.extend( - self._classes_from_module(PypeModule, module) - ) - except Exception: - self.log.warning( - "Couldn't import {}".format(import_name), exc_info=True - ) - - pype_modules = {} - for pype_module_class in pype_module_classes: - try: - pype_module = pype_module_class(settings) - if pype_module.enabled: - pype_modules[pype_module.id] = pype_module - except Exception: - self.log.warning( - "Couldn't create instance of {}".format( - pype_module_class.__class__.__name__ - ), - exc_info=True - ) - return pype_modules - - def _classes_from_module(self, superclass, module): - classes = list() - - def recursive_bases(klass): - output = [] - output.extend(klass.__bases__) - for base in klass.__bases__: - output.extend(recursive_bases(base)) - return output - - for name in dir(module): - # It could be anything at this point - obj = getattr(module, name) - - if not inspect.isclass(obj) or not len(obj.__bases__) > 0: - continue - - # Use string comparison rather than `issubclass` - # in order to support reloading of this module. - bases = recursive_bases(obj) - if not any(base.__name__ == superclass.__name__ for base in bases): - continue - - classes.append(obj) - - return classes From fb6c04cba247d866e28b02c9173c74a52dad1811 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 10 Dec 2020 10:39:07 +0100 Subject: [PATCH 25/78] pype tray is using TrayManager --- pype/tools/tray/modules_imports.json | 68 ------ pype/tools/tray/pype_tray.py | 326 ++------------------------- 2 files changed, 20 insertions(+), 374 deletions(-) delete mode 100644 pype/tools/tray/modules_imports.json diff --git a/pype/tools/tray/modules_imports.json b/pype/tools/tray/modules_imports.json deleted file mode 100644 index 499e5fc08c..0000000000 --- a/pype/tools/tray/modules_imports.json +++ /dev/null @@ -1,68 +0,0 @@ -[ - { - "title": "User settings", - "type": "module", - "import_path": "pype.modules.user", - "fromlist": ["pype", "modules"] - }, { - "title": "Ftrack", - "type": "module", - "import_path": "pype.modules.ftrack.tray", - "fromlist": ["pype", "modules", "ftrack"] - }, { - "title": "Muster", - "type": "module", - "import_path": "pype.modules.muster", - "fromlist": ["pype", "modules"] - }, { - "title": "Avalon", - "type": "module", - "import_path": "pype.modules.avalon_apps", - "fromlist": ["pype", "modules"] - }, { - "title": "Clockify", - "type": "module", - "import_path": "pype.modules.clockify", - "fromlist": ["pype", "modules"] - }, { - "title": "Standalone Publish", - "type": "module", - "import_path": "pype.modules.standalonepublish", - "fromlist": ["pype", "modules"] - }, { - "title": "Logging", - "type": "module", - "import_path": "pype.modules.logging.tray", - "fromlist": ["pype", "modules", "logging"] - }, { - "title": "Idle Manager", - "type": "module", - "import_path": "pype.modules.idle_manager", - "fromlist": ["pype","modules"] - }, { - "title": "Timers Manager", - "type": "module", - "import_path": "pype.modules.timers_manager", - "fromlist": ["pype","modules"] - }, { - "title": "Rest Api", - "type": "module", - "import_path": "pype.modules.rest_api", - "fromlist": ["pype","modules"] - }, { - "title": "Adobe Communicator", - "type": "module", - "import_path": "pype.modules.adobe_communicator", - "fromlist": ["pype", "modules"] - }, { - "title": "Websocket Server", - "type": "module", - "import_path": "pype.modules.websocket_server", - "fromlist": ["pype", "modules"] - }, { - "title": "Sync Server", - "type": "module", - "import_path": "pype.modules.sync_server", - "fromlist": ["pype","modules"] - } -] diff --git a/pype/tools/tray/pype_tray.py b/pype/tools/tray/pype_tray.py index 75c62592e3..c931e819a8 100644 --- a/pype/tools/tray/pype_tray.py +++ b/pype/tools/tray/pype_tray.py @@ -5,7 +5,8 @@ import platform from avalon import style from Qt import QtCore, QtGui, QtWidgets, QtSvg from pype.api import Logger, resources -from pype.settings.lib import get_system_settings, load_json_file +from pype.modules import TrayModulesManager, ITrayService +from pype.settings.lib import get_system_settings import pype.version try: import configparser @@ -26,89 +27,38 @@ class TrayManager: self.log = Logger().get_logger(self.__class__.__name__) - self.modules = {} - self.services = {} - self.services_submenu = None + self.module_settings = get_system_settings()["modules"] + + self.modules_manager = TrayModulesManager() self.errors = [] - CURRENT_DIR = os.path.dirname(__file__) - self.modules_imports = load_json_file( - os.path.join(CURRENT_DIR, "modules_imports.json") - ) - module_settings = get_system_settings()["modules"] - self.module_settings = module_settings + def initialize_modules(self): + """Add modules to tray.""" - self.icon_run = QtGui.QIcon( - resources.get_resource("icons", "circle_green.png") - ) - self.icon_stay = QtGui.QIcon( - resources.get_resource("icons", "circle_orange.png") - ) - self.icon_failed = QtGui.QIcon( - resources.get_resource("icons", "circle_red.png") - ) + self.modules_manager.initialize(self.tray_widget, self.main_window) - def process_presets(self): - """Add modules to tray by presets. - - This is start up method for TrayManager. Loads presets and import - modules described in "menu_items.json". In `item_usage` key you can - specify by item's title or import path if you want to import it. - Example of "menu_items.json" file: - { - "item_usage": { - "Statics Server": false - } - } - In this case `Statics Server` won't be used. - """ - - items = [] - # Get booleans is module should be used - for item in self.modules_imports: - import_path = item.get("import_path") - title = item.get("title") - - module_data = self.module_settings.get(title) - if not module_data: - if not title: - title = import_path - self.log.warning("{} - Module data not found".format(title)) - continue - - enabled = module_data.pop("enabled", True) - if not enabled: - self.log.debug("{} - Module is disabled".format(title)) - continue - - item["attributes"] = module_data - items.append(item) - - if items: - self.process_items(items, self.tray_widget.menu) + self.modules_manager.tray_menu(self.tray_widget.menu) # Add services if they are - if self.services_submenu is not None: - self.tray_widget.menu.addMenu(self.services_submenu) + services_submenu = ITrayService.services_submenu() + if services_submenu is not None: + self.tray_widget.menu.addMenu(services_submenu) # Add separator - if items and self.services_submenu is not None: - self.add_separator(self.tray_widget.menu) + self.tray_widget.menu.addSeparator() self._add_version_item() # Add Exit action to menu - aExit = QtWidgets.QAction("&Exit", self.tray_widget) - aExit.triggered.connect(self.tray_widget.exit) - self.tray_widget.menu.addAction(aExit) + exit_action = QtWidgets.QAction("Exit", self.tray_widget) + exit_action.triggered.connect(self.tray_widget.exit) + self.tray_widget.menu.addAction(exit_action) # Tell each module which modules were imported - self.connect_modules() - self.start_modules() + self.modules_manager.start_modules() def _add_version_item(self): - subversion = os.environ.get("PYPE_SUBVERSION") client_name = os.environ.get("PYPE_CLIENT") @@ -121,246 +71,10 @@ class TrayManager: version_action = QtWidgets.QAction(version_string, self.tray_widget) self.tray_widget.menu.addAction(version_action) - self.add_separator(self.tray_widget.menu) - - def process_items(self, items, parent_menu): - """ Loop through items and add them to parent_menu. - - :param items: contains dictionary objects representing each item - :type items: list - :param parent_menu: menu where items will be add - :type parent_menu: QtWidgets.QMenu - """ - for item in items: - i_type = item.get('type', None) - result = False - if i_type is None: - continue - elif i_type == 'module': - result = self.add_module(item, parent_menu) - elif i_type == 'action': - result = self.add_action(item, parent_menu) - elif i_type == 'menu': - result = self.add_menu(item, parent_menu) - elif i_type == 'separator': - result = self.add_separator(parent_menu) - - if result is False: - self.errors.append(item) - - def add_module(self, item, parent_menu): - """Inicialize object of module and add it to context. - - :param item: item from presets containing information about module - :type item: dict - :param parent_menu: menu where module's submenus/actions will be add - :type parent_menu: QtWidgets.QMenu - :returns: success of module implementation - :rtype: bool - - REQUIRED KEYS (item): - :import_path (*str*): - - full import path as python's import - - e.g. *"path.to.module"* - :fromlist (*list*): - - subparts of import_path (as from is used) - - e.g. *["path", "to"]* - OPTIONAL KEYS (item): - :title (*str*): - - represents label shown in services menu - - import_path is used if title is not set - - title is not used at all if module is not a service - - .. note:: - Module is added as **service** if object does not have - *tray_menu* method. - """ - import_path = item.get('import_path', None) - title = item.get('title', import_path) - fromlist = item.get('fromlist', []) - attributes = item.get("attributes", {}) - try: - module = __import__( - "{}".format(import_path), - fromlist=fromlist - ) - klass = getattr(module, "CLASS_DEFINIION", None) - if not klass and attributes: - self.log.debug(( - "There are defined attributes for module \"{}\" but" - "module does not have defined \"CLASS_DEFINIION\"." - ).format(import_path)) - - elif klass and attributes: - for key, value in attributes.items(): - if hasattr(klass, key): - setattr(klass, key, value) - else: - self.log.error(( - "Module \"{}\" does not have attribute \"{}\"." - " Check your settings please." - ).format(import_path, key)) - obj = module.tray_init(self.tray_widget, self.main_window) - name = obj.__class__.__name__ - if hasattr(obj, 'tray_menu'): - obj.tray_menu(parent_menu) - else: - if self.services_submenu is None: - self.services_submenu = QtWidgets.QMenu( - 'Services', self.tray_widget.menu - ) - action = QtWidgets.QAction(title, self.services_submenu) - action.setIcon(self.icon_run) - self.services_submenu.addAction(action) - if hasattr(obj, 'set_qaction'): - obj.set_qaction(action, self.icon_failed) - self.modules[name] = obj - self.log.info("{} - Module imported".format(title)) - except Exception as exc: - if self.services_submenu is None: - self.services_submenu = QtWidgets.QMenu( - 'Services', self.tray_widget.menu - ) - action = QtWidgets.QAction(title, self.services_submenu) - action.setIcon(self.icon_failed) - self.services_submenu.addAction(action) - self.log.warning( - "{} - Module import Error: {}".format(title, str(exc)), - exc_info=True - ) - return False - return True - - def add_action(self, item, parent_menu): - """Adds action to parent_menu. - - :param item: item from presets containing information about action - :type item: dictionary - :param parent_menu: menu where action will be added - :type parent_menu: QtWidgets.QMenu - :returns: success of adding item to parent_menu - :rtype: bool - - REQUIRED KEYS (item): - :title (*str*): - - represents label shown in menu - :sourcetype (*str*): - - type of action *enum["file", "python"]* - :command (*str*): - - filepath to script *(sourcetype=="file")* - - python code as string *(sourcetype=="python")* - OPTIONAL KEYS (item): - :tooltip (*str*): - - will be shown when hover over action - """ - sourcetype = item.get('sourcetype', None) - command = item.get('command', None) - title = item.get('title', '*ERROR*') - tooltip = item.get('tooltip', None) - - if sourcetype not in self.available_sourcetypes: - self.log.error('item "{}" has invalid sourcetype'.format(title)) - return False - if command is None or command.strip() == '': - self.log.error('item "{}" has invalid command'.format(title)) - return False - - new_action = QtWidgets.QAction(title, parent_menu) - if tooltip is not None and tooltip.strip() != '': - new_action.setToolTip(tooltip) - - if sourcetype == 'python': - new_action.triggered.connect( - lambda: exec(command) - ) - elif sourcetype == 'file': - command = os.path.normpath(command) - if '$' in command: - command_items = command.split(os.path.sep) - for i in range(len(command_items)): - if command_items[i].startswith('$'): - # TODO: raise error if environment was not found? - command_items[i] = os.environ.get( - command_items[i].replace('$', ''), command_items[i] - ) - command = os.path.sep.join(command_items) - - new_action.triggered.connect( - lambda: exec(open(command).read(), globals()) - ) - - parent_menu.addAction(new_action) - - def add_menu(self, item, parent_menu): - """ Adds submenu to parent_menu. - - :param item: item from presets containing information about menu - :type item: dictionary - :param parent_menu: menu where submenu will be added - :type parent_menu: QtWidgets.QMenu - :returns: success of adding item to parent_menu - :rtype: bool - - REQUIRED KEYS (item): - :title (*str*): - - represents label shown in menu - :items (*list*): - - list of submenus / actions / separators / modules *(dict)* - """ - try: - title = item.get('title', None) - if title is None or title.strip() == '': - self.log.error('Missing title in menu from presets') - return False - new_menu = QtWidgets.QMenu(title, parent_menu) - new_menu.setProperty('submenu', 'on') - parent_menu.addMenu(new_menu) - - self.process_items(item.get('items', []), new_menu) - return True - except Exception: - return False - - def add_separator(self, parent_menu): - """ Adds separator to parent_menu. - - :param parent_menu: menu where submenu will be added - :type parent_menu: QtWidgets.QMenu - :returns: success of adding item to parent_menu - :rtype: bool - """ - try: - parent_menu.addSeparator() - return True - except Exception: - return False - - def connect_modules(self): - """Sends all imported modules to imported modules - which have process_modules method. - """ - for obj in self.modules.values(): - if hasattr(obj, 'process_modules'): - obj.process_modules(self.modules) - - def start_modules(self): - """Modules which can be modified by another modules and - must be launched after *connect_modules* should have tray_start - to start their process afterwards. (e.g. Ftrack actions) - """ - for obj in self.modules.values(): - if hasattr(obj, 'tray_start'): - obj.tray_start() + self.tray_widget.menu.addSeparator() def on_exit(self): - for obj in self.modules.values(): - if hasattr(obj, 'tray_exit'): - try: - obj.tray_exit() - except Exception: - self.log.error("Failed to exit module {}".format( - obj.__class__.__name__ - )) + self.modules_manager.on_exit() class SystemTrayIcon(QtWidgets.QSystemTrayIcon): @@ -384,7 +98,7 @@ class SystemTrayIcon(QtWidgets.QSystemTrayIcon): # Set modules self.tray_man = TrayManager(self, self.parent) - self.tray_man.process_presets() + self.tray_man.initialize_modules() # Catch activate event self.activated.connect(self.on_systray_activated) From b9ac206f371ee87ab3d9dda6826865d648be303c Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 10 Dec 2020 10:40:13 +0100 Subject: [PATCH 26/78] get_default_components in pype.lib returns `PYPE_MONGO` instead of `AVALON_MONGO` --- pype/lib/mongo.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pype/lib/mongo.py b/pype/lib/mongo.py index d6da6dae83..f950572c6e 100644 --- a/pype/lib/mongo.py +++ b/pype/lib/mongo.py @@ -73,7 +73,7 @@ def compose_url(scheme=None, def get_default_components(): - mongo_url = os.environ.get("AVALON_MONGO") + mongo_url = os.environ.get("PYPE_MONGO") if mongo_url is None: raise MongoEnvNotSet( "URL for Mongo logging connection is not set." From 366e5e8900679167055ba656418122712aefd2cb Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 10 Dec 2020 10:49:54 +0100 Subject: [PATCH 27/78] do not skip all actions discover only skip apps if project not set --- pype/tools/launcher/models.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/pype/tools/launcher/models.py b/pype/tools/launcher/models.py index 3e869f3e4a..ad5dc3bccd 100644 --- a/pype/tools/launcher/models.py +++ b/pype/tools/launcher/models.py @@ -129,10 +129,6 @@ class ActionModel(QtGui.QStandardItemModel): def discover(self): """Set up Actions cache. Run this for each new project.""" - if not self.dbcon.Session.get("AVALON_PROJECT"): - self._registered_actions = list() - return - # Discover all registered actions actions = api.discover(api.Action) @@ -144,6 +140,9 @@ class ActionModel(QtGui.QStandardItemModel): def get_application_actions(self): actions = [] + if not self.dbcon.Session.get("AVALON_PROJECT"): + return actions + project_doc = self.dbcon.find_one({"type": "project"}) if not project_doc: return actions From 7da2e7568e289a43fcece8eb236a245247740598 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 10 Dec 2020 10:50:04 +0100 Subject: [PATCH 28/78] triggere discover on refresh --- pype/tools/launcher/models.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pype/tools/launcher/models.py b/pype/tools/launcher/models.py index ad5dc3bccd..c38e809c74 100644 --- a/pype/tools/launcher/models.py +++ b/pype/tools/launcher/models.py @@ -184,6 +184,8 @@ class ActionModel(QtGui.QStandardItemModel): self._groups.clear() + self.discover() + actions = self.filter_compatible_actions(self._registered_actions) self.beginResetModel() From 102f8bcf59552df21a61b739f770af4cbe614fee Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 10 Dec 2020 10:50:35 +0100 Subject: [PATCH 29/78] moved refreshed signal connection to initialization --- pype/tools/launcher/window.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/pype/tools/launcher/window.py b/pype/tools/launcher/window.py index 55635e2139..ac4558df8b 100644 --- a/pype/tools/launcher/window.py +++ b/pype/tools/launcher/window.py @@ -186,6 +186,7 @@ class AssetsPanel(QtWidgets.QWidget): # signals project_bar.project_changed.connect(self.on_project_changed) assets_widget.selection_changed.connect(self.on_asset_changed) + assets_widget.refreshed.connect(self.on_asset_changed) btn_back.clicked.connect(self.back_clicked) # Force initial refresh for the assets since we might not be @@ -206,9 +207,6 @@ class AssetsPanel(QtWidgets.QWidget): self.dbcon.Session["AVALON_PROJECT"] = project_name self.assets_widget.refresh() - # Force asset change callback to ensure tasks are correctly reset - self.assets_widget.refreshed.connect(self.on_asset_changed) - def on_asset_changed(self): """Callback on asset selection changed From 6313d4c8393e70994585fb87d8ca126f76421cf9 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 10 Dec 2020 11:56:22 +0100 Subject: [PATCH 30/78] do not set PYPE_REST_API_URL and PYPE_STATICS_SERVER as global environments --- pype/modules/rest_api/rest_api.py | 32 ++++++++++--------------------- 1 file changed, 10 insertions(+), 22 deletions(-) diff --git a/pype/modules/rest_api/rest_api.py b/pype/modules/rest_api/rest_api.py index 8cc6a213df..08b7032b82 100644 --- a/pype/modules/rest_api/rest_api.py +++ b/pype/modules/rest_api/rest_api.py @@ -1,3 +1,4 @@ +import os import socket import threading from abc import ABCMeta, abstractmethod @@ -104,24 +105,10 @@ class RestApiModule(PypeModule, ITrayService): self.default_port = rest_api_settings["default_port"] self.exclude_ports = rest_api_settings["exclude_ports"] - self._thread_initialized = False - self.rest_api_url = None self.rest_api_thread = None self.resources_url = None - def initialize_thread(self): - if self._thread_initialized: - return - - port = self.find_port() - self.rest_api_url = "http://localhost:{}".format(port) - self.rest_api_thread = RestApiThread(self, port) - self.register_statics("/res", resources.RESOURCES_DIR) - self.resources_url = "{}/res".format(self.rest_api_url) - - self._thread_initialized = True - def register_callback( self, path, callback, url_prefix="", methods=[], strict_match=False ): @@ -160,15 +147,16 @@ class RestApiModule(PypeModule, ITrayService): return None return found_port - def get_global_environments(self): - self.initialize_thread() - return { - "PYPE_REST_API_URL": self.rest_api_url, - "PYPE_STATICS_SERVER": self.resources_url - } - def tray_init(self, *_a, **_kw): - self.initialize_thread() + port = self.find_port() + self.rest_api_url = "http://localhost:{}".format(port) + self.rest_api_thread = RestApiThread(self, port) + self.register_statics("/res", resources.RESOURCES_DIR) + self.resources_url = "{}/res".format(self.rest_api_url) + + # Set rest api environments + os.environ["PYPE_REST_API_URL"] = self.rest_api_url + os.environ["PYPE_STATICS_SERVER"] = self.resources_url def tray_start(self): RestApiFactory.prepare_registered() From 314b424bb652d24ca5d80a4dd4505ac46f87a44c Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 10 Dec 2020 11:56:34 +0100 Subject: [PATCH 31/78] add timer manager label --- pype/modules/timers_manager/timers_manager.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pype/modules/timers_manager/timers_manager.py b/pype/modules/timers_manager/timers_manager.py index e31342b9b0..1d135ab9ef 100644 --- a/pype/modules/timers_manager/timers_manager.py +++ b/pype/modules/timers_manager/timers_manager.py @@ -36,6 +36,7 @@ class TimersManager(PypeModule, ITrayService, IIdleManager): when user idles for a long time (set in presets). """ name = "Timers Manager" + label = "Timers Service" def initialize(self, modules_settings): timers_settings = modules_settings[self.name] From 7310986492b064bb4a8731dccac4341c32bdc13b Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 10 Dec 2020 11:56:58 +0100 Subject: [PATCH 32/78] shortened Enabled/Disabled --- pype/modules/base.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pype/modules/base.py b/pype/modules/base.py index d4b1508699..25426f4df3 100644 --- a/pype/modules/base.py +++ b/pype/modules/base.py @@ -251,9 +251,9 @@ class ModulesManager: try: module = modules_item(self, modules_settings) self.modules[module.id] = module - enabled_str = " ENABLED" + enabled_str = "X" if not module.enabled: - enabled_str = "DISABLED" + enabled_str = " " self.log.debug("[{}] {}".format(enabled_str, name)) except Exception: From f3e7e27826b9370632b3a25c712f44f39d713428 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 10 Dec 2020 12:09:40 +0100 Subject: [PATCH 33/78] wrapper return wrapped function --- pype/lib/avalon_context.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pype/lib/avalon_context.py b/pype/lib/avalon_context.py index 9a9698cac1..d88878a41c 100644 --- a/pype/lib/avalon_context.py +++ b/pype/lib/avalon_context.py @@ -16,6 +16,7 @@ def with_avalon(func): def wrap_avalon(*args, **kwargs): from avalon import api, io, pipeline # noqa: F401 return func(*args, **kwargs) + return wrap_avalon @with_avalon From 80d53627d5d547115978185fb772de73fb8621bf Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 10 Dec 2020 12:25:22 +0100 Subject: [PATCH 34/78] fixed avalon import in avalon_context lib functions --- pype/lib/avalon_context.py | 50 ++++++++++++++++++++++---------------- 1 file changed, 29 insertions(+), 21 deletions(-) diff --git a/pype/lib/avalon_context.py b/pype/lib/avalon_context.py index d88878a41c..3a18e956d9 100644 --- a/pype/lib/avalon_context.py +++ b/pype/lib/avalon_context.py @@ -7,6 +7,9 @@ import functools from pype.settings import get_project_settings +# avalon module is not imported at the top +# - may not be in path at the time of pype.lib initialization +avalon = None log = logging.getLogger("AvalonContext") @@ -14,7 +17,9 @@ log = logging.getLogger("AvalonContext") def with_avalon(func): @functools.wraps(func) def wrap_avalon(*args, **kwargs): - from avalon import api, io, pipeline # noqa: F401 + global avalon + if avalon is None: + import avalon return func(*args, **kwargs) return wrap_avalon @@ -31,12 +36,12 @@ def is_latest(representation): """ - version = io.find_one({"_id": representation['parent']}) + version = avalon.io.find_one({"_id": representation['parent']}) if version["type"] == "master_version": return True # Get highest version under the parent - highest_version = io.find_one({ + highest_version = avalon.io.find_one({ "type": "version", "parent": version["parent"] }, sort=[("name", -1)], projection={"name": True}) @@ -58,9 +63,9 @@ def any_outdated(): if representation in checked: continue - representation_doc = io.find_one( + representation_doc = avalon.io.find_one( { - "_id": io.ObjectId(representation), + "_id": avalon.io.ObjectId(representation), "type": "representation" }, projection={"parent": True} @@ -91,7 +96,7 @@ def get_asset(asset_name=None): if not asset_name: asset_name = avalon.api.Session["AVALON_ASSET"] - asset_document = io.find_one({ + asset_document = avalon.io.find_one({ "name": asset_name, "type": "asset" }) @@ -115,9 +120,12 @@ def get_hierarchy(asset_name=None): """ if not asset_name: - asset_name = io.Session.get("AVALON_ASSET", os.environ["AVALON_ASSET"]) + asset_name = avalon.io.Session.get( + "AVALON_ASSET", + os.environ["AVALON_ASSET"] + ) - asset_entity = io.find_one({ + asset_entity = avalon.io.find_one({ "type": 'asset', "name": asset_name }) @@ -136,13 +144,13 @@ def get_hierarchy(asset_name=None): parent_id = entity.get("data", {}).get("visualParent") if not parent_id: break - entity = io.find_one({"_id": parent_id}) + entity = avalon.io.find_one({"_id": parent_id}) hierarchy_items.append(entity["name"]) # Add parents to entity data for next query entity_data = asset_entity.get("data", {}) entity_data["parents"] = hierarchy_items - io.update_many( + avalon.io.update_many( {"_id": asset_entity["_id"]}, {"$set": {"data": entity_data}} ) @@ -161,7 +169,7 @@ def get_linked_assets(asset_entity): (list) of MongoDB documents """ inputs = asset_entity["data"].get("inputs", []) - inputs = [io.find_one({"_id": x}) for x in inputs] + inputs = [avalon.io.find_one({"_id": x}) for x in inputs] return inputs @@ -187,9 +195,9 @@ def get_latest_version(asset_name, subset_name, dbcon=None, project_name=None): if not dbcon: log.debug("Using `avalon.io` for query.") - dbcon = io + dbcon = avalon.io # Make sure is installed - io.install() + dbcon.install() if project_name and project_name != dbcon.Session.get("AVALON_PROJECT"): # `avalon.io` has only `_database` attribute @@ -296,8 +304,8 @@ class BuildWorkfile: }] """ # Get current asset name and entity - current_asset_name = io.Session["AVALON_ASSET"] - current_asset_entity = io.find_one({ + current_asset_name = avalon.io.Session["AVALON_ASSET"] + current_asset_entity = avalon.io.find_one({ "type": "asset", "name": current_asset_name }) @@ -325,7 +333,7 @@ class BuildWorkfile: return # Get current task name - current_task_name = io.Session["AVALON_TASK"] + current_task_name = avalon.io.Session["AVALON_TASK"] # Load workfile presets for task self.build_presets = self.get_build_presets(current_task_name) @@ -426,7 +434,7 @@ class BuildWorkfile: (dict): preset per entered task name """ host_name = avalon.api.registered_host().__name__.rsplit(".", 1)[-1] - presets = get_project_settings(io.Session["AVALON_PROJECT"]) + presets = get_project_settings(avalon.io.Session["AVALON_PROJECT"]) # Get presets for host build_presets = ( presets.get(host_name, {}) @@ -766,7 +774,7 @@ class BuildWorkfile: is_loaded = True except Exception as exc: - if exc == pipeline.IncompatibleLoaderError: + if exc == avalon.pipeline.IncompatibleLoaderError: self.log.info(( "Loader `{}` is not compatible with" " representation `{}`" @@ -830,13 +838,13 @@ class BuildWorkfile: asset_entity_by_ids = {asset["_id"]: asset for asset in asset_entities} - subsets = list(io.find({ + subsets = list(avalon.io.find({ "type": "subset", "parent": {"$in": asset_entity_by_ids.keys()} })) subset_entity_by_ids = {subset["_id"]: subset for subset in subsets} - sorted_versions = list(io.find({ + sorted_versions = list(avalon.io.find({ "type": "version", "parent": {"$in": subset_entity_by_ids.keys()} }).sort("name", -1)) @@ -850,7 +858,7 @@ class BuildWorkfile: subset_id_with_latest_version.append(subset_id) last_versions_by_id[version["_id"]] = version - repres = io.find({ + repres = avalon.io.find({ "type": "representation", "parent": {"$in": last_versions_by_id.keys()} }) From 819180a632c71a703a47a6870d4bad90cd324ed5 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 10 Dec 2020 17:01:09 +0100 Subject: [PATCH 35/78] do not set PYBLISHPLUGINPATH in globals anymore --- pype/settings/defaults/system_settings/general.json | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/pype/settings/defaults/system_settings/general.json b/pype/settings/defaults/system_settings/general.json index 4e0358f447..636d86331b 100644 --- a/pype/settings/defaults/system_settings/general.json +++ b/pype/settings/defaults/system_settings/general.json @@ -20,8 +20,7 @@ "PYPE_PROJECT_CONFIGS", "PYPE_PYTHON_EXE", "PYPE_OCIO_CONFIG", - "PYBLISH_GUI", - "PYBLISHPLUGINPATH" + "PYBLISH_GUI" ] }, "FFMPEG_PATH": { @@ -46,9 +45,6 @@ "darwin": "{VIRTUAL_ENV}/bin/python" }, "PYPE_OCIO_CONFIG": "{STUDIO_SOFT}/OpenColorIO-Configs", - "PYBLISH_GUI": "pyblish_pype", - "PYBLISHPLUGINPATH": [ - "{PYPE_MODULE_ROOT}/pype/plugins/ftrack/publish" - ] + "PYBLISH_GUI": "pyblish_pype" } } \ No newline at end of file From 880825a98d16761768719b90c09f7d9120b49b45 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 10 Dec 2020 17:07:27 +0100 Subject: [PATCH 36/78] removed PYPE_CONFIG variable as pype-config repo is not used anymore --- pype.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/pype.py b/pype.py index 7f319f0d32..bb2b671d86 100644 --- a/pype.py +++ b/pype.py @@ -157,8 +157,6 @@ def boot(): # DEPRECATED: remove when `pype-config` dissolves into Pype for good. # .-=-----------------------=-=. ^ .=-=--------------------------=-. - os.environ["PYPE_CONFIG"] = os.path.join( - os.environ["PYPE_ROOT"], "repos", "pype-config") os.environ["PYPE_MODULE_ROOT"] = os.environ["PYPE_ROOT"] # ------------------------------------------------------------------ # HARDCODED: From 035d623142aa779dea8ece04c7bd9a601b23951c Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 10 Dec 2020 17:08:20 +0100 Subject: [PATCH 37/78] implemented `set_modules_environments` to set environments with help of modules manager on pype start --- pype.py | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/pype.py b/pype.py index bb2b671d86..87d45c7514 100644 --- a/pype.py +++ b/pype.py @@ -66,6 +66,56 @@ def set_environments() -> None: os.environ.update(env) +def set_modules_environments(): + """Set global environments for pype's modules. + + This requires to have pype in `sys.path`. + """ + + from pype.modules import ModulesManager, IPluginPaths + + modules_manager = ModulesManager() + + publish_plugin_dirs = [] + module_envs = {} + for module in modules_manager.get_enabled_modules(): + # Collect global module's global environments + _envs = module.get_global_environments() + for key, value in _envs.items(): + if key in module_envs: + # TODO better error message + raise AssertionError( + "Duplicated environment key {}".format(key) + ) + module_envs[key] = value + + # Collect plugin paths for publishing + if isinstance(module, IPluginPaths): + plugins_data = module.get_plugin_paths() or {} + publish_paths = plugins_data.get("publish") or [] + if not isinstance(publish_paths, (tuple, list, set)): + publish_paths = [publish_paths] + publish_plugin_dirs.extend(publish_paths) + + # Set pyblish plugins paths if any module want to register them + if publish_plugin_dirs: + publish_paths_str = os.environ.get("PYBLISHPLUGINPATH") or "" + publish_paths = publish_paths_str.split(os.pathsep) + _publish_paths = set() + for path in publish_paths: + if path: + _publish_paths.add(os.path.normpath(path)) + for path in publish_plugin_dirs: + _publish_paths.add(os.path.normpath(path)) + module_envs["PYBLISHPLUGINPATH"] = os.pathsep.join(_publish_paths) + + # Metge environments with current environments and update values + if module_envs: + env = acre.merge(module_envs, dict(os.environ)) + os.environ.clear() + os.environ.update(env) + + def boot(): """Bootstrap Pype.""" art = r""" From 59578abd0737751c8f5c603498e84a7cb1e31453 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 10 Dec 2020 17:08:38 +0100 Subject: [PATCH 38/78] execute set_modules_environments on start --- pype.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pype.py b/pype.py index 87d45c7514..0f5695f8e4 100644 --- a/pype.py +++ b/pype.py @@ -227,6 +227,7 @@ def boot(): from pype.version import __version__ print(">>> loading environments ...") set_environments() + set_modules_environments() info = get_info() info.insert(0, ">>> Using Pype from [ {} ]".format( From b715c9692bec330418a095e187a5e861a2c2360b Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 10 Dec 2020 17:08:56 +0100 Subject: [PATCH 39/78] removed reference to launchers in general environments --- pype/settings/defaults/system_settings/general.json | 1 - 1 file changed, 1 deletion(-) diff --git a/pype/settings/defaults/system_settings/general.json b/pype/settings/defaults/system_settings/general.json index 636d86331b..ff739cc75f 100644 --- a/pype/settings/defaults/system_settings/general.json +++ b/pype/settings/defaults/system_settings/general.json @@ -29,7 +29,6 @@ "linux": "{VIRTUAL_ENV}/localized/ffmpeg_exec/linux:{PYPE_SETUP_PATH}/vendor/bin/ffmpeg_exec/linux" }, "PATH": [ - "{PYPE_CONFIG}/launchers", "{FFMPEG_PATH}", "{PATH}" ], From a3308259017619d31c6c8e7d2ae9f38dbe16ce05 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 10 Dec 2020 17:09:21 +0100 Subject: [PATCH 40/78] removed avalon environments as avalon module handle them --- pype.py | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/pype.py b/pype.py index 0f5695f8e4..c74a27f685 100644 --- a/pype.py +++ b/pype.py @@ -60,7 +60,7 @@ def set_environments() -> None: """ # FIXME: remove everything except global - env = load_environments(["global", "avalon"]) + env = load_environments(["global"]) env = acre.merge(env, dict(os.environ)) os.environ.clear() os.environ.update(env) @@ -156,10 +156,6 @@ def boot(): else: os.environ["PYPE_MONGO"] = pype_mongo - # FIXME (antirotor): we need to set those in different way - if not os.getenv("AVALON_MONGO"): - os.environ["AVALON_MONGO"] = os.environ["PYPE_MONGO"] - if getattr(sys, 'frozen', False): if not pype_versions: import igniter @@ -208,12 +204,6 @@ def boot(): # DEPRECATED: remove when `pype-config` dissolves into Pype for good. # .-=-----------------------=-=. ^ .=-=--------------------------=-. os.environ["PYPE_MODULE_ROOT"] = os.environ["PYPE_ROOT"] - # ------------------------------------------------------------------ - # HARDCODED: - os.environ["AVALON_DB"] = "avalon" - os.environ["AVALON_LABEL"] = "Pype" - os.environ["AVALON_TIMEOUT"] = "1000" - # .-=-----------------------=-=. v .=-=--------------------------=-. # delete Pype module from cache so it is used from specific version try: From d15e51cbdf8acaf7d57fc3d179e022511ecb049d Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 10 Dec 2020 17:26:07 +0100 Subject: [PATCH 41/78] rest api do not register callbacks if not in tray --- pype/modules/rest_api/rest_api.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/pype/modules/rest_api/rest_api.py b/pype/modules/rest_api/rest_api.py index 08b7032b82..449be5f7ed 100644 --- a/pype/modules/rest_api/rest_api.py +++ b/pype/modules/rest_api/rest_api.py @@ -123,11 +123,13 @@ class RestApiModule(PypeModule, ITrayService): RestApiFactory.register_obj(obj) def connect_with_modules(self, enabled_modules): - for module in enabled_modules: - if not isinstance(module, IRestApi): - continue + # Do not register restapi callbacks out of tray + if self.tray_initialized: + for module in enabled_modules: + if not isinstance(module, IRestApi): + continue - module.rest_api_initialization(self) + module.rest_api_initialization(self) def find_port(self): start_port = self.default_port From aadc5f787142ae4bca969e11fae8beec430df2a4 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 10 Dec 2020 17:27:26 +0100 Subject: [PATCH 42/78] use different ways of storing modules --- pype/modules/base.py | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/pype/modules/base.py b/pype/modules/base.py index 25426f4df3..d5f7500ca6 100644 --- a/pype/modules/base.py +++ b/pype/modules/base.py @@ -219,7 +219,9 @@ class ModulesManager: def __init__(self): self.log = logging.getLogger(self.__class__.__name__) - self.modules = {} + self.modules = [] + self.modules_by_id = {} + self.modules_by_name = {} self.initialize_modules() self.connect_modules() @@ -250,7 +252,9 @@ class ModulesManager: try: module = modules_item(self, modules_settings) - self.modules[module.id] = module + self.modules.append(module) + self.modules_by_id[module.id] = module + self.modules_by_name[module.name] = module enabled_str = "X" if not module.enabled: enabled_str = " " @@ -271,7 +275,7 @@ class ModulesManager: def get_enabled_modules(self): return [ module - for module in self.modules.values() + for module in self.modules if module.enabled ] @@ -280,7 +284,9 @@ class TrayModulesManager(ModulesManager): def __init__(self): self.log = PypeLogger().get_logger(self.__class__.__name__) - self.modules = {} + self.modules = [] + self.modules_by_id = {} + self.modules_by_name = {} def initialize(self, tray_widget, main_window): self.tray_widget = tray_widget @@ -292,7 +298,7 @@ class TrayModulesManager(ModulesManager): def get_enabled_tray_modules(self): output = [] - for module in self.modules.values(): + for module in self.modules: if module.enabled and isinstance(module, ITrayModule): output.append(module) return output From 81205c1ba04feeee0a160f1134d1f73614e95364 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 10 Dec 2020 17:38:04 +0100 Subject: [PATCH 43/78] use resources for getting icon --- pype/modules/avalon_apps/avalon_app.py | 4 +++- pype/modules/clockify/widgets.py | 2 +- pype/modules/ftrack/tray/login_dialog.py | 2 +- pype/modules/ftrack/tray/login_tools.py | 2 +- pype/modules/muster/widget_login.py | 2 +- pype/modules/timers_manager/widget_user_idle.py | 5 ++++- pype/modules/user/widget_user.py | 2 +- 7 files changed, 12 insertions(+), 7 deletions(-) diff --git a/pype/modules/avalon_apps/avalon_app.py b/pype/modules/avalon_apps/avalon_app.py index b80930a88a..816c3f6f67 100644 --- a/pype/modules/avalon_apps/avalon_app.py +++ b/pype/modules/avalon_apps/avalon_app.py @@ -1,5 +1,6 @@ import os import pype +from pype import resources from .. import ( PypeModule, ITrayModule, @@ -83,9 +84,10 @@ class AvalonModule(PypeModule, ITrayModule, IRestApi): try: from avalon.tools.libraryloader import app from avalon import style + from Qt import QtGui self.libraryloader = app.Window( - icon=tray_widget.icon, + icon=QtGui.QIcon(resources.pype_icon_filepath()), show_projects=True, show_libraries=True ) diff --git a/pype/modules/clockify/widgets.py b/pype/modules/clockify/widgets.py index 74b2dede66..85120b4794 100644 --- a/pype/modules/clockify/widgets.py +++ b/pype/modules/clockify/widgets.py @@ -1,6 +1,6 @@ from Qt import QtCore, QtGui, QtWidgets from avalon import style -from pype.api import resources +from pype import resources class MessageWidget(QtWidgets.QWidget): diff --git a/pype/modules/ftrack/tray/login_dialog.py b/pype/modules/ftrack/tray/login_dialog.py index 6c7373e337..a49010effc 100644 --- a/pype/modules/ftrack/tray/login_dialog.py +++ b/pype/modules/ftrack/tray/login_dialog.py @@ -3,7 +3,7 @@ import requests from avalon import style from pype.modules.ftrack.lib import credentials from . import login_tools -from pype.api import resources +from pype import resources from Qt import QtCore, QtGui, QtWidgets diff --git a/pype/modules/ftrack/tray/login_tools.py b/pype/modules/ftrack/tray/login_tools.py index d3297eaa76..328ce49f5c 100644 --- a/pype/modules/ftrack/tray/login_tools.py +++ b/pype/modules/ftrack/tray/login_tools.py @@ -3,7 +3,7 @@ from urllib import parse import webbrowser import functools import threading -from pype.api import resources +from pype import resources class LoginServerHandler(BaseHTTPRequestHandler): diff --git a/pype/modules/muster/widget_login.py b/pype/modules/muster/widget_login.py index af10f8df83..0fd1913d0c 100644 --- a/pype/modules/muster/widget_login.py +++ b/pype/modules/muster/widget_login.py @@ -1,7 +1,7 @@ import os from Qt import QtCore, QtGui, QtWidgets from avalon import style -from pype.api import resources +from pype import resources class MusterLogin(QtWidgets.QWidget): diff --git a/pype/modules/timers_manager/widget_user_idle.py b/pype/modules/timers_manager/widget_user_idle.py index 22455846fd..9edf5bc976 100644 --- a/pype/modules/timers_manager/widget_user_idle.py +++ b/pype/modules/timers_manager/widget_user_idle.py @@ -1,5 +1,6 @@ from avalon import style from Qt import QtCore, QtGui, QtWidgets +from pype import resources class WidgetUserIdle(QtWidgets.QWidget): @@ -15,7 +16,9 @@ class WidgetUserIdle(QtWidgets.QWidget): self.bool_not_stopped = True self.module = module - self.setWindowIcon(tray_widget.icon) + + icon = QtGui.QIcon(resources.pype_icon_filepath()) + self.setWindowIcon(icon) self.setWindowFlags( QtCore.Qt.WindowCloseButtonHint | QtCore.Qt.WindowMinimizeButtonHint diff --git a/pype/modules/user/widget_user.py b/pype/modules/user/widget_user.py index ba211c4737..d12cd6175c 100644 --- a/pype/modules/user/widget_user.py +++ b/pype/modules/user/widget_user.py @@ -1,6 +1,6 @@ from Qt import QtCore, QtGui, QtWidgets from avalon import style -from pype.api import resources +from pype import resources class UserWidget(QtWidgets.QWidget): From 073e5b55327f3e7505ebd0f1c0bf6bc499c3640c Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 10 Dec 2020 17:39:04 +0100 Subject: [PATCH 44/78] do not pass any arguments to tray_init method --- pype/modules/avalon_apps/avalon_app.py | 2 +- pype/modules/base.py | 4 ++-- pype/modules/clockify/clockify.py | 2 +- pype/modules/ftrack/ftrack_module.py | 4 ++-- pype/modules/ftrack/tray/ftrack_tray.py | 4 +--- pype/modules/idle_manager/idle_manager.py | 2 +- pype/modules/logging/logging_module.py | 2 +- pype/modules/muster/muster.py | 2 +- pype/modules/rest_api/rest_api.py | 2 +- pype/modules/standalonepublish/standalonepublish_module.py | 6 +++--- pype/modules/timers_manager/timers_manager.py | 4 ++-- pype/modules/timers_manager/widget_user_idle.py | 2 +- pype/modules/user/user_module.py | 2 +- pype/modules/websocket_server/websocket_server.py | 2 +- 14 files changed, 19 insertions(+), 21 deletions(-) diff --git a/pype/modules/avalon_apps/avalon_app.py b/pype/modules/avalon_apps/avalon_app.py index 816c3f6f67..5f1690cf91 100644 --- a/pype/modules/avalon_apps/avalon_app.py +++ b/pype/modules/avalon_apps/avalon_app.py @@ -79,7 +79,7 @@ class AvalonModule(PypeModule, ITrayModule, IRestApi): "AVALON_DB_DATA": mongodb_data_dir } - def tray_init(self, tray_widget, main_window): + def tray_init(self): # Add library tool try: from avalon.tools.libraryloader import app diff --git a/pype/modules/base.py b/pype/modules/base.py index d5f7500ca6..18529f4021 100644 --- a/pype/modules/base.py +++ b/pype/modules/base.py @@ -89,7 +89,7 @@ class ITrayModule: self.tray_initialized = True @abstractmethod - def tray_init(self, tray_widget, main_window): + def tray_init(self): """Initialization part of tray implementation. Triggered between `initialization` and `connect_with_modules`. @@ -293,7 +293,7 @@ class TrayModulesManager(ModulesManager): self.main_window = main_window self.initialize_modules() - self.tray_init(tray_widget, main_window) + self.tray_init() self.connect_modules() def get_enabled_tray_modules(self): diff --git a/pype/modules/clockify/clockify.py b/pype/modules/clockify/clockify.py index 77a30761c9..61eaaa5747 100644 --- a/pype/modules/clockify/clockify.py +++ b/pype/modules/clockify/clockify.py @@ -44,7 +44,7 @@ class ClockifyModule( "CLOCKIFY_WORKSPACE": self.workspace_name } - def tray_init(self, tray_widget, main_window): + def tray_init(self): from .widgets import ClockifySettings, MessageWidget self.MessageWidgetClass = MessageWidget diff --git a/pype/modules/ftrack/ftrack_module.py b/pype/modules/ftrack/ftrack_module.py index 01a41559d4..e610166131 100644 --- a/pype/modules/ftrack/ftrack_module.py +++ b/pype/modules/ftrack/ftrack_module.py @@ -87,9 +87,9 @@ class FtrackModule( if self.tray_module: self.tray_module.changed_user() - def tray_init(self, tray_widget, main_window): + def tray_init(self): from .tray import FtrackTrayWrapper - self.tray_module = FtrackTrayWrapper(self, tray_widget, main_window) + self.tray_module = FtrackTrayWrapper(self) def tray_menu(self, parent_menu): return self.tray_module.tray_menu(parent_menu) diff --git a/pype/modules/ftrack/tray/ftrack_tray.py b/pype/modules/ftrack/tray/ftrack_tray.py index 3e2291ce28..56133208c2 100644 --- a/pype/modules/ftrack/tray/ftrack_tray.py +++ b/pype/modules/ftrack/tray/ftrack_tray.py @@ -17,10 +17,8 @@ log = Logger().get_logger("FtrackModule", "ftrack") class FtrackTrayWrapper: - def __init__(self, module, tray_widget, main_window): + def __init__(self, module): self.module = module - self.tray_widget = tray_widget - self.parent = main_window self.thread_action_server = None self.thread_socket_server = None diff --git a/pype/modules/idle_manager/idle_manager.py b/pype/modules/idle_manager/idle_manager.py index ca9872b2ed..42b96ec7db 100644 --- a/pype/modules/idle_manager/idle_manager.py +++ b/pype/modules/idle_manager/idle_manager.py @@ -50,7 +50,7 @@ class IdleManager(PypeModule, ITrayService): self.time_callbacks = collections.defaultdict(list) self.idle_thread = None - def tray_init(self, *_a, **_kw): + def tray_init(self): return def tray_start(self): diff --git a/pype/modules/logging/logging_module.py b/pype/modules/logging/logging_module.py index a4400a610a..2aa13cc118 100644 --- a/pype/modules/logging/logging_module.py +++ b/pype/modules/logging/logging_module.py @@ -12,7 +12,7 @@ class LoggingModule(PypeModule, ITrayModule): # Tray attributes self.window = None - def tray_init(self, main_parent, parent): + def tray_init(self): try: from .tray.app import LogsWindow self.window = LogsWindow() diff --git a/pype/modules/muster/muster.py b/pype/modules/muster/muster.py index 5ea02f7541..393a10e29a 100644 --- a/pype/modules/muster/muster.py +++ b/pype/modules/muster/muster.py @@ -34,7 +34,7 @@ class MusterModule(PypeModule, ITrayModule, IRestApi): "MUSTER_REST_URL": self.muster_url } - def tray_init(self, tray_widget, main_window): + def tray_init(self): from .widget_login import MusterLogin self.widget_login = MusterLogin(self) diff --git a/pype/modules/rest_api/rest_api.py b/pype/modules/rest_api/rest_api.py index 449be5f7ed..bd9e7d2a83 100644 --- a/pype/modules/rest_api/rest_api.py +++ b/pype/modules/rest_api/rest_api.py @@ -149,7 +149,7 @@ class RestApiModule(PypeModule, ITrayService): return None return found_port - def tray_init(self, *_a, **_kw): + def tray_init(self): port = self.find_port() self.rest_api_url = "http://localhost:{}".format(port) self.rest_api_thread = RestApiThread(self, port) diff --git a/pype/modules/standalonepublish/standalonepublish_module.py b/pype/modules/standalonepublish/standalonepublish_module.py index ddd58b27d2..7b995ff093 100644 --- a/pype/modules/standalonepublish/standalonepublish_module.py +++ b/pype/modules/standalonepublish/standalonepublish_module.py @@ -17,13 +17,13 @@ class StandAlonePublishModule(PypeModule, ITrayModule): ) ] - def tray_init(self, *_a, **_kw): + def tray_init(self): return - def tray_start(self, *_a, **_kw): + def tray_start(self): return - def tray_exit(self, *_a, **_kw): + def tray_exit(self): return def tray_menu(self, parent_menu): diff --git a/pype/modules/timers_manager/timers_manager.py b/pype/modules/timers_manager/timers_manager.py index 1d135ab9ef..f4f5243f13 100644 --- a/pype/modules/timers_manager/timers_manager.py +++ b/pype/modules/timers_manager/timers_manager.py @@ -60,9 +60,9 @@ class TimersManager(PypeModule, ITrayService, IIdleManager): self.modules = [] - def tray_init(self, tray_widget, main_widget): + def tray_init(self): from .widget_user_idle import WidgetUserIdle, SignalHandler - self.widget_user_idle = WidgetUserIdle(self, tray_widget) + self.widget_user_idle = WidgetUserIdle(self) self.signal_handler = SignalHandler(self) def tray_start(self, *_a, **_kw): diff --git a/pype/modules/timers_manager/widget_user_idle.py b/pype/modules/timers_manager/widget_user_idle.py index 9edf5bc976..5e47cdaddf 100644 --- a/pype/modules/timers_manager/widget_user_idle.py +++ b/pype/modules/timers_manager/widget_user_idle.py @@ -8,7 +8,7 @@ class WidgetUserIdle(QtWidgets.QWidget): SIZE_W = 300 SIZE_H = 160 - def __init__(self, module, tray_widget): + def __init__(self, module): super(WidgetUserIdle, self).__init__() diff --git a/pype/modules/user/user_module.py b/pype/modules/user/user_module.py index d60dfed353..240ede947b 100644 --- a/pype/modules/user/user_module.py +++ b/pype/modules/user/user_module.py @@ -43,7 +43,7 @@ class UserModule(PypeModule, ITrayModule, IRestApi): self.widget_login = None self.action_show_widget = None - def tray_init(self, main_parent=None, parent=None): + def tray_init(self): from .widget_user import UserWidget self.widget_login = UserWidget(self) diff --git a/pype/modules/websocket_server/websocket_server.py b/pype/modules/websocket_server/websocket_server.py index e99265f604..a96d2f249c 100644 --- a/pype/modules/websocket_server/websocket_server.py +++ b/pype/modules/websocket_server/websocket_server.py @@ -35,7 +35,7 @@ class WebsocketModule(PypeModule, ITrayService): def connect_with_modules(self, *_a, **kw): return - def tray_init(self, *_a, **kw): + def tray_init(self): self.websocket_server = WebSocketServer() self.websocket_server.on_stop_callbacks.append( self.set_service_failed From 0abe0ab90243faa6527d17e4ac521ccdc5f75e38 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 10 Dec 2020 17:39:26 +0100 Subject: [PATCH 45/78] removed deprecated tray_init from StandAlonePublishModule's __init__.py --- pype/modules/standalonepublish/__init__.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pype/modules/standalonepublish/__init__.py b/pype/modules/standalonepublish/__init__.py index 4038b696d9..5c40deb6f0 100644 --- a/pype/modules/standalonepublish/__init__.py +++ b/pype/modules/standalonepublish/__init__.py @@ -1,5 +1,5 @@ from .standalonepublish_module import StandAlonePublishModule - -def tray_init(tray_widget, main_widget): - return StandAlonePublishModule(main_widget, tray_widget) +__all__ = ( + "StandAlonePublishModule", +) From acdb503536ca3ba94772af77993f98ae4fe9e54f Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 10 Dec 2020 17:40:06 +0100 Subject: [PATCH 46/78] let tray_manager set `tray_initialized` attribute --- pype/modules/base.py | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/pype/modules/base.py b/pype/modules/base.py index 18529f4021..a320fd797e 100644 --- a/pype/modules/base.py +++ b/pype/modules/base.py @@ -78,16 +78,6 @@ class ITrayModule: """ tray_initialized = False - def do_tray_init(self, *args, **kwargs): - """Method called by Tray manager. - - Point is to set `tray_initialized` to True after process. - - TODO try to handle this with decorator on `tray_init`. - """ - self.tray_init(*args, **kwargs) - self.tray_initialized = True - @abstractmethod def tray_init(self): """Initialization part of tray implementation. @@ -303,9 +293,10 @@ class TrayModulesManager(ModulesManager): output.append(module) return output - def tray_init(self, *args, **kwargs): + def tray_init(self): for module in self.get_enabled_tray_modules(): - module.do_tray_init(*args, **kwargs) + module.tray_init() + module.tray_initialized = True def tray_menu(self, tray_menu): for module in self.get_enabled_tray_modules(): From bcb01729310f194ab2f4f85e26c55ac67337e8d0 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 10 Dec 2020 17:42:27 +0100 Subject: [PATCH 47/78] tray just pass menu to tray manager initialize --- pype/modules/base.py | 6 ++---- pype/tools/tray/pype_tray.py | 4 +--- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/pype/modules/base.py b/pype/modules/base.py index a320fd797e..bb166abca1 100644 --- a/pype/modules/base.py +++ b/pype/modules/base.py @@ -278,13 +278,11 @@ class TrayModulesManager(ModulesManager): self.modules_by_id = {} self.modules_by_name = {} - def initialize(self, tray_widget, main_window): - self.tray_widget = tray_widget - self.main_window = main_window - + def initialize(self, tray_menu): self.initialize_modules() self.tray_init() self.connect_modules() + self.tray_menu(tray_menu) def get_enabled_tray_modules(self): output = [] diff --git a/pype/tools/tray/pype_tray.py b/pype/tools/tray/pype_tray.py index c931e819a8..55f8e975db 100644 --- a/pype/tools/tray/pype_tray.py +++ b/pype/tools/tray/pype_tray.py @@ -36,9 +36,7 @@ class TrayManager: def initialize_modules(self): """Add modules to tray.""" - self.modules_manager.initialize(self.tray_widget, self.main_window) - - self.modules_manager.tray_menu(self.tray_widget.menu) + self.modules_manager.initialize(self.tray_widget.menu) # Add services if they are services_submenu = ITrayService.services_submenu() From 8b5f2a025d3af34408c2a18a187ed05a417ab1ee Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 10 Dec 2020 17:50:14 +0100 Subject: [PATCH 48/78] added some exception catches --- pype/modules/base.py | 51 +++++++++++++++++++++++++++++++++++++++----- 1 file changed, 46 insertions(+), 5 deletions(-) diff --git a/pype/modules/base.py b/pype/modules/base.py index bb166abca1..7b7805b490 100644 --- a/pype/modules/base.py +++ b/pype/modules/base.py @@ -293,17 +293,58 @@ class TrayModulesManager(ModulesManager): def tray_init(self): for module in self.get_enabled_tray_modules(): - module.tray_init() - module.tray_initialized = True + try: + module.tray_init() + module.tray_initialized = True + except Exception: + self.log.warning( + "Module \"{}\" crashed on `tray_init`.".format( + module.name + ), + exc_info=True + ) def tray_menu(self, tray_menu): for module in self.get_enabled_tray_modules(): - module.tray_menu(tray_menu) + if not module.tray_initialized: + continue + + try: + module.tray_menu(tray_menu) + except Exception: + # Unset initialized mark + module.tray_initialized = False + self.log.warning( + "Module \"{}\" crashed on `tray_menu`.".format( + module.name + ), + exc_info=True + ) def start_modules(self): for module in self.get_enabled_tray_modules(): - module.tray_start() + if not module.tray_initialized: + continue + + try: + module.tray_start() + except Exception: + self.log.warning( + "Module \"{}\" crashed on `tray_start`.".format( + module.name + ), + exc_info=True + ) def on_exit(self): for module in self.get_enabled_tray_modules(): - module.tray_exit() + if module.tray_initialized: + try: + module.tray_exit() + except Exception: + self.log.warning( + "Module \"{}\" crashed on `tray_exit`.".format( + module.name + ), + exc_info=True + ) From b4604152de38ec511310c874c5118db59f2b1062 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 10 Dec 2020 17:58:35 +0100 Subject: [PATCH 49/78] added order of modules adding to menu --- pype/modules/base.py | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/pype/modules/base.py b/pype/modules/base.py index 7b7805b490..85035098ff 100644 --- a/pype/modules/base.py +++ b/pype/modules/base.py @@ -271,6 +271,17 @@ class ModulesManager: class TrayModulesManager(ModulesManager): + # Define order of modules in menu + modules_menu_order = ( + "User setting", + "Ftrack", + "muster", + "Avalon", + "Clockify", + "Standalone Publish", + "Logging" + ) + def __init__(self): self.log = PypeLogger().get_logger(self.__class__.__name__) @@ -305,7 +316,19 @@ class TrayModulesManager(ModulesManager): ) def tray_menu(self, tray_menu): - for module in self.get_enabled_tray_modules(): + ordered_modules = [] + enabled_by_name = { + module.name: module + for module in self.get_enabled_tray_modules() + } + + for name in self.modules_menu_order: + module_by_name = enabled_by_name.pop(name, None) + if module_by_name: + ordered_modules.append(module_by_name) + ordered_modules.extend(enabled_by_name.values()) + + for module in ordered_modules: if not module.tray_initialized: continue From 77f855b064a5f008a4fe1a1b442463747640d306 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 10 Dec 2020 18:23:32 +0100 Subject: [PATCH 50/78] moved plugin paths interface --- pype/modules/base.py | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/pype/modules/base.py b/pype/modules/base.py index 85035098ff..64b4b04ffa 100644 --- a/pype/modules/base.py +++ b/pype/modules/base.py @@ -68,6 +68,22 @@ class PypeModule: return {} +@six.add_metaclass(ABCMeta) +class IPluginPaths: + """Module has plugin paths to return. + + Expected result is dictionary with keys "publish", "create", "load" or + "actions" and values as list or string. + { + "publish": ["path/to/publish_plugins"] + } + """ + # TODO validation of an output + @abstractmethod + def get_plugin_paths(self): + pass + + @six.add_metaclass(ABCMeta) class ITrayModule: """Module has special procedures when used in Pype Tray. @@ -108,22 +124,6 @@ class ITrayModule: pass -@six.add_metaclass(ABCMeta) -class IPluginPaths: - """Module has plugin paths to return. - - Expected result is dictionary with keys "publish", "create", "load" or - "actions" and values as list or string. - { - "publish": ["path/to/publish_plugins"] - } - """ - # TODO validation of an output - @abstractmethod - def get_plugin_paths(self): - pass - - class ITrayService(ITrayModule): menu_action = None # Class properties From 8fad3d9bfc8fd121a0febb623c332135598825ce Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 10 Dec 2020 18:30:54 +0100 Subject: [PATCH 51/78] set module as failed if is not initialized on start --- pype/modules/base.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pype/modules/base.py b/pype/modules/base.py index 64b4b04ffa..55868a5fe3 100644 --- a/pype/modules/base.py +++ b/pype/modules/base.py @@ -347,6 +347,8 @@ class TrayModulesManager(ModulesManager): def start_modules(self): for module in self.get_enabled_tray_modules(): if not module.tray_initialized: + if isinstance(module, ITrayService): + module.set_service_failed() continue try: From fe9c750dd255603132bf44b3c7cf14b740abf0fd Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 10 Dec 2020 18:55:04 +0100 Subject: [PATCH 52/78] formatting change --- pype/modules/ftrack/ftrack_module.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pype/modules/ftrack/ftrack_module.py b/pype/modules/ftrack/ftrack_module.py index e610166131..03ea4b96a2 100644 --- a/pype/modules/ftrack/ftrack_module.py +++ b/pype/modules/ftrack/ftrack_module.py @@ -22,6 +22,7 @@ class FtrackModule( PypeModule, ITrayModule, IPluginPaths, ITimersManager, IUserModule ): name = "Ftrack" + def initialize(self, settings): ftrack_settings = settings[self.name] From 421cd78699760dc313167460e5effea46998698d Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 10 Dec 2020 19:25:03 +0100 Subject: [PATCH 53/78] fix __all__ in logging module --- pype/modules/logging/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pype/modules/logging/__init__.py b/pype/modules/logging/__init__.py index 48128a0f7e..c87d8b7f43 100644 --- a/pype/modules/logging/__init__.py +++ b/pype/modules/logging/__init__.py @@ -2,5 +2,5 @@ from .logging_module import LoggingModule __all__ = ( - "LoggingModule" + "LoggingModule", ) From 1ee742f76f3f16287f3003fa03af6a3726ff5b7c Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 10 Dec 2020 19:42:02 +0100 Subject: [PATCH 54/78] fixed ITrayService menu and icons --- pype/modules/base.py | 52 ++++++++++++++++++++++++-------------------- 1 file changed, 28 insertions(+), 24 deletions(-) diff --git a/pype/modules/base.py b/pype/modules/base.py index 55868a5fe3..3e1d86cf0e 100644 --- a/pype/modules/base.py +++ b/pype/modules/base.py @@ -143,47 +143,51 @@ class ITrayService(ITrayModule): # def get_service_info(self): # pass - @classmethod - def services_submenu(cls): - return cls._services_submenu + @staticmethod + def services_submenu(): + return ITrayService._services_submenu - @classmethod - def _load_service_icons(cls): + @staticmethod + def _set_services_submenu(services_submenu): + ITrayService._services_submenu = services_submenu + + @staticmethod + def _load_service_icons(): from Qt import QtGui - cls._failed_icon = QtGui.QIcon( + ITrayService._failed_icon = QtGui.QIcon( resources.get_resource("icons", "circle_red.png") ) - cls._icon_running = QtGui.QIcon( + ITrayService._icon_running = QtGui.QIcon( resources.get_resource("icons", "circle_green.png") ) - cls._icon_idle = QtGui.QIcon( + ITrayService._icon_idle = QtGui.QIcon( resources.get_resource("icons", "circle_orange.png") ) - @classmethod - def get_icon_running(cls): - if cls._icon_running is None: - cls._load_service_icons() - return cls._icon_running + @staticmethod + def get_icon_running(): + if ITrayService._icon_running is None: + ITrayService._load_service_icons() + return ITrayService._icon_running - @classmethod - def get_icon_idle(cls): - if cls._icon_idle is None: - cls._load_service_icons() - return cls._icon_idle + @staticmethod + def get_icon_idle(): + if ITrayService._icon_idle is None: + ITrayService._load_service_icons() + return ITrayService._icon_idle - @classmethod - def get_icon_failed(cls): - if cls._failed_icon is None: - cls._load_service_icons() - return cls._failed_icon + @staticmethod + def get_icon_failed(): + if ITrayService._failed_icon is None: + ITrayService._load_service_icons() + return ITrayService._failed_icon def tray_menu(self, tray_menu): from Qt import QtWidgets services_submenu = self.services_submenu() if services_submenu is None: services_submenu = QtWidgets.QMenu("Services", tray_menu) - self.__class__._services_submenu = services_submenu + self._set_services_submenu(services_submenu) action = QtWidgets.QAction(self.label, services_submenu) services_submenu.addAction(action) From c0e20fe36eeb4fec780b14378eaf7552b2fd5d54 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 10 Dec 2020 19:44:17 +0100 Subject: [PATCH 55/78] hound fix --- pype/modules/clockify/widgets.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pype/modules/clockify/widgets.py b/pype/modules/clockify/widgets.py index 85120b4794..718e6668bd 100644 --- a/pype/modules/clockify/widgets.py +++ b/pype/modules/clockify/widgets.py @@ -10,7 +10,7 @@ class MessageWidget(QtWidgets.QWidget): closed = QtCore.Signal() - def __init__(self, messages=[], title="Message"): + def __init__(self, messages, title): super(MessageWidget, self).__init__() # Icon From b7bac89907177436da9bf193cb4d9977fee05c06 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 11 Dec 2020 10:28:53 +0100 Subject: [PATCH 56/78] renamed file clockify.py to clockify_module.py --- pype/modules/clockify/__init__.py | 2 +- pype/modules/clockify/{clockify.py => clockify_module.py} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename pype/modules/clockify/{clockify.py => clockify_module.py} (100%) diff --git a/pype/modules/clockify/__init__.py b/pype/modules/clockify/__init__.py index e4ca2611c4..98834b516c 100644 --- a/pype/modules/clockify/__init__.py +++ b/pype/modules/clockify/__init__.py @@ -1,4 +1,4 @@ -from .clockify import ClockifyModule +from .clockify_module import ClockifyModule __all__ = ( "ClockifyModule", diff --git a/pype/modules/clockify/clockify.py b/pype/modules/clockify/clockify_module.py similarity index 100% rename from pype/modules/clockify/clockify.py rename to pype/modules/clockify/clockify_module.py From 4a39f7b37124f5e3ccbb5f2f523c9612e555f0ac Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 11 Dec 2020 10:40:45 +0100 Subject: [PATCH 57/78] renamed service methods to contain icon --- pype/modules/base.py | 10 +++++----- pype/modules/idle_manager/idle_manager.py | 2 +- pype/modules/websocket_server/websocket_server.py | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/pype/modules/base.py b/pype/modules/base.py index 3e1d86cf0e..cd8ab6978c 100644 --- a/pype/modules/base.py +++ b/pype/modules/base.py @@ -194,17 +194,17 @@ class ITrayService(ITrayModule): self.menu_action = action - self.set_service_running() + self.set_service_running_icon() - def set_service_running(self): + def set_service_running_icon(self): if self.menu_action: self.menu_action.setIcon(self.get_icon_running()) - def set_service_failed(self): + def set_service_failed_icon(self): if self.menu_action: self.menu_action.setIcon(self.get_icon_failed()) - def set_service_idle(self): + def set_service_idle_icon(self): if self.menu_action: self.menu_action.setIcon(self.get_icon_idle()) @@ -352,7 +352,7 @@ class TrayModulesManager(ModulesManager): for module in self.get_enabled_tray_modules(): if not module.tray_initialized: if isinstance(module, ITrayService): - module.set_service_failed() + module.set_service_failed_icon() continue try: diff --git a/pype/modules/idle_manager/idle_manager.py b/pype/modules/idle_manager/idle_manager.py index 42b96ec7db..92592f6add 100644 --- a/pype/modules/idle_manager/idle_manager.py +++ b/pype/modules/idle_manager/idle_manager.py @@ -93,7 +93,7 @@ class IdleManager(PypeModule, ITrayService): self.idle_thread.join() def on_thread_stop(self): - self.set_service_failed() + self.set_service_failed_icon() class IdleManagerThread(threading.Thread): diff --git a/pype/modules/websocket_server/websocket_server.py b/pype/modules/websocket_server/websocket_server.py index a96d2f249c..7a6710349b 100644 --- a/pype/modules/websocket_server/websocket_server.py +++ b/pype/modules/websocket_server/websocket_server.py @@ -38,7 +38,7 @@ class WebsocketModule(PypeModule, ITrayService): def tray_init(self): self.websocket_server = WebSocketServer() self.websocket_server.on_stop_callbacks.append( - self.set_service_failed + self.set_service_failed_icon ) def tray_start(self): From a76980a1a969178d716984c926592d2baff9b6cb Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 11 Dec 2020 10:48:58 +0100 Subject: [PATCH 58/78] always create services menu but hide it until any action is added --- pype/modules/base.py | 27 ++++++++++++++++----------- pype/tools/tray/pype_tray.py | 5 ++--- 2 files changed, 18 insertions(+), 14 deletions(-) diff --git a/pype/modules/base.py b/pype/modules/base.py index cd8ab6978c..d02c49ccc5 100644 --- a/pype/modules/base.py +++ b/pype/modules/base.py @@ -144,12 +144,19 @@ class ITrayService(ITrayModule): # pass @staticmethod - def services_submenu(): + def services_submenu(tray_menu): + if ITrayService._services_submenu is None: + from Qt import QtWidgets + services_submenu = QtWidgets.QMenu("Services", tray_menu) + services_submenu.setVisible(False) + ITrayService._services_submenu = services_submenu return ITrayService._services_submenu @staticmethod - def _set_services_submenu(services_submenu): - ITrayService._services_submenu = services_submenu + def add_service_action(action): + ITrayService._services_submenu.addAction(action) + if not ITrayService._services_submenu.isVisible(): + ITrayService._services_submenu.setVisible(True) @staticmethod def _load_service_icons(): @@ -184,16 +191,14 @@ class ITrayService(ITrayModule): def tray_menu(self, tray_menu): from Qt import QtWidgets - services_submenu = self.services_submenu() - if services_submenu is None: - services_submenu = QtWidgets.QMenu("Services", tray_menu) - self._set_services_submenu(services_submenu) - - action = QtWidgets.QAction(self.label, services_submenu) - services_submenu.addAction(action) - + action = QtWidgets.QAction( + self.label, + self.services_submenu(tray_menu) + ) self.menu_action = action + self.add_service_action(action) + self.set_service_running_icon() def set_service_running_icon(self): diff --git a/pype/tools/tray/pype_tray.py b/pype/tools/tray/pype_tray.py index 55f8e975db..c8c04d229a 100644 --- a/pype/tools/tray/pype_tray.py +++ b/pype/tools/tray/pype_tray.py @@ -39,9 +39,8 @@ class TrayManager: self.modules_manager.initialize(self.tray_widget.menu) # Add services if they are - services_submenu = ITrayService.services_submenu() - if services_submenu is not None: - self.tray_widget.menu.addMenu(services_submenu) + services_submenu = ITrayService.services_submenu(self.tray_widget.menu) + self.tray_widget.menu.addMenu(services_submenu) # Add separator self.tray_widget.menu.addSeparator() From e20a183e13af9dc3967edb37b5d3cdc421cdbc8a Mon Sep 17 00:00:00 2001 From: Milan Kolar Date: Fri, 11 Dec 2020 10:53:03 +0100 Subject: [PATCH 59/78] add settings, tray and mongo launcher --- dev_mongo.ps1 | 2 ++ dev_settings.ps1 | 2 ++ dev_tray.ps1 | 2 ++ 3 files changed, 6 insertions(+) create mode 100644 dev_mongo.ps1 create mode 100644 dev_settings.ps1 create mode 100644 dev_tray.ps1 diff --git a/dev_mongo.ps1 b/dev_mongo.ps1 new file mode 100644 index 0000000000..9ad021e39d --- /dev/null +++ b/dev_mongo.ps1 @@ -0,0 +1,2 @@ +.\venv\Scripts\Activate.ps1 +python pype.py mongodb diff --git a/dev_settings.ps1 b/dev_settings.ps1 new file mode 100644 index 0000000000..3eab14dc37 --- /dev/null +++ b/dev_settings.ps1 @@ -0,0 +1,2 @@ +.\venv\Scripts\Activate.ps1 +python pype.py settings --dev diff --git a/dev_tray.ps1 b/dev_tray.ps1 new file mode 100644 index 0000000000..44f3f69754 --- /dev/null +++ b/dev_tray.ps1 @@ -0,0 +1,2 @@ +.\venv\Scripts\Activate.ps1 +python pype.py tray --debug From c2888ea0f6825e275dd95299a57404d84e40b64f Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 11 Dec 2020 10:58:47 +0100 Subject: [PATCH 60/78] added ability to set mongo timeout --- pype/settings/defaults/system_settings/modules.json | 1 + .../settings/gui_schemas/system_schema/schema_modules.json | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/pype/settings/defaults/system_settings/modules.json b/pype/settings/defaults/system_settings/modules.json index 93c099a43e..69727b5148 100644 --- a/pype/settings/defaults/system_settings/modules.json +++ b/pype/settings/defaults/system_settings/modules.json @@ -1,6 +1,7 @@ { "Avalon": { "AVALON_MONGO": "mongodb://localhost:2707", + "avalon_mongo_timeout": 1000, "AVALON_DB_DATA": "{PYPE_SETUP_PATH}/../mongo_db_data", "AVALON_THUMBNAIL_ROOT": "{PYPE_SETUP_PATH}/../avalon_thumails", "environment": { diff --git a/pype/tools/settings/settings/gui_schemas/system_schema/schema_modules.json b/pype/tools/settings/settings/gui_schemas/system_schema/schema_modules.json index 62aaafc27b..83a57f4b80 100644 --- a/pype/tools/settings/settings/gui_schemas/system_schema/schema_modules.json +++ b/pype/tools/settings/settings/gui_schemas/system_schema/schema_modules.json @@ -14,6 +14,12 @@ "key": "AVALON_MONGO", "label": "Avalon Mongo URL" }, + { + "type": "number", + "key": "avalon_mongo_timeout", + "minimum": 0, + "label": "Avalon Mongo Timeout (ms)" + }, { "type": "text", "key": "AVALON_DB_DATA", From d36804fe82bf3356ba5df7210711007327f14161 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 11 Dec 2020 10:59:15 +0100 Subject: [PATCH 61/78] AVALON_TIMEOUT environment is filled by settings value --- pype/modules/avalon_apps/avalon_app.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/pype/modules/avalon_apps/avalon_app.py b/pype/modules/avalon_apps/avalon_app.py index 5f1690cf91..51ab7b4ca5 100644 --- a/pype/modules/avalon_apps/avalon_app.py +++ b/pype/modules/avalon_apps/avalon_app.py @@ -30,9 +30,15 @@ class AvalonModule(PypeModule, ITrayModule, IRestApi): if not thumbnail_root: thumbnail_root = avalon_settings["AVALON_THUMBNAIL_ROOT"] + # Mongo timeout + avalon_mongo_timeout = os.environ.get("AVALON_TIMEOUT") + if not avalon_mongo_timeout: + avalon_mongo_timeout = avalon_settings["avalon_mongo_timeout"] + self.thumbnail_root = thumbnail_root self.avalon_mongo_url = avalon_mongo_url - self.avalon_settings = avalon_settings + self.avalon_mongo_timeout = avalon_mongo_timeout + self.schema_path = os.path.join( os.path.dirname(pype.PACKAGE_DIR), "schema" @@ -63,12 +69,12 @@ class AvalonModule(PypeModule, ITrayModule, IRestApi): # TODO thumbnails root should be multiplafrom # - thumbnails root "AVALON_THUMBNAIL_ROOT": self.thumbnail_root, + # - mongo timeout in ms + "AVALON_TIMEOUT": str(self.avalon_mongo_timeout), # May be modifiable? # - mongo database name where projects are stored "AVALON_DB": "avalon", - # - mongo timeout in ms - "AVALON_TIMEOUT": "1000", # Avalon environments not used in code "AVALON_DEBUG": "1", From fb6ffa8cabdf9d20cd1415ac06825f2efd99c4a4 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 11 Dec 2020 11:00:00 +0100 Subject: [PATCH 62/78] added placeholder for avalon mongo --- .../settings/gui_schemas/system_schema/schema_modules.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pype/tools/settings/settings/gui_schemas/system_schema/schema_modules.json b/pype/tools/settings/settings/gui_schemas/system_schema/schema_modules.json index 83a57f4b80..9f5b441dbc 100644 --- a/pype/tools/settings/settings/gui_schemas/system_schema/schema_modules.json +++ b/pype/tools/settings/settings/gui_schemas/system_schema/schema_modules.json @@ -12,7 +12,8 @@ "children": [{ "type": "text", "key": "AVALON_MONGO", - "label": "Avalon Mongo URL" + "label": "Avalon Mongo URL", + "placeholder": "Pype Mongo is used if not filled." }, { "type": "number", From bb2d5af187677b49ae511b08466e391a0bde0f1c Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 11 Dec 2020 11:16:04 +0100 Subject: [PATCH 63/78] added parse to modules environments --- pype.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pype.py b/pype.py index c74a27f685..5e5fa215bb 100644 --- a/pype.py +++ b/pype.py @@ -111,7 +111,8 @@ def set_modules_environments(): # Metge environments with current environments and update values if module_envs: - env = acre.merge(module_envs, dict(os.environ)) + parsed_envs = acre.parse(module_envs) + env = acre.merge(parsed_envs, dict(os.environ)) os.environ.clear() os.environ.update(env) From e9bf54a4ec5181c8c1972ff7426ff38b149ac445 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 11 Dec 2020 11:16:54 +0100 Subject: [PATCH 64/78] renamed `avalon_mongo_timeout` key to `AVALON_TIMEOUT` --- pype/modules/avalon_apps/avalon_app.py | 2 +- pype/settings/defaults/system_settings/modules.json | 2 +- .../settings/gui_schemas/system_schema/schema_modules.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pype/modules/avalon_apps/avalon_app.py b/pype/modules/avalon_apps/avalon_app.py index 51ab7b4ca5..4ff027658c 100644 --- a/pype/modules/avalon_apps/avalon_app.py +++ b/pype/modules/avalon_apps/avalon_app.py @@ -33,7 +33,7 @@ class AvalonModule(PypeModule, ITrayModule, IRestApi): # Mongo timeout avalon_mongo_timeout = os.environ.get("AVALON_TIMEOUT") if not avalon_mongo_timeout: - avalon_mongo_timeout = avalon_settings["avalon_mongo_timeout"] + avalon_mongo_timeout = avalon_settings["AVALON_TIMEOUT"] self.thumbnail_root = thumbnail_root self.avalon_mongo_url = avalon_mongo_url diff --git a/pype/settings/defaults/system_settings/modules.json b/pype/settings/defaults/system_settings/modules.json index 69727b5148..65ef60aaa2 100644 --- a/pype/settings/defaults/system_settings/modules.json +++ b/pype/settings/defaults/system_settings/modules.json @@ -1,7 +1,7 @@ { "Avalon": { "AVALON_MONGO": "mongodb://localhost:2707", - "avalon_mongo_timeout": 1000, + "AVALON_TIMEOUT": 1000, "AVALON_DB_DATA": "{PYPE_SETUP_PATH}/../mongo_db_data", "AVALON_THUMBNAIL_ROOT": "{PYPE_SETUP_PATH}/../avalon_thumails", "environment": { diff --git a/pype/tools/settings/settings/gui_schemas/system_schema/schema_modules.json b/pype/tools/settings/settings/gui_schemas/system_schema/schema_modules.json index 9f5b441dbc..2b6ed77f22 100644 --- a/pype/tools/settings/settings/gui_schemas/system_schema/schema_modules.json +++ b/pype/tools/settings/settings/gui_schemas/system_schema/schema_modules.json @@ -17,7 +17,7 @@ }, { "type": "number", - "key": "avalon_mongo_timeout", + "key": "AVALON_TIMEOUT", "minimum": 0, "label": "Avalon Mongo Timeout (ms)" }, From cbe1dbb52f4d8c85756cbff2260c5d049e512d67 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 11 Dec 2020 11:17:39 +0100 Subject: [PATCH 65/78] AVALON_THUMBNAIL_ROOT is multiplatform path input --- pype/settings/defaults/system_settings/modules.json | 6 +++++- .../gui_schemas/system_schema/schema_modules.json | 12 +++++++----- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/pype/settings/defaults/system_settings/modules.json b/pype/settings/defaults/system_settings/modules.json index 65ef60aaa2..1fb13cc6c9 100644 --- a/pype/settings/defaults/system_settings/modules.json +++ b/pype/settings/defaults/system_settings/modules.json @@ -2,8 +2,12 @@ "Avalon": { "AVALON_MONGO": "mongodb://localhost:2707", "AVALON_TIMEOUT": 1000, + "AVALON_THUMBNAIL_ROOT": { + "windows": "", + "darwin": "", + "linux": "" + }, "AVALON_DB_DATA": "{PYPE_SETUP_PATH}/../mongo_db_data", - "AVALON_THUMBNAIL_ROOT": "{PYPE_SETUP_PATH}/../avalon_thumails", "environment": { "__environment_keys__": { "avalon": [ diff --git a/pype/tools/settings/settings/gui_schemas/system_schema/schema_modules.json b/pype/tools/settings/settings/gui_schemas/system_schema/schema_modules.json index 2b6ed77f22..2e749e48b1 100644 --- a/pype/tools/settings/settings/gui_schemas/system_schema/schema_modules.json +++ b/pype/tools/settings/settings/gui_schemas/system_schema/schema_modules.json @@ -22,14 +22,16 @@ "label": "Avalon Mongo Timeout (ms)" }, { - "type": "text", - "key": "AVALON_DB_DATA", - "label": "Avalon Mongo Data Location" + "type": "path-widget", + "label": "Thumbnail Storage Location", + "key": "AVALON_THUMBNAIL_ROOT", + "multiplatform": true, + "multipath": false }, { "type": "text", - "key": "AVALON_THUMBNAIL_ROOT", - "label": "Thumbnail Storage Location" + "key": "AVALON_DB_DATA", + "label": "Avalon Mongo Data Location" }, { "key": "environment", From 0d04bed8bbbac25aea95421d2c9d9080da67f9ee Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 11 Dec 2020 11:17:56 +0100 Subject: [PATCH 66/78] AVALON_MONGO is empty by default --- pype/settings/defaults/system_settings/modules.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pype/settings/defaults/system_settings/modules.json b/pype/settings/defaults/system_settings/modules.json index 1fb13cc6c9..eaa8c1ebe1 100644 --- a/pype/settings/defaults/system_settings/modules.json +++ b/pype/settings/defaults/system_settings/modules.json @@ -1,6 +1,6 @@ { "Avalon": { - "AVALON_MONGO": "mongodb://localhost:2707", + "AVALON_MONGO": "", "AVALON_TIMEOUT": 1000, "AVALON_THUMBNAIL_ROOT": { "windows": "", From b603fd7b0b5e353977aa4959c2c6208ce2655a71 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 11 Dec 2020 11:45:49 +0100 Subject: [PATCH 67/78] added few docstrings --- pype/modules/base.py | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/pype/modules/base.py b/pype/modules/base.py index d02c49ccc5..64bb120c67 100644 --- a/pype/modules/base.py +++ b/pype/modules/base.py @@ -125,7 +125,9 @@ class ITrayModule: class ITrayService(ITrayModule): + # Module's property menu_action = None + # Class properties _services_submenu = None _icon_failed = None @@ -135,7 +137,7 @@ class ITrayService(ITrayModule): @property @abstractmethod def label(self): - """Service label.""" + """Service label showed in menu.""" pass # TODO be able to get any sort of information to show/print @@ -202,14 +204,17 @@ class ITrayService(ITrayModule): self.set_service_running_icon() def set_service_running_icon(self): + """Change icon of an QAction to green circle.""" if self.menu_action: self.menu_action.setIcon(self.get_icon_running()) def set_service_failed_icon(self): + """Change icon of an QAction to red circle.""" if self.menu_action: self.menu_action.setIcon(self.get_icon_failed()) def set_service_idle_icon(self): + """Change icon of an QAction to orange circle.""" if self.menu_action: self.menu_action.setIcon(self.get_icon_idle()) @@ -226,10 +231,14 @@ class ModulesManager: self.connect_modules() def initialize_modules(self): + """Import and initialize modules.""" self.log.debug("*** Pype modules initialization.") + # Prepare settings for modules modules_settings = get_system_settings()["modules"] + # Go through globals in `pype.modules` for name in dir(pype.modules): modules_item = getattr(pype.modules, name, None) + # Filter globals that are not classes which inherit from PypeModule if ( not inspect.isclass(modules_item) or modules_item is pype.modules.PypeModule @@ -237,20 +246,25 @@ class ModulesManager: ): continue + # Check if class is abstract (Developing purpose) if inspect.isabstract(modules_item): + # Find missing implementations by convetion on `abc` module not_implemented = [] for attr_name in dir(modules_item): attr = getattr(modules_item, attr_name, None) if attr and getattr(attr, "__isabstractmethod__", None): not_implemented.append(attr_name) + # Log missing implementations self.log.warning(( "Skipping abstract Class: {}. Missing implementations: {}" ).format(name, ", ".join(not_implemented))) continue try: + # Try initialize module module = modules_item(self, modules_settings) + # Store initialized object self.modules.append(module) self.modules_by_id[module.id] = module self.modules_by_name[module.name] = module @@ -266,12 +280,17 @@ class ModulesManager: ) def connect_modules(self): + """Trigger connection with other enabled modules. + + Modules should handle their interfaces in `connect_with_modules`. + """ enabled_modules = self.get_enabled_modules() self.log.debug("Has {} enabled modules.".format(len(enabled_modules))) for module in enabled_modules: module.connect_with_modules(enabled_modules) def get_enabled_modules(self): + """Enabled modules initialized by the manager.""" return [ module for module in self.modules From 871794d7618fbba35d790ed677e7765130bb071a Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 11 Dec 2020 11:51:41 +0100 Subject: [PATCH 68/78] implemented helper to get plugin paths --- pype/modules/base.py | 55 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/pype/modules/base.py b/pype/modules/base.py index 64bb120c67..a38ba6f127 100644 --- a/pype/modules/base.py +++ b/pype/modules/base.py @@ -297,6 +297,61 @@ class ModulesManager: if module.enabled ] + def collect_plugin_paths(self): + """Helper to collect all plugins from modules inherited IPluginPaths. + + Output is dictionary with keys "publish", "create", "load" and + "actions" each containing list of paths. + + Unknown keys are logged out. + """ + # Output structure + output = { + "publish": [], + "create": [], + "load": [], + "actions": [] + } + unknown_keys_by_module = {} + for module in self.get_enabled_modules(): + # Skip module that do not inherit from `IPluginPaths` + if not isinstance(module, IPluginPaths): + continue + plugin_paths = module.get_plugin_paths() + for key, value in plugin_paths.items(): + # Filter unknown keys + if key not in output: + if module.name not in unknown_keys_by_module: + unknown_keys_by_module[module.name] = [] + unknown_keys_by_module[module.name].append(key) + continue + + # Skip if value is empty + if not value: + continue + + # Convert to list if value is not list + if not isinstance(value, (list, tuple, set)): + value = [value] + output[key].extend(value) + + # Report unknown keys (Developing purposes) + if unknown_keys_by_module: + expected_keys = ", ".join([ + "\"{}\"".format(key) for key in output.keys() + ]) + msg_template = "Module: \"{}\" - got key {}" + msg_items = [] + for module_name, keys in unknown_keys_by_module.items(): + joined_keys = ", ".join([ + "\"{}\"".format(key) for key in keys + ]) + msg_items.append(msg_template.format(module_name, joined_keys)) + self.log.warning(( + "Expected keys from `get_plugin_paths` are {}. {}" + ).format(expected_keys, " | ".join(msg_items))) + return output + class TrayModulesManager(ModulesManager): # Define order of modules in menu From 77dc378e0748f669c4a86adc27ef117d993c1fa4 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 11 Dec 2020 11:52:03 +0100 Subject: [PATCH 69/78] used plugin collect helper in code --- pype.py | 9 +-------- pype/modules/avalon_apps/avalon_app.py | 16 ++-------------- .../standalonepublish_module.py | 9 ++------- 3 files changed, 5 insertions(+), 29 deletions(-) diff --git a/pype.py b/pype.py index 5e5fa215bb..37428df079 100644 --- a/pype.py +++ b/pype.py @@ -76,7 +76,6 @@ def set_modules_environments(): modules_manager = ModulesManager() - publish_plugin_dirs = [] module_envs = {} for module in modules_manager.get_enabled_modules(): # Collect global module's global environments @@ -89,13 +88,7 @@ def set_modules_environments(): ) module_envs[key] = value - # Collect plugin paths for publishing - if isinstance(module, IPluginPaths): - plugins_data = module.get_plugin_paths() or {} - publish_paths = plugins_data.get("publish") or [] - if not isinstance(publish_paths, (tuple, list, set)): - publish_paths = [publish_paths] - publish_plugin_dirs.extend(publish_paths) + publish_plugin_dirs = modules_manager.collect_plugin_paths()["publish"] # Set pyblish plugins paths if any module want to register them if publish_plugin_dirs: diff --git a/pype/modules/avalon_apps/avalon_app.py b/pype/modules/avalon_apps/avalon_app.py index 4ff027658c..a47bebc420 100644 --- a/pype/modules/avalon_apps/avalon_app.py +++ b/pype/modules/avalon_apps/avalon_app.py @@ -114,20 +114,8 @@ class AvalonModule(PypeModule, ITrayModule, IRestApi): exc_info=True ) - def connect_with_modules(self, enabled_modules): - plugin_paths = [] - for module in enabled_modules: - if not isinstance(module, IPluginPaths): - continue - - paths = (module.get_plugin_paths() or {}).get("actions") - if not paths: - continue - - if not isinstance(paths, (list, tuple, set)): - paths = [paths] - plugin_paths.extend(paths) - + def connect_with_modules(self, _enabled_modules): + plugin_paths = self.module.collect_plugin_paths()["actions"] if plugin_paths: env_paths_str = os.environ.get("AVALON_ACTIONS") or "" env_paths = env_paths_str.split(os.pathsep) diff --git a/pype/modules/standalonepublish/standalonepublish_module.py b/pype/modules/standalonepublish/standalonepublish_module.py index 7b995ff093..fd709f695d 100644 --- a/pype/modules/standalonepublish/standalonepublish_module.py +++ b/pype/modules/standalonepublish/standalonepublish_module.py @@ -34,13 +34,8 @@ class StandAlonePublishModule(PypeModule, ITrayModule): def connect_with_modules(self, enabled_modules): """Collect publish paths from other modules.""" - for module in enabled_modules: - if isinstance(module, IPluginPaths): - plugin_paths = module.get_plugin_paths() or {} - publish_paths = plugin_paths.get("publish") or [] - if not isinstance(publish_paths, (list, tuple, set)): - publish_paths = [publish_paths] - self.publish_paths.extend(publish_paths) + publish_paths = self.module.collect_plugin_paths()["publish"] + self.publish_paths.extend(publish_paths) def run_standalone_publisher(self): from pype import tools From e5e5924d8c4510417f133f189f27234e1c9992a5 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 11 Dec 2020 11:58:03 +0100 Subject: [PATCH 70/78] implemented helper for global environment variables --- pype.py | 13 +------------ pype/modules/base.py | 23 +++++++++++++++++++++++ 2 files changed, 24 insertions(+), 12 deletions(-) diff --git a/pype.py b/pype.py index 37428df079..a0843fa566 100644 --- a/pype.py +++ b/pype.py @@ -76,18 +76,7 @@ def set_modules_environments(): modules_manager = ModulesManager() - module_envs = {} - for module in modules_manager.get_enabled_modules(): - # Collect global module's global environments - _envs = module.get_global_environments() - for key, value in _envs.items(): - if key in module_envs: - # TODO better error message - raise AssertionError( - "Duplicated environment key {}".format(key) - ) - module_envs[key] = value - + module_envs = modules_manager.collect_global_environments() publish_plugin_dirs = modules_manager.collect_plugin_paths()["publish"] # Set pyblish plugins paths if any module want to register them diff --git a/pype/modules/base.py b/pype/modules/base.py index a38ba6f127..6ba9022f95 100644 --- a/pype/modules/base.py +++ b/pype/modules/base.py @@ -297,6 +297,29 @@ class ModulesManager: if module.enabled ] + def collect_global_environments(self): + """Helper to collect global enviornment variabled from modules. + + Returns: + dict: Global environment variables from enabled modules. + + Raises: + AssertionError: Gobal environment variables must be unique for + all modules. + """ + module_envs = {} + for module in self.get_enabled_modules(): + # Collect global module's global environments + _envs = module.get_global_environments() + for key, value in _envs.items(): + if key in module_envs: + # TODO better error message + raise AssertionError( + "Duplicated environment key {}".format(key) + ) + module_envs[key] = value + return module_envs + def collect_plugin_paths(self): """Helper to collect all plugins from modules inherited IPluginPaths. From 9c6494ecb5c0bd781f1007012a153cc61f883855 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 11 Dec 2020 11:58:11 +0100 Subject: [PATCH 71/78] modified docstrings --- pype/modules/base.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/pype/modules/base.py b/pype/modules/base.py index 6ba9022f95..72d0eb4503 100644 --- a/pype/modules/base.py +++ b/pype/modules/base.py @@ -290,7 +290,11 @@ class ModulesManager: module.connect_with_modules(enabled_modules) def get_enabled_modules(self): - """Enabled modules initialized by the manager.""" + """Enabled modules initialized by the manager. + + Returns: + list: Initialized and enabled modules. + """ return [ module for module in self.modules @@ -323,10 +327,11 @@ class ModulesManager: def collect_plugin_paths(self): """Helper to collect all plugins from modules inherited IPluginPaths. - Output is dictionary with keys "publish", "create", "load" and - "actions" each containing list of paths. - Unknown keys are logged out. + + Returns: + dict: Output is dictionary with keys "publish", "create", "load" + and "actions" each containing list of paths. """ # Output structure output = { From 6232b59a9a82a89682da64a4178396f0affdf96f Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 11 Dec 2020 11:59:41 +0100 Subject: [PATCH 72/78] implemented base of deadline module --- pype/modules/deadline/__init__.py | 6 ++++++ pype/modules/deadline/deadline_module.py | 21 +++++++++++++++++++++ 2 files changed, 27 insertions(+) create mode 100644 pype/modules/deadline/__init__.py create mode 100644 pype/modules/deadline/deadline_module.py diff --git a/pype/modules/deadline/__init__.py b/pype/modules/deadline/__init__.py new file mode 100644 index 0000000000..5631e501d8 --- /dev/null +++ b/pype/modules/deadline/__init__.py @@ -0,0 +1,6 @@ +from .deadline_module import DeadlineModule + + +__all__ = ( + "DeadlineModule", +) diff --git a/pype/modules/deadline/deadline_module.py b/pype/modules/deadline/deadline_module.py new file mode 100644 index 0000000000..e4c1e7fb2d --- /dev/null +++ b/pype/modules/deadline/deadline_module.py @@ -0,0 +1,21 @@ +import os +from .. import PypeModule + + +class DeadlineModule(PypeModule): + name = "deadline" + + def initialize(self, modules_settings): + # This module is always enabled + deadline_settings = modules_settings[self.name] + self.enabled = deadline_settings["enabled"] + self.deadline_url = deadline_settings["DEADLINE_REST_URL"] + + def get_global_environments(self): + """Deadline global environments for pype implementation.""" + return { + "DEADLINE_REST_URL": self.deadline_url + } + + def connect_with_modules(self, *_a, **_kw): + return From 2e6977759ca51baf922e44a7e34c9855191f9720 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 11 Dec 2020 12:02:37 +0100 Subject: [PATCH 73/78] removed unused import --- pype.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pype.py b/pype.py index a0843fa566..2580bafb55 100644 --- a/pype.py +++ b/pype.py @@ -72,7 +72,7 @@ def set_modules_environments(): This requires to have pype in `sys.path`. """ - from pype.modules import ModulesManager, IPluginPaths + from pype.modules import ModulesManager modules_manager = ModulesManager() From 14a9554d11ca545e9751f0eb6e422c61b0774bdb Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 11 Dec 2020 12:03:34 +0100 Subject: [PATCH 74/78] removed unused imports --- pype/modules/avalon_apps/avalon_app.py | 1 - pype/modules/deadline/deadline_module.py | 1 - pype/modules/standalonepublish/standalonepublish_module.py | 2 +- 3 files changed, 1 insertion(+), 3 deletions(-) diff --git a/pype/modules/avalon_apps/avalon_app.py b/pype/modules/avalon_apps/avalon_app.py index a47bebc420..8b13d8ff52 100644 --- a/pype/modules/avalon_apps/avalon_app.py +++ b/pype/modules/avalon_apps/avalon_app.py @@ -4,7 +4,6 @@ from pype import resources from .. import ( PypeModule, ITrayModule, - IPluginPaths, IRestApi ) diff --git a/pype/modules/deadline/deadline_module.py b/pype/modules/deadline/deadline_module.py index e4c1e7fb2d..6de68c390f 100644 --- a/pype/modules/deadline/deadline_module.py +++ b/pype/modules/deadline/deadline_module.py @@ -1,4 +1,3 @@ -import os from .. import PypeModule diff --git a/pype/modules/standalonepublish/standalonepublish_module.py b/pype/modules/standalonepublish/standalonepublish_module.py index fd709f695d..a749bacccd 100644 --- a/pype/modules/standalonepublish/standalonepublish_module.py +++ b/pype/modules/standalonepublish/standalonepublish_module.py @@ -2,7 +2,7 @@ import os import sys import subprocess import pype -from .. import PypeModule, ITrayModule, IPluginPaths +from .. import PypeModule, ITrayModule class StandAlonePublishModule(PypeModule, ITrayModule): From eb03d10d6107f908b3c02134e422e2164bd8484c Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 11 Dec 2020 12:13:25 +0100 Subject: [PATCH 75/78] fix variable --- pype/modules/avalon_apps/avalon_app.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pype/modules/avalon_apps/avalon_app.py b/pype/modules/avalon_apps/avalon_app.py index 8b13d8ff52..dc9c500955 100644 --- a/pype/modules/avalon_apps/avalon_app.py +++ b/pype/modules/avalon_apps/avalon_app.py @@ -114,7 +114,7 @@ class AvalonModule(PypeModule, ITrayModule, IRestApi): ) def connect_with_modules(self, _enabled_modules): - plugin_paths = self.module.collect_plugin_paths()["actions"] + plugin_paths = self.manager.collect_plugin_paths()["actions"] if plugin_paths: env_paths_str = os.environ.get("AVALON_ACTIONS") or "" env_paths = env_paths_str.split(os.pathsep) From 82f23bb1e239839ee5670c98b80aac34ab220d5e Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 11 Dec 2020 12:14:53 +0100 Subject: [PATCH 76/78] fix variable --- pype/modules/standalonepublish/standalonepublish_module.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pype/modules/standalonepublish/standalonepublish_module.py b/pype/modules/standalonepublish/standalonepublish_module.py index a749bacccd..abed6bddd9 100644 --- a/pype/modules/standalonepublish/standalonepublish_module.py +++ b/pype/modules/standalonepublish/standalonepublish_module.py @@ -34,7 +34,7 @@ class StandAlonePublishModule(PypeModule, ITrayModule): def connect_with_modules(self, enabled_modules): """Collect publish paths from other modules.""" - publish_paths = self.module.collect_plugin_paths()["publish"] + publish_paths = self.manager.collect_plugin_paths()["publish"] self.publish_paths.extend(publish_paths) def run_standalone_publisher(self): From 92296e581d334cdcc1c1c3fb8a312d59ff3efc86 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 11 Dec 2020 12:18:54 +0100 Subject: [PATCH 77/78] remove unused environments --- pype/modules/avalon_apps/avalon_app.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/pype/modules/avalon_apps/avalon_app.py b/pype/modules/avalon_apps/avalon_app.py index dc9c500955..d80c0afe6f 100644 --- a/pype/modules/avalon_apps/avalon_app.py +++ b/pype/modules/avalon_apps/avalon_app.py @@ -75,10 +75,6 @@ class AvalonModule(PypeModule, ITrayModule, IRestApi): # - mongo database name where projects are stored "AVALON_DB": "avalon", - # Avalon environments not used in code - "AVALON_DEBUG": "1", - "AVALON_EARLY_ADOPTER": "1", - # Not even connected to Avalon # TODO remove - pype's variable for local mongo "AVALON_DB_DATA": mongodb_data_dir From 07aec049d05377bd9761570e9d9e3c08bf841bfa Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 11 Dec 2020 12:20:44 +0100 Subject: [PATCH 78/78] remove avalon environments raw json from settings --- .../defaults/system_settings/modules.json | 36 +------------------ .../system_schema/schema_modules.json | 6 ---- 2 files changed, 1 insertion(+), 41 deletions(-) diff --git a/pype/settings/defaults/system_settings/modules.json b/pype/settings/defaults/system_settings/modules.json index eaa8c1ebe1..bd5b3e8294 100644 --- a/pype/settings/defaults/system_settings/modules.json +++ b/pype/settings/defaults/system_settings/modules.json @@ -7,41 +7,7 @@ "darwin": "", "linux": "" }, - "AVALON_DB_DATA": "{PYPE_SETUP_PATH}/../mongo_db_data", - "environment": { - "__environment_keys__": { - "avalon": [ - "AVALON_CONFIG", - "AVALON_PROJECTS", - "AVALON_USERNAME", - "AVALON_PASSWORD", - "AVALON_DEBUG", - "AVALON_MONGO", - "AVALON_DB", - "AVALON_DB_DATA", - "AVALON_EARLY_ADOPTER", - "AVALON_SCHEMA", - "AVALON_LOCATION", - "AVALON_LABEL", - "AVALON_TIMEOUT", - "AVALON_THUMBNAIL_ROOT" - ] - }, - "AVALON_CONFIG": "pype", - "AVALON_PROJECTS": "{PYPE_PROJECTS_PATH}", - "AVALON_USERNAME": "avalon", - "AVALON_PASSWORD": "secret", - "AVALON_DEBUG": "1", - "AVALON_MONGO": "mongodb://localhost:2707", - "AVALON_DB": "avalon", - "AVALON_DB_DATA": "{PYPE_SETUP_PATH}/../mongo_db_data", - "AVALON_EARLY_ADOPTER": "1", - "AVALON_SCHEMA": "{PYPE_MODULE_ROOT}/schema", - "AVALON_LOCATION": "http://127.0.0.1", - "AVALON_LABEL": "Pype", - "AVALON_TIMEOUT": "1000", - "AVALON_THUMBNAIL_ROOT": "{PYPE_SETUP_PATH}/../avalon_thumails" - } + "AVALON_DB_DATA": "{PYPE_SETUP_PATH}/../mongo_db_data" }, "Ftrack": { "enabled": true, diff --git a/pype/tools/settings/settings/gui_schemas/system_schema/schema_modules.json b/pype/tools/settings/settings/gui_schemas/system_schema/schema_modules.json index 2e749e48b1..af4426fd01 100644 --- a/pype/tools/settings/settings/gui_schemas/system_schema/schema_modules.json +++ b/pype/tools/settings/settings/gui_schemas/system_schema/schema_modules.json @@ -32,12 +32,6 @@ "type": "text", "key": "AVALON_DB_DATA", "label": "Avalon Mongo Data Location" - }, - { - "key": "environment", - "label": "Environment", - "type": "raw-json", - "env_group_key": "avalon" } ] }, {