Skip to content

Instantly share code, notes, and snippets.

Last active Aug 29, 2015
What would you like to do?
Pyblish Customisation

Pyblish Customisation

Building upon Pyblish Search, here's an example of plug-ins being published via searched and customised plug-ins.

How it works

Plug-ins are categories by "tags" and assigned a series of "options". The tags are used during search and options provide for user customisation, either directly or via a GUI. A GUI can then build an interface towards available options via the provided "schema" which defines attribute types, ranges, enums et. cetera.

import re
import pyblish.api
import pyblish.util
disk = {}
class ValidateHeight(pyblish.api.Validator):
tags = ["character", "measurement"]
# Schema is used to build the User Interface
# and validate incoming values.
schema = {
"height": float,
"tolerance": float
# Options used to populate default values
# in user interface and for final evaluation
# by process().
options = {
"height": 2,
"tolerance": 0.5
def process(self, instance):"%s compared with %s" % ("height"), self.options["height"]))
low_bound = self.options["height"] - self.options["tolerance"]
high_bound = self.options["height"] + self.options["tolerance"]
assert low_bound <"height") < high_bound, (
"Height out of bounds")
class ValidatePivot(pyblish.api.Validator):
tags = ["geometry", "character", "film"]
options = {
"predicate": "zeroed"
schema = {
"predicate": [
def process(self, instance):
if self.options["predicate"] == "zeroed":
assert False
if self.options["predicate"] == "straight":
assert True
class ValidateNamingComplex(pyblish.api.Validator):
tags = ["naming", "games", "tv", "film"]
options = {
# Anything suffixed with an uppercase
# three letter extension
"regex": "^.*_[A-Z]{3}$"
schema = {
"regex": str
def process(self, instance):
for child in instance:
assert re.match(self.options["regex"], child), (
"Invalid name for at least %s" % child)
class ValidateNamingSimple(pyblish.api.Validator):
tags = ["naming", "games", "tv", "film"]
options = {
"prefix": False,
"prefixLength": 3,
"prefixCase": "upper",
"suffix": True,
"suffixLength": 3,
"suffixCase": "upper",
"separator": "_",
schema = {
"prefix": bool,
"prefixLength": int,
"prefixCase": ["upper", "lower"],
"suffix": bool,
"suffixLength": int,
"suffixCase": ["upper", "lower"],
"separator": str
def process(self, instance):
for child in instance:
regex = "^.*_[A-Z]{suffixLength}$".format(**self.options)
assert re.match(regex, child), (
"Invalid name for at least %s" % child)
class ValidateDefaultPose(pyblish.api.Validator):
tags = ["rigging", "animation", "consistency"]
options = {
"tolerance": 0.01,
"considerUserDefined": True,
"considerBuiltin": True
schema = {
"tolerance": float,
"considerUserDefined": bool,
"considerBuiltin": bool
def process(self, instance):
assert True
# Two basic gatekeepers
class CollectInstances(pyblish.api.Collector):
def process(self, context):
class ExtractCharacter(pyblish.api.Extractor):
def process(self, instance):
disk["data"] = 0x05f4
# Database of all available validators
# Typically stored elsewhere, somewhere global
validators = [
def search(queries, config=None, inclusive=False):
"""Search engine, based on tags and inclusiveness
Search amongst available validators via keywords.
Optionally exlude individual validators by name.
queries (list): Individual tags, e.g. "film" and "games"
config (dict): Options per plug-in
plugins = list()
options = config.get("options", dict())
exclude = config.get("exclude", list())
for validator in validators:
name = validator.__name__
if name in exclude:
operator = any if inclusive else all
if not operator(tag in validator.tags for tag in queries):
# Produce a copy to modify with options
copy = type(name, (validator,), {})
copy.options.update(options.get(name, {}))
return plugins
# A full configuration file, provided externally
config = {
# Options provided by the user either a GUI or file on disk
"options": {
"ValidatePivot": {
"predicate": "straight"
"ValidateHeight": {
"height": 1.80,
"tolerance": 0.1
"ValidateNamingComplex": {
"regex": "^.*_[A-Z]{4}$"
# Provided via GUI or file, via e.g. unticking in a visual list
"exclude": [
# Perform search
validators = search(
queries="naming film measurement".split(),
# Register found
for plugin in validators + [CollectInstances, ExtractCharacter]:
# Perform publish
context = pyblish.util.publish()
for result in"results"):
if result["instance"] is None:
# Report results
print("{} + {} = {success}".format(**result))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment