diff --git a/src/bindings/python/src/openvino/utils.py b/src/bindings/python/src/openvino/utils.py index 6d41c6bbc45786..438a636540b30a 100644 --- a/src/bindings/python/src/openvino/utils.py +++ b/src/bindings/python/src/openvino/utils.py @@ -4,6 +4,8 @@ import os import sys +from functools import wraps +from typing import Callable, Any def add_openvino_libs_to_path() -> None: @@ -28,3 +30,20 @@ def add_openvino_libs_to_path() -> None: # On Windows, with Python >= 3.8, DLLs are no longer imported from the PATH. if (3, 8) <= sys.version_info: os.add_dll_directory(os.path.abspath(lib_path)) + + +def deprecated(version: str = "", message: str = "") -> Callable[..., Any]: + """Prints deprecation warning "{function_name} is deprecated and will be removed in version {version}. {message}" and runs the function. + + :param version: The version in which the code will be removed. + :param message: A message explaining why the function is deprecated and/or what to use instead. + """ + def decorator(wrapped: Callable[..., Any]) -> Callable[..., Any]: + @wraps(wrapped) + def wrapper(*args: Any, **kwargs: Any) -> Callable[..., Any]: + # it must be imported here; otherwise, there are errors with no loaded DLL for Windows + from openvino.pyopenvino.util import deprecation_warning + deprecation_warning(wrapped.__name__, version, message) + return wrapped(*args, **kwargs) + return wrapper + return decorator diff --git a/src/bindings/python/src/pyopenvino/graph/passes/manager.cpp b/src/bindings/python/src/pyopenvino/graph/passes/manager.cpp index f4f167e405434d..8d1a8206f41c27 100644 --- a/src/bindings/python/src/pyopenvino/graph/passes/manager.cpp +++ b/src/bindings/python/src/pyopenvino/graph/passes/manager.cpp @@ -5,15 +5,14 @@ #include "openvino/pass/manager.hpp" #include -#include #include #include "openvino/pass/constant_folding.hpp" #include "openvino/pass/pass.hpp" #include "openvino/pass/serialize.hpp" -#include "openvino/pass/validate.hpp" #include "pyopenvino/graph/passes/manager.hpp" +#include "pyopenvino/utils/utils.hpp" namespace py = pybind11; @@ -69,10 +68,9 @@ void regclass_passes_Manager(py::module m) { manager.def( "register_pass", [](ov::pass::Manager& self, const std::string& pass_name) -> void { - PyErr_WarnEx(PyExc_DeprecationWarning, - "register_pass with this arguments is deprecated! " - "Please use register_pass(ConstantFolding()) instead.", - 1); + Common::utils::deprecation_warning("register_pass(pass_name)", + "", + "Please use register_pass(ConstantFolding()) instead."); if (pass_name == "ConstantFolding") { self.register_pass(); } @@ -93,10 +91,9 @@ void regclass_passes_Manager(py::module m) { const std::string& pass_name, const FilePaths& file_paths, const std::string& version) -> void { - PyErr_WarnEx(PyExc_DeprecationWarning, - "register_pass with this arguments is deprecated! " - "Please use register_pass(Serialize(xml, bin, version)) instead.", - 1); + Common::utils::deprecation_warning("register_pass(pass_name, output_files, version)", + "", + "Please use register_pass(Serialize(xml, bin, version)) instead."); if (pass_name == "Serialize") { self.register_pass(file_paths.first, file_paths.second, @@ -139,10 +136,9 @@ void regclass_passes_Manager(py::module m) { const std::string& xml_path, const std::string& bin_path, const std::string& version) -> void { - PyErr_WarnEx(PyExc_DeprecationWarning, - "register_pass with this arguments is deprecated! " - "Please use register_pass(Serialize(xml, bin, version)) instead.", - 1); + Common::utils::deprecation_warning("register_pass(pass_name, xml_path, bin_path, version", + "", + "Please use register_pass(Serialize(xml, bin, version)) instead."); if (pass_name == "Serialize") { self.register_pass(xml_path, bin_path, convert_to_version(version)); } diff --git a/src/bindings/python/src/pyopenvino/graph/util.cpp b/src/bindings/python/src/pyopenvino/graph/util.cpp index a4ace76c49be1e..db4bab8e364db2 100644 --- a/src/bindings/python/src/pyopenvino/graph/util.cpp +++ b/src/bindings/python/src/pyopenvino/graph/util.cpp @@ -9,6 +9,7 @@ #include "openvino/core/graph_util.hpp" #include "openvino/core/validation_util.hpp" #include "openvino/pass/manager.hpp" +#include "pyopenvino/utils/utils.hpp" namespace py = pybind11; @@ -70,4 +71,20 @@ void regmodule_graph_util(py::module m) { py::arg("target"), py::arg("replacement"), py::arg("outputs_order")); + + mod.def( + "deprecation_warning", + [](const std::string& function_name, const std::string& version, const std::string& message) { + Common::utils::deprecation_warning(function_name, version, message); + }, + py::arg("function_name"), + py::arg("version") = "", + py::arg("message") = "", + R"( + Prints deprecation warning "{function_name} is deprecated and will be removed in version {version}. {message}". + + :param function_name: The name of the deprecated function. + :param version: The version in which the code will be removed. + :param message: A message explaining why the function is deprecated. + )"); } diff --git a/src/bindings/python/src/pyopenvino/utils/utils.cpp b/src/bindings/python/src/pyopenvino/utils/utils.cpp index 40637c78b36eac..440292e002ab16 100644 --- a/src/bindings/python/src/pyopenvino/utils/utils.cpp +++ b/src/bindings/python/src/pyopenvino/utils/utils.cpp @@ -136,5 +136,17 @@ std::string convert_path_to_string(const py::object& path) { "Examples:\n(1) '/home/user/models/model.onnx'\n(2) Path('/home/user/models/model/model.onnx')"; throw ov::Exception(str.str()); } + +void deprecation_warning(const std::string& function_name, const std::string& version, const std::string& message) { + std::stringstream ss; + ss << function_name << " is deprecated"; + if (!version.empty()) { + ss << " and will be removed in version " << version; + } + if (!message.empty()) { + ss << ". " << message; + } + PyErr_WarnEx(PyExc_DeprecationWarning, ss.str().data(), 2); +} }; // namespace utils }; // namespace Common diff --git a/src/bindings/python/src/pyopenvino/utils/utils.hpp b/src/bindings/python/src/pyopenvino/utils/utils.hpp index 309fe0fed8c7dc..dc7034d311c391 100644 --- a/src/bindings/python/src/pyopenvino/utils/utils.hpp +++ b/src/bindings/python/src/pyopenvino/utils/utils.hpp @@ -18,6 +18,8 @@ namespace utils { std::map properties_to_any_map(const std::map& properties); std::string convert_path_to_string(const py::object& path); + + void deprecation_warning(const std::string& function_name, const std::string& version = std::string(), const std::string& message = std::string()); }; // namespace utils }; // namespace Common diff --git a/src/bindings/python/tests/test_graph/test_utils.py b/src/bindings/python/tests/test_graph/test_utils.py index a2ac620ad10e6b..549f90cdec2149 100644 --- a/src/bindings/python/tests/test_graph/test_utils.py +++ b/src/bindings/python/tests/test_graph/test_utils.py @@ -4,6 +4,8 @@ import numpy as np import openvino.runtime as ov +import pytest +from openvino.pyopenvino.util import deprecation_warning from openvino.runtime import Shape @@ -27,3 +29,14 @@ def test_get_constant_from_source_failed(): folded_const = ov.utils.get_constant_from_source(reshape.input(1).get_source_output()) assert folded_const is None + + +def test_deprecation_warning(): + with pytest.warns(DeprecationWarning, match="function1 is deprecated"): + deprecation_warning("function1") + with pytest.warns(DeprecationWarning, match="function2 is deprecated and will be removed in version 2025.4"): + deprecation_warning("function2", "2025.4") + with pytest.warns(DeprecationWarning, match="function3 is deprecated. Use another function instead"): + deprecation_warning("function3", message="Use another function instead") + with pytest.warns(DeprecationWarning, match="function4 is deprecated and will be removed in version 2025.4. Use another function instead"): + deprecation_warning("function4", version="2025.4", message="Use another function instead") diff --git a/src/bindings/python/tests/test_utils/test_utils.py b/src/bindings/python/tests/test_utils/test_utils.py index 4fb27a2d2efddc..ba5f9f995d0bc4 100644 --- a/src/bindings/python/tests/test_utils/test_utils.py +++ b/src/bindings/python/tests/test_utils/test_utils.py @@ -2,12 +2,15 @@ # Copyright (C) 2018-2022 Intel Corporation # SPDX-License-Identifier: Apache-2.0 +from typing import Tuple, Union, List + +import numpy as np import openvino +import openvino.runtime.opset8 as ops +import pytest from openvino.runtime import Model, Core, Shape, Type from openvino.runtime.op import Parameter -import openvino.runtime.opset8 as ops -from typing import Tuple, Union, List -import numpy as np +from openvino.utils import deprecated def get_test_model(): @@ -49,3 +52,30 @@ def generate_add_model() -> openvino.pyopenvino.Model: param2 = ops.parameter(Shape([2, 1]), dtype=np.float32, name="data2") add = ops.add(param1, param2) return Model(add, [param1, param2], "TestFunction") + + +def test_deprecation_decorator(): + @deprecated() + def deprecated_function1(param1, param2=None): + pass + + @deprecated(version="2025.4") + def deprecated_function2(param1=None): + pass + + @deprecated(message="Use another function instead") + def deprecated_function3(): + pass + + @deprecated(version="2025.4", message="Use another function instead") + def deprecated_function4(): + pass + + with pytest.warns(DeprecationWarning, match="deprecated_function1 is deprecated"): + deprecated_function1("param1") + with pytest.warns(DeprecationWarning, match="deprecated_function2 is deprecated and will be removed in version 2025.4"): + deprecated_function2(param1=1) + with pytest.warns(DeprecationWarning, match="deprecated_function3 is deprecated. Use another function instead"): + deprecated_function3() + with pytest.warns(DeprecationWarning, match="deprecated_function4 is deprecated and will be removed in version 2025.4. Use another function instead"): + deprecated_function4()