Repository: OpenAssetIO/OpenAssetIO (language: python)

Issue:
Migrate `flushCaches` to C+
## ACs

- Default noop implementation
- Manager test harness flushes caches between each test case.
Migrate `flushCaches` to C+
## ACs

- Default noop implementation
- Manager test harness flushes caches between each test case.


Discussion / hints:

Fix patch:
diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md
index 6ae9d1d19..3186e5ad0 100644
--- a/RELEASE_NOTES.md
+++ b/RELEASE_NOTES.md
@@ -68,6 +68,15 @@ v1.0.0-alpha.xx
   objects, respectively.
   [#914](https://github.com/OpenAssetIO/OpenAssetIO/issues/914)
 
+- Migrated the `ManagerInterface`/`Manager` `flushCaches` method to C++
+  with Python bindings.
+  [#994](https://github.com/OpenAssetIO/OpenAssetIO/issues/994)
+
+- Added a call to `flushCaches` after every `openassetio.test.manager`
+  (aka `apiComplianceSuite`) test case, giving the manager plugin a
+  chance to clean up between tests.
+  [#994](https://github.com/OpenAssetIO/OpenAssetIO/issues/994)
+
 v1.0.0-alpha.12
 ---------------
 
diff --git a/src/openassetio-core/include/openassetio/hostApi/Manager.hpp b/src/openassetio-core/include/openassetio/hostApi/Manager.hpp
index 1db81bf40..88e196061 100644
--- a/src/openassetio-core/include/openassetio/hostApi/Manager.hpp
+++ b/src/openassetio-core/include/openassetio/hostApi/Manager.hpp
@@ -170,6 +170,15 @@ class OPENASSETIO_CORE_EXPORT Manager {
    */
   void initialize(InfoDictionary managerSettings);
 
+  /**
+   * Clears any internal caches.
+   *
+   * Only applicable if the manager makes use of any caching, otherwise
+   * it is a no-op.  In caching interfaces, this should cause any
+   * retained data to be discarded to ensure future queries are fresh.
+   */
+  void flushCaches();
+
   /**
    * @}
    */
diff --git a/src/openassetio-core/include/openassetio/managerApi/ManagerInterface.hpp b/src/openassetio-core/include/openassetio/managerApi/ManagerInterface.hpp
index c46da72bd..3448ad07d 100644
--- a/src/openassetio-core/include/openassetio/managerApi/ManagerInterface.hpp
+++ b/src/openassetio-core/include/openassetio/managerApi/ManagerInterface.hpp
@@ -304,6 +304,17 @@ class OPENASSETIO_CORE_EXPORT ManagerInterface {
    */
   virtual void initialize(InfoDictionary managerSettings, const HostSessionPtr& hostSession) = 0;
 
+  /**
+   * Clears any internal caches.
+   *
+   * Only applicable if the implementation makes use of any caching,
+   * otherwise it is a no-op. In caching interfaces, this will cause any
+   * retained data to be discarded to ensure future queries are fresh.
+   *
+   * @param hostSession The API session.
+   */
+  virtual void flushCaches(const HostSessionPtr& hostSession);
+
   /**
    * @}
    */
diff --git a/src/openassetio-core/src/hostApi/Manager.cpp b/src/openassetio-core/src/hostApi/Manager.cpp
index 5a0de0c0d..93da09caf 100644
--- a/src/openassetio-core/src/hostApi/Manager.cpp
+++ b/src/openassetio-core/src/hostApi/Manager.cpp
@@ -62,6 +62,8 @@ void Manager::initialize(InfoDictionary managerSettings) {
   managerInterface_->initialize(std::move(managerSettings), hostSession_);
 }
 
+void Manager::flushCaches() { managerInterface_->flushCaches(hostSession_); }
+
 trait::TraitsDatas Manager::managementPolicy(const trait::TraitSets &traitSets,
                                              const ContextConstPtr &context) const {
   return managerInterface_->managementPolicy(traitSets, context, hostSession_);
diff --git a/src/openassetio-core/src/managerApi/ManagerInterface.cpp b/src/openassetio-core/src/managerApi/ManagerInterface.cpp
index 5cb52e570..444c9dc3e 100644
--- a/src/openassetio-core/src/managerApi/ManagerInterface.cpp
+++ b/src/openassetio-core/src/managerApi/ManagerInterface.cpp
@@ -20,6 +20,8 @@ InfoDictionary ManagerInterface::settings(
   return openassetio::InfoDictionary{};
 }
 
+void ManagerInterface::flushCaches([[maybe_unused]] const HostSessionPtr& hostSession) {}
+
 ManagerStateBasePtr ManagerInterface::createState(
     [[maybe_unused]] const HostSessionPtr& hostSession) {
   return nullptr;
diff --git a/src/openassetio-python/cmodule/src/hostApi/ManagerBinding.cpp b/src/openassetio-python/cmodule/src/hostApi/ManagerBinding.cpp
index 24c6ba716..158e00c8f 100644
--- a/src/openassetio-python/cmodule/src/hostApi/ManagerBinding.cpp
+++ b/src/openassetio-python/cmodule/src/hostApi/ManagerBinding.cpp
@@ -68,6 +68,7 @@ void registerManager(const py::module& mod) {
       .def("info", &Manager::info)
       .def("settings", &Manager::settings)
       .def("initialize", &Manager::initialize, py::arg("managerSettings"))
+      .def("flushCaches", &Manager::flushCaches)
       .def("managementPolicy", &Manager::managementPolicy, py::arg("traitSets"),
            py::arg("context").none(false))
       .def("createContext", &Manager::createContext)
diff --git a/src/openassetio-python/cmodule/src/managerApi/ManagerInterfaceBinding.cpp b/src/openassetio-python/cmodule/src/managerApi/ManagerInterfaceBinding.cpp
index 5d3d48674..357eff712 100644
--- a/src/openassetio-python/cmodule/src/managerApi/ManagerInterfaceBinding.cpp
+++ b/src/openassetio-python/cmodule/src/managerApi/ManagerInterfaceBinding.cpp
@@ -50,6 +50,10 @@ struct PyManagerInterface : ManagerInterface {
                            hostSession);
   }
 
+  void flushCaches(const HostSessionPtr& hostSession) override {
+    PYBIND11_OVERRIDE(void, ManagerInterface, flushCaches, hostSession);
+  }
+
   [[nodiscard]] trait::TraitsDatas managementPolicy(
       const trait::TraitSets& traitSets, const ContextConstPtr& context,
       const HostSessionPtr& hostSession) const override {
@@ -185,6 +189,7 @@ void registerManagerInterface(const py::module& mod) {
       .def("settings", &ManagerInterface::settings, py::arg("hostSession").none(false))
       .def("initialize", &ManagerInterface::initialize, py::arg("managerSettings"),
            py::arg("hostSession").none(false))
+      .def("flushCaches", &ManagerInterface::flushCaches, py::arg("hostSession").none(false))
       .def("managementPolicy", &ManagerInterface::managementPolicy, py::arg("traitSets"),
            py::arg("context").none(false), py::arg("hostSession").none(false))
       .def("createState", &ManagerInterface::createState, py::arg("hostSession").none(false))
diff --git a/src/openassetio-python/package/openassetio/hostApi/Manager.py b/src/openassetio-python/package/openassetio/hostApi/Manager.py
index ffdf040fb..85c569fbb 100644
--- a/src/openassetio-python/package/openassetio/hostApi/Manager.py
+++ b/src/openassetio-python/package/openassetio/hostApi/Manager.py
@@ -135,26 +135,6 @@ def updateTerminology(self, stringDict):
 
     ## @}
 
-    ##
-    # @name Initialization
-    #
-    ## @{
-
-    @debugApiCall
-    @auditApiCall("Manager methods")
-    def flushCaches(self):
-        """
-        Clears any internal caches.  Only applicable if the manager
-        makes use of any caching, otherwise it is a no-op.  In caching
-        interfaces, this should cause any retained data to be discarded
-        to ensure future queries are fresh.
-
-        @unstable
-        """
-        return self.__impl.flushCaches(self.__hostSession)
-
-    ## @}
-
     ##
     # @name Entity Reference inspection
     #
diff --git a/src/openassetio-python/package/openassetio/managerApi/ManagerInterface.py b/src/openassetio-python/package/openassetio/managerApi/ManagerInterface.py
index 771f97028..202f30ac9 100644
--- a/src/openassetio-python/package/openassetio/managerApi/ManagerInterface.py
+++ b/src/openassetio-python/package/openassetio/managerApi/ManagerInterface.py
@@ -192,23 +192,6 @@ def updateTerminology(self, stringDict, hostSession):
 
     ## @}
 
-    ##
-    # @name Initialization
-    #
-    ## @{
-
-    def flushCaches(self, hostSession):
-        """
-        Clears any internal caches.  Only applicable if the
-        implementation makes use of any caching, otherwise it is a
-        no-op. In caching interfaces, this should cause any retained
-        data to be discarded to ensure future queries are fresh.
-
-        @unstable
-        """
-
-    ## @}
-
     ##
     # @name Entity Reference inspection
     #


Test patch:
diff --git a/src/openassetio-python/package/openassetio/test/manager/harness.py b/src/openassetio-python/package/openassetio/test/manager/harness.py
index 371de1a45..978d799c3 100644
--- a/src/openassetio-python/package/openassetio/test/manager/harness.py
+++ b/src/openassetio-python/package/openassetio/test/manager/harness.py
@@ -199,8 +199,13 @@ def __init__(self, fixtures, manager, locale, *args, **kwargs):
         self._fixtures = fixtures  # type: dict
         self._manager = manager  # type: hostApi.Manager
         self._locale = locale  # type: openassetio.Specification
+
         super(FixtureAugmentedTestCase, self).__init__(*args, **kwargs)
 
+        # Flush caches after every test, even if there was an error
+        # during setUp.
+        self.addCleanup(self._manager.flushCaches)
+
     def createTestContext(self, access=None):
         """
         A convenience method to create a context with the
diff --git a/src/openassetio-python/tests/package/hostApi/test_manager.py b/src/openassetio-python/tests/package/hostApi/test_manager.py
index 2a826a9f2..7b34250e1 100644
--- a/src/openassetio-python/tests/package/hostApi/test_manager.py
+++ b/src/openassetio-python/tests/package/hostApi/test_manager.py
@@ -203,15 +203,15 @@ def test_wraps_the_corresponding_method_of_the_held_interface(
 
 
 class Test_Manager_flushCaches:
-    def test_method_defined_in_python(self, method_introspector):
-        assert method_introspector.is_defined_in_python(Manager.flushCaches)
+    def test_method_defined_in_cpp(self, method_introspector):
+        assert not method_introspector.is_defined_in_python(Manager.flushCaches)
         assert method_introspector.is_implemented_once(Manager, "flushCaches")
 
     def test_wraps_the_corresponding_method_of_the_held_interface(
         self, manager, mock_manager_interface, a_host_session
     ):
         method = mock_manager_interface.mock.flushCaches
-        assert manager.flushCaches() == method.return_value
+        manager.flushCaches()
         method.assert_called_once_with(a_host_session)
 
 
diff --git a/src/openassetio-python/tests/package/managerApi/test_managerinterface.py b/src/openassetio-python/tests/package/managerApi/test_managerinterface.py
index 23ef6e76e..e69b7befa 100644
--- a/src/openassetio-python/tests/package/managerApi/test_managerinterface.py
+++ b/src/openassetio-python/tests/package/managerApi/test_managerinterface.py
@@ -71,6 +71,15 @@ def test_when_not_overridden_then_returns_empty_dict(self):
         assert info == {}
 
 
+class Test_ManagerInterface_flushCaches:
+    def test_method_defined_in_cpp(self, method_introspector):
+        assert not method_introspector.is_defined_in_python(ManagerInterface.flushCaches)
+        assert method_introspector.is_implemented_once(ManagerInterface, "flushCaches")
+
+    def test_default_implementation_exists(self, a_host_session):
+        ManagerInterface().flushCaches(a_host_session)
+
+
 class Test_ManagerInterface_createState:
     def test_method_defined_in_cpp(self, method_introspector):
         assert not method_introspector.is_defined_in_python(ManagerInterface.createState)