Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
db230be
[github-actions] Bump codecov/codecov-action from 5 to 6
dependabot[bot] Mar 26, 2026
b98b447
chore: add .osgrep to gitignore
AndreiDrang Apr 5, 2026
6765f19
docs: rewrite AGENTS.md tree with init-agents
AndreiDrang Apr 5, 2026
675d816
Update AGENTS.md
AndreiDrang Apr 21, 2026
e8069ff
Update AGENTS.md
AndreiDrang Apr 21, 2026
da8635e
Update ARCHITECTURE.md
AndreiDrang Apr 21, 2026
20af6d5
Update AGENTS.md
AndreiDrang Apr 21, 2026
d448254
Bump idna from 3.11 to 3.15
dependabot[bot] May 19, 2026
d702bbc
Merge pull request #169 from AndreiDrang/dependabot/uv/idna-3.15
AndreiDrang May 22, 2026
e832ca7
Merge pull request #168 from AndreiDrang/dependabot/github_actions/co…
AndreiDrang May 22, 2026
c702036
[github-actions] Bump codecov/codecov-action from 6 to 7
dependabot[bot] Jun 8, 2026
26a43d3
[github-actions] Bump actions/checkout from 6 to 7
dependabot[bot] Jun 18, 2026
334a085
Update msgspec requirement from <=0.21,>=0.18 to >=0.18,<=0.21.1
dependabot[bot] Jun 29, 2026
add80b4
Merge pull request #172 from AndreiDrang/dependabot/pip/msgspec-gte-0…
AndreiDrang Jul 23, 2026
e3b4e6b
Merge pull request #171 from AndreiDrang/dependabot/github_actions/ac…
AndreiDrang Jul 23, 2026
7464c39
Merge pull request #170 from AndreiDrang/dependabot/github_actions/co…
AndreiDrang Jul 23, 2026
6559291
docs: add Spanish README
webbrain-one Aug 8, 2026
9c16324
Merge pull request #173 from webbrain-one/webbrain/readme-translation
AndreiDrang Aug 8, 2026
d7c7103
Relax msgspec upper bound for Python 3.15
Filimoa Oct 7, 2026
8ffee29
Merge pull request #175 from Filimoa/fix/relax-msgspec-python315
AndreiDrang Oct 7, 2026
57e07fe
fix(config): publish to pypi with api token
AndreiDrang Oct 7, 2026
9b24330
chore(release): bump version to 1.2.1
AndreiDrang Oct 7, 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
2 changes: 1 addition & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ jobs:
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"]

steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
- name: Set up uv
uses: astral-sh/setup-uv@v7
with:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/build_sphinx.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ jobs:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
- name: Set up uv
uses: astral-sh/setup-uv@v7
with:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/install.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ jobs:
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"]

steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
- name: Set up uv
uses: astral-sh/setup-uv@v7
with:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ jobs:
python-version: ["3.12"]

steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
- name: Set up uv
uses: astral-sh/setup-uv@v7
with:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/sphinx.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ jobs:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
- name: Set up uv
uses: astral-sh/setup-uv@v7
with:
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ jobs:
python-version: ["3.12"]

steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
- name: Set up uv
uses: astral-sh/setup-uv@v7
with:
Expand All @@ -44,7 +44,7 @@ jobs:
run: make tests

- name: Upload coverage to Codecov
uses: codecov/codecov-action@v5
uses: codecov/codecov-action@v7
with:
token: ${{ secrets.CODECOV_TOKEN }}
files: ${{github.workspace}}/coverage/coverage.xml
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/zai-code-bot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v6
uses: actions/checkout@v7

- name: Run Z.ai Bot
uses: AndreiDrang/zai-code-bot@main
Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ instance/*
!instance/.gitignore
.webassets-cache
.env
.pypi-token

### Flask.Python Stack ###
# Byte-compiled / optimized / DLL files
Expand Down Expand Up @@ -430,3 +431,4 @@ env/
/.coverage
/src/python3_capsolver.egg-info/
/docs/_build/
.osgrep
123 changes: 60 additions & 63 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -1,72 +1,69 @@
# PROJECT KNOWLEDGE BASE
# AGENTS.md

**Generated:** 2026-03-15
**Commit:** b797332
**Branch:** main
## Repository overview

## OVERVIEW
Python 3.8+ library for Capsolver service API. Dual sync (`requests`) / async (`aiohttp`) support. `msgspec` for serialization, `tenacity` for retries.
Python 3.8+ client library for the Capsolver captcha-solving API. Single-package `src/` layout, dual sync (`requests`) / async (`aiohttp`) execution, `msgspec` for serialization, `tenacity` for async retries.

## STRUCTURE
```
## Where to work

```text
./
├── src/python3_capsolver/ # Main library (service implementations)
│ ├── core/ # Base classes, instruments, serializers
│ └── *.py # Service-specific (ReCaptcha, Cloudflare, etc.)
├── tests/ # Pytest suite (matches source structure)
├── docs/ # Sphinx documentation
├── ARCHITECTURE.md # System architecture (matklad-style)
└── pyproject.toml # Build, uv, pytest, black/isort config
├── src/python3_capsolver/ # Main library
│ ├── core/ # Base classes, instruments, serializers, enums
│ ├── control.py # Raw API: get_balance, create_task, get_task_result
│ ├── recaptcha.py # ReCaptcha V2/V3/Enterprise
│ ├── cloudflare.py # Cloudflare Turnstile/Challenge
│ └── *.py # Other captcha services (see package AGENTS.md)
├── tests/ # Pytest suite mirroring source structure
│ ├── conftest.py # BaseTest class, fixtures, rate-limiting delays
│ └── test_*.py # One file per service + test_core.py + test_instrument.py
├── docs/ # Sphinx documentation (make doc)
├── ARCHITECTURE.md # Layered architecture, data flow, invariants
├── pyproject.toml # Build, deps, black/isort/pytest config
└── Makefile # make tests, make refactor, make build, make doc
```

## WHERE TO LOOK
| Task | Location | Notes |
|------|----------|-------|
| **Architecture** | `ARCHITECTURE.md` | Layered design, invariants, life of a request |
| **Base Logic** | `src/python3_capsolver/core/` | `base.py`, `serializer.py`, `enum.py`, instruments |
| **Service Implementations** | `src/python3_capsolver/*.py` | `recaptcha.py`, `cloudflare.py`, `control.py` |
| **Tests** | `tests/` | `conftest.py` (BaseTest, fixtures), per-service tests |
| **Configuration** | `pyproject.toml` | uv, black (120), isort, pytest (asyncio auto) |
| **Commands** | `Makefile` | `make tests`, `make build`, `make upload` |

## CONVENTIONS
- **Toolchain**: `uv` for package management (`uv sync`, `uv run`, `uv build`, `uv publish`)
- **Formatter**: `black` (line-length 120), `isort` (profile "black")
- **Cleanup**: `autoflake` (remove unused imports/variables)
- **Serialization**: `msgspec` (not `json`) for performance
- **Concurrency**: Dual sync/async required for all instruments
- **Retries**: `tenacity` (async), `requests.Retry` (sync) — 5 attempts, exponential backoff
- **Testing**: pytest 7.0+, `pytest-asyncio` (auto mode), rate-limiting fixtures (1s func, 2s class)

## ANTI-PATTERNS (THIS PROJECT)
- **Empty `__init__.py` files**: `src/python3_capsolver/__init__.py` only exports `__version__`; `core/__init__.py` is completely empty. Users must import via full paths (`from python3_capsolver.recaptcha import ReCaptcha`)
- **AGENTS.md in package dirs**: Will ship with distribution unless excluded in `pyproject.toml`
- **No CLI entry points**: Library-only, no console_scripts defined

## UNIQUE STYLES
- **Service Pattern**: Each captcha service inherits from `CaptchaParams` with `captcha_handler()` (sync) + `aio_captcha_handler()` (async)
- **Task Payload**: Dict merged with internal params, passed to `create_task()` API
- **Context Managers**: All services support `with` / `async with` for session cleanup
- **Test Duplication**: Every sync test (`def test_*`) has async counterpart (`async def test_aio_*`)

## COMMANDS
## Architecture and boundaries

Four layers with strict dependency direction (top → bottom only):

1. **Service layer** (`src/python3_capsolver/*.py`) — thin wrappers inheriting `CaptchaParams`, zero HTTP logic
2. **Base layer** (`core/base.py`) — `CaptchaParams` merges payloads, delegates to instruments
3. **Instrument layer** (`core/*_instrument.py`) — `SIOCaptchaInstrument` (sync) and `AIOCaptchaInstrument` (async) handle all HTTP, retries, and polling
4. **Support layer** (`core/serializer.py`, `core/enum.py`, `core/const.py`) — `msgspec.Struct` classes, enums, constants

**Forbidden**: service files importing `requests`/`aiohttp` directly; support layer depending on upper layers.

## Change rules

- Every new captcha service must inherit `CaptchaParams`, provide `captcha_handler()` + `aio_captcha_handler()`, and register its type in `CaptchaTypeEnm`
- All serialization must use `msgspec.Struct` — never raw `json` module
- Dual sync/async is mandatory for any new instrument or service
- Service classes are thin: only `__init__` with captcha-type-specific params; all HTTP goes through instruments
- Context manager support (`with` / `async with`) is required on all service classes

## Validation

```bash
# Development
uv sync --all-groups # Install all dependencies
uv run pytest tests/ # Run tests
uv run black src/ tests/ # Format
uv run isort src/ tests/ # Sort imports

# Build & Publish
uv build # Build wheel/sdist
uv publish # Upload to PyPI

# Documentation
cd docs/ && uv run --group docs make html -e
make tests # pytest + coverage (HTML + XML)
make refactor # autoflake + black + isort on src/ and tests/
make lint # autoflake --check, black --check, isort --check-only
uv run pytest tests/ -k <name> # run specific tests
```

## NOTES
- **API Key**: Tests require `API_KEY` environment variable
- **Coverage**: HTML reports in `coverage/html/`, XML in `coverage/coverage.xml`
- **Python Support**: 3.8–3.12 (tested via `target-version = ['py310']`)
- **Dependencies**: `requests>=2.21.0`, `aiohttp>=3.9.2`, `msgspec>=0.18,<=0.21`, `tenacity>=8,<10`
Tests require the `API_KEY` environment variable. Rate-limiting fixtures (`delay_func` 1s, `delay_class` 2s) prevent API throttling.

## Key docs

- `ARCHITECTURE.md` — full system map, data flow, invariants
- `src/python3_capsolver/AGENTS.md` — service-level conventions
- `src/python3_capsolver/core/AGENTS.md` — core module internals
- `tests/AGENTS.md` — test patterns and fixtures

## Repository-specific gotchas

- **Empty `__init__.py` files**: users import via full path (`from python3_capsolver.recaptcha import ReCaptcha`), never from top-level package
- **`AGENTS.md` in package dirs**: these ship with the wheel unless excluded in `pyproject.toml` — do not add more inside `src/`
- **`control.py` is the largest file** (~431 lines) and provides direct API access without the captcha-handling abstraction
- **Toolchain is `uv`**: use `uv run`, `uv sync`, `uv build` — not bare `pip` or `pytest`
- **`captcha_instrument.py` is ~221 lines**: contains both `CaptchaInstrumentBase` and `FileInstrument`; edits here affect all services
Loading
Loading