diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml
index a9f1f1cc02..4d7d06a2c8 100644
--- a/.github/ISSUE_TEMPLATE/bug_report.yml
+++ b/.github/ISSUE_TEMPLATE/bug_report.yml
@@ -35,6 +35,7 @@ body:
label: Version
description: What version are you running? Look to OpenPype Tray
options:
+ - 3.15.8
- 3.15.8-nightly.3
- 3.15.8-nightly.2
- 3.15.8-nightly.1
@@ -134,7 +135,6 @@ body:
- 3.14.2-nightly.4
- 3.14.2-nightly.3
- 3.14.2-nightly.2
- - 3.14.2-nightly.1
validations:
required: true
- type: dropdown
diff --git a/CHANGELOG.md b/CHANGELOG.md
index bba6b64bfe..a33904735b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,304 @@
# Changelog
+## [3.15.8](https://github.com/ynput/OpenPype/tree/3.15.8)
+
+
+[Full Changelog](https://github.com/ynput/OpenPype/compare/3.15.7...3.15.8)
+
+### **🆕 New features**
+
+
+
+Publisher: Show instances in report page #4915
+
+Show publish instances in report page. Also added basic log view with logs grouped by instance. Validation error detail now have 2 colums, one with erro details second with logs. Crashed state shows fast access to report action buttons. Success will show only logs. Publish frame is shrunked automatically on publish stop.
+
+
+___
+
+
+
+
+
+Fusion - Loader plugins updates #4920
+
+Update to some Fusion loader plugins:The sequence loader can now load footage from the image and online family.The FBX loader can now import all formats Fusions FBX node can read.You can now import the content of another workfile into your current comp with the workfile loader.
+
+
+___
+
+
+
+
+
+Fusion: deadline farm rendering #4955
+
+Enabling Fusion for deadline farm rendering.
+
+
+___
+
+
+
+
+
+AfterEffects: set frame range and resolution #4983
+
+Frame information (frame start, duration, fps) and resolution (width and height) is applied to selected composition from Asset Management System (Ftrack or DB) automatically when published instance is created.It is also possible explicitly propagate both values from DB to selected composition by newly added menu buttons.
+
+
+___
+
+
+
+
+
+Publish: Enhance automated publish plugin settings #4986
+
+Added plugins option to define settings category where to look for settings of a plugin and added public helper functions to apply settings `get_plugin_settings` and `apply_plugin_settings_automatically`.
+
+
+___
+
+
+
+### **🚀 Enhancements**
+
+
+
+Load Rig References - Change Rig to Animation in Animation instance #4877
+
+We are using the template builder to build an animation scene. All the rig placeholders are imported correctly, but the automatically created animation instances retain the rig family in their names and subsets. In our example, we need animationMain instead of rigMain, because this name will be used in the following steps like lighting.Here is the result we need. I checked, and it's not a template builder problem, because even if I load a rig as a reference, the result is the same. For me, since we are in the animation instance, it makes more sense to have animation instead of rig in the name. The naming is just fine if we use create from the Openpype menu.
+
+
+___
+
+
+
+
+
+Enhancement: Resolve prelaunch code refactoring and update defaults #4916
+
+The main reason of this PR is wrong default settings in `openpype/settings/defaults/system_settings/applications.json` for Resolve host. The `bin` folder should not be a part of the macos and Linux `RESOLVE_PYTHON3_PATH` variable.The rest of this PR is some code cleanups for Resolve prelaunch hook to simplify further development.Also added a .gitignore for vscode workspace files.
+
+
+___
+
+
+
+
+
+Unreal: 🚚 move Unreal plugin to separate repository #4980
+
+To support Epic Marketplace have to move AYON Unreal integration plugins to separate repository. This is replacing current files with git submodule, so the change should be functionally without impact.New repository lives here: https://github.com/ynput/ayon-unreal-plugin
+
+
+___
+
+
+
+
+
+General: Lib code cleanup #5003
+
+Small cleanup in lib files in openpype.
+
+
+___
+
+
+
+
+
+Allow to open with djv by extension instead of representation name #5004
+
+Filter open in djv action by extension instead of representation.
+
+
+___
+
+
+
+
+
+DJV open action `extensions` as `set` #5005
+
+Change `extensions` attribute to `set`.
+
+
+___
+
+
+
+
+
+Nuke: extract thumbnail with multiple reposition nodes #5011
+
+Added support for multiple reposition nodes.
+
+
+___
+
+
+
+
+
+Enhancement: Improve logging levels and messages for artist facing publish reports #5018
+
+Tweak the logging levels and messages to try and only show those logs that an artist should see and could understand. Move anything that's slightly more involved into a "debug" message instead.
+
+
+___
+
+
+
+### **🐛 Bug fixes**
+
+
+
+Bugfix/frame variable fix #4978
+
+Renamed variables to match OpenPype terminology to reduce confusion and add consistency.
+___
+
+
+
+
+
+Global: plugins cleanup plugin will leave beauty rendered files #4790
+
+Attempt to mark more files to be cleaned up explicitly in intermediate `renders` folder in work area for farm jobs.
+
+
+___
+
+
+
+
+
+Fix: Download last workfile doesn't work if not already downloaded #4942
+
+Some optimization condition is messing with the feature: if the published workfile is not already downloaded, it won't download it...
+
+
+___
+
+
+
+
+
+Unreal: Fix transform when loading layout to match existing assets #4972
+
+Fixed transform when loading layout to match existing assets.
+
+
+___
+
+
+
+
+
+fix the bug of fbx loaders in Max #4977
+
+bug fix of fbx loaders for not being able to parent to the CON instances while importing cameras(and models) which is published from other DCCs such as Maya.
+
+
+___
+
+
+
+
+
+AfterEffects: allow returning stub with not saved workfile #4984
+
+Allows to use Workfile app to Save first empty workfile.
+
+
+___
+
+
+
+
+
+Blender: Fix Alembic loading #4985
+
+Fixed problem occurring when trying to load an Alembic model in Blender.
+
+
+___
+
+
+
+
+
+Unreal: Addon Py2 compatibility #4994
+
+Fixed Python 2 compatibility of unreal addon.
+
+
+___
+
+
+
+
+
+Nuke: fixed missing files key in representation #4999
+
+Issue with missing keys once rendering target set to existing frames is fixed. Instance has to be evaluated in validation for missing files.
+
+
+___
+
+
+
+
+
+Unreal: Fix the frame range when loading camera #5002
+
+The keyframes of the camera, when loaded, were not using the correct frame range.
+
+
+___
+
+
+
+
+
+Fusion: fixing frame range targeting #5013
+
+Frame range targeting at Rendering instances is now following configured options.
+
+
+___
+
+
+
+
+
+Deadline: fix selection from multiple webservices #5015
+
+Multiple different DL webservice could be configured. First they must by configured in System Settings., then they could be configured per project in `project_settings/deadline/deadline_servers`.Only single webservice could be a target of publish though.
+
+
+___
+
+
+
+### **Merged pull requests**
+
+
+
+3dsmax: Refactored publish plugins to use proper implementation of pymxs #4988
+
+
+___
+
+
+
+
+
+
## [3.15.7](https://github.com/ynput/OpenPype/tree/3.15.7)
diff --git a/openpype/hosts/fusion/plugins/publish/collect_inputs.py b/openpype/hosts/fusion/plugins/publish/collect_inputs.py
index 1bb3cd1220..a6628300db 100644
--- a/openpype/hosts/fusion/plugins/publish/collect_inputs.py
+++ b/openpype/hosts/fusion/plugins/publish/collect_inputs.py
@@ -113,4 +113,4 @@ class CollectUpstreamInputs(pyblish.api.InstancePlugin):
inputs = [c["representation"] for c in containers]
instance.data["inputRepresentations"] = inputs
- self.log.info("Collected inputs: %s" % inputs)
+ self.log.debug("Collected inputs: %s" % inputs)
diff --git a/openpype/hosts/fusion/plugins/publish/save_scene.py b/openpype/hosts/fusion/plugins/publish/save_scene.py
index a249c453d8..0798e7c8b7 100644
--- a/openpype/hosts/fusion/plugins/publish/save_scene.py
+++ b/openpype/hosts/fusion/plugins/publish/save_scene.py
@@ -17,5 +17,5 @@ class FusionSaveComp(pyblish.api.ContextPlugin):
current = comp.GetAttrs().get("COMPS_FileName", "")
assert context.data['currentFile'] == current
- self.log.info("Saving current file..")
+ self.log.info("Saving current file: {}".format(current))
comp.Save()
diff --git a/openpype/hosts/houdini/plugins/publish/collect_frames.py b/openpype/hosts/houdini/plugins/publish/collect_frames.py
index 6c695f64e9..059793e3c5 100644
--- a/openpype/hosts/houdini/plugins/publish/collect_frames.py
+++ b/openpype/hosts/houdini/plugins/publish/collect_frames.py
@@ -8,7 +8,6 @@ import pyblish.api
from openpype.hosts.houdini.api import lib
-
class CollectFrames(pyblish.api.InstancePlugin):
"""Collect all frames which would be saved from the ROP nodes"""
@@ -34,8 +33,10 @@ class CollectFrames(pyblish.api.InstancePlugin):
self.log.warning("Using current frame: {}".format(hou.frame()))
output = output_parm.eval()
- _, ext = lib.splitext(output,
- allowed_multidot_extensions=[".ass.gz"])
+ _, ext = lib.splitext(
+ output,
+ allowed_multidot_extensions=[".ass.gz"]
+ )
file_name = os.path.basename(output)
result = file_name
diff --git a/openpype/hosts/houdini/plugins/publish/collect_inputs.py b/openpype/hosts/houdini/plugins/publish/collect_inputs.py
index 6411376ea3..e92a42f2e8 100644
--- a/openpype/hosts/houdini/plugins/publish/collect_inputs.py
+++ b/openpype/hosts/houdini/plugins/publish/collect_inputs.py
@@ -117,4 +117,4 @@ class CollectUpstreamInputs(pyblish.api.InstancePlugin):
inputs = [c["representation"] for c in containers]
instance.data["inputRepresentations"] = inputs
- self.log.info("Collected inputs: %s" % inputs)
+ self.log.debug("Collected inputs: %s" % inputs)
diff --git a/openpype/hosts/houdini/plugins/publish/collect_instances.py b/openpype/hosts/houdini/plugins/publish/collect_instances.py
index bb85630552..5d5347f96e 100644
--- a/openpype/hosts/houdini/plugins/publish/collect_instances.py
+++ b/openpype/hosts/houdini/plugins/publish/collect_instances.py
@@ -55,7 +55,9 @@ class CollectInstances(pyblish.api.ContextPlugin):
has_family = node.evalParm("family")
assert has_family, "'%s' is missing 'family'" % node.name()
- self.log.info("processing {}".format(node))
+ self.log.info(
+ "Processing legacy instance node {}".format(node.path())
+ )
data = lib.read(node)
# Check bypass state and reverse
diff --git a/openpype/hosts/houdini/plugins/publish/collect_workfile.py b/openpype/hosts/houdini/plugins/publish/collect_workfile.py
index a6e94ec29e..aa533bcf1b 100644
--- a/openpype/hosts/houdini/plugins/publish/collect_workfile.py
+++ b/openpype/hosts/houdini/plugins/publish/collect_workfile.py
@@ -32,5 +32,4 @@ class CollectWorkfile(pyblish.api.InstancePlugin):
"stagingDir": folder,
}]
- self.log.info('Collected instance: {}'.format(file))
- self.log.info('staging Dir: {}'.format(folder))
+ self.log.debug('Collected workfile instance: {}'.format(file))
diff --git a/openpype/hosts/houdini/plugins/publish/save_scene.py b/openpype/hosts/houdini/plugins/publish/save_scene.py
index d6e07ccab0..703d3e4895 100644
--- a/openpype/hosts/houdini/plugins/publish/save_scene.py
+++ b/openpype/hosts/houdini/plugins/publish/save_scene.py
@@ -20,7 +20,7 @@ class SaveCurrentScene(pyblish.api.ContextPlugin):
)
if host.has_unsaved_changes():
- self.log.info("Saving current file {}...".format(current_file))
+ self.log.info("Saving current file: {}".format(current_file))
host.save_workfile(current_file)
else:
self.log.debug("No unsaved changes, skipping file save..")
diff --git a/openpype/hosts/houdini/plugins/publish/validate_workfile_paths.py b/openpype/hosts/houdini/plugins/publish/validate_workfile_paths.py
index 7707cc2dba..543c8e1407 100644
--- a/openpype/hosts/houdini/plugins/publish/validate_workfile_paths.py
+++ b/openpype/hosts/houdini/plugins/publish/validate_workfile_paths.py
@@ -28,18 +28,37 @@ class ValidateWorkfilePaths(
if not self.is_active(instance.data):
return
invalid = self.get_invalid()
- self.log.info(
- "node types to check: {}".format(", ".join(self.node_types)))
- self.log.info(
- "prohibited vars: {}".format(", ".join(self.prohibited_vars))
+ self.log.debug(
+ "Checking node types: {}".format(", ".join(self.node_types)))
+ self.log.debug(
+ "Searching prohibited vars: {}".format(
+ ", ".join(self.prohibited_vars)
+ )
)
- if invalid:
- for param in invalid:
- self.log.error(
- "{}: {}".format(param.path(), param.unexpandedString()))
- raise PublishValidationError(
- "Invalid paths found", title=self.label)
+ if invalid:
+ all_container_vars = set()
+ for param in invalid:
+ value = param.unexpandedString()
+ contained_vars = [
+ var for var in self.prohibited_vars
+ if var in value
+ ]
+ all_container_vars.update(contained_vars)
+
+ self.log.error(
+ "Parm {} contains prohibited vars {}: {}".format(
+ param.path(),
+ ", ".join(contained_vars),
+ value)
+ )
+
+ message = (
+ "Prohibited vars {} found in parameter values".format(
+ ", ".join(all_container_vars)
+ )
+ )
+ raise PublishValidationError(message, title=self.label)
@classmethod
def get_invalid(cls):
@@ -63,7 +82,7 @@ class ValidateWorkfilePaths(
def repair(cls, instance):
invalid = cls.get_invalid()
for param in invalid:
- cls.log.info("processing: {}".format(param.path()))
+ cls.log.info("Processing: {}".format(param.path()))
cls.log.info("Replacing {} for {}".format(
param.unexpandedString(),
hou.text.expandString(param.unexpandedString())))
diff --git a/openpype/hosts/maya/plugins/publish/collect_inputs.py b/openpype/hosts/maya/plugins/publish/collect_inputs.py
index 9c3f0f5efa..895c92762b 100644
--- a/openpype/hosts/maya/plugins/publish/collect_inputs.py
+++ b/openpype/hosts/maya/plugins/publish/collect_inputs.py
@@ -166,7 +166,7 @@ class CollectUpstreamInputs(pyblish.api.InstancePlugin):
inputs = [c["representation"] for c in containers]
instance.data["inputRepresentations"] = inputs
- self.log.info("Collected inputs: %s" % inputs)
+ self.log.debug("Collected inputs: %s" % inputs)
def _collect_renderlayer_inputs(self, scene_containers, instance):
"""Collects inputs from nodes in renderlayer, incl. shaders + camera"""
diff --git a/openpype/hosts/maya/plugins/publish/save_scene.py b/openpype/hosts/maya/plugins/publish/save_scene.py
index 45e62e7b44..495c339731 100644
--- a/openpype/hosts/maya/plugins/publish/save_scene.py
+++ b/openpype/hosts/maya/plugins/publish/save_scene.py
@@ -31,5 +31,5 @@ class SaveCurrentScene(pyblish.api.ContextPlugin):
# remove lockfile before saving
if is_workfile_lock_enabled("maya", project_name, project_settings):
remove_workfile_lock(current)
- self.log.info("Saving current file..")
+ self.log.info("Saving current file: {}".format(current))
cmds.file(save=True, force=True)
diff --git a/openpype/hosts/nuke/plugins/publish/extract_thumbnail.py b/openpype/hosts/nuke/plugins/publish/extract_thumbnail.py
index f391ca1e7c..21eefda249 100644
--- a/openpype/hosts/nuke/plugins/publish/extract_thumbnail.py
+++ b/openpype/hosts/nuke/plugins/publish/extract_thumbnail.py
@@ -5,6 +5,8 @@ import pyblish.api
from openpype.pipeline import publish
from openpype.hosts.nuke import api as napi
+from openpype.hosts.nuke.api.lib import set_node_knobs_from_settings
+
if sys.version_info[0] >= 3:
unicode = str
@@ -28,7 +30,7 @@ class ExtractThumbnail(publish.Extractor):
bake_viewer_process = True
bake_viewer_input_process = True
nodes = {}
-
+ reposition_nodes = None
def process(self, instance):
if instance.data.get("farm"):
@@ -123,18 +125,32 @@ class ExtractThumbnail(publish.Extractor):
temporary_nodes.append(rnode)
previous_node = rnode
- reformat_node = nuke.createNode("Reformat")
- ref_node = self.nodes.get("Reformat", None)
- if ref_node:
- for k, v in ref_node:
- self.log.debug("k, v: {0}:{1}".format(k, v))
- if isinstance(v, unicode):
- v = str(v)
- reformat_node[k].setValue(v)
+ if self.reposition_nodes is None:
+ # [deprecated] create reformat node old way
+ reformat_node = nuke.createNode("Reformat")
+ ref_node = self.nodes.get("Reformat", None)
+ if ref_node:
+ for k, v in ref_node:
+ self.log.debug("k, v: {0}:{1}".format(k, v))
+ if isinstance(v, unicode):
+ v = str(v)
+ reformat_node[k].setValue(v)
- reformat_node.setInput(0, previous_node)
- previous_node = reformat_node
- temporary_nodes.append(reformat_node)
+ reformat_node.setInput(0, previous_node)
+ previous_node = reformat_node
+ temporary_nodes.append(reformat_node)
+ else:
+ # create reformat node new way
+ for repo_node in self.reposition_nodes:
+ node_class = repo_node["node_class"]
+ knobs = repo_node["knobs"]
+ node = nuke.createNode(node_class)
+ set_node_knobs_from_settings(node, knobs)
+
+ # connect in order
+ node.setInput(0, previous_node)
+ previous_node = node
+ temporary_nodes.append(node)
# only create colorspace baking if toggled on
if bake_viewer_process:
diff --git a/openpype/hosts/substancepainter/plugins/publish/save_workfile.py b/openpype/hosts/substancepainter/plugins/publish/save_workfile.py
index 4874b5e5c7..9662f31922 100644
--- a/openpype/hosts/substancepainter/plugins/publish/save_workfile.py
+++ b/openpype/hosts/substancepainter/plugins/publish/save_workfile.py
@@ -16,11 +16,12 @@ class SaveCurrentWorkfile(pyblish.api.ContextPlugin):
def process(self, context):
host = registered_host()
- if context.data["currentFile"] != host.get_current_workfile():
+ current = host.get_current_workfile()
+ if context.data["currentFile"] != current:
raise KnownPublishError("Workfile has changed during publishing!")
if host.has_unsaved_changes():
- self.log.info("Saving current file..")
+ self.log.info("Saving current file: {}".format(current))
host.save_workfile()
else:
self.log.debug("Skipping workfile save because there are no "
diff --git a/openpype/hosts/unreal/plugins/load/load_animation.py b/openpype/hosts/unreal/plugins/load/load_animation.py
index 778ddf693d..a5ecb677e8 100644
--- a/openpype/hosts/unreal/plugins/load/load_animation.py
+++ b/openpype/hosts/unreal/plugins/load/load_animation.py
@@ -156,7 +156,7 @@ class AnimationFBXLoader(plugin.Loader):
package_paths=[f"{root}/{hierarchy[0]}"],
recursive_paths=False)
levels = ar.get_assets(_filter)
- master_level = levels[0].get_full_name()
+ master_level = levels[0].get_asset().get_path_name()
hierarchy_dir = root
for h in hierarchy:
@@ -168,7 +168,7 @@ class AnimationFBXLoader(plugin.Loader):
package_paths=[f"{hierarchy_dir}/"],
recursive_paths=True)
levels = ar.get_assets(_filter)
- level = levels[0].get_full_name()
+ level = levels[0].get_asset().get_path_name()
unreal.EditorLevelLibrary.save_all_dirty_levels()
unreal.EditorLevelLibrary.load_level(level)
diff --git a/openpype/hosts/unreal/plugins/load/load_camera.py b/openpype/hosts/unreal/plugins/load/load_camera.py
index 1bd398349f..072b3b1467 100644
--- a/openpype/hosts/unreal/plugins/load/load_camera.py
+++ b/openpype/hosts/unreal/plugins/load/load_camera.py
@@ -365,7 +365,7 @@ class CameraLoader(plugin.Loader):
maps = ar.get_assets(filter)
# There should be only one map in the list
- EditorLevelLibrary.load_level(maps[0].get_full_name())
+ EditorLevelLibrary.load_level(maps[0].get_asset().get_path_name())
level_sequence = sequences[0].get_asset()
@@ -513,7 +513,7 @@ class CameraLoader(plugin.Loader):
map = maps[0]
EditorLevelLibrary.save_all_dirty_levels()
- EditorLevelLibrary.load_level(map.get_full_name())
+ EditorLevelLibrary.load_level(map.get_asset().get_path_name())
# Remove the camera from the level.
actors = EditorLevelLibrary.get_all_level_actors()
@@ -523,7 +523,7 @@ class CameraLoader(plugin.Loader):
EditorLevelLibrary.destroy_actor(a)
EditorLevelLibrary.save_all_dirty_levels()
- EditorLevelLibrary.load_level(world.get_full_name())
+ EditorLevelLibrary.load_level(world.get_asset().get_path_name())
# There should be only one sequence in the path.
sequence_name = sequences[0].asset_name
diff --git a/openpype/hosts/unreal/plugins/load/load_layout.py b/openpype/hosts/unreal/plugins/load/load_layout.py
index e5f32c3412..d94e6e5837 100644
--- a/openpype/hosts/unreal/plugins/load/load_layout.py
+++ b/openpype/hosts/unreal/plugins/load/load_layout.py
@@ -740,7 +740,7 @@ class LayoutLoader(plugin.Loader):
loaded_assets = self._process(self.fname, asset_dir, shot)
for s in sequences:
- EditorAssetLibrary.save_asset(s.get_full_name())
+ EditorAssetLibrary.save_asset(s.get_path_name())
EditorLevelLibrary.save_current_level()
@@ -819,7 +819,7 @@ class LayoutLoader(plugin.Loader):
recursive_paths=False)
levels = ar.get_assets(filter)
- layout_level = levels[0].get_full_name()
+ layout_level = levels[0].get_asset().get_path_name()
EditorLevelLibrary.save_all_dirty_levels()
EditorLevelLibrary.load_level(layout_level)
@@ -919,7 +919,7 @@ class LayoutLoader(plugin.Loader):
package_paths=[f"{root}/{ms_asset}"],
recursive_paths=False)
levels = ar.get_assets(_filter)
- master_level = levels[0].get_full_name()
+ master_level = levels[0].get_asset().get_path_name()
sequences = [master_sequence]
diff --git a/openpype/lib/__init__.py b/openpype/lib/__init__.py
index 9eb7724a60..06de486f2e 100644
--- a/openpype/lib/__init__.py
+++ b/openpype/lib/__init__.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
# flake8: noqa E402
-"""Pype module API."""
+"""OpenPype lib functions."""
# add vendor to sys path based on Python version
import sys
import os
@@ -94,7 +94,8 @@ from .python_module_tools import (
modules_from_path,
recursive_bases_from_class,
classes_from_module,
- import_module_from_dirpath
+ import_module_from_dirpath,
+ is_func_signature_supported,
)
from .profiles_filtering import (
@@ -243,6 +244,7 @@ __all__ = [
"recursive_bases_from_class",
"classes_from_module",
"import_module_from_dirpath",
+ "is_func_signature_supported",
"get_transcode_temp_directory",
"should_convert_for_ffmpeg",
diff --git a/openpype/lib/events.py b/openpype/lib/events.py
index bed00fe659..dca58fcf93 100644
--- a/openpype/lib/events.py
+++ b/openpype/lib/events.py
@@ -6,10 +6,9 @@ import inspect
import logging
import weakref
from uuid import uuid4
-try:
- from weakref import WeakMethod
-except Exception:
- from openpype.lib.python_2_comp import WeakMethod
+
+from .python_2_comp import WeakMethod
+from .python_module_tools import is_func_signature_supported
class MissingEventSystem(Exception):
@@ -80,40 +79,8 @@ class EventCallback(object):
# Get expected arguments from function spec
# - positional arguments are always preferred
- expect_args = False
- expect_kwargs = False
- fake_event = "fake"
- if hasattr(inspect, "signature"):
- # Python 3 using 'Signature' object where we try to bind arg
- # or kwarg. Using signature is recommended approach based on
- # documentation.
- sig = inspect.signature(func)
- try:
- sig.bind(fake_event)
- expect_args = True
- except TypeError:
- pass
-
- try:
- sig.bind(event=fake_event)
- expect_kwargs = True
- except TypeError:
- pass
-
- else:
- # In Python 2 'signature' is not available so 'getcallargs' is used
- # - 'getcallargs' is marked as deprecated since Python 3.0
- try:
- inspect.getcallargs(func, fake_event)
- expect_args = True
- except TypeError:
- pass
-
- try:
- inspect.getcallargs(func, event=fake_event)
- expect_kwargs = True
- except TypeError:
- pass
+ expect_args = is_func_signature_supported(func, "fake")
+ expect_kwargs = is_func_signature_supported(func, event="fake")
self._func_ref = func_ref
self._func_name = func_name
diff --git a/openpype/lib/execute.py b/openpype/lib/execute.py
index ef456395e7..6f52efdfcc 100644
--- a/openpype/lib/execute.py
+++ b/openpype/lib/execute.py
@@ -190,7 +190,7 @@ def run_openpype_process(*args, **kwargs):
Example:
```
- run_openpype_process("run", "")
+ run_detached_process("run", "")
```
Args:
diff --git a/openpype/lib/python_2_comp.py b/openpype/lib/python_2_comp.py
index d7137dbe9c..091c51a6f6 100644
--- a/openpype/lib/python_2_comp.py
+++ b/openpype/lib/python_2_comp.py
@@ -1,41 +1,44 @@
import weakref
-class _weak_callable:
- def __init__(self, obj, func):
- self.im_self = obj
- self.im_func = func
+WeakMethod = getattr(weakref, "WeakMethod", None)
- def __call__(self, *args, **kws):
- if self.im_self is None:
- return self.im_func(*args, **kws)
- else:
- return self.im_func(self.im_self, *args, **kws)
+if WeakMethod is None:
+ class _WeakCallable:
+ def __init__(self, obj, func):
+ self.im_self = obj
+ self.im_func = func
+
+ def __call__(self, *args, **kws):
+ if self.im_self is None:
+ return self.im_func(*args, **kws)
+ else:
+ return self.im_func(self.im_self, *args, **kws)
-class WeakMethod:
- """ Wraps a function or, more importantly, a bound method in
- a way that allows a bound method's object to be GCed, while
- providing the same interface as a normal weak reference. """
+ class WeakMethod:
+ """ Wraps a function or, more importantly, a bound method in
+ a way that allows a bound method's object to be GCed, while
+ providing the same interface as a normal weak reference. """
- def __init__(self, fn):
- try:
- self._obj = weakref.ref(fn.im_self)
- self._meth = fn.im_func
- except AttributeError:
- # It's not a bound method
- self._obj = None
- self._meth = fn
+ def __init__(self, fn):
+ try:
+ self._obj = weakref.ref(fn.im_self)
+ self._meth = fn.im_func
+ except AttributeError:
+ # It's not a bound method
+ self._obj = None
+ self._meth = fn
- def __call__(self):
- if self._dead():
- return None
- return _weak_callable(self._getobj(), self._meth)
+ def __call__(self):
+ if self._dead():
+ return None
+ return _WeakCallable(self._getobj(), self._meth)
- def _dead(self):
- return self._obj is not None and self._obj() is None
+ def _dead(self):
+ return self._obj is not None and self._obj() is None
- def _getobj(self):
- if self._obj is None:
- return None
- return self._obj()
+ def _getobj(self):
+ if self._obj is None:
+ return None
+ return self._obj()
diff --git a/openpype/lib/python_module_tools.py b/openpype/lib/python_module_tools.py
index 9e8e94842c..a10263f991 100644
--- a/openpype/lib/python_module_tools.py
+++ b/openpype/lib/python_module_tools.py
@@ -230,3 +230,70 @@ def import_module_from_dirpath(dirpath, folder_name, dst_module_name=None):
dirpath, folder_name, dst_module_name
)
return module
+
+
+def is_func_signature_supported(func, *args, **kwargs):
+ """Check if a function signature supports passed args and kwargs.
+
+ This check does not actually call the function, just look if function can
+ be called with the arguments.
+
+ Notes:
+ This does NOT check if the function would work with passed arguments
+ only if they can be passed in. If function have *args, **kwargs
+ in paramaters, this will always return 'True'.
+
+ Example:
+ >>> def my_function(my_number):
+ ... return my_number + 1
+ ...
+ >>> is_func_signature_supported(my_function, 1)
+ True
+ >>> is_func_signature_supported(my_function, 1, 2)
+ False
+ >>> is_func_signature_supported(my_function, my_number=1)
+ True
+ >>> is_func_signature_supported(my_function, number=1)
+ False
+ >>> is_func_signature_supported(my_function, "string")
+ True
+ >>> def my_other_function(*args, **kwargs):
+ ... my_function(*args, **kwargs)
+ ...
+ >>> is_func_signature_supported(
+ ... my_other_function,
+ ... "string",
+ ... 1,
+ ... other=None
+ ... )
+ True
+
+ Args:
+ func (function): A function where the signature should be tested.
+ *args (tuple[Any]): Positional arguments for function signature.
+ **kwargs (dict[str, Any]): Keyword arguments for function signature.
+
+ Returns:
+ bool: Function can pass in arguments.
+ """
+
+ if hasattr(inspect, "signature"):
+ # Python 3 using 'Signature' object where we try to bind arg
+ # or kwarg. Using signature is recommended approach based on
+ # documentation.
+ sig = inspect.signature(func)
+ try:
+ sig.bind(*args, **kwargs)
+ return True
+ except TypeError:
+ pass
+
+ else:
+ # In Python 2 'signature' is not available so 'getcallargs' is used
+ # - 'getcallargs' is marked as deprecated since Python 3.0
+ try:
+ inspect.getcallargs(func, *args, **kwargs)
+ return True
+ except TypeError:
+ pass
+ return False
diff --git a/openpype/modules/deadline/plugins/publish/submit_fusion_deadline.py b/openpype/modules/deadline/plugins/publish/submit_fusion_deadline.py
index 717391100d..a48596c6bf 100644
--- a/openpype/modules/deadline/plugins/publish/submit_fusion_deadline.py
+++ b/openpype/modules/deadline/plugins/publish/submit_fusion_deadline.py
@@ -73,7 +73,7 @@ class FusionSubmitDeadline(
def process(self, instance):
if not instance.data.get("farm"):
- self.log.info("Skipping local instance.")
+ self.log.debug("Skipping local instance.")
return
attribute_values = self.get_attr_values_from_data(
diff --git a/openpype/modules/deadline/plugins/publish/submit_nuke_deadline.py b/openpype/modules/deadline/plugins/publish/submit_nuke_deadline.py
index 5c598df94b..4900231783 100644
--- a/openpype/modules/deadline/plugins/publish/submit_nuke_deadline.py
+++ b/openpype/modules/deadline/plugins/publish/submit_nuke_deadline.py
@@ -86,7 +86,7 @@ class NukeSubmitDeadline(pyblish.api.InstancePlugin,
def process(self, instance):
if not instance.data.get("farm"):
- self.log.info("Skipping local instance.")
+ self.log.debug("Skipping local instance.")
return
instance.data["attributeValues"] = self.get_attr_values_from_data(
diff --git a/openpype/modules/deadline/plugins/publish/submit_publish_job.py b/openpype/modules/deadline/plugins/publish/submit_publish_job.py
index eeb813cb62..68eb0a437d 100644
--- a/openpype/modules/deadline/plugins/publish/submit_publish_job.py
+++ b/openpype/modules/deadline/plugins/publish/submit_publish_job.py
@@ -762,7 +762,7 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin):
"""
if not instance.data.get("farm"):
- self.log.info("Skipping local instance.")
+ self.log.debug("Skipping local instance.")
return
data = instance.data.copy()
diff --git a/openpype/modules/deadline/plugins/publish/validate_deadline_pools.py b/openpype/modules/deadline/plugins/publish/validate_deadline_pools.py
index 7c8ab62d4d..e1c0595830 100644
--- a/openpype/modules/deadline/plugins/publish/validate_deadline_pools.py
+++ b/openpype/modules/deadline/plugins/publish/validate_deadline_pools.py
@@ -26,7 +26,7 @@ class ValidateDeadlinePools(OptionalPyblishPluginMixin,
def process(self, instance):
if not instance.data.get("farm"):
- self.log.info("Skipping local instance.")
+ self.log.debug("Skipping local instance.")
return
# get default deadline webservice url from deadline module
diff --git a/openpype/pipeline/publish/lib.py b/openpype/pipeline/publish/lib.py
index 40186238aa..b55f813b5e 100644
--- a/openpype/pipeline/publish/lib.py
+++ b/openpype/pipeline/publish/lib.py
@@ -1,12 +1,10 @@
import os
import sys
-import types
import inspect
import copy
import tempfile
import xml.etree.ElementTree
-import six
import pyblish.util
import pyblish.plugin
import pyblish.api
@@ -42,7 +40,9 @@ def get_template_name_profiles(
Args:
project_name (str): Name of project where to look for templates.
- project_settings(Dic[str, Any]): Prepared project settings.
+ project_settings (Dict[str, Any]): Prepared project settings.
+ logger (Optional[logging.Logger]): Logger object to be used instead
+ of default logger.
Returns:
List[Dict[str, Any]]: Publish template profiles.
@@ -103,7 +103,9 @@ def get_hero_template_name_profiles(
Args:
project_name (str): Name of project where to look for templates.
- project_settings(Dic[str, Any]): Prepared project settings.
+ project_settings (Dict[str, Any]): Prepared project settings.
+ logger (Optional[logging.Logger]): Logger object to be used instead
+ of default logger.
Returns:
List[Dict[str, Any]]: Publish template profiles.
@@ -172,9 +174,10 @@ def get_publish_template_name(
project_name (str): Name of project where to look for settings.
host_name (str): Name of host integration.
family (str): Family for which should be found template.
- task_name (str): Task name on which is intance working.
- task_type (str): Task type on which is intance working.
- project_setting (Dict[str, Any]): Prepared project settings.
+ task_name (str): Task name on which is instance working.
+ task_type (str): Task type on which is instance working.
+ project_settings (Dict[str, Any]): Prepared project settings.
+ hero (bool): Template is for hero version publishing.
logger (logging.Logger): Custom logger used for 'filter_profiles'
function.
@@ -264,19 +267,18 @@ def load_help_content_from_plugin(plugin):
def publish_plugins_discover(paths=None):
"""Find and return available pyblish plug-ins
- Overridden function from `pyblish` module to be able collect crashed files
- and reason of their crash.
+ Overridden function from `pyblish` module to be able to collect
+ crashed files and reason of their crash.
Arguments:
paths (list, optional): Paths to discover plug-ins from.
If no paths are provided, all paths are searched.
-
"""
# The only difference with `pyblish.api.discover`
result = DiscoverResult(pyblish.api.Plugin)
- plugins = dict()
+ plugins = {}
plugin_names = []
allow_duplicates = pyblish.plugin.ALLOW_DUPLICATES
@@ -302,7 +304,7 @@ def publish_plugins_discover(paths=None):
mod_name, mod_ext = os.path.splitext(fname)
- if not mod_ext == ".py":
+ if mod_ext != ".py":
continue
try:
@@ -533,10 +535,10 @@ def find_close_plugin(close_plugin_name, log):
def remote_publish(log, close_plugin_name=None, raise_error=False):
"""Loops through all plugins, logs to console. Used for tests.
- Args:
- log (openpype.lib.Logger)
- close_plugin_name (str): name of plugin with responsibility to
- close host app
+ Args:
+ log (Logger)
+ close_plugin_name (str): name of plugin with responsibility to
+ close host app
"""
# Error exit as soon as any error occurs.
error_format = "Failed {plugin.__name__}: {error} -- {error.traceback}"
@@ -845,3 +847,22 @@ def _validate_transient_template(project_name, template_name, anatomy):
raise ValueError(("There is not set \"folder\" template in \"{}\" anatomy" # noqa
" for project \"{}\"."
).format(template_name, project_name))
+
+
+def add_repre_files_for_cleanup(instance, repre):
+ """ Explicitly mark repre files to be deleted.
+
+ Should be used on intermediate files (eg. review, thumbnails) to be
+ explicitly deleted.
+ """
+ files = repre["files"]
+ staging_dir = repre.get("stagingDir")
+ if not staging_dir:
+ return
+
+ if isinstance(files, str):
+ files = [files]
+
+ for file_name in files:
+ expected_file = os.path.join(staging_dir, file_name)
+ instance.context.data["cleanupFullPaths"].append(expected_file)
diff --git a/openpype/pipeline/publish/publish_plugins.py b/openpype/pipeline/publish/publish_plugins.py
index 2b1e846b9c..4a7b1b3a27 100644
--- a/openpype/pipeline/publish/publish_plugins.py
+++ b/openpype/pipeline/publish/publish_plugins.py
@@ -384,7 +384,9 @@ class ColormanagedPyblishPluginMixin(object):
# check if ext in lower case is in self.allowed_ext
if ext.lstrip(".").lower() not in self.allowed_ext:
- self.log.debug("Extension is not in allowed extensions.")
+ self.log.debug(
+ "Extension '{}' is not in allowed extensions.".format(ext)
+ )
return
if colorspace_settings is None:
@@ -398,8 +400,12 @@ class ColormanagedPyblishPluginMixin(object):
# unpack colorspace settings
config_data, file_rules = colorspace_settings
- self.log.info("Config data is : `{}`".format(
- config_data))
+ if not config_data:
+ # warn in case no colorspace path was defined
+ self.log.warning("No colorspace management was defined")
+ return
+
+ self.log.debug("Config data is: `{}`".format(config_data))
project_name = context.data["projectName"]
host_name = context.data["hostName"]
@@ -410,8 +416,7 @@ class ColormanagedPyblishPluginMixin(object):
if isinstance(filename, list):
filename = filename[0]
- self.log.debug("__ filename: `{}`".format(
- filename))
+ self.log.debug("__ filename: `{}`".format(filename))
# get matching colorspace from rules
colorspace = colorspace or get_imageio_colorspace_from_filepath(
@@ -420,8 +425,7 @@ class ColormanagedPyblishPluginMixin(object):
file_rules=file_rules,
project_settings=project_settings
)
- self.log.debug("__ colorspace: `{}`".format(
- colorspace))
+ self.log.debug("__ colorspace: `{}`".format(colorspace))
# infuse data to representation
if colorspace:
diff --git a/openpype/plugins/publish/cleanup.py b/openpype/plugins/publish/cleanup.py
index b90c88890d..57cc9c0ab5 100644
--- a/openpype/plugins/publish/cleanup.py
+++ b/openpype/plugins/publish/cleanup.py
@@ -81,7 +81,8 @@ class CleanUp(pyblish.api.InstancePlugin):
staging_dir = instance.data.get("stagingDir", None)
if not staging_dir:
- self.log.info("Staging dir not set.")
+ self.log.debug("Skipping cleanup. Staging dir not set "
+ "on instance: {}.".format(instance))
return
if not os.path.normpath(staging_dir).startswith(temp_root):
@@ -90,7 +91,7 @@ class CleanUp(pyblish.api.InstancePlugin):
return
if not os.path.exists(staging_dir):
- self.log.info("No staging directory found: %s" % staging_dir)
+ self.log.debug("No staging directory found at: %s" % staging_dir)
return
if instance.data.get("stagingDir_persistent"):
@@ -131,7 +132,9 @@ class CleanUp(pyblish.api.InstancePlugin):
try:
os.remove(src)
except PermissionError:
- self.log.warning("Insufficient permission to delete {}".format(src))
+ self.log.warning(
+ "Insufficient permission to delete {}".format(src)
+ )
continue
# add dir for cleanup
diff --git a/openpype/plugins/publish/collect_anatomy_context_data.py b/openpype/plugins/publish/collect_anatomy_context_data.py
index 55ce8e06f4..508b01447b 100644
--- a/openpype/plugins/publish/collect_anatomy_context_data.py
+++ b/openpype/plugins/publish/collect_anatomy_context_data.py
@@ -67,5 +67,6 @@ class CollectAnatomyContextData(pyblish.api.ContextPlugin):
# Store
context.data["anatomyData"] = anatomy_data
- self.log.info("Global anatomy Data collected")
- self.log.debug(json.dumps(anatomy_data, indent=4))
+ self.log.debug("Global Anatomy Context Data collected:\n{}".format(
+ json.dumps(anatomy_data, indent=4)
+ ))
diff --git a/openpype/plugins/publish/collect_anatomy_instance_data.py b/openpype/plugins/publish/collect_anatomy_instance_data.py
index 4fbb93324b..128ad90b4f 100644
--- a/openpype/plugins/publish/collect_anatomy_instance_data.py
+++ b/openpype/plugins/publish/collect_anatomy_instance_data.py
@@ -46,17 +46,17 @@ class CollectAnatomyInstanceData(pyblish.api.ContextPlugin):
follow_workfile_version = False
def process(self, context):
- self.log.info("Collecting anatomy data for all instances.")
+ self.log.debug("Collecting anatomy data for all instances.")
project_name = context.data["projectName"]
self.fill_missing_asset_docs(context, project_name)
self.fill_latest_versions(context, project_name)
self.fill_anatomy_data(context)
- self.log.info("Anatomy Data collection finished.")
+ self.log.debug("Anatomy Data collection finished.")
def fill_missing_asset_docs(self, context, project_name):
- self.log.debug("Qeurying asset documents for instances.")
+ self.log.debug("Querying asset documents for instances.")
context_asset_doc = context.data.get("assetEntity")
@@ -271,7 +271,7 @@ class CollectAnatomyInstanceData(pyblish.api.ContextPlugin):
instance_name = instance.data["name"]
instance_label = instance.data.get("label")
if instance_label:
- instance_name += "({})".format(instance_label)
+ instance_name += " ({})".format(instance_label)
self.log.debug("Anatomy data for instance {}: {}".format(
instance_name,
json.dumps(anatomy_data, indent=4)
diff --git a/openpype/plugins/publish/collect_anatomy_object.py b/openpype/plugins/publish/collect_anatomy_object.py
index 725cae2b14..f792cf3abd 100644
--- a/openpype/plugins/publish/collect_anatomy_object.py
+++ b/openpype/plugins/publish/collect_anatomy_object.py
@@ -30,6 +30,6 @@ class CollectAnatomyObject(pyblish.api.ContextPlugin):
context.data["anatomy"] = Anatomy(project_name)
- self.log.info(
+ self.log.debug(
"Anatomy object collected for project \"{}\".".format(project_name)
)
diff --git a/openpype/plugins/publish/collect_custom_staging_dir.py b/openpype/plugins/publish/collect_custom_staging_dir.py
index b749b251c0..669c4873e0 100644
--- a/openpype/plugins/publish/collect_custom_staging_dir.py
+++ b/openpype/plugins/publish/collect_custom_staging_dir.py
@@ -65,6 +65,6 @@ class CollectCustomStagingDir(pyblish.api.InstancePlugin):
else:
result_str = "Not adding"
- self.log.info("{} custom staging dir for instance with '{}'".format(
+ self.log.debug("{} custom staging dir for instance with '{}'".format(
result_str, family
))
diff --git a/openpype/plugins/publish/collect_from_create_context.py b/openpype/plugins/publish/collect_from_create_context.py
index 5fcf8feb56..4888476fff 100644
--- a/openpype/plugins/publish/collect_from_create_context.py
+++ b/openpype/plugins/publish/collect_from_create_context.py
@@ -92,5 +92,5 @@ class CollectFromCreateContext(pyblish.api.ContextPlugin):
instance.data["transientData"] = transient_data
- self.log.info("collected instance: {}".format(instance.data))
- self.log.info("parsing data: {}".format(in_data))
+ self.log.debug("collected instance: {}".format(instance.data))
+ self.log.debug("parsing data: {}".format(in_data))
diff --git a/openpype/plugins/publish/collect_rendered_files.py b/openpype/plugins/publish/collect_rendered_files.py
index 8f8d0a5eeb..6c8d1e9ca5 100644
--- a/openpype/plugins/publish/collect_rendered_files.py
+++ b/openpype/plugins/publish/collect_rendered_files.py
@@ -13,6 +13,7 @@ import json
import pyblish.api
from openpype.pipeline import legacy_io, KnownPublishError
+from openpype.pipeline.publish.lib import add_repre_files_for_cleanup
class CollectRenderedFiles(pyblish.api.ContextPlugin):
@@ -89,6 +90,7 @@ class CollectRenderedFiles(pyblish.api.ContextPlugin):
# now we can just add instances from json file and we are done
for instance_data in data.get("instances"):
+
self.log.info(" - processing instance for {}".format(
instance_data.get("subset")))
instance = self._context.create_instance(
@@ -107,6 +109,8 @@ class CollectRenderedFiles(pyblish.api.ContextPlugin):
self._fill_staging_dir(repre_data, anatomy)
representations.append(repre_data)
+ add_repre_files_for_cleanup(instance, repre_data)
+
instance.data["representations"] = representations
# add audio if in metadata data
@@ -157,6 +161,8 @@ class CollectRenderedFiles(pyblish.api.ContextPlugin):
os.environ.update(session_data)
session_is_set = True
self._process_path(data, anatomy)
+ context.data["cleanupFullPaths"].append(path)
+ context.data["cleanupEmptyDirs"].append(os.path.dirname(path))
except Exception as e:
self.log.error(e, exc_info=True)
raise Exception("Error") from e
diff --git a/openpype/plugins/publish/collect_scene_version.py b/openpype/plugins/publish/collect_scene_version.py
index fdbcb3cb9d..cd3231a07d 100644
--- a/openpype/plugins/publish/collect_scene_version.py
+++ b/openpype/plugins/publish/collect_scene_version.py
@@ -48,10 +48,13 @@ class CollectSceneVersion(pyblish.api.ContextPlugin):
if '' in filename:
return
+ self.log.debug(
+ "Collecting scene version from filename: {}".format(filename)
+ )
+
version = get_version_from_path(filename)
assert version, "Cannot determine version"
rootVersion = int(version)
context.data['version'] = rootVersion
- self.log.info("{}".format(type(rootVersion)))
self.log.info('Scene Version: %s' % context.data.get('version'))
diff --git a/openpype/plugins/publish/extract_burnin.py b/openpype/plugins/publish/extract_burnin.py
index a12e8d18b4..6a8ae958d2 100644
--- a/openpype/plugins/publish/extract_burnin.py
+++ b/openpype/plugins/publish/extract_burnin.py
@@ -19,6 +19,7 @@ from openpype.lib import (
should_convert_for_ffmpeg
)
from openpype.lib.profiles_filtering import filter_profiles
+from openpype.pipeline.publish.lib import add_repre_files_for_cleanup
class ExtractBurnin(publish.Extractor):
@@ -353,6 +354,8 @@ class ExtractBurnin(publish.Extractor):
# Add new representation to instance
instance.data["representations"].append(new_repre)
+ add_repre_files_for_cleanup(instance, new_repre)
+
# Cleanup temp staging dir after procesisng of output definitions
if do_convert:
temp_dir = repre["stagingDir"]
@@ -517,8 +520,8 @@ class ExtractBurnin(publish.Extractor):
"""
if "burnin" not in (repre.get("tags") or []):
- self.log.info((
- "Representation \"{}\" don't have \"burnin\" tag. Skipped."
+ self.log.debug((
+ "Representation \"{}\" does not have \"burnin\" tag. Skipped."
).format(repre["name"]))
return False
diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py
index 58e0350a2e..45b10620d1 100644
--- a/openpype/plugins/publish/extract_color_transcode.py
+++ b/openpype/plugins/publish/extract_color_transcode.py
@@ -336,13 +336,13 @@ class ExtractOIIOTranscode(publish.Extractor):
if repre.get("ext") not in self.supported_exts:
self.log.debug((
- "Representation '{}' of unsupported extension. Skipped."
- ).format(repre["name"]))
+ "Representation '{}' has unsupported extension: '{}'. Skipped."
+ ).format(repre["name"], repre.get("ext")))
return False
if not repre.get("files"):
self.log.debug((
- "Representation '{}' have empty files. Skipped."
+ "Representation '{}' has empty files. Skipped."
).format(repre["name"]))
return False
diff --git a/openpype/plugins/publish/extract_review.py b/openpype/plugins/publish/extract_review.py
index 1062683319..fa58c03df1 100644
--- a/openpype/plugins/publish/extract_review.py
+++ b/openpype/plugins/publish/extract_review.py
@@ -24,6 +24,7 @@ from openpype.lib.transcoding import (
get_transcode_temp_directory,
)
from openpype.pipeline.publish import KnownPublishError
+from openpype.pipeline.publish.lib import add_repre_files_for_cleanup
class ExtractReview(pyblish.api.InstancePlugin):
@@ -92,8 +93,8 @@ class ExtractReview(pyblish.api.InstancePlugin):
host_name = instance.context.data["hostName"]
family = self.main_family_from_instance(instance)
- self.log.info("Host: \"{}\"".format(host_name))
- self.log.info("Family: \"{}\"".format(family))
+ self.log.debug("Host: \"{}\"".format(host_name))
+ self.log.debug("Family: \"{}\"".format(family))
profile = filter_profiles(
self.profiles,
@@ -351,7 +352,7 @@ class ExtractReview(pyblish.api.InstancePlugin):
temp_data = self.prepare_temp_data(instance, repre, output_def)
files_to_clean = []
if temp_data["input_is_sequence"]:
- self.log.info("Filling gaps in sequence.")
+ self.log.debug("Checking sequence to fill gaps in sequence..")
files_to_clean = self.fill_sequence_gaps(
files=temp_data["origin_repre"]["files"],
staging_dir=new_repre["stagingDir"],
@@ -425,6 +426,8 @@ class ExtractReview(pyblish.api.InstancePlugin):
)
instance.data["representations"].append(new_repre)
+ add_repre_files_for_cleanup(instance, new_repre)
+
def input_is_sequence(self, repre):
"""Deduce from representation data if input is sequence."""
# TODO GLOBAL ISSUE - Find better way how to find out if input
diff --git a/openpype/plugins/publish/extract_thumbnail.py b/openpype/plugins/publish/extract_thumbnail.py
index 54b933a76d..b98ab64f56 100644
--- a/openpype/plugins/publish/extract_thumbnail.py
+++ b/openpype/plugins/publish/extract_thumbnail.py
@@ -36,7 +36,7 @@ class ExtractThumbnail(pyblish.api.InstancePlugin):
).format(subset_name))
return
- self.log.info(
+ self.log.debug(
"Processing instance with subset name {}".format(subset_name)
)
@@ -89,13 +89,13 @@ class ExtractThumbnail(pyblish.api.InstancePlugin):
src_staging = os.path.normpath(repre["stagingDir"])
full_input_path = os.path.join(src_staging, input_file)
- self.log.info("input {}".format(full_input_path))
+ self.log.debug("input {}".format(full_input_path))
filename = os.path.splitext(input_file)[0]
jpeg_file = filename + "_thumb.jpg"
full_output_path = os.path.join(dst_staging, jpeg_file)
if oiio_supported:
- self.log.info("Trying to convert with OIIO")
+ self.log.debug("Trying to convert with OIIO")
# If the input can read by OIIO then use OIIO method for
# conversion otherwise use ffmpeg
thumbnail_created = self.create_thumbnail_oiio(
@@ -148,7 +148,7 @@ class ExtractThumbnail(pyblish.api.InstancePlugin):
def _already_has_thumbnail(self, repres):
for repre in repres:
- self.log.info("repre {}".format(repre))
+ self.log.debug("repre {}".format(repre))
if repre["name"] == "thumbnail":
return True
return False
@@ -173,20 +173,20 @@ class ExtractThumbnail(pyblish.api.InstancePlugin):
return filtered_repres
def create_thumbnail_oiio(self, src_path, dst_path):
- self.log.info("outputting {}".format(dst_path))
+ self.log.info("Extracting thumbnail {}".format(dst_path))
oiio_tool_path = get_oiio_tools_path()
oiio_cmd = [
oiio_tool_path,
"-a", src_path,
"-o", dst_path
]
- self.log.info("running: {}".format(" ".join(oiio_cmd)))
+ self.log.debug("running: {}".format(" ".join(oiio_cmd)))
try:
run_subprocess(oiio_cmd, logger=self.log)
return True
except Exception:
self.log.warning(
- "Failed to create thubmnail using oiiotool",
+ "Failed to create thumbnail using oiiotool",
exc_info=True
)
return False
diff --git a/openpype/plugins/publish/extract_thumbnail_from_source.py b/openpype/plugins/publish/extract_thumbnail_from_source.py
index a92f762cde..a9c95d6065 100644
--- a/openpype/plugins/publish/extract_thumbnail_from_source.py
+++ b/openpype/plugins/publish/extract_thumbnail_from_source.py
@@ -39,7 +39,7 @@ class ExtractThumbnailFromSource(pyblish.api.InstancePlugin):
self._create_context_thumbnail(instance.context)
subset_name = instance.data["subset"]
- self.log.info(
+ self.log.debug(
"Processing instance with subset name {}".format(subset_name)
)
thumbnail_source = instance.data.get("thumbnailSource")
@@ -104,7 +104,7 @@ class ExtractThumbnailFromSource(pyblish.api.InstancePlugin):
full_output_path = os.path.join(dst_staging, dst_filename)
if oiio_supported:
- self.log.info("Trying to convert with OIIO")
+ self.log.debug("Trying to convert with OIIO")
# If the input can read by OIIO then use OIIO method for
# conversion otherwise use ffmpeg
thumbnail_created = self.create_thumbnail_oiio(
diff --git a/openpype/plugins/publish/integrate.py b/openpype/plugins/publish/integrate.py
index 8e984a9e97..f392cf67f7 100644
--- a/openpype/plugins/publish/integrate.py
+++ b/openpype/plugins/publish/integrate.py
@@ -267,7 +267,7 @@ class IntegrateAsset(pyblish.api.InstancePlugin):
instance_stagingdir = instance.data.get("stagingDir")
if not instance_stagingdir:
- self.log.info((
+ self.log.debug((
"{0} is missing reference to staging directory."
" Will try to get it from representation."
).format(instance))
@@ -480,7 +480,7 @@ class IntegrateAsset(pyblish.api.InstancePlugin):
update_data
)
- self.log.info("Prepared subset: {}".format(subset_name))
+ self.log.debug("Prepared subset: {}".format(subset_name))
return subset_doc
def prepare_version(self, instance, op_session, subset_doc, project_name):
@@ -521,7 +521,9 @@ class IntegrateAsset(pyblish.api.InstancePlugin):
project_name, version_doc["type"], version_doc
)
- self.log.info("Prepared version: v{0:03d}".format(version_doc["name"]))
+ self.log.debug(
+ "Prepared version: v{0:03d}".format(version_doc["name"])
+ )
return version_doc
diff --git a/openpype/plugins/publish/integrate_legacy.py b/openpype/plugins/publish/integrate_legacy.py
index c67ce62bf6..c238cca633 100644
--- a/openpype/plugins/publish/integrate_legacy.py
+++ b/openpype/plugins/publish/integrate_legacy.py
@@ -147,7 +147,9 @@ class IntegrateAssetNew(pyblish.api.InstancePlugin):
def process(self, instance):
if instance.data.get("processedWithNewIntegrator"):
- self.log.info("Instance was already processed with new integrator")
+ self.log.debug(
+ "Instance was already processed with new integrator"
+ )
return
for ef in self.exclude_families:
@@ -274,7 +276,7 @@ class IntegrateAssetNew(pyblish.api.InstancePlugin):
stagingdir = instance.data.get("stagingDir")
if not stagingdir:
- self.log.info((
+ self.log.debug((
"{0} is missing reference to staging directory."
" Will try to get it from representation."
).format(instance))
diff --git a/openpype/plugins/publish/integrate_thumbnail.py b/openpype/plugins/publish/integrate_thumbnail.py
index 16cc47d432..f6d4f654f5 100644
--- a/openpype/plugins/publish/integrate_thumbnail.py
+++ b/openpype/plugins/publish/integrate_thumbnail.py
@@ -41,7 +41,7 @@ class IntegrateThumbnails(pyblish.api.ContextPlugin):
# Filter instances which can be used for integration
filtered_instance_items = self._prepare_instances(context)
if not filtered_instance_items:
- self.log.info(
+ self.log.debug(
"All instances were filtered. Thumbnail integration skipped."
)
return
@@ -162,7 +162,7 @@ class IntegrateThumbnails(pyblish.api.ContextPlugin):
# Skip instance if thumbnail path is not available for it
if not thumbnail_path:
- self.log.info((
+ self.log.debug((
"Skipping thumbnail integration for instance \"{}\"."
" Instance and context"
" thumbnail paths are not available."
diff --git a/openpype/settings/defaults/project_settings/nuke.json b/openpype/settings/defaults/project_settings/nuke.json
index a1e2e5c15b..5262694484 100644
--- a/openpype/settings/defaults/project_settings/nuke.json
+++ b/openpype/settings/defaults/project_settings/nuke.json
@@ -397,7 +397,39 @@
false
]
]
- }
+ },
+ "reposition_nodes": [
+ {
+ "node_class": "Reformat",
+ "knobs": [
+ {
+ "type": "text",
+ "name": "type",
+ "value": "to format"
+ },
+ {
+ "type": "text",
+ "name": "format",
+ "value": "HD_1080"
+ },
+ {
+ "type": "text",
+ "name": "filter",
+ "value": "Lanczos6"
+ },
+ {
+ "type": "bool",
+ "name": "black_outside",
+ "value": true
+ },
+ {
+ "type": "bool",
+ "name": "pbb",
+ "value": false
+ }
+ ]
+ }
+ ]
},
"ExtractReviewData": {
"enabled": false
diff --git a/openpype/settings/entities/lib.py b/openpype/settings/entities/lib.py
index 1c7dc9bed0..93abc27b0e 100644
--- a/openpype/settings/entities/lib.py
+++ b/openpype/settings/entities/lib.py
@@ -323,7 +323,10 @@ class SchemasHub:
filled_template = self._fill_template(
schema_data, template_def
)
- return filled_template
+ new_template_def = []
+ for item in filled_template:
+ new_template_def.extend(self.resolve_schema_data(item))
+ return new_template_def
def create_schema_object(self, schema_data, *args, **kwargs):
"""Create entity for passed schema data.
diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_nuke_publish.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_nuke_publish.json
index ce9fa04c6a..3019c9b1b5 100644
--- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_nuke_publish.json
+++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_nuke_publish.json
@@ -158,10 +158,43 @@
"label": "Nodes",
"collapsible": true,
"children": [
+ {
+ "type": "label",
+ "label": "Nodes attribute will be deprecated in future releases. Use reposition_nodes instead."
+ },
{
"type": "raw-json",
"key": "nodes",
- "label": "Nodes"
+ "label": "Nodes [depricated]"
+ },
+ {
+ "type": "label",
+ "label": "Reposition knobs supported only. You can add multiple reformat nodes
and set their knobs. Order of reformat nodes is important. First reformat node
will be applied first and last reformat node will be applied last."
+ },
+ {
+ "key": "reposition_nodes",
+ "type": "list",
+ "label": "Reposition nodes",
+ "object_type": {
+ "type": "dict",
+ "children": [
+ {
+ "key": "node_class",
+ "label": "Node class",
+ "type": "text"
+ },
+ {
+ "type": "schema_template",
+ "name": "template_nuke_knob_inputs",
+ "template_data": [
+ {
+ "label": "Node knobs",
+ "key": "knobs"
+ }
+ ]
+ }
+ ]
+ }
}
]
}
diff --git a/openpype/version.py b/openpype/version.py
index 3d7f64b991..342bbfc85a 100644
--- a/openpype/version.py
+++ b/openpype/version.py
@@ -1,3 +1,3 @@
# -*- coding: utf-8 -*-
"""Package declaring Pype version."""
-__version__ = "3.15.8-nightly.3"
+__version__ = "3.15.8"
diff --git a/pyproject.toml b/pyproject.toml
index 190ecb9329..a72a3d66d7 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[tool.poetry]
name = "OpenPype"
-version = "3.15.7" # OpenPype
+version = "3.15.8" # OpenPype
description = "Open VFX and Animation pipeline with support."
authors = ["OpenPype Team "]
license = "MIT License"