generated from ynput/ayon-addon-template
-
Notifications
You must be signed in to change notification settings - Fork 9
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'develop' into enhancement/alembic_exclude_parent_hierar…
…chy_validate_parent_isbility
- Loading branch information
Showing
1 changed file
with
168 additions
and
0 deletions.
There are no files selected for viewing
168 changes: 168 additions & 0 deletions
168
client/ayon_maya/plugins/publish/validate_clashing_sibling_names.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,168 @@ | ||
from collections import defaultdict | ||
import inspect | ||
|
||
import pyblish.api | ||
from ayon_core.pipeline.publish import ( | ||
OptionalPyblishPluginMixin, | ||
PublishValidationError, | ||
ValidateContentsOrder, | ||
) | ||
|
||
import ayon_maya.api.action | ||
from ayon_maya.api import plugin, lib | ||
|
||
|
||
def remove_namespace(path: str) -> str: | ||
"""Remove namespace from full path. | ||
Example: | ||
>>> remove_namespace("|aa:bb:foo|aa:bb:bar|cc:hello|dd:world") | ||
'|foo|bar|hello|world' | ||
Arguments: | ||
path (str): Full node path. | ||
Returns: | ||
str: Node path with namespaces removed. | ||
""" | ||
return "|".join( | ||
name.rsplit(":", 1)[-1] for name in path.split("|") | ||
) | ||
|
||
|
||
class ValidateClashingSiblingNames(plugin.MayaInstancePlugin, | ||
OptionalPyblishPluginMixin): | ||
"""Validate siblings have unique names when namespaces are stripped.""" | ||
|
||
order = ValidateContentsOrder | ||
families = ["pointcache", "animation", "usd"] | ||
label = "Validate clashing sibling names" | ||
actions = [ayon_maya.api.action.SelectInvalidAction] | ||
|
||
@staticmethod | ||
def get_invalid(instance): | ||
"""Return all nodes that have non-unique names with siblings when | ||
namespaces are stripped. | ||
Returns: | ||
list[str]: Non-unique siblings | ||
""" | ||
stripped_name_to_full_path = defaultdict(set) | ||
for node in instance: | ||
stripped_name = remove_namespace(node) | ||
stripped_name_to_full_path[stripped_name].add(node) | ||
|
||
invalid: "list[str]" = [] | ||
for _stripped_name, nodes in stripped_name_to_full_path.items(): | ||
if len(nodes) > 1: | ||
invalid.extend(nodes) | ||
|
||
if invalid: | ||
# We only care about the highest conflicts since child conflicts | ||
# only occur due to the conflicts higher up anyway | ||
invalid = lib.get_highest_in_hierarchy(invalid) | ||
|
||
return invalid | ||
|
||
def process(self, instance): | ||
"""Process all the nodes in the instance "objectSet""" | ||
if not self.is_active(instance.data): | ||
return | ||
|
||
if not self.is_strip_namespaces_enabled(instance): | ||
return | ||
|
||
invalid = self.get_invalid(instance) | ||
if invalid: | ||
|
||
report_list = "\n".join(f"- {node}" for node in sorted(invalid)) | ||
|
||
raise PublishValidationError( | ||
"With stripped namespaces there are conflicting sibling names " | ||
"that are not unique:\n" | ||
f"{report_list}", | ||
description=self.get_description()) | ||
|
||
def is_strip_namespaces_enabled(self, instance) -> bool: | ||
"""Return whether any extractor is enabled for instance that has | ||
`stripNamespaces` enabled.""" | ||
# TODO: Preferably there would be a better way to detect whether the | ||
# flag was enabled or not. | ||
|
||
plugins = instance.context.data["create_context"].publish_plugins | ||
plugins_by_name = {plugin.__name__: plugin for plugin in plugins} | ||
|
||
def _is_plugin_active(plugin_name: str) -> bool: | ||
"""Return whether plugin is active for instance""" | ||
# Check if Plug-in is found | ||
plugin = plugins_by_name.get(plugin_name) | ||
if not plugin: | ||
self.log.debug(f"Plugin {plugin_name} not found. " | ||
"It may be disabled in settings") | ||
return False | ||
|
||
# Check if plug-in is globally enabled | ||
if not getattr(plugin, "enabled", True): | ||
self.log.debug(f"Plugin {plugin_name} is disabled. " | ||
"It is disabled in settings") | ||
return False | ||
|
||
# Check if optional state has active state set to False | ||
publish_attributes = instance.data["publish_attributes"] | ||
default_active = getattr(plugin, "active", True) | ||
active_for_instance = publish_attributes.get( | ||
plugin_name, {}).get("active", default_active) | ||
if not active_for_instance: | ||
self.log.debug( | ||
f"Plugin {plugin_name} is disabled for this instance.") | ||
return False | ||
|
||
# Check if the instance, according to pyblish is a match for the | ||
# plug-in. This may e.g. be excluded due to different families | ||
# or matching algorithm (e.g. ExtractMultiverseUsdAnim uses | ||
# `pyblish.api.Subset` | ||
if not pyblish.api.instances_by_plugin([instance], plugin): | ||
self.log.debug( | ||
f"Plugin {plugin_name} does not match for this instance.") | ||
return False | ||
|
||
return True | ||
|
||
for plugin_name in [ | ||
"ExtractAlembic", # pointcache | ||
"ExtractAnimation", # animation | ||
"ExtractMayaUsd", # usd | ||
"ExtractMayaUsdPointcache", # pointcache | ||
"ExtractMayaUsdAnim", # animation | ||
]: | ||
if _is_plugin_active(plugin_name): | ||
plugin = plugins_by_name[plugin_name] | ||
|
||
# Use the value from the instance publish attributes | ||
publish_attributes = instance.data["publish_attributes"] | ||
strip_namespaces = publish_attributes.get( | ||
plugin_name, {}).get("stripNamespaces") | ||
if strip_namespaces: | ||
return True | ||
|
||
# Find some default on the plugin class, if any | ||
default = getattr(plugin, "stripNamespaces", False) | ||
if default: | ||
self.log.debug( | ||
f"{plugin_name} has strip namespaces enabled as " | ||
"default value.") | ||
return True | ||
return False | ||
|
||
def get_description(self): | ||
return inspect.cleandoc(""" | ||
### Clashing sibling names with stripped namespaces | ||
The export has **strip namespaces** enabled but a conflict on | ||
sibling names are found where, without namespaces, they do not have | ||
unique names and can not be exported. | ||
To resolve this, either export with 'strip namespaces' disabled or | ||
reorder the hierarchy so that nodes sharing the parent do not have | ||
the same name. | ||
""") |