Skip to content

feat: API provider and PyPI publishing (0.2.0) - #2

Merged
tehw0lf merged 2 commits into
mainfrom
feat/api-provider
Sep 30, 2026
Merged

tehw0lf merged 2 commits into
mainfrom
feat/api-provider

Conversation

@tehw0lf

@tehw0lf tehw0lf commented Sep 30, 2026 •

Copy link
Copy Markdown
Owner

Stage 2 of yaft-python: the API provider, and publishing to PyPI.

API provider

APIFeatureProvider(base_url, group) loads one toggle group from a YaFT backend over urllib and adds no dependency.

  • refresh() -> bool: asks /collectionHash/{uuid} first and fetches /features/{uuid} only when the hash changed. The hash is recorded only after the group was applied, so a failed refresh is retried (R30). A body that is not a group raises RefreshError (R32), and the old data stays.
  • refresh_quietly() for schedulers: it logs the failure instead of raising.
  • Guards: canonical UUID only (it goes into the path), no redirects, max_body_bytes (default 1 MiB), and a timeout that also cuts off a body that trickles in (read1 plus a deadline). A body of nothing but [ gives RecursionError; that is caught too.
  • Lookup by name or by full uuid|name key, the same as yaft-go and yaft-java.
  • Own User-Agent yaft-python/<version>. Cloudflare in front of yaft.tehwolf.de answers urllib's default Python-urllib/3.x with 403. The smoke test against the live backend found this; before the fix every refresh failed.

Conformance

The suite's refresh cases now run through APIFeatureProvider over HTTP, against a local stand-in backend (tests/backend.py), and no longer through LocalFeatureProvider.load. retry comes with the same hash as the rejected body.

PyPI

.github/workflows/publish.yml uploads the build artifact of a push to main through Trusted Publishing, in environment pypi. It does not live in tehw0lf/workflows because a Trusted Publisher cannot name a reusable workflow from another repository. Pull requests from forks are excluded explicitly.

Before merging: create a pending Trusted Publisher on pypi.org with project yaft, owner tehw0lf, repository yaft-python, workflow publish.yml and environment pypi.

Checked

  • ruff, ruff format, mypy (strict), 201 tests on 3.11 and 3.14, uv build.
  • Mutation check: I planted 8 bugs (hash recorded before the load, rejected body swallowed, redirects followed, no deadline, no name lookup, no size limit, 204 accepted, hash read by truthiness). Every one turns the tests red. The last one only did so after I added a test for it.
  • Live against yaft.tehwolf.de: the first refresh returns True and the second False, with no second fetch. The playground groups are currently empty.

https://claude.ai/code/session_01RxukupkbvZyVVFzDRuiniV

Summary by CodeRabbit

  • New Features
    • Added an API-backed feature provider that loads features for a toggle group, refreshes data when it changes, and evaluates feature status against the configured clock.
    • Added configurable request time and response-size limits, redirect rejection, and a quiet refresh option that logs failures.
    • Added PyPI publishing after successful builds on the main branch, using Trusted Publishing.
    • Updated installation instructions to use the yaft package on PyPI.
  • Documentation
    • Added guidance for using the API-backed provider, configuring refresh behavior, handling errors, and implementing a custom provider.
  • Other
    • Updated the project version to 0.2.0.

APIFeatureProvider loads one toggle group from a YaFT backend with urllib
alone: hash first, group only on change, hash recorded only after the group
loaded (R30). refresh() returns whether new data came and raises
RefreshError otherwise (R32); refresh_quietly() logs for schedulers. No
redirects, a body limit, a deadline that also covers a trickling body, and
lookup by name within the group.

It sends its own User-Agent: Cloudflare in front of yaft.tehwolf.de answers
urllib's "Python-urllib/3.x" with 403, so every refresh would have failed.

The suite's refresh cases now run through the provider against a local
stand-in backend instead of LocalFeatureProvider.load. publish.yml uploads
the build artifact of a push to main via Trusted Publishing, since that
cannot name a reusable workflow from another repository.

Claude-Session: https://claude.ai/code/session_01RxukupkbvZyVVFzDRuiniV
@coderabbitai

coderabbitai Bot commented Sep 30, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Essentials

Run ID: 757149b7-4e21-4294-a3d1-0a0c837dfd37

📥 Commits

Reviewing files that changed from the base of the PR and between fab8ff8 and ce894fa.

⛔ Files ignored due to path filters (1)
  • uv.lock is excluded by !**/*.lock
📒 Files selected for processing (11)
  • .github/workflows/build.yml
  • .github/workflows/publish.yml
  • CLAUDE.md
  • README.md
  • pyproject.toml
  • src/yaft/__init__.py
  • src/yaft/api.py
  • tests/backend.py
  • tests/conformance/test_mapping.py
  • tests/conftest.py
  • tests/test_api.py

Included review availability: This review used your included allowance. 2 included reviews remain after this review. Your included PR review attempts over the past 7 days set your current allowance at 3 reviews per hour.


📝 Walkthrough

Walkthrough

The pull request adds and exports APIFeatureProvider, which retrieves feature data from a YaFT backend. It adds provider tests and documentation, updates the package version to 0.2.0, and adds a GitHub Actions workflow to publish build artifacts to PyPI.

Changes

API provider and package release

Layer / File(s) Summary
API provider and public interface
src/yaft/api.py, src/yaft/__init__.py, README.md, CLAUDE.md
Adds APIFeatureProvider and RefreshError as package exports. The provider validates configuration, checks collection hashes, retrieves feature data, enforces response limits and deadlines, and supports feature lookup and refresh error handling. The documentation describes provider setup and behavior.
Provider tests and conformance
tests/backend.py, tests/conftest.py, tests/test_api.py, tests/conformance/test_mapping.py
Adds a local HTTP backend and a fixture. Tests cover provider validation, refresh behavior, feature lookup, request failures, timeout handling, and recovery. Conformance cases now exercise refreshes through the API provider.
Build artifact publication and package release
.github/workflows/build.yml, .github/workflows/publish.yml, pyproject.toml, README.md, CLAUDE.md
Adds a workflow that publishes artifacts from successful Build runs on main to PyPI using Trusted Publishing. Updates the package version to 0.2.0 and documents PyPI installation and publication behavior.

Priority: ➖ Normal

Estimated code review effort: 3 (Moderate) | ~25 minutes

Change: Feature

Sequence Diagram(s)

sequenceDiagram
  participant Application
  participant APIFeatureProvider
  participant YaFTBackend
  Application->>APIFeatureProvider: refresh()
  APIFeatureProvider->>YaFTBackend: request collection hash
  YaFTBackend-->>APIFeatureProvider: return collection hash
  APIFeatureProvider->>YaFTBackend: request group features when hash changes
  YaFTBackend-->>APIFeatureProvider: return group features
  APIFeatureProvider-->>Application: return refresh result
Loading

Merge Risk: ⚪ Minimal · up to ce894

This change adds an API-backed feature provider, its tests and docs, and a PyPI publishing workflow. No actionable merge-blocking risk is evident. PyPI Trusted Publisher configuration must be completed on the PyPI side, and later merges must bump the version.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 22.86% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 35 functions across 6 files. (5 skipped: … Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly identifies the two main changes: the API provider and PyPI publishing. The version number is also consistent with the release change to 0.2.0.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Full details: Docstring Coverage

Explanation

Docstring coverage is 22.86% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 35 functions across 6 files. (5 skipped: 5 unsupported.)

  • Fix all pre-merge checks with AI
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Autopilot is currently an internal CodeRabbit preview.


Comment @coderabbitai help to get the list of available commands.

@tehw0lf
tehw0lf merged commit daa7165 into main Sep 30, 2026
24 checks passed
@tehw0lf
tehw0lf deleted the feat/api-provider branch September 30, 2026 20:44
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