pyfig 0.7.0 → 0.7.1
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 +76 -178
- package/SECURITY.md +16 -5
- package/dist/cjs/index.js +482 -1131
- package/dist/cjs/index.js.map +6 -16
- package/dist/index.d.ts +2 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -5
- package/dist/index.js.map +1 -1
- package/dist/process.d.ts +6 -12
- package/dist/process.d.ts.map +1 -1
- package/dist/process.js +35 -54
- package/dist/process.js.map +1 -1
- package/dist/pyfig.d.ts +31 -0
- package/dist/pyfig.d.ts.map +1 -0
- package/dist/pyfig.js +275 -0
- package/dist/pyfig.js.map +1 -0
- package/dist/uv.d.ts +3 -0
- package/dist/uv.d.ts.map +1 -0
- package/dist/uv.js +423 -0
- package/dist/uv.js.map +1 -0
- package/package.json +6 -12
- package/dist/cjs/sync-resolver.js +0 -1342
- package/dist/cjs/sync-resolver.js.map +0 -20
- package/dist/cli.d.ts +0 -3
- package/dist/cli.d.ts.map +0 -1
- package/dist/cli.js +0 -57
- package/dist/cli.js.map +0 -1
- package/dist/client.d.ts +0 -15
- package/dist/client.d.ts.map +0 -1
- package/dist/client.js +0 -35
- package/dist/client.js.map +0 -1
- package/dist/config.d.ts +0 -69
- package/dist/config.d.ts.map +0 -1
- package/dist/config.js +0 -159
- package/dist/config.js.map +0 -1
- package/dist/package-target.d.ts +0 -8
- package/dist/package-target.d.ts.map +0 -1
- package/dist/package-target.js +0 -81
- package/dist/package-target.js.map +0 -1
- package/dist/pep440.d.ts +0 -12
- package/dist/pep440.d.ts.map +0 -1
- package/dist/pep440.js +0 -21
- package/dist/pep440.js.map +0 -1
- package/dist/probes.d.ts +0 -37
- package/dist/probes.d.ts.map +0 -1
- package/dist/probes.js +0 -145
- package/dist/probes.js.map +0 -1
- package/dist/project-root.d.ts +0 -3
- package/dist/project-root.d.ts.map +0 -1
- package/dist/project-root.js +0 -22
- package/dist/project-root.js.map +0 -1
- package/dist/provision-lock.d.ts +0 -10
- package/dist/provision-lock.d.ts.map +0 -1
- package/dist/provision-lock.js +0 -24
- package/dist/provision-lock.js.map +0 -1
- package/dist/provision.d.ts +0 -20
- package/dist/provision.d.ts.map +0 -1
- package/dist/provision.js +0 -673
- package/dist/provision.js.map +0 -1
- package/dist/python-venv.d.ts +0 -12
- package/dist/python-venv.d.ts.map +0 -1
- package/dist/python-venv.js +0 -52
- package/dist/python-venv.js.map +0 -1
- package/dist/runners.d.ts +0 -14
- package/dist/runners.d.ts.map +0 -1
- package/dist/runners.js +0 -71
- package/dist/runners.js.map +0 -1
- package/dist/runtime-files.d.ts +0 -3
- package/dist/runtime-files.d.ts.map +0 -1
- package/dist/runtime-files.js +0 -12
- package/dist/runtime-files.js.map +0 -1
- package/dist/sync-resolver.d.ts +0 -2
- package/dist/sync-resolver.d.ts.map +0 -1
- package/dist/sync-resolver.js +0 -15
- package/dist/sync-resolver.js.map +0 -1
- package/dist/venv-identity.d.ts +0 -36
- package/dist/venv-identity.d.ts.map +0 -1
- package/dist/venv-identity.js +0 -80
- package/dist/venv-identity.js.map +0 -1
package/README.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# pyfig
|
|
2
2
|
|
|
3
|
-
`pyfig`
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
`pyfig` resolves compatible uv and Python executables from Node.js or Bun and
|
|
4
|
+
exposes one guarded `uv run` spawn method. Its only managed state is a uv tool
|
|
5
|
+
cache; uv retains responsibility for Python, environments, and dependencies.
|
|
6
6
|
|
|
7
7
|
## Install
|
|
8
8
|
|
|
@@ -10,206 +10,100 @@ Pyfig never invokes standalone `pip`, discovers Python project metadata, edits c
|
|
|
10
10
|
bun add pyfig
|
|
11
11
|
```
|
|
12
12
|
|
|
13
|
+
Pyfig reuses a compatible uv from `PATH` or its OS cache. When neither exists,
|
|
14
|
+
it installs the greatest official uv release allowed by the requested version.
|
|
15
|
+
Python discovery and managed installation are delegated to
|
|
16
|
+
[`uv python find`](https://docs.astral.sh/uv/reference/cli/#uv-python-find).
|
|
17
|
+
|
|
13
18
|
## Use
|
|
14
19
|
|
|
15
20
|
```ts
|
|
16
|
-
import {
|
|
21
|
+
import { pyfig } from 'pyfig';
|
|
17
22
|
|
|
18
|
-
const
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
requiresPython: '>=3.11,<4',
|
|
22
|
-
requiresUv: '>=0.10,<1',
|
|
23
|
+
const python = await pyfig({
|
|
24
|
+
uv: '>=0.10,<1',
|
|
25
|
+
python: '>=3.11,<4',
|
|
23
26
|
requireEmbedding: true,
|
|
24
27
|
requireGil: true,
|
|
25
28
|
});
|
|
26
29
|
|
|
27
|
-
|
|
30
|
+
console.log(python.uv);
|
|
31
|
+
console.log(python.python);
|
|
28
32
|
|
|
29
|
-
|
|
30
|
-
console.log(environment.python);
|
|
31
|
-
console.log(environment.managed);
|
|
32
|
-
console.log(environment.venvId);
|
|
33
|
-
|
|
34
|
-
await pyenv.uv(
|
|
35
|
-
['run', '--', 'python', 'script.py'],
|
|
36
|
-
{ stdio: 'inherit' },
|
|
37
|
-
);
|
|
33
|
+
python.run('python', '--version', { stdio: 'inherit' });
|
|
38
34
|
```
|
|
39
35
|
|
|
40
|
-
`
|
|
41
|
-
|
|
42
|
-
The same uv-only surface handles direct commands:
|
|
36
|
+
`pyfig()` returns a `PythonContext` containing:
|
|
43
37
|
|
|
44
|
-
|
|
45
|
-
|
|
38
|
+
| Field | Meaning |
|
|
39
|
+
| --- | --- |
|
|
40
|
+
| `uv` | Absolute path to the resolved uv executable. |
|
|
41
|
+
| `python` | Absolute path to the resolved Python interpreter. |
|
|
42
|
+
| `run(...args)` | Spawn `uv run` with that Python interpreter. |
|
|
46
43
|
|
|
47
|
-
|
|
44
|
+
`run()` treats a final object as Node `SpawnOptions`. When no `--` argument is
|
|
45
|
+
present, every string is treated as command argv:
|
|
48
46
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
);
|
|
47
|
+
```ts
|
|
48
|
+
python.run('python', '--version');
|
|
49
|
+
// uv run --python /resolved/python -- python --version
|
|
53
50
|
```
|
|
54
51
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
The async spawn helper returns `Promise<ChildProcess>` because uv or environment setup may happen before spawning. The synchronous helper performs setup through `process.execPath`, so a Bun process remains Bun and does not assume a `node` command exists.
|
|
58
|
-
|
|
59
|
-
## Selection policy
|
|
60
|
-
|
|
61
|
-
Pyfig uses this order:
|
|
62
|
-
|
|
63
|
-
1. A compatible configured or `PATH` uv.
|
|
64
|
-
2. Pyfig's cached, checksum-verified uv.
|
|
65
|
-
3. For callers without dependencies or requirements files, a compatible explicitly configured venv, active `VIRTUAL_ENV`, or root `.venv`.
|
|
66
|
-
4. A compatible base interpreter from those venvs, then every installed system Python reported by uv with downloads disabled.
|
|
67
|
-
5. An already-cached uv-managed Python that passes the same checks.
|
|
68
|
-
6. A managed Python download, only when installation policy allows it.
|
|
69
|
-
7. An existing hashed venv for the exact interpreter and resolved dependencies, or a new venv below `node_modules/.pyfig/venvs`.
|
|
70
|
-
|
|
71
|
-
An explicit `python` option is strict: an unavailable or incompatible interpreter is an error. Without runtime requirements, an explicitly configured compatible venv can be reused. With dependencies or requirements files, its base interpreter is used for a private venv. Incompatible external venvs are always preserved. Only an incomplete or damaged Pyfig-managed venv may be rebuilt.
|
|
72
|
-
|
|
73
|
-

|
|
74
|
-
|
|
75
|
-
[Mermaid source](docs/diagrams/python-selection.mmd)
|
|
76
|
-
|
|
77
|
-
Native consumers can set `requireEmbedding: true` to require CPython, `Python.h`, a linkable embedding library, and architecture/pointer-width compatibility with the JavaScript runtime. Set `requireGil: true` when the native addon requires the standard GIL-enabled ABI. These checks apply to system, existing-venv, cached, and downloaded interpreters alike.
|
|
78
|
-
|
|
79
|
-
## Runtime dependency resolution
|
|
52
|
+
When `--` is present, strings before it are passed to uv and strings after it
|
|
53
|
+
are command argv:
|
|
80
54
|
|
|
81
55
|
```ts
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
56
|
+
python.run('--no-project', '--with', 'httpx', '--', 'python', 'script.py', {
|
|
57
|
+
cwd: '/workspace',
|
|
58
|
+
stdio: 'inherit',
|
|
85
59
|
});
|
|
86
|
-
|
|
87
|
-
await pyenv.prepare();
|
|
88
|
-
```
|
|
89
|
-
|
|
90
|
-
Resolution follows the equivalent of:
|
|
91
|
-
|
|
92
|
-
```sh
|
|
93
|
-
uv pip compile /project/requirements/runtime.txt \
|
|
94
|
-
/temporary/runtime-dependencies.txt \
|
|
95
|
-
--python /selected/base/python \
|
|
96
|
-
--no-header --no-annotate --generate-hashes \
|
|
97
|
-
--output-file /temporary/resolved-dependencies.txt
|
|
98
|
-
|
|
99
|
-
uv venv --python /selected/base/python \
|
|
100
|
-
node_modules/.pyfig/venvs/cpython-3.12.7-0123456789abcdef0123
|
|
101
|
-
|
|
102
|
-
uv pip install \
|
|
103
|
-
--python /hashed/venv/python \
|
|
104
|
-
-r /temporary/resolved-dependencies.txt
|
|
105
60
|
```
|
|
106
61
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
Raw `pyenv.uv(['pip', ...])` remains an intentional low-level escape hatch. It can mutate the selected venv after publication and bypass the content-addressing guarantee. Use constructor-level `dependencies` or `requirements` when isolation and repeatability matter.
|
|
112
|
-
|
|
113
|
-
Separate venvs work for subprocesses and isolated workers. Native libraries that initialize CPython in the same JavaScript process must use one combined compatible dependency set or move each interpreter into a separate worker process; two venv directories cannot provide two independent process-global CPython runtimes.
|
|
114
|
-
|
|
115
|
-

|
|
116
|
-
|
|
117
|
-
[Mermaid source](docs/diagrams/runtime-dependencies.mmd)
|
|
118
|
-
|
|
119
|
-
## Project root
|
|
120
|
-
|
|
121
|
-
Pyfig does not interpret `package.json`, workspace metadata, `pyproject.toml`, or lockfiles. The root only locates private venv storage and resolves relative requirements-file paths. Root precedence is:
|
|
62
|
+
Pyfig inserts `--python <python>` immediately before the separator. Caller
|
|
63
|
+
supplied `--python <value>` and `--python=<value>` arguments are rejected before
|
|
64
|
+
`--`; Python is selected only by `pyfig()`. A `--python` appearing after
|
|
65
|
+
`--` belongs to the command and is left untouched.
|
|
122
66
|
|
|
123
|
-
|
|
124
|
-
2. `PYFIG_PROJECT_ROOT`
|
|
125
|
-
3. nearest Git worktree marker above `INIT_CWD`, when set, otherwise the current directory
|
|
126
|
-
4. that starting directory when no Git marker exists
|
|
67
|
+
`pyfig()` accepts:
|
|
127
68
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
## Public API
|
|
131
|
-
|
|
132
|
-
| Export | Responsibility |
|
|
133
|
-
| --- | --- |
|
|
134
|
-
| `new Python(options)` | Bind one immutable venv configuration and memoize its preparation. |
|
|
135
|
-
| `pyenv.prepare()` | Resolve and prepare that configuration's exact venv. |
|
|
136
|
-
| `pyenv.pythonInfo()` | Return the selected interpreter and embedding metadata. |
|
|
137
|
-
| `pyenv.uvInfo()` | Return the selected uv executable and version. |
|
|
138
|
-
| `pyenv.uv()` / `pyenv.uvSync()` | Spawn uv and pin pip/run commands to the same venv. |
|
|
139
|
-
| `ensurePython(options?)` | Standalone Python preparation API using the same options. |
|
|
140
|
-
| `ensureUv(options?)` | Prefer existing uv, otherwise bootstrap verified uv when allowed. |
|
|
141
|
-
| `uvSpawn()` / `uvSpawnSync()` | Standalone uv spawn API. |
|
|
142
|
-
| `resolveProjectRoot(start?)` | Resolve the nearest Git root without reading manifests. |
|
|
143
|
-
|
|
144
|
-
The package exposes matching ESM and CommonJS entry points. Probe, download, lock, and command-policy internals are not exported.
|
|
145
|
-
|
|
146
|
-
## Configuration
|
|
147
|
-
|
|
148
|
-
Precedence is API or CLI option, environment variable, then default.
|
|
149
|
-
|
|
150
|
-
`requiresPython` and `requiresUv` use PEP 440 specifiers. Comma-separated clauses, compatible releases (`~=`), exclusions (`!=`), wildcard equality, arbitrary equality (`===`), and pre-release versions are supported. For example: `requiresPython: '>=3.10,!=3.12.*,<4'` and `requiresUv: '==0.13.*'`. Pyfig reuses a compatible installed or cached uv first. When installation is necessary, it resolves the greatest published uv release satisfying `requiresUv` and containing the required platform assets.
|
|
151
|
-
|
|
152
|
-
When embedding files are available, `PythonInfo.embedding.stableAbiLibraryPath` identifies the platform's Python 3 Stable ABI import library when the installation provides one. Consumers can use it to build a single Limited API binary for multiple CPython 3 minor versions while retaining `libraryPath` for version-specific embedding.
|
|
153
|
-
|
|
154
|
-
| API option | Environment variable | Default |
|
|
69
|
+
| Option | Default | Meaning |
|
|
155
70
|
| --- | --- | --- |
|
|
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
|
-
bunx --bun pyfig --check
|
|
182
|
-
bunx --bun pyfig --requires-python '>=3.12,<3.14'
|
|
183
|
-
bunx --bun pyfig --requires-uv '>=0.12,<1'
|
|
184
|
-
bunx --bun pyfig --dependency 'requests>=2.32' --requirement requirements/runtime.txt
|
|
185
|
-
bunx --bun pyfig --require-embedding --require-gil
|
|
186
|
-
bunx --bun pyfig --project-root /path/to/repository
|
|
187
|
-
bunx --bun pyfig --json
|
|
188
|
-
```
|
|
189
|
-
|
|
190
|
-
`bunx --bun` runs the CLI with Bun and does not require a separate Node.js executable.
|
|
191
|
-
|
|
192
|
-
## Concurrency and installation safety
|
|
71
|
+
| `uv` | `>=0.10,<1` | uv executable or PEP 440 version constraint. |
|
|
72
|
+
| `python` | system, then managed | Python executable or PEP 440 version constraint. |
|
|
73
|
+
| `requireEmbedding` | `false` | Require CPython headers, a linkable library, and matching architecture. |
|
|
74
|
+
| `requireGil` | `false` | Reject free-threaded Python builds. |
|
|
75
|
+
| `cwd` | `process.cwd()` | Directory used for uv configuration and project discovery. |
|
|
76
|
+
| `env` | `process.env` | Environment used to locate executables and invoke child processes. |
|
|
77
|
+
|
|
78
|
+
For both `uv` and `python`, Pyfig first checks whether the value resolves to an
|
|
79
|
+
executable. An executable wins over version parsing. An explicit uv executable
|
|
80
|
+
is trusted without a version check or setup. An explicit Python executable skips
|
|
81
|
+
version selection and installation, but it must still be a working Python and
|
|
82
|
+
satisfy requested embedding, architecture, and GIL capabilities. A value that
|
|
83
|
+
is not executable must be a valid PEP 440 specifier.
|
|
84
|
+
|
|
85
|
+
For a Python version specifier, Pyfig first asks uv for a matching system
|
|
86
|
+
interpreter. If it is absent or fails the requested capabilities, Pyfig asks uv
|
|
87
|
+
for a managed interpreter. For a uv version specifier, Pyfig checks `PATH`, then
|
|
88
|
+
its cache, then downloads a matching checksum-verified release. Cached
|
|
89
|
+
executables are used without a metadata or network request. Release metadata is
|
|
90
|
+
cached for 24 hours and refreshed under Loqkit's layered check-lock-check flow.
|
|
91
|
+
The cache lives below `%LOCALAPPDATA%\pyfig` on Windows,
|
|
92
|
+
`~/Library/Caches/pyfig` on macOS, and `${XDG_CACHE_HOME:-~/.cache}/pyfig` on
|
|
93
|
+
Linux.
|
|
94
|
+
|
|
95
|
+
Executable paths can be used directly:
|
|
193
96
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
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.
|
|
201
|
-
|
|
202
|
-
## Publishing
|
|
203
|
-
|
|
204
|
-
Pyfig publishes from a `v<package-version>` Git tag through `.github/workflows/publish.yml`. The trusted-publishing workflow validates the tag, rejects an existing registry version, builds and audits one archive, and publishes that archive with provenance.
|
|
205
|
-
|
|
206
|
-
From a clean, synchronized `main` branch:
|
|
207
|
-
|
|
208
|
-
```sh
|
|
209
|
-
bun run release
|
|
97
|
+
```ts
|
|
98
|
+
const python = await pyfig({
|
|
99
|
+
uv: '/opt/bin/uv',
|
|
100
|
+
python: '/opt/python/bin/python3',
|
|
101
|
+
requireEmbedding: true,
|
|
102
|
+
});
|
|
210
103
|
```
|
|
211
104
|
|
|
212
|
-
The
|
|
105
|
+
The runtime package exports `pyfig` and `PythonContext`. TypeScript also exposes
|
|
106
|
+
`PyfigOptions`. Matching ESM and CommonJS entry points are published.
|
|
213
107
|
|
|
214
108
|
## Development
|
|
215
109
|
|
|
@@ -217,7 +111,11 @@ The local gate derives the next version from remote GitHub tags, verifies locall
|
|
|
217
111
|
bun install --ignore-scripts
|
|
218
112
|
bun run verify
|
|
219
113
|
bun run build
|
|
220
|
-
bun pm pack --dry-run
|
|
221
114
|
```
|
|
222
115
|
|
|
223
|
-
|
|
116
|
+
## Publishing
|
|
117
|
+
|
|
118
|
+
From a clean, synchronized `main` branch, `bun run release` verifies locally,
|
|
119
|
+
builds and audits the package, commits the next version, creates its Git tag,
|
|
120
|
+
and pushes both atomically. The tag workflow publishes that exact package to
|
|
121
|
+
npm through trusted publishing.
|
package/SECURITY.md
CHANGED
|
@@ -1,9 +1,20 @@
|
|
|
1
1
|
# Security
|
|
2
2
|
|
|
3
|
-
Pyfig
|
|
3
|
+
Pyfig spawns only the uv executable resolved from `uv`, found on `PATH`, or
|
|
4
|
+
installed in its OS cache. Setup downloads an official Astral release archive
|
|
5
|
+
and adjacent SHA-256 checksum over HTTPS, verifies the archive before
|
|
6
|
+
publication, and probes the staged executable. It never runs an installer
|
|
7
|
+
script, invokes a shell by default, or modifies shell and project configuration.
|
|
4
8
|
|
|
5
|
-
|
|
9
|
+
Compatible cached uv executables are used without network access. GitHub release
|
|
10
|
+
metadata is cached for 24 hours, with valid stale metadata retained as a network
|
|
11
|
+
fallback. Cache publication and metadata refresh use Loqkit's in-memory,
|
|
12
|
+
worker, native-flock-when-available, and portable file-lock coordination.
|
|
6
13
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
14
|
+
The executable path, working directory, environment, version requests, uv
|
|
15
|
+
arguments, command arguments, and spawn options are trusted caller input. The
|
|
16
|
+
`run()` method can execute arbitrary commands, and callers can explicitly
|
|
17
|
+
enable shell execution through `SpawnOptions`. uv owns Python discovery and
|
|
18
|
+
managed-Python downloads. Supply an explicit uv executable or pre-populate the
|
|
19
|
+
cache when setup downloads are prohibited, and configure uv itself when Python
|
|
20
|
+
downloads must be offline or restricted by organizational policy.
|