Skip to content

validate_loaded_plugin

Validator for Loaded Plugin.

ValidateLoadedPlugin

Bases: OptionalPyblishPluginMixin, InstancePlugin

Validates if the specific plugin is loaded in 3ds max. Studio Admin(s) can add the plugins they want to check in validation via studio defined project settings

Source code in client/ayon_max/plugins/publish/validate_loaded_plugin.py
 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
133
134
135
136
137
138
139
140
141
142
143
class ValidateLoadedPlugin(OptionalPyblishPluginMixin,
                           pyblish.api.InstancePlugin):
    """Validates if the specific plugin is loaded in 3ds max.
    Studio Admin(s) can add the plugins they want to check in validation
    via studio defined project settings
    """

    order = pyblish.api.ValidatorOrder
    hosts = ["max"]
    label = "Validate Loaded Plugins"
    optional = True
    actions = [RepairAction]

    settings_category = "max"

    family_plugins_mapping = []

    @classmethod
    def get_invalid(cls, instance):
        """Plugin entry point."""
        family_plugins_mapping = cls.family_plugins_mapping
        if not family_plugins_mapping:
            return

        # Backward compatibility - settings did have 'product_types'
        if "product_types" in family_plugins_mapping:
            family_plugins_mapping["families"] = family_plugins_mapping.pop(
                "product_types"
            )

        invalid = []
        # Find all plug-in requirements for current instance
        instance_families = {instance.data["productType"]}
        instance_families.update(instance.data.get("families", []))
        cls.log.debug("Checking plug-in validation "
                      f"for instance families: {instance_families}")
        all_required_plugins = set()

        for mapping in family_plugins_mapping:
            # Check for matching families
            if not mapping:
                return

            match_families = {
                fam.strip() for fam in mapping["families"]
            }
            has_match = "*" in match_families or match_families.intersection(
                instance_families)

            if not has_match:
                continue

            cls.log.debug(
                f"Found plug-in family requirements: {match_families}")
            required_plugins = [
                # match lowercase and format with os.environ to allow
                # plugin names defined by max version, e.g. {3DSMAX_VERSION}
                plugin.format(**os.environ).lower()
                for plugin in mapping["plugins"]
                # ignore empty fields in settings
                if plugin.strip()
            ]

            all_required_plugins.update(required_plugins)

        if not all_required_plugins:
            # Instance has no plug-in requirements
            return

        # get all DLL loaded plugins in Max and their plugin index
        available_plugins = {
            plugin_name.lower(): index for index, plugin_name in enumerate(
                get_plugins())
        }
        # validate the required plug-ins
        for plugin in sorted(all_required_plugins):
            plugin_index = available_plugins.get(plugin)
            if plugin_index is None:
                debug_msg = (
                    f"Plugin {plugin} does not exist"
                    " in 3dsMax Plugin List."
                )
                invalid.append((plugin, debug_msg))
                continue
            if not rt.pluginManager.isPluginDllLoaded(plugin_index):
                debug_msg = f"Plugin {plugin} not loaded."
                invalid.append((plugin, debug_msg))
        return invalid

    def process(self, instance):
        if not self.is_active(instance.data):
            self.log.debug("Skipping Validate Loaded Plugin...")
            return
        invalid = self.get_invalid(instance)
        if invalid:
            bullet_point_invalid_statement = "\n".join(
                "- {}".format(message) for _, message in invalid
            )
            report = (
                "Required plugins are not loaded.\n\n"
                f"{bullet_point_invalid_statement}\n\n"
                "You can use repair action to load the plugin."
            )
            raise PublishValidationError(
                report, title="Missing Required Plugins")

    @classmethod
    def repair(cls, instance):
        # get all DLL loaded plugins in Max and their plugin index
        invalid = cls.get_invalid(instance)
        if not invalid:
            return

        # get all DLL loaded plugins in Max and their plugin index
        available_plugins = {
            plugin_name.lower(): index for index, plugin_name in enumerate(
                get_plugins())
        }

        for invalid_plugin, _ in invalid:
            plugin_index = available_plugins.get(invalid_plugin)

            if plugin_index is None:
                cls.log.warning(
                    f"Can't enable missing plugin: {invalid_plugin}")
                continue

            if not rt.pluginManager.isPluginDllLoaded(plugin_index):
                rt.pluginManager.loadPluginDll(plugin_index)

get_invalid(instance) classmethod

Plugin entry point.

Source code in client/ayon_max/plugins/publish/validate_loaded_plugin.py
 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
@classmethod
def get_invalid(cls, instance):
    """Plugin entry point."""
    family_plugins_mapping = cls.family_plugins_mapping
    if not family_plugins_mapping:
        return

    # Backward compatibility - settings did have 'product_types'
    if "product_types" in family_plugins_mapping:
        family_plugins_mapping["families"] = family_plugins_mapping.pop(
            "product_types"
        )

    invalid = []
    # Find all plug-in requirements for current instance
    instance_families = {instance.data["productType"]}
    instance_families.update(instance.data.get("families", []))
    cls.log.debug("Checking plug-in validation "
                  f"for instance families: {instance_families}")
    all_required_plugins = set()

    for mapping in family_plugins_mapping:
        # Check for matching families
        if not mapping:
            return

        match_families = {
            fam.strip() for fam in mapping["families"]
        }
        has_match = "*" in match_families or match_families.intersection(
            instance_families)

        if not has_match:
            continue

        cls.log.debug(
            f"Found plug-in family requirements: {match_families}")
        required_plugins = [
            # match lowercase and format with os.environ to allow
            # plugin names defined by max version, e.g. {3DSMAX_VERSION}
            plugin.format(**os.environ).lower()
            for plugin in mapping["plugins"]
            # ignore empty fields in settings
            if plugin.strip()
        ]

        all_required_plugins.update(required_plugins)

    if not all_required_plugins:
        # Instance has no plug-in requirements
        return

    # get all DLL loaded plugins in Max and their plugin index
    available_plugins = {
        plugin_name.lower(): index for index, plugin_name in enumerate(
            get_plugins())
    }
    # validate the required plug-ins
    for plugin in sorted(all_required_plugins):
        plugin_index = available_plugins.get(plugin)
        if plugin_index is None:
            debug_msg = (
                f"Plugin {plugin} does not exist"
                " in 3dsMax Plugin List."
            )
            invalid.append((plugin, debug_msg))
            continue
        if not rt.pluginManager.isPluginDllLoaded(plugin_index):
            debug_msg = f"Plugin {plugin} not loaded."
            invalid.append((plugin, debug_msg))
    return invalid