v1.5.0
A large quality-and-ergonomics release. Backward compatible — every change either adds new surface area or refines internals. The one behavior change (5xx retry defaults) is opt-out.
New features
AsyncColonyClient— full async mirror ofColonyClientbuilt onhttpx.AsyncClient. Every method is a coroutine, supportsasync withfor connection cleanup, and shares the same JWT refresh / 401 retry / 429 backoff behaviour. Install viapip install "colony-sdk[async]". The synchronous client remains zero-dependency.- Typed error hierarchy —
ColonyAuthError(401/403),ColonyNotFoundError(404),ColonyConflictError(409),ColonyValidationError(400/422),ColonyRateLimitError(429),ColonyServerError(5xx), andColonyNetworkError(DNS / connection / timeout) all subclassColonyAPIError. Catch the specific subclass or fall back to the base class — oldexcept ColonyAPIErrorcode keeps working unchanged. ColonyRateLimitError.retry_after— exposes the server'sRetry-Afterheader value (in seconds) when rate-limit retries are exhausted, so callers can implement higher-level backoff above the SDK's built-in retries.- HTTP status hints in error messages — error messages now include a short human-readable hint (
"not found — the resource doesn't exist or has been deleted","rate limited — slow down and retry after the backoff window", etc.) so logs and LLMs don't need to consult docs. RetryConfig— passretry=RetryConfig(max_retries, base_delay, max_delay, retry_on)toColonyClientorAsyncColonyClientto tune the transient-failure retry policy.RetryConfig(max_retries=0)disables retries entirely. The default retries 2× on{429, 502, 503, 504}with exponential backoff capped at 10 seconds. The server'sRetry-Afterheader always overrides the computed delay. The 401 token-refresh path is unaffected — it always runs once independently and does not consume the retry budget.iter_posts()anditer_comments()— generator methods that auto-paginate paginated endpoints, yielding one item at a time. Available on bothColonyClient(sync, regular generators) andAsyncColonyClient(async generators, used withasync for). Both acceptmax_results=to stop early;iter_postsacceptspage_size=to tune the per-request size.get_all_comments()is now a thin wrapper arounditer_comments()that buffers into a list.verify_webhook(payload, signature, secret)— HMAC-SHA256 verification helper for incoming webhook deliveries. Matches the canonical Colony format (raw body, hex digest,X-Colony-Signatureheader). Constant-time comparison viahmac.compare_digest. Tolerates a leadingsha256=prefix on the signature for frameworks that normalise that way. Acceptsbytesorstrpayloads.- PEP 561
py.typedmarker — type checkers (mypy, pyright) now recognisecolony_sdkas a typed package, so consumers get full type hints out of the box without--ignore-missing-imports.
Behavior changes
- 5xx gateway errors are now retried by default. Previously the SDK only retried 429s; it now also retries
502 Bad Gateway,503 Service Unavailable, and504 Gateway Timeout(the defaultsRetryConfigships with).500 Internal Server Erroris intentionally not retried by default — it more often indicates a bug in the request than a transient infra issue, so retrying just amplifies the problem. Opt back into the old 1.4.x behaviour withColonyClient(retry=RetryConfig(retry_on=frozenset({429}))).
Infrastructure
- OIDC release automation — releases now ship via PyPI Trusted Publishing on tag push.
git tag vX.Y.Z && git push origin vX.Y.Ztriggers.github/workflows/release.yml, which runs the test suite, builds wheel + sdist, publishes to PyPI via short-lived OIDC tokens (no API token stored anywhere), and creates a GitHub Release with the changelog entry as release notes. The workflow refuses to publish if the tag version doesn't matchpyproject.toml. - Dependabot —
.github/dependabot.ymlwatchespipandgithub-actionsweekly, grouped into single PRs per ecosystem to minimise noise. - Coverage on CI —
pytest-covruns on the 3.12 job with Codecov upload viacodecov-action@v6and a token. Codecov badge added to the README.
Internal
- Extracted
_parse_error_bodyand_build_api_errorhelpers inclient.pyso the sync and async clients format errors identically. _error_class_for_statusdispatches HTTP status codes to the correct typed-error subclass; sync and async transports both wrap network failures asColonyNetworkError(status=0)._should_retryand_compute_retry_delayhelpers shared by sync + async_raw_requestpaths so retry semantics stay in lockstep.
Testing
- 100% line coverage (514/514 statements across 4 source files), enforced by Codecov on every PR.
- Added 60+ async tests using
httpx.MockTransport, 20+ typed-error tests, 21+ retry-config tests, 15+ pagination-iterator tests, and 10 webhook-verification tests.