faceit 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- faceit/__init__.py +35 -0
- faceit/__version__.py +6 -0
- faceit/_faceit.py +123 -0
- faceit/_repr.py +54 -0
- faceit/_resources/__init__.py +105 -0
- faceit/_resources/_base.py +184 -0
- faceit/_resources/_championships.py +476 -0
- faceit/_resources/_matches.py +116 -0
- faceit/_resources/_pagination.py +847 -0
- faceit/_resources/_players.py +1024 -0
- faceit/_typing.py +158 -0
- faceit/_utils.py +168 -0
- faceit/constants.py +422 -0
- faceit/exceptions.py +12 -0
- faceit/http/__init__.py +4 -0
- faceit/http/_client.py +765 -0
- faceit/http/_helpers.py +57 -0
- faceit/models/__init__.py +29 -0
- faceit/models/_championship.py +73 -0
- faceit/models/_custom_types/__init__.py +15 -0
- faceit/models/_custom_types/_adaptable.py +86 -0
- faceit/models/_custom_types/_faceit_uuid.py +115 -0
- faceit/models/_custom_types/_utils.py +28 -0
- faceit/models/_match.py +78 -0
- faceit/models/_page.py +228 -0
- faceit/models/_player.py +329 -0
- faceit/py.typed +0 -0
- faceit/types.py +94 -0
- faceit-0.1.0.dist-info/LICENSE +201 -0
- faceit-0.1.0.dist-info/METADATA +123 -0
- faceit-0.1.0.dist-info/RECORD +32 -0
- faceit-0.1.0.dist-info/WHEEL +4 -0
faceit/__init__.py
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
from .__version__ import __version__
|
|
2
|
+
from ._faceit import AsyncFaceit, Faceit
|
|
3
|
+
from ._resources import (
|
|
4
|
+
AsyncPageIterator,
|
|
5
|
+
CollectReturnFormat,
|
|
6
|
+
SyncPageIterator,
|
|
7
|
+
check_pagination_support,
|
|
8
|
+
)
|
|
9
|
+
from .constants import (
|
|
10
|
+
ELO_THRESHOLDS,
|
|
11
|
+
EventCategory,
|
|
12
|
+
ExpandOption,
|
|
13
|
+
GameID,
|
|
14
|
+
HighTierLevel,
|
|
15
|
+
SkillLevel,
|
|
16
|
+
)
|
|
17
|
+
from .http import AsyncClient, SyncClient
|
|
18
|
+
|
|
19
|
+
__all__ = (
|
|
20
|
+
"ELO_THRESHOLDS",
|
|
21
|
+
"AsyncClient",
|
|
22
|
+
"AsyncFaceit",
|
|
23
|
+
"AsyncPageIterator",
|
|
24
|
+
"CollectReturnFormat",
|
|
25
|
+
"EventCategory",
|
|
26
|
+
"ExpandOption",
|
|
27
|
+
"Faceit",
|
|
28
|
+
"GameID",
|
|
29
|
+
"HighTierLevel",
|
|
30
|
+
"SkillLevel",
|
|
31
|
+
"SyncClient",
|
|
32
|
+
"SyncPageIterator",
|
|
33
|
+
"__version__",
|
|
34
|
+
"check_pagination_support",
|
|
35
|
+
)
|
faceit/__version__.py
ADDED
faceit/_faceit.py
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import typing as t
|
|
4
|
+
from abc import ABC
|
|
5
|
+
from warnings import warn
|
|
6
|
+
|
|
7
|
+
from ._repr import representation
|
|
8
|
+
from ._resources import AsyncResources, SyncResources
|
|
9
|
+
from ._typing import ClientT, ResourceT, Self, ValidUUID
|
|
10
|
+
from .constants import BASE_WIKI_URL
|
|
11
|
+
from .http import AsyncClient, SyncClient
|
|
12
|
+
|
|
13
|
+
if t.TYPE_CHECKING:
|
|
14
|
+
from types import TracebackType
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@representation("client", "resources")
|
|
18
|
+
class BaseFaceit(t.Generic[ClientT, ResourceT], ABC):
|
|
19
|
+
__slots__ = "_client", "_resources"
|
|
20
|
+
|
|
21
|
+
_client_cls: t.Type[ClientT]
|
|
22
|
+
_resources_cls: t.Type[ResourceT]
|
|
23
|
+
|
|
24
|
+
@t.overload
|
|
25
|
+
def __init__(
|
|
26
|
+
self,
|
|
27
|
+
api_key: ValidUUID,
|
|
28
|
+
**client_options: t.Any,
|
|
29
|
+
) -> None: ...
|
|
30
|
+
|
|
31
|
+
@t.overload
|
|
32
|
+
def __init__(
|
|
33
|
+
self,
|
|
34
|
+
*,
|
|
35
|
+
client: ClientT,
|
|
36
|
+
) -> None: ...
|
|
37
|
+
|
|
38
|
+
def __init__(
|
|
39
|
+
self,
|
|
40
|
+
api_key: t.Optional[ValidUUID] = None,
|
|
41
|
+
*,
|
|
42
|
+
client: t.Optional[ClientT] = None,
|
|
43
|
+
**client_options: t.Any,
|
|
44
|
+
) -> None:
|
|
45
|
+
if api_key is None and client is None:
|
|
46
|
+
raise ValueError("Either 'api_key' or 'client' must be provided")
|
|
47
|
+
if api_key is not None and client is not None:
|
|
48
|
+
raise ValueError("Provide either 'api_key' or 'client', not both")
|
|
49
|
+
|
|
50
|
+
if client is not None:
|
|
51
|
+
if client_options:
|
|
52
|
+
warn(
|
|
53
|
+
"'client_options' are ignored when an existing client "
|
|
54
|
+
"instance is provided. Configure your client before "
|
|
55
|
+
"passing it to this constructor.",
|
|
56
|
+
UserWarning,
|
|
57
|
+
stacklevel=2,
|
|
58
|
+
)
|
|
59
|
+
self._client = client
|
|
60
|
+
else:
|
|
61
|
+
self._client = self.__class__._client_cls(
|
|
62
|
+
api_key, **client_options
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
self._resources = self.__class__._resources_cls(self._client)
|
|
66
|
+
|
|
67
|
+
@property
|
|
68
|
+
def client(self) -> ClientT:
|
|
69
|
+
return self._client
|
|
70
|
+
|
|
71
|
+
@property
|
|
72
|
+
def resources(self) -> ResourceT:
|
|
73
|
+
return self._resources
|
|
74
|
+
|
|
75
|
+
def __str__(self) -> str:
|
|
76
|
+
return (
|
|
77
|
+
f"FACEIT API interface "
|
|
78
|
+
f"(resources and client, docs: {BASE_WIKI_URL})"
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
@t.final
|
|
83
|
+
class Faceit(BaseFaceit[SyncClient, SyncResources]):
|
|
84
|
+
__slots__ = ()
|
|
85
|
+
|
|
86
|
+
_client_cls = SyncClient
|
|
87
|
+
_resources_cls = SyncResources
|
|
88
|
+
|
|
89
|
+
def __enter__(self) -> Self:
|
|
90
|
+
self.client.__enter__()
|
|
91
|
+
return self
|
|
92
|
+
|
|
93
|
+
def __exit__(
|
|
94
|
+
self,
|
|
95
|
+
typ: t.Optional[t.Type[BaseException]],
|
|
96
|
+
exc: t.Optional[BaseException],
|
|
97
|
+
tb: t.Optional[TracebackType],
|
|
98
|
+
) -> None:
|
|
99
|
+
self.client.__exit__(typ, exc, tb)
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
@t.final
|
|
103
|
+
class AsyncFaceit(BaseFaceit[AsyncClient, AsyncResources]):
|
|
104
|
+
__slots__ = ()
|
|
105
|
+
|
|
106
|
+
_client_cls = AsyncClient
|
|
107
|
+
_resources_cls = AsyncResources
|
|
108
|
+
|
|
109
|
+
MAX_CONCURRENT_REQUESTS: t.ClassVar[int] = (
|
|
110
|
+
AsyncClient.MAX_CONCURRENT_REQUESTS
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
async def __aenter__(self) -> Self:
|
|
114
|
+
await self.client.__aenter__()
|
|
115
|
+
return self
|
|
116
|
+
|
|
117
|
+
async def __aexit__(
|
|
118
|
+
self,
|
|
119
|
+
typ: t.Optional[t.Type[BaseException]],
|
|
120
|
+
exc: t.Optional[BaseException],
|
|
121
|
+
tb: t.Optional[TracebackType],
|
|
122
|
+
) -> None:
|
|
123
|
+
await self.client.__aexit__(typ, exc, tb)
|
faceit/_repr.py
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import typing as t
|
|
4
|
+
|
|
5
|
+
if t.TYPE_CHECKING:
|
|
6
|
+
from ._typing import TypeAlias
|
|
7
|
+
|
|
8
|
+
_ReprMethod: TypeAlias = t.Callable[[], str]
|
|
9
|
+
_ClassT = t.TypeVar("_ClassT", bound=t.Type)
|
|
10
|
+
|
|
11
|
+
_UNINITIALIZED_MARKER: t.Final = "uninitialized"
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def _format_fields(
|
|
15
|
+
obj: t.Any, fields: t.Tuple[str, ...], joiner: str = " ", /
|
|
16
|
+
) -> str:
|
|
17
|
+
return (
|
|
18
|
+
joiner.join(f"{field}={getattr(obj, field)!r}" for field in fields)
|
|
19
|
+
if all(hasattr(obj, field) for field in fields)
|
|
20
|
+
else repr(_UNINITIALIZED_MARKER)
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def _apply_representation(
|
|
25
|
+
cls: _ClassT, fields: t.Tuple[str, ...], use_str: bool, /
|
|
26
|
+
) -> _ClassT:
|
|
27
|
+
has_str = getattr(cls, "__str__", None) is not object.__str__
|
|
28
|
+
|
|
29
|
+
if use_str and not has_str:
|
|
30
|
+
raise TypeError(f"Class {cls.__name__} must define __str__ method")
|
|
31
|
+
|
|
32
|
+
def repr_(self: _ClassT) -> str:
|
|
33
|
+
str_args = (
|
|
34
|
+
f"'{self}'" if use_str else _format_fields(self, fields, ", ")
|
|
35
|
+
)
|
|
36
|
+
return f"{cls.__name__}({str_args})"
|
|
37
|
+
|
|
38
|
+
def str_(self: _ClassT) -> str:
|
|
39
|
+
return _format_fields(self, fields)
|
|
40
|
+
|
|
41
|
+
cls.__repr__ = t.cast(_ReprMethod, repr_)
|
|
42
|
+
if not has_str:
|
|
43
|
+
cls.__str__ = t.cast(_ReprMethod, str_)
|
|
44
|
+
|
|
45
|
+
return cls
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def representation(
|
|
49
|
+
*fields: str, use_str: bool = False
|
|
50
|
+
) -> t.Callable[[_ClassT], _ClassT]:
|
|
51
|
+
def decorator(cls: _ClassT) -> _ClassT:
|
|
52
|
+
return _apply_representation(cls, fields, use_str)
|
|
53
|
+
|
|
54
|
+
return decorator
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import typing as t
|
|
2
|
+
from abc import ABC
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from functools import cached_property
|
|
5
|
+
|
|
6
|
+
from faceit._typing import ClientT, Model, Raw
|
|
7
|
+
from faceit.http import AsyncClient, SyncClient
|
|
8
|
+
|
|
9
|
+
from ._base import BaseResource
|
|
10
|
+
from ._championships import (
|
|
11
|
+
AsyncChampionships,
|
|
12
|
+
BaseChampionships,
|
|
13
|
+
SyncChampionships,
|
|
14
|
+
)
|
|
15
|
+
from ._matches import AsyncMatches, BaseMatches, SyncMatches
|
|
16
|
+
from ._pagination import (
|
|
17
|
+
AsyncPageIterator,
|
|
18
|
+
BasePageIterator,
|
|
19
|
+
CollectReturnFormat,
|
|
20
|
+
SyncPageIterator,
|
|
21
|
+
TimestampPaginationConfig,
|
|
22
|
+
check_pagination_support,
|
|
23
|
+
)
|
|
24
|
+
from ._players import AsyncPlayers, BasePlayers, SyncPlayers
|
|
25
|
+
|
|
26
|
+
__all__ = (
|
|
27
|
+
"AsyncChampionships",
|
|
28
|
+
"AsyncMatches",
|
|
29
|
+
"AsyncPageIterator",
|
|
30
|
+
"AsyncPlayers",
|
|
31
|
+
"AsyncResources",
|
|
32
|
+
"BaseChampionships",
|
|
33
|
+
"BaseMatches",
|
|
34
|
+
"BasePageIterator",
|
|
35
|
+
"BasePlayers",
|
|
36
|
+
"BaseResource",
|
|
37
|
+
"BaseResources",
|
|
38
|
+
"CollectReturnFormat",
|
|
39
|
+
"SyncChampionships",
|
|
40
|
+
"SyncMatches",
|
|
41
|
+
"SyncPageIterator",
|
|
42
|
+
"SyncPlayers",
|
|
43
|
+
"SyncResources",
|
|
44
|
+
"TimestampPaginationConfig",
|
|
45
|
+
"check_pagination_support",
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
@dataclass(eq=False, frozen=True)
|
|
50
|
+
class BaseResources(t.Generic[ClientT], ABC):
|
|
51
|
+
_client: ClientT
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
@t.final
|
|
55
|
+
class SyncResources(BaseResources[SyncClient]):
|
|
56
|
+
@cached_property
|
|
57
|
+
def raw_championships(self) -> SyncChampionships[Raw]:
|
|
58
|
+
return SyncChampionships(self._client, raw=True)
|
|
59
|
+
|
|
60
|
+
@cached_property
|
|
61
|
+
def championships(self) -> SyncChampionships[Model]:
|
|
62
|
+
return SyncChampionships(self._client, raw=False)
|
|
63
|
+
|
|
64
|
+
@cached_property
|
|
65
|
+
def raw_matches(self) -> SyncMatches[Raw]:
|
|
66
|
+
return SyncMatches(self._client, raw=True)
|
|
67
|
+
|
|
68
|
+
@cached_property
|
|
69
|
+
def matches(self) -> SyncMatches[Model]:
|
|
70
|
+
return SyncMatches(self._client, raw=False)
|
|
71
|
+
|
|
72
|
+
@cached_property
|
|
73
|
+
def raw_players(self) -> SyncPlayers[Raw]:
|
|
74
|
+
return SyncPlayers(self._client, raw=True)
|
|
75
|
+
|
|
76
|
+
@cached_property
|
|
77
|
+
def players(self) -> SyncPlayers[Model]:
|
|
78
|
+
return SyncPlayers(self._client, raw=False)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
@t.final
|
|
82
|
+
class AsyncResources(BaseResources[AsyncClient]):
|
|
83
|
+
@cached_property
|
|
84
|
+
def raw_championships(self) -> AsyncChampionships[Raw]:
|
|
85
|
+
return AsyncChampionships(self._client, raw=True)
|
|
86
|
+
|
|
87
|
+
@cached_property
|
|
88
|
+
def championships(self) -> AsyncChampionships[Model]:
|
|
89
|
+
return AsyncChampionships(self._client, raw=False)
|
|
90
|
+
|
|
91
|
+
@cached_property
|
|
92
|
+
def raw_matches(self) -> AsyncMatches[Raw]:
|
|
93
|
+
return AsyncMatches(self._client, raw=True)
|
|
94
|
+
|
|
95
|
+
@cached_property
|
|
96
|
+
def matches(self) -> AsyncMatches[Model]:
|
|
97
|
+
return AsyncMatches(self._client, raw=False)
|
|
98
|
+
|
|
99
|
+
@cached_property
|
|
100
|
+
def raw_players(self) -> AsyncPlayers[Raw]:
|
|
101
|
+
return AsyncPlayers(self._client, raw=True)
|
|
102
|
+
|
|
103
|
+
@cached_property
|
|
104
|
+
def players(self) -> AsyncPlayers[Model]:
|
|
105
|
+
return AsyncPlayers(self._client, raw=False)
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
import typing as t
|
|
5
|
+
from abc import ABC
|
|
6
|
+
from dataclasses import dataclass
|
|
7
|
+
from warnings import warn
|
|
8
|
+
|
|
9
|
+
from pydantic import ValidationError
|
|
10
|
+
from strenum import StrEnum
|
|
11
|
+
|
|
12
|
+
from faceit._typing import (
|
|
13
|
+
ClientT,
|
|
14
|
+
ModelT,
|
|
15
|
+
NotRequired,
|
|
16
|
+
RawAPIPageResponse,
|
|
17
|
+
RawAPIResponse,
|
|
18
|
+
)
|
|
19
|
+
from faceit.http import Endpoint
|
|
20
|
+
from faceit.models import ItemPage
|
|
21
|
+
|
|
22
|
+
from ._pagination import (
|
|
23
|
+
AsyncPageIterator,
|
|
24
|
+
SyncPageIterator,
|
|
25
|
+
TimestampPaginationConfig,
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
if t.TYPE_CHECKING:
|
|
29
|
+
_ResponseT = t.TypeVar("_ResponseT", bound=RawAPIResponse)
|
|
30
|
+
|
|
31
|
+
_logger = logging.getLogger(__name__)
|
|
32
|
+
|
|
33
|
+
_KT = t.TypeVar("_KT")
|
|
34
|
+
|
|
35
|
+
# Temporary placeholder type for unimplemented models.
|
|
36
|
+
# Serves as a stub during development and should be replaced with
|
|
37
|
+
# concrete models as implementation progresses.
|
|
38
|
+
ModelPlaceholder: None = None
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
@t.final
|
|
42
|
+
class RequestPayload(t.TypedDict):
|
|
43
|
+
endpoint: Endpoint
|
|
44
|
+
params: t.Dict[str, t.Any]
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
@t.final
|
|
48
|
+
class MappedValidatorConfig(t.TypedDict, t.Generic[_KT, ModelT]):
|
|
49
|
+
validator_map: t.Dict[_KT, t.Type[ModelT]]
|
|
50
|
+
is_paged: bool
|
|
51
|
+
key_name: NotRequired[str]
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
@t.final
|
|
55
|
+
class FaceitResourcePath(StrEnum):
|
|
56
|
+
CHAMPIONSHIPS = "championships"
|
|
57
|
+
MATCHES = "matches"
|
|
58
|
+
PLAYERS = "players"
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
@dataclass(eq=False, frozen=True)
|
|
62
|
+
class BaseResource(t.Generic[ClientT], ABC):
|
|
63
|
+
__slots__ = "_client", "raw"
|
|
64
|
+
|
|
65
|
+
_client: ClientT
|
|
66
|
+
raw: bool
|
|
67
|
+
|
|
68
|
+
_sync_page_iterator: t.ClassVar = SyncPageIterator
|
|
69
|
+
_async_page_iterator: t.ClassVar = AsyncPageIterator
|
|
70
|
+
_timestamp_cfg: t.ClassVar = TimestampPaginationConfig
|
|
71
|
+
|
|
72
|
+
PATH: t.ClassVar[Endpoint]
|
|
73
|
+
|
|
74
|
+
_PARAM_NAME_MAP: t.ClassVar = {
|
|
75
|
+
"start": "from",
|
|
76
|
+
"category": "type",
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
def __init_subclass__(
|
|
80
|
+
cls,
|
|
81
|
+
*,
|
|
82
|
+
resource_path: t.Optional[FaceitResourcePath] = None,
|
|
83
|
+
**kwargs: t.Any,
|
|
84
|
+
) -> None:
|
|
85
|
+
super().__init_subclass__(**kwargs)
|
|
86
|
+
if hasattr(cls, "PATH"):
|
|
87
|
+
return
|
|
88
|
+
if resource_path is None:
|
|
89
|
+
raise TypeError(
|
|
90
|
+
f"Class {cls.__name__} requires 'path' parameter or a "
|
|
91
|
+
f"parent with 'PATH' defined."
|
|
92
|
+
)
|
|
93
|
+
cls.PATH = Endpoint(resource_path)
|
|
94
|
+
|
|
95
|
+
# NOTE: These overloads are necessary as this function directly returns in resource
|
|
96
|
+
# methods, where typing must be strict for public API. Current implementation
|
|
97
|
+
# is sufficient, though alternative typing approaches could be considered.
|
|
98
|
+
|
|
99
|
+
# TODO: Replace named arguments with a single `config: MappedValidatorConfig`
|
|
100
|
+
# parameter, but this is currently not possible due to Python 3.8 compatibility
|
|
101
|
+
# issues with Generic type subscriptions. Once Python 3.8 support is dropped,
|
|
102
|
+
# this should be refactored.
|
|
103
|
+
|
|
104
|
+
@t.overload
|
|
105
|
+
def _process_response_with_mapped_validator(
|
|
106
|
+
self,
|
|
107
|
+
response: RawAPIPageResponse,
|
|
108
|
+
key: _KT,
|
|
109
|
+
/,
|
|
110
|
+
*,
|
|
111
|
+
validator_map: t.Dict[_KT, t.Type[ModelT]],
|
|
112
|
+
is_paged: t.Literal[False],
|
|
113
|
+
key_name: str = ...,
|
|
114
|
+
) -> t.Union[ModelT, RawAPIPageResponse]: ...
|
|
115
|
+
|
|
116
|
+
@t.overload
|
|
117
|
+
def _process_response_with_mapped_validator(
|
|
118
|
+
self,
|
|
119
|
+
response: RawAPIPageResponse,
|
|
120
|
+
key: _KT,
|
|
121
|
+
/,
|
|
122
|
+
*,
|
|
123
|
+
validator_map: t.Dict[_KT, t.Type[ModelT]],
|
|
124
|
+
is_paged: t.Literal[True],
|
|
125
|
+
key_name: str = ...,
|
|
126
|
+
) -> t.Union[ItemPage[ModelT], RawAPIPageResponse]: ...
|
|
127
|
+
|
|
128
|
+
def _process_response_with_mapped_validator(
|
|
129
|
+
self,
|
|
130
|
+
response: RawAPIPageResponse,
|
|
131
|
+
key: _KT,
|
|
132
|
+
/,
|
|
133
|
+
*,
|
|
134
|
+
validator_map: t.Dict[_KT, t.Type[ModelT]],
|
|
135
|
+
is_paged: bool,
|
|
136
|
+
key_name: str = "key",
|
|
137
|
+
) -> t.Union[ModelT, ItemPage[ModelT], RawAPIPageResponse]:
|
|
138
|
+
_logger.debug(
|
|
139
|
+
"Processing response with mapped validator for key: %s", key
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
validator = validator_map.get(key)
|
|
143
|
+
if validator is None:
|
|
144
|
+
warn(
|
|
145
|
+
f"No model defined for {key_name} '{key}'. "
|
|
146
|
+
f"Consider using the raw response.",
|
|
147
|
+
UserWarning,
|
|
148
|
+
stacklevel=3,
|
|
149
|
+
)
|
|
150
|
+
return response
|
|
151
|
+
|
|
152
|
+
# Suppressing type checking warning because we're using a
|
|
153
|
+
# dynamic runtime subscript `ItemPage` is being subscripted
|
|
154
|
+
# with a variable (`validator`) which mypy cannot statically verify
|
|
155
|
+
return self._validate_response(
|
|
156
|
+
response,
|
|
157
|
+
t.cast(t.Type[ModelT], ItemPage[validator]) # type: ignore[valid-type]
|
|
158
|
+
if is_paged
|
|
159
|
+
else validator,
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
def _validate_response(
|
|
163
|
+
self,
|
|
164
|
+
response: _ResponseT,
|
|
165
|
+
validator: t.Optional[t.Type[ModelT]],
|
|
166
|
+
/,
|
|
167
|
+
) -> t.Union[_ResponseT, ModelT]:
|
|
168
|
+
if validator is not None and not self.raw:
|
|
169
|
+
try:
|
|
170
|
+
return validator.model_validate(response)
|
|
171
|
+
except ValidationError:
|
|
172
|
+
_logger.exception(
|
|
173
|
+
"Response validation failed for %s model",
|
|
174
|
+
validator.__name__,
|
|
175
|
+
)
|
|
176
|
+
return response
|
|
177
|
+
|
|
178
|
+
@classmethod
|
|
179
|
+
def _build_params(cls, **params: t.Any) -> t.Dict[str, t.Any]:
|
|
180
|
+
return {
|
|
181
|
+
cls._PARAM_NAME_MAP.get(key, key): value
|
|
182
|
+
for key, value in params.items()
|
|
183
|
+
if value is not None
|
|
184
|
+
}
|