pyfig 0.4.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -12
- package/SECURITY.md +1 -1
- package/dist/cjs/index.js +82 -45
- package/dist/cjs/index.js.map +8 -7
- package/dist/cjs/sync-resolver.js +80 -43
- package/dist/cjs/sync-resolver.js.map +7 -6
- package/dist/cli.js +2 -2
- package/dist/cli.js.map +1 -1
- package/dist/client.d.ts +1 -1
- package/dist/client.js +2 -2
- package/dist/config.d.ts +5 -5
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +15 -8
- package/dist/config.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/probes.d.ts +1 -1
- package/dist/probes.d.ts.map +1 -1
- package/dist/probes.js +13 -4
- package/dist/probes.js.map +1 -1
- package/dist/provision.d.ts +2 -2
- package/dist/provision.d.ts.map +1 -1
- package/dist/provision.js +50 -33
- package/dist/provision.js.map +1 -1
- package/dist/python-version.d.ts +10 -0
- package/dist/python-version.d.ts.map +1 -0
- package/dist/python-version.js +17 -0
- package/dist/python-version.js.map +1 -0
- package/package.json +5 -4
package/README.md
CHANGED
|
@@ -16,7 +16,7 @@ bun add pyfig
|
|
|
16
16
|
import { createPyfig } from 'pyfig';
|
|
17
17
|
|
|
18
18
|
const pyfig = createPyfig({
|
|
19
|
-
|
|
19
|
+
requiresPython: '>=3.11,<4',
|
|
20
20
|
requireEmbedding: true,
|
|
21
21
|
requireGil: true,
|
|
22
22
|
});
|
|
@@ -36,7 +36,7 @@ await pyfig.uv(
|
|
|
36
36
|
);
|
|
37
37
|
```
|
|
38
38
|
|
|
39
|
-
`prepare()` accepts
|
|
39
|
+
`prepare()` accepts PEP 508 dependency specifiers plus optional `extras`, `groups`, or `allExtras`. If the project has a `pyproject.toml`, uv resolves that read-only input together with a temporary dependency file. Pyfig installs the resolved result with `uv pip install -r`, which preserves unrelated packages. Temporary inputs are created outside the repository and removed afterward.
|
|
40
40
|
|
|
41
41
|
The same uv-only surface handles direct commands:
|
|
42
42
|
|
|
@@ -89,19 +89,19 @@ await pyfig.prepare(
|
|
|
89
89
|
Resolution follows the equivalent of:
|
|
90
90
|
|
|
91
91
|
```sh
|
|
92
|
-
uv pip compile pyproject.toml /temporary/runtime-
|
|
92
|
+
uv pip compile pyproject.toml /temporary/runtime-dependencies.txt \
|
|
93
93
|
--extra postgres --group runtime \
|
|
94
94
|
--python /selected/venv/python \
|
|
95
|
-
--output-file /temporary/resolved-
|
|
95
|
+
--output-file /temporary/resolved-dependencies.txt
|
|
96
96
|
|
|
97
97
|
uv pip install \
|
|
98
98
|
--python /selected/venv/python \
|
|
99
|
-
-r /temporary/resolved-
|
|
99
|
+
-r /temporary/resolved-dependencies.txt
|
|
100
100
|
```
|
|
101
101
|
|
|
102
|
-
When no `pyproject.toml` exists, only the temporary
|
|
102
|
+
When no `pyproject.toml` exists, only the temporary dependency input is compiled. Extras and dependency groups require an existing `pyproject.toml`. Pyfig does not use `uv add` or `uv pip sync`: the former would edit project metadata, while the latter could remove unrelated packages from the venv.
|
|
103
103
|
|
|
104
|
-

|
|
105
105
|
|
|
106
106
|
[Mermaid source](docs/diagrams/runtime-dependencies.mmd)
|
|
107
107
|
|
|
@@ -121,11 +121,11 @@ Set `projectRoot` explicitly in reusable libraries and unusual workspace layouts
|
|
|
121
121
|
| Export | Responsibility |
|
|
122
122
|
| --- | --- |
|
|
123
123
|
| `createPyfig(options)` | Create a scoped client with memoized base preparation. |
|
|
124
|
-
| `client.prepare(
|
|
124
|
+
| `client.prepare(dependencies?, options?)` | Select a venv and optionally resolve/install runtime dependencies. |
|
|
125
125
|
| `client.pythonInfo()` | Return the selected interpreter and embedding metadata. |
|
|
126
126
|
| `client.uvInfo()` | Return the selected uv executable and version. |
|
|
127
127
|
| `client.uv()` / `client.uvSync()` | Spawn uv and pin pip/run commands to the selected venv. |
|
|
128
|
-
| `ensureEnvironment(options?,
|
|
128
|
+
| `ensureEnvironment(options?, dependencies?, dependencyOptions?)` | Standalone preparation API. |
|
|
129
129
|
| `ensureUv(options?)` | Prefer existing uv, otherwise bootstrap verified uv when allowed. |
|
|
130
130
|
| `uvSpawn()` / `uvSpawnSync()` | Standalone uv spawn API. |
|
|
131
131
|
| `resolveProjectRoot(start?)` | Resolve the nearest Git root without reading manifests. |
|
|
@@ -136,10 +136,12 @@ The package exposes matching ESM and CommonJS entry points. Probe, download, loc
|
|
|
136
136
|
|
|
137
137
|
Precedence is API or CLI option, environment variable, then default.
|
|
138
138
|
|
|
139
|
+
`requiresPython` uses the same PEP 440 specifier syntax as `[project].requires-python` in `pyproject.toml`. Comma-separated clauses, compatible releases (`~=`), exclusions (`!=`), wildcard equality (`==3.12.*`), arbitrary equality (`===`), and pre-release versions are supported. For example: `>=3.10,!=3.12.*,<4`.
|
|
140
|
+
|
|
139
141
|
| API option | Environment variable | Default |
|
|
140
142
|
| --- | --- | --- |
|
|
141
143
|
| `python` | `PYFIG_PYTHON` | uv discovery |
|
|
142
|
-
| `
|
|
144
|
+
| `requiresPython` | `PYFIG_REQUIRES_PYTHON` | `>=3.10,<4` |
|
|
143
145
|
| `managedPython` | `PYFIG_MANAGED_PYTHON` | `3.12` |
|
|
144
146
|
| `uvVersion` | `PYFIG_UV_VERSION` | `0.13.6` |
|
|
145
147
|
| `uvRange` | `PYFIG_UV_RANGE` | `>=0.10.0 <1.0.0` |
|
|
@@ -163,7 +165,7 @@ Set `PYFIG_UV` to choose an explicit uv executable. Set `PYFIG_AUTO_INSTALL=0` o
|
|
|
163
165
|
```sh
|
|
164
166
|
bunx --bun pyfig
|
|
165
167
|
bunx --bun pyfig --check
|
|
166
|
-
bunx --bun pyfig --python
|
|
168
|
+
bunx --bun pyfig --requires-python '>=3.12,<3.14'
|
|
167
169
|
bunx --bun pyfig --require-embedding --require-gil
|
|
168
170
|
bunx --bun pyfig --project-root /path/to/repository
|
|
169
171
|
bunx --bun pyfig --json
|
|
@@ -179,7 +181,7 @@ Provisioning mutations use Loqkit's layered in-memory, worker, native-file-lock-
|
|
|
179
181
|
|
|
180
182
|
## Platform support
|
|
181
183
|
|
|
182
|
-
Pyfig bootstraps uv on x64 and arm64 Windows, macOS, and Linux. Linux uses Astral's static musl artifact, which also runs on glibc. Node.js 22 through 26 and Bun 1.2+ are supported. Other JavaScript runtimes may work when they provide compatible Node built-ins and process semantics.
|
|
184
|
+
Pyfig bootstraps uv on x64 and arm64 Windows, macOS, and Linux. Linux uses Astral's static musl artifact, which also runs on glibc. Node.js 22.11+ on the 22.x line, Node.js 24.10 through 26.x, and Bun 1.2+ are supported. Other JavaScript runtimes may work when they provide compatible Node built-ins and process semantics.
|
|
183
185
|
|
|
184
186
|
## Publishing
|
|
185
187
|
|
package/SECURITY.md
CHANGED
|
@@ -6,4 +6,4 @@ The uv release URL is pinned to an exact validated version and fetched over HTTP
|
|
|
6
6
|
|
|
7
7
|
Provisioning uses in-process, worker, optional native advisory, and portable filesystem coordination followed by a post-lock capability check. Runtime dependency mutation uses uv's target-environment lock. Pyfig pins uv commands to the selected venv and removes configuration values that could redirect them. It reads an existing `pyproject.toml` only as a resolver input and never writes project manifests or requirements files.
|
|
8
8
|
|
|
9
|
-
Executable paths, project roots, version
|
|
9
|
+
Executable paths, project roots, version specifiers, package specifications, indexes, and proxy settings remain trusted configuration. Do not let an untrusted user choose `python`, `PYFIG_UV`, `cacheDir`, package arguments, or related environment variables in a privileged process. A checksum fetched from the same release protects transfer integrity, not a compromised upstream publisher.
|
package/dist/cjs/index.js
CHANGED
|
@@ -66,7 +66,7 @@ var __export = (target, all) => {
|
|
|
66
66
|
var exports_src = {};
|
|
67
67
|
__export(exports_src, {
|
|
68
68
|
DEFAULT_MANAGED_PYTHON: () => DEFAULT_MANAGED_PYTHON,
|
|
69
|
-
|
|
69
|
+
DEFAULT_REQUIRES_PYTHON: () => DEFAULT_REQUIRES_PYTHON,
|
|
70
70
|
DEFAULT_UV_RANGE: () => DEFAULT_UV_RANGE,
|
|
71
71
|
DEFAULT_UV_VERSION: () => DEFAULT_UV_VERSION,
|
|
72
72
|
createPyfig: () => createPyfig,
|
|
@@ -105,8 +105,20 @@ function* ancestors(startDirectory) {
|
|
|
105
105
|
}
|
|
106
106
|
}
|
|
107
107
|
|
|
108
|
+
// src/python-version.ts
|
|
109
|
+
var import_pep440 = require("@renovatebot/pep440");
|
|
110
|
+
function isValidPythonSpecifier(specifier) {
|
|
111
|
+
return import_pep440.validRange(specifier);
|
|
112
|
+
}
|
|
113
|
+
function isValidPythonVersion(version) {
|
|
114
|
+
return import_pep440.valid(version) !== null;
|
|
115
|
+
}
|
|
116
|
+
function pythonVersionSatisfies(version, specifier) {
|
|
117
|
+
return import_pep440.filter([version], specifier).length === 1;
|
|
118
|
+
}
|
|
119
|
+
|
|
108
120
|
// src/config.ts
|
|
109
|
-
var
|
|
121
|
+
var DEFAULT_REQUIRES_PYTHON = ">=3.10,<4";
|
|
110
122
|
var DEFAULT_MANAGED_PYTHON = "3.12";
|
|
111
123
|
var DEFAULT_UV_VERSION = "0.13.6";
|
|
112
124
|
var DEFAULT_UV_RANGE = ">=0.10.0 <1.0.0";
|
|
@@ -122,13 +134,13 @@ var envPathsFunction = (() => {
|
|
|
122
134
|
})();
|
|
123
135
|
function resolveOptions(options = {}) {
|
|
124
136
|
const env = { ...options.env ?? process.env };
|
|
125
|
-
const
|
|
137
|
+
const requiresPython = options.requiresPython ?? env["PYFIG_REQUIRES_PYTHON"] ?? DEFAULT_REQUIRES_PYTHON;
|
|
126
138
|
const uvRange = options.uvRange ?? env["PYFIG_UV_RANGE"] ?? DEFAULT_UV_RANGE;
|
|
127
|
-
|
|
139
|
+
assertPythonSpecifier(requiresPython);
|
|
128
140
|
assertRange("uv", uvRange);
|
|
129
141
|
const managedPython = options.managedPython ?? env["PYFIG_MANAGED_PYTHON"] ?? DEFAULT_MANAGED_PYTHON;
|
|
130
142
|
const uvVersion = options.uvVersion ?? env["PYFIG_UV_VERSION"] ?? DEFAULT_UV_VERSION;
|
|
131
|
-
assertManagedPythonRequest(managedPython,
|
|
143
|
+
assertManagedPythonRequest(managedPython, requiresPython);
|
|
132
144
|
assertUvVersion(uvVersion, uvRange);
|
|
133
145
|
const probeTimeoutMs = resolveDuration("probeTimeoutMs", options.probeTimeoutMs, env["PYFIG_PROBE_TIMEOUT_MS"], DEFAULT_PROBE_TIMEOUT_MS);
|
|
134
146
|
const installTimeoutMs = resolveDuration("installTimeoutMs", options.installTimeoutMs, env["PYFIG_INSTALL_TIMEOUT_MS"], DEFAULT_INSTALL_TIMEOUT_MS);
|
|
@@ -141,7 +153,7 @@ function resolveOptions(options = {}) {
|
|
|
141
153
|
const autoInstall = options.autoInstall ?? parseBoolean(env["PYFIG_AUTO_INSTALL"], true);
|
|
142
154
|
return {
|
|
143
155
|
python: options.python ?? env["PYFIG_PYTHON"] ?? "",
|
|
144
|
-
|
|
156
|
+
requiresPython,
|
|
145
157
|
managedPython,
|
|
146
158
|
uvVersion,
|
|
147
159
|
uvRange,
|
|
@@ -182,14 +194,19 @@ function assertUvVersion(version, range) {
|
|
|
182
194
|
throw new TypeError(`Pinned uv ${version} does not satisfy supported range ${range}.`);
|
|
183
195
|
}
|
|
184
196
|
}
|
|
185
|
-
function
|
|
197
|
+
function assertPythonSpecifier(specifier) {
|
|
198
|
+
if (!isValidPythonSpecifier(specifier)) {
|
|
199
|
+
throw new TypeError(`Invalid requires-python PEP 440 specifier: ${specifier}`);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
function assertManagedPythonRequest(request, requiresPython) {
|
|
186
203
|
if (!request || /[\\/\0\r\n]/u.test(request) || request.includes("..")) {
|
|
187
204
|
throw new TypeError(`Invalid managed Python request: ${request}`);
|
|
188
205
|
}
|
|
189
206
|
if (/^\d+\.\d+(?:\.\d+)?$/u.test(request)) {
|
|
190
207
|
const normalized = request.split(".").length === 2 ? `${request}.0` : request;
|
|
191
|
-
if (!
|
|
192
|
-
throw new TypeError(`Managed Python ${request} cannot satisfy
|
|
208
|
+
if (!pythonVersionSatisfies(normalized, requiresPython)) {
|
|
209
|
+
throw new TypeError(`Managed Python ${request} cannot satisfy requires-python ${requiresPython}.`);
|
|
193
210
|
}
|
|
194
211
|
}
|
|
195
212
|
}
|
|
@@ -222,7 +239,7 @@ var import_node_os = __toESM(require("node:os"), 1);
|
|
|
222
239
|
var import_node_path5 = __toESM(require("node:path"), 1);
|
|
223
240
|
var import_node_stream = require("node:stream");
|
|
224
241
|
var import_promises2 = require("node:stream/promises");
|
|
225
|
-
var
|
|
242
|
+
var import_fflate = require("fflate");
|
|
226
243
|
var tar = __toESM(require("tar"), 1);
|
|
227
244
|
var import_undici = require("undici");
|
|
228
245
|
|
|
@@ -512,7 +529,15 @@ if os.path.isfile(header_path) and library_path:
|
|
|
512
529
|
}
|
|
513
530
|
|
|
514
531
|
release_level = sys.version_info.releaselevel
|
|
515
|
-
|
|
532
|
+
release_suffixes = {
|
|
533
|
+
"alpha": "a",
|
|
534
|
+
"beta": "b",
|
|
535
|
+
"candidate": "rc",
|
|
536
|
+
"final": "",
|
|
537
|
+
}
|
|
538
|
+
release_suffix = release_suffixes[release_level]
|
|
539
|
+
if release_suffix:
|
|
540
|
+
release_suffix += str(sys.version_info.serial)
|
|
516
541
|
|
|
517
542
|
print(json.dumps({
|
|
518
543
|
"executable": sys.executable,
|
|
@@ -532,8 +557,8 @@ print(json.dumps({
|
|
|
532
557
|
"embedding": embedding,
|
|
533
558
|
}))
|
|
534
559
|
`;
|
|
535
|
-
function
|
|
536
|
-
return
|
|
560
|
+
function pythonSatisfiesPolicy(python, options) {
|
|
561
|
+
return pythonVersionSatisfies(python.version, options.requiresPython) && (!options.requireEmbedding || python.implementation === "cpython" && python.embedding !== null && pythonArchitectureMatchesRuntime(python)) && (!options.requireGil || !python.gilDisabled);
|
|
537
562
|
}
|
|
538
563
|
async function probePython(candidate, options) {
|
|
539
564
|
const executable = await resolveExecutable(candidate.executable, options.env);
|
|
@@ -542,7 +567,7 @@ async function probePython(candidate, options) {
|
|
|
542
567
|
try {
|
|
543
568
|
const { stdout } = run(executable, [...candidate.args, "-c", PYTHON_PROBE], options.env, options.probeTimeoutMs);
|
|
544
569
|
const info = JSON.parse(stdout.trim());
|
|
545
|
-
return
|
|
570
|
+
return isValidPythonVersion(info.version) ? info : null;
|
|
546
571
|
} catch {
|
|
547
572
|
return null;
|
|
548
573
|
}
|
|
@@ -585,14 +610,14 @@ function pythonArchitectureMatchesRuntime(python) {
|
|
|
585
610
|
async function ensureUv(options = {}) {
|
|
586
611
|
return ensureUvResolved(resolveOptions(options));
|
|
587
612
|
}
|
|
588
|
-
async function ensureEnvironment(options = {},
|
|
613
|
+
async function ensureEnvironment(options = {}, dependencies = [], dependencyOptions = {}) {
|
|
589
614
|
const resolved = resolveOptions(options);
|
|
590
|
-
const normalized =
|
|
615
|
+
const normalized = normalizeDependencies(dependencies);
|
|
591
616
|
const selections = normalizeDependencyOptions(dependencyOptions);
|
|
592
617
|
const uv = await ensureUvResolved(resolved);
|
|
593
618
|
const python = await ensureVirtualEnvironmentResolved(resolved, uv);
|
|
594
619
|
if (normalized.length > 0 || selections.extras.length > 0 || selections.groups.length > 0 || selections.allExtras) {
|
|
595
|
-
await
|
|
620
|
+
await installRuntimeDependencies(uv, python, normalized, selections, resolved);
|
|
596
621
|
}
|
|
597
622
|
return {
|
|
598
623
|
python,
|
|
@@ -607,7 +632,7 @@ async function ensureVirtualEnvironmentResolved(options, uv) {
|
|
|
607
632
|
const directory = virtualEnvironmentDirectory(options.projectRoot);
|
|
608
633
|
const check = async () => {
|
|
609
634
|
const candidate = await inspectVirtualEnvironment(directory, options);
|
|
610
|
-
return candidate &&
|
|
635
|
+
return candidate && pythonSatisfiesPolicy(candidate, options) && await virtualEnvironmentMatchesConfiguredPython(candidate, options) ? candidate : null;
|
|
611
636
|
};
|
|
612
637
|
if (!options.createVirtualEnvironment) {
|
|
613
638
|
throw new Error(`No compatible environment is available and creation is disabled. ` + `Pyfig would create ${directory}.`);
|
|
@@ -640,7 +665,7 @@ async function ensureVirtualEnvironmentResolved(options, uv) {
|
|
|
640
665
|
async function findReusableEnvironment(options) {
|
|
641
666
|
if (options.python) {
|
|
642
667
|
const configured = await probePython({ executable: options.python, args: [] }, options);
|
|
643
|
-
if (!configured || !
|
|
668
|
+
if (!configured || !pythonSatisfiesPolicy(configured, options)) {
|
|
644
669
|
throw incompatibleConfiguredPython(options);
|
|
645
670
|
}
|
|
646
671
|
if (configured.virtualEnvironment)
|
|
@@ -651,7 +676,7 @@ async function findReusableEnvironment(options) {
|
|
|
651
676
|
virtualEnvironmentDirectory(options.projectRoot)
|
|
652
677
|
]) {
|
|
653
678
|
const info = await inspectVirtualEnvironment(directory, options);
|
|
654
|
-
if (info &&
|
|
679
|
+
if (info && pythonSatisfiesPolicy(info, options) && await virtualEnvironmentMatchesConfiguredPython(info, options)) {
|
|
655
680
|
return info;
|
|
656
681
|
}
|
|
657
682
|
}
|
|
@@ -660,13 +685,13 @@ async function findReusableEnvironment(options) {
|
|
|
660
685
|
async function ensureBasePythonResolved(uv, options) {
|
|
661
686
|
if (options.python) {
|
|
662
687
|
const configured = await probePython({ executable: options.python, args: [] }, options);
|
|
663
|
-
if (!configured || !
|
|
688
|
+
if (!configured || !pythonSatisfiesPolicy(configured, options)) {
|
|
664
689
|
throw incompatibleConfiguredPython(options);
|
|
665
690
|
}
|
|
666
691
|
if (!configured.virtualEnvironment)
|
|
667
692
|
return configured;
|
|
668
693
|
const base = await probePython({ executable: configured.baseExecutable, args: [] }, options);
|
|
669
|
-
if (base &&
|
|
694
|
+
if (base && pythonSatisfiesPolicy(base, options))
|
|
670
695
|
return base;
|
|
671
696
|
throw incompatibleConfiguredPython(options);
|
|
672
697
|
}
|
|
@@ -708,7 +733,7 @@ async function findPythonWithUv(uv, options, source) {
|
|
|
708
733
|
try {
|
|
709
734
|
const { stdout } = await runAsync(uv.executable, args, explicitPythonSourceEnvironment(options), options.probeTimeoutMs);
|
|
710
735
|
const info = await probePython({ executable: stdout.trim(), args: [] }, options);
|
|
711
|
-
if (info &&
|
|
736
|
+
if (info && pythonSatisfiesPolicy(info, options))
|
|
712
737
|
return info;
|
|
713
738
|
} catch {}
|
|
714
739
|
}
|
|
@@ -726,7 +751,7 @@ async function findPythonWithUv(uv, options, source) {
|
|
|
726
751
|
const candidates = installedPythonPaths(stdout);
|
|
727
752
|
for (const executable of candidates) {
|
|
728
753
|
const info = await probePython({ executable, args: [] }, options);
|
|
729
|
-
if (info && !info.virtualEnvironment &&
|
|
754
|
+
if (info && !info.virtualEnvironment && pythonSatisfiesPolicy(info, options)) {
|
|
730
755
|
return info;
|
|
731
756
|
}
|
|
732
757
|
}
|
|
@@ -750,7 +775,7 @@ async function inspectVirtualEnvironment(directory, options) {
|
|
|
750
775
|
const info = await probePython({ executable, args: [] }, options);
|
|
751
776
|
return info?.virtualEnvironment && pathsEqual(info.virtualEnvironment, directory) ? info : null;
|
|
752
777
|
}
|
|
753
|
-
async function
|
|
778
|
+
async function installRuntimeDependencies(uv, python, dependencies, dependencyOptions, options) {
|
|
754
779
|
if (!python.virtualEnvironment) {
|
|
755
780
|
throw new Error("Runtime dependency installation requires a virtual environment.");
|
|
756
781
|
}
|
|
@@ -759,18 +784,18 @@ async function installRuntimeRequirements(uv, python, requirements, dependencyOp
|
|
|
759
784
|
timeoutMs: options.coordinationTimeoutMs,
|
|
760
785
|
check: async () => null,
|
|
761
786
|
mutate: async () => {
|
|
762
|
-
const temporary = await import_promises.mkdtemp(import_node_path5.default.join(import_node_os.default.tmpdir(), "pyfig-
|
|
787
|
+
const temporary = await import_promises.mkdtemp(import_node_path5.default.join(import_node_os.default.tmpdir(), "pyfig-dependencies-"));
|
|
763
788
|
try {
|
|
764
|
-
const additions = import_node_path5.default.join(temporary, "runtime-
|
|
765
|
-
const resolved = import_node_path5.default.join(temporary, "resolved-
|
|
789
|
+
const additions = import_node_path5.default.join(temporary, "runtime-dependencies.txt");
|
|
790
|
+
const resolved = import_node_path5.default.join(temporary, "resolved-dependencies.txt");
|
|
766
791
|
const pyproject = import_node_path5.default.join(options.projectRoot, "pyproject.toml");
|
|
767
792
|
const hasPyproject = import_node_fs4.default.existsSync(pyproject);
|
|
768
793
|
if (!hasPyproject && (dependencyOptions.extras.length > 0 || dependencyOptions.groups.length > 0 || dependencyOptions.allExtras)) {
|
|
769
794
|
throw new Error("Runtime extras and groups require an existing pyproject.toml.");
|
|
770
795
|
}
|
|
771
796
|
const sources = hasPyproject ? [pyproject] : [];
|
|
772
|
-
if (
|
|
773
|
-
await import_promises.writeFile(additions, `${
|
|
797
|
+
if (dependencies.length > 0) {
|
|
798
|
+
await import_promises.writeFile(additions, `${dependencies.join(`
|
|
774
799
|
`)}
|
|
775
800
|
`, "utf8");
|
|
776
801
|
sources.push(additions);
|
|
@@ -829,11 +854,11 @@ async function findAvailableUv(options) {
|
|
|
829
854
|
const managed = probeUv(uvExecutable(options), options);
|
|
830
855
|
return managed?.version === options.uvVersion ? managed : null;
|
|
831
856
|
}
|
|
832
|
-
function
|
|
833
|
-
const normalized =
|
|
834
|
-
for (const
|
|
835
|
-
if (!
|
|
836
|
-
throw new TypeError(`Invalid runtime
|
|
857
|
+
function normalizeDependencies(dependencies) {
|
|
858
|
+
const normalized = dependencies.map((dependency) => dependency.trim());
|
|
859
|
+
for (const dependency of normalized) {
|
|
860
|
+
if (!dependency || /[\0\r\n]/u.test(dependency)) {
|
|
861
|
+
throw new TypeError(`Invalid runtime dependency: ${JSON.stringify(dependency)}`);
|
|
837
862
|
}
|
|
838
863
|
}
|
|
839
864
|
return [...new Set(normalized)];
|
|
@@ -870,17 +895,17 @@ function targetUvEnvironment(options, python) {
|
|
|
870
895
|
return uvCommandEnvironment(uvEnvironment(options), python);
|
|
871
896
|
}
|
|
872
897
|
function incompatibleConfiguredPython(options) {
|
|
873
|
-
return new Error(`Configured Python ${options.python} is unavailable or does not satisfy ` +
|
|
898
|
+
return new Error(`Configured Python ${options.python} is unavailable or does not satisfy ` + `requires-python ${options.requiresPython}` + `${options.requireEmbedding ? " with CPython embedding files" : ""}.`);
|
|
874
899
|
}
|
|
875
900
|
function missingPythonMessage(options, reason) {
|
|
876
901
|
const embedding = options.requireEmbedding ? " with compatible CPython headers, library, architecture, and GIL ABI" : "";
|
|
877
|
-
return `No Python satisfies ${options.
|
|
902
|
+
return `No Python satisfies requires-python ${options.requiresPython}${embedding}; ${reason}.`;
|
|
878
903
|
}
|
|
879
904
|
async function virtualEnvironmentMatchesConfiguredPython(virtualEnvironment, options) {
|
|
880
905
|
if (!options.python)
|
|
881
906
|
return true;
|
|
882
907
|
const configured = await probePython({ executable: options.python, args: [] }, options);
|
|
883
|
-
if (!configured || !
|
|
908
|
+
if (!configured || !pythonSatisfiesPolicy(configured, options))
|
|
884
909
|
return false;
|
|
885
910
|
if (configured.virtualEnvironment && pathsEqual(configured.executable, virtualEnvironment.executable)) {
|
|
886
911
|
return true;
|
|
@@ -909,11 +934,7 @@ async function installUv(installDir, options) {
|
|
|
909
934
|
}
|
|
910
935
|
const extracted = import_node_path5.default.join(temporary, "extracted");
|
|
911
936
|
await import_promises.mkdir(extracted, { recursive: true });
|
|
912
|
-
|
|
913
|
-
await import_extract_zip.default(archive, { dir: extracted });
|
|
914
|
-
else
|
|
915
|
-
await tar.x({ file: archive, cwd: extracted, strict: true });
|
|
916
|
-
const source = await findFile(extracted, process.platform === "win32" ? "uv.exe" : "uv");
|
|
937
|
+
const source = await extractUvExecutable(archive, asset, extracted);
|
|
917
938
|
if (!source)
|
|
918
939
|
throw new Error(`uv archive did not contain the expected executable: ${asset}`);
|
|
919
940
|
await publishUvAtomically(source, installDir, options);
|
|
@@ -922,6 +943,22 @@ async function installUv(installDir, options) {
|
|
|
922
943
|
await import_promises.rm(temporary, { recursive: true, force: true });
|
|
923
944
|
}
|
|
924
945
|
}
|
|
946
|
+
async function extractUvExecutable(archive, asset, destination) {
|
|
947
|
+
const executableName = process.platform === "win32" ? "uv.exe" : "uv";
|
|
948
|
+
if (!asset.endsWith(".zip")) {
|
|
949
|
+
await tar.x({ file: archive, cwd: destination, strict: true });
|
|
950
|
+
return findFile(destination, executableName);
|
|
951
|
+
}
|
|
952
|
+
const entries = import_fflate.unzipSync(await import_promises.readFile(archive), {
|
|
953
|
+
filter: ({ name }) => import_node_path5.default.posix.basename(name.replaceAll("\\", "/")) === executableName
|
|
954
|
+
});
|
|
955
|
+
const matches = Object.entries(entries).filter(([name]) => import_node_path5.default.posix.basename(name.replaceAll("\\", "/")) === executableName);
|
|
956
|
+
if (matches.length !== 1 || !matches[0])
|
|
957
|
+
return null;
|
|
958
|
+
const source = import_node_path5.default.join(destination, executableName);
|
|
959
|
+
await import_promises.writeFile(source, matches[0][1], { flag: "wx" });
|
|
960
|
+
return source;
|
|
961
|
+
}
|
|
925
962
|
async function publishUvAtomically(source, installDir, options) {
|
|
926
963
|
await import_promises.mkdir(installDir, { recursive: true });
|
|
927
964
|
const executable = uvExecutable(options);
|
|
@@ -1091,7 +1128,7 @@ function createPyfig(options = {}) {
|
|
|
1091
1128
|
const baseEnvironment = import_loqkit2.memoize(() => ensureEnvironment(scopedOptions));
|
|
1092
1129
|
const uvInfo = import_loqkit2.memoize(() => ensureUv(scopedOptions));
|
|
1093
1130
|
return {
|
|
1094
|
-
prepare: (
|
|
1131
|
+
prepare: (dependencies = [], dependencyOptions = {}) => dependencies.length === 0 && !dependencyOptions.allExtras && (dependencyOptions.extras?.length ?? 0) === 0 && (dependencyOptions.groups?.length ?? 0) === 0 ? baseEnvironment() : ensureEnvironment(scopedOptions, dependencies, dependencyOptions),
|
|
1095
1132
|
pythonInfo: async () => (await baseEnvironment()).python,
|
|
1096
1133
|
uvInfo,
|
|
1097
1134
|
uv: (args = [], spawnOptions = {}) => uvSpawn(args, spawnOptions, scopedOptions),
|
|
@@ -1099,4 +1136,4 @@ function createPyfig(options = {}) {
|
|
|
1099
1136
|
};
|
|
1100
1137
|
}
|
|
1101
1138
|
|
|
1102
|
-
//# debugId=
|
|
1139
|
+
//# debugId=5564427996CDDBA964756E2164756E21
|