Repository navigation
[Fix] Resolve deprecated pipeline modules in from_pretrained - #14026
Nas01010101 wants to merge 3 commits into
Conversation
|
Claude: Thanks for this — the One thing to flag: with this PR applied, loading a deprecated pipeline still doesn't complete. ( Drafted by Claude. |
Follow-up to the review feedback on huggingface#14026: the earlier fix gets past the spurious ValueError in _get_custom_components_and_folders, but loading a deprecated pipeline still failed end-to-end. get_class_obj_and_candidates received is_pipeline_module=False for relocated modules (hasattr(pipelines, library_name) is False), so it fell to the bare importlib.import_module(library_name) branch and raised ModuleNotFoundError: No module named 'wuerstchen'. Even resolving the import there is not enough: that branch builds class_candidates via getattr(library, c, None), and a relocated module does not expose the base classes in importable_classes, so no load method would resolve and load_sub_model would then raise 'cannot be loaded ...'. Deprecated modules must be handled with pipeline-module semantics. Add an explicit branch that imports diffusers.pipelines.deprecated.<library_name> and resolves the class via dict.fromkeys, matching the is_pipeline_module path. Genuinely missing modules still raise ModuleNotFoundError. Adds GetClassObjAndCandidatesTests covering the wuerstchen prior component (class + from_pretrained load method) and the missing-module case.
|
For me the code changes by the 2 contributors of this PR fix the problem as tested on my side yesterday. I have not tried to run the related patched automated test collections. |
Pipelines relocated to `diffusers.pipelines.deprecated` (e.g. Wuerstchen via `hasattr(pipelines, module_candidate)` check in `_get_custom_components_and_folders` mistook them for missing custom modules and raised a spurious ValueError when loading repos such as `warp-ai/wuerstchen-prior`. Also check the deprecated namespace (via `importlib.util.find_spec`, no import side effects) before treating a component as a missing custom module. Genuinely missing modules still raise. Fixes huggingface#14008
Follow-up to the review feedback on huggingface#14026: the earlier fix gets past the spurious ValueError in _get_custom_components_and_folders, but loading a deprecated pipeline still failed end-to-end. get_class_obj_and_candidates received is_pipeline_module=False for relocated modules (hasattr(pipelines, library_name) is False), so it fell to the bare importlib.import_module(library_name) branch and raised ModuleNotFoundError: No module named 'wuerstchen'. Even resolving the import there is not enough: that branch builds class_candidates via getattr(library, c, None), and a relocated module does not expose the base classes in importable_classes, so no load method would resolve and load_sub_model would then raise 'cannot be loaded ...'. Deprecated modules must be handled with pipeline-module semantics. Add an explicit branch that imports diffusers.pipelines.deprecated.<library_name> and resolves the class via dict.fromkeys, matching the is_pipeline_module path. Genuinely missing modules still raise ModuleNotFoundError. Adds GetClassObjAndCandidatesTests covering the wuerstchen prior component (class + from_pretrained load method) and the missing-module case.
Gap 1 — _fetch_class_library_tuple: deprecated modules (e.g. Wuerstchen)
live under diffusers.pipelines.deprecated, so hasattr(pipelines, pipeline_dir)
returns False and the function fell back to returning ("diffusers", class_name).
That breaks save_pretrained because the written model_index.json entry ("diffusers",
"WuerstchenPrior") is not resolvable on reload. Fix: promote the pipeline_dir to
is_pipeline_module=True when _is_deprecated_pipeline_module(pipeline_dir) is True,
so the returned library is the subpackage name (e.g. "wuerstchen").
Gap 2 — maybe_raise_or_warn: the guard was `if not is_pipeline_module`, which caused
deprecated-library callers to enter the type-check path, import the non-existent
top-level module, and eventually hit issubclass(model_cls, None) (TypeError) once
expected_class_obj stayed None. Fix: extend the guard to also skip the type-check
path for deprecated modules (`not _is_deprecated_pipeline_module(library_name)`),
mirroring the existing pipeline-module behaviour.
Gap 3 — simple_get_class_obj: the same is_pipeline_module=False path caused
importlib.import_module("wuerstchen") to raise ModuleNotFoundError. Fix: add an
elif branch that imports from diffusers.pipelines.deprecated.<library_name>,
mirroring the fix already present in get_class_obj_and_candidates.
Tests added for all three gaps (FetchClassLibraryTupleTests,
MaybeRaiseOrWarnTests, SimpleGetClassObjTests), including a roundtrip assertion
that proves the tuple written by _fetch_class_library_tuple is resolvable through
get_class_obj_and_candidates.
aadc755 to
8a0866e
Compare
What does this PR do?
Fixes #14008
The bug
DiffusionPipeline.from_pretrainedraises a spuriousValueErrorwhen loading any pipeline that was relocated underdiffusers.pipelines.deprecated(for example Wuerstchen, moved in #13157):Root cause
This is a bug in the deprecation mechanism itself, not in the deprecated pipeline.
_get_custom_components_and_foldersdecides whether a component refers to a built-in module viahasattr(diffusers.pipelines, module_candidate). When a pipeline is moved todiffusers.pipelines.deprecated, its module name is no longer a top-level attribute ofdiffusers.pipelines(deprecated modules are nested under thedeprecatedkey in_import_structure). SohasattrreturnsFalse, the component is mistaken for a missing custom module, and loading fails.The fix suggested in the issue (
hasattr(diffusers.pipelines.deprecated, module_candidate)) does not work:diffusers.pipelines.deprecatedis a_LazyModule, so the module name (wuerstchen) is not an attribute until it is imported; only the lazily exposed class names are.hasattrtherefore still returnsFalse.Fix
Resolve the module spec under the deprecated namespace without importing it, via
importlib.util.find_spec("diffusers.pipelines.deprecated.<module>"). This is import side-effect free (no heavy import is triggered) and correctly recognizes relocated modules. Genuinely missing modules still raise, sincefind_specreturnsNonefor them.Tests
Added
GetCustomComponentsAndFoldersTestsintests/pipelines/test_pipeline_utils.py(the existing home forpipeline_loading_utilsunit tests). All are function level and require no network or GPU.Reverting only the production change reproduces the issue (
from_pretrainedraises theValueErrorabove) and makes the new tests fail, confirming they guard the bug.ruff checkandruff format --checkpass on the changed files.Before submitting
.ai/review-rules.md, and I understand and take responsibility for the change.