This commit is contained in:
Petr Kalis 2021-08-25 11:42:04 +02:00
commit d5cc025da5
295 changed files with 1140 additions and 712 deletions

10
.gitmodules vendored
View file

@ -4,9 +4,9 @@
[submodule "repos/avalon-unreal-integration"]
path = repos/avalon-unreal-integration
url = https://github.com/pypeclub/avalon-unreal-integration.git
[submodule "openpype/modules/ftrack/python2_vendor/ftrack-python-api"]
path = openpype/modules/ftrack/python2_vendor/ftrack-python-api
[submodule "openpype/modules/default_modules/ftrack/python2_vendor/arrow"]
path = openpype/modules/default_modules/ftrack/python2_vendor/arrow
url = git@github.com:arrow-py/arrow.git
[submodule "openpype/modules/default_modules/ftrack/python2_vendor/ftrack-python-api"]
path = openpype/modules/default_modules/ftrack/python2_vendor/ftrack-python-api
url = https://bitbucket.org/ftrack/ftrack-python-api.git
[submodule "openpype/modules/ftrack/python2_vendor/arrow"]
path = openpype/modules/ftrack/python2_vendor/arrow
url = https://github.com/arrow-py/arrow.git

View file

@ -1,10 +1,21 @@
# Changelog
## [3.3.1-nightly.1](https://github.com/pypeclub/OpenPype/tree/HEAD)
## [3.4.0-nightly.2](https://github.com/pypeclub/OpenPype/tree/HEAD)
[Full Changelog](https://github.com/pypeclub/OpenPype/compare/3.3.0...HEAD)
[Full Changelog](https://github.com/pypeclub/OpenPype/compare/3.3.1...HEAD)
**🐛 Bug fixes**
**Merged pull requests:**
- Maya: Add Xgen family support [\#1947](https://github.com/pypeclub/OpenPype/pull/1947)
- Add face sets to exported alembics [\#1942](https://github.com/pypeclub/OpenPype/pull/1942)
- Environments: Tool environments in alphabetical order [\#1910](https://github.com/pypeclub/OpenPype/pull/1910)
- Dynamic modules [\#1872](https://github.com/pypeclub/OpenPype/pull/1872)
## [3.3.1](https://github.com/pypeclub/OpenPype/tree/3.3.1) (2021-08-20)
[Full Changelog](https://github.com/pypeclub/OpenPype/compare/CI/3.3.1-nightly.1...3.3.1)
**Merged pull requests:**
- TVPaint: Fixed rendered frame indexes [\#1946](https://github.com/pypeclub/OpenPype/pull/1946)
- Maya: Menu actions fix [\#1945](https://github.com/pypeclub/OpenPype/pull/1945)
@ -15,80 +26,66 @@
[Full Changelog](https://github.com/pypeclub/OpenPype/compare/CI/3.3.0-nightly.11...3.3.0)
**🚀 Enhancements**
**Merged pull requests:**
- Python console interpreter [\#1940](https://github.com/pypeclub/OpenPype/pull/1940)
- Fix - make AE workfile publish to Ftrack configurable [\#1937](https://github.com/pypeclub/OpenPype/pull/1937)
- Fix - ftrack family was added incorrectly in some cases [\#1935](https://github.com/pypeclub/OpenPype/pull/1935)
- Settings UI: Breadcrumbs in settings [\#1932](https://github.com/pypeclub/OpenPype/pull/1932)
- Fix - Deadline publish on Linux started Tray instead of headless publishing [\#1930](https://github.com/pypeclub/OpenPype/pull/1930)
- Maya: Validate Model Name - repair accident deletion in settings defaults [\#1929](https://github.com/pypeclub/OpenPype/pull/1929)
- Global: Updated logos and Default settings [\#1927](https://github.com/pypeclub/OpenPype/pull/1927)
- Nuke: submit to farm failed due `ftrack` family remove [\#1926](https://github.com/pypeclub/OpenPype/pull/1926)
- Check for missing ✨ Python when using `pyenv` [\#1925](https://github.com/pypeclub/OpenPype/pull/1925)
- Maya: Scene patching 🩹on submission to Deadline [\#1923](https://github.com/pypeclub/OpenPype/pull/1923)
- Fix - validate takes repre\["files"\] as list all the time [\#1922](https://github.com/pypeclub/OpenPype/pull/1922)
- Settings: Default values for enum [\#1920](https://github.com/pypeclub/OpenPype/pull/1920)
- Settings UI: Modifiable dict view enhance [\#1919](https://github.com/pypeclub/OpenPype/pull/1919)
- standalone: validator asset parents [\#1917](https://github.com/pypeclub/OpenPype/pull/1917)
- Nuke: update video file crassing [\#1916](https://github.com/pypeclub/OpenPype/pull/1916)
- Fix - texture validators for workfiles triggers only for textures workfiles [\#1914](https://github.com/pypeclub/OpenPype/pull/1914)
- submodules: avalon-core update [\#1911](https://github.com/pypeclub/OpenPype/pull/1911)
- Settings UI: List order works as expected [\#1906](https://github.com/pypeclub/OpenPype/pull/1906)
- Add support for multiple Deadline ☠️➖ servers [\#1905](https://github.com/pypeclub/OpenPype/pull/1905)
- Hiero: loaded clip was not set colorspace from version data [\#1904](https://github.com/pypeclub/OpenPype/pull/1904)
- Pyblish UI: Fix collecting stage processing [\#1903](https://github.com/pypeclub/OpenPype/pull/1903)
- Burnins: Use input's bitrate in h624 [\#1902](https://github.com/pypeclub/OpenPype/pull/1902)
- Feature AE local render [\#1901](https://github.com/pypeclub/OpenPype/pull/1901)
- Ftrack: Where I run action enhancement [\#1900](https://github.com/pypeclub/OpenPype/pull/1900)
- Ftrack: Private project server actions [\#1899](https://github.com/pypeclub/OpenPype/pull/1899)
- Support nested studio plugins paths. [\#1898](https://github.com/pypeclub/OpenPype/pull/1898)
- Bug: fixed python detection [\#1893](https://github.com/pypeclub/OpenPype/pull/1893)
- Settings: global validators with options [\#1892](https://github.com/pypeclub/OpenPype/pull/1892)
- Settings: Conditional dict enum positioning [\#1891](https://github.com/pypeclub/OpenPype/pull/1891)
- global: integrate name missing default template [\#1890](https://github.com/pypeclub/OpenPype/pull/1890)
- publisher: editorial plugins fixes [\#1889](https://github.com/pypeclub/OpenPype/pull/1889)
- Expose stop timer through rest api. [\#1886](https://github.com/pypeclub/OpenPype/pull/1886)
- TVPaint: Increment workfile [\#1885](https://github.com/pypeclub/OpenPype/pull/1885)
- Allow Multiple Notes to run on tasks. [\#1882](https://github.com/pypeclub/OpenPype/pull/1882)
- Normalize path returned from Workfiles. [\#1880](https://github.com/pypeclub/OpenPype/pull/1880)
- Prepare for pyside2 [\#1869](https://github.com/pypeclub/OpenPype/pull/1869)
- Filter hosts in settings host-enum [\#1868](https://github.com/pypeclub/OpenPype/pull/1868)
- Local actions with process identifier [\#1867](https://github.com/pypeclub/OpenPype/pull/1867)
- Workfile tool start at host launch support [\#1865](https://github.com/pypeclub/OpenPype/pull/1865)
- Maya: support for configurable `dirmap` 🗺️ [\#1859](https://github.com/pypeclub/OpenPype/pull/1859)
- Settings list can use template or schema as object type [\#1815](https://github.com/pypeclub/OpenPype/pull/1815)
**🐛 Bug fixes**
- Fix - ftrack family was added incorrectly in some cases [\#1935](https://github.com/pypeclub/OpenPype/pull/1935)
- Fix - Deadline publish on Linux started Tray instead of headless publishing [\#1930](https://github.com/pypeclub/OpenPype/pull/1930)
- Maya: Validate Model Name - repair accident deletion in settings defaults [\#1929](https://github.com/pypeclub/OpenPype/pull/1929)
- Nuke: submit to farm failed due `ftrack` family remove [\#1926](https://github.com/pypeclub/OpenPype/pull/1926)
- Fix - validate takes repre\["files"\] as list all the time [\#1922](https://github.com/pypeclub/OpenPype/pull/1922)
- standalone: validator asset parents [\#1917](https://github.com/pypeclub/OpenPype/pull/1917)
- Nuke: update video file crassing [\#1916](https://github.com/pypeclub/OpenPype/pull/1916)
- Fix - texture validators for workfiles triggers only for textures workfiles [\#1914](https://github.com/pypeclub/OpenPype/pull/1914)
- Fix - validators for textures workfiles trigger only for textures workfiles [\#1913](https://github.com/pypeclub/OpenPype/pull/1913)
- Settings UI: List order works as expected [\#1906](https://github.com/pypeclub/OpenPype/pull/1906)
- Hiero: loaded clip was not set colorspace from version data [\#1904](https://github.com/pypeclub/OpenPype/pull/1904)
- Pyblish UI: Fix collecting stage processing [\#1903](https://github.com/pypeclub/OpenPype/pull/1903)
- Burnins: Use input's bitrate in h624 [\#1902](https://github.com/pypeclub/OpenPype/pull/1902)
- Bug: fixed python detection [\#1893](https://github.com/pypeclub/OpenPype/pull/1893)
- global: integrate name missing default template [\#1890](https://github.com/pypeclub/OpenPype/pull/1890)
- publisher: editorial plugins fixes [\#1889](https://github.com/pypeclub/OpenPype/pull/1889)
- Normalize path returned from Workfiles. [\#1880](https://github.com/pypeclub/OpenPype/pull/1880)
- Workfiles tool event arguments fix [\#1862](https://github.com/pypeclub/OpenPype/pull/1862)
- Maya: don't add reference members as connections to the container set 📦 [\#1855](https://github.com/pypeclub/OpenPype/pull/1855)
- Settings error dialog on show [\#1798](https://github.com/pypeclub/OpenPype/pull/1798)
**Merged pull requests:**
- Fix - make AE workfile publish to Ftrack configurable [\#1937](https://github.com/pypeclub/OpenPype/pull/1937)
- Settings UI: Breadcrumbs in settings [\#1932](https://github.com/pypeclub/OpenPype/pull/1932)
- Add support for multiple Deadline ☠️➖ servers [\#1905](https://github.com/pypeclub/OpenPype/pull/1905)
- Maya: add support for `RedshiftNormalMap` node, fix `tx` linear space 🚀 [\#1863](https://github.com/pypeclub/OpenPype/pull/1863)
- Workfiles tool event arguments fix [\#1862](https://github.com/pypeclub/OpenPype/pull/1862)
- Maya: support for configurable `dirmap` 🗺️ [\#1859](https://github.com/pypeclub/OpenPype/pull/1859)
- Maya: don't add reference members as connections to the container set 📦 [\#1855](https://github.com/pypeclub/OpenPype/pull/1855)
- Settings list can use template or schema as object type [\#1815](https://github.com/pypeclub/OpenPype/pull/1815)
- Maya: expected files -\> render products ⚙️ overhaul [\#1812](https://github.com/pypeclub/OpenPype/pull/1812)
- Settings error dialog on show [\#1798](https://github.com/pypeclub/OpenPype/pull/1798)
## [3.2.0](https://github.com/pypeclub/OpenPype/tree/3.2.0) (2021-07-13)
[Full Changelog](https://github.com/pypeclub/OpenPype/compare/CI/3.2.0-nightly.7...3.2.0)
**🚀 Enhancements**
- Nuke: ftrack family plugin settings preset [\#1805](https://github.com/pypeclub/OpenPype/pull/1805)
- Standalone publisher last project [\#1799](https://github.com/pypeclub/OpenPype/pull/1799)
- Ftrack Multiple notes as server action [\#1795](https://github.com/pypeclub/OpenPype/pull/1795)
**🐛 Bug fixes**
- nuke: fixing wrong name of family folder when `used existing frames` [\#1803](https://github.com/pypeclub/OpenPype/pull/1803)
- Collect ftrack family bugs [\#1801](https://github.com/pypeclub/OpenPype/pull/1801)
**Merged pull requests:**
- Build: don't add Poetry to `PATH` [\#1808](https://github.com/pypeclub/OpenPype/pull/1808)
- Nuke: ftrack family plugin settings preset [\#1805](https://github.com/pypeclub/OpenPype/pull/1805)
- nuke: fixing wrong name of family folder when `used existing frames` [\#1803](https://github.com/pypeclub/OpenPype/pull/1803)
- Collect ftrack family bugs [\#1801](https://github.com/pypeclub/OpenPype/pull/1801)
- Standalone publisher last project [\#1799](https://github.com/pypeclub/OpenPype/pull/1799)
## [2.18.4](https://github.com/pypeclub/OpenPype/tree/2.18.4) (2021-06-24)

View file

@ -68,6 +68,10 @@ def patched_discover(superclass):
def install():
"""Install Pype to Avalon."""
from pyblish.lib import MessageHandler
from openpype.modules import load_modules
# Make sure modules are loaded
load_modules()
def modified_emit(obj, record):
"""Method replacing `emit` in Pyblish's MessageHandler."""

View file

@ -24,6 +24,7 @@ class CreateAnimation(plugin.Creator):
# Write vertex colors with the geometry.
self.data["writeColorSets"] = False
self.data["writeFaceSets"] = False
# Include only renderable visible shapes.
# Skips locators and empty transforms

View file

@ -15,6 +15,7 @@ class CreateModel(plugin.Creator):
# Vertex colors with the geometry
self.data["writeColorSets"] = False
self.data["writeFaceSets"] = False
# Include attributes by attribute name or prefix
self.data["attr"] = ""

View file

@ -20,6 +20,7 @@ class CreatePointCache(plugin.Creator):
self.data.update(lib.collect_animation_data())
self.data["writeColorSets"] = False # Vertex colors with the geometry.
self.data["writeFaceSets"] = False # Vertex colors with the geometry.
self.data["renderableOnly"] = False # Only renderable visible shapes
self.data["visibleOnly"] = False # only nodes that are visible
self.data["includeParentHierarchy"] = False # Include parent groups

View file

@ -57,7 +57,8 @@ class ExtractAnimation(openpype.api.Extractor):
"uvWrite": True,
"selection": True,
"worldSpace": instance.data.get("worldSpace", True),
"writeColorSets": instance.data.get("writeColorSets", False)
"writeColorSets": instance.data.get("writeColorSets", False),
"writeFaceSets": instance.data.get("writeFaceSets", False)
}
if not instance.data.get("includeParentHierarchy", True):

View file

@ -28,6 +28,7 @@ class ExtractModel(openpype.api.Extractor):
hosts = ["maya"]
families = ["model"]
scene_type = "ma"
optional = True
def process(self, instance):
"""Plugin entry point."""

View file

@ -38,6 +38,7 @@ class ExtractAlembic(openpype.api.Extractor):
# Get extra export arguments
writeColorSets = instance.data.get("writeColorSets", False)
writeFaceSets = instance.data.get("writeFaceSets", False)
self.log.info("Extracting pointcache..")
dirname = self.staging_dir(instance)
@ -53,6 +54,7 @@ class ExtractAlembic(openpype.api.Extractor):
"writeVisibility": True,
"writeCreases": True,
"writeColorSets": writeColorSets,
"writeFaceSets": writeFaceSets,
"uvWrite": True,
"selection": True,
"worldSpace": instance.data.get("worldSpace", True)

View file

@ -52,9 +52,11 @@ from .vendor_bin_utils import (
)
from .python_module_tools import (
import_filepath,
modules_from_path,
recursive_bases_from_class,
classes_from_module
classes_from_module,
import_module_from_dirpath
)
from .avalon_context import (
@ -170,9 +172,11 @@ __all__ = [
"get_ffmpeg_tool_path",
"ffprobe_streams",
"import_filepath",
"modules_from_path",
"recursive_bases_from_class",
"classes_from_module",
"import_module_from_dirpath",
"CURRENT_DOC_SCHEMAS",
"PROJECT_NAME_ALLOWED_SYMBOLS",

View file

@ -1107,7 +1107,7 @@ def prepare_host_environments(data, implementation_envs=True):
asset_doc = data.get("asset_doc")
# Add tools environments
groups_by_name = {}
tool_by_group_name = collections.defaultdict(list)
tool_by_group_name = collections.defaultdict(dict)
if asset_doc:
# Make sure each tool group can be added only once
for key in asset_doc["data"].get("tools_env") or []:
@ -1115,12 +1115,14 @@ def prepare_host_environments(data, implementation_envs=True):
if not tool:
continue
groups_by_name[tool.group.name] = tool.group
tool_by_group_name[tool.group.name].append(tool)
tool_by_group_name[tool.group.name][tool.name] = tool
for group_name, group in groups_by_name.items():
for group_name in sorted(groups_by_name.keys()):
group = groups_by_name[group_name]
environments.append(group.environment)
added_env_keys.add(group_name)
for tool in tool_by_group_name[group_name]:
for tool_name in sorted(tool_by_group_name[group_name].keys()):
tool = tool_by_group_name[group_name][tool_name]
environments.append(tool.environment)
added_env_keys.add(tool.name)

View file

@ -9,6 +9,38 @@ log = logging.getLogger(__name__)
PY3 = sys.version_info[0] == 3
def import_filepath(filepath, module_name=None):
"""Import python file as python module.
Python 2 and Python 3 compatibility.
Args:
filepath(str): Path to python file.
module_name(str): Name of loaded module. Only for Python 3. By default
is filled with filename of filepath.
"""
if module_name is None:
module_name = os.path.splitext(os.path.basename(filepath))[0]
# Prepare module object where content of file will be parsed
module = types.ModuleType(module_name)
if PY3:
# Use loader so module has full specs
module_loader = importlib.machinery.SourceFileLoader(
module_name, filepath
)
module_loader.exec_module(module)
else:
# Execute module code and store content to module
with open(filepath) as _stream:
# Execute content and store it to module object
exec(_stream.read(), module.__dict__)
module.__file__ = filepath
return module
def modules_from_path(folder_path):
"""Get python scripts as modules from a path.
@ -55,23 +87,7 @@ def modules_from_path(folder_path):
continue
try:
# Prepare module object where content of file will be parsed
module = types.ModuleType(mod_name)
if PY3:
# Use loader so module has full specs
module_loader = importlib.machinery.SourceFileLoader(
mod_name, full_path
)
module_loader.exec_module(module)
else:
# Execute module code and store content to module
with open(full_path) as _stream:
# Execute content and store it to module object
exec(_stream.read(), module.__dict__)
module.__file__ = full_path
module = import_filepath(full_path, mod_name)
modules.append((full_path, module))
except Exception:
@ -127,3 +143,96 @@ def classes_from_module(superclass, module):
classes.append(obj)
return classes
def _import_module_from_dirpath_py2(dirpath, module_name, dst_module_name):
"""Import passed dirpath as python module using `imp`."""
if dst_module_name:
full_module_name = "{}.{}".format(dst_module_name, module_name)
dst_module = sys.modules[dst_module_name]
else:
full_module_name = module_name
dst_module = None
if full_module_name in sys.modules:
return sys.modules[full_module_name]
import imp
fp, pathname, description = imp.find_module(module_name, [dirpath])
module = imp.load_module(full_module_name, fp, pathname, description)
if dst_module is not None:
setattr(dst_module, module_name, module)
return module
def _import_module_from_dirpath_py3(dirpath, module_name, dst_module_name):
"""Import passed dirpath as python module using Python 3 modules."""
if dst_module_name:
full_module_name = "{}.{}".format(dst_module_name, module_name)
dst_module = sys.modules[dst_module_name]
else:
full_module_name = module_name
dst_module = None
# Skip import if is already imported
if full_module_name in sys.modules:
return sys.modules[full_module_name]
import importlib.util
from importlib._bootstrap_external import PathFinder
# Find loader for passed path and name
loader = PathFinder.find_module(full_module_name, [dirpath])
# Load specs of module
spec = importlib.util.spec_from_loader(
full_module_name, loader, origin=dirpath
)
# Create module based on specs
module = importlib.util.module_from_spec(spec)
# Store module to destination module and `sys.modules`
# WARNING this mus be done before module execution
if dst_module is not None:
setattr(dst_module, module_name, module)
sys.modules[full_module_name] = module
# Execute module import
loader.exec_module(module)
return module
def import_module_from_dirpath(dirpath, folder_name, dst_module_name=None):
"""Import passed directory as a python module.
Python 2 and 3 compatible.
Imported module can be assigned as a child attribute of already loaded
module from `sys.modules` if has support of `setattr`. That is not default
behavior of python modules so parent module must be a custom module with
that ability.
It is not possible to reimport already cached module. If you need to
reimport module you have to remove it from caches manually.
Args:
dirpath(str): Parent directory path of loaded folder.
folder_name(str): Folder name which should be imported inside passed
directory.
dst_module_name(str): Parent module name under which can be loaded
module added.
"""
if PY3:
module = _import_module_from_dirpath_py3(
dirpath, folder_name, dst_module_name
)
else:
module = _import_module_from_dirpath_py2(
dirpath, folder_name, dst_module_name
)
return module

View file

@ -1,7 +1,19 @@
# Pype modules
Pype modules should contain separated logic of specific kind of implementation, like Ftrack connection and usage code or Deadline farm rendering.
# OpenPype modules/addons
OpenPype modules should contain separated logic of specific kind of implementation, like Ftrack connection and usage code or Deadline farm rendering or may contain only special plugins. Addons work the same way currently there is no difference in module and addon.
## Base class `PypeModule`
## Modules concept
- modules and addons are dynamically imported to virtual python module `openpype_modules` from which it is possible to import them no matter where is the modulo located
- modules or addons should never be imported directly even if you know possible full import path
- it is because all of their content must be imported in specific order and should not be imported without defined functions as it may also break few implementation parts
### TODOs
- add module/addon manifest
- definition of module (not 100% defined content e.g. minimum require OpenPype version etc.)
- defying that folder is content of a module or an addon
- module/addon have it's settings schemas and default values outside OpenPype
- add general setting of paths to modules
## Base class `OpenPypeModule`
- abstract class as base for each module
- implementation should be module's api withou GUI parts
- may implement `get_global_environments` method which should return dictionary of environments that are globally appliable and value is the same for whole studio if launched at any workstation (except os specific paths)
@ -17,6 +29,16 @@ Pype modules should contain separated logic of specific kind of implementation,
- interface is class that has defined abstract methods to implement and may contain preimplemented helper methods
- module that inherit from an interface must implement those abstract methods otherwise won't be initialized
- it is easy to find which module object inherited from which interfaces withh 100% chance they have implemented required methods
- interfaces can be defined in `interfaces.py` inside module directory
- the file can't use relative imports or import anything from other parts
of module itself at the header of file
- this is one of reasons why modules/addons can't be imported directly without using defined functions in OpenPype modules implementation
## Base class `OpenPypeInterface`
- has nothing implemented
- has ABCMeta as metaclass
- is defined to be able find out classes which inherit from this base to be
able tell this is an Interface
## Global interfaces
- few interfaces are implemented for global usage
@ -70,7 +92,7 @@ Pype modules should contain separated logic of specific kind of implementation,
- Clockify has more inharitance it's class definition looks like
```
class ClockifyModule(
PypeModule, # Says it's Pype module so ModulesManager will try to initialize.
OpenPypeModule, # Says it's Pype module so ModulesManager will try to initialize.
ITrayModule, # Says has special implementation when used in tray.
IPluginPaths, # Says has plugin paths that want to register (paths to clockify actions for launcher).
IFtrackEventHandlerPaths, # Says has Ftrack actions/events for user/server.

View file

@ -1,86 +1,21 @@
# -*- coding: utf-8 -*-
from .base import (
PypeModule,
ITrayModule,
ITrayAction,
ITrayService,
IPluginPaths,
ILaunchHookPaths,
OpenPypeModule,
OpenPypeInterface,
load_modules,
ModulesManager,
TrayModulesManager
)
from .settings_action import (
SettingsAction,
ISettingsChangeListener,
LocalSettingsAction
)
from .webserver import (
WebServerModule,
IWebServerRoutes
)
from .idle_manager import (
IdleManager,
IIdleManager
)
from .timers_manager import (
TimersManager,
ITimersManager
)
from .avalon_apps import AvalonModule
from .launcher_action import LauncherAction
from .ftrack import (
FtrackModule,
IFtrackEventHandlerPaths
)
from .clockify import ClockifyModule
from .log_viewer import LogViewModule
from .muster import MusterModule
from .deadline import DeadlineModule
from .project_manager_action import ProjectManagerAction
from .standalonepublish_action import StandAlonePublishAction
from .python_console_interpreter import PythonInterpreterAction
from .sync_server import SyncServerModule
from .slack import SlackIntegrationModule
__all__ = (
"PypeModule",
"ITrayModule",
"ITrayAction",
"ITrayService",
"IPluginPaths",
"ILaunchHookPaths",
"OpenPypeModule",
"OpenPypeInterface",
"load_modules",
"ModulesManager",
"TrayModulesManager",
"SettingsAction",
"LocalSettingsAction",
"WebServerModule",
"IWebServerRoutes",
"IdleManager",
"IIdleManager",
"TimersManager",
"ITimersManager",
"AvalonModule",
"LauncherAction",
"FtrackModule",
"IFtrackEventHandlerPaths",
"ClockifyModule",
"IdleManager",
"LogViewModule",
"MusterModule",
"DeadlineModule",
"ProjectManagerAction",
"StandAlonePublishAction",
"PythonInterpreterAction",
"SyncServerModule",
"SlackIntegrationModule"
"TrayModulesManager"
)

View file

@ -1,8 +1,11 @@
# -*- coding: utf-8 -*-
"""Base class for Pype Modules."""
import os
import sys
import time
import inspect
import logging
import threading
import collections
from uuid import uuid4
from abc import ABCMeta, abstractmethod
@ -11,11 +14,305 @@ import six
import openpype
from openpype.settings import get_system_settings
from openpype.lib import PypeLogger
from openpype import resources
# Inherit from `object` for Python 2 hosts
class _ModuleClass(object):
"""Fake module class for storing OpenPype modules.
Object of this class can be stored to `sys.modules` and used for storing
dynamically imported modules.
"""
def __init__(self, name):
# Call setattr on super class
super(_ModuleClass, self).__setattr__("name", name)
# Where modules and interfaces are stored
super(_ModuleClass, self).__setattr__("__attributes__", dict())
super(_ModuleClass, self).__setattr__("__defaults__", set())
super(_ModuleClass, self).__setattr__("_log", None)
def __getattr__(self, attr_name):
if attr_name not in self.__attributes__:
if attr_name in ("__path__"):
return None
raise ImportError("No module named {}.{}".format(
self.name, attr_name
))
return self.__attributes__[attr_name]
def __iter__(self):
for module in self.values():
yield module
def __setattr__(self, attr_name, value):
if attr_name in self.__attributes__:
self.log.warning(
"Duplicated name \"{}\" in {}. Overriding.".format(
self.name, attr_name
)
)
self.__attributes__[attr_name] = value
def __setitem__(self, key, value):
self.__setattr__(key, value)
def __getitem__(self, key):
return getattr(self, key)
@property
def log(self):
if self._log is None:
super(_ModuleClass, self).__setattr__(
"_log", PypeLogger.get_logger(self.name)
)
return self._log
def get(self, key, default=None):
return self.__attributes__.get(key, default)
def keys(self):
return self.__attributes__.keys()
def values(self):
return self.__attributes__.values()
def items(self):
return self.__attributes__.items()
class _InterfacesClass(_ModuleClass):
"""Fake module class for storing OpenPype interfaces.
MissingInterface object is returned if interfaces does not exists.
- this is because interfaces must be available even if are missing
implementation
"""
def __getattr__(self, attr_name):
if attr_name not in self.__attributes__:
# Fake Interface if is not missing
self.__attributes__[attr_name] = type(
attr_name,
(MissingInteface, ),
{}
)
return self.__attributes__[attr_name]
class _LoadCache:
interfaces_lock = threading.Lock()
modules_lock = threading.Lock()
interfaces_loaded = False
modules_loaded = False
def get_default_modules_dir():
"""Path to default OpenPype modules."""
current_dir = os.path.abspath(os.path.dirname(__file__))
return os.path.join(current_dir, "default_modules")
def get_module_dirs():
"""List of paths where OpenPype modules can be found."""
dirpaths = [
get_default_modules_dir()
]
return dirpaths
def load_interfaces(force=False):
"""Load interfaces from modules into `openpype_interfaces`.
Only classes which inherit from `OpenPypeInterface` are loaded and stored.
Args:
force(bool): Force to load interfaces even if are already loaded.
This won't update already loaded and used (cached) interfaces.
"""
if _LoadCache.interfaces_loaded and not force:
return
if not _LoadCache.interfaces_lock.locked():
with _LoadCache.interfaces_lock:
_load_interfaces()
_LoadCache.interfaces_loaded = True
else:
# If lock is locked wait until is finished
while _LoadCache.interfaces_lock.locked():
time.sleep(0.1)
def _load_interfaces():
# Key under which will be modules imported in `sys.modules`
from openpype.lib import import_filepath
modules_key = "openpype_interfaces"
sys.modules[modules_key] = openpype_interfaces = (
_InterfacesClass(modules_key)
)
log = PypeLogger.get_logger("InterfacesLoader")
dirpaths = get_module_dirs()
interface_paths = []
interface_paths.append(
os.path.join(get_default_modules_dir(), "interfaces.py")
)
for dirpath in dirpaths:
for filename in os.listdir(dirpath):
if filename in ("__pycache__", ):
continue
full_path = os.path.join(dirpath, filename)
if not os.path.isdir(full_path):
continue
interfaces_path = os.path.join(full_path, "interfaces.py")
if os.path.exists(interfaces_path):
interface_paths.append(interfaces_path)
for full_path in interface_paths:
if not os.path.exists(full_path):
continue
try:
# Prepare module object where content of file will be parsed
module = import_filepath(full_path)
except Exception:
log.warning(
"Failed to load path: \"{0}\"".format(full_path),
exc_info=True
)
continue
for attr_name in dir(module):
attr = getattr(module, attr_name)
if (
not inspect.isclass(attr)
or attr is OpenPypeInterface
or not issubclass(attr, OpenPypeInterface)
):
continue
setattr(openpype_interfaces, attr_name, attr)
def load_modules(force=False):
"""Load OpenPype modules as python modules.
Modules does not load only classes (like in Interfaces) because there must
be ability to use inner code of module and be able to import it from one
defined place.
With this it is possible to import module's content from predefined module.
Function makes sure that `load_interfaces` was triggered. Modules import
has specific order which can't be changed.
Args:
force(bool): Force to load modules even if are already loaded.
This won't update already loaded and used (cached) modules.
"""
if _LoadCache.modules_loaded and not force:
return
# First load interfaces
# - modules must not be imported before interfaces
load_interfaces(force)
if not _LoadCache.modules_lock.locked():
with _LoadCache.modules_lock:
_load_modules()
_LoadCache.modules_loaded = True
else:
# If lock is locked wait until is finished
while _LoadCache.modules_lock.locked():
time.sleep(0.1)
def _load_modules():
# Import helper functions from lib
from openpype.lib import (
import_filepath,
import_module_from_dirpath
)
# Key under which will be modules imported in `sys.modules`
modules_key = "openpype_modules"
# Change `sys.modules`
sys.modules[modules_key] = openpype_modules = _ModuleClass(modules_key)
log = PypeLogger.get_logger("ModulesLoader")
# Look for OpenPype modules in paths defined with `get_module_dirs`
dirpaths = get_module_dirs()
for dirpath in dirpaths:
if not os.path.exists(dirpath):
log.warning((
"Could not find path when loading OpenPype modules \"{}\""
).format(dirpath))
continue
for filename in os.listdir(dirpath):
# Ignore filenames
if filename in ("__pycache__", ):
continue
fullpath = os.path.join(dirpath, filename)
basename, ext = os.path.splitext(filename)
# TODO add more logic how to define if folder is module or not
# - check manifest and content of manifest
if os.path.isdir(fullpath):
import_module_from_dirpath(dirpath, filename, modules_key)
elif ext in (".py", ):
module = import_filepath(fullpath)
setattr(openpype_modules, basename, module)
class _OpenPypeInterfaceMeta(ABCMeta):
"""OpenPypeInterface meta class to print proper string."""
def __str__(self):
return "<'OpenPypeInterface.{}'>".format(self.__name__)
def __repr__(self):
return str(self)
@six.add_metaclass(_OpenPypeInterfaceMeta)
class OpenPypeInterface:
"""Base class of Interface that can be used as Mixin with abstract parts.
This is way how OpenPype module or addon can tell that has implementation
for specific part or for other module/addon.
Child classes of OpenPypeInterface may be used as mixin in different
OpenPype modules which means they have to have implemented methods defined
in the interface. By default interface does not have any abstract parts.
"""
pass
class MissingInteface(OpenPypeInterface):
"""Class representing missing interface class.
Used when interface is not available from currently registered paths.
"""
pass
@six.add_metaclass(ABCMeta)
class PypeModule:
class OpenPypeModule:
"""Base class of pype module.
Attributes:
@ -38,7 +335,7 @@ class PypeModule:
def __init__(self, manager, settings):
self.manager = manager
self.log = PypeLogger().get_logger(self.name)
self.log = PypeLogger.get_logger(self.name)
self.initialize(settings)
@ -70,265 +367,8 @@ 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 ILaunchHookPaths:
"""Module has launch hook paths to return.
Expected result is list of paths.
["path/to/launch_hooks_dir"]
"""
@abstractmethod
def get_launch_hook_paths(self):
pass
@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
_tray_manager = None
@abstractmethod
def tray_init(self):
"""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
def execute_in_main_thread(self, callback):
""" Pushes callback to the queue or process 'callback' on a main thread
Some callbacks need to be processed on main thread (menu actions
must be added on main thread or they won't get triggered etc.)
"""
# called without initialized tray, still main thread needed
if not self.tray_initialized:
try:
callback = self._main_thread_callbacks.popleft()
callback()
except:
self.log.warning(
"Failed to execute {} in main thread".format(callback),
exc_info=True)
return
self.manager.tray_manager.execute_in_main_thread(callback)
def show_tray_message(self, title, message, icon=None, msecs=None):
"""Show tray message.
Args:
title (str): Title of message.
message (str): Content of message.
icon (QSystemTrayIcon.MessageIcon): Message's icon. Default is
Information icon, may differ by Qt version.
msecs (int): Duration of message visibility in miliseconds.
Default is 10000 msecs, may differ by Qt version.
"""
if self._tray_manager:
self._tray_manager.show_tray_message(title, message, icon, msecs)
def add_doubleclick_callback(self, callback):
if hasattr(self.manager, "add_doubleclick_callback"):
self.manager.add_doubleclick_callback(self, callback)
class ITrayAction(ITrayModule):
"""Implementation of Tray action.
Add action to tray menu which will trigger `on_action_trigger`.
It is expected to be used for showing tools.
Methods `tray_start`, `tray_exit` and `connect_with_modules` are overriden
as it's not expected that action will use them. But it is possible if
necessary.
"""
admin_action = False
_admin_submenu = None
@property
@abstractmethod
def label(self):
"""Service label showed in menu."""
pass
@abstractmethod
def on_action_trigger(self):
"""What happens on actions click."""
pass
def tray_menu(self, tray_menu):
from Qt import QtWidgets
if self.admin_action:
menu = self.admin_submenu(tray_menu)
action = QtWidgets.QAction(self.label, menu)
menu.addAction(action)
if not menu.menuAction().isVisible():
menu.menuAction().setVisible(True)
else:
action = QtWidgets.QAction(self.label, tray_menu)
tray_menu.addAction(action)
action.triggered.connect(self.on_action_trigger)
def tray_start(self):
return
def tray_exit(self):
return
@staticmethod
def admin_submenu(tray_menu):
if ITrayAction._admin_submenu is None:
from Qt import QtWidgets
admin_submenu = QtWidgets.QMenu("Admin", tray_menu)
admin_submenu.menuAction().setVisible(False)
ITrayAction._admin_submenu = admin_submenu
return ITrayAction._admin_submenu
class ITrayService(ITrayModule):
# Module's property
menu_action = None
# Class properties
_services_submenu = None
_icon_failed = None
_icon_running = None
_icon_idle = None
@property
@abstractmethod
def label(self):
"""Service label showed in menu."""
pass
# TODO be able to get any sort of information to show/print
# @abstractmethod
# def get_service_info(self):
# pass
@staticmethod
def services_submenu(tray_menu):
if ITrayService._services_submenu is None:
from Qt import QtWidgets
services_submenu = QtWidgets.QMenu("Services", tray_menu)
services_submenu.menuAction().setVisible(False)
ITrayService._services_submenu = services_submenu
return ITrayService._services_submenu
@staticmethod
def add_service_action(action):
ITrayService._services_submenu.addAction(action)
if not ITrayService._services_submenu.menuAction().isVisible():
ITrayService._services_submenu.menuAction().setVisible(True)
@staticmethod
def _load_service_icons():
from Qt import QtGui
ITrayService._failed_icon = QtGui.QIcon(
resources.get_resource("icons", "circle_red.png")
)
ITrayService._icon_running = QtGui.QIcon(
resources.get_resource("icons", "circle_green.png")
)
ITrayService._icon_idle = QtGui.QIcon(
resources.get_resource("icons", "circle_orange.png")
)
@staticmethod
def get_icon_running():
if ITrayService._icon_running is None:
ITrayService._load_service_icons()
return ITrayService._icon_running
@staticmethod
def get_icon_idle():
if ITrayService._icon_idle is None:
ITrayService._load_service_icons()
return ITrayService._icon_idle
@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
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):
"""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())
class OpenPypeAddOn(OpenPypeModule):
pass
class ModulesManager:
@ -357,6 +397,11 @@ class ModulesManager:
def initialize_modules(self):
"""Import and initialize modules."""
# Make sure modules are loaded
load_modules()
import openpype_modules
self.log.debug("*** Pype modules initialization.")
# Prepare settings for modules
system_settings = getattr(self, "_system_settings", None)
@ -368,33 +413,43 @@ class ModulesManager:
time_start = time.time()
prev_start_time = time_start
# Go through globals in `pype.modules`
for name in dir(openpype.modules):
modules_item = getattr(openpype.modules, name, None)
# Filter globals that are not classes which inherit from PypeModule
if (
not inspect.isclass(modules_item)
or modules_item is openpype.modules.PypeModule
or not issubclass(modules_item, openpype.modules.PypeModule)
):
continue
module_classes = []
for module in openpype_modules:
# Go through globals in `pype.modules`
for name in dir(module):
modules_item = getattr(module, name, None)
# Filter globals that are not classes which inherit from
# OpenPypeModule
if (
not inspect.isclass(modules_item)
or modules_item is OpenPypeModule
or not issubclass(modules_item, OpenPypeModule)
):
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)
# 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)
abs_method = getattr(
attr, "__isabstractmethod__", None
)
if attr and abs_method:
not_implemented.append(attr_name)
# Log missing implementations
self.log.warning((
"Skipping abstract Class: {}. Missing implementations: {}"
).format(name, ", ".join(not_implemented)))
continue
# Log missing implementations
self.log.warning((
"Skipping abstract Class: {}."
" Missing implementations: {}"
).format(name, ", ".join(not_implemented)))
continue
module_classes.append(modules_item)
for modules_item in module_classes:
try:
name = modules_item.__name__
# Try initialize module
module = modules_item(self, modules_settings)
# Store initialized object
@ -492,6 +547,8 @@ class ModulesManager:
and "actions" each containing list of paths.
"""
# Output structure
from openpype_interfaces import IPluginPaths
output = {
"publish": [],
"create": [],
@ -544,6 +601,8 @@ class ModulesManager:
Returns:
list: Paths to launch hook directories.
"""
from openpype_interfaces import ILaunchHookPaths
str_type = type("")
expected_types = (list, tuple, set)
@ -711,6 +770,7 @@ class TrayModulesManager(ModulesManager):
self.modules_by_id = {}
self.modules_by_name = {}
self._report = {}
self.tray_manager = None
self.doubleclick_callbacks = {}
@ -743,6 +803,8 @@ class TrayModulesManager(ModulesManager):
self.tray_menu(tray_menu)
def get_enabled_tray_modules(self):
from openpype_interfaces import ITrayModule
output = []
for module in self.modules:
if module.enabled and isinstance(module, ITrayModule):
@ -818,6 +880,8 @@ class TrayModulesManager(ModulesManager):
self._report["Tray menu"] = report
def start_modules(self):
from openpype_interfaces import ITrayService
report = {}
time_start = time.time()
prev_start_time = time_start

View file

@ -1,14 +1,14 @@
import os
import openpype
from openpype import resources
from .. import (
PypeModule,
from openpype.modules import OpenPypeModule
from openpype_interfaces import (
ITrayModule,
IWebServerRoutes
)
class AvalonModule(PypeModule, ITrayModule, IWebServerRoutes):
class AvalonModule(OpenPypeModule, ITrayModule, IWebServerRoutes):
name = "avalon"
def initialize(self, modules_settings):

View file

@ -1,16 +1,13 @@
import os
import re
import json
import datetime
import bson
from bson.objectid import ObjectId
import bson.json_util
from aiohttp.web_response import Response
from avalon.api import AvalonMongoDB
from openpype.modules.webserver.base_routes import RestApiEndpoint
from openpype_modules.webserver.base_routes import RestApiEndpoint
class _RestApiEndpoint(RestApiEndpoint):

View file

@ -7,8 +7,8 @@ from .constants import (
CLOCKIFY_FTRACK_USER_PATH,
CLOCKIFY_FTRACK_SERVER_PATH
)
from openpype.modules import (
PypeModule,
from openpype.modules import OpenPypeModule
from openpype_interfaces import (
ITrayModule,
IPluginPaths,
IFtrackEventHandlerPaths,
@ -17,7 +17,7 @@ from openpype.modules import (
class ClockifyModule(
PypeModule,
OpenPypeModule,
ITrayModule,
IPluginPaths,
IFtrackEventHandlerPaths,

View file

@ -1,7 +1,7 @@
import os
import json
from openpype.modules.ftrack.lib import ServerAction
from openpype.modules.clockify.clockify_api import ClockifyAPI
from openpype_modules.ftrack.lib import ServerAction
from openpype_modules.clockify.clockify_api import ClockifyAPI
class SyncClocifyServer(ServerAction):

View file

@ -1,6 +1,6 @@
import json
from openpype.modules.ftrack.lib import BaseAction, statics_icon
from openpype.modules.clockify.clockify_api import ClockifyAPI
from openpype_modules.ftrack.lib import BaseAction, statics_icon
from openpype_modules.clockify.clockify_api import ClockifyAPI
class SyncClocifyLocal(BaseAction):

View file

@ -1,6 +1,6 @@
from avalon import api, io
from openpype.api import Logger
from openpype.modules.clockify.clockify_api import ClockifyAPI
from openpype_modules.clockify.clockify_api import ClockifyAPI
log = Logger().get_logger(__name__)

View file

@ -1,5 +1,5 @@
from avalon import api, io
from openpype.modules.clockify.clockify_api import ClockifyAPI
from openpype_modules.clockify.clockify_api import ClockifyAPI
from openpype.api import Logger
log = Logger().get_logger(__name__)

View file

@ -1,9 +1,9 @@
import os
from openpype.modules import (
PypeModule, IPluginPaths)
from openpype.modules import OpenPypeModule
from openpype_interfaces import IPluginPaths
class DeadlineModule(PypeModule, IPluginPaths):
class DeadlineModule(OpenPypeModule, IPluginPaths):
name = "deadline"
def __init__(self, manager, settings):

View file

@ -1,11 +1,9 @@
from .ftrack_module import (
FtrackModule,
IFtrackEventHandlerPaths,
FTRACK_MODULE_DIR
)
__all__ = (
"FtrackModule",
"IFtrackEventHandlerPaths",
"FTRACK_MODULE_DIR"
)

View file

@ -1,6 +1,6 @@
import json
from openpype.modules.ftrack.lib import ServerAction
from openpype_modules.ftrack.lib import ServerAction
def clone_review_session(session, entity):

View file

@ -1,4 +1,4 @@
from openpype.modules.ftrack.lib import ServerAction
from openpype_modules.ftrack.lib import ServerAction
class MultipleNotesServer(ServerAction):

View file

@ -4,7 +4,7 @@ from avalon.api import AvalonMongoDB
from openpype.api import ProjectSettings
from openpype.lib import create_project
from openpype.modules.ftrack.lib import (
from openpype_modules.ftrack.lib import (
ServerAction,
get_openpype_attr,
CUST_ATTR_AUTO_SYNC

View file

@ -1,4 +1,4 @@
from openpype.modules.ftrack.lib import ServerAction
from openpype_modules.ftrack.lib import ServerAction
class PrivateProjectDetectionAction(ServerAction):

View file

@ -2,7 +2,7 @@ import sys
import json
import collections
import ftrack_api
from openpype.modules.ftrack.lib import ServerAction
from openpype_modules.ftrack.lib import ServerAction
class PushHierValuesToNonHier(ServerAction):

View file

@ -1,8 +1,8 @@
import time
import traceback
from openpype.modules.ftrack.lib import ServerAction
from openpype.modules.ftrack.lib.avalon_sync import SyncEntitiesFactory
from openpype_modules.ftrack.lib import ServerAction
from openpype_modules.ftrack.lib.avalon_sync import SyncEntitiesFactory
class SyncToAvalonServer(ServerAction):

View file

@ -1,6 +1,6 @@
from openpype.modules.ftrack.lib import BaseEvent
from openpype.modules.ftrack.lib.avalon_sync import CUST_ATTR_ID_KEY
from openpype.modules.ftrack.event_handlers_server.event_sync_to_avalon import (
from openpype_modules.ftrack.lib import BaseEvent
from openpype_modules.ftrack.lib.avalon_sync import CUST_ATTR_ID_KEY
from openpype_modules.ftrack.event_handlers_server.event_sync_to_avalon import (
SyncToAvalonEvent
)

View file

@ -1,4 +1,4 @@
from openpype.modules.ftrack.lib import BaseEvent
from openpype_modules.ftrack.lib import BaseEvent
class FirstVersionStatus(BaseEvent):

View file

@ -1,5 +1,5 @@
import collections
from openpype.modules.ftrack.lib import BaseEvent
from openpype_modules.ftrack.lib import BaseEvent
class NextTaskUpdate(BaseEvent):

View file

@ -2,7 +2,7 @@ import collections
import datetime
import ftrack_api
from openpype.modules.ftrack.lib import (
from openpype_modules.ftrack.lib import (
BaseEvent,
query_custom_attributes
)

View file

@ -1,5 +1,5 @@
import ftrack_api
from openpype.modules.ftrack.lib import BaseEvent
from openpype_modules.ftrack.lib import BaseEvent
class RadioButtons(BaseEvent):

View file

@ -17,7 +17,7 @@ import ftrack_api
from avalon import schema
from avalon.api import AvalonMongoDB
from openpype.modules.ftrack.lib import (
from openpype_modules.ftrack.lib import (
get_openpype_attr,
CUST_ATTR_ID_KEY,
CUST_ATTR_AUTO_SYNC,

View file

@ -1,5 +1,5 @@
import collections
from openpype.modules.ftrack.lib import BaseEvent
from openpype_modules.ftrack.lib import BaseEvent
class TaskStatusToParent(BaseEvent):

View file

@ -1,5 +1,5 @@
import collections
from openpype.modules.ftrack.lib import BaseEvent
from openpype_modules.ftrack.lib import BaseEvent
class TaskToVersionStatus(BaseEvent):

View file

@ -1,5 +1,5 @@
import collections
from openpype.modules.ftrack.lib import BaseEvent
from openpype_modules.ftrack.lib import BaseEvent
class ThumbnailEvents(BaseEvent):

View file

@ -2,8 +2,8 @@ import os
import re
import subprocess
from openpype.modules.ftrack.lib import BaseEvent
from openpype.modules.ftrack.lib.avalon_sync import CUST_ATTR_ID_KEY
from openpype_modules.ftrack.lib import BaseEvent
from openpype_modules.ftrack.lib.avalon_sync import CUST_ATTR_ID_KEY
from avalon.api import AvalonMongoDB
from bson.objectid import ObjectId

View file

@ -1,4 +1,4 @@
from openpype.modules.ftrack.lib import BaseEvent
from openpype_modules.ftrack.lib import BaseEvent
class VersionToTaskStatus(BaseEvent):

View file

@ -1,7 +1,7 @@
import os
from uuid import uuid4
from openpype.modules.ftrack.lib import BaseAction
from openpype_modules.ftrack.lib import BaseAction
from openpype.lib import (
ApplicationManager,
ApplicationLaunchFailed,

View file

@ -2,7 +2,7 @@
Taken from https://github.com/tokejepsen/ftrack-hooks/tree/master/batch_tasks
"""
from openpype.modules.ftrack.lib import BaseAction, statics_icon
from openpype_modules.ftrack.lib import BaseAction, statics_icon
class BatchTasksAction(BaseAction):

View file

@ -1,6 +1,6 @@
import collections
import ftrack_api
from openpype.modules.ftrack.lib import (
from openpype_modules.ftrack.lib import (
BaseAction,
statics_icon,
get_openpype_attr

View file

@ -1,4 +1,4 @@
from openpype.modules.ftrack.lib import BaseAction, statics_icon
from openpype_modules.ftrack.lib import BaseAction, statics_icon
try:
from functools import cmp_to_key
except Exception:

View file

@ -1,7 +1,7 @@
import os
import sys
import subprocess
from openpype.modules.ftrack.lib import BaseAction, statics_icon
from openpype_modules.ftrack.lib import BaseAction, statics_icon
class ComponentOpen(BaseAction):

View file

@ -2,7 +2,7 @@ import collections
import json
import arrow
import ftrack_api
from openpype.modules.ftrack.lib import (
from openpype_modules.ftrack.lib import (
BaseAction,
statics_icon,
@ -43,7 +43,7 @@ dictionary level, task's attributes are nested more.
group (string)
- name of group
- based on attribute `openpype.modules.ftrack.lib.CUST_ATTR_GROUP`
- based on attribute `openpype_modules.ftrack.lib.CUST_ATTR_GROUP`
- "pype" by default
*** Required ***************************************************************

View file

@ -1,5 +1,5 @@
import os
from openpype.modules.ftrack.lib import BaseAction, statics_icon
from openpype_modules.ftrack.lib import BaseAction, statics_icon
from avalon import lib as avalonlib
from openpype.api import (
Anatomy,

View file

@ -2,7 +2,7 @@ import os
import re
import json
from openpype.modules.ftrack.lib import BaseAction, statics_icon
from openpype_modules.ftrack.lib import BaseAction, statics_icon
from openpype.api import Anatomy, get_project_settings

View file

@ -4,7 +4,7 @@ from datetime import datetime
from queue import Queue
from bson.objectid import ObjectId
from openpype.modules.ftrack.lib import BaseAction, statics_icon
from openpype_modules.ftrack.lib import BaseAction, statics_icon
from avalon.api import AvalonMongoDB

View file

@ -5,7 +5,7 @@ import uuid
import clique
from pymongo import UpdateOne
from openpype.modules.ftrack.lib import BaseAction, statics_icon
from openpype_modules.ftrack.lib import BaseAction, statics_icon
from avalon.api import AvalonMongoDB
from openpype.api import Anatomy

View file

@ -6,8 +6,8 @@ import collections
from bson.objectid import ObjectId
from openpype.api import Anatomy, config
from openpype.modules.ftrack.lib import BaseAction, statics_icon
from openpype.modules.ftrack.lib.avalon_sync import CUST_ATTR_ID_KEY
from openpype_modules.ftrack.lib import BaseAction, statics_icon
from openpype_modules.ftrack.lib.avalon_sync import CUST_ATTR_ID_KEY
from openpype.lib.delivery import (
path_from_representation,
get_format_dict,

View file

@ -1,7 +1,7 @@
import os
import subprocess
from operator import itemgetter
from openpype.modules.ftrack.lib import BaseAction, statics_icon
from openpype_modules.ftrack.lib import BaseAction, statics_icon
class DJVViewAction(BaseAction):

View file

@ -1,5 +1,5 @@
import json
from openpype.modules.ftrack.lib import BaseAction, statics_icon
from openpype_modules.ftrack.lib import BaseAction, statics_icon
class JobKiller(BaseAction):

View file

@ -1,4 +1,4 @@
from openpype.modules.ftrack.lib import BaseAction, statics_icon
from openpype_modules.ftrack.lib import BaseAction, statics_icon
class MultipleNotes(BaseAction):

View file

@ -4,7 +4,7 @@ from avalon.api import AvalonMongoDB
from openpype.api import ProjectSettings
from openpype.lib import create_project
from openpype.modules.ftrack.lib import (
from openpype_modules.ftrack.lib import (
BaseAction,
statics_icon,
get_openpype_attr,

View file

@ -3,7 +3,7 @@ import subprocess
import traceback
import json
from openpype.modules.ftrack.lib import BaseAction, statics_icon
from openpype_modules.ftrack.lib import BaseAction, statics_icon
import ftrack_api
from avalon import io, api

View file

@ -1,6 +1,6 @@
import os
from operator import itemgetter
from openpype.modules.ftrack.lib import BaseAction, statics_icon
from openpype_modules.ftrack.lib import BaseAction, statics_icon
class SeedDebugProject(BaseAction):

View file

@ -4,11 +4,11 @@ import json
import requests
from bson.objectid import ObjectId
from openpype.modules.ftrack.lib import BaseAction, statics_icon
from openpype_modules.ftrack.lib import BaseAction, statics_icon
from openpype.api import Anatomy
from avalon.api import AvalonMongoDB
from openpype.modules.ftrack.lib.avalon_sync import CUST_ATTR_ID_KEY
from openpype_modules.ftrack.lib.avalon_sync import CUST_ATTR_ID_KEY
class StoreThumbnailsToAvalon(BaseAction):

View file

@ -1,8 +1,8 @@
import time
import traceback
from openpype.modules.ftrack.lib import BaseAction, statics_icon
from openpype.modules.ftrack.lib.avalon_sync import SyncEntitiesFactory
from openpype_modules.ftrack.lib import BaseAction, statics_icon
from openpype_modules.ftrack.lib.avalon_sync import SyncEntitiesFactory
class SyncToAvalonLocal(BaseAction):

View file

@ -1,4 +1,4 @@
from openpype.modules.ftrack.lib import BaseAction, statics_icon
from openpype_modules.ftrack.lib import BaseAction, statics_icon
class TestAction(BaseAction):

View file

@ -1,5 +1,5 @@
import json
from openpype.modules.ftrack.lib import BaseAction, statics_icon
from openpype_modules.ftrack.lib import BaseAction, statics_icon
class ThumbToChildren(BaseAction):

View file

@ -1,5 +1,5 @@
import json
from openpype.modules.ftrack.lib import BaseAction, statics_icon
from openpype_modules.ftrack.lib import BaseAction, statics_icon
class ThumbToParent(BaseAction):

View file

@ -2,7 +2,7 @@ import platform
import socket
import getpass
from openpype.modules.ftrack.lib import BaseAction, statics_icon
from openpype_modules.ftrack.lib import BaseAction
class ActionWhereIRun(BaseAction):

View file

@ -1,35 +1,24 @@
import os
import json
import collections
from abc import ABCMeta, abstractmethod
import six
import openpype
from openpype.modules import (
PypeModule,
from openpype.modules import OpenPypeModule
from openpype_interfaces import (
ITrayModule,
IPluginPaths,
ITimersManager,
ILaunchHookPaths,
ISettingsChangeListener
ISettingsChangeListener,
IFtrackEventHandlerPaths
)
from openpype.settings import SaveWarningExc
FTRACK_MODULE_DIR = os.path.dirname(os.path.abspath(__file__))
@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,
OpenPypeModule,
ITrayModule,
IPluginPaths,
ITimersManager,
@ -242,7 +231,7 @@ class FtrackModule(
return
import ftrack_api
from openpype.modules.ftrack.lib import get_openpype_attr
from openpype_modules.ftrack.lib import get_openpype_attr
try:
session = self.create_ftrack_session()

View file

@ -18,17 +18,10 @@ from openpype.lib import (
get_pype_execute_args,
OpenPypeMongoConnection
)
from openpype.modules.ftrack import FTRACK_MODULE_DIR
from openpype.modules.ftrack.lib import (
credentials,
get_ftrack_url_from_settings
)
from openpype.modules.ftrack.ftrack_server.lib import (
check_ftrack_url,
get_ftrack_event_mongo_info
)
from openpype.modules.ftrack.ftrack_server import socket_thread
from openpype_modules.ftrack import FTRACK_MODULE_DIR
from openpype_modules.ftrack.lib import credentials
from openpype_modules.ftrack.ftrack_server.lib import check_ftrack_url
from openpype_modules.ftrack.ftrack_server import socket_thread
class MongoPermissionsError(Exception):

View file

@ -22,7 +22,7 @@ try:
from weakref import WeakMethod
except ImportError:
from ftrack_api._weakref import WeakMethod
from openpype.modules.ftrack.lib import get_ftrack_event_mongo_info
from openpype_modules.ftrack.lib import get_ftrack_event_mongo_info
from openpype.lib import OpenPypeMongoConnection
from openpype.api import Logger

View file

@ -0,0 +1,12 @@
from abc import abstractmethod
from openpype.modules import OpenPypeInterface
class IFtrackEventHandlerPaths(OpenPypeInterface):
"""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

View file

@ -1,6 +1,6 @@
import os
from openpype.lib import PreLaunchHook
from openpype.modules.ftrack import FTRACK_MODULE_DIR
from openpype_modules.ftrack import FTRACK_MODULE_DIR
class PrePython2Support(PreLaunchHook):

View file

@ -10,7 +10,7 @@ from openpype.api import Logger
from openpype.settings import get_project_settings
import ftrack_api
from openpype.modules.ftrack import ftrack_server
from openpype_modules.ftrack import ftrack_server
class MissingPermision(Exception):

Some files were not shown because too many files have changed in this diff Show more