Discussion on validators communicating with actions

problem

While creating various plugins, doing the following feels limited:

  • access the failed instances from a validator that finished

example: i want to access the failed instances of a validation-plugin, in the select_failed_mesh action.

My expectation in OOP would be that the validator-instance can track which (pyblish)instances it ran on.
The action has access to the validator-plugin, therefor it has access to the (pyblish)instances.

class ActionSelect(pyblish.api.Action):
    def process(self, context, plugin):
        print plugin.instances

But this is not currently the case. So I find myself doing it in this non-intuitive way for now.

class ActionSelect(pyblish.api.Action):
    def process(self, context, plugin):
        # because pyblish doesnt support getting instances from a plugin yet
        # we have to do this manually :(
        # if only we would get the plugin instances when using an action
        instances = []
        for result in context.data["results"]:
            if result["error"] and result["plugin"] == plugin:
                instance = result["instance"]
                instances.extend(instance)

part solution

discovered following helper function while going through sourcecode , don’t think there is any docs online.

class ActionSelect(pyblish.api.Action):
    def process(self, context, plugin):
        instances = pyblish.api.instances_by_plugin(context, plugin)

but instances are not filtered. so it selects both failed and successfull meshes.


dependency injection UX

Also worth noting:
dependency injection allows following code to run, but since instance is always None it’d be better to not allow this.
not sure what i’d expect to happen here since an action runs on a plugin, and a plugin can run on multiple instances, not just 1 instance. But easy to get confused here.

class ActionSelect(pyblish.api.Action):
    def process(self, context, plugin, instance):
        print instance # prints None

related to this:

In the validator, we run a function. if said function returns anything it fails. and we store the failed results ex. mesh, vert-indices, meshname, …
to ensure we always have a failed result, even when the function hits an exception. it needs to be places in a try, except loop.
this in turn then hides exceptions, which require extra code to display.

if we could access failed instances through the action/plugin all of this would not be needed. and creating plugins would be cleaner.

    def func():
        # check stuff. ex: open borders.
        # return edge IDs on failure of affected edges

    class ValidationPlugin(pyblish.api.Validator):
        _func = [func]  # we can't store func directly or it will pass self when running self.func()

        def process(self, instance, context):
            mesh_names = instance[:]
            for mesh_name in mesh_names:
                my_exception = None
                try:
                    func = self._func[0]
                    errors = func(mesh_name)
                except Exception as ex:
                    errors = [mesh_name]
                    my_exception = ex

                temp_list = context.data.get(self.label, [])
                temp_list.extend(errors)
                context.data[self.label] = temp_list  # save failed results for reuse later

                # raise exception to prevent original exceptions to be hidden by the following assert.
                if my_exception:
                    raise my_exception

                assert not errors, 'check failed on:' + str(errors)

in an ideal world we only need to run our function, and assert:

    def func():
        # check stuff. ex: open borders.
        # return edge IDs on failure of affected edges

    class ValidationPlugin(pyblish.api.Validator):
        _func = [func]  # we can't store func directly or it will pass self when running self.func()

        def process(self, instance, context):
            mesh_names = instance[:]
            for mesh_name in mesh_names:
                func = self._func[0]
                errors = func(mesh_name)

                assert not errors, 'check failed on:' + str(errors)