mirror of
https://github.com/ynput/ayon-core.git
synced 2025-12-24 21:04:40 +01:00
Merge branch 'release/2.2.0'
This commit is contained in:
commit
bb51000871
7 changed files with 177 additions and 9 deletions
27
changelog.md
27
changelog.md
|
|
@ -1,7 +1,32 @@
|
||||||
# Pype changelog #
|
# Pype changelog #
|
||||||
Welcome to pype changelog
|
Welcome to pype changelog
|
||||||
|
|
||||||
## 2.1 ##
|
## 2.2.0 ##
|
||||||
|
_release date: 8 Sept 2019_
|
||||||
|
|
||||||
|
**new**:
|
||||||
|
- _(pype)_ add customisable workflow for creating quicktimes from renders or playblasts
|
||||||
|
- _(nuke)_ option to choose deadline chunk size on write nodes
|
||||||
|
- _(nukestudio)_ added option to publish soft effects (subTrackItems) from NukeStudio as subsets including LUT files. these can then be loaded in nuke or NukeStudio
|
||||||
|
- _(nuke)_ option to build nuke script from previously published latest versions of plate and render subsets.
|
||||||
|
- _(nuke)_ nuke writes now have deadline tab.
|
||||||
|
- _(ftrack)_ Prepare Project action can now be used for creating the base folder structure on disk and in ftrack, setting up all the initial project attributes and it automatically prepares `pype_project_config` folder for the given project.
|
||||||
|
- _(clockify)_ Added support for time tracking in clockify. This currently in addition to ftrack time logs, but does not completely replace them.
|
||||||
|
- _(pype)_ any attributes in Creator and Loader plugins can now be customised using pype preset system
|
||||||
|
|
||||||
|
**changed**:
|
||||||
|
- nukestudio now uses workio API for workfiles
|
||||||
|
- _(maya)_ "FIX FPS" prompt in maya now appears in the middle of the screen
|
||||||
|
- _(muster)_ can now be configured with custom templates
|
||||||
|
- _(pype)_ global publishing plugins can now be configured using presets as well as host specific ones
|
||||||
|
|
||||||
|
|
||||||
|
**fix**:
|
||||||
|
- wrong version retrieval from path in certain scenarios
|
||||||
|
- nuke reset resolution wasn't working in certain scenarios
|
||||||
|
|
||||||
|
## 2.1.0 ##
|
||||||
|
_release date: 6 Aug 2019_
|
||||||
|
|
||||||
A large cleanup release. Most of the change are under the hood.
|
A large cleanup release. Most of the change are under the hood.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,8 @@ import os
|
||||||
from pyblish import api as pyblish
|
from pyblish import api as pyblish
|
||||||
from avalon import api as avalon
|
from avalon import api as avalon
|
||||||
from .lib import filter_pyblish_plugins
|
from .lib import filter_pyblish_plugins
|
||||||
|
from pypeapp import config
|
||||||
|
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
@ -16,6 +18,51 @@ PLUGINS_DIR = os.path.join(PACKAGE_DIR, "plugins")
|
||||||
PUBLISH_PATH = os.path.join(PLUGINS_DIR, "global", "publish")
|
PUBLISH_PATH = os.path.join(PLUGINS_DIR, "global", "publish")
|
||||||
LOAD_PATH = os.path.join(PLUGINS_DIR, "global", "load")
|
LOAD_PATH = os.path.join(PLUGINS_DIR, "global", "load")
|
||||||
|
|
||||||
|
# we are monkey patching `avalon.api.discover()` to allow us to load
|
||||||
|
# plugin presets on plugins being discovered by avalon. Little bit of
|
||||||
|
# hacking, but it allows us to add out own features without need
|
||||||
|
# to modify upstream code.
|
||||||
|
|
||||||
|
_original_discover = avalon.discover
|
||||||
|
|
||||||
|
|
||||||
|
def patched_discover(superclass):
|
||||||
|
"""
|
||||||
|
Monkey patched version of :func:`avalon.api.discover()`. It allows
|
||||||
|
us to load presets on plugins being discovered.
|
||||||
|
"""
|
||||||
|
# run original discover and get plugins
|
||||||
|
plugins = _original_discover(superclass)
|
||||||
|
|
||||||
|
# determine host application to use for finding presets
|
||||||
|
host = avalon.registered_host().__name__.split(".")[-1]
|
||||||
|
|
||||||
|
# map plugin superclass to preset json. Currenly suppoted is load and
|
||||||
|
# create (avalon.api.Loader and avalon.api.Creator)
|
||||||
|
plugin_type = "undefined"
|
||||||
|
if superclass.__name__.split(".")[-1] == "Loader":
|
||||||
|
plugin_type = "load"
|
||||||
|
elif superclass.__name__.split(".")[-1] == "Creator":
|
||||||
|
plugin_type = "create"
|
||||||
|
|
||||||
|
print(">>> trying to find presets for {}:{} ...".format(host, plugin_type))
|
||||||
|
try:
|
||||||
|
config_data = config.get_presets()['plugins'][host][plugin_type]
|
||||||
|
except KeyError:
|
||||||
|
print("*** no presets found.")
|
||||||
|
else:
|
||||||
|
for plugin in plugins:
|
||||||
|
if plugin.__name__ in config_data:
|
||||||
|
print(">>> We have preset for {}".format(plugin.__name__))
|
||||||
|
for option, value in config_data[plugin.__name__].items():
|
||||||
|
if option == "enabled" and value is False:
|
||||||
|
setattr(plugin, "active", False)
|
||||||
|
print(" - is disabled by preset")
|
||||||
|
else:
|
||||||
|
setattr(plugin, option, value)
|
||||||
|
print(" - setting `{}`: `{}`".format(option, value))
|
||||||
|
return plugins
|
||||||
|
|
||||||
|
|
||||||
def install():
|
def install():
|
||||||
log.info("Registering global plug-ins..")
|
log.info("Registering global plug-ins..")
|
||||||
|
|
@ -23,6 +70,9 @@ def install():
|
||||||
pyblish.register_discovery_filter(filter_pyblish_plugins)
|
pyblish.register_discovery_filter(filter_pyblish_plugins)
|
||||||
avalon.register_plugin_path(avalon.Loader, LOAD_PATH)
|
avalon.register_plugin_path(avalon.Loader, LOAD_PATH)
|
||||||
|
|
||||||
|
# apply monkey patched discover to original one
|
||||||
|
avalon.discover = patched_discover
|
||||||
|
|
||||||
|
|
||||||
def uninstall():
|
def uninstall():
|
||||||
log.info("Deregistering global plug-ins..")
|
log.info("Deregistering global plug-ins..")
|
||||||
|
|
@ -30,3 +80,6 @@ def uninstall():
|
||||||
pyblish.deregister_discovery_filter(filter_pyblish_plugins)
|
pyblish.deregister_discovery_filter(filter_pyblish_plugins)
|
||||||
avalon.deregister_plugin_path(avalon.Loader, LOAD_PATH)
|
avalon.deregister_plugin_path(avalon.Loader, LOAD_PATH)
|
||||||
log.info("Global plug-ins unregistred")
|
log.info("Global plug-ins unregistred")
|
||||||
|
|
||||||
|
# restore original discover
|
||||||
|
avalon.discover = _original_discover
|
||||||
|
|
|
||||||
|
|
@ -194,7 +194,7 @@ class ClockifyModule:
|
||||||
self.message_widget = MessageWidget(
|
self.message_widget = MessageWidget(
|
||||||
self.main_parent, msg, "Clockify - Info Message"
|
self.main_parent, msg, "Clockify - Info Message"
|
||||||
)
|
)
|
||||||
self.message_widget.closed.connect(self.message_widget)
|
self.message_widget.closed.connect(self.on_message_widget_close)
|
||||||
self.message_widget.show()
|
self.message_widget.show()
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
|
||||||
19
pype/lib.py
19
pype/lib.py
|
|
@ -5,6 +5,7 @@ import importlib
|
||||||
import itertools
|
import itertools
|
||||||
import contextlib
|
import contextlib
|
||||||
import subprocess
|
import subprocess
|
||||||
|
import inspect
|
||||||
|
|
||||||
from .vendor import pather
|
from .vendor import pather
|
||||||
from .vendor.pather.error import ParseError
|
from .vendor.pather.error import ParseError
|
||||||
|
|
@ -469,9 +470,7 @@ def filter_pyblish_plugins(plugins):
|
||||||
|
|
||||||
host = api.current_host()
|
host = api.current_host()
|
||||||
|
|
||||||
presets = config.get_presets().get('plugins', {}).get(host, {}).get(
|
presets = config.get_presets().get('plugins', {})
|
||||||
"publish", {}
|
|
||||||
)
|
|
||||||
|
|
||||||
# iterate over plugins
|
# iterate over plugins
|
||||||
for plugin in plugins[:]:
|
for plugin in plugins[:]:
|
||||||
|
|
@ -479,10 +478,20 @@ def filter_pyblish_plugins(plugins):
|
||||||
if not presets:
|
if not presets:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
file = os.path.normpath(inspect.getsourcefile(plugin))
|
||||||
|
file = os.path.normpath(file)
|
||||||
|
|
||||||
|
# host determined from path
|
||||||
|
host_from_file = file.split(os.path.sep)[-3:-2][0]
|
||||||
|
plugin_kind = file.split(os.path.sep)[-2:-1][0]
|
||||||
|
|
||||||
try:
|
try:
|
||||||
config_data = presets[plugin.__name__] # noqa: E501
|
config_data = presets[host]["publish"][plugin.__name__]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
continue
|
try:
|
||||||
|
config_data = presets[host_from_file][plugin_kind][plugin.__name__] # noqa: E501
|
||||||
|
except KeyError:
|
||||||
|
continue
|
||||||
|
|
||||||
for option, value in config_data.items():
|
for option, value in config_data.items():
|
||||||
if option == "enabled" and value is False:
|
if option == "enabled" and value is False:
|
||||||
|
|
|
||||||
|
|
@ -2,13 +2,54 @@ import tempfile
|
||||||
import os
|
import os
|
||||||
import pyblish.api
|
import pyblish.api
|
||||||
|
|
||||||
|
from pypeapp import config
|
||||||
|
import inspect
|
||||||
|
|
||||||
ValidatePipelineOrder = pyblish.api.ValidatorOrder + 0.05
|
ValidatePipelineOrder = pyblish.api.ValidatorOrder + 0.05
|
||||||
ValidateContentsOrder = pyblish.api.ValidatorOrder + 0.1
|
ValidateContentsOrder = pyblish.api.ValidatorOrder + 0.1
|
||||||
ValidateSceneOrder = pyblish.api.ValidatorOrder + 0.2
|
ValidateSceneOrder = pyblish.api.ValidatorOrder + 0.2
|
||||||
ValidateMeshOrder = pyblish.api.ValidatorOrder + 0.3
|
ValidateMeshOrder = pyblish.api.ValidatorOrder + 0.3
|
||||||
|
|
||||||
|
|
||||||
class Extractor(pyblish.api.InstancePlugin):
|
def imprint_attributes(plugin):
|
||||||
|
"""
|
||||||
|
Load presets by class and set them as attributes (if found)
|
||||||
|
|
||||||
|
:param plugin: plugin instance
|
||||||
|
:type plugin: instance
|
||||||
|
"""
|
||||||
|
file = inspect.getfile(plugin.__class__)
|
||||||
|
file = os.path.normpath(file)
|
||||||
|
plugin_kind = file.split(os.path.sep)[-2:-1][0]
|
||||||
|
plugin_host = file.split(os.path.sep)[-3:-2][0]
|
||||||
|
plugin_name = type(plugin).__name__
|
||||||
|
try:
|
||||||
|
config_data = config.get_presets()['plugins'][plugin_host][plugin_kind][plugin_name] # noqa: E501
|
||||||
|
except KeyError:
|
||||||
|
print("preset not found")
|
||||||
|
return
|
||||||
|
|
||||||
|
for option, value in config_data.items():
|
||||||
|
if option == "enabled" and value is False:
|
||||||
|
setattr(plugin, "active", False)
|
||||||
|
else:
|
||||||
|
setattr(plugin, option, value)
|
||||||
|
print("setting {}: {} on {}".format(option, value, plugin_name))
|
||||||
|
|
||||||
|
|
||||||
|
class ContextPlugin(pyblish.api.ContextPlugin):
|
||||||
|
def process(cls, *args, **kwargs):
|
||||||
|
imprint_attributes(cls)
|
||||||
|
super(ContextPlugin, cls).process(cls, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class InstancePlugin(pyblish.api.InstancePlugin):
|
||||||
|
def process(cls, *args, **kwargs):
|
||||||
|
imprint_attributes(cls)
|
||||||
|
super(ContextPlugin, cls).process(cls, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class Extractor(InstancePlugin):
|
||||||
"""Extractor base class.
|
"""Extractor base class.
|
||||||
|
|
||||||
The extractor base class implements a "staging_dir" function used to
|
The extractor base class implements a "staging_dir" function used to
|
||||||
|
|
|
||||||
40
pype/tests/test_avalon_plugin_presets.py
Normal file
40
pype/tests/test_avalon_plugin_presets.py
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
import avalon.api as api
|
||||||
|
import pype
|
||||||
|
|
||||||
|
|
||||||
|
class MyTestCreator(api.Creator):
|
||||||
|
|
||||||
|
my_test_property = "A"
|
||||||
|
|
||||||
|
def __init__(self, name, asset, options=None, data=None):
|
||||||
|
super(MyTestCreator, self).__init__(self, name, asset,
|
||||||
|
options=None, data=None)
|
||||||
|
|
||||||
|
|
||||||
|
# this is hack like no other - we need to inject our own avalon host
|
||||||
|
# and bypass all its validation. Avalon hosts are modules that needs
|
||||||
|
# `ls` callable as attribute. Voila:
|
||||||
|
class Test:
|
||||||
|
__name__ = "test"
|
||||||
|
ls = len
|
||||||
|
|
||||||
|
def __call__(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def test_avalon_plugin_presets(monkeypatch, printer):
|
||||||
|
|
||||||
|
pype.install()
|
||||||
|
api.register_host(Test())
|
||||||
|
api.register_plugin(api.Creator, MyTestCreator)
|
||||||
|
plugins = api.discover(api.Creator)
|
||||||
|
printer("Test if we got our test plugin")
|
||||||
|
assert MyTestCreator in plugins
|
||||||
|
for p in plugins:
|
||||||
|
if p.__name__ == "MyTestCreator":
|
||||||
|
printer("Test if we have overriden existing property")
|
||||||
|
assert p.my_test_property == "B"
|
||||||
|
printer("Test if we have overriden superclass property")
|
||||||
|
assert p.active is False
|
||||||
|
printer("Test if we have added new property")
|
||||||
|
assert p.new_property == "new"
|
||||||
|
|
@ -18,7 +18,7 @@ def test_pyblish_plugin_filter_modifier(printer, monkeypatch):
|
||||||
assert len(plugins) == 0
|
assert len(plugins) == 0
|
||||||
paths = pyblish.api.registered_paths()
|
paths = pyblish.api.registered_paths()
|
||||||
printer("Test if we have no registered plugin paths")
|
printer("Test if we have no registered plugin paths")
|
||||||
print(paths)
|
assert len(paths) == 0
|
||||||
|
|
||||||
class MyTestPlugin(pyblish.api.InstancePlugin):
|
class MyTestPlugin(pyblish.api.InstancePlugin):
|
||||||
my_test_property = 1
|
my_test_property = 1
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue