Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

User friendly error messages #193

Merged
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -30,19 +30,22 @@ def process(self, instance):

node = hou.node(instance.data["instance_node"])

try:
node.cook(force=True) # required to clear existing errors
except hou.Error as exc:
errors = node.errors()
MustafaJafar marked this conversation as resolved.
Show resolved Hide resolved
if errors:
errors = "\n\n - ".join(errors)
raise PublishError(
f"Failed to cook node: '{node.path()}'. Please fix it to proceed.",
detail=f"{exc} \n\n - {errors}"
)
MustafaJafar marked this conversation as resolved.
Show resolved Hide resolved

# Render the component builder LOPs
# TODO: Do we want this? or use existing frames? Usually a Collector
# should not 'extract' but in this case we need the resulting USD
# file.
node.cook(force=True) # required to clear existing errors
node.parm("execute").pressButton()
MustafaJafar marked this conversation as resolved.
Show resolved Hide resolved

errors = node.errors()
if errors:
for error in errors:
self.log.error(error)
raise PublishError(f"Failed to save to disk '{node.path()}'")

# Define the main asset usd file
filepath = node.evalParm("lopoutput")
representations = instance.data.setdefault("representations", [])
Expand Down
3 changes: 2 additions & 1 deletion client/ayon_houdini/plugins/publish/collect_output_node.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,8 @@ def process(self, instance):

else:
raise KnownPublishError(
"ROP node type '{}' is not supported.".format(node_type)
f"ROP node type '{node_type}' is not supported"
f" for product type '{instance.data['product_type']}'"
)

if not out_node:
Expand Down
10 changes: 6 additions & 4 deletions client/ayon_houdini/plugins/publish/collect_render_products.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

import pyblish.api

from ayon_core.pipeline import PublishError
from ayon_houdini.api import plugin
from ayon_houdini.api.usd import (
get_usd_render_rop_rendersettings
Expand Down Expand Up @@ -107,10 +108,11 @@ def replace(match):
filename = os.path.join(dirname, filename_base)
filename = filename.replace("\\", "/")

assert "#" in filename, (
"Couldn't resolve render product name "
"with frame number: %s" % name
)
if "#" not in filename:
raise PublishError(
"Couldn't resolve render product output file"
f" '{name}' with frame number."
)

filenames.append(filename)

Expand Down
7 changes: 4 additions & 3 deletions client/ayon_houdini/plugins/publish/collect_usd_layers.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import pyblish.api

from ayon_core.pipeline import KnownPublishError
from ayon_core.pipeline.create import get_product_name
from ayon_houdini.api import plugin
import ayon_houdini.api.usd as usdlib
Expand All @@ -27,8 +28,7 @@ def copy_instance_data(instance_src, instance_dest, attr):
in the source instance's data.

Raises:
KeyError: If the key does not exist on the source instance.
AssertionError: If a parent key already exists on the destination
KnownPublishError: If a parent key already exists on the destination
instance but is not of the correct type (= is not a dict)

"""
Expand All @@ -43,7 +43,8 @@ def copy_instance_data(instance_src, instance_dest, attr):
src_value = src_data[key]
if i != len(key):
dest_data = dest_data.setdefault(key, {})
assert isinstance(dest_data, dict), "Destination must be a dict"
if not isinstance(dest_data, dict):
raise KnownPublishError("Destination must be a dict.")
src_data = src_value
else:
# Last iteration - assign the value
Expand Down
3 changes: 2 additions & 1 deletion client/ayon_houdini/plugins/publish/extract_hda.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,8 @@ def process(self, instance):
parm_folder = parm_group.findFolder("Extra")
if not parm_folder:
raise PublishError(
f"Extra parm folder does not exist: {hda_node.path()}"
f"Extra AYON parm folder does not exist on {hda_node.path()}\n\n"
"Please select the node and create an HDA product from the publisher UI."
)

# Remove `Extra` AYON parameters
Expand Down
14 changes: 8 additions & 6 deletions client/ayon_houdini/plugins/publish/extract_render.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

import pyblish.api

from ayon_core.pipeline import PublishError
from ayon_houdini.api import plugin


Expand Down Expand Up @@ -75,13 +76,14 @@ def process(self, instance):
all_frames.extend(value)
# Check missing frames.
# Frames won't exist if user cancels the render.
missing_frames = [
missing_frames = "\n\n - ".join(
MustafaJafar marked this conversation as resolved.
Show resolved Hide resolved
frame
for frame in all_frames
if not os.path.exists(frame)
]
)
if missing_frames:
# TODO: Use user friendly error reporting.
raise RuntimeError("Failed to complete render extraction. "
"Missing output files: {}".format(
missing_frames))
raise PublishError(
"Failed to complete render extraction.\n"
"Please render any missing output files.",
detail=f"Missing output files: \n\n - {missing_frames}"
)
MustafaJafar marked this conversation as resolved.
Show resolved Hide resolved
12 changes: 8 additions & 4 deletions client/ayon_houdini/plugins/publish/extract_rop.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import os
import pyblish.api

from ayon_core.pipeline import publish
from ayon_core.pipeline import publish, PublishError
from ayon_houdini.api import plugin
from ayon_houdini.api.lib import splitext

Expand Down Expand Up @@ -68,12 +68,16 @@ def validate_expected_frames(self, instance: pyblish.api.Instance):
# Single frame
filenames = [filenames]

missing_filenames = [
missing_filenames = "\n\n - ".join(
filename for filename in filenames
if not os.path.isfile(os.path.join(staging_dir, filename))
]
)
if missing_filenames:
raise RuntimeError(f"Missing frames: {missing_filenames}")
raise PublishError(
"Failed to complete render extraction.\n"
"Please render any missing output files.",
detail=f"Missing output files: \n\n - {missing_filenames}"
)

def update_representation_data(self,
instance: pyblish.api.Instance,
Expand Down
9 changes: 6 additions & 3 deletions client/ayon_houdini/plugins/publish/extract_usd.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

import pyblish.api

from ayon_core.pipeline import KnownPublishError, PublishError
from ayon_core.pipeline.entity_uri import construct_ayon_entity_uri
from ayon_core.pipeline.publish.lib import get_instance_expected_output_path
from ayon_houdini.api import plugin
Expand Down Expand Up @@ -47,7 +48,8 @@ def process(self, instance):
with remap_paths(ropnode, mapping):
render_rop(ropnode)

assert os.path.exists(output), "Output does not exist: %s" % output
if not os.path.exists(output):
PublishError(f"Output does not exist: {output}")

if "representations" not in instance.data:
instance.data["representations"] = []
Expand Down Expand Up @@ -135,5 +137,6 @@ def get_source_paths(
# Single file
return [os.path.join(staging, files)]

raise TypeError(f"Unsupported type for representation files: {files} "
"(supports list or str)")
raise KnownPublishError(
f"Unsupported type for representation files: {files} (supports list or str)"
)
24 changes: 18 additions & 6 deletions client/ayon_houdini/plugins/publish/increment_current_file.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import inspect
import pyblish.api

from ayon_core.lib import version_up
from ayon_core.pipeline import registered_host
from ayon_core.pipeline.publish import (
get_errored_plugins_from_context,
KnownPublishError
from ayon_core.pipeline import (
registered_host,
KnownPublishError,
PublishError
)
from ayon_core.pipeline.publish import get_errored_plugins_from_context

from ayon_houdini.api import plugin

Expand Down Expand Up @@ -46,9 +48,19 @@ def process(self, context):
host = registered_host()
current_file = host.current_file()
if context.data["currentFile"] != current_file:
raise KnownPublishError(
"Collected filename mismatches from current scene name."
raise PublishError(
MustafaJafar marked this conversation as resolved.
Show resolved Hide resolved
f"Collected filename '{context.data['currentFile']}' differs"
f" from current scene name '{current_file}'.",
description=self.get_error_description()
)

new_filepath = version_up(current_file)
host.save_workfile(new_filepath)

def get_error_description(self):
return inspect.cleandoc(
"""### Scene File Name Change During Publishing
This error occurs when you validate the scene and then save it as a new file manually, or if you open a new file and continue publishing.
Please reset the publisher and publish without changing the scene file midway.
"""
)
22 changes: 17 additions & 5 deletions client/ayon_houdini/plugins/publish/save_scene.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import inspect
import pyblish.api

from ayon_core.pipeline import registered_host
from ayon_core.pipeline import registered_host, PublishError

from ayon_houdini.api import plugin

Expand All @@ -16,12 +17,23 @@ def process(self, context):
# Filename must not have changed since collecting
host = registered_host()
current_file = host.get_current_workfile()
assert context.data['currentFile'] == current_file, (
"Collected filename from current scene name."
)

if context.data['currentFile'] != current_file:
MustafaJafar marked this conversation as resolved.
Show resolved Hide resolved
raise PublishError(
f"Collected filename '{context.data['currentFile']}' differs"
f" from current scene name '{current_file}'.",
description=self.get_error_description()
)
if host.workfile_has_unsaved_changes():
self.log.info("Saving current file: {}".format(current_file))
host.save_workfile(current_file)
else:
self.log.debug("No unsaved changes, skipping file save..")


def get_error_description(self):
return inspect.cleandoc(
"""### Scene File Name Change During Publishing
MustafaJafar marked this conversation as resolved.
Show resolved Hide resolved
This error occurs when you validate the scene and then save it as a new file manually, or if you open a new file and continue publishing.
Please reset the publisher and publish without changing the scene file midway.
"""
)
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,12 @@ def process(self, instance):
invalid = self.get_invalid(instance)
if invalid:
raise PublishValidationError(
("Primitives found with inconsistent primitive "
"to detail attributes. See log."),
title=self.label
"Primitives found with inconsistent primitive "
"to detail attributes.",
detail=(
"See log for more info."
f"Incorrect Rop(s)\n\n - {invalid[0].pah()}"
)
)

@classmethod
Expand Down
3 changes: 2 additions & 1 deletion client/ayon_houdini/plugins/publish/validate_frame_token.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import pyblish.api

from ayon_core.pipeline import PublishValidationError
from ayon_houdini.api import lib, plugin


Expand Down Expand Up @@ -31,7 +32,7 @@ def process(self, instance):

invalid = self.get_invalid(instance)
if invalid:
raise RuntimeError(
raise PublishValidationError(
"Output settings do no match for '%s'" % instance
)

Expand Down
19 changes: 11 additions & 8 deletions client/ayon_houdini/plugins/publish/validate_sop_output_node.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,10 @@ def process(self, instance):
invalid = self.get_invalid(instance)
if invalid:
raise PublishValidationError(
"Output node(s) are incorrect",
title="Invalid output node(s)"
"Output node(s) are incorrect.",
detail=(
f"Incorrect output SOP path on Rop(s)\n\n - {invalid[0].path()}"
)
)

@classmethod
Expand Down Expand Up @@ -65,9 +67,9 @@ def get_invalid(cls, instance):
# the isinstance check above should be stricter than this category
if output_node.type().category().name() != "Sop":
raise PublishValidationError(
("Output node {} is not of category Sop. "
"This is a bug.").format(output_node.path()),
title=cls.label)
f"Output node {output_node.path()} is not of category Sop.",
title=cls.label
)

# Ensure the node is cooked and succeeds to cook so we can correctly
# check for its geometry data.
Expand All @@ -76,9 +78,10 @@ def get_invalid(cls, instance):
try:
output_node.cook()
except hou.Error as exc:
cls.log.error("Cook failed: %s" % exc)
cls.log.error(output_node.errors()[0])
return [output_node]
raise PublishValidationError(
f"Failed to cook node: {output_node.path()}.",
detail=str(exc)
)

# Ensure the output node has at least Geometry data
if not output_node.geometry():
Expand Down
Loading