From 88ca9ba3b34f2222d120d9b8f3121a5755b9494e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=A8=E5=A0=83?= Date: Thu, 20 Aug 2026 13:02:22 +0800 Subject: [PATCH 1/2] fix: map pre-prod UserInfo fields to canonical names (BUG-041) --- src/modelscope_hub/_openapi.py | 2 +- src/modelscope_hub/types.py | 12 ++++++++++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/modelscope_hub/_openapi.py b/src/modelscope_hub/_openapi.py index b49e248..2f44df4 100644 --- a/src/modelscope_hub/_openapi.py +++ b/src/modelscope_hub/_openapi.py @@ -404,7 +404,7 @@ def get_current_username(self) -> str: data = self.get_current_user() if not isinstance(data, dict): return "" - for key in ("Username", "username", "preferred_username", "name"): + for key in ("Username", "username", "name", "preferred_username"): value = data.get(key) if value: return str(value) diff --git a/src/modelscope_hub/types.py b/src/modelscope_hub/types.py index f690d49..15d7de2 100644 --- a/src/modelscope_hub/types.py +++ b/src/modelscope_hub/types.py @@ -11,7 +11,7 @@ from dataclasses import dataclass, field, fields from datetime import datetime, timezone from enum import Enum -from typing import Any, Generic, TypedDict, TypeVar +from typing import Any, ClassVar, Generic, TypedDict, TypeVar from .constants import RepoType, Visibility @@ -40,12 +40,15 @@ def _coerce_datetime(value: Any) -> datetime | None: class _FromDictMixin: """Adds tolerant ``from_dict`` construction to a dataclass.""" + _field_aliases: ClassVar[dict[str, str]] = {} + @classmethod def from_dict(cls: type[_TDataclass], data: Mapping[str, Any] | None) -> _TDataclass: if not data: return cls() # type: ignore[call-arg] known = {f.name for f in fields(cls)} # type: ignore[arg-type] - kwargs = {key: value for key, value in data.items() if key in known} + aliases = cls._field_aliases + kwargs = {aliases.get(key, key): value for key, value in data.items() if aliases.get(key, key) in known} return cls(**kwargs) # type: ignore[arg-type] @@ -54,6 +57,11 @@ def from_dict(cls: type[_TDataclass], data: Mapping[str, Any] | None) -> _TDatac # --------------------------------------------------------------------------- @dataclass(slots=True) class UserInfo(_FromDictMixin): + _field_aliases: ClassVar[dict[str, str]] = { + "name": "username", + "avatar": "avatar_url", + } + id: str | int | None = None username: str | None = None email: str | None = None From 7aa2a9a42741e4a4980e08ae222dc6acf475ceba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=A8=E5=A0=83?= Date: Fri, 21 Aug 2026 10:51:31 +0800 Subject: [PATCH 2/2] change test_openapi --- tests/cli/test_openapi.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/cli/test_openapi.py b/tests/cli/test_openapi.py index e81e265..2d0a295 100644 --- a/tests/cli/test_openapi.py +++ b/tests/cli/test_openapi.py @@ -409,7 +409,7 @@ class TestCurrentUsernameFieldCompat: ({"username": "bob"}, "bob"), # A server populating both must yield the login handle, not the # human-readable display name. - ({"preferred_username": "carol", "name": "Carol Smith"}, "carol"), + ({"preferred_username": "Carol Smith", "name": "carol"}, "carol"), ({"Username": "dave", "name": "Dave X"}, "dave"), # Unresolvable responses degrade to "" so callers can report it. ({}, ""),