Skip to content
Merged

V3.0 #20

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
1fde371
correct changelog improvement reference for CI/CD
ZEDGR May 23, 2026
bbd81c6
add ruff lint and format check to CI
ZEDGR May 23, 2026
4d28bef
remove requirements.txt, superseded by uv.lock
ZEDGR May 23, 2026
15d52c1
fix error handling in fetch() to re-raise non-422 errors with origina…
ZEDGR May 24, 2026
f918bf9
simplify _parse() to only convert _at fields to datetime, drop float …
ZEDGR May 24, 2026
1f41b5a
validate envelope structure in _parse() with a clear error instead of…
ZEDGR May 24, 2026
1529dce
replace json.loads(response.text) with response.json(), drop unused i…
ZEDGR May 24, 2026
8c493bc
update README: use h2 headings, simplify installation section
ZEDGR May 28, 2026
de68821
switch to pytest, add to dev dependencies
ZEDGR May 28, 2026
d6d54c8
refactor tests to pytest style, update ci to use pytest
ZEDGR May 28, 2026
57bd879
add dataclass models for tournament, participant, match, attachment
ZEDGR May 28, 2026
8f85de7
implement client architecture, replace global state with Client and A…
ZEDGR May 28, 2026
294c59c
update tests to use Client, switch to attribute access on dataclasses
ZEDGR May 28, 2026
635ad39
ruff format client and domain modules
ZEDGR May 28, 2026
8e2861a
ruff format tests, remove unused AsyncClient import
ZEDGR May 28, 2026
d58a49a
update readme for v3.0 client api, async usage, pytest
ZEDGR May 28, 2026
57d7891
add maintainer credit to readme
ZEDGR May 28, 2026
1bac3a1
add async smoke tests, add pytest-asyncio dev dependency
ZEDGR May 28, 2026
c0b4167
bump version to 3.0.0, note phases 3+4 move to v4.0
ZEDGR Jun 2, 2026
5015769
update changelog for v3.0.0
ZEDGR Jun 2, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 20 additions & 1 deletion .github/workflows/test_suite.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,25 @@ name: Test Suite
on: [push]

jobs:
lint:
name: Lint
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v6
- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: "3.12"
- name: Install uv
uses: astral-sh/setup-uv@v8.1.0
- name: Install dependencies
run: uv sync
- name: Ruff check
run: uv run ruff check .
- name: Ruff format
run: uv run ruff format --check .

tests:
name: Python ${{ matrix.python-version }}
runs-on: ubuntu-latest
Expand All @@ -27,4 +46,4 @@ jobs:
env:
CHALLONGE_USER: ${{ secrets.CHALLONGE_USER }}
CHALLONGE_KEY: ${{ secrets.CHALLONGE_KEY }}
run: uv run python -m unittest tests.py -v
run: uv run pytest tests.py -v
23 changes: 22 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,26 @@
# Release History

## 3.0.0 (2026-06-02)

**Breaking Changes**

- Replace module-level functions with a `Client` / `AsyncClient` class — all calls now go through an instance (`client.tournaments.show(...)` instead of `challonge.tournaments.show(...)`)
- API responses are now typed dataclasses (`Tournament`, `Participant`, `Match`, `MatchAttachment`) instead of plain dicts — use attribute access (`t.name`) instead of key access (`t["name"]`)
- `set_credentials()`, `set_timezone()`, and other module-level state helpers removed — pass `user`, `api_key`, and `timezone` to the `Client` constructor instead
- `fetch()` / `fetch_and_parse()` are no longer public

**New Features**

- `AsyncClient` with full async/await support via `httpx.AsyncClient` — all domain methods are awaitable
- Context manager support: `with Client(...) as client` and `async with AsyncClient(...) as client`
- `timezone` parameter on `Client` / `AsyncClient` accepts IANA timezone strings (e.g. `"Asia/Seoul"`)
- New `models.py` module with `Tournament`, `Participant`, `Match`, and `MatchAttachment` dataclasses

**Improvements**

- Switch from unittest to pytest
- Add async smoke tests covering all four resource domains

## 2.0.0 (2026-05-24)

**Breaking Changes**
Expand All @@ -22,8 +43,8 @@
- Add docstrings to all modules
- Migrate from Poetry to uv for package management
- Replace `setup.py` with `pyproject.toml` using hatchling as build backend
- Replace Travis CI/CD with GitHub Actions
- Add GitHub Actions workflow for publishing to PyPI
- Update GitHub Actions to Node.js 24 compatible versions

**Bugfixes**

Expand Down
89 changes: 55 additions & 34 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,50 +1,74 @@
# pychallonge

Lightweight Python wrapper for the [Challonge API](http://api.challonge.com/v1).
The pychallonge module was created by [Russ Amos](https://github.com/russ-)
The pychallonge module was created by [Russ Amos](https://github.com/russ-) and maintained by [George Lemanis](https://github.com/ZEDGR)

# Python version support
## Python version support

- `3.10+`
- 3.10 or later

# Installation
## Installation

For the stable version
The `pychallonge` package is available on PyPI and you can install it through your favorite package manager:

pip install pychallonge

For latest development

pip install -e git+https://github.com/ZEDGR/pychallonge#egg=pychallonge

# Usage
## Usage

```python
import challonge
from challonge import Client

# Tell pychallonge about your [Challonge API credentials](http://api.challonge.com/v1).
challonge.set_credentials("your_challonge_username", "your_api_key")
# Create a client with your Challonge API credentials.
client = Client(user="your_challonge_username", api_key="your_api_key")

# Retrieve a tournament by its id (or its url).
tournament = challonge.tournaments.show(3272)
tournament = client.tournaments.show(3272)

# Tournaments, matches, and participants are all represented as normal Python dicts.
print(tournament["id"]) # 3272
print(tournament["name"]) # My Awesome Tournament
print(tournament["started_at"]) # None
# Tournaments, matches, and participants are returned as typed dataclasses.
print(tournament.id) # 3272
print(tournament.name) # My Awesome Tournament
print(tournament.started_at) # None

# Retrieve the participants for a given tournament.
participants = challonge.participants.index(tournament["id"])
print(len(participants)) # 13
participants = client.participants.index(tournament.id)
print(len(participants)) # 13

# Mutations (POST/PUT) return the updated resource directly.
tournament = challonge.tournaments.start(tournament["id"])
print(tournament["started_at"]) # 2011-07-31 16:16:02-04:00
tournament = client.tournaments.start(tournament.id)
print(tournament.started_at) # 2011-07-31 16:16:02-04:00

# Close the client when done, or use it as a context manager.
client.close()
```

### Context manager

```python
with Client(user="your_challonge_username", api_key="your_api_key") as client:
tournament = client.tournaments.show(3272)
```

### Async

```python
from challonge import AsyncClient

async with AsyncClient(user="your_challonge_username", api_key="your_api_key") as client:
tournament = await client.tournaments.show(3272)
participants = await client.participants.index(tournament.id)
```

### Timezone

By default datetime fields are normalised to your machine's local timezone. Pass a timezone string to override:

```python
client = Client(user="your_challonge_username", api_key="your_api_key", timezone="UTC")
```

See [challonge.com](http://api.challonge.com/v1) for full API documentation.

# API Issues
## API Issues

The Challonge API has some issues with the attachments endpoints. When uploading
an attachment with a file (asset), the API returns a 500 internal server error.
Expand All @@ -54,26 +78,23 @@ The check-in undo endpoint has unexpected behaviour: the `checked_in` field in
the API response remains `True` even after a successful undo. The participant is
correctly marked as not checked in on the website.

Datetime fields from the API carry inconsistent timezone offsets. Pychallonge
normalises these to your machine's local timezone. You can also set a specific
timezone with the `set_timezone` function.

# Running the tests
## Running the tests

Tests make real API calls and require a Challonge account. Set `CHALLONGE_USER`
and `CHALLONGE_KEY` in your environment before running.

$ git clone https://github.com/ZEDGR/pychallonge
$ cd pychallonge
$ CHALLONGE_USER=my_user CHALLONGE_KEY=my_api_key uv run python -m unittest tests.py
$ CHALLONGE_USER=my_user CHALLONGE_KEY=my_api_key uv run pytest tests.py -v

Note that several tournaments are created and destroyed over the course of the
tests. If any test fails mid-run, orphaned tournaments can be cleaned up as follows:

```python
import challonge
challonge.set_credentials("my_user", "my_api_key")
for t in challonge.tournaments.index():
if t["name"].startswith("pychal"):
challonge.tournaments.destroy(t["id"])
from challonge import Client

with Client(user="my_user", api_key="my_api_key") as client:
for t in client.tournaments.index():
if t.name.startswith("pychal"):
client.tournaments.destroy(t.id)
```
Loading