The use of hosts attribute?


#1

After doing a couple of different plugins for Maya and Nuke, something seems a bit off on the logic of hosts. Basically when I have a host specific plugin, I would import the hosts api at the top of the plugin, resulting in that plugin not being included cause the import fails on other hosts. So in theory I wouldn’t need to specify a host in this case.

There is the case of plugins that are multi-host compatible, and uses only python libraries. In this case I would assume the plugins not to be selectors, and therefore reliant on instances to execute, meaning that in the hosts the plugin isn’t compatible with the instances wouldn’t be present.

Lastly there is the case of using multiple imports inside the plugin class, which I think @mkolar have run into. I would call these rare cases (?) and surely you would be able to split the code and/or use a common library of functions.

I guess the benefit of not having the host attribute would be easier entry for new comers, and a “forced” split of host specific coding?

Let me know what you think, just throwing it out there.


#2

haha, I may have answered my own question with this plugin case.

import os
import platform

import pyblish.api
import pyblish.plugin


@pyblish.api.log
class ValidateOutputLocation(pyblish.api.Validator):
    """Validates whether the output is local or networked"""

    families = ['deadline.render']
    hosts = ['*']
    version = (0, 1, 0)
    optional = True

    def process_instance(self, instance):
        # checking output
        path = instance.data('deadlineOutput')
        mount = self.find_mount_point(path)
        if platform.system() == 'Windows':
            if 'c' in mount.lower():
                msg = 'Output path is not a network path: %s' % path
                raise ValueError(msg)

        # checking project path in maya
        if pyblish.plugin.current_host() == 'maya':
            path = instance.data('deadlinePluginData')['ProjectPath']
            mount = self.find_mount_point(path)
            if platform.system() == 'Windows':
                if 'c' in mount.lower():
                    msg = 'Project path is not a network path: %s' % path
                    raise ValueError(msg)

    def find_mount_point(self, path):
        path = os.path.abspath(path)
        while not os.path.ismount(path):
            path = os.path.dirname(path)
        return path

Although it doesn’t use the host attribute of the plugin, the plug is tailored to Maya when validating the project path. So the use of querying the host is definitely useful.
The hosts attribute on the plugin might still be questionable though.


#3

It’s good that you question it, I can see how it may seem unnecessary, especially considering plug-ins that import any host-bound modules are being discarded regardless anyway, because they fail to get imported.

The hosts attribute is one of the optional ways you can associate a registered plug-in with data, similar to families; one distinguishes between the environment and the other between content. Whether you need to make the distinction in your collection of plug-ins depends on how your write and organise them.

You can, for example, register plug-ins only relevant to a particular host, in which case a plug-in of an unsupported host may never be registered in the first place and thus not be affected by the filtering mechanism. But if you do register one plug-in with support for one host, and one with support for another, then the hosts attribute can help you distinguish between which plug-in operates in which host.

There are a few other ways planned for future releases.

  • Filter by version
  • Filter by OS
class ValidateInstance(...):
   families = ["myFamily", "myFamily.childA"]
   hosts = ["hostA", "hostB"]
   version = "2.4.1"
   oss = ["win", "osx"]

And like with hosts and families, depending on the complexity and size of your plug-in collection, and how you organise them, all or none of these may be necessary.

I think with the new defaults for hosts and families this will make more sense as being optional. If you can’t find a need for them right away, don’t fret. And if the time comes when you do need to make this distinction, you can.

Communication

On another note, with regards to rolling your own filtering mechanism, e.g.

class MyPlugin(...):
  def process(...):
    if "maya" in sys.executable:
      # do Maya things
      if "nt" in os.name:
        # do Windows things in Maya

Consider still making note of which hosts and families are supported via the corresponding attributes for the purpose of communication.

In a lengthy plug-in, it might get difficult for another developer, or your future self, to figure out which hosts a plug-in supports, especially when a host-library isn’t being directly imported at the top of the file or when said library isn’t one we recognise (like one from Clarisse or Cinema4D).

Future

Finally, by having plug-ins share a common ground for supported hosts, families, OS’s and versions opens up doors for when sharing is more common and filtering more important.

For example, you may be looking for a Maya plug-in, or a plug-in related to OSX file permissions or a later version of a plug-in you already have. These attributes make this possible and I would make it a habit of at least filling them out, if not only for the purpose of future-proofing them.


Learning Pyblish by Example