Coverage for physioblocks / utils / dynamic_import_utils.py: 87%
46 statements
« prev ^ index » next coverage.py v7.13.1, created at 2026-01-09 16:40 +0100
« prev ^ index » next coverage.py v7.13.1, created at 2026-01-09 16:40 +0100
1# SPDX-FileCopyrightText: Copyright INRIA
2#
3# SPDX-License-Identifier: LGPL-3.0-only
4#
5# Copyright INRIA
6#
7# This file is part of PhysioBlocks, a library mostly developed by the
8# [Ananke project-team](https://team.inria.fr/ananke) at INRIA.
9#
10# Authors:
11# - Colin Drieu
12# - Dominique Chapelle
13# - François Kimmig
14# - Philippe Moireau
15#
16# PhysioBlocks is free software: you can redistribute it and/or modify it under the
17# terms of the GNU Lesser General Public License as published by the Free Software
18# Foundation, version 3 of the License.
19#
20# PhysioBlocks is distributed in the hope that it will be useful, but WITHOUT ANY
21# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
22# PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
23#
24# You should have received a copy of the GNU Lesser General Public License along with
25# PhysioBlocks. If not, see <https://www.gnu.org/licenses/>.
27"""
28Defines methods to dynamically import modules while in a script or python
29application.
30"""
32import importlib
33import logging
34import pkgutil
35import sys
36from pathlib import Path
38from physioblocks.utils.exceptions_utils import log_exception
40_logger = logging.getLogger(__name__)
43def import_libraries(libraries_folder_paths: list[Path]) -> None:
44 """
45 Dynamically import all the modules at the given paths.
47 .. note:: The libraries folders must be in a site to be able to
48 load theirs modules.
50 :param libraries_folder_path: the paths
51 :type libraries_folder_path: list[Path]
53 Example
54 ^^^^^^^
56 .. code:: python
58 # Add site for the library to load
59 site.addsitedir(ABSOLUTE_PATH_TO_LIBRARY)
61 lib_path = Path(ABSOLUTE_PATH_TO_LIBRARY)
62 import_libraries([lib_path]) # dynamically import the library
64 """
65 packages_absolute_paths: list[tuple[Path, str]] = []
67 for library_path in libraries_folder_paths:
68 if library_path.exists() is True:
69 absolute_path = library_path.absolute()
70 full_package_name = _get_full_package_name(absolute_path)
71 packages_absolute_paths.append((absolute_path, full_package_name))
72 else:
73 _logger.error(
74 str.format(
75 "There is no library folder at path {0}. Path skipped.",
76 str(library_path),
77 )
78 )
80 for package_path, full_package_name in packages_absolute_paths:
81 _import_modules_recursivly_at_path(package_path, full_package_name)
84def _get_full_package_name(package_path: Path) -> str:
85 if _is_package(package_path) is False:
86 raise ImportError(str.format("{0} is not a package", package_path.name))
88 full_package_name = package_path.name
89 parent = package_path.parent
90 check_parent = True
91 while check_parent is True:
92 check_parent = False
93 if _is_package(parent) is True:
94 full_package_name = ".".join([parent.name, full_package_name])
95 parent = parent.parent
96 check_parent = True
98 return full_package_name
101def _is_package(dir_path: Path) -> bool:
102 return (
103 any(
104 path.is_file() and path.name == "__init__.py" for path in dir_path.iterdir()
105 )
106 is True
107 )
110def _import_modules_recursivly_at_path(
111 package_path: Path, full_package_name: str
112) -> None:
113 for module_info in pkgutil.walk_packages([str(package_path)]):
114 full_module_name = module_info.name
115 if full_package_name is not None:
116 full_module_name = ".".join([full_package_name, module_info.name])
117 if full_module_name in sys.modules:
118 # already loaded module
119 _logger.warning(
120 str.format(
121 "Module {0} at {1} already loaded. Module skipped ",
122 full_module_name,
123 str(package_path),
124 )
125 )
126 else:
127 try:
128 importlib.import_module(full_module_name)
129 except ImportError as import_exception:
130 # Error while loading the module, log and skip the module
131 log_exception(
132 _logger,
133 ImportError,
134 import_exception,
135 import_exception.__traceback__,
136 logging.WARNING,
137 )
138 _logger.warning(
139 str.format(
140 "Import Error while loading {0} from {1}. Module skipped ",
141 full_module_name,
142 str(package_path),
143 )
144 )
146 if module_info.ispkg is True:
147 # recursivle load packa submodules
148 _import_modules_recursivly_at_path(
149 package_path / module_info.name, full_module_name
150 )