Metadata-Version: 2.4
Name: eternalreturn
Version: 0.1.0
Summary: Unofficial Python SDK for the Eternal Return Open API.
Keywords: eternal-return,api,sdk,asyncio
Author: Ryu Juheon
License-Expression: MIT
License-File: LICENSE
Classifier: Framework :: AsyncIO
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Typing :: Typed
Requires-Dist: aiohttp>=3.14.5,<4
Requires-Python: >=3.11
Project-URL: Issues, https://github.com/intempesta/eternalreturnpy/issues
Project-URL: Repository, https://github.com/intempesta/eternalreturnpy
Description-Content-Type: text/markdown

# eternalreturn

Unofficial Python SDK for the [Eternal Return Open API](https://developer.eternalreturn.io/).
Built with `aiohttp`, with async and sync clients, typed dataclass models,
pagination, and automatic rate-limit retries. Requires Python 3.11+.

## Installation

Using uv:

```sh
uv add eternalreturn
```

Or with pip:

```sh
pip install eternalreturn
```

Get an API key from the developer portal and set the `ETERNALRETURN_API_KEY`
environment variable before running the examples.

## Async

```python
import asyncio
import os

from eternalreturn import EternalReturn


async def main() -> None:
    async with EternalReturn(os.environ["ETERNALRETURN_API_KEY"]) as client:
        response = await client.get_user("Player")
        if response.user is not None:
            games = await client.get_user_games(response.user.uid)
            for game in games.user_games or []:
                print(game.game_id, game.game_rank)


asyncio.run(main())
```

## Sync

```python
import os

from eternalreturn import EternalReturn

with EternalReturn.sync(os.environ["ETERNALRETURN_API_KEY"]) as client:
    response = client.get_user("Player")
    if response.user is not None:
        print(response.user.uid)
```

Use the async client inside an event loop. Context managers close owned sessions;
sessions supplied through `session=` remain owned by the caller.

## Pagination

Use a UID from `get_user()` while the client is open:

```python
async with client.user_games(uid) as pages:
    async for page in pages:
        for game in page.user_games or []:
            print(game.game_id)
```

For manual paging, `await pages.next()` returns a page or `None` when exhausted.
With a sync client, use `with`, `for`, and `pages.next()`.
Failed requests preserve the cursor for retry. Missing or null `userGames` raises
`InvalidResponseError`; an empty list ends pagination normally.

## Behavior

- Responses use typed dataclasses with `snake_case` fields. Unknown fields are
  preserved in `.extra`, and designated JSON string fields are decoded automatically.
- Clients support users, matches, rankings, statistics, union teams, game data,
  localization URLs, and recommended routes.
- Defaults: `timeout=30.0`, `request_interval=1.0`, `max_retries=3`.
  Rate-limit retries honor `Retry-After` and share a cooldown within each client.
  Retry waits are outside the per-request timeout.
- Transport, API, and decoding failures inherit from `EternalReturnError`.
  `ModelDecodeError` also inherits from `ValueError` and exposes the failing `.path`.

## Development

```sh
uv sync --locked
uv run pytest
uv run ruff check .
uv run ruff format --check .
uv run mypy
uv run pyright
uv build
```

## License

[MIT](LICENSE)
