Commit 79fe2aa
feat(inline-scripts): add cache layout, meta.json sidecar, env-usability guard (PEP 723 PR 2/3)
Helpers that resolve the on-disk cache layout for PEP 723 inline-script
envs (under <globalStorageUri>/script-envs-v1/<hash>/), read/write the
.meta.json sidecar that lives at the root of every cached env, and
verify on cache hit that the base interpreter the env was built against
still exists on disk. Second foundation PR; pure utility, no behavior
change on its own.
What is in:
- src/common/inlineScriptCacheLayout.ts
- Path helpers: getScriptEnvCacheRoot, getScriptEnvDir,
getMetaJsonPath. All take globalStorageUri: Uri rather than
ExtensionContext so they are easy to test.
- InlineScriptEnvMeta interface with schemaVersion: 1. Minimal
shape: only fields with a concrete consumer in a planned PR
(scriptFsPath for cache cleanup, lastUsedAt for TTL, optional
requiresPython for cache-hit re-verify). Extras dropped on read
per the v1 evolution policy.
- readMetaJson(envDir) -- never throws. Returns undefined (with a
traceWarn that includes err.code) for: missing file, non-regular
file, oversize file (>1 MiB defensive cap), malformed JSON,
invalid shape, unknown schemaVersion, non-canonical ISO 8601
timestamps. Strict ISO check uses round-trip equality.
- writeMetaJson(envDir, meta) -- atomic temp-file + native
fs.rename (NOT fs-extra move(), which does remove+rename and
has both a no-sidecar window and a crash-loses-data failure
mode).
- CacheEntrySummary + selectStaleEntries(entries, now, ttlMs) --
pure selector for Q7s TTL eviction path. Zero I/O. Entries with
undefined lastUsedAt are never TTLed (deliberate: do not delete
what we do not understand).
- verifyEnvUsable(envDir): Promise<boolean> -- cache-hit guard
that confirms the base interpreter the cached env was built
against still exists on disk. The cache-key path hash detects
"user switched to a different Python" because the path changes;
it does NOT detect "user uninstalled the Python at the cached
path" (the cache directory and .meta.json are unchanged, only
the file at the launcher target is gone). Common triggers:
pyenv/uv python uninstall, brew/apt removal, Add/Remove
Programs.
* POSIX: stat <envDir>/bin/python. fs.stat follows symlinks so
a dead symlink to a removed base throws ENOENT.
* Windows: <envDir>/Scripts/python.exe is a COPY of the base
(not a symlink), so checking it does not help. Read
pyvenv.cfg, parse `home = <dir>`, stat <home>/python.exe.
* Never throws. Returns false + traceWarn(message) on any
failure mode (missing launcher, missing/malformed
pyvenv.cfg, non-regular file at the launcher path, EACCES
etc.). Mirrors readMetaJson's failure policy.
Internal helpers `statRegularFile` (POSIX/Windows shared
stat+isFile+error-tagging) and `parsePyvenvHome` (CRLF + extra
whitespace tolerated).
- META_SCHEMA_VERSION evolution policy documented inline.
- src/test/common/inlineScriptCacheLayout.unit.test.ts -- 44 unit
tests (real-fs, mirroring readMetaJson conventions):
* path-helper shape
* round-trip writeMetaJson/readMetaJson, concurrent writers,
and all rejection paths (ENOENT-specific warn, malformed JSON,
unknown schemaVersion, non-canonical ISO, size cap, etc.)
* selectStaleEntries boundaries (TTL=0, exact-equal age,
undefined lastUsedAt, future timestamps)
* verifyEnvUsable POSIX branch (isWindows stubbed to false):
bin/python exists / missing / is-a-directory; symlink-alive
and dead-symlink cases gated on process.platform !== 'win32'
* verifyEnvUsable Windows branch (isWindows stubbed to true):
home points to existing / removed python.exe, missing
pyvenv.cfg, no home= line, empty home value, whitespace and
CRLF tolerance.
Design context
Implements Q2 (disk location), Q3 (env folder contents + .meta.json
shape), Q4 step 4 (cache-hit interpreter-existence guard), and the
pure selector half of Q7 (TTL eviction) from
pep723_design_questions.md. The disk walk and deletion calls live in
a later PR. The verifyEnvUsable guard was added in response to
reviewer feedback on the design doc.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>1 parent b16150a commit 79fe2aa
2 files changed
Lines changed: 740 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
| 180 | + | |
| 181 | + | |
| 182 | + | |
| 183 | + | |
| 184 | + | |
| 185 | + | |
| 186 | + | |
| 187 | + | |
| 188 | + | |
| 189 | + | |
| 190 | + | |
| 191 | + | |
| 192 | + | |
| 193 | + | |
| 194 | + | |
| 195 | + | |
| 196 | + | |
| 197 | + | |
| 198 | + | |
| 199 | + | |
| 200 | + | |
| 201 | + | |
| 202 | + | |
| 203 | + | |
| 204 | + | |
| 205 | + | |
| 206 | + | |
| 207 | + | |
| 208 | + | |
| 209 | + | |
| 210 | + | |
| 211 | + | |
| 212 | + | |
| 213 | + | |
| 214 | + | |
| 215 | + | |
| 216 | + | |
| 217 | + | |
| 218 | + | |
| 219 | + | |
| 220 | + | |
| 221 | + | |
| 222 | + | |
| 223 | + | |
| 224 | + | |
| 225 | + | |
| 226 | + | |
| 227 | + | |
| 228 | + | |
| 229 | + | |
| 230 | + | |
| 231 | + | |
| 232 | + | |
| 233 | + | |
| 234 | + | |
| 235 | + | |
| 236 | + | |
| 237 | + | |
| 238 | + | |
| 239 | + | |
| 240 | + | |
| 241 | + | |
| 242 | + | |
| 243 | + | |
| 244 | + | |
| 245 | + | |
| 246 | + | |
0 commit comments