Skip to content

fix(utils): narrow check_import exception handling - #1720

Merged
Yunnglin merged 2 commits into
modelscope:mainfrom
Bruce-Yii:fix/check-import-exception-boundary
Sep 11, 2026
Merged

Yunnglin merged 2 commits into
modelscope:mainfrom
Bruce-Yii:fix/check-import-exception-boundary

Conversation

@Bruce-Yii

@Bruce-Yii Bruce-Yii commented Sep 8, 2026 •

Copy link
Copy Markdown
Contributor

check_import previously reported any import-time exception as module-not-found, hiding real breakage behind a pip-install hint. The first revision (catching ImportError only) still misclassified two cases, as pointed out in review: a module body deliberately raising ImportError("boom"), and an installed module failing on its own missing internal dependency (ModuleNotFoundError, an ImportError subclass).

This revision splits discovery from execution, as suggested:

  • importlib.util.find_spec() decides whether the requested module is truly discoverable; only undiscoverable modules enter the missing-module warning/install-hint/raise_error path.
  • A ModuleNotFoundError from find_spec() on a dotted name counts as missing only when the absent module is the requested module itself or one of its parents (name-prefix check); a broken parent failing on its own internal dependency re-raises untouched.
  • importlib.import_module() runs outside any handler, so every import-time failure of an available module propagates with its original traceback — never rewritten as " not found".

Behavior and messages for genuinely missing modules are unchanged.

Focused tests in tests/utils/test_import_utils.py cover: missing module returns False; missing module with raise_error raises ImportError("not found"); existing module (os) and dotted existing module (os.path) return True; body-raised RuntimeError / ImportError("boom") propagate with the original message and no warning; an internal missing dependency propagates as ModuleNotFoundError naming the internal dependency (not the requested module) with no warning; dotted missing parent and dotted missing child of an existing package stay on the missing-module path (False + warning + raise_error).

Note: the new tests could not run via repository pytest in this sandbox (collection requires the full installed dependency chain, e.g. datasets); they were validated with a standalone harness loading the real import_utils.py (9/9 scenarios pass, including fail-before confirmation on the ImportError-family cases). Please let CI run the full suite as final validation. Ruff check passes on both touched files; the applicable Ruff format check also passes.

Catch ImportError instead of bare Exception so present-but-broken modules surface their real error instead of a misleading missing-module hint. Focused tests in tests/utils/test_import_utils.py.

@Yunnglin Yunnglin left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the focused fix and the regression coverage — the underlying issue is valid.

However, changing except Exception to except ImportError does not fully separate a missing requested module from a module that exists but fails during initialization. ImportError can be raised deliberately by the module itself, and ModuleNotFoundError is also an ImportError subclass, including when an already-installed module is missing one of its private dependencies. Those cases would still be converted into the misleading "<module> not found" / install-hint path.

I suggest splitting discovery from execution instead of catching import-time errors broadly:

  1. Use importlib.util.find_spec() to determine whether the requested module is discoverable.
  2. Treat find_spec(...) is None (or a ModuleNotFoundError for the requested module / an absent parent of a dotted requested name) as the only “missing module” case.
  3. Run importlib.import_module() after discovery, outside that missing-module handler, so every failure from executing an available module propagates with its original traceback.

This keeps the existing missing-dependency UX while preserving actionable errors from broken or incompatible installations.

Please also add regressions for:

  • an existing module whose body raises ImportError("boom");
  • an existing module that imports a missing internal dependency and raises ModuleNotFoundError;
  • both cases propagating the original exception without a not found warning or install hint.

The current RuntimeError test is useful, but these two ImportError-family cases are the boundary this change needs to handle.

@Bruce-Yii

Copy link
Copy Markdown
Contributor Author

Thanks for the detailed review — revised in "46fe5b5b" as suggested.

"check_import" now uses "find_spec()" for discovery and runs "import_module()" outside the missing-module handler, so import-time failures from available modules propagate unchanged. I also added the requested regressions for body-raised "ImportError("boom")" and an internal-dependency "ModuleNotFoundError", plus dotted-module boundary coverage.

Repository pytest still can't collect in this sandbox because the full dependency chain ("datasets") isn't installed; the focused harness passes all 9 scenarios and Ruff passes. CI can provide the final repository-level validation.

@Yunnglin Yunnglin left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. The requested discovery/execution separation and regression coverage address the review feedback; targeted tests and Ruff checks pass.

@Yunnglin
Yunnglin merged commit d1ac106 into modelscope:main Sep 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants