From d0fe0a7cf6cf9299527c44ee66ed15e90e20cf58 Mon Sep 17 00:00:00 2001 From: Mei Chu Date: Wed, 13 May 2026 00:01:00 -0700 Subject: [PATCH 1/5] Add to PathUtils Signed-off-by: Mei Chu --- src/OpenColorIO/Config.cpp | 15 +++++++++++---- src/OpenColorIO/PathUtils.cpp | 30 ++++++++++++++++++++++++++++++ src/OpenColorIO/PathUtils.h | 3 +++ 3 files changed, 44 insertions(+), 4 deletions(-) diff --git a/src/OpenColorIO/Config.cpp b/src/OpenColorIO/Config.cpp index 1b5a8c70db..0d1bbcf6f3 100644 --- a/src/OpenColorIO/Config.cpp +++ b/src/OpenColorIO/Config.cpp @@ -1155,7 +1155,7 @@ ConstConfigRcPtr Config::CreateFromFile(const char * filename) { if (!filename || !*filename) { - throw ExceptionMissingFile ("The config filepath is missing."); + throw ExceptionMissingFile("The config filepath is missing."); } // Check for URI Pattern: ocio:// @@ -1167,6 +1167,13 @@ ConstConfigRcPtr Config::CreateFromFile(const char * filename) return CreateFromBuiltinConfig(uri.c_str()); } + if (!FileExists(filename)) + { + std::ostringstream oss; + oss << filename << " does not exists."; + throw ExceptionMissingFile(oss.str().c_str()); + } + std::ifstream ifstream = Platform::CreateInputFileStream( filename, std::ios_base::in | std::ios_base::binary @@ -6012,7 +6019,7 @@ bool Config::isArchivable() const // Current archive implementation needs a working directory to look for LUT files and // working directory must be an absolute path. const char * workingDirectory = getWorkingDir(); - if ((workingDirectory && !workingDirectory[0]) || !pystring::os::path::isabs(workingDirectory)) + if ((workingDirectory && !workingDirectory[0]) || !IsPathAbs(workingDirectory)) { return false; } @@ -6021,10 +6028,10 @@ bool Config::isArchivable() const auto validatePathForArchiving = [](const std::string & path) { // Using the normalized path. - const std::string normPath = pystring::os::path::normpath(path); + const std::string normPath = NormalizePath(path, false); if ( // 1) Path may not be absolute. - pystring::os::path::isabs(normPath) || + IsPathAbs(normPath) || // 2) Path may not start with double dot ".." (going above working directory). pystring::startswith(normPath, "..") || // 3) A context variable may not be located at the start of the path. diff --git a/src/OpenColorIO/PathUtils.cpp b/src/OpenColorIO/PathUtils.cpp index 3a68d366b4..43fc2b7b3c 100644 --- a/src/OpenColorIO/PathUtils.cpp +++ b/src/OpenColorIO/PathUtils.cpp @@ -4,6 +4,7 @@ #include #include +#include #include @@ -120,6 +121,11 @@ bool FileExists(const std::string & filename, const Context & context) return (!hash.empty()); } +bool FileExists(const std::string & filename) +{ + return std::filesystem::exists(filename); +} + void ClearPathCaches() { AutoMutex lock(g_fastFileHashCache_mutex); @@ -157,6 +163,30 @@ std::string AbsPath(const std::string & path) return pystring::os::path::normpath(p); } +bool IsPathAbs(const std::string & path) +{ + std::string p = path; + return std::filesystem::path(p).is_absolute(); +} + +std::string NormalizePath(const std::string & path, bool canonical) +{ + std::filesystem::path p; + // Fall back to lexically_normal() if path does not exists + if (canonical && FileExists(path)) + { + p = std::filesystem::canonical(path); + } + else + { + p = std::filesystem::path(path).lexically_normal(); + } + if (p.has_relative_path() && !p.has_filename()) { + p = p.parent_path(); + } + return p.generic_string(); +} + namespace { void AdjustRightmost(const std::string & name, int index, int & colorspacePos, diff --git a/src/OpenColorIO/PathUtils.h b/src/OpenColorIO/PathUtils.h index 0f1554d34f..655dcab303 100644 --- a/src/OpenColorIO/PathUtils.h +++ b/src/OpenColorIO/PathUtils.h @@ -16,9 +16,12 @@ namespace OCIO_NAMESPACE // So let's define it locally for now std::string AbsPath(const std::string & path); +bool IsPathAbs(const std::string & path); +std::string NormalizePath(const std::string & path, bool canonical); // Check if a file exists bool FileExists(const std::string & filename, const Context & context); +bool FileExists(const std::string & filename); // Get a fast hash for a file, without reading all the contents. // Currently, this checks the mtime and the inode number. From 1d354377a301b4a6906c8a9d9aa015ecc1475b9a Mon Sep 17 00:00:00 2001 From: Mei Chu Date: Wed, 13 May 2026 00:25:00 -0700 Subject: [PATCH 2/5] Python test switch to ExceptionMissingFile Signed-off-by: Mei Chu --- tests/python/ConfigTest.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/python/ConfigTest.py b/tests/python/ConfigTest.py index 9debc3da2e..4834f06ac8 100644 --- a/tests/python/ConfigTest.py +++ b/tests/python/ConfigTest.py @@ -1011,7 +1011,7 @@ def testFromEnvAndFromFile(uri, numberOfExpectedColorspaces, expectedConfigName) nbOfColorspacesForStudioConfig = 55 # Test that CreateFromFile does not work without ocio:// prefix for built-in config. - with self.assertRaises(OCIO.Exception) as cm: + with self.assertRaises(OCIO.ExceptionMissingFile) as cm: OCIO.Config.CreateFromFile(cgConfigName) # Test CG config. From 57d80305dd6759eeff2a260c88bf8d3c314f68c4 Mon Sep 17 00:00:00 2001 From: Mei Chu Date: Wed, 13 May 2026 00:39:18 -0700 Subject: [PATCH 3/5] Updated tests to use ExceptionMissingFile instead Signed-off-by: Mei Chu --- src/OpenColorIO/Config.cpp | 2 +- tests/cpu/builtinconfigs/BuiltinConfig_tests.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/OpenColorIO/Config.cpp b/src/OpenColorIO/Config.cpp index 0d1bbcf6f3..7b011e48b6 100644 --- a/src/OpenColorIO/Config.cpp +++ b/src/OpenColorIO/Config.cpp @@ -1170,7 +1170,7 @@ ConstConfigRcPtr Config::CreateFromFile(const char * filename) if (!FileExists(filename)) { std::ostringstream oss; - oss << filename << " does not exists."; + oss << "'" << filename << "' does not exist."; throw ExceptionMissingFile(oss.str().c_str()); } diff --git a/tests/cpu/builtinconfigs/BuiltinConfig_tests.cpp b/tests/cpu/builtinconfigs/BuiltinConfig_tests.cpp index c58c3edd24..86a7adfc98 100644 --- a/tests/cpu/builtinconfigs/BuiltinConfig_tests.cpp +++ b/tests/cpu/builtinconfigs/BuiltinConfig_tests.cpp @@ -446,8 +446,8 @@ OCIO_ADD_TEST(BuiltinConfigs, create_builtin_config) // Test that CreateFromFile does not work without ocio:// prefix for built-in config. OCIO_CHECK_THROW_WHAT( OCIO::Config::CreateFromFile("cg-config-v1.0.0_aces-v1.3_ocio-v2.1"), - OCIO::Exception, - "Error could not read 'cg-config-v1.0.0_aces-v1.3_ocio-v2.1' OCIO profile." + OCIO::ExceptionMissingFile, + "'cg-config-v1.0.0_aces-v1.3_ocio-v2.1' does not exist." ); { From 008214a90d77248049d0467ff94095b650ca6f95 Mon Sep 17 00:00:00 2001 From: Mei Chu Date: Thu, 14 May 2026 15:28:19 -0700 Subject: [PATCH 4/5] Revert PathUtil related changes. Signed-off-by: Mei Chu --- src/OpenColorIO/Config.cpp | 11 +++---- src/OpenColorIO/PathUtils.cpp | 29 ------------------- src/OpenColorIO/PathUtils.h | 3 -- .../builtinconfigs/BuiltinConfig_tests.cpp | 2 +- 4 files changed, 7 insertions(+), 38 deletions(-) diff --git a/src/OpenColorIO/Config.cpp b/src/OpenColorIO/Config.cpp index 7b011e48b6..89f59eda5d 100644 --- a/src/OpenColorIO/Config.cpp +++ b/src/OpenColorIO/Config.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include @@ -1167,10 +1168,10 @@ ConstConfigRcPtr Config::CreateFromFile(const char * filename) return CreateFromBuiltinConfig(uri.c_str()); } - if (!FileExists(filename)) + if (!std::filesystem::exists(filename)) { std::ostringstream oss; - oss << "'" << filename << "' does not exist."; + oss << "'" << filename << "' file does not exist."; throw ExceptionMissingFile(oss.str().c_str()); } @@ -6019,7 +6020,7 @@ bool Config::isArchivable() const // Current archive implementation needs a working directory to look for LUT files and // working directory must be an absolute path. const char * workingDirectory = getWorkingDir(); - if ((workingDirectory && !workingDirectory[0]) || !IsPathAbs(workingDirectory)) + if ((workingDirectory && !workingDirectory[0]) || !pystring::os::path::isabs(workingDirectory)) { return false; } @@ -6028,10 +6029,10 @@ bool Config::isArchivable() const auto validatePathForArchiving = [](const std::string & path) { // Using the normalized path. - const std::string normPath = NormalizePath(path, false); + const std::string normPath = pystring::os::path::normpath(path); if ( // 1) Path may not be absolute. - IsPathAbs(normPath) || + pystring::os::path::isabs(normPath) || // 2) Path may not start with double dot ".." (going above working directory). pystring::startswith(normPath, "..") || // 3) A context variable may not be located at the start of the path. diff --git a/src/OpenColorIO/PathUtils.cpp b/src/OpenColorIO/PathUtils.cpp index 43fc2b7b3c..2dd7e1434d 100644 --- a/src/OpenColorIO/PathUtils.cpp +++ b/src/OpenColorIO/PathUtils.cpp @@ -121,11 +121,6 @@ bool FileExists(const std::string & filename, const Context & context) return (!hash.empty()); } -bool FileExists(const std::string & filename) -{ - return std::filesystem::exists(filename); -} - void ClearPathCaches() { AutoMutex lock(g_fastFileHashCache_mutex); @@ -163,30 +158,6 @@ std::string AbsPath(const std::string & path) return pystring::os::path::normpath(p); } -bool IsPathAbs(const std::string & path) -{ - std::string p = path; - return std::filesystem::path(p).is_absolute(); -} - -std::string NormalizePath(const std::string & path, bool canonical) -{ - std::filesystem::path p; - // Fall back to lexically_normal() if path does not exists - if (canonical && FileExists(path)) - { - p = std::filesystem::canonical(path); - } - else - { - p = std::filesystem::path(path).lexically_normal(); - } - if (p.has_relative_path() && !p.has_filename()) { - p = p.parent_path(); - } - return p.generic_string(); -} - namespace { void AdjustRightmost(const std::string & name, int index, int & colorspacePos, diff --git a/src/OpenColorIO/PathUtils.h b/src/OpenColorIO/PathUtils.h index 655dcab303..0f1554d34f 100644 --- a/src/OpenColorIO/PathUtils.h +++ b/src/OpenColorIO/PathUtils.h @@ -16,12 +16,9 @@ namespace OCIO_NAMESPACE // So let's define it locally for now std::string AbsPath(const std::string & path); -bool IsPathAbs(const std::string & path); -std::string NormalizePath(const std::string & path, bool canonical); // Check if a file exists bool FileExists(const std::string & filename, const Context & context); -bool FileExists(const std::string & filename); // Get a fast hash for a file, without reading all the contents. // Currently, this checks the mtime and the inode number. diff --git a/tests/cpu/builtinconfigs/BuiltinConfig_tests.cpp b/tests/cpu/builtinconfigs/BuiltinConfig_tests.cpp index 86a7adfc98..74a6298efa 100644 --- a/tests/cpu/builtinconfigs/BuiltinConfig_tests.cpp +++ b/tests/cpu/builtinconfigs/BuiltinConfig_tests.cpp @@ -447,7 +447,7 @@ OCIO_ADD_TEST(BuiltinConfigs, create_builtin_config) OCIO_CHECK_THROW_WHAT( OCIO::Config::CreateFromFile("cg-config-v1.0.0_aces-v1.3_ocio-v2.1"), OCIO::ExceptionMissingFile, - "'cg-config-v1.0.0_aces-v1.3_ocio-v2.1' does not exist." + "'cg-config-v1.0.0_aces-v1.3_ocio-v2.1' file does not exist." ); { From b4fdc76d57ece58e7787c1510315cdb7a7a0f32b Mon Sep 17 00:00:00 2001 From: Mei Chu Date: Thu, 14 May 2026 15:30:00 -0700 Subject: [PATCH 5/5] Remove PathUtils changes Signed-off-by: Mei Chu --- src/OpenColorIO/PathUtils.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/OpenColorIO/PathUtils.cpp b/src/OpenColorIO/PathUtils.cpp index 2dd7e1434d..3a68d366b4 100644 --- a/src/OpenColorIO/PathUtils.cpp +++ b/src/OpenColorIO/PathUtils.cpp @@ -4,7 +4,6 @@ #include #include -#include #include