faceit 0.2.2__tar.gz → 0.2.3__tar.gz

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.
Files changed (61) hide show
  1. faceit-0.2.2/.github/workflows/ci.yml → faceit-0.2.3/.github/workflows/ci.yaml +1 -1
  2. {faceit-0.2.2 → faceit-0.2.3}/.mypy.ini +4 -2
  3. {faceit-0.2.2 → faceit-0.2.3}/.ruff.toml +5 -2
  4. {faceit-0.2.2 → faceit-0.2.3}/PKG-INFO +5 -5
  5. {faceit-0.2.2 → faceit-0.2.3}/README.md +4 -4
  6. {faceit-0.2.2 → faceit-0.2.3}/pyproject.toml +1 -1
  7. {faceit-0.2.2 → faceit-0.2.3}/src/faceit/__init__.py +1 -5
  8. {faceit-0.2.2 → faceit-0.2.3}/src/faceit/api/base.py +0 -5
  9. {faceit-0.2.2 → faceit-0.2.3}/src/faceit/api/data/championships.py +10 -8
  10. {faceit-0.2.2 → faceit-0.2.3}/src/faceit/api/data/games.py +10 -8
  11. {faceit-0.2.2 → faceit-0.2.3}/src/faceit/api/data/players.py +80 -79
  12. {faceit-0.2.2 → faceit-0.2.3}/src/faceit/api/data/rankings.py +10 -4
  13. {faceit-0.2.2 → faceit-0.2.3}/src/faceit/api/data/teams.py +10 -8
  14. {faceit-0.2.2 → faceit-0.2.3}/src/faceit/api/pagination.py +81 -207
  15. faceit-0.2.3/src/faceit/http/__init__.py +14 -0
  16. {faceit-0.2.2 → faceit-0.2.3}/src/faceit/models/championships.py +7 -6
  17. {faceit-0.2.2 → faceit-0.2.3}/src/faceit/models/custom_types/__init__.py +8 -0
  18. {faceit-0.2.2 → faceit-0.2.3}/src/faceit/models/custom_types/common.py +64 -5
  19. {faceit-0.2.2 → faceit-0.2.3}/src/faceit/models/custom_types/faceit_uuid.py +9 -12
  20. {faceit-0.2.2 → faceit-0.2.3}/src/faceit/models/item_page.py +24 -32
  21. {faceit-0.2.2 → faceit-0.2.3}/src/faceit/models/players/match.py +6 -4
  22. {faceit-0.2.2 → faceit-0.2.3}/src/faceit/types.py +4 -4
  23. {faceit-0.2.2 → faceit-0.2.3}/src/faceit/utils.py +17 -20
  24. {faceit-0.2.2 → faceit-0.2.3}/tests/test_e2e_data_resource.py +16 -3
  25. {faceit-0.2.2 → faceit-0.2.3}/tests/test_pagination.py +32 -57
  26. {faceit-0.2.2 → faceit-0.2.3}/tests/test_players_resource.py +24 -13
  27. {faceit-0.2.2 → faceit-0.2.3}/tests/test_repr.py +3 -5
  28. {faceit-0.2.2 → faceit-0.2.3}/tests/test_warn_stacklevel.py +8 -11
  29. faceit-0.2.2/src/faceit/http/__init__.py +0 -31
  30. {faceit-0.2.2 → faceit-0.2.3}/.editorconfig +0 -0
  31. /faceit-0.2.2/.github/workflows/publish.yml → /faceit-0.2.3/.github/workflows/publish.yaml +0 -0
  32. {faceit-0.2.2 → faceit-0.2.3}/.gitignore +0 -0
  33. {faceit-0.2.2 → faceit-0.2.3}/.pre-commit-config.yaml +0 -0
  34. {faceit-0.2.2 → faceit-0.2.3}/.pytest.ini +0 -0
  35. {faceit-0.2.2 → faceit-0.2.3}/LICENSE +0 -0
  36. {faceit-0.2.2 → faceit-0.2.3}/examples/simple_discord_bot.py +0 -0
  37. {faceit-0.2.2 → faceit-0.2.3}/scripts/clean.py +0 -0
  38. {faceit-0.2.2 → faceit-0.2.3}/src/faceit/api/__init__.py +0 -0
  39. {faceit-0.2.2 → faceit-0.2.3}/src/faceit/api/aggregator.py +0 -0
  40. {faceit-0.2.2 → faceit-0.2.3}/src/faceit/api/data/__init__.py +0 -0
  41. {faceit-0.2.2 → faceit-0.2.3}/src/faceit/api/data/helpers.py +0 -0
  42. {faceit-0.2.2 → faceit-0.2.3}/src/faceit/api/data/leagues.py +0 -0
  43. {faceit-0.2.2 → faceit-0.2.3}/src/faceit/api/data/matches.py +0 -0
  44. {faceit-0.2.2 → faceit-0.2.3}/src/faceit/api/data/matchmakings.py +0 -0
  45. {faceit-0.2.2 → faceit-0.2.3}/src/faceit/constants.py +0 -0
  46. {faceit-0.2.2 → faceit-0.2.3}/src/faceit/exceptions.py +0 -0
  47. {faceit-0.2.2 → faceit-0.2.3}/src/faceit/faceit.py +0 -0
  48. {faceit-0.2.2 → faceit-0.2.3}/src/faceit/http/client.py +0 -0
  49. {faceit-0.2.2 → faceit-0.2.3}/src/faceit/http/helpers.py +0 -0
  50. {faceit-0.2.2 → faceit-0.2.3}/src/faceit/models/__init__.py +0 -0
  51. {faceit-0.2.2 → faceit-0.2.3}/src/faceit/models/players/__init__.py +0 -0
  52. {faceit-0.2.2 → faceit-0.2.3}/src/faceit/models/players/general.py +0 -0
  53. {faceit-0.2.2 → faceit-0.2.3}/src/faceit/py.typed +0 -0
  54. {faceit-0.2.2 → faceit-0.2.3}/tests/__init__.py +0 -0
  55. {faceit-0.2.2 → faceit-0.2.3}/tests/conftest.py +0 -0
  56. {faceit-0.2.2 → faceit-0.2.3}/tests/test_custom_types.py +0 -0
  57. {faceit-0.2.2 → faceit-0.2.3}/tests/test_data_resource_integration.py +0 -0
  58. {faceit-0.2.2 → faceit-0.2.3}/tests/test_http_client.py +0 -0
  59. {faceit-0.2.2 → faceit-0.2.3}/tests/test_pagination_integration.py +0 -0
  60. {faceit-0.2.2 → faceit-0.2.3}/tests/test_resources_integration.py +0 -0
  61. {faceit-0.2.2 → faceit-0.2.3}/tests/test_skill_level.py +0 -0
@@ -11,7 +11,7 @@ jobs:
11
11
  strategy:
12
12
  fail-fast: false
13
13
  matrix:
14
- python-version: ["3.8", "3.12", "3.14"]
14
+ python-version: ["3.8", "3.10", "3.12", "3.14"]
15
15
 
16
16
  steps:
17
17
  - uses: actions/checkout@v4
@@ -2,11 +2,13 @@
2
2
  exclude = (?x)(
3
3
  ^assets/
4
4
  | ^docs/
5
+ | ^examples/
6
+ | ^tests/
5
7
  )
6
8
  local_partial_types = True
7
- ; We target Python 3.9+ for type checking because modern mypy releases
9
+ ; We target Python 3.10+ for type checking because modern mypy releases
8
10
  ; have dropped support for Python 3.8, even though our library supports 3.8
9
- python_version = 3.9
11
+ python_version = 3.10
10
12
  strict = True
11
13
  warn_unreachable = True
12
14
 
@@ -12,7 +12,6 @@ ignore = [
12
12
  "D", # pydocstyle docstring violations
13
13
  "DOC", # pydoclint docstring validation violations
14
14
  "E501", # Line too long
15
- "EM102", # f-string used directly in exception
16
15
  "FIX002", # TODO comment detected
17
16
  "PLR0904", # Too many public methods
18
17
  "PLR0913", # Too many function arguments
@@ -20,7 +19,6 @@ ignore = [
20
19
  "SLF001", # Private member accessed outside class
21
20
  "S101", # Use of assert detected
22
21
  "TD", # flake8-todos violations
23
- "TRY003", # Long exception message defined outside exception class
24
22
  ]
25
23
 
26
24
  [lint.flake8-bugbear]
@@ -85,5 +83,10 @@ ignore-names = ["env", "pages"]
85
83
  "PYI018", # Unused private `TypeVar`, `ParamSpec`, or `TypeVarTuple` declaration
86
84
  ]
87
85
 
86
+ [lint.pylint]
87
+ allow-dunder-method-names = [
88
+ "__get_pydantic_core_schema__",
89
+ ]
90
+
88
91
  [lint.pyupgrade]
89
92
  keep-runtime-typing = true
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: faceit
3
- Version: 0.2.2
3
+ Version: 0.2.3
4
4
  Summary: The Python wrapper for the FACEIT API
5
5
  Project-URL: Documentation, https://docs.faceit.com/docs
6
6
  Author-email: zombyacoff <zombyacoff@gmail.com>
@@ -58,7 +58,7 @@ Description-Content-Type: text/markdown
58
58
 
59
59
  ## Installation
60
60
 
61
- > Requires Python 3.8+
61
+ > Requires **Python 3.8+**.
62
62
 
63
63
  ```bash
64
64
  pip install faceit
@@ -138,9 +138,9 @@ The goal is to provide a solution approaching enterprise-level quality, while re
138
138
 
139
139
  ### Planned Improvements
140
140
 
141
- - Support for more endpoints and models
142
- - Webhooks and chat API integration
143
- - Full documentation and usage guides
141
+ - Support for more endpoints and models.
142
+ - Webhooks and chat API integration.
143
+ - Full documentation and usage guides.
144
144
 
145
145
  ---
146
146
 
@@ -29,7 +29,7 @@
29
29
 
30
30
  ## Installation
31
31
 
32
- > Requires Python 3.8+
32
+ > Requires **Python 3.8+**.
33
33
 
34
34
  ```bash
35
35
  pip install faceit
@@ -109,9 +109,9 @@ The goal is to provide a solution approaching enterprise-level quality, while re
109
109
 
110
110
  ### Planned Improvements
111
111
 
112
- - Support for more endpoints and models
113
- - Webhooks and chat API integration
114
- - Full documentation and usage guides
112
+ - Support for more endpoints and models.
113
+ - Webhooks and chat API integration.
114
+ - Full documentation and usage guides.
115
115
 
116
116
  ---
117
117
 
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "faceit"
3
- version = "0.2.2"
3
+ version = "0.2.3"
4
4
  description = "The Python wrapper for the FACEIT API"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.8"
@@ -18,21 +18,17 @@ from .api import (
18
18
  pages,
19
19
  )
20
20
  from .constants import EventCategory, ExpandedField, GameID, Region, SkillLevel
21
- from .exceptions import APIError, FaceitError # deprecated import
22
21
  from .faceit import AsyncFaceit, Faceit
23
- from .http import EnvKey, FromEnv, MaxConcurrentRequests
22
+ from .http import FromEnv, MaxConcurrentRequests
24
23
 
25
24
  __all__ = [
26
- "APIError",
27
25
  "AsyncDataResource",
28
26
  "AsyncFaceit", # deprecated (remove in v0.3.0 ?)
29
27
  "AsyncPageIterator",
30
28
  "CollectReturnFormat",
31
- "EnvKey", # deprecated (remove in v0.2.2 ?)
32
29
  "EventCategory",
33
30
  "ExpandedField",
34
31
  "Faceit", # deprecated (remove in v0.3.0 ?)
35
- "FaceitError",
36
32
  "FromEnv",
37
33
  "GameID",
38
34
  "MaxConcurrentRequests",
@@ -21,8 +21,6 @@ from faceit.types import (
21
21
  )
22
22
  from faceit.utils import warn_stacklevel
23
23
 
24
- from .pagination import AsyncPageIterator, SyncPageIterator
25
-
26
24
  if typing.TYPE_CHECKING:
27
25
  _ResponseT = typing.TypeVar("_ResponseT", bound=RawAPIResponse)
28
26
 
@@ -66,9 +64,6 @@ class BaseResource(ABC, typing.Generic[ClientT]):
66
64
  "category": "type",
67
65
  })
68
66
 
69
- _sync_page_iterator: typing.ClassVar = SyncPageIterator
70
- _async_page_iterator: typing.ClassVar = AsyncPageIterator
71
-
72
67
  def __init__(
73
68
  self,
74
69
  client: ClientT,
@@ -1,4 +1,3 @@
1
- # mypy: disable-error-code="no-any-return"
2
1
  from __future__ import annotations
3
2
 
4
3
  import typing
@@ -8,7 +7,12 @@ from pydantic import AfterValidator, Field, validate_call
8
7
  from typing_extensions import Annotated, TypeAlias
9
8
 
10
9
  from faceit.api.base import BaseResource, ModelPlaceholder
11
- from faceit.api.pagination import MaxItemsType, pages
10
+ from faceit.api.pagination import (
11
+ AsyncPageIterator,
12
+ MaxItemsType,
13
+ SyncPageIterator,
14
+ pages,
15
+ )
12
16
  from faceit.constants import EventCategory, ExpandedField, GameID
13
17
  from faceit.http import AsyncClient, SyncClient
14
18
  from faceit.models import Championship, ItemPage
@@ -107,9 +111,8 @@ class SyncChampionships(
107
111
  category: EventCategory = EventCategory.ALL,
108
112
  max_items: MaxItemsType = pages(30),
109
113
  ) -> typing.Union[typing.List[RawAPIItem], ItemPage[Championship]]:
110
- return self.__class__._sync_page_iterator.gather_pages(
111
- self.items, game, category, max_items=max_items
112
- )
114
+ iterator = SyncPageIterator(self.items, game, category, max_items=max_items)
115
+ return iterator.collect()
113
116
 
114
117
  @typing.overload
115
118
  def get(
@@ -327,9 +330,8 @@ class AsyncChampionships(
327
330
  category: EventCategory = EventCategory.ALL,
328
331
  max_items: MaxItemsType = pages(30),
329
332
  ) -> typing.Union[typing.List[RawAPIItem], ItemPage[Championship]]:
330
- return await self.__class__._async_page_iterator.gather_pages(
331
- self.items, game, category, max_items=max_items
332
- )
333
+ iterator = AsyncPageIterator(self.items, game, category, max_items=max_items)
334
+ return await iterator.collect()
333
335
 
334
336
  @typing.overload
335
337
  async def get(
@@ -1,4 +1,3 @@
1
- # mypy: disable-error-code="no-any-return"
2
1
  from __future__ import annotations
3
2
 
4
3
  import typing
@@ -7,7 +6,12 @@ from abc import ABC
7
6
  from pydantic import Field, validate_call
8
7
 
9
8
  from faceit.api.base import BaseResource, ModelPlaceholder
10
- from faceit.api.pagination import MaxItems, MaxItemsType
9
+ from faceit.api.pagination import (
10
+ AsyncPageIterator,
11
+ MaxItems,
12
+ MaxItemsType,
13
+ SyncPageIterator,
14
+ )
11
15
  from faceit.http import AsyncClient, SyncClient
12
16
  from faceit.models import ItemPage # noqa: TC001
13
17
  from faceit.types import (
@@ -78,9 +82,8 @@ class SyncGames(BaseGames[SyncClient], typing.Generic[APIResponseFormatT]):
78
82
  def all_items(
79
83
  self, max_items: MaxItemsType = MaxItems.SAFE
80
84
  ) -> typing.Union[typing.List[RawAPIItem], ItemPage[ModelNotImplemented]]:
81
- return self.__class__._sync_page_iterator.gather_pages(
82
- self.items, max_items=max_items
83
- )
85
+ iterator = SyncPageIterator(self.items, max_items=max_items)
86
+ return iterator.collect()
84
87
 
85
88
 
86
89
  @typing.final
@@ -132,6 +135,5 @@ class AsyncGames(BaseGames[AsyncClient], typing.Generic[APIResponseFormatT]):
132
135
  async def all_items(
133
136
  self, max_items: MaxItemsType = MaxItems.SAFE
134
137
  ) -> typing.Union[typing.List[RawAPIItem], ItemPage[ModelNotImplemented]]:
135
- return await self.__class__._async_page_iterator.gather_pages(
136
- self.items, max_items=max_items
137
- )
138
+ iterator = AsyncPageIterator(self.items, max_items=max_items)
139
+ return await iterator.collect()
@@ -1,4 +1,3 @@
1
- # mypy: disable-error-code="no-any-return"
2
1
  from __future__ import annotations
3
2
 
4
3
  import logging
@@ -15,8 +14,10 @@ from faceit.api.base import (
15
14
  RequestPayload,
16
15
  )
17
16
  from faceit.api.pagination import (
17
+ AsyncPageIterator,
18
18
  MaxItems,
19
19
  MaxItemsType,
20
+ SyncPageIterator,
20
21
  TimestampPaginationConfig,
21
22
  pages,
22
23
  )
@@ -34,6 +35,7 @@ from faceit.models import (
34
35
  Player,
35
36
  Tournament,
36
37
  )
38
+ from faceit.models.custom_types import NotStrictTimestampMs # noqa: TC001
37
39
  from faceit.models.players.general import AnyPlayerStats
38
40
  from faceit.models.players.match import AbstractMatchPlayerStats
39
41
  from faceit.types import (
@@ -44,7 +46,6 @@ from faceit.types import (
44
46
  Raw,
45
47
  RawAPIItem,
46
48
  RawAPIPageResponse,
47
- TimestampMillis,
48
49
  ValidUUID,
49
50
  )
50
51
  from faceit.utils import is_valid_uuid
@@ -237,9 +238,8 @@ class SyncPlayers(BasePlayers[SyncClient], typing.Generic[APIResponseFormatT]):
237
238
  def all_bans(
238
239
  self, player_id: PlayerID, max_items: MaxItemsType = MaxItems.SAFE
239
240
  ) -> typing.Union[typing.List[RawAPIItem], ItemPage[BanEntry]]:
240
- return self.__class__._sync_page_iterator.gather_pages(
241
- self.bans, player_id, max_items=max_items
242
- )
241
+ iterator = SyncPageIterator(self.bans, player_id, max_items=max_items)
242
+ return iterator.collect()
243
243
 
244
244
  @typing.overload
245
245
  def matches_stats(
@@ -249,8 +249,8 @@ class SyncPlayers(BasePlayers[SyncClient], typing.Generic[APIResponseFormatT]):
249
249
  *,
250
250
  offset: int = Field(0, ge=0, le=200),
251
251
  limit: int = Field(20, ge=1, le=100),
252
- start: typing.Optional[TimestampMillis] = None,
253
- to: typing.Optional[TimestampMillis] = None,
252
+ start: typing.Optional[NotStrictTimestampMs] = None,
253
+ to: typing.Optional[NotStrictTimestampMs] = None,
254
254
  ) -> RawAPIPageResponse: ...
255
255
 
256
256
  # TODO: This overload-based approach for specific games feels clunky and doesn't scale well.
@@ -264,20 +264,20 @@ class SyncPlayers(BasePlayers[SyncClient], typing.Generic[APIResponseFormatT]):
264
264
  *,
265
265
  offset: int = Field(0, ge=0, le=200),
266
266
  limit: int = Field(20, ge=1, le=100),
267
- start: typing.Optional[TimestampMillis] = None,
268
- to: typing.Optional[TimestampMillis] = None,
267
+ start: typing.Optional[NotStrictTimestampMs] = None,
268
+ to: typing.Optional[NotStrictTimestampMs] = None,
269
269
  ) -> ItemPage[CS2MatchPlayerStats]: ...
270
270
 
271
271
  @typing.overload
272
- def matches_stats(
272
+ def matches_stats( # Fallback
273
273
  self: SyncPlayers[Model],
274
274
  player_id: PlayerID,
275
275
  game: GameID,
276
276
  *,
277
277
  offset: int = Field(0, ge=0, le=200),
278
278
  limit: int = Field(20, ge=1, le=100),
279
- start: typing.Optional[TimestampMillis] = None,
280
- to: typing.Optional[TimestampMillis] = None,
279
+ start: typing.Optional[NotStrictTimestampMs] = None,
280
+ to: typing.Optional[NotStrictTimestampMs] = None,
281
281
  ) -> ItemPage[AbstractMatchPlayerStats]: ...
282
282
 
283
283
  @validate_call
@@ -288,8 +288,8 @@ class SyncPlayers(BasePlayers[SyncClient], typing.Generic[APIResponseFormatT]):
288
288
  *,
289
289
  offset: int = Field(0, ge=0, le=200),
290
290
  limit: int = Field(20, ge=1, le=100),
291
- start: typing.Optional[TimestampMillis] = None,
292
- to: typing.Optional[TimestampMillis] = None,
291
+ start: typing.Optional[NotStrictTimestampMs] = None,
292
+ to: typing.Optional[NotStrictTimestampMs] = None,
293
293
  ) -> typing.Union[
294
294
  RawAPIPageResponse,
295
295
  ItemPage[AbstractMatchPlayerStats],
@@ -341,12 +341,14 @@ class SyncPlayers(BasePlayers[SyncClient], typing.Generic[APIResponseFormatT]):
341
341
  ItemPage[CS2MatchPlayerStats],
342
342
  ItemPage[AbstractMatchPlayerStats],
343
343
  ]:
344
- return self.__class__._sync_page_iterator.gather_pages(
345
- self.matches_stats,
346
- player_id,
347
- game,
348
- max_items=max_items,
349
- unix=self.__class__._matches_stats_timestamp_cfg,
344
+ return SyncPageIterator.gather_from_iterator(
345
+ SyncPageIterator.unix(
346
+ self.matches_stats,
347
+ player_id,
348
+ game,
349
+ cfg=self.__class__._matches_stats_timestamp_cfg,
350
+ max_items=max_items,
351
+ )
350
352
  )
351
353
 
352
354
  @typing.overload
@@ -357,8 +359,8 @@ class SyncPlayers(BasePlayers[SyncClient], typing.Generic[APIResponseFormatT]):
357
359
  *,
358
360
  offset: int = Field(0, ge=0, le=1000),
359
361
  limit: int = Field(20, ge=1, le=100),
360
- start: typing.Optional[TimestampMillis] = None,
361
- to: typing.Optional[TimestampMillis] = None,
362
+ start: typing.Optional[NotStrictTimestampMs] = None,
363
+ to: typing.Optional[NotStrictTimestampMs] = None,
362
364
  ) -> RawAPIPageResponse: ...
363
365
 
364
366
  @typing.overload
@@ -369,8 +371,8 @@ class SyncPlayers(BasePlayers[SyncClient], typing.Generic[APIResponseFormatT]):
369
371
  *,
370
372
  offset: int = Field(0, ge=0, le=1000),
371
373
  limit: int = Field(20, ge=1, le=100),
372
- start: typing.Optional[TimestampMillis] = None,
373
- to: typing.Optional[TimestampMillis] = None,
374
+ start: typing.Optional[NotStrictTimestampMs] = None,
375
+ to: typing.Optional[NotStrictTimestampMs] = None,
374
376
  ) -> ItemPage[Match]: ...
375
377
 
376
378
  @validate_call
@@ -381,8 +383,8 @@ class SyncPlayers(BasePlayers[SyncClient], typing.Generic[APIResponseFormatT]):
381
383
  *,
382
384
  offset: int = Field(0, ge=0, le=1000),
383
385
  limit: int = Field(20, ge=1, le=100),
384
- start: typing.Optional[TimestampMillis] = None,
385
- to: typing.Optional[TimestampMillis] = None,
386
+ start: typing.Optional[NotStrictTimestampMs] = None,
387
+ to: typing.Optional[NotStrictTimestampMs] = None,
386
388
  ) -> typing.Union[RawAPIPageResponse, ItemPage[Match]]:
387
389
  return self._validate_response(
388
390
  self._client.get(
@@ -417,12 +419,14 @@ class SyncPlayers(BasePlayers[SyncClient], typing.Generic[APIResponseFormatT]):
417
419
  game: GameID,
418
420
  max_items: MaxItemsType = pages(50),
419
421
  ) -> typing.Union[typing.List[RawAPIItem], ItemPage[Match]]:
420
- return self.__class__._sync_page_iterator.gather_pages(
421
- self.history,
422
- player_id,
423
- game,
424
- max_items=max_items,
425
- unix=self.__class__._history_timestamp_cfg,
422
+ return SyncPageIterator.gather_from_iterator(
423
+ SyncPageIterator.unix(
424
+ self.history,
425
+ player_id,
426
+ game,
427
+ cfg=self.__class__._history_timestamp_cfg,
428
+ max_items=max_items,
429
+ )
426
430
  )
427
431
 
428
432
  @typing.overload
@@ -477,9 +481,8 @@ class SyncPlayers(BasePlayers[SyncClient], typing.Generic[APIResponseFormatT]):
477
481
  def all_hubs(
478
482
  self, player_id: PlayerID, max_items: MaxItemsType = MaxItems.SAFE
479
483
  ) -> typing.Union[typing.List[RawAPIItem], ItemPage[Hub]]:
480
- return self.__class__._sync_page_iterator.gather_pages(
481
- self.hubs, player_id, max_items=max_items
482
- )
484
+ iterator = SyncPageIterator(self.hubs, player_id, max_items=max_items)
485
+ return iterator.collect()
483
486
 
484
487
  @typing.overload
485
488
  def stats(
@@ -561,9 +564,8 @@ class SyncPlayers(BasePlayers[SyncClient], typing.Generic[APIResponseFormatT]):
561
564
  def all_teams(
562
565
  self, player_id: PlayerID, max_items: MaxItemsType = MaxItems.SAFE
563
566
  ) -> typing.Union[typing.List[RawAPIItem], ItemPage[GeneralTeam]]:
564
- return self.__class__._sync_page_iterator.gather_pages(
565
- self.teams, player_id, max_items=max_items
566
- )
567
+ iterator = SyncPageIterator(self.teams, player_id, max_items=max_items)
568
+ return iterator.collect()
567
569
 
568
570
  @typing.overload
569
571
  def tournaments(
@@ -617,9 +619,8 @@ class SyncPlayers(BasePlayers[SyncClient], typing.Generic[APIResponseFormatT]):
617
619
  def all_tournaments(
618
620
  self, player_id: PlayerID, max_items: MaxItemsType = MaxItems.SAFE
619
621
  ) -> typing.Union[typing.List[RawAPIItem], ItemPage[Tournament]]:
620
- return self.__class__._sync_page_iterator.gather_pages(
621
- self.tournaments, player_id, max_items=max_items
622
- )
622
+ iterator = SyncPageIterator(self.tournaments, player_id, max_items=max_items)
623
+ return iterator.collect()
623
624
 
624
625
 
625
626
  @typing.final
@@ -716,9 +717,8 @@ class AsyncPlayers(BasePlayers[AsyncClient], typing.Generic[APIResponseFormatT])
716
717
  async def all_bans(
717
718
  self, player_id: PlayerID, max_items: MaxItemsType = MaxItems.SAFE
718
719
  ) -> typing.Union[typing.List[RawAPIItem], ItemPage[BanEntry]]:
719
- return await self.__class__._async_page_iterator.gather_pages(
720
- self.bans, player_id, max_items=max_items
721
- )
720
+ iterator = AsyncPageIterator(self.bans, player_id, max_items=max_items)
721
+ return await iterator.collect()
722
722
 
723
723
  @typing.overload
724
724
  async def matches_stats(
@@ -728,8 +728,8 @@ class AsyncPlayers(BasePlayers[AsyncClient], typing.Generic[APIResponseFormatT])
728
728
  *,
729
729
  offset: int = Field(0, ge=0, le=200),
730
730
  limit: int = Field(20, ge=1, le=100),
731
- start: typing.Optional[TimestampMillis] = None,
732
- to: typing.Optional[TimestampMillis] = None,
731
+ start: typing.Optional[NotStrictTimestampMs] = None,
732
+ to: typing.Optional[NotStrictTimestampMs] = None,
733
733
  ) -> RawAPIPageResponse: ...
734
734
 
735
735
  @typing.overload
@@ -740,8 +740,8 @@ class AsyncPlayers(BasePlayers[AsyncClient], typing.Generic[APIResponseFormatT])
740
740
  *,
741
741
  offset: int = Field(0, ge=0, le=200),
742
742
  limit: int = Field(20, ge=1, le=100),
743
- start: typing.Optional[TimestampMillis] = None,
744
- to: typing.Optional[TimestampMillis] = None,
743
+ start: typing.Optional[NotStrictTimestampMs] = None,
744
+ to: typing.Optional[NotStrictTimestampMs] = None,
745
745
  ) -> ItemPage[CS2MatchPlayerStats]: ...
746
746
 
747
747
  @typing.overload
@@ -752,8 +752,8 @@ class AsyncPlayers(BasePlayers[AsyncClient], typing.Generic[APIResponseFormatT])
752
752
  *,
753
753
  offset: int = Field(0, ge=0, le=200),
754
754
  limit: int = Field(20, ge=1, le=100),
755
- start: typing.Optional[TimestampMillis] = None,
756
- to: typing.Optional[TimestampMillis] = None,
755
+ start: typing.Optional[NotStrictTimestampMs] = None,
756
+ to: typing.Optional[NotStrictTimestampMs] = None,
757
757
  ) -> ItemPage[AbstractMatchPlayerStats]: ...
758
758
 
759
759
  @validate_call
@@ -764,8 +764,8 @@ class AsyncPlayers(BasePlayers[AsyncClient], typing.Generic[APIResponseFormatT])
764
764
  *,
765
765
  offset: int = Field(0, ge=0, le=200),
766
766
  limit: int = Field(20, ge=1, le=100),
767
- start: typing.Optional[TimestampMillis] = None,
768
- to: typing.Optional[TimestampMillis] = None,
767
+ start: typing.Optional[NotStrictTimestampMs] = None,
768
+ to: typing.Optional[NotStrictTimestampMs] = None,
769
769
  ) -> typing.Union[
770
770
  RawAPIPageResponse,
771
771
  ItemPage[AbstractMatchPlayerStats],
@@ -817,12 +817,14 @@ class AsyncPlayers(BasePlayers[AsyncClient], typing.Generic[APIResponseFormatT])
817
817
  ItemPage[AbstractMatchPlayerStats],
818
818
  ItemPage[CS2MatchPlayerStats],
819
819
  ]:
820
- return await self.__class__._async_page_iterator.gather_pages(
821
- self.matches_stats,
822
- player_id,
823
- game,
824
- max_items=max_items,
825
- unix=self.__class__._matches_stats_timestamp_cfg,
820
+ return await AsyncPageIterator.gather_from_iterator(
821
+ AsyncPageIterator.unix(
822
+ self.matches_stats,
823
+ player_id,
824
+ game,
825
+ cfg=self.__class__._matches_stats_timestamp_cfg,
826
+ max_items=max_items,
827
+ )
826
828
  )
827
829
 
828
830
  @typing.overload
@@ -833,8 +835,8 @@ class AsyncPlayers(BasePlayers[AsyncClient], typing.Generic[APIResponseFormatT])
833
835
  *,
834
836
  offset: int = Field(0, ge=0, le=1000),
835
837
  limit: int = Field(20, ge=1, le=100),
836
- start: typing.Optional[TimestampMillis] = None,
837
- to: typing.Optional[TimestampMillis] = None,
838
+ start: typing.Optional[NotStrictTimestampMs] = None,
839
+ to: typing.Optional[NotStrictTimestampMs] = None,
838
840
  ) -> RawAPIPageResponse: ...
839
841
 
840
842
  @typing.overload
@@ -845,8 +847,8 @@ class AsyncPlayers(BasePlayers[AsyncClient], typing.Generic[APIResponseFormatT])
845
847
  *,
846
848
  offset: int = Field(0, ge=0, le=1000),
847
849
  limit: int = Field(20, ge=1, le=100),
848
- start: typing.Optional[TimestampMillis] = None,
849
- to: typing.Optional[TimestampMillis] = None,
850
+ start: typing.Optional[NotStrictTimestampMs] = None,
851
+ to: typing.Optional[NotStrictTimestampMs] = None,
850
852
  ) -> ItemPage[Match]: ...
851
853
 
852
854
  @validate_call
@@ -857,8 +859,8 @@ class AsyncPlayers(BasePlayers[AsyncClient], typing.Generic[APIResponseFormatT])
857
859
  *,
858
860
  offset: int = Field(0, ge=0, le=1000),
859
861
  limit: int = Field(20, ge=1, le=100),
860
- start: typing.Optional[TimestampMillis] = None,
861
- to: typing.Optional[TimestampMillis] = None,
862
+ start: typing.Optional[NotStrictTimestampMs] = None,
863
+ to: typing.Optional[NotStrictTimestampMs] = None,
862
864
  ) -> typing.Union[RawAPIPageResponse, ItemPage[Match]]:
863
865
  return self._validate_response(
864
866
  await self._client.get(
@@ -893,12 +895,14 @@ class AsyncPlayers(BasePlayers[AsyncClient], typing.Generic[APIResponseFormatT])
893
895
  game: GameID,
894
896
  max_items: MaxItemsType = pages(50),
895
897
  ) -> typing.Union[typing.List[RawAPIItem], ItemPage[Match]]:
896
- return await self.__class__._async_page_iterator.gather_pages(
897
- self.history,
898
- player_id,
899
- game,
900
- max_items=max_items,
901
- unix=self.__class__._history_timestamp_cfg,
898
+ return await AsyncPageIterator.gather_from_iterator(
899
+ AsyncPageIterator.unix(
900
+ self.history,
901
+ player_id,
902
+ game,
903
+ cfg=self.__class__._history_timestamp_cfg,
904
+ max_items=max_items,
905
+ )
902
906
  )
903
907
 
904
908
  @typing.overload
@@ -953,9 +957,8 @@ class AsyncPlayers(BasePlayers[AsyncClient], typing.Generic[APIResponseFormatT])
953
957
  async def all_hubs(
954
958
  self, player_id: PlayerID, max_items: MaxItemsType = MaxItems.SAFE
955
959
  ) -> typing.Union[typing.List[RawAPIItem], ItemPage[Hub]]:
956
- return await self.__class__._async_page_iterator.gather_pages(
957
- self.hubs, player_id, max_items=max_items
958
- )
960
+ iterator = AsyncPageIterator(self.hubs, player_id, max_items=max_items)
961
+ return await iterator.collect()
959
962
 
960
963
  @typing.overload
961
964
  async def stats(
@@ -1037,9 +1040,8 @@ class AsyncPlayers(BasePlayers[AsyncClient], typing.Generic[APIResponseFormatT])
1037
1040
  async def all_teams(
1038
1041
  self, player_id: PlayerID, max_items: MaxItemsType = MaxItems.SAFE
1039
1042
  ) -> typing.Union[typing.List[RawAPIItem], ItemPage[GeneralTeam]]:
1040
- return await self.__class__._async_page_iterator.gather_pages(
1041
- self.teams, player_id, max_items=max_items
1042
- )
1043
+ iterator = AsyncPageIterator(self.teams, player_id, max_items=max_items)
1044
+ return await iterator.collect()
1043
1045
 
1044
1046
  @typing.overload
1045
1047
  async def tournaments(
@@ -1093,6 +1095,5 @@ class AsyncPlayers(BasePlayers[AsyncClient], typing.Generic[APIResponseFormatT])
1093
1095
  async def all_tournaments(
1094
1096
  self, player_id: PlayerID, max_items: MaxItemsType = MaxItems.SAFE
1095
1097
  ) -> typing.Union[typing.List[RawAPIItem], ItemPage[Tournament]]:
1096
- return await self.__class__._async_page_iterator.gather_pages(
1097
- self.tournaments, player_id, max_items=max_items
1098
- )
1098
+ iterator = AsyncPageIterator(self.tournaments, player_id, max_items=max_items)
1099
+ return await iterator.collect()
@@ -1,4 +1,3 @@
1
- # mypy: disable-error-code="no-any-return"
2
1
  from __future__ import annotations
3
2
 
4
3
  import typing
@@ -7,7 +6,12 @@ from abc import ABC
7
6
  from pydantic import Field, validate_call
8
7
 
9
8
  from faceit.api.base import BaseResource, ModelPlaceholder
10
- from faceit.api.pagination import MaxItemsType, pages
9
+ from faceit.api.pagination import (
10
+ AsyncPageIterator,
11
+ MaxItemsType,
12
+ SyncPageIterator,
13
+ pages,
14
+ )
11
15
  from faceit.constants import GameID # noqa: TC001
12
16
  from faceit.http import AsyncClient, SyncClient
13
17
  from faceit.models import ItemPage # noqa: TC001
@@ -107,9 +111,10 @@ class SyncRankings(BaseRankings[SyncClient], typing.Generic[APIResponseFormatT])
107
111
  country: typing.Optional[CountryCode] = None,
108
112
  max_items: MaxItemsType = pages(10),
109
113
  ) -> typing.Union[typing.List[RawAPIItem], ItemPage[ModelNotImplemented]]:
110
- return self.__class__._sync_page_iterator.gather_pages(
114
+ iterator = SyncPageIterator(
111
115
  self.unbounded, game, region, country, max_items=max_items
112
116
  )
117
+ return iterator.collect()
113
118
 
114
119
  @typing.overload
115
120
  def player(
@@ -230,9 +235,10 @@ class AsyncRankings(BaseRankings[AsyncClient], typing.Generic[APIResponseFormatT
230
235
  country: typing.Optional[CountryCode] = None,
231
236
  max_items: MaxItemsType = pages(10),
232
237
  ) -> typing.Union[typing.List[RawAPIItem], ItemPage[ModelNotImplemented]]:
233
- return await self.__class__._async_page_iterator.gather_pages(
238
+ iterator = AsyncPageIterator(
234
239
  self.unbounded, game, region, country, max_items=max_items
235
240
  )
241
+ return await iterator.collect()
236
242
 
237
243
  @typing.overload
238
244
  async def player(