Skip to content

validate_knobs

ValidateKnobs

Bases: ContextPlugin

Ensure knobs are consistent.

Knobs to validate and their values comes from the

Controlled by plugin settings that require json in following structure

"ValidateKnobs": { "enabled": true, "knobs": { "family": { "knob_name": knob_value } } }

Source code in client/ayon_nuke/plugins/publish/validate_knobs.py
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 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
class ValidateKnobs(pyblish.api.ContextPlugin):
    """Ensure knobs are consistent.

    Knobs to validate and their values comes from the

    Controlled by plugin settings that require json in following structure:
        "ValidateKnobs": {
            "enabled": true,
            "knobs": {
                "family": {
                    "knob_name": knob_value
                    }
                }
            }
    """

    order = pyblish.api.ValidatorOrder
    label = "Validate Knobs"
    hosts = ["nuke"]
    actions = [RepairContextAction]
    optional = True

    settings_category = "nuke"

    knobs = "{}"

    def process(self, context):
        invalid = self.get_invalid(context, compute=True)
        if invalid:
            invalid_items = [
                (
                    "Node __{node_name}__ with knob _{label}_ "
                    "expecting _{expected}_, "
                    "but is set to _{current}_"
                ).format(**i)
                for i in invalid
            ]
            raise PublishXmlValidationError(
                self,
                "Found knobs with invalid values:\n{}".format(invalid),
                formatting_data={
                    "invalid_items": "\n".join(invalid_items)}
            )

    @classmethod
    def get_invalid(cls, context, compute=False):
        invalid = context.data.get("invalid_knobs", [])
        if compute:
            invalid = cls.get_invalid_knobs(context)

        return invalid

    @classmethod
    def get_invalid_knobs(cls, context):
        invalid_knobs = []

        for instance in context:
            # Load fresh knobs data for each instance
            settings_knobs = json.loads(cls.knobs)

            # Filter families.
            families = [instance.data["productType"]]
            families += instance.data.get("families", [])

            # Get all knobs to validate.
            knobs = {}
            for family in families:
                # check if dot in family
                if "." in family:
                    family = family.split(".")[0]

                # avoid families not in settings
                if family not in settings_knobs:
                    continue

                # get presets of knobs
                for preset in settings_knobs[family]:
                    knobs[preset] = settings_knobs[family][preset]

            # Get invalid knobs.
            nodes = []

            for node in nuke.allNodes():
                nodes.append(node)
                if node.Class() == "Group":
                    node.begin()
                    nodes.extend(iter(nuke.allNodes()))
                    node.end()

            for node in nodes:
                for knob in node.knobs():
                    if knob not in knobs.keys():
                        continue

                    expected = knobs[knob]
                    if node[knob].value() != expected:
                        invalid_knobs.append(
                            {
                                "node_name": node.name(),
                                "knob": node[knob],
                                "name": node[knob].name(),
                                "label": node[knob].label(),
                                "expected": expected,
                                "current": node[knob].value()
                            }
                        )

        context.data["invalid_knobs"] = invalid_knobs
        return invalid_knobs

    @classmethod
    def repair(cls, instance):
        invalid = cls.get_invalid(instance)
        for data in invalid:
            # TODO: will need to improve type definitions
            # with the new settings for knob types
            if isinstance(data["expected"], str):
                data["knob"].setValue(str(data["expected"]))
                continue

            data["knob"].setValue(data["expected"])