Skip to content

Calling typing.get_type_hints on a ModelHubMixin breaks #2727

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

Closed
aliberts opened this issue Jan 3, 2025 · 5 comments
Closed

Calling typing.get_type_hints on a ModelHubMixin breaks #2727

aliberts opened this issue Jan 3, 2025 · 5 comments
Labels
bug Something isn't working

Comments

@aliberts
Copy link
Contributor

aliberts commented Jan 3, 2025

Describe the bug

Calling typing.get_type_hints on a class inheriting from ModelHubMixin gets you a NameError when that function tries to resolve its type because "DataclassInstance" (which is what ModelHubMixin uses in its annotations) doesn't seem to be available in the scope when that happens.

I'll see if I can fix this and open a PR.

Reproduction

import typing
from huggingface_hub import ModelHubMixin

class MyModel(ModelHubMixin): ...

my_model = MyModel()
typing.get_type_hints(my_model)

Logs

Traceback (most recent call last):
  File "/Users/simon/projects/draccus/demo2.py", line 7, in <module>
    typing.get_type_hints(my_model)
  File "/opt/homebrew/Caskroom/miniconda/base/envs/hub_310/lib/python3.10/typing.py", line 1871, in get_type_hints
    value = _eval_type(value, globalns, localns)
  File "/opt/homebrew/Caskroom/miniconda/base/envs/hub_310/lib/python3.10/typing.py", line 329, in _eval_type
    ev_args = tuple(_eval_type(a, globalns, localns, recursive_guard) for a in t.__args__)
  File "/opt/homebrew/Caskroom/miniconda/base/envs/hub_310/lib/python3.10/typing.py", line 329, in <genexpr>
    ev_args = tuple(_eval_type(a, globalns, localns, recursive_guard) for a in t.__args__)
  File "/opt/homebrew/Caskroom/miniconda/base/envs/hub_310/lib/python3.10/typing.py", line 327, in _eval_type
    return t._evaluate(globalns, localns, recursive_guard)
  File "/opt/homebrew/Caskroom/miniconda/base/envs/hub_310/lib/python3.10/typing.py", line 694, in _evaluate
    eval(self.__forward_code__, globalns, localns),
  File "<string>", line 1, in <module>
NameError: name 'DataclassInstance' is not defined

System info

- huggingface_hub version: 0.27.0
- Platform: macOS-15.2-arm64-arm-64bit
- Python version: 3.10.16
- Running in iPython ?: No
- Running in notebook ?: No
- Running in Google Colab ?: No
- Running in Google Colab Enterprise ?: No
- Token path ?: /Users/simon/.cache/huggingface/token
- Has saved token ?: True
- Who am I ?: aliberts
- Configured git credential helpers: cache, osxkeychain
- FastAI: N/A
- Tensorflow: N/A
- Torch: N/A
- Jinja2: N/A
- Graphviz: N/A
- keras: N/A
- Pydot: N/A
- Pillow: N/A
- hf_transfer: N/A
- gradio: N/A
- tensorboard: N/A
- numpy: N/A
- pydantic: N/A
- aiohttp: N/A
- ENDPOINT: https://huggingface.co
- HF_HUB_CACHE: /Users/simon/.cache/huggingface/hub
- HF_ASSETS_CACHE: /Users/simon/.cache/huggingface/assets
- HF_TOKEN_PATH: /Users/simon/.cache/huggingface/token
- HF_STORED_TOKENS_PATH: /Users/simon/.cache/huggingface/stored_tokens
- HF_HUB_OFFLINE: False
- HF_HUB_DISABLE_TELEMETRY: False
- HF_HUB_DISABLE_PROGRESS_BARS: None
- HF_HUB_DISABLE_SYMLINKS_WARNING: False
- HF_HUB_DISABLE_EXPERIMENTAL_WARNING: False
- HF_HUB_DISABLE_IMPLICIT_TOKEN: False
- HF_HUB_ENABLE_HF_TRANSFER: False
- HF_HUB_ETAG_TIMEOUT: 10
- HF_HUB_DOWNLOAD_TIMEOUT: 10
@aliberts aliberts added the bug Something isn't working label Jan 3, 2025
@hanouticelina
Copy link
Contributor

one simple solution would be to fallback to obj.__annotations__ , the drawback is that it (sometimes) doesn't provide partially resolved types. I'm interested to know what's your use case here.

import typing
from huggingface_hub import ModelHubMixin

class MyModel(ModelHubMixin): ...


def get_type_hints_safe(obj, ignore_errors=True):
    try:
        return typing.get_type_hints(obj)
    except NameError:
        return obj.__annotations__

my_model = MyModel()
get_type_hints_safe(my_model)

output:

{'_hub_mixin_config': typing.Union[dict, ForwardRef('DataclassInstance'), NoneType], '_hub_mixin_info': <class 'huggingface_hub.hub_mixin.MixinInfo'>, '_hub_mixin_inject_config': <class 'bool'>, '_hub_mixin_init_parameters': typing.Dict[str, inspect.Parameter], '_hub_mixin_jsonable_default_values': typing.Dict[str, typing.Any], '_hub_mixin_jsonable_custom_types': typing.Tuple[typing.Type, ...], '_hub_mixin_coders': typing.Dict[typing.Type, typing.Tuple[typing.Callable[[~ARGS_T], typing.Any], typing.Callable[[typing.Any], ~ARGS_T]]]}

the initial issue comes from the fact that get_type_hints() tries to evaluate all type annotations at runtime but DataclassInstance which is only available during type checking.

@aliberts
Copy link
Contributor Author

aliberts commented Jan 3, 2025

I'm using an external library (draccus) to serialize/deserialize from files or from cli commands our configs in LeRobot.

Some of our configs are dataclasses which also happen to subclass ModelHubMixin.
In order to parse and deserialize those, that tool calls typing.get_type_hints (over which I have no control).

Interestingly, they seem to have had the same issue and they redefine DataclassInstance at runtime (so when TYPE_CHECKING is false)

Adapting it here, it would look like this in hub_mixin.py:

if TYPE_CHECKING:
    from _typeshed import DataclassInstance
else:
    class DataclassInstance(Protocol):
        __dataclass_fields__: ClassVar[Dict[str, Field]]

Dataclass = TypeVar("Dataclass", bound=DataclassInstance)

And then in the rest of the file, replacing type annotations "DataclassInstance" with Type[Dataclass]

I've tried on my fork and it seems to do the trick, here's the PR if you want to check it out: #2729

@hanouticelina
Copy link
Contributor

Fixed by #2729

@hanouticelina
Copy link
Contributor

@aliberts I've just released a patch 0.27.1 for huggingface_hub that contains the fix in #2729 :)

@aliberts
Copy link
Contributor Author

aliberts commented Jan 6, 2025

Awesome, thank you @hanouticelina! 🤗

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

2 participants