diff --git a/otio_openassetio/operations/openassetio_media_linker.py b/otio_openassetio/operations/openassetio_media_linker.py index e6b291e..89c23fb 100644 --- a/otio_openassetio/operations/openassetio_media_linker.py +++ b/otio_openassetio/operations/openassetio_media_linker.py @@ -7,7 +7,7 @@ from collections import namedtuple -from openassetio import log, exceptions +from openassetio import log, errors from openassetio.access import ResolveAccess from openassetio.hostApi import HostInterface, ManagerFactory from openassetio.pluginSystem import PythonPluginSystemManagerImplementationFactory @@ -60,9 +60,12 @@ def link_media_reference(in_clip, media_linker_argument_map): ResolveAccess.kRead, session_state.context, ) - mr.target_url = LocatableContentTrait(entity_data).getLocation() + target_url = LocatableContentTrait(entity_data).getLocation() + if target_url is None: + raise ValueError(f"Entity '{entity_reference}' has no location") + mr.target_url = target_url except Exception as exc: - raise exceptions.EntityResolutionError( + raise errors.OpenAssetIOException( "Failed to resolve location from LocatableContent trait", entity_reference ) from exc @@ -152,7 +155,14 @@ def _createSessionState(args: dict) -> SessionState: else: manager = ManagerFactory.defaultManagerForInterface(host, factory, logger) if not manager: - raise RuntimeError("No default OpenAssetIO manager configured") + raise errors.ConfigurationException( + "No default OpenAssetIO manager configured" + ) + + if not manager.hasCapability(manager.Capability.kResolution): + raise errors.ConfigurationException( + f"{manager.displayName()} is not capable of resolving entity references" + ) # The lifetime of the context would ideally be tied to each specific # call to read_from_string or similar. Maybe we could introspect diff --git a/setup.py b/setup.py index d88e636..a404b17 100644 --- a/setup.py +++ b/setup.py @@ -28,7 +28,7 @@ }, install_requires=[ "OpenTimelineIO >= 0.12.0", - "openassetio == 1.0.0a14", + "openassetio >= 1.0.0b1.rev0, < 1.0.0b2.rev0", "openassetio-mediacreation == 1.0.0a7", ], extras_require={ @@ -37,7 +37,7 @@ "pytest", "flake8", "twine", - "openassetio-manager-bal == 1.0.0a10" + "openassetio-manager-bal == 1.0.0a12" ] }, classifiers=[ diff --git a/tests/test_otio_openassetio.py b/tests/test_otio_openassetio.py index 14582bd..1fe50d9 100644 --- a/tests/test_otio_openassetio.py +++ b/tests/test_otio_openassetio.py @@ -1,12 +1,14 @@ # Copyright The Foundry Visionmongers Ltd # SPDX-License-Identifier: Apache-2.0 import os +from unittest import mock import pytest import opentimelineio as otio -from openassetio import exceptions -from openassetio.hostApi import ManagerFactory +from openassetio import errors +from openassetio.hostApi import ManagerFactory, Manager + raw = """{ "OTIO_SCHEMA": "Timeline.1", @@ -104,7 +106,7 @@ def test_when_linker_used_then_references_are_resolved(bal_linker_args): def test_when_linker_used_with_incorrect_data_exception_thrown( bal_linker_args_missing_asset, ): - with pytest.raises(exceptions.EntityResolutionError): + with pytest.raises(errors.OpenAssetIOException): otio.adapters.read_from_string( raw, media_linker_name="openassetio_media_linker", @@ -113,7 +115,7 @@ def test_when_linker_used_with_incorrect_data_exception_thrown( def test_when_manager_cant_be_found_exception_thrown(bal_linker_args_malformed_manager): - with pytest.raises(exceptions.PluginError): + with pytest.raises(errors.InputValidationException): otio.adapters.read_from_string( raw, media_linker_name="openassetio_media_linker", @@ -121,16 +123,37 @@ def test_when_manager_cant_be_found_exception_thrown(bal_linker_args_malformed_m ) +def test_when_manager_cant_resolve_then_exception_thrown(bal_linker_args, monkeypatch): + hasCapability = mock.Mock(spec=Manager.hasCapability) + monkeypatch.setattr(Manager, "hasCapability", hasCapability) + hasCapability.return_value = False + + with pytest.raises( + errors.ConfigurationException, + match="Basic Asset Library 📖 is not capable of resolving entity references", + ): + otio.adapters.read_from_string( + raw, + media_linker_name="openassetio_media_linker", + media_linker_argument_map=bal_linker_args, + ) + + hasCapability.assert_called_once_with(Manager.Capability.kResolution) + + def test_when_no_locatable_content_trait_exception_thrown( bal_linker_args_no_locatable_content, ): - with pytest.raises(exceptions.EntityResolutionError): + with pytest.raises(errors.OpenAssetIOException) as err: otio.adapters.read_from_string( raw, media_linker_name="openassetio_media_linker", media_linker_argument_map=bal_linker_args_no_locatable_content, ) + assert isinstance(err.value.__cause__, ValueError) + assert str(err.value.__cause__) == "Entity 'bal:///asset1' has no location" + def test_when_no_manager_setting_then_default_config_used( linker_args_no_settings, monkeypatch @@ -148,7 +171,7 @@ def test_when_no_manager_setting_then_default_config_used( monkeypatch.delenv("BAL_LIBRARY_PATH", raising=False) with pytest.raises( - exceptions.PluginError, + errors.ConfigurationException, match="'library_path'/BAL_LIBRARY_PATH not set or is empty", ): otio.adapters.read_from_string( @@ -163,7 +186,7 @@ def test_when_no_manager_setting_and_no_default_config_then_error_raised( ): monkeypatch.delenv(ManagerFactory.kDefaultManagerConfigEnvVarName, raising=False) with pytest.raises( - RuntimeError, match="No default OpenAssetIO manager configured" + errors.ConfigurationException, match="No default OpenAssetIO manager configured" ): otio.adapters.read_from_string( raw,