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

Use PublishError along publishing #190

Closed
Closed
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
1ddddab
refactor `RuntimeError` to `PublishError`
MustafaJafar Dec 4, 2024
ee0745b
refactor `assert` to `PublishError`
MustafaJafar Dec 4, 2024
47cd409
remove redundant `message` argument
MustafaJafar Dec 4, 2024
4502143
better error message in `SaveCurrentScene`
MustafaJafar Dec 4, 2024
6c891cf
add error description in `SaveCurrentScene`
MustafaJafar Dec 4, 2024
fea6de3
refactor some errors to `PublishError`
MustafaJafar Dec 4, 2024
84be3d4
Use `PublishError` to the caller functions in publish plugins for `re…
MustafaJafar Dec 5, 2024
ece72b3
Move the `PublishError` to the collector
MustafaJafar Dec 5, 2024
65249cc
revert `copy_instance_data` and move the `PublishError` to the `proce…
MustafaJafar Dec 5, 2024
bde4ab3
replace `●` with regular dash `-`
MustafaJafar Dec 5, 2024
0363a57
move `evalParmNoFrame` to `HoudiniInstancePlugin`
MustafaJafar Dec 5, 2024
bb44614
Use `PublishValidationError` instead and use better log message
MustafaJafar Dec 6, 2024
452f73e
remove redundant validator
MustafaJafar Dec 6, 2024
ce5a314
Move `publishError` to the caller publish plugin
MustafaJafar Dec 6, 2024
6de4af0
Move the `PublishError` to `HoudiniInstancePlugin`
MustafaJafar Dec 6, 2024
33c2bb3
revert changes in `get_top_referenced_parm`
MustafaJafar Dec 6, 2024
15dbe43
Implement `PublishError` when calling colorspace function.
MustafaJafar Dec 6, 2024
a03e49b
refactor `f{exc}` to `str(exc)`
MustafaJafar Dec 9, 2024
8bb8bf7
add missing argument
MustafaJafar Dec 9, 2024
107012c
refactor `HoudiniInstancePlugin.evalParmNoFrame` to `HoudiniInstanceP…
MustafaJafar Dec 9, 2024
d92689e
Fix `eval_parm_no_frame` call
MustafaJafar Dec 9, 2024
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
30 changes: 21 additions & 9 deletions client/ayon_houdini/api/lib.py
MustafaJafar marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
from ayon_core.pipeline.workfile.workfile_template_builder import (
TemplateProfileNotFound
)
from ayon_core.pipeline.publish import PublishError
from ayon_core.tools.utils import PopupUpdateKeys, SimplePopup
from ayon_core.tools.utils.host_tools import get_tool_by_name

Expand Down Expand Up @@ -286,9 +287,11 @@ def render_rop(ropnode, frame_range=None):
# The hou.Error is not inherited from a Python Exception class,
# so we explicitly capture the houdini error, otherwise pyblish
# will remain hanging.
import traceback
traceback.print_exc()
raise RuntimeError("Render failed: {0}".format(exc))
raise PublishError(
message="Render failed or interrupted",
description=f"An Error occurred while rendering {ropnode.path()}",
detail=f"{exc}"
)


def imprint(node, data, update=False):
Expand Down Expand Up @@ -655,7 +658,9 @@ def get_top_referenced_parm(parm):
processed = set() # disallow infinite loop
while True:
if parm.path() in processed:
raise RuntimeError("Parameter references result in cycle.")
raise PublishError(
message="Parameter references result in cycle.",
)

processed.add(parm.path())

Expand All @@ -671,7 +676,10 @@ def get_top_referenced_parm(parm):
def evalParmNoFrame(node, parm, pad_character="#"):

parameter = node.parm(parm)
assert parameter, "Parameter does not exist: %s.%s" % (node, parm)
if not parameter:
raise PublishError(
message=f"Parameter does not exist: {node}.{parm}",
)
MustafaJafar marked this conversation as resolved.
Show resolved Hide resolved

# If the parameter has a parameter reference, then get that
# parameter instead as otherwise `unexpandedString()` fails.
Expand All @@ -681,8 +689,10 @@ def evalParmNoFrame(node, parm, pad_character="#"):
try:
raw = parameter.unexpandedString()
except hou.Error as exc:
print("Failed: %s" % parameter)
raise RuntimeError(exc)
raise PublishError(
message=f"Failed: {parameter}",
detail=f"{exc}"
)

def replace(match):
padding = 1
Expand Down Expand Up @@ -726,8 +736,10 @@ def get_color_management_preferences():
config_path = preferences["config"]
config = PyOpenColorIO.Config.CreateFromFile(config_path)
display = config.getDefaultDisplay()
assert display == preferences["display"], \
"Houdini default OCIO display must match config default display"
if display != preferences["display"]:
raise PublishError(
message="Houdini default OCIO display must match config default display"
)
view = config.getDefaultView(display)
preferences["display"] = display
preferences["view"] = view
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.publish 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(
message="Couldn't resolve render product name"
f" with frame number: {name}"
MustafaJafar marked this conversation as resolved.
Show resolved Hide resolved
)

filenames.append(filename)

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

from ayon_core.pipeline.create import get_product_name
from ayon_core.pipeline.publish import PublishError
from ayon_houdini.api import plugin
import ayon_houdini.api.usd as usdlib

Expand All @@ -27,8 +28,8 @@ 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
PublishError: If the key does not exist on the source instance.
PublishError: If a parent key already exists on the destination
instance but is not of the correct type (= is not a dict)

"""
Expand All @@ -38,12 +39,17 @@ def copy_instance_data(instance_src, instance_dest, attr):
keys = attr.split(".")
for i, key in enumerate(keys):
if key not in src_data:
break
raise PublishError(
message= f"key '{key}' does not exist on the source instance."
)

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 PublishError(
message="Destination must be a dict."
)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This provides way too little detail to be any meaningful error to any artist or alike. What destination where, for which key - and what did it get instead? We're really (I feel) going the wrong way if this can't just be a TypeError but requires a PublishError since we suddenly are introducing a super generic error much less clear? (I mean, still better than the assert, don't get me wrong)

@Illicit thoughts?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should every error (that can be raised along the publishing) be a PublishError ?
I've copula of scattered thoughts.

  • Any error that is not PublishError or PublishValidationError will cause the publisher UI to show the message (It's not your fault etc).
  • Some errors are originated from user interactions e.g. not selecting the correct node or using a wrong colorspace. I believe these definitely should be PublishError and PublishValidationError . These errors can be fixed by the user.
  • Other errors like the one you highlighted above, honestly, I don't know why/how it can happen and if the user has something to do with it. I assume it requires a developer to deal with them. but using PublishError can help developers know about the problem from a screenshot. (Note: I didn't use the title in the PublishError so the publish plugin's label will be used.)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any error that is not PublishError or PublishValidationError will cause the publisher UI to show the message (It's not your fault etc).

Yup - this was a design decision at some point. I'm still not 100% confident that it's the right one, but it did force us into writing better PublishValidationError. If now we're just going to make EVERYTHING PublishError without providing better messages I think we're in the same problematic realm and we could basically revert to allow any error be nicely reported - we'll just still need to make sure that the majority of errors we raise are very clear what do to about them.

Some errors are originated from user interactions e.g. not selecting the correct node or using a wrong colorspace. I believe these definitely should be PublishError and PublishValidationError . These errors can be fixed by the user.

I can't think of any now - but that does sound right. However, the error must be very clear, describe what is wrong and how to go about fixing it, etc.

Other errors like the one you highlighted above, honestly, I don't know why/how it can happen and if the user has something to do with it. I assume it requires a developer to deal with them. but using PublishError can help developers know about the problem from a screenshot. (Note: I didn't use the title in the PublishError so the publish plugin's label will be used.)

Some assertions in the codebase were just there - to assert for a potential edge case where it might happen... not necessarily that they would happen. That's why some might not necessarily be bad to remain asserts (or other regular errors). They were just there because we just really wanted to make sure if the code continued that something behaved a certain way.

src_data = src_value
else:
# Last iteration - assign the value
Expand Down
10 changes: 6 additions & 4 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.publish import PublishError
from ayon_houdini.api import plugin


Expand Down Expand Up @@ -81,7 +82,8 @@ def process(self, instance):
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))
missing_frames = "\n\n ● ".join(missing_frames)
MustafaJafar marked this conversation as resolved.
Show resolved Hide resolved
raise PublishError(
message="Failed to complete render extraction.",
detail=f"Missing output files:\n\n ● {missing_frames}"
)
7 changes: 6 additions & 1 deletion client/ayon_houdini/plugins/publish/extract_rop.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 publish
from ayon_core.pipeline.publish import PublishError
from ayon_houdini.api import plugin
from ayon_houdini.api.lib import splitext

Expand Down Expand Up @@ -73,7 +74,11 @@ def validate_expected_frames(self, instance: pyblish.api.Instance):
if not os.path.isfile(os.path.join(staging_dir, filename))
]
if missing_filenames:
raise RuntimeError(f"Missing frames: {missing_filenames}")
missing_filenames = "\n\n ● ".join(missing_filenames)
raise PublishError(
message="Failed to complete render extraction.",
detail=f"Missing frames:\n\n ● {missing_filenames}"
)

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

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_core.pipeline.publish import PublishError
from ayon_houdini.api import plugin
from ayon_houdini.api.lib import render_rop
from ayon_houdini.api.usd import remap_paths
Expand Down Expand Up @@ -47,7 +48,9 @@ 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(
message=f"Output does not exist: {output}")

if "representations" not in instance.data:
instance.data["representations"] = []
Expand Down
8 changes: 5 additions & 3 deletions client/ayon_houdini/plugins/publish/save_scene.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import pyblish.api

from ayon_core.pipeline import registered_host
from ayon_core.pipeline.publish import PublishError

from ayon_houdini.api import plugin

Expand All @@ -16,9 +17,10 @@ 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:
raise PublishError(
message="Collected filename from current scene name."
BigRoy marked this conversation as resolved.
Show resolved Hide resolved
)

if host.workfile_has_unsaved_changes():
self.log.info("Saving current file: {}".format(current_file))
Expand Down
6 changes: 3 additions & 3 deletions client/ayon_houdini/plugins/publish/validate_frame_token.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import hou

import pyblish.api

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


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

invalid = self.get_invalid(instance)
if invalid:
raise RuntimeError(
"Output settings do no match for '%s'" % instance
raise PublishError(
f"Output settings do no match for '{instance}'"
MustafaJafar marked this conversation as resolved.
Show resolved Hide resolved
)

@classmethod
Expand Down
Loading