Commit f5fb6c3
Add uv fallback for inline script environments (PEP 723 PR 6/16) (#1696)
> Part of #1602 (PEP 723 inline script env support). Design doc: #1601.
>
> This replaces the earlier closed draft #1652 with the finalized
implementation rebased on `main`.
### Roadmap context
This is **PR 6 of 16** in the PEP 723 inline-script roadmap. It extends
the PR 5 `create()` happy path with the missing-compatible-interpreter
fallback.
| Phase 2: Manager | PR | Status |
|---|---|---|
| | PR 4: `InlineScriptEnvManager` skeleton | merged (#1610) |
| | PR 5a: generic env-creation utilities | merged (#1651) |
| | PR 5b: inline-script cache + interpreter utilities | merged (#1655)
|
| | PR 5c: `create()` happy path | merged (#1656) |
| | **PR 6: `create()` uv-install fallback** | **this PR** |
| | PR 7: persistence (`get` / `set` + Memento) | separate follow-up |
### Why this PR
PR 5 can create or reuse an inline-script environment when an installed
base interpreter already satisfies the script's `requires-python`. It
deliberately stops when no compatible interpreter exists.
This PR adds the consent-gated fallback for that case:
1. Re-check installed interpreters after entering a narrow fallback
queue.
2. Select a safe Python version satisfying the complete PEP 440
requirement.
3. Ask the user before installing uv or Python.
4. Refresh environment discovery after installation.
5. If discovery is stale or unavailable, resolve and validate uv's
returned executable directly.
6. Continue through the existing PR 5 cache create/reuse path.
### What this PR does
**Adds the inline-script fallback to `InlineScriptEnvManager.create()`**
- Keeps the installed-interpreter path unchanged and only enters the
fallback when no compatible base exists.
- Skips installation prompts for quick-create/noninteractive calls.
- Serializes only fallback installations, then re-checks discovery
before prompting so concurrent compatible requests reuse one
installation.
- Retains successfully direct-resolved uv interpreters so queued
requests can reuse them even while discovery remains stale.
**Selects a safe uv target from `requires-python`**
- Uses simple safe selectors directly, such as `>=3.13` → `3.13` and
`==3.13.1` → `3.13.1`.
- Queries uv's advertised versions for bounded or exclusion-heavy
constraints.
- Restricts catalog candidates to default CPython 3 builds satisfying
the complete specifier.
- Handles exclusions such as `>=3.13.2,!=3.13.2` without installing the
excluded floor.
- Uses an advertised release for bounded ranges rather than fabricating
a potentially unavailable patch-zero release.
- Applies full PEP 440 prerelease semantics consistently across
discovery, catalog selection, direct resolution, and cache validation.
- Normalizes accepted prerelease aliases (for example, `c1` → `rc1`)
before passing a version to uv.
**Extends the uv installer's consent flow**
- Adds an inline-script-specific prompt that shows both the script
requirement and selected Python version.
- Sanitizes and caps script-controlled prompt details.
- Validates install selectors before forwarding them to uv.
- If catalog lookup is required and uv is missing, asks for consent to
install uv first.
- Re-checks whether a newly installed uv is usable by the current
extension host and surfaces the existing restart-required message when
needed.
**Handles stale discovery after installation**
- Refreshes environment discovery after uv installs Python.
- Treats refresh/discovery failures as recoverable.
- Resolves the executable returned by uv directly, verifies that it
satisfies `requires-python`, and canonicalizes its path before creating
the cached environment.
### Examples
| `requires-python` | Fallback behavior |
|---|---|
| `>=3.13` | Request uv's `3.13` selector |
| `==3.13.1` | Request exactly `3.13.1` without requiring a catalog
lookup |
| `>=3.11,<3.12` | Choose an advertised compatible `3.11.x` release |
| `>=3.13.2,!=3.13.2` | Skip `3.13.2` and choose a compatible advertised
release |
| `>=3.15.0a1,<3.16` | Permit an explicitly requested prerelease |
| `>=3.14,<3.16` | Do not select a prerelease implicitly |
### Safety and concurrency
- No uv or Python installation occurs without explicit user consent.
- Script-derived values cannot inject arbitrary uv arguments.
- Declined, cancelled, or failed installations do not mutate the
script-environment cache.
- The fallback queue does not globally serialize environment selection
or normal cache creation.
- Existing cross-process cache locking and rollback behavior from PR 5
remains unchanged.
### Tests
Coverage includes:
- simple, exact, bounded, exclusion, and prerelease requirements;
- uv catalog filtering and consented uv bootstrap;
- declined and failed installations;
- refresh and discovery failures with direct resolution;
- simultaneous same- and different-constraint requests;
- quick-create prompt suppression;
- strict post-install and cache compatibility checks;
- prompt sanitization and install-selector validation.
`npm run compile-tests`, `npm run lint`, the full unit suite, and the
focused inline-script/uv suites are clean.
### User impact
**No default-path user impact yet.** This completes an internal Phase 2
manager capability. Automatic routing and user-facing entry points
arrive in later roadmap PRs.
When those entry points are wired, users whose scripts require an
unavailable Python will be able to approve installing a compatible
interpreter rather than having environment creation stop.
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 1a9f6ba1-9bd3-4664-bc25-a0d34d7a2e911 parent 6138122 commit f5fb6c3
5 files changed
Lines changed: 1014 additions & 44 deletions
File tree
- src
- common
- managers/builtin
- inlineScript
- test/managers/builtin
- inlineScript
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
238 | 238 | | |
239 | 239 | | |
240 | 240 | | |
| 241 | + | |
241 | 242 | | |
242 | 243 | | |
243 | 244 | | |
| |||
281 | 282 | | |
282 | 283 | | |
283 | 284 | | |
| 285 | + | |
| 286 | + | |
| 287 | + | |
| 288 | + | |
| 289 | + | |
| 290 | + | |
284 | 291 | | |
285 | 292 | | |
286 | 293 | | |
| |||
0 commit comments