Skip to content

Commit b3b1a9b

Browse files
Merge pull request #134 from pyscript/better-auto-proxy
Better experimental_create_proxy
2 parents 4e5e892 + 24c9093 commit b3b1a9b

File tree

14 files changed

+249
-70
lines changed

14 files changed

+249
-70
lines changed

docs/index.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

docs/index.js.map

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

docs/zip-1TWXRld0.js

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

docs/zip-1TWXRld0.js.map

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

esm/interpreter/pyodide.js

Lines changed: 50 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
1-
import { create } from 'gc-hook';
2-
3-
import { RUNNING_IN_WORKER, createProgress, writeFile } from './_utils.js';
1+
import { createProgress, writeFile } from './_utils.js';
42
import { getFormat, loader, loadProgress, registerJSModule, run, runAsync, runEvent } from './_python.js';
53
import { stdio } from './_io.js';
64
import { IDBMapSync, isArray, fixedRelative } from '../utils.js';
@@ -10,67 +8,56 @@ const toJsOptions = { dict_converter: Object.fromEntries };
108

119
const { stringify } = JSON;
1210

11+
const { apply } = Reflect;
12+
const FunctionPrototype = Function.prototype;
13+
1314
// REQUIRES INTEGRATION TEST
1415
/* c8 ignore start */
15-
let overrideFunction = false;
16-
const overrideMethod = method => (...args) => {
17-
try {
18-
overrideFunction = true;
19-
return method(...args);
20-
}
21-
finally {
22-
overrideFunction = false;
23-
}
16+
const overrideMethod = method => function (...args) {
17+
return apply(method, this, args);
2418
};
2519

26-
let overridden = false;
27-
const applyOverride = () => {
28-
if (overridden) return;
29-
overridden = true;
20+
let pyproxy, to_js;
21+
const override = intercept => {
3022

3123
const proxies = new WeakMap;
32-
const onGC = value => value.destroy();
33-
const patchArgs = args => {
34-
for (let i = 0; i < args.length; i++) {
35-
const value = args[i];
36-
if (
37-
typeof value === 'function' &&
38-
'copy' in value
39-
) {
40-
// avoid seppuku / Harakiri + speed up
41-
overrideFunction = false;
42-
// reuse copied value if known already
43-
let proxy = proxies.get(value)?.deref();
44-
if (!proxy) {
45-
try {
46-
// observe the copy and return a Proxy reference
47-
proxy = create(value.copy(), onGC);
48-
proxies.set(value, new WeakRef(proxy));
49-
}
50-
catch (error) {
51-
console.error(error);
24+
25+
const patch = args => {
26+
for (let arg, i = 0; i < args.length; i++) {
27+
switch (typeof(arg = args[i])) {
28+
case 'object':
29+
if (arg === null) break;
30+
// falls through
31+
case 'function': {
32+
if (pyproxy in arg && !arg[pyproxy].shared?.gcRegistered) {
33+
intercept = false;
34+
let proxy = proxies.get(arg)?.deref();
35+
if (!proxy) {
36+
proxy = to_js(arg);
37+
const wr = new WeakRef(proxy);
38+
proxies.set(arg, wr);
39+
proxies.set(proxy, wr);
40+
}
41+
args[i] = proxy;
42+
intercept = true;
5243
}
44+
break;
5345
}
54-
if (proxy) args[i] = proxy;
55-
overrideFunction = true;
5646
}
5747
}
5848
};
5949

60-
// trap apply to make call possible after the patch
61-
const { call } = Function;
62-
const apply = call.bind(call, call.apply);
6350
// the patch
64-
Object.defineProperties(Function.prototype, {
51+
Object.defineProperties(FunctionPrototype, {
6552
apply: {
6653
value(context, args) {
67-
if (overrideFunction) patchArgs(args);
54+
if (intercept) patch(args);
6855
return apply(this, context, args);
6956
}
7057
},
7158
call: {
7259
value(context, ...args) {
73-
if (overrideFunction) patchArgs(args);
60+
if (intercept) patch(args);
7461
return apply(this, context, args);
7562
}
7663
}
@@ -82,12 +69,9 @@ const indexURLs = new WeakMap();
8269

8370
export default {
8471
type,
85-
module: (version = '0.27.5') =>
72+
module: (version = '0.27.6') =>
8673
`https://cdn.jsdelivr.net/pyodide/v${version}/full/pyodide.mjs`,
8774
async engine({ loadPyodide }, config, url, baseURL) {
88-
// apply override ASAP then load foreign code
89-
if (!RUNNING_IN_WORKER && config.experimental_create_proxy === 'auto')
90-
applyOverride();
9175
progress('Loading Pyodide');
9276
let { packages, index_urls } = config;
9377
if (packages) packages = packages.map(fixedRelative, baseURL);
@@ -134,13 +118,30 @@ export default {
134118
await storage.close();
135119
if (options.lockFileURL) URL.revokeObjectURL(options.lockFileURL);
136120
progress('Loaded Pyodide');
121+
if (config.experimental_create_proxy === 'auto') {
122+
interpreter.runPython([
123+
'import js',
124+
'from pyodide.ffi import to_js',
125+
'o=js.Object.fromEntries',
126+
'js.experimental_create_proxy=lambda r:to_js(r,dict_converter=o)'
127+
].join(';'), { globals: interpreter.toPy({}) });
128+
to_js = globalThis.experimental_create_proxy;
129+
delete globalThis.experimental_create_proxy;
130+
[pyproxy] = Reflect.ownKeys(to_js).filter(
131+
k => (
132+
typeof k === 'symbol' &&
133+
String(k) === 'Symbol(pyproxy.attrs)'
134+
)
135+
);
136+
override(true);
137+
}
137138
return interpreter;
138139
},
139140
registerJSModule,
140141
run: overrideMethod(run),
141142
runAsync: overrideMethod(runAsync),
142143
runEvent: overrideMethod(runEvent),
143-
transform: (interpreter, value) => transform.call(interpreter, value),
144+
transform: (interpreter, value) => apply(transform, interpreter, [value]),
144145
writeFile: (interpreter, path, buffer, url) => {
145146
const format = getFormat(path, url);
146147
if (format) {

esm/interpreter/webr.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ const run = async (interpreter, code) => {
2121
export default {
2222
type,
2323
experimental: true,
24-
module: (version = '0.4.2') =>
24+
module: (version = '0.4.3') =>
2525
`https://cdn.jsdelivr.net/npm/webr@${version}/dist/webr.mjs`,
2626
async engine(module, config, _, baseURL) {
2727
const { get } = stdio();

node.importmap

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"imports": {
33
"http://pyodide": "./test/mocked/pyodide.mjs",
4-
"https://cdn.jsdelivr.net/pyodide/v0.27.5/full/pyodide.mjs": "./test/mocked/pyodide.mjs",
4+
"https://cdn.jsdelivr.net/pyodide/v0.27.6/full/pyodide.mjs": "./test/mocked/pyodide.mjs",
55
"https://cdn.jsdelivr.net/npm/@micropython/[email protected]/micropython.mjs": "./test/mocked/micropython.mjs",
66
"./3rd-party/toml.js": "./test/mocked/toml.mjs"
77
}

package-lock.json

Lines changed: 10 additions & 10 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "polyscript",
3-
"version": "0.17.15",
3+
"version": "0.17.18",
44
"description": "PyScript single core to rule them all",
55
"main": "./esm/index.js",
66
"types": "./types/polyscript/esm/index.d.ts",
@@ -50,7 +50,7 @@
5050
"@playwright/test": "^1.52.0",
5151
"@rollup/plugin-node-resolve": "^16.0.1",
5252
"@rollup/plugin-terser": "^0.4.4",
53-
"@zip.js/zip.js": "^2.7.60",
53+
"@zip.js/zip.js": "^2.7.61",
5454
"c8": "^10.1.3",
5555
"chokidar": "^4.0.3",
5656
"eslint": "^9.26.0",
@@ -88,14 +88,14 @@
8888
"@webreflection/utils": "^0.1.0",
8989
"basic-devtools": "^0.1.6",
9090
"codedent": "^0.1.2",
91-
"coincident": "^3.0.4",
91+
"coincident": "^3.0.5",
9292
"gc-hook": "^0.4.1",
9393
"html-escaper": "^3.0.3",
9494
"proxy-target": "^3.0.2",
9595
"sticky-module": "^0.1.1",
9696
"to-json-callback": "^0.1.1"
9797
},
9898
"worker": {
99-
"blob": "sha256-xlgyKcR1rQj2E2TB4Pp07IZjwgkIyN9uf8HfcuUXwro="
99+
"blob": "sha256-O+HvjdLH9ZO5EJ2AhdtN4bWQt6Cy5lFI1uvUhA7mP1M="
100100
}
101101
}

0 commit comments

Comments
 (0)