diff --git a/CHANGES b/CHANGES index ad412405..87ade3ab 100644 --- a/CHANGES +++ b/CHANGES @@ -6,7 +6,11 @@ This contains all major version changes between pyblish releases. Version 1.1.3 ------------- -- Decrementing order actually works now. (#206) +- Bugfix: Decrementing order actually works now. (#206) +- Feature: Dict-like getting of instances from Context (#208) +- Feature: Host registration (#177) +- `collect` and `integrate` now available via pyblish.util +- "asset" available in context.data("result") for forwards compatibility Version 1.1.2 ------------- diff --git a/pyblish/__init__.py b/pyblish/__init__.py index f06284e5..dba65fb5 100644 --- a/pyblish/__init__.py +++ b/pyblish/__init__.py @@ -14,3 +14,4 @@ _registered_plugins = dict() _registered_services = dict() _registered_test = None +_registered_hosts = set() diff --git a/pyblish/api.py b/pyblish/api.py index b3a66514..c21d8944 100644 --- a/pyblish/api.py +++ b/pyblish/api.py @@ -33,6 +33,11 @@ Config as __Config, discover, + register_host, + registered_hosts, + deregister_host, + deregister_all_hosts, + register_plugin, deregister_plugin, deregister_all_plugins, @@ -93,6 +98,13 @@ def __init__(): + """Initialise Pyblish + + This function registered default services, + hosts and tests. It is idempotent and thread-safe. + + """ + # Register default services register_service("time", __time) register_service("user", getpass.getuser()) @@ -100,6 +112,9 @@ def __init__(): register_service("context", None) register_service("instance", None) + # Register default host + register_host("python") + # Register default test register_test(__default_test) @@ -130,6 +145,11 @@ def __init__(): "configured_paths", "environment_paths", + "register_host", + "registered_hosts", + "deregister_host", + "deregister_all_hosts", + "register_plugin", "deregister_plugin", "deregister_all_plugins", diff --git a/pyblish/lib.py b/pyblish/lib.py index fdfc0853..22fd4933 100644 --- a/pyblish/lib.py +++ b/pyblish/lib.py @@ -10,7 +10,6 @@ 'LPT1', 'LPT2', 'LPT3', 'PRN', 'NUL') - def inrange(number, base, offset=0): r"""Evaluate whether `number` is within `base` +- `offset` diff --git a/pyblish/plugin.py b/pyblish/plugin.py index 41ce8d85..610ce1b1 100644 --- a/pyblish/plugin.py +++ b/pyblish/plugin.py @@ -335,6 +335,7 @@ def process(plugin, context, instance=None): "success": False, "plugin": plugin, "instance": instance, + "asset": instance, # Forwards compatibility "error": None, "records": list(), "duration": None @@ -501,6 +502,24 @@ def has_data(self, key): class Context(AbstractEntity): """Maintain a collection of Instances""" + @property + def id(self): + return "Context" + + def __init__(self, *args, **kwargs): + super(Context, self).__init__(*args, **kwargs) + + # Cache children for faster lookup + self._children = dict() + + def add(self, other): + super(Context, self).add(other) + self._children[other.id] = other + + def remove(self, other): + super(Context, self).remove(other) + self._children.pop(other.id) + def create_instance(self, name, **kwargs): """Convenience method of the following. @@ -521,6 +540,25 @@ def create_instance(self, name, **kwargs): # Alias create_asset = create_instance + def __getitem__(self, item): + """Enable support for dict-like getting of children by id + + Example: + >>> context = Context() + >>> instance = context.create_instance("MyInstance") + >>> assert context["MyInstance"].name == "MyInstance" + >>> assert context[0].name == "MyInstance" + >>> assert context.get("MyInstance").name == "MyInstance" + + """ + + if isinstance(item, int): + return super(Context, self).__getitem__(item) + return self._children[item] + + def get(self, key, default=None): + return self._children.get(key, default) + @pyblish.lib.log class Instance(AbstractEntity): @@ -606,7 +644,7 @@ def data(self, key=None, default=None): def current_host(): - """Return currently active host + """Return host last registered thru `register_host()` When running Pyblish from within a host, this function determines which host is running and returns the equivalent keyword. @@ -624,27 +662,7 @@ def current_host(): """ - executable = os.path.basename(sys.executable).lower() - - if "maya" in executable: - return "maya" - - if "nuke" in executable: - return "nuke" - - if "modo" in executable: - return "modo" - - if "houdini" in executable or "hou" in sys.modules: - return 'houdini' - - if "houdini" in executable: - return "houdini" - - if "python" in executable: - return "python" - - return "unknown" + return pyblish._registered_hosts[-1] or "unknown" def register_plugin(plugin): @@ -783,6 +801,49 @@ def registered_plugins(): return pyblish._registered_plugins.values() +def register_host(host): + """Register a new host + + Registered hosts are used to filter discovered + plug-ins by host. + + Example: + >>> register_host("My Host") + >>> "My Host" in registered_hosts() + True + + """ + + pyblish._registered_hosts.add(host) + + +def deregister_host(host, quiet=False): + """Remove an already registered host + + Arguments: + host (str): Name of host + quiet (bool): Whether to raise an exception + when attempting to remove a host that is + not already registered. + + """ + + try: + pyblish._registered_hosts.remove(host) + except Exception as e: + if not quiet: + raise e + + +def deregister_all_hosts(): + pyblish._registered_hosts.clear() + + +def registered_hosts(): + """Return the currently registered hosts""" + return list(pyblish._registered_hosts) + + def configured_paths(): """Return paths added via configuration""" paths = list() @@ -1011,14 +1072,17 @@ def version_is_compatible(plugin): def host_is_compatible(plugin): """Determine whether plug-in `plugin` is compatible with the current host - The current host is determined by :func:`current_host`. + Available hosts are determined by :func:`registered_hosts`. Arguments: plugin (Plugin): Plug-in to assess. """ - return any(["*" in plugin.hosts, current_host() in plugin.hosts]) + if "*" in plugin.hosts: + return True + + return any(host in plugin.hosts for host in registered_hosts()) def sort(plugins): diff --git a/pyblish/util.py b/pyblish/util.py index bc3e3ed6..9682c568 100644 --- a/pyblish/util.py +++ b/pyblish/util.py @@ -81,28 +81,34 @@ def publish(context=None, plugins=None, **kwargs): def select(*args, **kwargs): """Convenience function for selection""" - return _convenience(1, *args, **kwargs) + return _convenience(0.5, *args, **kwargs) def validate(*args, **kwargs): """Convenience function for validation""" - return _convenience(2, *args, **kwargs) + return _convenience(1.5, *args, **kwargs) def extract(*args, **kwargs): """Convenience function for extraction""" - return _convenience(3, *args, **kwargs) + return _convenience(2.5, *args, **kwargs) def conform(*args, **kwargs): """Convenience function for conform""" - return _convenience(4, *args, **kwargs) + return _convenience(3.5, *args, **kwargs) + + +collect = select +integrate = conform def _convenience(order, *args, **kwargs): plugins = [p for p in pyblish.plugin.discover() if p.order < order] + print [p.id for p in plugins] + args = list(args) if len(args) > 1: args[1] = plugins diff --git a/tests/test_context.py b/tests/test_context.py index b72da38d..8ac04074 100644 --- a/tests/test_context.py +++ b/tests/test_context.py @@ -57,5 +57,19 @@ def test_instance_equality(): assert inst2 == inst3 +def test_context_itemgetter(): + """Context.get() works""" + context = pyblish.api.Context() + context.create_instance("MyInstanceA") + context.create_instance("MyInstanceB") + + assert context["MyInstanceA"].name == "MyInstanceA" + assert context["MyInstanceB"].name == "MyInstanceB" + assert context.get("MyInstanceA").name == "MyInstanceA" + assert context.get("MyInstanceB").name == "MyInstanceB" + assert context[0].name == "MyInstanceA" + assert context[1].name == "MyInstanceB" + + if __name__ == '__main__': test_add_remove_instances() diff --git a/tests/test_plugin.py b/tests/test_plugin.py index 3b4bcf6f..5ea98bcc 100644 --- a/tests/test_plugin.py +++ b/tests/test_plugin.py @@ -62,7 +62,7 @@ def process(self, context): def test_asset(): """Using asset over instance works fine""" context = pyblish.plugin.Context() - + asseta = context.create_asset("MyAssetA", family="myFamily") assetb = context.create_asset("MyAssetB", family="myFamily") @@ -140,7 +140,6 @@ class OnlyInMaya(pyblish.api.Plugin): """This plug-in is only discoverable from maya""" hosts = ["maya"] - pyblish.api.register_plugin(Always) pyblish.api.register_plugin(OnlyInUnknown) pyblish.api.register_plugin(OnlyInMaya) @@ -151,30 +150,21 @@ class OnlyInMaya(pyblish.api.Plugin): assert OnlyInUnknown not in discovered # It's known to be python assert OnlyInMaya not in discovered # Host is not maya - def _current_host(): - return "maya" + pyblish.api.deregister_all_hosts() + pyblish.api.register_host("unknown") + sys.executable = "/root/some_executable" - try: - old = sys.executable - sys.executable = "/root/some_executable" - - discovered = pyblish.api.discover() - assert OnlyInUnknown in discovered - assert OnlyInMaya not in discovered - - finally: - sys.executable = old + discovered = pyblish.api.discover() + assert OnlyInUnknown in discovered + assert OnlyInMaya not in discovered - try: - old = sys.executable - sys.executable = "/root/maya" - - discovered = pyblish.api.discover() - assert OnlyInUnknown not in discovered - assert OnlyInMaya in discovered + pyblish.api.deregister_host("unknown") + pyblish.api.deregister_all_hosts() + pyblish.api.register_host("maya") - finally: - sys.executable = old + discovered = pyblish.api.discover() + assert OnlyInUnknown not in discovered + assert OnlyInMaya in discovered @with_setup(lib.setup_empty, lib.teardown) @@ -204,7 +194,6 @@ class NotDiscoverable(pyblish.api.Plugin): with open(os.path.join(d, "_undiscoverable.py"), "w") as f: f.write(notdiscoverable) - plugins = [p.__name__ for p in pyblish.api.discover()] assert "Discoverable" in plugins assert "NotDiscoverable" not in plugins