pyblish / pyblish-base

Pyblish base library - see https://github.com/pyblish/pyblish for details.
Other
127 stars 59 forks source link

ContextPlugin fails to work correctly if process method does not have `context` argument #351

Open BigRoy opened 5 years ago

BigRoy commented 5 years ago

Issue

Whenever a pyblish.api.ContextPlugin inherited plug-in has the process() method defined with an argument name different from context it will fail to work correctly. Below are some example cases to reproduce which fail, the docstring describes how they are failing.

1. ContextPlugin accidentally using instance as argument

import pyblish.api

class Collector(pyblish.api.ContextPlugin):
    """This plug-in will run *per instance*!

    This plug-in will run like an InstancePlugin as opposed to ContextPlugin.

    However, the argument `instance` then actually still refers to `context`. 
    The issue thus being that this plug-in runs more than one time which 
    should not happen with a ContextPlugin.

    """
    order = pyblish.api.CollectorOrder

    def process(self, instance):
        self.log.info(instance)

2. ContextPlugin using another argument name (e.g. typo in it)

import pyblish.api

class Collector(pyblish.api.ContextPlugin):
    """This plug-in will not show whatsoever. It's silently ignored.

    Any `process` argument that is *not* `instance` or `context` will have the
    plug-in fail to load without a warning.

    """
    order = pyblish.api.CollectorOrder

    def process(self, test):
        self.log.info(test)

Initial investigation

For 1) Likely we'll need to make the signature validation more explicit based on the core type it inherited from, so that ContextPlugin must have context and InstancePlugin must have instance.

For 2) The last plug-in failing silently is odd as it should at the very least print "Invalid signature" as it's signature is validated in the EplicitMetaPlugin. I tested this in Houdini, so maybe the Houdini log is somehow hiding it.

BigRoy commented 5 years ago

This has been transfered from pyblish-qml and is reproducable.

1) It does have an instance in the results for that plugin in the context which indicates it ran as InstancePlugin and it does seem to run per instance (e.g. when you have multiple instances it will run MORE than once!) Keep in mind when checking the logged instance it actually shows in the logs the context as it shows a list of instances from self.log.info(instance). Similar to described in the original issue.

As such the difference being that in

2) Yes, it silently fails.

Tested with:

context = pyblish.util.collect()

# Check what happened
runs = 0
for result in context.data["results"]:
    if result["plugin"].__name__ == "Collector":
        # Found the Collector plug-in
        runs += 1

        # Detect a part of case 1) 
        if result["instance"]:
            print("ContextPlugin has instance. It should be 'None'!")

        # Print the logs for debugging
        for log in result["records"]:
            print(log.msg)

if runs > 1:
    # Detect case 1)
    # Note: For this to be detected just must have *at least two instances!*
    print("Plug-in ran more than once! Runs: %s" % runs)

elif runs == 0:
    # Detect case 2)
    print("Plug-in not found!")

When testing against multiple instances make sure they are created at a CollectOrder - 0.1 or increase the reproducible examples with a positive offset (e.g. CollectOrder + 0.1) to ensure the instances are created prior to running these examples. Or:

import pyblish.api
import pyblish.util

context = pyblish.api.Context()
context.create_instance("Instance1")
context.create_instance("Instance2")
pyblish.util.collect(context)

# Use the "Check what happened" from above here..