Skip to content
Open
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
11 changes: 8 additions & 3 deletions src/huggingface_hub/hub_mixin.py
Original file line number Diff line number Diff line change
Expand Up @@ -499,10 +499,15 @@ def from_pretrained(
model_id = str(pretrained_model_name_or_path)
config_file: str | None = None
if os.path.isdir(model_id):
if constants.CONFIG_NAME in os.listdir(model_id):
config_file = os.path.join(model_id, constants.CONFIG_NAME)
# Use a real path check: CONFIG_NAME may contain subdirectories (e.g. `codecs/image/config.json`).
# `os.listdir(model_id)` only returns the top level, so nested paths were never found locally.
local_config_path = os.path.join(model_id, constants.CONFIG_NAME)
if os.path.isfile(local_config_path):
config_file = local_config_path
else:
logger.warning(f"{constants.CONFIG_NAME} not found in {Path(model_id).resolve()}")
logger.warning(
f"{constants.CONFIG_NAME} not found under local directory {Path(model_id).resolve()}"
)
else:
try:
config_file = hf_hub_download(
Expand Down
46 changes: 46 additions & 0 deletions tests/test_hub_mixin.py
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,30 @@ def _from_pretrained(
return cls(**model_kwargs)


class DummyModelNestedConfigFile(ModelHubMixin):
"""Model whose config.json lives in a subdirectory when loading from a local folder."""

def __init__(self, answer: int):
self.answer = answer

def _save_pretrained(self, save_directory: Path) -> None:
return

@classmethod
def _from_pretrained(
cls,
*,
model_id: str,
revision: Optional[str],
cache_dir: Optional[Union[str, Path]],
force_download: bool,
local_files_only: bool,
token: Optional[Union[str, bool]],
**model_kwargs,
):
return cls(**model_kwargs)


class CustomType:
def __init__(self, value: str):
self.value = value
Expand Down Expand Up @@ -435,6 +459,28 @@ def test_from_pretrained_when_cls_is_a_dataclass(self):
assert model.bar == "baz"
assert not hasattr(model, "other")

def test_from_pretrained_nested_config_path_in_local_dir(self) -> None:
"""`CONFIG_NAME` may include subdirectories; local loading must still find the file.

Regression: previously only `CONFIG_NAME in os.listdir(root)` was used, which fails for paths like
`nested/sub/config.json`. Remote `hf_hub_download` already supported nested filenames.
"""
with SoftTemporaryDirectory() as tmp:
root = Path(tmp)
cfg_file = root / "nested" / "sub" / "config.json"
cfg_file.parent.mkdir(parents=True)
cfg_file.write_text(json.dumps({"answer": 42}))

from huggingface_hub.hub_mixin import constants as hub_mixin_constants

previous = hub_mixin_constants.CONFIG_NAME
try:
hub_mixin_constants.CONFIG_NAME = "nested/sub/config.json"
model = DummyModelNestedConfigFile.from_pretrained(str(root))
self.assertEqual(model.answer, 42)
finally:
hub_mixin_constants.CONFIG_NAME = previous

def test_from_cls_with_custom_type(self):
model = DummyModelWithCustomTypes(
1,
Expand Down