Merge branch 'release/2.2.0'

This commit is contained in:
Milan Kolar 2019-09-06 19:09:18 +02:00
commit bb51000871
7 changed files with 177 additions and 9 deletions

View file

@ -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.

View file

@ -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

View file

@ -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

View file

@ -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:

View file

@ -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

View 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"

View file

@ -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