Skip to content

validate_unknown_nodes

RemoveUnknownNodesAction

Bases: RepairContextAction

Repair action to remove unknown nodes from the scene.

Source code in client/ayon_maya/plugins/publish/validate_unknown_nodes.py
27
28
29
30
class RemoveUnknownNodesAction(RepairContextAction):
    """Repair action to remove unknown nodes from the scene."""

    label = "Remove Unknown Nodes"

ValidateSceneUnknownNodes

Bases: ContextPlugin, OptionalPyblishPluginMixin

Checks to see if there are any unknown nodes in the scene.

This often happens if nodes from plug-ins are used but are not available on this machine.

Some studios use unknown nodes to store data on (as attributes)

because it's a lightweight node.

This differs from validate no unknown nodes since it checks the full scene - not just the nodes in the instance.

Source code in client/ayon_maya/plugins/publish/validate_unknown_nodes.py
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
class ValidateSceneUnknownNodes(pyblish.api.ContextPlugin,
                                OptionalPyblishPluginMixin):
    """Checks to see if there are any unknown nodes in the scene.

    This often happens if nodes from plug-ins are used but are not available
    on this machine.

    Note: Some studios use unknown nodes to store data on (as attributes)
        because it's a lightweight node.

    This differs from validate no unknown nodes since it checks the
    full scene - not just the nodes in the instance.

    """

    order = ValidateContentsOrder
    hosts = ['maya']
    families = ["model", "rig", "mayaScene", "look", "renderlayer", "yetiRig"]
    optional = True
    label = "Unknown Nodes in Scene"
    actions = [SelectInvalidAction, RemoveUnknownNodesAction]

    def _is_workfile_extension_align_with_extension_mapping(self, context) -> bool:
        """Check if the workfile extension is aligned with the extension mapping.

        This is to prevent false positives when the workfile extension is not
        aligned with the extension mapping.

        Args:
            context (pyblish.api.Context): The publish context.

        Returns:
            bool: True if the workfile extension is aligned with the extension
                mapping, False otherwise.
        """
        maya_settings = context.data["project_settings"]["maya"]
        ext_mapping = {
            item["name"]: item["value"]
            for item in maya_settings["ext_mapping"]
        }
        current_file = context.data["currentFile"]
        if not current_file:
            # Unsaved file: can't validate extension alignment; allow validation to run
            return True
        workfile_extension = os.path.splitext(current_file)[-1].strip(".")
        for instance in context:
            # Skip inactivate instances
            if not instance.data.get("publish", True):
                continue
            # Consider only relevant instances
            if not pyblish.logic.plugins_by_families(
                [self.__class__],
                self.families
            ):
                continue
            instance_extension = ext_mapping.get(
                instance.data["productBaseType"]
            )
            if workfile_extension != instance_extension:
                return False
        return True

    @staticmethod
    def get_invalid(context) -> list:
        return cmds.ls(type=("unknown", "unknownDag"), long=True)

    def process(self, context):
        """Process all the nodes in the instance"""
        if not self.is_active(context.data):
            return

        invalid = self.get_invalid(context)
        if invalid:
            self.log.debug(f"Unknown {len(invalid)} nodes found: {invalid}")
            if not self._is_workfile_extension_align_with_extension_mapping(context):
                self.log.warning(
                    "Allowing unknown nodes because file extension matches publish file "
                    "export extensions. Skipping unknown nodes validation."
                )
                return

            raise PublishValidationError(
                "Unknown nodes found: {0}".format(invalid),
                description=self.get_description()
            )

    @classmethod
    def repair(cls, context):
        for node in cls.get_invalid(context):
            try:
                force_delete(node)
            except RuntimeError as exc:
                cls.log.error(exc)

    @staticmethod
    def get_description() -> str:
        return inspect.cleandoc("""
            ## Unknown Nodes Found

            Unknown nodes were found in the scene. This often happens if nodes
            from plug-ins are used but are not available on this machine.
            Note: Some studios use unknown nodes to store data on (as
            attributes) because it's a lightweight node.

            ### How to Fix

            You can either:
            - Install the missing plug-in that the unknown nodes belong to.
            - Delete the unknown nodes from the scene. You can use the "Remove
            Unknown Nodes" action to automatically delete the unknown nodes.
        """)

process(context)

Process all the nodes in the instance

Source code in client/ayon_maya/plugins/publish/validate_unknown_nodes.py
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
def process(self, context):
    """Process all the nodes in the instance"""
    if not self.is_active(context.data):
        return

    invalid = self.get_invalid(context)
    if invalid:
        self.log.debug(f"Unknown {len(invalid)} nodes found: {invalid}")
        if not self._is_workfile_extension_align_with_extension_mapping(context):
            self.log.warning(
                "Allowing unknown nodes because file extension matches publish file "
                "export extensions. Skipping unknown nodes validation."
            )
            return

        raise PublishValidationError(
            "Unknown nodes found: {0}".format(invalid),
            description=self.get_description()
        )

force_delete(node)

Forcefully deletes a node in the Maya scene.

Parameters:

Name Type Description Default
node str

invalid node to delete

required
Source code in client/ayon_maya/plugins/publish/validate_unknown_nodes.py
16
17
18
19
20
21
22
23
24
def force_delete(node: str) -> None:
    """Forcefully deletes a node in the Maya scene.

    Args:
        node (str): invalid node to delete
    """
    if cmds.objExists(node):
        cmds.lockNode(node, lock=False)
        cmds.delete(node)