Skip to content

validate_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
 27
 28
 29
 30
 31
 32
 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
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, RepairContextAction]

    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.
        """
        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
            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")

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

        if self._is_workfile_extension_align_with_extension_mapping(context):
            self.log.warning(
                "Workfile extension is not aligned with the extension mapping."
                " Skipping unknown nodes validation to prevent false"
                " positives."
            )
            return

        invalid = self.get_invalid(context)
        if invalid:
            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 "Repair"
            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
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
def process(self, context):
    """Process all the nodes in the instance"""
    if not self.is_active(context.data):
        return

    if self._is_workfile_extension_align_with_extension_mapping(context):
        self.log.warning(
            "Workfile extension is not aligned with the extension mapping."
            " Skipping unknown nodes validation to prevent false"
            " positives."
        )
        return

    invalid = self.get_invalid(context)
    if invalid:
        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)