Skip to content

Commit

Permalink
[Py OV] Change approach to openvino.runtime lazy loading (#29053)
Browse files Browse the repository at this point in the history
### Details:
- In current approach for lazy import, we use
`importlib.util.LazyLoader`. This approach first creates a module, adds
it to python `sys.module`s, and then LazyLoader executes the module when
sb tries to access e.g. `openvino.runtime` attribute.

- The issue is that in this approach `openvino.runtime` might be
initialized too early, e.g. by [`inspect`
module](https://github.com/python/cpython/blob/140e69c4a878d7a0a26d4406bdad55e56ddae0b7/Lib/inspect.py#L915)
which iterates over modules present in `sys.modules`, e.g.:


![torch_issue_25_0](https://github.com/user-attachments/assets/19d9e2e5-858a-4917-b9a4-c1ff1ede203e)
 
 Proposal: 
- Change the lazy loading approach. Import `openvino.runtime` and add it
to `sys.modules` only when an attribute is accessed. Possible thanks to
overloading `__getattr__` and `__dir__` methods.

![torch_issue_25_1](https://github.com/user-attachments/assets/561121f5-2f22-4408-82c3-269dd31d2a79)



### Tickets:
 - *ticket-id*

---------

Signed-off-by: Alicja Miloszewska <alicja.miloszewska@intel.com>
  • Loading branch information
almilosz authored Feb 21, 2025
1 parent 980bdd5 commit b803639
Show file tree
Hide file tree
Showing 4 changed files with 30 additions and 19 deletions.
4 changes: 2 additions & 2 deletions src/bindings/python/src/openvino/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,8 @@
from openvino._ov_api import Op

# Import all public modules
from openvino.package_utils import lazy_import
runtime = lazy_import("openvino.runtime")
from openvino.package_utils import LazyLoader
runtime = LazyLoader("openvino.runtime")
from openvino import frontend as frontend
from openvino import helpers as helpers
from openvino import experimental as experimental
Expand Down
37 changes: 24 additions & 13 deletions src/bindings/python/src/openvino/package_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import os
import sys
from functools import wraps
from typing import Callable, Any
from typing import Callable, Any, Optional
from pathlib import Path
import importlib.util
from types import ModuleType
Expand Down Expand Up @@ -117,17 +117,28 @@ def __get__(self, obj: Any, cls: Any = None) -> Any:
return decorator


def lazy_import(module_name: str) -> ModuleType:
spec = importlib.util.find_spec(module_name)
if spec is None or spec.loader is None:
raise ImportError(f"Module {module_name} not found")
class LazyLoader:
"""A class to lazily load a module, importing it only when an attribute is accessed."""

loader = importlib.util.LazyLoader(spec.loader)
module = importlib.util.module_from_spec(spec)
sys.modules[module_name] = module
def __init__(self, module_name: str):
self.module_name = module_name
self._module: Optional[ModuleType] = None

try:
loader.exec_module(module)
except Exception as e:
raise ImportError(f"Failed to load module {module_name}") from e
return module
def _load_module(self) -> None:
if self._module is None:
# Import the module and update sys.modules with the loaded module
self._module = importlib.import_module(self.module_name)
# Update the LazyLoader instance's __dict__ with the module's __dict__
# This ensures that subsequent attribute accesses use the module's attributes directly (by __getattribute__() )
self.__dict__.update(self._module.__dict__)

def __getattr__(self, item: str) -> Any:
self._load_module()
return getattr(self._module, item)

def __dir__(self) -> list:
self._load_module()
return dir(self._module)

def __repr__(self) -> str:
return f"<LazyLoader for module '{self.module_name}'>"
4 changes: 2 additions & 2 deletions tools/benchmark_tool/openvino/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,8 @@
from openvino._ov_api import Op

# Import all public modules
from openvino.package_utils import lazy_import
runtime = lazy_import("openvino.runtime")
from openvino.package_utils import LazyLoader
runtime = LazyLoader("openvino.runtime")
from openvino import frontend as frontend
from openvino import helpers as helpers
from openvino import experimental as experimental
Expand Down
4 changes: 2 additions & 2 deletions tools/ovc/openvino/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,8 @@
from openvino._ov_api import Op

# Import all public modules
from openvino.package_utils import lazy_import
runtime = lazy_import("openvino.runtime")
from openvino.package_utils import LazyLoader
runtime = LazyLoader("openvino.runtime")
from openvino import frontend as frontend
from openvino import helpers as helpers
from openvino import experimental as experimental
Expand Down

0 comments on commit b803639

Please sign in to comment.