From f499242625a4b00a20ef0e9c65398b431caa3da0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Achard?= Date: Mon, 10 Mar 2025 16:35:51 +0100 Subject: [PATCH 1/2] Add missing setConfigIOProxy call to the Python API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémi Achard --- src/bindings/python/PyConfig.cpp | 4 ++ tests/cpu/Config_tests.cpp | 100 +++++++++++++++++++++++++++++++ tests/python/ConfigTest.py | 28 ++++++++- 3 files changed, 131 insertions(+), 1 deletion(-) diff --git a/src/bindings/python/PyConfig.cpp b/src/bindings/python/PyConfig.cpp index 675f7155ca..9534ddc358 100644 --- a/src/bindings/python/PyConfig.cpp +++ b/src/bindings/python/PyConfig.cpp @@ -284,6 +284,10 @@ void bindPyConfig(py::module & m) DOC(Config, getWorkingDir)) .def("setWorkingDir", &Config::setWorkingDir, "dirName"_a, DOC(Config, setWorkingDir)) + .def("getConfigIOProxy", &Config::getConfigIOProxy, + DOC(Config, getConfigIOProxy)) + .def("setConfigIOProxy", &Config::setConfigIOProxy, "ciop"_a, + DOC(Config, setConfigIOProxy)) // ColorSpaces .def("getColorSpaces", &Config::getColorSpaces, "category"_a, diff --git a/tests/cpu/Config_tests.cpp b/tests/cpu/Config_tests.cpp index 40ac3742be..1d95beba8f 100644 --- a/tests/cpu/Config_tests.cpp +++ b/tests/cpu/Config_tests.cpp @@ -9799,3 +9799,103 @@ OCIO_ADD_TEST(Config, create_from_config_io_proxy) OCIO_CHECK_NO_THROW(proc->getDefaultCPUProcessor()); } } + +OCIO_ADD_TEST(Config, set_config_io_proxy) +{ + std::vector paths = { + std::string(OCIO::GetTestFilesDir()), + std::string("configs"), + std::string("context_test1"), + std::string("config.ocio"), + }; + static const std::string configPath = pystring::os::path::normpath( + pystring::os::path::join(paths) + ); + + { + // Dummy ConfigIOProxy test class. Replace all the LUTs by Exponent + // transform and raises an exception from getConfigData as it shouldn't + // be called in the context of this test. + class CIOPTest : public OCIO::ConfigIOProxy + { + public: + inline std::string getConfigData() const override + { + throw OCIO::Exception( + "getConfigData() should not be called when using setConfigIOProxy()"); + } + + inline std::vector getLutData( + const char * /* filepath */) const override + { + // For the purpose of this simple test, blindly replace any transform + // by an exponent which we can easily detect in the test below. + const std::string new_lut = R"( + + + + +)"; + + return std::vector(new_lut.begin(), new_lut.end()); + } + + inline std::string getFastLutFileHash(const char * filename) const override + { + // We don't care about the original file existence for the purpose of this test, + // a typical implementation may check that the requested filename is expected and + // generate a proper hash not only based on the filename. + return filename; + } + }; + + std::shared_ptr ciop = std::shared_ptr( + new CIOPTest() + ); + + OCIO::ConstConfigRcPtr config; + OCIO_CHECK_NO_THROW(config = OCIO::Config::CreateFromFile(configPath.c_str())); + OCIO_REQUIRE_ASSERT(config); + OCIO_CHECK_NO_THROW(config->validate()); + + // Simple check on the number of color spaces in the test config. + OCIO_CHECK_EQUAL(config->getNumColorSpaces(), 13); + + + // Check the config behaviour before patching with IOProxy. + { + OCIO::ConstProcessorRcPtr proc; + OCIO_CHECK_NO_THROW(proc = config->getProcessor("plain_lut1_cs", "shot1_lut1_cs")); + OCIO_REQUIRE_ASSERT(proc); + OCIO_CHECK_NO_THROW(proc->getDefaultCPUProcessor()); + OCIO_CHECK_ASSERT(!proc->isNoOp()); + + auto group = proc->createGroupTransform(); + OCIO_REQUIRE_EQUAL(group->getNumTransforms(), 2); + OCIO_REQUIRE_EQUAL(group->getTransform(0)->getTransformType(), OCIO::TRANSFORM_TYPE_MATRIX); + OCIO_REQUIRE_EQUAL(group->getTransform(1)->getTransformType(), OCIO::TRANSFORM_TYPE_MATRIX); + } + + // Required to clear the file cache and force OCIO to call the IOProxy methods. + OCIO::ClearAllCaches(); + + // Check the config behaviour after patching with IOProxy, any FileTransform + // gets replaced by an ExponentTransform. + { + OCIO::ConfigRcPtr configProxy; + OCIO_CHECK_NO_THROW(configProxy = config->createEditableCopy()); + OCIO_CHECK_NO_THROW(configProxy->setConfigIOProxy(ciop)); + + OCIO::ConstProcessorRcPtr proc; + OCIO_CHECK_NO_THROW(proc = configProxy->getProcessor("plain_lut1_cs", "shot1_lut1_cs")); + OCIO_REQUIRE_ASSERT(proc); + OCIO_CHECK_NO_THROW(proc->getDefaultCPUProcessor()); + OCIO_CHECK_ASSERT(!proc->isNoOp()); + + auto group = proc->createGroupTransform(); + OCIO_REQUIRE_EQUAL(group->getNumTransforms(), 2); + OCIO_REQUIRE_EQUAL(group->getTransform(0)->getTransformType(), OCIO::TRANSFORM_TYPE_EXPONENT); + OCIO_REQUIRE_EQUAL(group->getTransform(1)->getTransformType(), OCIO::TRANSFORM_TYPE_EXPONENT); + } + } +} diff --git a/tests/python/ConfigTest.py b/tests/python/ConfigTest.py index c6ca03c1ed..32e1cce9dc 100644 --- a/tests/python/ConfigTest.py +++ b/tests/python/ConfigTest.py @@ -1083,7 +1083,7 @@ def test_create_from_archive(self): with self.assertRaises(OCIO.Exception): config.getProcessor("plain_lut1_cs", "shot1_lut1_cs") - def test_create_from_config_io_proxy(self): + def test_config_io_proxy(self): # Simulate that the config and LUT are in memory by initializing three variables # simple_config is the config @@ -1179,6 +1179,7 @@ def lutExists(filepath): hash = filepath return hash + # First, create the config directly from IOProxy. ciop = CIOPTest() config = OCIO.Config.CreateFromConfigIOProxy(ciop) config.validate() @@ -1190,6 +1191,31 @@ def lutExists(filepath): processor = config.getProcessor("c1", "c2") processor.getDefaultCPUProcessor() + # Clear the file cache to force OCIO to look for LUTs. + OCIO.ClearAllCaches() + + # Second, create the config the stream. + config = OCIO.Config.CreateFromStream(SIMPLE_CONFIG) + config.validate() + + # We have not assigned the IOProxy yet, this should fail because + # there is no my_unique_luts folder with the required files. + with self.assertRaises(OCIO.ExceptionMissingFile): + processor = config.getProcessor("c1", "c2") + processor.getDefaultCPUProcessor() + + # Clear the file cache again to force OCIO to look for LUTs. This is + # required because the above failed attempt will fill the cache. + OCIO.ClearAllCaches() + + # The ConfigIOProxy object is now assigned. + config.setConfigIOProxy(ciop) + self.assertEqual(config.getConfigIOProxy(), ciop) + + # Simple test to exercise ConfigIOProxy. + processor = config.getProcessor("c1", "c2") + processor.getDefaultCPUProcessor() + def test_resolve_config(self): defaultBuiltinConfig = "ocio://cg-config-v2.2.0_aces-v1.3_ocio-v2.4" cgLatestBuiltinConfig = "ocio://cg-config-v2.2.0_aces-v1.3_ocio-v2.4" From 602c91d13726ad8a48fedbfcaed8df9400b1bd9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Achard?= Date: Mon, 10 Mar 2025 17:03:27 +0100 Subject: [PATCH 2/2] Restore a clean cache for other unit tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémi Achard --- tests/cpu/Config_tests.cpp | 3 +++ tests/python/ConfigTest.py | 3 +++ 2 files changed, 6 insertions(+) diff --git a/tests/cpu/Config_tests.cpp b/tests/cpu/Config_tests.cpp index 1d95beba8f..518b97275b 100644 --- a/tests/cpu/Config_tests.cpp +++ b/tests/cpu/Config_tests.cpp @@ -9897,5 +9897,8 @@ OCIO_ADD_TEST(Config, set_config_io_proxy) OCIO_REQUIRE_EQUAL(group->getTransform(0)->getTransformType(), OCIO::TRANSFORM_TYPE_EXPONENT); OCIO_REQUIRE_EQUAL(group->getTransform(1)->getTransformType(), OCIO::TRANSFORM_TYPE_EXPONENT); } + + // Clear cache for following unit tests. + OCIO::ClearAllCaches(); } } diff --git a/tests/python/ConfigTest.py b/tests/python/ConfigTest.py index 32e1cce9dc..5130ebbf88 100644 --- a/tests/python/ConfigTest.py +++ b/tests/python/ConfigTest.py @@ -1216,6 +1216,9 @@ def lutExists(filepath): processor = config.getProcessor("c1", "c2") processor.getDefaultCPUProcessor() + # Clear cache for following unit tests. + OCIO.ClearAllCaches() + def test_resolve_config(self): defaultBuiltinConfig = "ocio://cg-config-v2.2.0_aces-v1.3_ocio-v2.4" cgLatestBuiltinConfig = "ocio://cg-config-v2.2.0_aces-v1.3_ocio-v2.4"