diff --git a/CHANGES b/CHANGES index ec4181f0..70647fe5 100644 --- a/CHANGES +++ b/CHANGES @@ -3,6 +3,13 @@ pyblish Changelog This contains all major version changes between pyblish releases. +Version 1.0.14 +-------------- + +- Added pyblish.api.version +- Matched verbosity level from processing context as the processing of instances. +- Processing logic change; processing of plug-in *without* compatible instances will now *not* yield anything; previously it yielded a pair of (None, None). + Version 1.0.13 -------------- diff --git a/pyblish/api.py b/pyblish/api.py index c640a78a..5f536538 100644 --- a/pyblish/api.py +++ b/pyblish/api.py @@ -58,7 +58,7 @@ "initializing configuration") config = pyblish.config - +version = pyblish.version __all__ = [ # Base objects @@ -84,13 +84,14 @@ "plugins_by_host", "instances_by_plugin", "sort_plugins", + "format_filename", # Configuration "config", + "version", # Decorators "log", - "format_filename", # Exceptions "PyblishError", diff --git a/pyblish/plugin.py b/pyblish/plugin.py index 2dedb5c0..97dc5886 100644 --- a/pyblish/plugin.py +++ b/pyblish/plugin.py @@ -181,18 +181,20 @@ def process(self, context, instances=None): self.process_context(context) except Exception as err: - self.log.error("Could not process context: " - "%s: %s" % (context, err)) + try: + _, _, exc_tb = sys.exc_info() + err.traceback = traceback.extract_tb( + exc_tb)[-1] + except: + pass + yield None, err finally: compatible_instances = instances_by_plugin( instances=context, plugin=self) - if not compatible_instances: - yield None, None - - else: + if compatible_instances: for instance in compatible_instances: # Limit instances to those specified in `instances` if instances is not None and \ diff --git a/pyblish/tests/test_context.py b/pyblish/tests/test_context.py index aeebbfc5..eb273333 100644 --- a/pyblish/tests/test_context.py +++ b/pyblish/tests/test_context.py @@ -8,7 +8,7 @@ from pyblish.tests.lib import ( setup_full, teardown) -from pyblish.vendor.nose.tools import with_setup +from pyblish.vendor.nose.tools import * # Setup HOST = 'python' @@ -81,5 +81,52 @@ def test_limited_to_instances(): if name == "Instance02": assert inst.data('validated', False) is False + +def test_failing_context(): + """Context processing yields identical information to instances""" + + class SelectFailure(pyblish.api.Selector): + def process_context(self, context): + raise pyblish.api.SelectionError("I was programmed to fail") + + ctx = pyblish.api.Context() + + for instance, error in SelectFailure().process(ctx): + assert_true(error is not None) + assert_true(hasattr(error, "traceback")) + assert_true(error.traceback is not None) + + +def test_failing_validator(): + """All plug-ins yield both context and instance exceptions""" + class ValidateFailure(pyblish.api.Validator): + families = ["test"] + + def process_context(self, context): + raise pyblish.api.ValidationError("context failed") + + def process_instance(self, instance): + raise pyblish.api.ValidationError("instance failed") + + ctx = pyblish.api.Context() + instance = ctx.create_instance("MyInstance") + instance.set_data("family", "test") + + # Context is always processed first + processor = ValidateFailure().process(ctx) + instance, error = processor.next() + assert_equal(error.message, "context failed") + assert_true(hasattr(error, "traceback")) + + # Next up is the instance + instance, error = processor.next() + assert_equal(error.message, "instance failed") + assert_true(hasattr(error, "traceback")) + assert_equal(instance.name, "MyInstance") + + # Nothing else is yeilded + assert_raises(StopIteration, processor.next) + + if __name__ == '__main__': test_add_remove_instances()