- Note
- This section is a work-in-progress. Over time we will add flow diagrams for key operations that happen through the API, along with more extensive sample implementations for both hosts and managers. Currently it is limited to illustrating a few common operations that a host of the API may perform.
- Warning
- At this stage, until we ship a sample manager implementation, the code for later examples won't actually function.
Initializing the API in a Host
This example covers the steps required to initialize the API within a 'host' tool, script or application that wishes to interact with an Asset Management System.
It makes use of the Plugin System to discover available PythonPluginSystemManagerPlugin "PythonPluginSystemManagerPlugins" and CppPluginSystemManagerPlugins.
It also includes a bare-minimum example of a HostInterface implementation.
1 from openassetio.log
import ConsoleLogger, SeverityFilter
4 CppPluginSystemManagerImplementationFactory,
5 PythonPluginSystemManagerImplementationFactory,
6 HybridPluginSystemManagerImplementationFactory)
8 class ExamplesHost(HostInterface):
10 A minimal host implementation. 13 return "org.openassetio.examples" 15 def displayName(self):
16 return "OpenAssetIO Examples" 22 logger = SeverityFilter(ConsoleLogger())
28 factory_impl = HybridPluginSystemManagerImplementationFactory([
29 CppPluginSystemManagerImplementationFactory(logger),
30 PythonPluginSystemManagerImplementationFactory(logger)], logger)
33 host_interface = ExamplesHost()
37 managerFactory = ManagerFactory(host_interface, factory_impl, logger)
Setting up a Manager
This example makes use of the newly initialized factory to show how to construct and configure a specific manager (it assumes that some example Asset Management System has a plugin, installed on $OPENASSETIO_PLUGIN_PATH).
Check out Python Manager Template for an example minimalist plugin.
Also, for a "real world" manager plugin, check out the Plugin for Ayon.
1 availableManagers = managerFactory.availableManagers()
3 >
'org.openassetio.example.manager':
4 > ManagerFactory.ManagerDetail(
5 > identifier=
'org.openassetio.example.manager',
6 > displayName=
'Example Asset Manager',
13 manager = managerFactory.createManager(
'org.openassetio.example.manager')
23 settings = manager.settings()
25 settings[
"server"] =
"my.server.com" 31 manager.initialize(settings)
To make it easier to deploy a range of OpenAssetIO enabled hosts, the API supports a simple file-based configuration mechanism. Users set the $OPENASSETIO_DEFAULT_CONFIG environment variable to point to a suitable TOML file, which contains their preferred manager identifier and settings. As a Host, you can use the defaultManagerForInterface method instead of creating your own ManagerFactory. This will return a fully initialized manager using this configuration if set:
1 manager = ManagerFactory.defaultManagerForInterface(
2 host_interface, impl_factory, logger)
Resolving a Reference
This example shows how to use the instantiated manager to resolve a string (some_string
) that is assumed to be an entity reference to an entity with the LocatableContent
Trait (from the MediaCreation package) covering use of the correct context.
- Note
- The caller must convert a string to an EntityReference object in order to use any OpenAssetIO API that expects an Entity Reference. There is more than one approach to this. Below we rely on the exception thrown by createEntityReference when given an invalid reference. Alternatively, we could use createEntityReferenceIfValid and test if the result is falsey.
-
Ensuring that an entity reference is valid before handing it to the manager reduces the validation overhead in the manager's implementation of the API. This affords significant gains in real-world production use cases where thousands of references may be operated upon in time-critical scenarios.
The API middleware provides assorted short-circuit validation optimisations that can reduce the number of inter-language hops required. See ManagerInterface.info and the kInfoKey_EntityReferencesMatchPrefix
key.
1 from openassetio.access
import ResolveAccess
2 from openassetio_mediacreation.traits.content
import LocatableContentTrait
8 entity_reference = manager.createEntityReference(some_string)
13 context = manager.createContext()
22 if not manager.hasCapability(Manager.Capability.kResolution):
25 resolved_asset = manager.resolve(
26 entity_reference, {LocatableContentTrait.kId},
27 ResolveAccess.kRead, context)
28 url = LocatableContentTrait(resolved_asset).getLocation()
Inspecting a reference
This example shows how an entity reference of unknown provenance can be inspected to determine the qualities of the entity it points to.
Let's say we're an audio application written in Qt, and someone has just dragged and dropped a line of text into the application. We want to know if the text is an entity reference, and if so, does it point to an audio clip.
1 from openassetio.access
import EntityTraitsAccess
2 from openassetio_mediacreation.traits.content
import LocatableContentTrait
3 from openassetio_mediacreation.specifications.audio
import SampledAudioResource
5 class MyAudioApp(QMainWindow):
7 def __init__(self, manager):
9 self.__manager = manager
10 self.__context = manager.createContext()
11 self.setAcceptDrops(
True)
14 def dragEnterEvent(self, event):
15 if event.mimeData().hasUrls():
16 event.acceptProposedAction()
18 def dropEvent(self, event):
19 if not event.mimeData().hasUrls():
21 uri = event.mimeData().urls().first().toString()
22 ref = self.__manager.createEntityReferenceIfValid(uri)
26 entity_trait_set = self.__manager.entityTraits(
27 ref, EntityTraitsAccess.kRead, self.__context)
31 if not SampledAudioResource.kTraitSet <= entity_trait_set
34 resolved_asset = manager.resolve(
35 entity_reference, {LocatableContentTrait.kId},
36 ResolveAccess.kRead, context)
37 url = LocatableContentTrait(resolved_asset).getLocation()
39 self.load_audio_from_url(url)
Discovering capability
In the previous example, we saw how to resolve a specific trait for an entity. In a real-world scenario, it is important to remember that:
- Managers may not support a particular workflow at all.
- Managers may not handle certain types of entity (determined by their Trait Set).
- Managers may not be able to provide data for all traits.
Capabilities
The hasCapability method allows a host to determine which workflows that a manager has implemented.
This is achieved by grouping sets of methods potentially implemented by the manager into "capabilities". These capabilities are defined in Capability.
Calling a method not implemented by the manager will result in an exception. Therefore hosts should check hasCapability before calling into any of these optional methods. The return value of hasCapability is runtime invariant, post-initialize, therefore the check only needs to be made once per manager.
Policy
The managementPolicy method allows a host to query a manager's behaviours and intentions towards different types of entity.
This method should be used wherever possible to adapt the host's behaviour so that:
- The manager is not invoked in relation to unsupported entity types.
- User workflows are appropriate to the supported behaviour for a given entity type.
Failing to check the policy leaves a host vulnerable to recieving empty, null or invalid data by making queries outside of the managers realm of understanding.
Example
This example demonstrates how to query a manager in relation to a specific entity type - in this case a simple text file, and inspect its capabilities and the data it may be able to resolve for it.
1 from openassetio.access
import PolicyAccess
4 from openassetio_mediacreation.traits.managementPolicy
import (
7 from openassetio_mediacreation.traits.content
import (
10 from openassetio_mediacreation.specifications.files
import (
15 if not manager.hasCapability(Manager.Capability.kResolution):
22 policy = manager.managementPolicy(
23 TextFileSpecification.kTraitSet, PolicyAccess.kRead, context)
28 if not ManagedTrait.isImbuedTo(policy):
40 if LocatableContentTrait.isImbuedTo(policy):
41 print(
"The manager can provide the URL for the file")
43 if TextEncodingTrait.isImbuedTo(policy):
44 print(
"The manager can provide the text encoding used")
Publishing a File
This example demonstrates how an API host should involve the manager in the creation of new data. In this case, a simple text file.
1 from openassetio.access
import (
2 PolicyAccess, ResolveAccess, PublishingAccess)
4 from openassetio_mediacreation.traits.managementPolicy
import ManagedTrait
5 from openassetio_mediacreation.specifications.files
import (
10 if not manager.hasCapability(Manager.Capability.kPublishing):
14 context = manager.createContext()
19 policy = manager.managementPolicy(
20 TextFileSpecification.kTraitSet, PolicyAccess.kWrite, context)
22 if not ManagedTrait.isImbuedTo(policy):
29 manager_driven_policy = manager.managementPolicy(
30 TextFileSpecification.kTraitSet, PolicyAccess.kManagerDriven, context)
33 save_path = os.path.join(os.path.expanduser(
'~'),
'greeting.txt')
41 file_spec = TextFileSpecification.create()
42 file_spec.markupTrait().setMarkupType(
"plain")
47 working_ref = manager.preflight(
48 entity_ref, file_spec, PublishingAccess.kWrite, context)
55 if (LocatableContentTrait.isImbuedTo(manager_driven_policy)
or 56 TextEncodingTrait.isImbuedTo(manager_driven_policy)):
57 working_data = manager.resolve(
59 {LocatableContentTrait.kId, TextEncodingTrait.kId},
60 ResolveAccess.kManagerDriven, context)
61 if save_url := LocatableContentTrait(working_data).getLocation():
63 save_path = utils.pathFromUrl(save_url)
64 encoding = TextEncodingTrait(working_data).getEncoding(defaultValue=encoding):
67 with open(save_path,
'w', encoding=encoding)
as f:
68 f.write(
"Hello from the documentation example\n")
72 file_spec.locatableContentTrait().setLocation(pathToURL(save_path))
73 file_spec.textEncodingTrait().setEncoding(encoding)
77 final_ref = manager.register(working_ref, file_spec.traitsData(),
78 PublishAccess.kWrite, context)
81 with open(os.path.join(os.path.expanduser(
'~'),
'history',
'a')
as f:
82 f.write(f
"{final_ref}\n")
Generating a Thumbnail During Publish
This example demonstrates the correct handling in a host of a hypothetical WantsThumbnail trait if set by a manager in its managementPolicy response.
It follows on from the preceding publishing example.
- Note
- This example uses imaginary, illustrative traits and specifications that are yet to be finalized.
2 if not manager.hasCapability(Manager.Capability.kPublishing):
6 if not WantsThumbnailTrait.isImbuedTo(policy):
12 thumbnail_ref = manager.preflight(
13 final_ref, ThumbnailFileSpecification.kTraitSet,
14 PublishAccess.kCreateRelated, context)
16 thumbnail_path = os.path.join(os.path.expanduser(
'~'),
'greeting.preview.png')
17 thumbnail_attr = {
"width": 128,
"height": 128}
20 if (LocatableContentTrait.isImbuedTo(manager_driven_policy)
or 21 RasterTrait.isImbuedTo(manager_driven_policy)):
22 requested = manager.resolve(
24 {LocatableContentTrait.kId, RasterTrait.kId},
25 ResolveAccess.kManagerDriven, context)
26 if requested_path := LocatableContentTrait(requested).getLocation():
27 thumbnail_path = utils.pathFromURL(requested_path)
28 raster_trait = RasterTrait(requested)
29 if raster_trait.isImbued():
31 thumbnail_attr[
"width"] = raster_trait.getWidth(thumbnail_attr[
"width"])
32 thumbnail_attr[
"height"] = raster_trait.getHeight(thumbnail_attr[
"height"])
35 mk_thumbnail(thumbnail_path, thumbnail_attr[
"width"], thumbnail_attr[
"height"])
40 thumbail_spec = ThumbnailFileSpecification.create()
41 thumbnail_spec.fileTrait().setPath(thumbnail_path)
42 raster_trait = thumbnail_spec.rasterTrait()
43 raster_trait.setWidth(thumbnail_attr[
"width"])
44 raster_trait.setHeight(thumbnail_attr[
"height"])
47 thumbnail_ref, thumbnail_spec.traitsData(),
48 PublishAccess.kWrite, context)