pyfig 0.6.3 → 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 +77 -166
- package/SECURITY.md +16 -5
- package/dist/cjs/index.js +471 -980
- package/dist/cjs/index.js.map +6 -15
- package/dist/index.d.ts +2 -9
- 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 -1217
- package/dist/cjs/sync-resolver.js.map +0 -19
- package/dist/cli.d.ts +0 -3
- package/dist/cli.d.ts.map +0 -1
- package/dist/cli.js +0 -48
- 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 -25
- package/dist/client.js.map +0 -1
- package/dist/config.d.ts +0 -62
- package/dist/config.d.ts.map +0 -1
- package/dist/config.js +0 -112
- 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 -26
- package/dist/provision.d.ts.map +0 -1
- package/dist/provision.js +0 -685
- package/dist/provision.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/virtual-environment.d.ts +0 -11
- package/dist/virtual-environment.d.ts.map +0 -1
- package/dist/virtual-environment.js +0 -49
- package/dist/virtual-environment.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,193 +10,100 @@ Pyfig never invokes standalone `pip`, never writes `pyproject.toml` or requireme
|
|
|
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
|
-
|
|
23
|
+
const python = await pyfig({
|
|
24
|
+
uv: '>=0.10,<1',
|
|
25
|
+
python: '>=3.11,<4',
|
|
21
26
|
requireEmbedding: true,
|
|
22
27
|
requireGil: true,
|
|
23
28
|
});
|
|
24
29
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
{ extras: ['database'] },
|
|
28
|
-
);
|
|
29
|
-
|
|
30
|
-
console.log(environment.uv);
|
|
31
|
-
console.log(environment.python);
|
|
32
|
-
console.log(environment.managed);
|
|
33
|
-
|
|
34
|
-
await pyfig.uv(
|
|
35
|
-
['run', '--', 'python', 'script.py'],
|
|
36
|
-
{ stdio: 'inherit' },
|
|
37
|
-
);
|
|
38
|
-
```
|
|
39
|
-
|
|
40
|
-
`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.
|
|
41
|
-
|
|
42
|
-
The same uv-only surface handles direct commands:
|
|
30
|
+
console.log(python.uv);
|
|
31
|
+
console.log(python.python);
|
|
43
32
|
|
|
44
|
-
|
|
45
|
-
import { uvSpawn, uvSpawnSync } from 'pyfig';
|
|
46
|
-
|
|
47
|
-
await uvSpawn(['pip', 'list'], { stdio: 'inherit' });
|
|
48
|
-
|
|
49
|
-
const result = uvSpawnSync(
|
|
50
|
-
['run', '--', 'python', '-c', 'import sys; print(sys.executable)'],
|
|
51
|
-
{ encoding: 'utf8' },
|
|
52
|
-
);
|
|
33
|
+
python.run('python', '--version', { stdio: 'inherit' });
|
|
53
34
|
```
|
|
54
35
|
|
|
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. A compatible explicitly configured venv, active `VIRTUAL_ENV`, root `.venv`, or Pyfig-owned venv.
|
|
66
|
-
4. Every installed system Python reported by uv, with downloads disabled, until one passes all capability checks.
|
|
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.
|
|
36
|
+
`pyfig()` returns a `PythonContext` containing:
|
|
69
37
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
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.
|
|
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. |
|
|
77
43
|
|
|
78
|
-
|
|
44
|
+
`run()` treats a final object as Node `SpawnOptions`. When no `--` argument is
|
|
45
|
+
present, every string is treated as command argv:
|
|
79
46
|
|
|
80
47
|
```ts
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
{
|
|
84
|
-
extras: ['postgres'],
|
|
85
|
-
groups: ['runtime'],
|
|
86
|
-
},
|
|
87
|
-
);
|
|
48
|
+
python.run('python', '--version');
|
|
49
|
+
// uv run --python /resolved/python -- python --version
|
|
88
50
|
```
|
|
89
51
|
|
|
90
|
-
|
|
52
|
+
When `--` is present, strings before it are passed to uv and strings after it
|
|
53
|
+
are command argv:
|
|
91
54
|
|
|
92
|
-
```
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
uv pip install \
|
|
99
|
-
--python /selected/venv/python \
|
|
100
|
-
-r /temporary/resolved-dependencies.txt
|
|
55
|
+
```ts
|
|
56
|
+
python.run('--no-project', '--with', 'httpx', '--', 'python', 'script.py', {
|
|
57
|
+
cwd: '/workspace',
|
|
58
|
+
stdio: 'inherit',
|
|
59
|
+
});
|
|
101
60
|
```
|
|
102
61
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
[Mermaid source](docs/diagrams/runtime-dependencies.mmd)
|
|
108
|
-
|
|
109
|
-
## Project root
|
|
110
|
-
|
|
111
|
-
Pyfig does not interpret `package.json`, workspace metadata, `pyproject.toml`, or lockfiles to choose ownership. Root precedence is:
|
|
112
|
-
|
|
113
|
-
1. `projectRoot`
|
|
114
|
-
2. `PYFIG_PROJECT_ROOT`
|
|
115
|
-
3. nearest Git worktree marker above `INIT_CWD`, when set, otherwise the current directory
|
|
116
|
-
4. that starting directory when no Git marker exists
|
|
117
|
-
|
|
118
|
-
Set `projectRoot` explicitly in reusable libraries and unusual workspace layouts.
|
|
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.
|
|
119
66
|
|
|
120
|
-
|
|
67
|
+
`pyfig()` accepts:
|
|
121
68
|
|
|
122
|
-
|
|
|
123
|
-
| --- | --- |
|
|
124
|
-
| `createPyfig(options)` | Create a scoped client with memoized base preparation. |
|
|
125
|
-
| `client.prepare(dependencies?, options?)` | Select a venv and optionally resolve/install runtime dependencies. |
|
|
126
|
-
| `client.pythonInfo()` | Return the selected interpreter and embedding metadata. |
|
|
127
|
-
| `client.uvInfo()` | Return the selected uv executable and version. |
|
|
128
|
-
| `client.uv()` / `client.uvSync()` | Spawn uv and pin pip/run commands to the selected venv. |
|
|
129
|
-
| `ensureEnvironment(options?, dependencies?, dependencyOptions?)` | Standalone preparation API. |
|
|
130
|
-
| `ensureUv(options?)` | Prefer existing uv, otherwise bootstrap verified uv when allowed. |
|
|
131
|
-
| `uvSpawn()` / `uvSpawnSync()` | Standalone uv spawn API. |
|
|
132
|
-
| `resolveProjectRoot(start?)` | Resolve the nearest Git root without reading manifests. |
|
|
133
|
-
|
|
134
|
-
The package exposes matching ESM and CommonJS entry points. Probe, download, lock, and command-policy internals are not exported.
|
|
135
|
-
|
|
136
|
-
## Configuration
|
|
137
|
-
|
|
138
|
-
Precedence is API or CLI option, environment variable, then default.
|
|
139
|
-
|
|
140
|
-
`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.
|
|
141
|
-
|
|
142
|
-
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.
|
|
143
|
-
|
|
144
|
-
| API option | Environment variable | Default |
|
|
69
|
+
| Option | Default | Meaning |
|
|
145
70
|
| --- | --- | --- |
|
|
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
|
-
bunx --bun pyfig --requires-uv '>=0.12,<1'
|
|
172
|
-
bunx --bun pyfig --require-embedding --require-gil
|
|
173
|
-
bunx --bun pyfig --project-root /path/to/repository
|
|
174
|
-
bunx --bun pyfig --json
|
|
175
|
-
```
|
|
176
|
-
|
|
177
|
-
`bunx --bun` runs the CLI with Bun and does not require a separate Node.js executable.
|
|
178
|
-
|
|
179
|
-
## Concurrency and installation safety
|
|
180
|
-
|
|
181
|
-
Pyfig resolves a compatible uv release from Astral's GitHub releases, downloads that exact archive over HTTPS, verifies its adjacent SHA-256 checksum, validates the staged executable version, and atomically publishes it in Pyfig's cache. A compatible cached executable is probed locally without a network request. Release metadata is cached on disk for 24 hours; stale metadata is refreshed under a Loqkit lock and remains available as a fallback when GitHub's API is temporarily unavailable. Set `GITHUB_TOKEN` or `GH_TOKEN` to authenticate release discovery on CI and other rate-limited hosts. Pyfig does not execute installer scripts or modify shell profiles.
|
|
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:
|
|
182
96
|
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
## Publishing
|
|
190
|
-
|
|
191
|
-
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.
|
|
192
|
-
|
|
193
|
-
From a clean, synchronized `main` branch:
|
|
194
|
-
|
|
195
|
-
```sh
|
|
196
|
-
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
|
+
});
|
|
197
103
|
```
|
|
198
104
|
|
|
199
|
-
The
|
|
105
|
+
The runtime package exports `pyfig` and `PythonContext`. TypeScript also exposes
|
|
106
|
+
`PyfigOptions`. Matching ESM and CommonJS entry points are published.
|
|
200
107
|
|
|
201
108
|
## Development
|
|
202
109
|
|
|
@@ -204,7 +111,11 @@ The local gate derives the next version from remote GitHub tags, verifies locall
|
|
|
204
111
|
bun install --ignore-scripts
|
|
205
112
|
bun run verify
|
|
206
113
|
bun run build
|
|
207
|
-
bun pm pack --dry-run
|
|
208
114
|
```
|
|
209
115
|
|
|
210
|
-
|
|
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.
|