Keep the npm download out of the pyright tests' time budget - #11
Open
christophwille wants to merge 1 commit into
Open
Keep the npm download out of the pyright tests' time budget#11christophwille wants to merge 1 commit into
christophwille wants to merge 1 commit into
Conversation
The three tests that talk to pyright each hand one three-minute token to the handshake and their requests. On a runner with node and no pyright the server comes through npx, whose first start downloads the package from the npm registry before the server says a word - inside that same token. Every CI runner is fresh, so the first pyright test of a run always paid for the download, and when the registry had a slow morning (16 s one day, 109 s the next on the same runner image, over 180 s twice) the test failed with a cancellation inside initialize that named neither the download nor how long it took. The download now happens once per test assembly, through ExternalTool.RunAsync with a budget of its own, before any pyright test starts its server. RunAsync logs the command with its elapsed time, so a slow registry is a number in the test output rather than a timeout three minutes later. A download that fails still fails the tests, with that reason: a registry that cannot be reached is a fact about the run. Raising the three-minute budget was rejected because it hides the cause and slows every failure; a warm-up step in the workflow was rejected because a developer's first run has the same exposure and the test should measure the server, not the network, wherever it runs. Assisted-by: Claude:claude-fable-5-1:Claude Code
Member
Author
|
Build run 33842356786 on this branch is green on all three OSes. The warm-up line in each TRX gives the npm download time the tests used to absorb inside their three-minute budget:
Two of the three would have failed the old test again this run. The registry is evidently still slow; 421 s is inside the ten-minute warm-up budget, but not by a wide margin. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Symptom
PythonInterpreterTests.AnImportResolvesIntoAnEnvironmentThatIsNotInTheWorktreefailed on Windows in run 33840465175 and on Windows and Linux in run 33841144458, each time after exactly three minutes:The branch those runs built (#10) changes only
build.ymlanddotnet-tools.json. Its merge base 7b0e186 passed the same test on all three OSes in run 33785386015 the evening before. Nothing in the repository, the runner image (ubuntu-24.04 20260831.293.1in both the passing and the failing Linux job) or the pyright version (1.1.413 since 2026-08-14) changed in between.Cause
The TRX artifacts keep each test's own log, including for passed tests. On a runner with node and no pyright on PATH,
LanguageServers.Python()resolves tonpx --yes --package pyright -- pyright-langserver --stdio, and the first npx start downloads the package from the npm registry before the server says a word. The gap between-> started (pid ...)andPyright language server 1.1.413 startingis that download, and that gap is what grew:The test creates one
CancellationTokenSource(TimeSpan.FromMinutes(3))and hands it toLspConnection.StartAsync, so the token meant to bound a language server's handshake also bounded a network download from a registry the test does not control. Every CI runner starts with an empty npm cache, so the download happens every run, and this fixture is the first pyright test alphabetically, so it is the one that paid for it.PythonLspTestsandPythonProjectConfigTestsshare the same three-minute budget and the same exposure; they passed only because npx had the package (at least partially) cached by the time they ran, after the first attempt was killed.Why now
npm's status page lists Intermittent Failures Impacting npm Publish from 2026-09-03 21:42 to 23:21 UTC with Package installation degraded. The passing run was before it; the failing runs were the next morning, and the download times above show installs were still slow then. The test was always one slow registry away from red.
The app is not affected:
ReviewWorkspace.TryStartPythonAsyncpasses the session token, which has no timeout, so a slow download only makes "Starting pyright" take longer.Fix
A new test helper,
PythonServer.ResolveAsync(), replaces the directLanguageServers.Python()call in the three pyright tests. When the spec is the npx form, it first awaits a sharedLazy<Task>that runsonce per test assembly through
ExternalTool.RunAsync, with a ten-minute budget of its own. npx keys its cache on the package spec, so this puts the package in the same_npx/<hash>directorypyright-langserveris then started from.RunAsyncalready logs the command with its elapsed time, so the download shows up in the test output as a number:The three-minute tokens stay as they are; they now measure pyright and not the registry. A download that fails or runs out its budget fails every pyright test with that reason instead of an opaque cancellation inside
initialize. It is deliberately not anAssert.Ignore: a registry that cannot be reached is a fact about the run, not about the machine.Rejected alternatives
build.ymlonly. Fixes CI but leaves the test measuring the wrong thing; a developer's first run on a fresh npm cache has the same exposure.--package pyright@<version>. Does nothing for download speed, and the unpinned spec is what the app itself uses.Verification
🤖 Generated with Claude Code