Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Re-adding load_submodules #8118

Merged
merged 2 commits into from
Sep 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 21 additions & 17 deletions monai/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,25 +80,29 @@ def filter(self, record):
)


from . import ( # noqa: E402
apps,
auto3dseg,
bundle,
config,
data,
engines,
fl,
handlers,
inferers,
losses,
metrics,
networks,
optimizers,
transforms,
utils,
visualize,
from .utils.module import load_submodules # noqa: E402

# handlers_* have some external decorators the users may not have installed
# *.so files and folder "_C" may not exist when the cpp extensions are not compiled
excludes = "|".join(
[
"(^(monai.handlers))",
"(^(monai.bundle))",
"(^(monai.fl))",
"((\\.so)$)",
"(^(monai._C))",
"(.*(__main__)$)",
"(.*(video_dataset)$)",
"(.*(nnunet).*$)",
]
)

# load directory modules only, skip loading individual files
load_submodules(sys.modules[__name__], False, exclude_pattern=excludes)

# load all modules, this will trigger all export decorations
load_submodules(sys.modules[__name__], True, exclude_pattern=excludes)

__all__ = [
"apps",
"auto3dseg",
Expand Down
1 change: 1 addition & 0 deletions monai/utils/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@
get_package_version,
get_torch_version_tuple,
instantiate,
load_submodules,
look_up_option,
min_version,
optional_import,
Expand Down
36 changes: 35 additions & 1 deletion monai/utils/module.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,15 @@
import os
import pdb
import re
import sys
import warnings
from collections.abc import Callable, Collection, Hashable, Mapping
from functools import partial, wraps
from importlib import import_module
from pkgutil import walk_packages
from pydoc import locate
from re import match
from types import FunctionType
from types import FunctionType, ModuleType
from typing import Any, Iterable, cast

import torch
Expand Down Expand Up @@ -168,6 +170,38 @@ def damerau_levenshtein_distance(s1: str, s2: str) -> int:
return d[string_1_length - 1, string_2_length - 1]


def load_submodules(
basemod: ModuleType, load_all: bool = True, exclude_pattern: str = "(.*[tT]est.*)|(_.*)"
) -> tuple[list[ModuleType], list[str]]:
"""
Traverse the source of the module structure starting with module `basemod`, loading all packages plus all files if
`load_all` is True, excluding anything whose name matches `exclude_pattern`.
"""
submodules = []
err_mod: list[str] = []
for importer, name, is_pkg in walk_packages(
basemod.__path__, prefix=basemod.__name__ + ".", onerror=err_mod.append
):
if (is_pkg or load_all) and name not in sys.modules and match(exclude_pattern, name) is None:
try:
mod = import_module(name)
mod_spec = importer.find_spec(name) # type: ignore
if mod_spec and mod_spec.loader:
loader = mod_spec.loader
loader.exec_module(mod)
submodules.append(mod)
except OptionalImportError:
pass # could not import the optional deps., they are ignored
except ImportError as e:
msg = (
"\nMultiple versions of MONAI may have been installed?\n"
"Please see the installation guide: https://docs.monai.io/en/stable/installation.html\n"
) # issue project-monai/monai#5193
raise type(e)(f"{e}\n{msg}").with_traceback(e.__traceback__) from e # raise with modified message

return submodules, err_mod


def instantiate(__path: str, __mode: str, **kwargs: Any) -> Any:
"""
Create an object instance or call a callable object from a class or function represented by ``_path``.
Expand Down
6 changes: 6 additions & 0 deletions tests/min_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,12 @@ def run_testsuit():


if __name__ == "__main__":
# testing import submodules
from monai.utils.module import load_submodules

_, err_mod = load_submodules(sys.modules["monai"], True)
assert not err_mod, f"err_mod={err_mod} not empty"

# testing all modules
test_runner = unittest.TextTestRunner(stream=sys.stdout, verbosity=2)
result = test_runner.run(run_testsuit())
Expand Down
Loading