Skip to content

No test makes a served-digest read fail with a URLError that has no HTTP status, so release.yml's except can be narrowed with the suite green #286

Open

Description

@brettheap

Found by lane openRepoTools-2's independent re-check of #284 (PR 3b of #228), its comment 6284132726, NIT 4. Lane 2's own weighting: "Re-run failed jobs" recovers from it.

Line numbers are .github/workflows/release.yml at 7376b34, as lane 2 gives them. That file is blob 9a27e2e at 7376b34, at #284's head 2ff7094 and in tree 7ad2bbe (the tree main holds once #284 lands), so the lines are #284's and, once it lands, main's. test :N is tests/test_release_workflow.py at 2ff7094 (blob 8273f40, also in 7ad2bbe).

What happens

  • After each upload, the job reads the index's JSON up to 20 times, 15 seconds apart. A failed read is caught by except (urllib.error.URLError, TimeoutError, ConnectionError) as error: (:1223 in the testpypi job, :1723 in the pypi job) and retried, unless its code is a client error other than 404.
  • urllib raises a URLError with no status when it cannot connect, as for a refused connection or a DNS failure. No test makes a read fail that way: SERVED_CASES (test :3270-3285) answer with a status (200, 403, 404 or 503) or with DROPPED, a connection closed with no response (test :3130). That raises http.client.RemoteDisconnected, which urllib does not wrap (lane 2's question 4), so only the ConnectionError member is exercised.

Where

  • release.yml:1223-1227 and :1723-1727, two copies of one script; the fake index is FakeIndex (test :3148-3197).

How it was found

  • Lane 2's re-check at 7376b34: narrowing the except at :1223 and :1723 to (HTTPError, TimeoutError, ConnectionError) passes the suite (its measurement).

The fix

  • Lane 2 proposed none; its NIT names the gap.
  • What a fix has to settle (nothing here is decided):
    • how a case makes a read fail with a URLError and no status while the suite stays offline, with the fake index the only server (a port nobody listens on, for example);
    • whether a read that times out is added with it: no case makes one, since the handler answers or closes at once (test :3159-3177).

Why it is not in v1.1.0

Filed in milestone v1.2.0 on Brett Heap's word (2026-10-20T09:40Z, "yes, file the four NITs in v1.2.0"). It is unclaimed.

Lane: openRepoTools-3

🤖 Generated with Claude Code

Activity

  1. added this to the v1.2.0 milestone on Oct 20, 2026
  2. brettheap commented on Oct 20, 2026

    @brettheap
    ContributorAuthor

    CLAIMED — lane openRepoTools-2, session 202778d2-0e63-4478-9c95-fdde314b0524@Eagle, 2026-10-20T23:00:14Z, for #286

    Logged in lanes/log/openRepoTools-2.md at b592499a6fa61eb2a04ae133110b9c6068f86169.

    The three reads (lane-collision-protocol Rule 1):

    1. existing `CLAIMED —` comments on opensoft/openRepoTools#286:
       none
    2. `gh pr list --repo opensoft/openRepoTools --state all --search "#286"`, kept where the row or its body names `#286`:
    284	PR 3's re-check survivors are killed: tests/test_release_workflow.py runs the fetch, pipx, approval, tag, ref, clean, stamp, dist and digest steps instead of reading them, and release.yml changes only in comments (#228, PR 3b)	006-release-pypi-pr3b	MERGED
    3. `git ls-remote --heads git@github.com:opensoft/openRepoTools` branches naming `286` as a whole token:
       none
    
  3. brettheap commented on Oct 21, 2026

    @brettheap
    ContributorAuthor

    Lane openRepoTools-2, 2026-10-21T02:2xZ: #286 is done, on a branch only. It is branch test/286-served-read-urlerror-lane2 at 35b18b2, one commit on main a42946e, to be cherry-picked into openRepoTools-code after v1.1.0 is tagged.

    • The new case is a-refused-connection-then-served, run for both upload jobs. The fake index redirects the read to a port nobody listens on, so urlopen raises a URLError with no status. The test also requires the step's output to carry urlopen error.
    • The proof: with both excepts narrowed to (HTTPError, TimeoutError, ConnectionError), the two new cases fail with an uncaught URLError. Without the narrowing, 24 pass.
    • -k test_release_workflow passes 501.
    • Not covered: a timed-out read. The step's urlopen(timeout=30) waits a real 30 s, which the fake clock does not shorten.

    Lane: openRepoTools-2

  4. brettheap commented on Oct 21, 2026

    @brettheap
    ContributorAuthor

    Lane openRepoTools-2, 2026-10-21T11:2xZ: #286 has a second commit, ae3e8e0, on the same branch. It covers a read that times out. No case reached that member of the served check's except before.

    • How the case works:
      • The fake index now takes a request and answers nothing.
      • The timeout of the step's first connection is cut to 0.5 s, so the step's own urlopen raises a bare TimeoutError through a real socket.
      • release.yml is unchanged.
    • The new case, a-timed-out-read-then-served, runs for both upload jobs. It also requires the step to print "did not answer yet (timed out)", so a dropped connection cannot pass for a timeout.
    • The mutant proof: with TimeoutError removed from the except at release.yml:1223 and :1723, the 2 new cases fail and the 24 other served-check cases pass.
    • Below Python 3.10 the case skips, because there a timed-out read raises socket.timeout, which is not a TimeoutError.
    • Results:

    It is still a branch only, to be cherry-picked into openRepoTools-code after v1.1.0 is tagged.

    Lane: openRepoTools-2

  5. added a commit that references this issue on Oct 21, 2026
  6. brettheap commented on Oct 21, 2026

    @brettheap
    ContributorAuthor

    Lane openRepoTools-2, 2026-10-21T16:2xZ: the final review of #286 is READY, with no MUST and no SHOULD. It was an opus pass, on Brett Heap's word.

    • Head: 4a83bac. It holds the branch's own commits 35b18b2 and ae3e8e0, plus a merge of main 50d05d4 that carries only main's delta.
    • The timed-out read (ae3e8e0) holds.
      • With TimeoutError dropped from both excepts, exactly the two new cases fail, with an uncaught TimeoutError: timed out. The other 24 served-check cases pass.
      • With the served reads' timeout removed, both new cases fail at their "(timed out)" assertion. So the case also guards the read's timeout.
      • Each new case takes under a second, and the 3.10 skip cannot reach the package job's no-skip check.
    • For the series: in the all-seven tree, release.yml's wheel checks disagree on a member's mode, its corrupt-member and index checks end in tracebacks, and its macOS rerun places none of the 16 commands under bash 3.2 #278 rewrites the served check's except to (OSError, http.client.HTTPException). The mutant in this issue's earlier note then no longer applies as written. The behaviour still holds, because OSError catches TimeoutError, and the combined run passes.
    • CI: run 39616697362 on 4a83bac has all four jobs green.

    Lane: openRepoTools-2

  7. modified the milestones: v1.2.0, v1.2.1 on Nov 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions