Skip to content

Publish the download count instead of rendering it live, and name the traffic 403 - #138

Merged
arpanghoshal merged 2 commits into
mainfrom
fix/traffic-403-message
Sep 11, 2026
Merged

arpanghoshal merged 2 commits into
mainfrom
fix/traffic-403-message

Conversation

@arpanghoshal

@arpanghoshal arpanghoshal commented Sep 11, 2026

Copy link
Copy Markdown
Member

Downloads: rate limited within a day

img.shields.io/pypi/dm renders the count live, so shields asks pypistats on behalf of every project it serves. The badge returned rate limited by upstream service the day it shipped. pypistats' own API answers a single caller instantly:

{"last_day":30,"last_month":3450,"last_week":3286}

So traffic.yml now asks once a day and publishes downloads-badge.json to the badges branch, and the badge reads that document instead of a third party's cache. There is no history file, unlike clones: pypistats keeps the series and answers for any day, so this is a reading rather than an accumulation and a stored file would make it no more correct.

The two sources are independent. Either can fail without taking the other's badge down, the publish step writes whichever documents were read, and the retry rebuilds each from the untracked download it already has.

Exercised against a real remote rather than reasoned about:

clones downloads commit files
ok ok clones: 30; downloads: 3,450/month all three
ok failed clones: 30 clones only
failed ok downloads: 3,450/month downloads only

And against a competing publisher holding a stale downloads badge plus an extra clone day, the retry lands this run's reading (3,450/month, not the stale 1/month) and both clone days (99 + 45 = 144).

The 403

The first real run failed with curl: (22) The requested URL returned error: 403, which says the token cannot read traffic and nothing about why. The status is now read explicitly; 403 and 404 warn with exactly what to grant and exit 0, the same trade the missing-secret branch makes. Any other non-200 is still an error, with the body printed.

Regression pin

pypi/dm/ctrlrun is now asserted absent from the README, because the live form is the obvious thing to reach for again.

🤖 Generated with Claude Code

…thing else

The first real run failed with `curl: (22) The requested URL returned error:
403`, which says the token cannot read traffic and nothing about why. The two
failures that actually happen here are a token without `Administration:
Read-only` and a fine-grained token the org has not approved, and both are
403.

The status is now read explicitly. A 403 or 404 warns with what to grant and
exits 0, the same trade the missing-secret branch already makes: neither is
something the workflow can fix, and a nightly red run teaches us to ignore red
runs. Any other non-200 is still an error, and the body is printed with it.
@coderabbitai

coderabbitai Bot commented Sep 11, 2026

Copy link
Copy Markdown

Warning

Review limit reached

Next included review available in 5 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available.

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Learn how review limits work.

Review configuration:

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Advanced

Run ID: 9cd642c9-abf3-4200-ac7a-2a27bbb35157

📥 Commits

Reviewing files that changed from the base of the PR and between 4bc703a and f3a2833.

📒 Files selected for processing (5)
  • .github/workflows/traffic.yml
  • README.md
  • adopt-site/.vite/deps/_metadata.json
  • adopt-site/.vite/deps/package.json
  • tests/test_readme_assets.py

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

… 403

Two fixes to the same workflow, both found by running it.

**Downloads.** `img.shields.io/pypi/dm` renders the count live, which means
shields asks pypistats on behalf of every project it serves; the badge came
back `rate limited by upstream service` the day it shipped. pypistats answers
a single caller instantly, so the fix is to stop being one of shields' many
callers and become one of pypistats' rare ones: one request a day from here,
published as `downloads-badge.json` on the badges branch like the other three.
No history file, unlike clones -- pypistats keeps the series and answers for
any day, so what this publishes is a reading rather than an accumulation and
there is nothing for a stored file to make more correct.

The two sources are independent: either can fail without taking the other's
badge down, the publish step writes whichever documents were read, and the
retry rebuilds each from the untracked download it already has. Exercised on
a real remote: all three source combinations commit exactly their own files,
and against a competing publisher holding a stale downloads badge and an extra
clone day, the retry lands this run's reading and both days.

**The 403.** The first real run failed with `curl: (22) The requested URL
returned error: 403`, which says the token cannot read traffic and nothing
about why. The status is now read explicitly; a 403 or 404 warns with what to
grant and exits 0, the same trade the missing-secret branch makes. Any other
non-200 is still an error, with the body.

`pypi/dm/ctrlrun` is pinned as an absence in the README test: the live form is
the obvious thing to reach for again.
@arpanghoshal arpanghoshal changed the title Name the traffic API's 403 instead of exiting 22 Publish the download count instead of rendering it live, and name the traffic 403 Sep 11, 2026
@arpanghoshal
arpanghoshal enabled auto-merge (squash) September 11, 2026 16:01
@arpanghoshal
arpanghoshal merged commit 4a93ad5 into main Sep 11, 2026
11 checks passed
@arpanghoshal
arpanghoshal deleted the fix/traffic-403-message branch September 11, 2026 16:08
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