Skip to content

2.0.3#158

Merged
NandaScott merged 3 commits intomainfrom
develop
Apr 7, 2026
Merged

2.0.3#158
NandaScott merged 3 commits intomainfrom
develop

Conversation

@NandaScott
Copy link
Copy Markdown
Owner

This pull request introduces robust, automatic per-endpoint rate limiting to the Scrython library, aligning with Scryfall's documented API limits. It also improves error handling, updates documentation, and cleans up some internal implementation details. The most important changes are grouped below:

Rate Limiting Improvements:

  • Added automatic per-endpoint rate limiting: endpoints like Search, Named, Random, and Collection now enforce a stricter 2 requests/second limit, while all others default to 10 requests/second. Each endpoint category uses its own global rate limiter instance, ensuring independent throttling. (scrython/cards/cards.py, scrython/rate_limiter.py, README.md, CHANGELOG.md, CLAUDE.md) [1] [2] [3] [4] [5] [6] [7] [8] [9]
  • Introduced SlowRateLimiter for endpoints requiring stricter limits and added the _rate_limiter_class mechanism to endpoint classes for tier assignment. (scrython/cards/cards.py, scrython/rate_limiter.py, CLAUDE.md) [1] [2] [3] [4] [5]
  • The rate_limit_per_second kwarg now creates a per-instance limiter, scoped to the handler's lifecycle. Passing this kwarg to iter_all() emits a warning and is ignored; it must be set at construction time. (scrython/base.py, scrython/base_mixins.py, README.md, CHANGELOG.md) [1] [2] [3] [4] [5] [6] [7]
  • Updated documentation to reflect the new rate limiting behavior and usage patterns, including override and disabling options. (README.md, CLAUDE.md, CHANGELOG.md) [1] [2] [3] [4] [5]

Error Handling and Internal Improvements:

  • Improved error handling for HTTP errors: now parses Scryfall's JSON error responses and raises ScryfallError with detailed information. (scrython/base.py)
  • Minor fixes and code cleanup, including safer dictionary access and docstring updates. (scrython/base.py, gen_docs.py, scripts/update_version_for_testpypi.py) [1] [2] [3] [4]

Workflow and Miscellaneous:

  • Removed the unused .github/workflows/claude-code-review.yml workflow file.

These changes ensure Scrython is compliant with Scryfall's API usage policies by default and provide users with more granular control over request throttling.

NandaScott and others added 3 commits April 6, 2026 22:22
* feat: add per-endpoint rate limiting for Scryfall API

Scryfall now enforces tiered rate limits: 2/s for /cards/search,
/cards/named, /cards/random, /cards/collection, and 10/s for all
other endpoints. The four slow endpoints share a single 2/s budget.

- Add SlowRateLimiter subclass with 2.0 calls/second default
- Change RateLimiter singleton to per-class registry so each rate
  tier maintains independent state
- Add _rate_limiter_class to ScrythonRequestHandler so endpoint
  classes declare their rate tier
- Assign SlowRateLimiter to Search, Named, Random, Collection
- rate_limit_per_second kwarg still overrides per-request
- rate_limit=False still disables entirely
- No public API changes; existing user code works without modification

* Formatting

* fix: share rate limiter state for rate_limit_per_second overrides

The rate_limit_per_second kwarg was creating a new RateLimiter per
request, so consecutive calls never throttled against each other.
Use a shared registry keyed by (class, rate) so custom rates enforce
across calls. Also rename reset_global_limiter to reset_all_limiters
to reflect that it clears the entire registry.

* docs: add comment explaining limiter coordination trade-off

* docs: add Scryfall rate limit docs link

* test: widen timing assertion upper bounds for CI reliability

* style: revert assert formatting to multi-line form

* docs: update README with per-endpoint rate limiting details

* Add indirection class method for reset_global_limiter

* Add general override key to registry

* refactor: move rate_limit_per_second to per-instance limiter

Instead of caching override limiters in the global registry (which
grows unbounded with unique rate values), store the override limiter
on the handler instance. This keeps pagination throttled within a
single handler while avoiding an unbounded global cache.

Removes get_global_limiter_for_rate() and simplifies the registry
type back to dict[type, RateLimiter].

* test: widen all timing test upper bounds for CI reliability

Lower bounds prove correctness (rate limiting works). Upper bounds
only assert "not absurdly slow" and should be generous enough to
pass on heavily loaded CI runners.

* docs: fix get_global_limiter docstring to include base class

* docs: document rate_limit_per_second per-instance semantics

Add __init__ docstring explaining that rate_limit_per_second creates
a per-instance limiter scoped to the handler's lifecycle. Also widen
test_rate_limit_can_be_disabled upper bound for CI reliability.

* fix: add deprecation shim for get_global_limiter param and test override throttling

- get_global_limiter() now accepts the old calls_per_second param
  with a DeprecationWarning (previously would TypeError)
- Add test proving per-instance override limiter throttles repeated
  calls within the same handler (e.g., pagination)
- Remove stale rate_limit_per_second reference from iter_all docstring

* fix: warn when rate_limit_per_second is passed to iter_all

The kwarg is only effective at construction time. Passing it to
iter_all() was silently ignored. Now emits a UserWarning and strips
the kwarg. Also adds a note to the README that the override is
scoped to the handler instance.

* docs: update CHANGELOG and CLAUDE.md for per-endpoint rate limiting

Add 2.0.1 changelog entry documenting new rate limiting features,
behavioral changes, and deprecations. Update CLAUDE.md to reflect
built-in rate limiting, correct Python version, and add rate_limiter
and cache modules to the file structure.

* fix: add class-level default for _override_limiter to prevent AttributeError on from_dict instances

* Fix gitignore and changelog
* Re-raise error from urllib as ScryfallError and fix tests

* Use .get for optional fields from scryfall data
@NandaScott NandaScott merged commit 97cf877 into main Apr 7, 2026
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant