From 1c5f72d9f6c3154a9197d3c0c41db554fcfa256f Mon Sep 17 00:00:00 2001 From: David Feltell Date: Thu, 19 Sep 2024 18:43:36 +0100 Subject: [PATCH] squash! [Docs] Add hybrid plugin system notebook Use a custom hybrid plugin created especially for the notebook, rather than sellotaping BAL and SimpleCppManager together. This allows the notebook to avoid potentially confusing readers with misleading text about configuring two completely independent plugins to work together. Also add a section about information sharing between plugins via the `Context.managerState`. For the custom plugin sources, use linter config lifted from the main OpenAssetIO repo, with little modification. Signed-off-by: David Feltell --- .github/build_openassetio/action.yml | 17 +- .github/workflows/examples.yml | 33 +- .github/workflows/test.yml | 29 +- examples/hybrid_plugin_system.ipynb | 284 ++++++++++-------- .../SimpleCppManager/README.md | 10 - .../SimpleHybridManager/.clang-format | 5 + .../SimpleHybridManager/.clang-tidy | 79 +++++ .../SimpleHybridManager/README.md | 19 ++ .../SimpleHybridManager/cmake-format.yaml | 44 +++ .../linter-requirements.txt | 10 + .../PyComponentOfSimpleHybridManager.py | 141 +++++++++ .../SimpleHybridManager/pyproject.toml | 39 +++ .../SimpleHybridManager/src/CMakeLists.txt | 71 +++++ .../src/CppComponentOfSimpleHybridManager.cpp | 130 ++++++++ .../hybrid_plugin_system/bal_database.json | 42 --- .../openassetio_config.toml | 46 +-- 16 files changed, 742 insertions(+), 257 deletions(-) delete mode 100644 examples/resources/hybrid_plugin_system/SimpleCppManager/README.md create mode 100755 examples/resources/hybrid_plugin_system/SimpleHybridManager/.clang-format create mode 100644 examples/resources/hybrid_plugin_system/SimpleHybridManager/.clang-tidy create mode 100644 examples/resources/hybrid_plugin_system/SimpleHybridManager/README.md create mode 100644 examples/resources/hybrid_plugin_system/SimpleHybridManager/cmake-format.yaml create mode 100644 examples/resources/hybrid_plugin_system/SimpleHybridManager/linter-requirements.txt create mode 100644 examples/resources/hybrid_plugin_system/SimpleHybridManager/plugin/PyComponentOfSimpleHybridManager.py create mode 100644 examples/resources/hybrid_plugin_system/SimpleHybridManager/pyproject.toml create mode 100644 examples/resources/hybrid_plugin_system/SimpleHybridManager/src/CMakeLists.txt create mode 100644 examples/resources/hybrid_plugin_system/SimpleHybridManager/src/CppComponentOfSimpleHybridManager.cpp delete mode 100644 examples/resources/hybrid_plugin_system/bal_database.json diff --git a/.github/build_openassetio/action.yml b/.github/build_openassetio/action.yml index 5f28b2c..68cd603 100644 --- a/.github/build_openassetio/action.yml +++ b/.github/build_openassetio/action.yml @@ -7,6 +7,10 @@ name: Build OpenAssetIO description: Builds OpenAssetIO and publishes an artifact +inputs: + install-prefix: + description: Where to install OpenAssetIO once built + required: true runs: using: "composite" steps: @@ -20,13 +24,6 @@ runs: - name: Build OpenAssetIO shell: bash run: | - cd openassetio-checkout - mkdir build - cmake -G Ninja -S . -B build -DOPENASSETIO_ENABLE_SIMPLECPPMANAGER=ON - cmake --build build - cmake --install build - - uses: actions/upload-artifact@v3 - with: - name: OpenAssetIO Build - path: openassetio-checkout/build/dist - retention-days: 1 + cmake -G Ninja -S openassetio-checkout -B openassetio-checkout/build + cmake --build openassetio-checkout/build + cmake --install openassetio-checkout/build --prefix ${{ inputs.install-prefix }} diff --git a/.github/workflows/examples.yml b/.github/workflows/examples.yml index 3eb670e..6c6ee7e 100644 --- a/.github/workflows/examples.yml +++ b/.github/workflows/examples.yml @@ -40,27 +40,44 @@ jobs: test-cpp-notebooks: # A special job just for the Hybrid Plugin System because it needs a - # build of SimpleCppManager. Use the OpenAssetIO Docker container to - # get a build. If/when OpenAssetIO publishes SimpleCppManager with - # its release artifacts, this could be simplified. + # C++ build. name: Test Hybrid Plugin System notebook runs-on: ubuntu-latest container: image: ghcr.io/openassetio/openassetio-build + env: + SIMPLEHYBRIDMANAGER_SUBDIR: examples/resources/hybrid_plugin_system/SimpleHybridManager steps: - uses: actions/checkout@v3 - name: Install Dependencies run: | python -m pip install . python -m pip install -r examples/resources/requirements.txt + python -m pip install openassetio-traitgen - - name: Build SimpleCppManager + - name: Build/install OpenAssetIO uses: ./.github/build_openassetio + with: + install-prefix: ${{ env.SIMPLEHYBRIDMANAGER_SUBDIR }}/dependencies - - name: Copy SimpleCppManager to expected location for notebook - run: > - cp openassetio-checkout/build/dist/SimpleCppManager.so - examples/resources/hybrid_plugin_system/SimpleCppManager/ + - name: Build/install MediaCreation + run: | + cmake -S . -B build -G Ninja + cmake --build build + cmake --install build --prefix $SIMPLEHYBRIDMANAGER_SUBDIR/dependencies + + - name: Build/install SimpleHybridManager + run: | + cmake -S src -B build -G Ninja + cmake --build build + cmake --install build --prefix plugin + env: + CMAKE_PREFIX_PATH: dependencies + # Since we're in a Docker container, `github.workspace` doesn't + # give the correct location, except, for some reason, when used + # in a `working-directory` option. See + # https://github.com/actions/runner/issues/2058#issuecomment-1541828550 + working-directory: ${{ github.workspace }}/${{ env.SIMPLEHYBRIDMANAGER_SUBDIR }} - name: Test notebook run: jupyter nbconvert --to html --execute examples/hybrid_plugin_system.ipynb diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index b971612..1127b7b 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -11,16 +11,6 @@ concurrency: cancel-in-progress: true jobs: - build-openassetio: - name: Build OpenAssetIO - runs-on: ubuntu-latest - container: - image: ghcr.io/openassetio/openassetio-build - steps: - - uses: actions/checkout@v3 - - name: Build - uses: ./.github/build_openassetio - test-python: name: "${{ matrix.os }} python-${{ matrix.python }}" runs-on: ${{ matrix.os }} @@ -43,25 +33,22 @@ jobs: test-cpp: name: Test Cpp runs-on: ubuntu-latest - needs: build-openassetio container: - image: aswf/ci-base:2024 + image: ghcr.io/openassetio/openassetio-build steps: - uses: actions/checkout@v3 + - name: Build OpenAssetIO + uses: ./.github/build_openassetio + with: + install-prefix: openassetio + - name: Install Traitgen run: python -m pip install openassetio-traitgen==1.0.0a10 - - name: Get OpenAssetIO - uses: actions/download-artifact@v3 - with: - name: OpenAssetIO Build - path: ./openassetio-build - - name: Configure CMake build - run: > - cmake -S . -DCMAKE_PREFIX_PATH=`pwd`/openassetio-build -B build -G Ninja - --preset test + run: | + cmake -S . -DCMAKE_PREFIX_PATH=$(pwd)/openassetio -B build -G Ninja --preset test - name: Build tests run: cmake --build build diff --git a/examples/hybrid_plugin_system.ipynb b/examples/hybrid_plugin_system.ipynb index 03836ac..1373fd7 100644 --- a/examples/hybrid_plugin_system.ipynb +++ b/examples/hybrid_plugin_system.ipynb @@ -43,97 +43,45 @@ ], "id": "6ebc020b4c31f1b8" }, - { - "metadata": {}, - "cell_type": "markdown", - "source": "## Example", - "id": "959ede09b6700a1e" - }, { "metadata": {}, "cell_type": "markdown", "source": [ - "### Preamble\n", + "#### A note on information sharing\n", "\n", - "First lets get some boilerplate out of the way. This section contains instructions and code blocks required for setting up the environment required to execute this notebook. Reading through the following is not necessary to be able to understand the hybrid plugin system, and can be safely skipped." - ], - "id": "5b0c4ae1f1b914e6" - }, - { - "metadata": {}, - "cell_type": "markdown", - "source": [ + "Having two plugins in completely different languages, which logically form a single plugin, raises the question of how data should be shared between them.\n", "\n", - "In order to illustrate the hybrid plugin system, we'll make use of two ready-made example plugins - the \"Basic Asset Libary\" (aka BAL), a pure Python manager/plugin, and the \"Simple C++ Manager\" (aka SimpleCppManager), a pure C++ manager/plugin. \n", + "OpenAssetIO has a mechanism to help with this via the `Context` object. A `Context` instance is passed to (almost) every API method. A well-behaved host will re-use the same `Context` for all requests in the same logical process (typically an application session). \n", "\n", - "BAL should be installed into the Python environment of this notebook (see `resources/requirements.txt`), and so will be trivially discoverable by OpenAssetIO.\n", + "The `Context` object holds a `managerState` object, which can be used to communicate arbitrary information between the plugins. See the [API documentation](http://docs.openassetio.org/OpenAssetIO/stable_resolution.html#stable_resolution_manager_state) for more information.\n", "\n", - "SimpleCppManager is more complex, and must be built with a compiler toolchain compatible with the OpenAssetIO libraries in the Python environment of this notebook. See `resources/hybrid_plugin_system/SimpleCppManager/README.md` for more details. We assume it is installed into `resources/hybrid_plugin_system/SimpleCppManager`, and will be discovered by adding this location to the standard `OPENASSETIO_PLUGIN_PATH` environment variable." + "Populating the manager state in a way that can be read by both Python and C++ is left as an exercise to the reader. It's likely that the C++ plugin will require CPython as a dependency in order to translate between languages." ], - "id": "791f52d06eea9aea" - }, - { - "metadata": { - "ExecuteTime": { - "end_time": "2024-09-02T12:26:34.161690Z", - "start_time": "2024-09-02T12:26:34.155916Z" - } - }, - "cell_type": "code", - "source": [ - "import os\n", - "\n", - "\n", - "os.environ[\"OPENASSETIO_PLUGIN_PATH\"] = os.path.join(\n", - " \"resources\", \"hybrid_plugin_system\", \"SimpleCppManager\")" - ], - "id": "e3a94c5be66ad34e", - "outputs": [], - "execution_count": 1 + "id": "25aa01663aa0e7c3" }, { "metadata": {}, "cell_type": "markdown", - "source": [ - "\n", - "\n", - "These two plugins each advertise a different unique identifier, so will not work with the hybrid plugin system out of the box. Luckily, they are both designed to be used in testing situations and are fully configurable. We can control the identifier that they will advertise using environment variables:\n" - ], - "id": "e19f60f60b11d7f9" - }, - { - "metadata": { - "ExecuteTime": { - "end_time": "2024-09-02T12:26:34.202882Z", - "start_time": "2024-09-02T12:26:34.200517Z" - } - }, - "cell_type": "code", - "source": [ - "os.environ[\"OPENASSETIO_BAL_IDENTIFIER\"] = \"org.openassetio.examples.manager.hybrid\"\n", - "os.environ[\"OPENASSETIO_SIMPLECPPMANAGER_IDENTIFIER\"] = \"org.openassetio.examples.manager.hybrid\"" - ], - "id": "6c95ee177f2a9028", - "outputs": [], - "execution_count": 2 + "source": "## Example", + "id": "959ede09b6700a1e" }, { "metadata": {}, "cell_type": "markdown", - "source": "Other configuration is provided by the configuration file `resources/hybrid_plugin_system/openassetio_config.toml`. ", - "id": "2332a406713d0e76" + "source": "### Preamble", + "id": "5b0c4ae1f1b914e6" }, { "metadata": {}, "cell_type": "markdown", - "source": "Finally, let's get the standard OpenAssetIO bootstrapping boilerplate out of the way. See the \"Hello OpenAssetIO\" notebook for more details.", + "source": "Reading through the following is not necessary to be able to understand how to use the hybrid plugin system, and can be safely skipped. Let's get the standard OpenAssetIO bootstrapping boilerplate out of the way. See the \"Hello OpenAssetIO\" notebook for more details.", "id": "a80b0202bf806edb" }, { "metadata": { "ExecuteTime": { - "end_time": "2024-09-02T12:26:34.413131Z", - "start_time": "2024-09-02T12:26:34.374740Z" + "end_time": "2024-09-20T07:28:16.927468Z", + "start_time": "2024-09-20T07:28:16.901376Z" } }, "cell_type": "code", @@ -172,7 +120,45 @@ ], "id": "db2739c3f0a96d70", "outputs": [], - "execution_count": 3 + "execution_count": 1 + }, + { + "metadata": {}, + "cell_type": "markdown", + "source": "#### The example plugin(s)", + "id": "db3177717e0b2174" + }, + { + "metadata": {}, + "cell_type": "markdown", + "source": [ + "\n", + "In order to illustrate the hybrid plugin system, we'll make use of a super simple example hybrid plugin created just for this notebook, available in `resources/hybrid_plugin_system/SimpleHybridManager`.\n", + "\n", + "The Python plugin component is trivially available. However, the C++ plugin component is more complex, and must be built with a compiler toolchain compatible with the OpenAssetIO libraries in the Python environment of this notebook. See `resources/hybrid_plugin_system/SimpleHybridManager/README.md` for more details. \n", + "\n", + "We assume both the C++ and Python plugin components are installed into `resources/hybrid_plugin_system/SimpleHybridManager/plugin`, and will be discovered by adding this location to the standard `OPENASSETIO_PLUGIN_PATH` environment variable." + ], + "id": "791f52d06eea9aea" + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2024-09-20T07:28:16.966383Z", + "start_time": "2024-09-20T07:28:16.959210Z" + } + }, + "cell_type": "code", + "source": [ + "import os\n", + "\n", + "\n", + "os.environ[\"OPENASSETIO_PLUGIN_PATH\"] = os.path.join(\n", + " \"resources\", \"hybrid_plugin_system\", \"SimpleHybridManager\", \"plugin\")" + ], + "id": "e3a94c5be66ad34e", + "outputs": [], + "execution_count": 2 }, { "metadata": {}, @@ -189,8 +175,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2024-09-02T12:26:34.449434Z", - "start_time": "2024-09-02T12:26:34.427167Z" + "end_time": "2024-09-20T07:28:17.097817Z", + "start_time": "2024-09-20T07:28:17.084463Z" } }, "cell_type": "code", @@ -203,7 +189,7 @@ "cpp_factory = CppPluginSystemManagerImplementationFactory(logger)\n", "\n", "try:\n", - " _ = ManagerFactory.defaultManagerForInterface(\n", + " cpp_manager = ManagerFactory.defaultManagerForInterface(\n", " \"resources/hybrid_plugin_system/openassetio_config.toml\",\n", " host_interface,\n", " cpp_factory,\n", @@ -214,28 +200,52 @@ "\n", "python_factory = PythonPluginSystemManagerImplementationFactory(logger)\n", "\n", - "try:\n", - " _ = ManagerFactory.defaultManagerForInterface(\n", - " \"resources/hybrid_plugin_system/openassetio_config.toml\",\n", - " host_interface,\n", - " python_factory,\n", - " logger)\n", - "\n", - "except ConfigurationException as exc:\n", - " helpers.display_result(f\"Python plugin error: {exc}\")\n" + "py_manager = ManagerFactory.defaultManagerForInterface(\n", + " \"resources/hybrid_plugin_system/openassetio_config.toml\",\n", + " host_interface,\n", + " python_factory,\n", + " logger)" ], "id": "129439ba58f8e81f", "outputs": [ { "data": { - "text/markdown": "> **Result:**\n> `C++ plugin error: Manager implementation for 'org.openassetio.examples.manager.hybrid' does not support the required capabilities: managementPolicyQueries`" + "text/markdown": "> **Result:**\n> `C++ plugin error: Manager implementation for 'org.openassetio.examples.simplehybridmanager' does not support the required capabilities: entityReferenceIdentification, managementPolicyQueries, entityTraitIntrospection`" }, "metadata": {}, "output_type": "display_data" - }, + } + ], + "execution_count": 3 + }, + { + "metadata": {}, + "cell_type": "markdown", + "source": [ + "So the C++ plugin system found a plugin, but it doesn't support any of the required capabilities.\n", + "\n", + "We had better luck with the Python plugin system. However, we're going to want to `resolve` an entity. Is the Python plugin capable of resolution?" + ], + "id": "9328df2151a26971" + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2024-09-20T07:28:17.149217Z", + "start_time": "2024-09-20T07:28:17.144232Z" + } + }, + "cell_type": "code", + "source": [ + "can_resolve = py_manager.hasCapability(py_manager.Capability.kResolution)\n", + "\n", + "helpers.display_result(f\"Can resolve? {can_resolve}\")" + ], + "id": "27535337eda9f49e", + "outputs": [ { "data": { - "text/markdown": "> **Result:**\n> `Python plugin error: Manager implementation for 'org.openassetio.examples.manager.hybrid' does not support the required capabilities: entityReferenceIdentification`" + "text/markdown": "> **Result:**\n> `Can resolve? False`" }, "metadata": {}, "output_type": "display_data" @@ -246,8 +256,8 @@ { "metadata": {}, "cell_type": "markdown", - "source": "Neither of them advertise all the required capabilities! At least, on their own...", - "id": "9328df2151a26971" + "source": "So no, the Python plugin does not support the `resolve` method, at least on its own...", + "id": "8b67117d2f42a87e" }, { "metadata": {}, @@ -262,8 +272,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2024-09-02T12:26:34.481292Z", - "start_time": "2024-09-02T12:26:34.477619Z" + "end_time": "2024-09-20T07:28:17.201569Z", + "start_time": "2024-09-20T07:28:17.198604Z" } }, "cell_type": "code", @@ -288,18 +298,59 @@ "metadata": {}, "cell_type": "markdown", "source": [ - "Success! Notice how only a single config file (`openassetio_config.toml`) was provided. With hybrid plugins, the same configuration file is used for all the constituent plugins. In particular, any and all manager settings specified in the config file are passed to all plugins during initialisation.\n", + "Success! \n", + "\n", + "Notice how only a single config file (`openassetio_config.toml`) was provided. With hybrid plugins, the same configuration file is used for all the constituent plugins. In particular, any and all manager settings specified in the config file are passed to all plugins during initialisation.\n", "\n", + "Is this combined hybrid plugin now capable of resolution?" + ], + "id": "4924fc6cb8e0620f" + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2024-09-20T07:28:17.268483Z", + "start_time": "2024-09-20T07:28:17.265302Z" + } + }, + "cell_type": "code", + "source": [ + "can_resolve = manager.hasCapability(py_manager.Capability.kResolution)\n", "\n", - "Now lets retrieve some data. " + "helpers.display_result(f\"Can resolve? {can_resolve}\")" + ], + "id": "2869873c40ecc805", + "outputs": [ + { + "data": { + "text/markdown": "> **Result:**\n> `Can resolve? True`" + }, + "metadata": {}, + "output_type": "display_data" + } ], + "execution_count": 6 + }, + { + "metadata": {}, + "cell_type": "markdown", + "source": "Again, success! The resolution capability from the C++ plugin has been combined with the capabilities of the Python plugin.", "id": "52d907bb3a19db77" }, + { + "metadata": {}, + "cell_type": "markdown", + "source": [ + "\n", + "Now lets retrieve an entity's trait set from the manager:" + ], + "id": "e12f1acc9f62324a" + }, { "metadata": { "ExecuteTime": { - "end_time": "2024-09-02T12:26:34.532696Z", - "start_time": "2024-09-02T12:26:34.528549Z" + "end_time": "2024-09-20T07:28:17.337338Z", + "start_time": "2024-09-20T07:28:17.328692Z" } }, "cell_type": "code", @@ -308,7 +359,7 @@ "\n", "\n", "context = manager.createContext()\n", - "entity_ref = manager.createEntityReference(\"examplehybrid:///project_artwork/logos/openassetio\")\n", + "entity_ref = manager.createEntityReference(\"examplehybrid://example_entity\")\n", "\n", "trait_set = manager.entityTraits(entity_ref, EntityTraitsAccess.kRead, context)\n", "\n", @@ -318,77 +369,64 @@ "outputs": [ { "data": { - "text/markdown": "> **Result:**\n> `{'openassetio-mediacreation:identity.DisplayName', 'openassetio-mediacreation:usage.Entity', 'openassetio-mediacreation:twoDimensional.Image'}`" + "text/markdown": "> **Result:**\n> `{'openassetio-mediacreation:usage.Entity', 'openassetio-mediacreation:content.LocatableContent'}`" }, "metadata": {}, "output_type": "display_data" } ], - "execution_count": 6 + "execution_count": 7 }, { "metadata": {}, "cell_type": "markdown", - "source": [ - "So this is the entity's trait set, according to the (hybrid) manager. \n", - "\n", - "Or is it?\n", - "\n", - "What if both plugins advertise the same capability? In this case, the first plugin in the list supplied to the `HybridPluginSystemManagerImplementationFactory` constructor will be used." - ], + "source": "So the entity has the `LocatableContent` trait. Let's `resolve` its location:\n", "id": "85d2a4a633cc9e82" }, - { - "metadata": {}, - "cell_type": "markdown", - "source": "Now lets try a different configuration of plugins - note that this time `python_factory` is ordered before `cpp_factory`.", - "id": "76fa30f9d2f31969" - }, { "metadata": { "ExecuteTime": { - "end_time": "2024-09-02T12:26:34.598874Z", - "start_time": "2024-09-02T12:26:34.584335Z" + "end_time": "2024-09-20T07:28:17.393969Z", + "start_time": "2024-09-20T07:28:17.390013Z" } }, "cell_type": "code", "source": [ - "alt_hybrid_factory = HybridPluginSystemManagerImplementationFactory(\n", - " [python_factory, cpp_factory], logger)\n", + "from openassetio.access import ResolveAccess\n", + "from openassetio_mediacreation.traits.content import LocatableContentTrait\n", "\n", - "alt_manager = ManagerFactory.defaultManagerForInterface(\n", - " \"resources/hybrid_plugin_system/openassetio_config.toml\",\n", - " host_interface,\n", - " alt_hybrid_factory,\n", - " logger)\n", "\n", - "trait_set = alt_manager.entityTraits(entity_ref, EntityTraitsAccess.kRead, context)\n", + "trait_data = manager.resolve(entity_ref, {LocatableContentTrait.kId}, ResolveAccess.kRead, context)\n", "\n", - "helpers.display_result(trait_set)" + "url = LocatableContentTrait(trait_data).getLocation()\n", + "\n", + "helpers.display_result(url)\n" ], - "id": "e8008d8183fb262d", + "id": "af6d8d7ec842c204", "outputs": [ { "data": { - "text/markdown": "> **Result:**\n> `{'openassetio-mediacreation:timeDomain.FrameRanged', 'openassetio-mediacreation:lifecycle.Version', 'openassetio-mediacreation:content.LocatableContent', 'openassetio-mediacreation:twoDimensional.Image', 'openassetio-mediacreation:identity.DisplayName', 'openassetio-mediacreation:usage.Entity'}`" + "text/markdown": "> **Result:**\n> `/some/path.exr`" }, "metadata": {}, "output_type": "display_data" } ], - "execution_count": 7 + "execution_count": 8 }, { "metadata": {}, "cell_type": "markdown", "source": [ - "The trait set of the entity is different! \n", + "Success! Pretty straightforward.\n", + " \n", + "For further reading, if you inspect the C++ implementation at `resources/hybrid_plugin_system/SimpleHybridManager/src/CppComponentOfSimpleHybridManager.cpp` you'll find no implementation of the `entityTraits` method, or indeed any non-trivial method, other than `resolve`. \n", "\n", - "This is because both the C++ and Python plugins support the `\"entityTraitIntrospection\"` capability, and in the `alt_manager` case the Python plugin system was first in the list provided to the hybrid factory constructor. The plugin loaded by the Python factory therefore gets priority for API calls, and the value returned from `entityTraits` by the Python plugin is different from the value returned by the C++ plugin.\n", + "Similarly, if you inspect the Python implementation at `resources/hybrid_plugin_system/SimpleHybridManager/plugin/PyComponentOfSimpleHybridManager.py` you'll find no implementation of the `resolve` method. \n", "\n", - "> ⚠ In real-world usage, constituent plugins must return the same results when they support the same capability!" + "So the two plugins have been seamlessly combined into a single interface." ], - "id": "1fb66b7c6fd38071" + "id": "be76095fb0d00046" } ], "metadata": { diff --git a/examples/resources/hybrid_plugin_system/SimpleCppManager/README.md b/examples/resources/hybrid_plugin_system/SimpleCppManager/README.md deleted file mode 100644 index bdb479d..0000000 --- a/examples/resources/hybrid_plugin_system/SimpleCppManager/README.md +++ /dev/null @@ -1,10 +0,0 @@ -# Simple C++ Manager - -This directory is where the hybrid_plugin_system.ipynb notebook will -look for the SimpleCppManager plugin. - -In order for that notebook to run, the SimpleCppManager plugin must be -built and installed here. - -See the [SimpleCppManager README](https://github.com/OpenAssetIO/OpenAssetIO/blob/main/examples/manager/SimpleCppManager/README.md) -for more details. \ No newline at end of file diff --git a/examples/resources/hybrid_plugin_system/SimpleHybridManager/.clang-format b/examples/resources/hybrid_plugin_system/SimpleHybridManager/.clang-format new file mode 100755 index 0000000..5873ad1 --- /dev/null +++ b/examples/resources/hybrid_plugin_system/SimpleHybridManager/.clang-format @@ -0,0 +1,5 @@ +BasedOnStyle: Google +Language: Cpp +Standard: c++17 +ColumnLimit: 99 +IncludeBlocks: Preserve diff --git a/examples/resources/hybrid_plugin_system/SimpleHybridManager/.clang-tidy b/examples/resources/hybrid_plugin_system/SimpleHybridManager/.clang-tidy new file mode 100644 index 0000000..b133980 --- /dev/null +++ b/examples/resources/hybrid_plugin_system/SimpleHybridManager/.clang-tidy @@ -0,0 +1,79 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2022 Google LLC +# Copyright 2024 The Foundry Visionmongers Ltd + +# Modified from: https://raw.githubusercontent.com/googleapis/google-cloud-cpp/main/.clang-tidy +# See: https://releases.llvm.org/10.0.0/tools/clang/tools/extra/docs/clang-tidy/checks/list.html +--- +# Configure clang-tidy for this project. + +# Here is an explanation for why some of the checks are disabled: +# +# -modernize-use-trailing-return-type: clang-tidy recommends using +# `auto Foo() -> std::string { return ...; }`, we think the code is less +# readable in this form. +# +# -modernize-return-braced-init-list: We think removing typenames and using +# only braced-init can hurt readability. +# +# -modernize-avoid-c-arrays: We only use C arrays when they seem to be the +# right tool for the job, such as `char foo[] = "hello"`. In these cases, +# avoiding C arrays often makes the code less readable, and std::array is +# not a drop-in replacement because it doesn't deduce the size. +# +# -google-runtime-references: Allow usage of non-const references as +# function parameters. Otherwise we'd have to use pointers, which +# cpp core guidelines recommends against unless the parameter is +# nullable: https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#f60-prefer-t-over-t-when-no-argument-is-a-valid-option +# +# +Checks: > + -*, + bugprone-*, + google-*, + misc-*, + modernize-*, + performance-*, + portability-*, + readability-*, + -modernize-return-braced-init-list, + -modernize-use-trailing-return-type, + -modernize-avoid-c-arrays, + +# Turn all the warnings from the checks above into errors. +WarningsAsErrors: "*" +# Scan all (non-system) headers. +HeaderFilterRegex: '.*' +# Use .clang-format for fix suggestions. +FormatStyle: file + +CheckOptions: + - { key: readability-identifier-naming.NamespaceCase, value: camelBack } + - { key: readability-identifier-naming.ClassCase, value: CamelCase } + - { key: readability-identifier-naming.StructCase, value: CamelCase } + - { key: readability-identifier-naming.TemplateParameterCase, value: CamelCase } + - { key: readability-identifier-naming.FunctionCase, value: camelBack } + - { key: readability-identifier-naming.VariableCase, value: camelBack } + # Allow _1, _2, etc as variable (placeholder) names - used for + # Trompeloeil mock setup. + # TODO(DF): This should be placed in `tests/.clang-tidy` but that + # doesn't work. Reported upstream: https://github.com/llvm/llvm-project/issues/54781 + - { key: readability-identifier-naming.VariableIgnoredRegexp, value: "^_[0-9]+$"} + - { key: readability-identifier-naming.ClassMemberCase, value: camelBack } + - { key: readability-identifier-naming.PrivateMemberSuffix, value: _ } + - { key: readability-identifier-naming.ProtectedMemberSuffix, value: _ } + - { key: readability-identifier-naming.EnumConstantCase, value: CamelCase } + - { key: readability-identifier-naming.EnumConstantPrefix, value: k } + - { key: readability-identifier-naming.ConstexprVariableCase, value: CamelCase } + - { key: readability-identifier-naming.ConstexprVariablePrefix, value: k } + - { key: readability-identifier-naming.GlobalConstantCase, value: CamelCase } + - { key: readability-identifier-naming.GlobalConstantPrefix, value: k } + - { key: readability-identifier-naming.MemberConstantCase, value: CamelCase } + - { key: readability-identifier-naming.MemberConstantPrefix, value: k } + - { key: readability-identifier-naming.StaticConstantCase, value: CamelCase } + - { key: readability-identifier-naming.StaticConstantPrefix, value: k } + - { key: readability-implicit-bool-conversion.AllowIntegerConditions, value: 1 } + - { key: readability-implicit-bool-conversion.AllowPointerConditions, value: 1 } + # Allow structs where (all) member variables are public, even if + # the struct has member functions. + - { key: misc-non-private-member-variables-in-classes.IgnoreClassesWithAllMemberVariablesBeingPublic, value: 1 } diff --git a/examples/resources/hybrid_plugin_system/SimpleHybridManager/README.md b/examples/resources/hybrid_plugin_system/SimpleHybridManager/README.md new file mode 100644 index 0000000..3683415 --- /dev/null +++ b/examples/resources/hybrid_plugin_system/SimpleHybridManager/README.md @@ -0,0 +1,19 @@ +# Simple Hybrid Manager + +This directory contains a very simple OpenAssetIO hybrid C++/Python +manager plugin. It is used in the _Hybrid Plugin System_ Jupyter +notebook. + +## C++ component + +The C++ component of the plugin is provided as sources under the `src` +directory, and so must be built before it can be used. + +It has OpenAssetIO and OpenAssetIO-MediaCreation as CMake dependencies, +so these projects need to be built and installed somewhere discoverable +by CMake. + +For the Jupyter Notebook to run, the resulting `.so`/`.dll` must be +placed in the `plugin` directory. This can be done using `cmake +--install` and setting `--install-prefix`/`--prefix` to the `plugin` +directory; or by simply copying the file from the build directory. \ No newline at end of file diff --git a/examples/resources/hybrid_plugin_system/SimpleHybridManager/cmake-format.yaml b/examples/resources/hybrid_plugin_system/SimpleHybridManager/cmake-format.yaml new file mode 100644 index 0000000..dd37e5f --- /dev/null +++ b/examples/resources/hybrid_plugin_system/SimpleHybridManager/cmake-format.yaml @@ -0,0 +1,44 @@ +_help_format: Options affecting formatting. +format: + _help_line_width: + - How wide to allow formatted cmake files + line_width: 99 + _help_tab_size: + - How many spaces to tab for indent + tab_size: 4 +_help_lint: Options affecting the linter +lint: + _help_disabled_codes: + - function( list of lint codes to disable + - C0113, Missing COMMENT in statement which allows it + disabled_codes: ["C0113"] + _help_function_pattern: + - regular expression pattern describing valid function names + function_pattern: "[0-9a-z_]+" + _help_macro_pattern: + - regular expression pattern describing valid macro names + macro_pattern: "[0-9a-z_]+" + _help_global_var_pattern: + - regular expression pattern describing valid names for + - variables with global (cache) scope + global_var_pattern: "[A-Z][0-9A-Z_]+" + _help_internal_var_pattern: + - regular expression pattern describing valid names for + - variables with global scope (but internal semantic) + internal_var_pattern: _[A-Z][0-9A-Z_]+ + _help_local_var_pattern: + - regular expression pattern describing valid names for + - variables with local scope + local_var_pattern: "[a-z][a-z0-9_]+" + _help_private_var_pattern: + - regular expression pattern describing valid names for + - private directory variables + private_var_pattern: _[0-9a-z_]+ + _help_public_var_pattern: + - regular expression pattern describing valid names for public + - directory variables + public_var_pattern: "[A-Z][0-9A-Z_]+" + _help_argument_var_pattern: + - regular expression pattern describing valid names for + - function/macro arguments and loop variables. + argument_var_pattern: "[a-z][a-z0-9_]+" diff --git a/examples/resources/hybrid_plugin_system/SimpleHybridManager/linter-requirements.txt b/examples/resources/hybrid_plugin_system/SimpleHybridManager/linter-requirements.txt new file mode 100644 index 0000000..84c2cf6 --- /dev/null +++ b/examples/resources/hybrid_plugin_system/SimpleHybridManager/linter-requirements.txt @@ -0,0 +1,10 @@ +# Python auto-formatter. +black==24.8.0 +# Python linter. +pylint==3.2.7 +# pflake8 - flake8 PEP8 linter with settings from pyproject.toml. +pyproject-flake8==7.0.0 +# CMake linter. +cmakelang==0.6.13 +# cmakelang dependency. +pyyaml==6.0.0 \ No newline at end of file diff --git a/examples/resources/hybrid_plugin_system/SimpleHybridManager/plugin/PyComponentOfSimpleHybridManager.py b/examples/resources/hybrid_plugin_system/SimpleHybridManager/plugin/PyComponentOfSimpleHybridManager.py new file mode 100644 index 0000000..070586d --- /dev/null +++ b/examples/resources/hybrid_plugin_system/SimpleHybridManager/plugin/PyComponentOfSimpleHybridManager.py @@ -0,0 +1,141 @@ +# SPDX-License-Identifier: BSD-3-Clause +# Copyright 2024 The Foundry Visionmongers Ltd +""" +A single-class module, providing the SimpleHybridManagerInterface class. +""" +# pylint: disable=unused-argument + +from openassetio.trait import TraitsData +from openassetio.errors import BatchElementError +from openassetio.access import PolicyAccess, EntityTraitsAccess +from openassetio.managerApi import ManagerInterface +from openassetio.pluginSystem import PythonPluginSystemManagerPlugin + +from openassetio_mediacreation.traits.content import LocatableContentTrait +from openassetio_mediacreation.traits.managementPolicy import ManagedTrait +from openassetio_mediacreation.traits.usage import EntityTrait + +# Unique ID of the plugin. Must match that advertised by the partner C++ +# plugin. +kPluginId = "org.openassetio.examples.simplehybridmanager" +# The one and only entity reference we support. +kTheEntityReference = "examplehybrid://example_entity" + + +class SimpleHybridManagerInterface(ManagerInterface): + """ + Python side of the hybrid plugin. + """ + + def identifier(self): + """ + Identifier must match the partner C++ plugin's identifier. + """ + return kPluginId + + def displayName(self): + """ + This display name will be used if the Python plugin system takes + precedence in the hybrid plugin system. + """ + return "Simple Hybrid Manager" + + def hasCapability(self, capability): + """ + This plugin supports only the minimal required set of + capabilities. It does not even support `resolve`. However, the + partner C++ plugin does support `resolve`. + """ + if capability in ( + ManagerInterface.Capability.kEntityReferenceIdentification, + ManagerInterface.Capability.kManagementPolicyQueries, + ManagerInterface.Capability.kEntityTraitIntrospection, + ): + return True + + return False + + def managementPolicy(self, traitSets, policyAccess, context, hostSession): + """ + Only support reading file paths (or URLs). + """ + policies = [TraitsData() for _ in traitSets] + if policyAccess != PolicyAccess.kRead: + # We only support read access. + return policies + + for trait_set, policy_data in zip(traitSets, policies): + if not {EntityTrait.kId, LocatableContentTrait.kId} <= trait_set: + # We only support file entities. + continue + ManagedTrait.imbueTo(policy_data) + + return policies + + def isEntityReferenceString(self, someString, hostSession): + """ + Both Python and C++ plugins should expect the same entity + reference format. + """ + return someString.startswith("examplehybrid://") + + def entityTraits( + self, + entityReferences, + entityTraitsAccess, + context, + _hostSession, + successCallback, + errorCallback, + ): + """ + This Python plugin provides introspection of entities to get + their trait set, whereas the values for the properties of the + traits are `resolve`d through the partner C++ plugin. + """ + + # Only support reading. + if entityTraitsAccess != EntityTraitsAccess.kRead: + result = BatchElementError( + BatchElementError.ErrorCode.kEntityAccessError, "Entities are read-only" + ) + for idx in range(len(entityReferences)): + errorCallback(idx, result) + return + + for idx, ref in enumerate(entityReferences): + if ref.toString() == kTheEntityReference: + successCallback(idx, {EntityTrait.kId, LocatableContentTrait.kId}) + else: + errorCallback( + idx, + BatchElementError( + BatchElementError.ErrorCode.kEntityResolutionError, + f"Entity '{ref.toString()}' not found", + ), + ) + + +class SimpleHybridManagerPlugin(PythonPluginSystemManagerPlugin): + """ + Entry point for the plugin. + """ + + @staticmethod + def identifier(): + """ + Identifier must match the partner C++ plugin's identifier. + """ + return kPluginId + + @classmethod + def interface(cls): + """ + Create the Python side of the hybrid plugin interface. + """ + return SimpleHybridManagerInterface() + + +# Public entry point that will be searched for by the plugin system. +# pylint: disable=invalid-name +openassetioPlugin = SimpleHybridManagerPlugin diff --git a/examples/resources/hybrid_plugin_system/SimpleHybridManager/pyproject.toml b/examples/resources/hybrid_plugin_system/SimpleHybridManager/pyproject.toml new file mode 100644 index 0000000..6bc0e87 --- /dev/null +++ b/examples/resources/hybrid_plugin_system/SimpleHybridManager/pyproject.toml @@ -0,0 +1,39 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 The Foundry Visionmongers Ltd + +[tool.pylint.messages_control] +disable = [ + "too-many-arguments", + "too-few-public-methods", +] + +# NB: This requires the use of pyproject-flake8 +[tool.flake8] +max-line-length = 99 +extend-ignore = "E266," + +[tool.pylint.format] +max-line-length = 99 + +[tool.pylint.basic] +argument-naming-style = "camelCase" +class-const-naming-style = "camelCase" +variable-naming-style = "snake_case" + +# Support both camelCase and PascalCase for modules +module-rgx = "_?([a-z]|[A-Z])+([A-Z][a-z0-9]*)*" +# camelCase doesn't include "__camelCase" or "test_camelCase" +attr-rgx = "_?_?[a-z0-9]+([A-Z][a-z0-9]*)*" +method-rgx = "(_?_|test_)?[a-z0-9]+([A-Z][a-z0-9]*)*" +function-rgx = "(_|test_)?[a-z0-9]+([A-Z][a-z0-9]*)*" +# C++ style constants, e.g. `kThing_SubThing`. +const-rgx = "k([A-Z0-9]+[a-z0-9]*)+_?([A-Z0-9]+[a-z0-9]*)*" +class-const-rgx = "k([A-Z0-9]+[a-z0-9]*)+_?([A-Z0-9]+[a-z0-9]*)*" + +[tool.pylint.similarities] +# Ignore imports when computing similarities. +ignore-imports = true + +[tool.black] +line-length = 99 +target-version = ["py311"] \ No newline at end of file diff --git a/examples/resources/hybrid_plugin_system/SimpleHybridManager/src/CMakeLists.txt b/examples/resources/hybrid_plugin_system/SimpleHybridManager/src/CMakeLists.txt new file mode 100644 index 0000000..2cdbf0b --- /dev/null +++ b/examples/resources/hybrid_plugin_system/SimpleHybridManager/src/CMakeLists.txt @@ -0,0 +1,71 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 The Foundry Visionmongers Ltd +cmake_minimum_required(VERSION 3.27) + +project(CppComponentOfSimpleHybridManager LANGUAGES CXX) + +set(_target_name ${PROJECT_NAME}) + +add_library(${_target_name} MODULE) + +install( + TARGETS ${_target_name} + EXPORT ${PROJECT_NAME}_EXPORTED_TARGETS + DESTINATION . +) + +#----------------------------------------------------------------------- +# Target properties. + +set_target_properties( + ${_target_name} + PROPERTIES + + # Ensure consistent C++17 standard. + CXX_STANDARD 17 + CXX_STANDARD_REQUIRED YES + CXX_EXTENSIONS NO + + # Ensure non-exported symbols are hidden from the host application. + C_VISIBILITY_PRESET hidden + CXX_VISIBILITY_PRESET hidden + VISIBILITY_INLINES_HIDDEN YES + + # Use a predictable name for the plugin binary. + OUTPUT_NAME ${PROJECT_NAME} + PREFIX "" + SOVERSION "" + VERSION "" +) + +#----------------------------------------------------------------------- +# Compiler warnings. + +if (CMAKE_CXX_COMPILER_ID MATCHES "Clang" OR CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + set(_project_warnings -Wall -Wextra -Wpedantic) +endif () + +target_compile_options(${_target_name} PRIVATE ${_project_warnings}) + +#----------------------------------------------------------------------- +# API export header. + +include(GenerateExportHeader) +generate_export_header( + ${_target_name} + EXPORT_FILE_NAME ${CMAKE_CURRENT_BINARY_DIR}/include/export.h + EXPORT_MACRO_NAME OPENASSETIO_EXAMPLE_SIMPLEHYBRIDMANAGER_EXPORT +) + +#----------------------------------------------------------------------- +# Target dependencies. + +target_sources(${_target_name} PRIVATE ${PROJECT_NAME}.cpp) + +# For generated API export header. +target_include_directories(${_target_name} PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/include) + +find_package(OpenAssetIO REQUIRED) +target_link_libraries(${_target_name} PRIVATE OpenAssetIO::openassetio-core) +find_package(OpenAssetIO-MediaCreation REQUIRED) +target_link_libraries(${_target_name} PRIVATE OpenAssetIO-MediaCreation::openassetio-mediacreation) diff --git a/examples/resources/hybrid_plugin_system/SimpleHybridManager/src/CppComponentOfSimpleHybridManager.cpp b/examples/resources/hybrid_plugin_system/SimpleHybridManager/src/CppComponentOfSimpleHybridManager.cpp new file mode 100644 index 0000000..d6a8c2e --- /dev/null +++ b/examples/resources/hybrid_plugin_system/SimpleHybridManager/src/CppComponentOfSimpleHybridManager.cpp @@ -0,0 +1,130 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2024 The Foundry Visionmongers Ltd +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace { +// Unique ID of the plugin. Must match that advertised by the partner +// Python plugin. +constexpr std::string_view kPluginId = "org.openassetio.examples.simplehybridmanager"; +// The one and only entity reference we support. These two lines +// represent our backend database. +constexpr std::string_view kTheEntityReference = "examplehybrid://example_entity"; +constexpr std::string_view kTheEntityPath = "/some/path.exr"; +} // namespace + +/** + * C++ side of Simple Hybrid Manager. + */ +struct SimpleHybridManagerInterface final : openassetio::managerApi::ManagerInterface { + /** + * Identifier must match the partner Python plugin's identifier. + */ + [[nodiscard]] openassetio::Identifier identifier() const override { + return openassetio::Identifier{kPluginId}; + } + + /** + * displayName has no base class implementation so must be + * implemented. + * + * This display name will be used if the C++ plugin system takes + * precedence in the hybrid plugin system. + */ + [[nodiscard]] openassetio::Str displayName() const override { return "Simple Hybrid Manager"; } + + /** + * The C++ side of this hybrid plugin is solely responsible for + * `resolve` and nothing else. + */ + [[nodiscard]] bool hasCapability(const Capability capability) override { + return capability == Capability::kResolution; + } + + /** + * Implementation of `resolve` in C++ - the only capability supported + * by this plugin. Other capabilities are handled by the partner + * Python plugin. + */ + void resolve(const openassetio::EntityReferences& entityReferences, + const openassetio::trait::TraitSet& traitSet, + const openassetio::access::ResolveAccess resolveAccess, + [[maybe_unused]] const openassetio::ContextConstPtr& context, + [[maybe_unused]] const openassetio::managerApi::HostSessionPtr& hostSession, + const ResolveSuccessCallback& successCallback, + const BatchElementErrorCallback& errorCallback) override { + using openassetio::EntityReference; + using openassetio::access::ResolveAccess; + using openassetio::errors::BatchElementError; + using openassetio::trait::TraitsData; + using openassetio::trait::TraitsDataPtr; + using openassetio_mediacreation::traits::content::LocatableContentTrait; + + // We only support read access. + if (resolveAccess != ResolveAccess::kRead) { + for (std::size_t idx = 0; idx < entityReferences.size(); ++idx) { + errorCallback(idx, BatchElementError{BatchElementError::ErrorCode::kEntityAccessError, + "Entity access is read-only"}); + } + return; + } + + // Loop each entity reference in the input batch. + for (std::size_t idx = 0; idx < entityReferences.size(); ++idx) { + // We only support one entity. + if (entityReferences[idx].toString() == kTheEntityReference) { + TraitsDataPtr traitsData = TraitsData::make(); + + // Populate the requested traits with their properties. We only + // support one trait. + if (traitSet.count(LocatableContentTrait::kId)) { + LocatableContentTrait{traitsData}.setLocation(openassetio::Str{kTheEntityPath}); + } + + successCallback(idx, std::move(traitsData)); + } else { + // If we can't find the entity reference in the database, then + // flag an error. + errorCallback(idx, BatchElementError{BatchElementError::ErrorCode::kEntityResolutionError, + "Entity not found"}); + } + } + } +}; + +/** + * Subclass of the CppPluginSystemManagerPlugin that can be used to + * construct instances of our simple ManagerInterface. + */ +struct SimpleHybridManagerPlugin final : openassetio::pluginSystem::CppPluginSystemManagerPlugin { + [[nodiscard]] openassetio::Identifier identifier() const override { + return openassetio::Identifier{kPluginId}; + } + openassetio::managerApi::ManagerInterfacePtr interface() override { + return std::make_shared(); + } +}; + +extern "C" { +/** + * External entry point that the OpenAssetIO plugin system will query. + */ +OPENASSETIO_EXAMPLE_SIMPLEHYBRIDMANAGER_EXPORT +openassetio::pluginSystem::PluginFactory openassetioPlugin() noexcept { + return []() noexcept -> openassetio::pluginSystem::CppPluginSystemPluginPtr { + return std::make_shared(); + }; +} +} diff --git a/examples/resources/hybrid_plugin_system/bal_database.json b/examples/resources/hybrid_plugin_system/bal_database.json deleted file mode 100644 index f3894d2..0000000 --- a/examples/resources/hybrid_plugin_system/bal_database.json +++ /dev/null @@ -1,42 +0,0 @@ -{ - "capabilities": ["managementPolicyQueries","entityTraitIntrospection"], - "managementPolicy": { - "read": { - "default": { - "openassetio-mediacreation:managementPolicy.Managed": {}, - "openassetio-mediacreation:content.LocatableContent": {}, - "openassetio-mediacreation:identity.DisplayName": {}, - "openassetio-mediacreation:lifecycle.Version": {} - } - }, - "write": { - "default": { - "openassetio-mediacreation:managementPolicy.Managed": {}, - "openassetio-mediacreation:content.LocatableContent": {}, - "openassetio-mediacreation:identity.DisplayName": {} - } - }, - "managerDriven": { - "default": { - "openassetio-mediacreation:managementPolicy.Managed": {}, - "openassetio-mediacreation:content.LocatableContent": {}, - "openassetio-mediacreation:identity.DisplayName": {} - } - } - }, - "entities": { - "project_artwork/logos/openassetio": { - "versions": [ - { - "traits": { - "openassetio-mediacreation:usage.Entity": {}, - "openassetio-mediacreation:twoDimensional.Image": {}, - "openassetio-mediacreation:identity.DisplayName": {}, - "openassetio-mediacreation:content.LocatableContent": {}, - "openassetio-mediacreation:timeDomain.FrameRanged": {} - } - } - ] - } - } -} diff --git a/examples/resources/hybrid_plugin_system/openassetio_config.toml b/examples/resources/hybrid_plugin_system/openassetio_config.toml index 5ddb98e..8d85bed 100644 --- a/examples/resources/hybrid_plugin_system/openassetio_config.toml +++ b/examples/resources/hybrid_plugin_system/openassetio_config.toml @@ -1,44 +1,4 @@ [manager] -# Identifier advertised by BAL and SimpleCppManager, overridden using -# environment variables. -identifier = "org.openassetio.examples.manager.hybrid" - -[manager.settings] - -######################################################################## -# BAL settings - -# BAL JSON library containing its settings/database. -# -# In the JSON config file we: -# * Set the capabilities to not advertise entityReferenceIdentification, -# or resolution, since SimpleCppManager will satisfy those -# capabilities (see below). -# * Note that entityTraitIntrospection is advertised by both managers, -# so which manager is used for `entityTraits(...)` API calls depends -# on priority order. -library_path = "${config_dir}/bal_database.json" - -# Entity reference format should match. Overridden similarly for -# SimpleCppManager, below. -entity_reference_url_scheme = "examplehybrid" - -######################################################################## -# SimpleCppManager settings: - -# Entity reference format should match. Overridden similarly for BAL, -# above. -prefix = "examplehybrid:///" - -# SimpleCppManager does not advertise "managementPolicyQueries", which -# is a required capability for a manager. But when used as part of a -# hybrid plugin, the capability can be satisfied by the other -# constituent plugins(s). -capabilities = "entityReferenceIdentification,resolution,entityTraitIntrospection" - -# The database of entities, as regurgitated by SimpleCppManager. -read_traits = ''' -examplehybrid:///project_artwork/logos/openassetio,openassetio-mediacreation:usage.Entity -examplehybrid:///project_artwork/logos/openassetio,openassetio-mediacreation:twoDimensional.Image -examplehybrid:///project_artwork/logos/openassetio,openassetio-mediacreation:identity.DisplayName,name,The OpenAssetIO Logo -''' +# Identifier advertised by both the C++ and Python plugins that make up +# the hybrid manager SimpleHybridManager. +identifier = "org.openassetio.examples.simplehybridmanager" \ No newline at end of file