Skip to content

Commit

Permalink
[core] Check if model file exists on read model (#24864)
Browse files Browse the repository at this point in the history
### Details:
- Add more detailed error messages when model and/or weights files not
exist at path

### Tickets:
 - [CVS-142133](https://jira.devtools.intel.com/browse/CVS-142133)

---------

Co-authored-by: Ilya Lavrenov <ilya.lavrenov@intel.com>
Co-authored-by: Tomasz Jankowski <tomasz1.jankowski@intel.com>
Co-authored-by: Michal Lukaszewski <michal.lukaszewski@intel.com>
  • Loading branch information
4 people authored Jul 3, 2024
1 parent 4506142 commit 4282043
Show file tree
Hide file tree
Showing 25 changed files with 193 additions and 27 deletions.
22 changes: 22 additions & 0 deletions src/common/util/include/openvino/util/file_util.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,17 @@ struct FileTraits<wchar_t> {
}
};

/**
* @brief Convert path as char string to to a single-byte chain.
* @param path Path as char string.
* @return Reference to input path (no conversion).
*/
template <class Path,
typename std::enable_if<std::is_same<typename std::decay<Path>::type, std::string>::value>::type* = nullptr>
const std::string& path_to_string(const Path& path) {
return path;
}

#ifdef OPENVINO_ENABLE_UNICODE_PATH_SUPPORT
/**
* @brief Conversion from wide character string to a single-byte chain.
Expand All @@ -91,6 +102,17 @@ std::string wstring_to_string(const std::wstring& wstr);
*/
std::wstring string_to_wstring(const std::string& str);

/**
* @brief Convert path as wide character string to a single-byte chain.
* @param path Path as wide-char string.
* @return A char string
*/
template <class Path,
typename std::enable_if<std::is_same<typename std::decay<Path>::type, std::wstring>::value>::type* = nullptr>
std::string path_to_string(const Path& path) {
return ov::util::wstring_to_string(path);
}

#endif

/// \brief Remove path components which would allow traversing up a directory tree.
Expand Down
2 changes: 1 addition & 1 deletion src/core/tests/frontend/frontend_manager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ TEST(FrontEndManagerTest, testDefaultFrontEnd) {
FrontEndManager fem;
fem.register_front_end("mock1", mock_fe_path());
FrontEnd::Ptr fe;
ASSERT_NO_THROW(fe = fem.load_by_model(""));
ASSERT_NO_THROW(fe = fem.load_by_model());
ASSERT_EQ(nullptr, fe);

class MockFrontEnd : public FrontEnd {};
Expand Down
5 changes: 5 additions & 0 deletions src/frontends/common/include/openvino/frontend/frontend.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,11 @@ class FRONTEND_API FrontEnd {

virtual InputModel::Ptr load_impl(const std::vector<ov::Any>& variants) const;

void validate_path(const std::string& path) const;
#if defined(OPENVINO_ENABLE_UNICODE_PATH_SUPPORT) && defined(_WIN32)
void validate_path(const std::wstring& path) const;
#endif

std::vector<ov::Extension::Ptr> m_extensions;

private:
Expand Down
14 changes: 14 additions & 0 deletions src/frontends/common/src/frontend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -108,3 +108,17 @@ std::string FrontEnd::get_name() const {
}
FRONTEND_RETURN_STATEMENT("Getting frontend name", m_actual->get_name();)
}

void FrontEnd::validate_path(const std::string& path) const {
FRONT_END_GENERAL_CHECK(util::directory_exists(path) || util::file_exists(path),
get_name(),
": Could not open the file: \"",
path,
'"');
}

#if defined(OPENVINO_ENABLE_UNICODE_PATH_SUPPORT) && defined(_WIN32)
void FrontEnd::validate_path(const std::wstring& path) const {
validate_path(ov::util::wstring_to_string(path));
}
#endif
4 changes: 4 additions & 0 deletions src/frontends/ir/src/frontend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -74,10 +74,12 @@ bool FrontEnd::supported_impl(const std::vector<ov::Any>& variants) const {
const auto& model_variant = variants[0];
if (model_variant.is<std::string>()) {
const auto& path = model_variant.as<std::string>();
validate_path(path);
local_model_stream.open(path, std::ios::in | std::ifstream::binary);
#if defined(OPENVINO_ENABLE_UNICODE_PATH_SUPPORT) && defined(_WIN32)
} else if (model_variant.is<std::wstring>()) {
const auto& path = model_variant.as<std::wstring>();
validate_path(path);
local_model_stream.open(path.c_str(), std::ios::in | std::ifstream::binary);
#endif
} else if (model_variant.is<std::istream*>()) {
Expand Down Expand Up @@ -149,6 +151,7 @@ InputModel::Ptr FrontEnd::load_impl(const std::vector<ov::Any>& variants) const

if (model_variant.is<std::string>()) {
const auto& tmp_path = model_variant.as<std::string>();
validate_path(tmp_path);
#if defined(OPENVINO_ENABLE_UNICODE_PATH_SUPPORT) && defined(_WIN32)
model_path = ov::util::string_to_wstring(tmp_path.c_str());
#else
Expand All @@ -158,6 +161,7 @@ InputModel::Ptr FrontEnd::load_impl(const std::vector<ov::Any>& variants) const
#if defined(OPENVINO_ENABLE_UNICODE_PATH_SUPPORT) && defined(_WIN32)
} else if (model_variant.is<std::wstring>()) {
model_path = model_variant.as<std::wstring>();
validate_path(model_path);
local_model_stream.open(model_path.c_str(), std::ios::in | std::ifstream::binary);
#endif
} else if (model_variant.is<std::istream*>()) {
Expand Down
1 change: 1 addition & 0 deletions src/frontends/ir/tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ ov_add_test_target(
gtest_main
openvino::runtime::dev
common_test_utils
frontend_shared_test_classes
INCLUDES
"${CMAKE_CURRENT_SOURCE_DIR}/../include"
ADD_CLANG_FORMAT
Expand Down
37 changes: 37 additions & 0 deletions src/frontends/ir/tests/frontend_test_basic.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@
// SPDX-License-Identifier: Apache-2.0
//

#include "common_test_utils/test_assertions.hpp"
#include "frontend_test.hpp"
#include "openvino/opsets/opset1.hpp"
#include "openvino/opsets/opset3.hpp"
#include "openvino/opsets/opset6.hpp"
#include "utils.hpp"

class IRFrontendTests : public ::testing::Test, public IRFrontendTestsImpl {
protected:
Expand Down Expand Up @@ -1400,3 +1402,38 @@ TEST_F(IRFrontendTests, DetectionOutput) {
ASSERT_NO_THROW(model = getWithIRFrontend(testModel));
ASSERT_TRUE(!!model);
}

TEST_F(IRFrontendTests, load_model_not_exists_at_path) {
const auto model_name = "not_existing_model";
auto error_msg = std::string("Could not open the file: ");
auto model_file_path = FrontEndTestUtils::make_model_path(model_name);
error_msg += '"' + model_file_path + '"';

auto fem = ov::frontend::FrontEndManager();
auto fe = fem.load_by_framework("ir");

OV_EXPECT_THROW(fe->supported({model_file_path}), ov::Exception, testing::HasSubstr(error_msg));
OV_EXPECT_THROW(fe->load(model_file_path), ov::Exception, testing::HasSubstr(error_msg));
}

TEST_F(IRFrontendTests, load_model_weights_not_exist_at_path) {
const auto error_msg = std::string(" cannot be opened");
const auto name_prefix = ov::test::utils::generateTestFilePrefix();
const auto model_file_path = name_prefix + "existing_model.xml";
const auto weights_file_path = name_prefix + "not_existing_weights.bin";

{
std::ofstream model_file;
model_file.open(model_file_path);
model_file.close();
}

auto fem = ov::frontend::FrontEndManager();
auto fe = fem.load_by_framework("ir");

OV_EXPECT_THROW(fe->load(model_file_path, weights_file_path),
ov::Exception,
testing::HasSubstr(weights_file_path + error_msg));

std::remove(model_file_path.c_str());
}
2 changes: 2 additions & 0 deletions src/frontends/onnx/frontend/src/frontend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -192,11 +192,13 @@ bool FrontEnd::supported_impl(const std::vector<ov::Any>& variants) const {
std::ifstream model_stream;
if (variants[0].is<std::string>()) {
const auto path = variants[0].as<std::string>();
validate_path(path);
model_stream.open(path, std::ios::in | std::ifstream::binary);
}
#if defined(OPENVINO_ENABLE_UNICODE_PATH_SUPPORT) && defined(_WIN32)
else if (variants[0].is<std::wstring>()) {
const auto path = variants[0].as<std::wstring>();
validate_path(path);
model_stream.open(path.c_str(), std::ios::in | std::ifstream::binary);
}
#endif
Expand Down
4 changes: 2 additions & 2 deletions src/frontends/onnx/onnx_common/src/parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ ModelProto parse_from_file(const std::string& file_path) {
std::ifstream file_stream{file_path.c_str(), std::ios::in | std::ios::binary};

if (!file_stream.is_open()) {
OPENVINO_THROW("Could not open the file: " + file_path);
OPENVINO_THROW("Could not open the file: \"" + file_path, '"');
};

auto model_proto = parse_from_istream(file_stream);
Expand All @@ -34,7 +34,7 @@ ModelProto parse_from_file(const std::wstring& file_path) {
std::ifstream file_stream{file_path.c_str(), std::ios::in | std::ios::binary};

if (!file_stream.is_open()) {
OPENVINO_THROW("Could not open the file: " + ov::util::wstring_to_string(file_path));
OPENVINO_THROW("Could not open the file: \"", ov::util::wstring_to_string(file_path), '"');
};

auto model_proto = parse_from_istream(file_stream);
Expand Down
14 changes: 14 additions & 0 deletions src/frontends/onnx/tests/load_from.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

#include <fstream>

#include "common_test_utils/test_assertions.hpp"
#include "onnx_utils.hpp"
#include "utils.hpp"

Expand Down Expand Up @@ -43,6 +44,19 @@ TEST_P(FrontEndLoadFromTest, testLoadFromStreamAndPassPath) {
ASSERT_NE(function, nullptr);
}

TEST_P(FrontEndLoadFromTest, load_model_not_exists_at_path) {
const auto model_name = "not_existing_model";
auto error_msg = std::string("Could not open the file: ");
auto model_file_path = FrontEndTestUtils::make_model_path(model_name);
error_msg += '"' + model_file_path + '"';

auto fem = ov::frontend::FrontEndManager();
auto fe = fem.load_by_framework("onnx");

OV_EXPECT_THROW(fe->supported({model_file_path}), ov::Exception, testing::HasSubstr(error_msg));
OV_EXPECT_THROW(fe->load(model_file_path), ov::Exception, testing::HasSubstr(error_msg));
}

INSTANTIATE_TEST_SUITE_P(ONNXLoadTest,
FrontEndLoadFromTest,
::testing::Values(getTestData()),
Expand Down
11 changes: 9 additions & 2 deletions src/frontends/onnx/tests/tests_python/test_frontend_onnx.py
Original file line number Diff line number Diff line change
Expand Up @@ -318,8 +318,15 @@ def test_load_by_model():
decoded_function = fe.decode(model)
assert decoded_function

assert not fem.load_by_model("test.xx")
assert not fem.load_by_model("onnx.yy")
with pytest.raises(Exception) as e:
fem.load_by_model("test.xx")

assert e.match(r'Could not open the file: "test.xx"')

with pytest.raises(Exception) as e:
fem.load_by_model("onnx.yy")

assert e.match(r'Could not open the file: "onnx.yy"')


def test_onnx_conversion_extension_check_attributes():
Expand Down
5 changes: 5 additions & 0 deletions src/frontends/paddle/src/frontend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -378,6 +378,7 @@ bool FrontEnd::supported_impl(const std::vector<ov::Any>& variants) const {
if (variants[0].is<std::string>()) {
std::string suffix = ".pdmodel";
std::string model_path = variants[0].as<std::string>();
FRONT_END_GENERAL_CHECK(util::file_exists(model_path), "Could not open the file: \"", model_path, '"');
if (!ov::util::ends_with(model_path, suffix)) {
model_path += paddle::get_path_sep<char>() + "__model__";
}
Expand All @@ -390,6 +391,10 @@ bool FrontEnd::supported_impl(const std::vector<ov::Any>& variants) const {
else if (variants[0].is<std::wstring>()) {
std::wstring suffix = L".pdmodel";
std::wstring model_path = variants[0].as<std::wstring>();
FRONT_END_GENERAL_CHECK(util::file_exists(model_path),
"Could not open the file: \"",
util::path_to_string(model_path),
'"');
if (!ov::util::ends_with(model_path, suffix)) {
model_path += paddle::get_path_sep<wchar_t>() + L"__model__";
}
Expand Down
7 changes: 5 additions & 2 deletions src/frontends/paddle/src/input_model.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -399,7 +399,10 @@ InputModel::InputModelImpl::InputModelImpl(const std::basic_string<T>& path,
std::ifstream weights_stream;
std::ifstream pb_stream(get_model_path<T>(path, &weights_stream).c_str(), std::ios::in | std::ifstream::binary);

FRONT_END_GENERAL_CHECK(pb_stream && pb_stream.is_open(), "Model file doesn't exist");
FRONT_END_GENERAL_CHECK(pb_stream && pb_stream.is_open(),
"Could not open the file: \"",
util::path_to_string(path),
'"');
FRONT_END_GENERAL_CHECK(m_fw_ptr->ParseFromIstream(&pb_stream), "Model can't be parsed");
// According to Paddle, the saved model has the framework version
// For example Paddle 2.1.0 is encoded as 2001000. 0 means the latest framework.
Expand Down Expand Up @@ -650,4 +653,4 @@ void InputModel::set_tensor_value(const Place::Ptr& place, const void* value) {

} // namespace paddle
} // namespace frontend
} // namespace ov
} // namespace ov
5 changes: 2 additions & 3 deletions src/frontends/tensorflow/src/frontend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -282,9 +282,8 @@ ov::frontend::InputModel::Ptr FrontEnd::load_impl(const std::vector<ov::Any>& va
HashTableKeysValuesMap{},
graph_iterator->get_checkpoint_v1_reader(),
false);
}
auto saved_model_tags = paths[1];
if (GraphIteratorSavedModel::is_supported(model_path)) {
} else if (GraphIteratorSavedModel::is_supported(model_path)) {
auto saved_model_tags = paths[1];
std::shared_ptr<GraphIteratorSavedModel> graph_iterator;
graph_iterator = std::make_shared<GraphIteratorSavedModel>(model_path, saved_model_tags, mmap_enabled);
return std::make_shared<InputModel>(graph_iterator,
Expand Down
4 changes: 4 additions & 0 deletions src/frontends/tensorflow/src/graph_iterator_meta.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,10 @@ class GraphIteratorMeta : public GraphIteratorProto {

template <typename T>
static bool is_supported(const std::basic_string<T>& path) {
FRONT_END_GENERAL_CHECK(util::directory_exists(path) || util::file_exists(path),
"Could not open the file: \"",
util::path_to_string(path),
'"');
try {
std::ifstream mg_stream(path.c_str(), std::ios::in | std::ifstream::binary);
auto metagraph_def = std::make_shared<::tensorflow::MetaGraphDef>();
Expand Down
5 changes: 5 additions & 0 deletions src/frontends/tensorflow/src/graph_iterator_proto.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include "openvino/frontend/exception.hpp"
#include "openvino/frontend/graph_iterator.hpp"
#include "openvino/frontend/tensorflow/decoder.hpp"
#include "openvino/util/file_util.hpp"
#include "ov_tensorflow/graph.pb.h"

namespace ov {
Expand Down Expand Up @@ -141,6 +142,10 @@ class GraphIteratorProto : public GraphIterator {
/// \brief Check if the input file is supported
template <typename T>
static bool is_supported(const std::basic_string<T>& path) {
FRONT_END_GENERAL_CHECK(util::directory_exists(path) || util::file_exists(path),
"Could not open the file: \"",
util::path_to_string(path),
'"');
try {
#if defined(__MINGW32__) || defined(__MINGW64__)
std::ifstream pb_stream(std::filesystem::path(path), std::ios::in | std::ifstream::binary);
Expand Down
5 changes: 5 additions & 0 deletions src/frontends/tensorflow/src/graph_iterator_proto_txt.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include "google/protobuf/text_format.h"
#include "graph_iterator_proto.hpp"
#include "openvino/frontend/exception.hpp"
#include "openvino/util/file_util.hpp"

namespace ov {
namespace frontend {
Expand Down Expand Up @@ -59,6 +60,10 @@ class GraphIteratorProtoTxt : public GraphIteratorProto {
/// \brief Check if the input file is supported
template <typename T>
static bool is_supported(const std::basic_string<T>& path) {
FRONT_END_GENERAL_CHECK(util::directory_exists(path) || util::file_exists(path),
"Could not open the file: \"",
util::path_to_string(path),
'"');
try {
std::ifstream pbtxt_stream(path.c_str(), std::ios::in);
bool model_exists = (pbtxt_stream && pbtxt_stream.is_open());
Expand Down
10 changes: 9 additions & 1 deletion src/frontends/tensorflow/src/graph_iterator_saved_model.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,15 @@ bool GraphIteratorSavedModel::is_valid_signature(const ::tensorflow::SignatureDe
}

bool GraphIteratorSavedModel::is_supported(const std::string& path) {
return ov::util::directory_exists(path) && ov::util::file_exists(ov::util::path_join({path, "saved_model.pb"}));
if (ov::util::directory_exists(path)) {
FRONT_END_GENERAL_CHECK(util::file_exists(ov::util::path_join({path, "saved_model.pb"})),
"Could not open the file: \"",
util::path_to_string(ov::util::path_join({path, "saved_model.pb"})),
'"');
return true;
} else {
return false;
}
}

#if defined(OPENVINO_ENABLE_UNICODE_PATH_SUPPORT) && defined(_WIN32)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ template <typename T>
std::basic_string<T> get_model_extension() {}
template <>
std::basic_string<char> get_model_extension<char>();

#if defined(OPENVINO_ENABLE_UNICODE_PATH_SUPPORT) && defined(_WIN32)
template <>
std::basic_string<wchar_t> get_model_extension<wchar_t>();
Expand Down Expand Up @@ -58,6 +59,10 @@ class GraphIteratorFlatBuffer : public GraphIterator {
/// Verifies file is supported
template <typename T>
static bool is_supported(const std::basic_string<T>& path) {
FRONT_END_GENERAL_CHECK(util::file_exists(path),
"Could not open the file: \"",
util::path_to_string(path),
'"');
try {
if (!ov::util::ends_with<T>(path, get_model_extension<T>())) {
return false;
Expand Down
Loading

0 comments on commit 4282043

Please sign in to comment.