pynect 0.2.7 → 0.2.10
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 +68 -27
- package/SECURITY.md +6 -6
- package/dist/cli/codegen.d.ts +3 -0
- package/dist/cli/codegen.d.ts.map +1 -1
- package/dist/cli/codegen.js +15 -3
- package/dist/cli/codegen.js.map +1 -1
- package/dist/cli/sync.d.ts.map +1 -1
- package/dist/cli/sync.js +38 -19
- package/dist/cli/sync.js.map +1 -1
- package/dist/config/project.d.ts +8 -52
- package/dist/config/project.d.ts.map +1 -1
- package/dist/config/project.js +74 -121
- package/dist/config/project.js.map +1 -1
- package/dist/core/isolated-worker.js +4 -4
- package/dist/core/isolated-worker.js.map +1 -1
- package/dist/core/py-proxy.d.ts +2 -1
- package/dist/core/py-proxy.d.ts.map +1 -1
- package/dist/core/py-proxy.js +55 -7
- package/dist/core/py-proxy.js.map +1 -1
- package/dist/core/py-runtime.d.ts +1 -5
- package/dist/core/py-runtime.d.ts.map +1 -1
- package/dist/core/py-runtime.js +36 -100
- package/dist/core/py-runtime.js.map +1 -1
- package/dist/core/pynect-callback.d.ts +16 -0
- package/dist/core/pynect-callback.d.ts.map +1 -0
- package/dist/core/pynect-callback.js +52 -0
- package/dist/core/pynect-callback.js.map +1 -0
- package/dist/env/project-environment.d.ts +9 -30
- package/dist/env/project-environment.d.ts.map +1 -1
- package/dist/env/project-environment.js +32 -217
- package/dist/env/project-environment.js.map +1 -1
- package/dist/env/python.d.ts +16 -0
- package/dist/env/python.d.ts.map +1 -0
- package/dist/env/python.js +9 -0
- package/dist/env/python.js.map +1 -0
- package/dist/index.d.ts +8 -24
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -31
- package/dist/index.js.map +1 -1
- package/dist/native/native-addon-loader.d.ts.map +1 -1
- package/dist/native/native-addon-loader.js +15 -5
- package/dist/native/native-addon-loader.js.map +1 -1
- package/dist/types/addon.d.ts +6 -0
- package/dist/types/addon.d.ts.map +1 -1
- package/dist/types/addon.js.map +1 -1
- package/dist/types/config.d.ts +18 -85
- package/dist/types/config.d.ts.map +1 -1
- package/dist/types/config.js +17 -15
- package/dist/types/config.js.map +1 -1
- package/dist/types/index.d.ts +1 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/python.d.ts +2 -0
- package/dist/types/python.d.ts.map +1 -1
- package/package.json +3 -4
- package/prebuilds/darwin-arm64/pynect-prebuild.json +1 -1
- package/prebuilds/darwin-arm64/pynect_addon.node +0 -0
- package/prebuilds/darwin-x64/pynect-prebuild.json +1 -1
- package/prebuilds/darwin-x64/pynect_addon.node +0 -0
- package/prebuilds/linux-arm64/pynect-prebuild.json +1 -1
- package/prebuilds/linux-arm64/pynect_addon.node +0 -0
- package/prebuilds/linux-x64/pynect-prebuild.json +1 -1
- package/prebuilds/linux-x64/pynect_addon.node +0 -0
- package/prebuilds/win32-arm64/pynect-prebuild.json +1 -1
- package/prebuilds/win32-arm64/pynect_addon.node +0 -0
- package/prebuilds/win32-x64/pynect-prebuild.json +1 -1
- package/prebuilds/win32-x64/pynect_addon.node +0 -0
- package/python/pynect_runtime.py +1 -1
- package/python/type_inspector.py +12 -0
- package/scripts/build-native.js +4 -3
- package/scripts/postinstall.js +2 -2
- package/scripts/python.js +33 -0
- package/scripts/smoke-python-boundary.js +72 -21
- package/src/native/callback_bridge.h +11 -0
- package/src/native/native-addon-loader.ts +13 -4
- package/src/native/pynect_addon.cpp +346 -4
- package/src/native/python_bridge.cpp +12 -0
- package/src/native/python_bridge.h +7 -0
- package/src/native/thread_pool.cpp +62 -55
- package/src/native/thread_pool.h +13 -0
- package/src/native/type_converter.h +2 -0
- package/dist/env/executables.d.ts +0 -3
- package/dist/env/executables.d.ts.map +0 -1
- package/dist/env/executables.js +0 -14
- package/dist/env/executables.js.map +0 -1
- package/dist/env/package-installer.d.ts +0 -232
- package/dist/env/package-installer.d.ts.map +0 -1
- package/dist/env/package-installer.js +0 -615
- package/dist/env/package-installer.js.map +0 -1
- package/dist/env/pyfig-adapter.d.ts +0 -38
- package/dist/env/pyfig-adapter.d.ts.map +0 -1
- package/dist/env/pyfig-adapter.js +0 -10
- package/dist/env/pyfig-adapter.js.map +0 -1
- package/dist/env/python-detector.d.ts +0 -48
- package/dist/env/python-detector.d.ts.map +0 -1
- package/dist/env/python-detector.js +0 -182
- package/dist/env/python-detector.js.map +0 -1
- package/dist/env/python-provisioner.d.ts +0 -16
- package/dist/env/python-provisioner.d.ts.map +0 -1
- package/dist/env/python-provisioner.js +0 -42
- package/dist/env/python-provisioner.js.map +0 -1
- package/dist/env/venv-manager.d.ts +0 -56
- package/dist/env/venv-manager.d.ts.map +0 -1
- package/dist/env/venv-manager.js +0 -160
- package/dist/env/venv-manager.js.map +0 -1
- package/scripts/pyfig.js +0 -142
- package/scripts/python-provisioner.js +0 -23
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pynect",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.10",
|
|
4
4
|
"packageManager": "bun@1.4.4",
|
|
5
5
|
"description": "Typed, optionally Pydantic-validated Python calls from Node.js and Bun through embedded CPython.",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -29,8 +29,7 @@
|
|
|
29
29
|
"scripts/cli.js",
|
|
30
30
|
"scripts/ensure-native.js",
|
|
31
31
|
"scripts/postinstall.js",
|
|
32
|
-
"scripts/
|
|
33
|
-
"scripts/python-provisioner.js",
|
|
32
|
+
"scripts/python.js",
|
|
34
33
|
"scripts/smoke-python-boundary.js",
|
|
35
34
|
"binding.gyp",
|
|
36
35
|
"README.md",
|
|
@@ -86,7 +85,7 @@
|
|
|
86
85
|
"node-gyp": "^11.5.0",
|
|
87
86
|
"node-gyp-build": "^4.8.4",
|
|
88
87
|
"pacwich": "^0.7.3",
|
|
89
|
-
"pyfig": "^0.
|
|
88
|
+
"pyfig": "^0.7.0",
|
|
90
89
|
"semver": "^7.8.5"
|
|
91
90
|
},
|
|
92
91
|
"devDependencies": {
|
|
@@ -6,5 +6,5 @@
|
|
|
6
6
|
"pythonAbi": "cp310-abi3",
|
|
7
7
|
"pythonLibraryName": "libpython3.dylib",
|
|
8
8
|
"napi": 8,
|
|
9
|
-
"nativeFingerprint": "sha256:
|
|
9
|
+
"nativeFingerprint": "sha256:9cfa5f1c9ffc2988456c3560130f8e2a4bc8df0755003cea0645ccc4f13a1d37"
|
|
10
10
|
}
|
|
Binary file
|
|
@@ -6,5 +6,5 @@
|
|
|
6
6
|
"pythonAbi": "cp310-abi3",
|
|
7
7
|
"pythonLibraryName": "libpython3.dylib",
|
|
8
8
|
"napi": 8,
|
|
9
|
-
"nativeFingerprint": "sha256:
|
|
9
|
+
"nativeFingerprint": "sha256:9cfa5f1c9ffc2988456c3560130f8e2a4bc8df0755003cea0645ccc4f13a1d37"
|
|
10
10
|
}
|
|
Binary file
|
|
@@ -6,6 +6,6 @@
|
|
|
6
6
|
"pythonAbi": "cp310-abi3",
|
|
7
7
|
"pythonLibraryName": "libpython3.so",
|
|
8
8
|
"napi": 8,
|
|
9
|
-
"nativeFingerprint": "sha256:
|
|
9
|
+
"nativeFingerprint": "sha256:9cfa5f1c9ffc2988456c3560130f8e2a4bc8df0755003cea0645ccc4f13a1d37",
|
|
10
10
|
"glibc": "2.39"
|
|
11
11
|
}
|
|
Binary file
|
|
@@ -6,6 +6,6 @@
|
|
|
6
6
|
"pythonAbi": "cp310-abi3",
|
|
7
7
|
"pythonLibraryName": "libpython3.so",
|
|
8
8
|
"napi": 8,
|
|
9
|
-
"nativeFingerprint": "sha256:
|
|
9
|
+
"nativeFingerprint": "sha256:9cfa5f1c9ffc2988456c3560130f8e2a4bc8df0755003cea0645ccc4f13a1d37",
|
|
10
10
|
"glibc": "2.35"
|
|
11
11
|
}
|
|
Binary file
|
|
@@ -6,5 +6,5 @@
|
|
|
6
6
|
"pythonAbi": "cp310-abi3",
|
|
7
7
|
"pythonLibraryName": "python3.dll",
|
|
8
8
|
"napi": 8,
|
|
9
|
-
"nativeFingerprint": "sha256:
|
|
9
|
+
"nativeFingerprint": "sha256:9cfa5f1c9ffc2988456c3560130f8e2a4bc8df0755003cea0645ccc4f13a1d37"
|
|
10
10
|
}
|
|
Binary file
|
|
@@ -6,5 +6,5 @@
|
|
|
6
6
|
"pythonAbi": "cp310-abi3",
|
|
7
7
|
"pythonLibraryName": "python3.dll",
|
|
8
8
|
"napi": 8,
|
|
9
|
-
"nativeFingerprint": "sha256:
|
|
9
|
+
"nativeFingerprint": "sha256:9cfa5f1c9ffc2988456c3560130f8e2a4bc8df0755003cea0645ccc4f13a1d37"
|
|
10
10
|
}
|
|
Binary file
|
package/python/pynect_runtime.py
CHANGED
|
@@ -433,7 +433,7 @@ def _make_error(exc_type: str, message: str, tb: str) -> Dict[str, Any]:
|
|
|
433
433
|
# Diagnostic helpers
|
|
434
434
|
|
|
435
435
|
def get_python_info() -> Dict[str, Any]:
|
|
436
|
-
"""Return basic
|
|
436
|
+
"""Return basic information about the embedded Python interpreter."""
|
|
437
437
|
import platform
|
|
438
438
|
return {
|
|
439
439
|
"version": sys.version,
|
package/python/type_inspector.py
CHANGED
|
@@ -11,6 +11,7 @@ Returns a JSON-serializable dict matching RawModuleInspection in TypeGenerator.t
|
|
|
11
11
|
from __future__ import annotations
|
|
12
12
|
|
|
13
13
|
import inspect
|
|
14
|
+
import collections.abc
|
|
14
15
|
import json
|
|
15
16
|
import importlib
|
|
16
17
|
import sys
|
|
@@ -40,6 +41,9 @@ def _get_annotation(annotation: Any) -> str:
|
|
|
40
41
|
return 'None'
|
|
41
42
|
if isinstance(annotation, str):
|
|
42
43
|
return annotation
|
|
44
|
+
origin = getattr(annotation, '__origin__', None)
|
|
45
|
+
if origin is collections.abc.Callable:
|
|
46
|
+
return _stringify_generic(annotation)
|
|
43
47
|
if hasattr(annotation, '__name__'):
|
|
44
48
|
return annotation.__name__
|
|
45
49
|
if hasattr(annotation, '__origin__'):
|
|
@@ -63,6 +67,14 @@ def _stringify_generic(tp: Any) -> str:
|
|
|
63
67
|
parts = [_get_annotation(a) for a in args]
|
|
64
68
|
return f"Union[{', '.join(parts)}]"
|
|
65
69
|
|
|
70
|
+
if origin is collections.abc.Callable:
|
|
71
|
+
if len(args) == 2 and args[0] is Ellipsis:
|
|
72
|
+
return f"Callable[..., {_get_annotation(args[1])}]"
|
|
73
|
+
if args:
|
|
74
|
+
parameters = ', '.join(_get_annotation(a) for a in args[:-1])
|
|
75
|
+
return f"Callable[[{parameters}], {_get_annotation(args[-1])}]"
|
|
76
|
+
return 'Callable'
|
|
77
|
+
|
|
66
78
|
# typing.Optional[X] is Union[X, None]
|
|
67
79
|
if len(args) == 2 and type(None) in args:
|
|
68
80
|
non_none = [a for a in args if a is not type(None)][0]
|
package/scripts/build-native.js
CHANGED
|
@@ -3,16 +3,17 @@
|
|
|
3
3
|
const fs = require('fs');
|
|
4
4
|
const path = require('path');
|
|
5
5
|
const { spawnSync } = require('child_process');
|
|
6
|
-
const {
|
|
6
|
+
const { preparePython } = require('./python');
|
|
7
7
|
|
|
8
8
|
const ROOT = path.resolve(__dirname, '..');
|
|
9
9
|
|
|
10
10
|
async function main() {
|
|
11
11
|
const projectRoot = process.env.PYNECT_PROJECT_ROOT || process.env.INIT_CWD || ROOT;
|
|
12
|
-
const {
|
|
12
|
+
const { environment } = await preparePython({
|
|
13
13
|
projectRoot,
|
|
14
|
-
|
|
14
|
+
python: process.env.PYNECT_PYTHON,
|
|
15
15
|
});
|
|
16
|
+
const { python } = environment;
|
|
16
17
|
const embedding = python.embedding;
|
|
17
18
|
const stableAbiLibrary = process.platform === 'win32'
|
|
18
19
|
? embedding.stableAbiLibraryPath || path.join(embedding.libraryDirectory, 'python3.lib')
|
package/scripts/postinstall.js
CHANGED
|
@@ -29,8 +29,8 @@ async function main() {
|
|
|
29
29
|
|
|
30
30
|
const result = await syncProject({ projectRoot });
|
|
31
31
|
console.log(
|
|
32
|
-
`[pynect]
|
|
33
|
-
`with
|
|
32
|
+
`[pynect] Prepared ${result.plan.dependencies.length} Python dependency constraint(s) ` +
|
|
33
|
+
`with Pyfig in ${result.environment.python.virtualEnvironment}.`,
|
|
34
34
|
);
|
|
35
35
|
}
|
|
36
36
|
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const path = require('path');
|
|
4
|
+
const { Python, resolveProjectRoot } = require('pyfig');
|
|
5
|
+
|
|
6
|
+
function projectRoot(explicit) {
|
|
7
|
+
const hint = explicit || process.env.PYNECT_PROJECT_ROOT || process.env.INIT_CWD;
|
|
8
|
+
return hint ? path.resolve(hint) : resolveProjectRoot(process.cwd());
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function createPython(options = {}) {
|
|
12
|
+
return new Python({
|
|
13
|
+
...options,
|
|
14
|
+
projectRoot: projectRoot(options.projectRoot),
|
|
15
|
+
requireEmbedding: true,
|
|
16
|
+
requireGil: true,
|
|
17
|
+
env: options.env ? { ...process.env, ...options.env } : process.env,
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
async function preparePython(options = {}) {
|
|
22
|
+
const runtime = createPython(options);
|
|
23
|
+
const environment = await runtime.prepare();
|
|
24
|
+
if (!environment.python.embedding) {
|
|
25
|
+
throw new Error(
|
|
26
|
+
`Python ${environment.python.version} at ${environment.python.executable} is missing ` +
|
|
27
|
+
'Python.h or its CPython embedding library.',
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
return { runtime, environment };
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
module.exports = { createPython, preparePython, projectRoot };
|
|
@@ -1,48 +1,56 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
process.env.PYNECT_PREFER_LOCAL ??= '1';
|
|
4
|
+
|
|
3
5
|
const path = require('path');
|
|
4
|
-
const {
|
|
6
|
+
const { preparePython } = require('./python');
|
|
5
7
|
|
|
6
8
|
const ROOT = path.resolve(__dirname, '..');
|
|
7
9
|
|
|
8
10
|
async function main() {
|
|
9
|
-
const {
|
|
11
|
+
const { runtime, environment } = await preparePython({ projectRoot: ROOT });
|
|
10
12
|
const { python } = environment;
|
|
11
13
|
const expected = normalize(python.executable);
|
|
12
14
|
const pynect = require('../dist');
|
|
13
15
|
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
assertSamePython(detected.executable, expected, 'PythonDetector');
|
|
17
|
-
const probed = detector.probe(detected.executable);
|
|
18
|
-
if (!probed) throw new Error('PythonDetector could not probe the selected repository Python.');
|
|
19
|
-
assertSamePython(probed.executable, expected, 'PythonDetector.probe');
|
|
20
|
-
|
|
21
|
-
const selected = client.pythonSync(
|
|
22
|
-
['-c', 'import sys; print(sys.executable)'],
|
|
16
|
+
const selected = runtime.uvSync(
|
|
17
|
+
['run', '--', 'python', '-c', 'import sys; print(sys.executable)'],
|
|
23
18
|
{ encoding: 'utf8' },
|
|
24
19
|
);
|
|
25
|
-
assertSuccess(selected, '
|
|
26
|
-
assertSamePython(selected.stdout.trim(), expected, '
|
|
27
|
-
|
|
28
|
-
const pip = client.pipSync(['list', '--format', 'json'], { encoding: 'utf8' });
|
|
29
|
-
assertSuccess(pip, 'PyfigClient.pipSync');
|
|
20
|
+
assertSuccess(selected, 'Pyfig Python uvSync run');
|
|
21
|
+
assertSamePython(selected.stdout.trim(), expected, 'Pyfig Python uvSync run');
|
|
30
22
|
|
|
31
|
-
const uvPip =
|
|
23
|
+
const uvPip = runtime.uvSync(
|
|
32
24
|
['pip', 'list', '--format', 'json'],
|
|
33
25
|
{ encoding: 'utf8' },
|
|
34
26
|
);
|
|
35
27
|
assertSuccess(uvPip, 'PyfigClient.uvSync pip');
|
|
36
28
|
|
|
37
|
-
const inspector =
|
|
38
|
-
[path.join(ROOT, 'python', 'type_inspector.py'), 'json'],
|
|
29
|
+
const inspector = runtime.uvSync(
|
|
30
|
+
['run', '--', 'python', path.join(ROOT, 'python', 'type_inspector.py'), 'json'],
|
|
39
31
|
{ encoding: 'utf8' },
|
|
40
32
|
);
|
|
41
33
|
assertSuccess(inspector, 'codegen inspector');
|
|
42
34
|
JSON.parse(inspector.stdout);
|
|
35
|
+
const callbackInspection = runtime.uvSync(
|
|
36
|
+
['run', '--', 'python', path.join(ROOT, 'python', 'type_inspector.py'), 'pynect_callback_fixture'],
|
|
37
|
+
{
|
|
38
|
+
encoding: 'utf8',
|
|
39
|
+
env: {
|
|
40
|
+
...process.env,
|
|
41
|
+
PYTHONPATH: path.join(ROOT, 'tests', 'fixtures'),
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
);
|
|
45
|
+
assertSuccess(callbackInspection, 'callback type inspector');
|
|
46
|
+
const inspectedCallback = JSON.parse(callbackInspection.stdout).functions
|
|
47
|
+
.find(item => item.name === 'invoke')?.parameters?.[0]?.type;
|
|
48
|
+
if (inspectedCallback !== 'Callable[[Any], Any]') {
|
|
49
|
+
throw new Error(`Callback annotation was inspected as ${String(inspectedCallback)}.`);
|
|
50
|
+
}
|
|
43
51
|
|
|
44
|
-
const worker =
|
|
45
|
-
[path.join(ROOT, 'python', 'isolated_worker.py')],
|
|
52
|
+
const worker = runtime.uvSync(
|
|
53
|
+
['run', '--', 'python', path.join(ROOT, 'python', 'isolated_worker.py')],
|
|
46
54
|
{
|
|
47
55
|
encoding: 'utf8',
|
|
48
56
|
input: `${JSON.stringify({
|
|
@@ -69,10 +77,43 @@ async function main() {
|
|
|
69
77
|
projectRoot: ROOT,
|
|
70
78
|
allowUnsafeCode: true,
|
|
71
79
|
logLevel: 'error',
|
|
80
|
+
pythonModulePaths: [path.join(ROOT, 'tests', 'fixtures')],
|
|
81
|
+
threadPool: { size: 1 },
|
|
82
|
+
callbacks: { maxDepth: 2 },
|
|
72
83
|
});
|
|
73
84
|
try {
|
|
74
85
|
const embedded = await pynect.py.eval('__import__("sys").executable');
|
|
75
86
|
assertSamePython(embedded, expected, 'embedded CPython');
|
|
87
|
+
const fixture = await pynect.importModule('pynect_callback_fixture');
|
|
88
|
+
const asyncCallback = async value => {
|
|
89
|
+
await Promise.resolve();
|
|
90
|
+
return value * 3;
|
|
91
|
+
};
|
|
92
|
+
if (await fixture.invoke(asyncCallback, 4) !== 12) {
|
|
93
|
+
throw new Error('Async JavaScript callback returned the wrong value.');
|
|
94
|
+
}
|
|
95
|
+
await fixture.retain(asyncCallback);
|
|
96
|
+
await assertRejects(
|
|
97
|
+
() => fixture.invoke_retained(4),
|
|
98
|
+
/callback.*(?:disposed|registered)|(?:disposed|registered).*callback/i,
|
|
99
|
+
'settled callback lifetime',
|
|
100
|
+
);
|
|
101
|
+
|
|
102
|
+
const reentrantCallback = async value => fixture.double(value);
|
|
103
|
+
if (await fixture.invoke(reentrantCallback, 5) !== 10) {
|
|
104
|
+
throw new Error('Reentrant JavaScript callback returned the wrong value.');
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
let recursiveCallback;
|
|
108
|
+
recursiveCallback = async depth => {
|
|
109
|
+
if (depth <= 0) return 0;
|
|
110
|
+
return fixture.invoke(recursiveCallback, depth - 1);
|
|
111
|
+
};
|
|
112
|
+
await assertRejects(
|
|
113
|
+
() => fixture.invoke(recursiveCallback, 2),
|
|
114
|
+
/recursion depth 3 exceeds the configured maximum of 2/,
|
|
115
|
+
'callback recursion limit',
|
|
116
|
+
);
|
|
76
117
|
} finally {
|
|
77
118
|
await pynect.shutdown();
|
|
78
119
|
}
|
|
@@ -80,6 +121,16 @@ async function main() {
|
|
|
80
121
|
console.log(`Python ${python.version} boundary verified: ${python.executable}`);
|
|
81
122
|
}
|
|
82
123
|
|
|
124
|
+
async function assertRejects(action, pattern, label) {
|
|
125
|
+
try {
|
|
126
|
+
await action();
|
|
127
|
+
} catch (error) {
|
|
128
|
+
if (pattern.test(String(error?.message || error))) return;
|
|
129
|
+
throw new Error(`${label} failed with an unexpected error: ${error?.message || error}`);
|
|
130
|
+
}
|
|
131
|
+
throw new Error(`${label} did not reject.`);
|
|
132
|
+
}
|
|
133
|
+
|
|
83
134
|
function assertSuccess(result, label) {
|
|
84
135
|
if (result.status !== 0 || result.error) {
|
|
85
136
|
throw new Error(`${label} failed: ${String(result.stderr || result.error?.message || result.status)}`);
|
|
@@ -18,15 +18,24 @@ export interface NativeAddonBuildOptions {
|
|
|
18
18
|
export function loadOrBuildNativeAddon(options: NativeAddonBuildOptions): NativeAddon {
|
|
19
19
|
let initialError: unknown = new Error('Native rebuild explicitly requested.');
|
|
20
20
|
if (process.env['PYNECT_FORCE_BUILD'] !== '1') {
|
|
21
|
+
if (process.env['PYNECT_PREFER_LOCAL'] === '1') {
|
|
22
|
+
try {
|
|
23
|
+
return loadMatchingLocalAddon(options);
|
|
24
|
+
} catch (error) {
|
|
25
|
+
initialError = error;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
21
28
|
try {
|
|
22
29
|
return loadMatchingPrebuild(options);
|
|
23
30
|
} catch (error) {
|
|
24
31
|
initialError = error;
|
|
25
32
|
}
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
33
|
+
if (process.env['PYNECT_PREFER_LOCAL'] !== '1') {
|
|
34
|
+
try {
|
|
35
|
+
return loadMatchingLocalAddon(options);
|
|
36
|
+
} catch (error) {
|
|
37
|
+
initialError = new AggregateError([initialError, error], 'No compatible native addon loaded.');
|
|
38
|
+
}
|
|
30
39
|
}
|
|
31
40
|
}
|
|
32
41
|
|