pyodide 0.18.2 → 0.19.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 +4 -4
- package/api.js +113 -53
- package/error_handling.gen.ts +294 -0
- package/index.test-d.ts +12 -9
- package/load-pyodide.js +141 -112
- package/module.js +37 -9
- package/package.json +1 -1
- package/pyodide.js +145 -157
- package/{pyproxy.gen.js → pyproxy.gen.ts} +414 -607
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// This file is generated by applying the C preprocessor to core/pyproxy.
|
|
1
|
+
// This file is generated by applying the C preprocessor to core/pyproxy.ts
|
|
2
2
|
// It uses the macros defined in core/pyproxy.c
|
|
3
3
|
// Do not edit it directly!
|
|
4
4
|
|
|
@@ -21,154 +21,47 @@
|
|
|
21
21
|
|
|
22
22
|
|
|
23
23
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
24
|
/**
|
|
131
25
|
* Every public Python entrypoint goes through this file! The main entrypoint is
|
|
132
26
|
* the callPyObject method, but of course one can also execute arbitrary code
|
|
133
27
|
* via the various __dundermethods__ associated to classes.
|
|
134
28
|
*
|
|
135
|
-
* The only entrypoint into Python that avoids this file is our bootstrap method
|
|
136
|
-
* runPythonSimple which is defined in main.c
|
|
137
|
-
*
|
|
138
29
|
* Any time we call into wasm, the call should be wrapped in a try catch block.
|
|
139
|
-
* This way if a
|
|
30
|
+
* This way if a JavaScript error emerges from the wasm, we can escalate it to a
|
|
140
31
|
* fatal error.
|
|
141
32
|
*
|
|
142
33
|
* This is file is preprocessed with -imacros "pyproxy.c". As a result of this,
|
|
143
34
|
* any macros available in pyproxy.c are available here. We only need the flags
|
|
144
35
|
* macros HAS_LENGTH, etc.
|
|
145
36
|
*
|
|
146
|
-
* See Makefile recipe for src/js/pyproxy.
|
|
37
|
+
* See Makefile recipe for src/js/pyproxy.gen.ts
|
|
147
38
|
*/
|
|
148
|
-
|
|
149
|
-
import { Module } from "./module.js";
|
|
150
|
-
|
|
39
|
+
import { Module, API, Hiwire } from "./module.js";
|
|
151
40
|
/**
|
|
152
41
|
* Is the argument a :any:`PyProxy`?
|
|
153
|
-
* @param jsobj
|
|
154
|
-
* @returns
|
|
42
|
+
* @param jsobj Object to test.
|
|
43
|
+
* @returns Is ``jsobj`` a :any:`PyProxy`?
|
|
155
44
|
*/
|
|
156
|
-
export function isPyProxy(jsobj) {
|
|
45
|
+
export function isPyProxy(jsobj: any): jsobj is PyProxy {
|
|
157
46
|
return !!jsobj && jsobj.$$ !== undefined && jsobj.$$.type === "PyProxy";
|
|
158
47
|
}
|
|
159
|
-
|
|
160
|
-
|
|
48
|
+
API.isPyProxy = isPyProxy;
|
|
49
|
+
declare var FinalizationRegistry: any;
|
|
50
|
+
declare var globalThis: any;
|
|
161
51
|
if (globalThis.FinalizationRegistry) {
|
|
162
|
-
Module.finalizationRegistry = new FinalizationRegistry(
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
52
|
+
Module.finalizationRegistry = new FinalizationRegistry(
|
|
53
|
+
([ptr, cache]: [ptr: number, cache: PyProxyCache]) => {
|
|
54
|
+
cache.leaked = (!!1);
|
|
55
|
+
pyproxy_decref_cache(cache);
|
|
56
|
+
try {
|
|
57
|
+
Module._Py_DecRef(ptr);
|
|
58
|
+
} catch (e) {
|
|
59
|
+
// I'm not really sure what happens if an error occurs inside of a
|
|
60
|
+
// finalizer...
|
|
61
|
+
API.fatal_error(e);
|
|
62
|
+
}
|
|
170
63
|
}
|
|
171
|
-
|
|
64
|
+
);
|
|
172
65
|
// For some unclear reason this code screws up selenium FirefoxDriver. Works
|
|
173
66
|
// fine in chrome and when I test it in browser. It seems to be sensitive to
|
|
174
67
|
// changes that don't make a difference to the semantics.
|
|
@@ -178,35 +71,33 @@ if (globalThis.FinalizationRegistry) {
|
|
|
178
71
|
// Module._PyBuffer_Release(ptr);
|
|
179
72
|
// Module._PyMem_Free(ptr);
|
|
180
73
|
// } catch (e) {
|
|
181
|
-
//
|
|
74
|
+
// API.fatal_error(e);
|
|
182
75
|
// }
|
|
183
76
|
// });
|
|
184
77
|
} else {
|
|
185
78
|
Module.finalizationRegistry = { register() {}, unregister() {} };
|
|
186
79
|
// Module.bufferFinalizationRegistry = finalizationRegistry;
|
|
187
80
|
}
|
|
188
|
-
|
|
189
81
|
let pyproxy_alloc_map = new Map();
|
|
190
82
|
Module.pyproxy_alloc_map = pyproxy_alloc_map;
|
|
191
|
-
let trace_pyproxy_alloc;
|
|
192
|
-
let trace_pyproxy_dealloc;
|
|
193
|
-
|
|
83
|
+
let trace_pyproxy_alloc: (proxy: any) => void;
|
|
84
|
+
let trace_pyproxy_dealloc: (proxy: any) => void;
|
|
194
85
|
Module.enable_pyproxy_allocation_tracing = function () {
|
|
195
|
-
trace_pyproxy_alloc = function (proxy) {
|
|
86
|
+
trace_pyproxy_alloc = function (proxy: any) {
|
|
196
87
|
pyproxy_alloc_map.set(proxy, Error().stack);
|
|
197
88
|
};
|
|
198
|
-
trace_pyproxy_dealloc = function (proxy) {
|
|
89
|
+
trace_pyproxy_dealloc = function (proxy: any) {
|
|
199
90
|
pyproxy_alloc_map.delete(proxy);
|
|
200
91
|
};
|
|
201
92
|
};
|
|
202
93
|
Module.disable_pyproxy_allocation_tracing = function () {
|
|
203
|
-
trace_pyproxy_alloc = function (proxy) {};
|
|
204
|
-
trace_pyproxy_dealloc = function (proxy) {};
|
|
94
|
+
trace_pyproxy_alloc = function (proxy: any) {};
|
|
95
|
+
trace_pyproxy_dealloc = function (proxy: any) {};
|
|
205
96
|
};
|
|
206
97
|
Module.disable_pyproxy_allocation_tracing();
|
|
207
|
-
|
|
98
|
+
type PyProxyCache = { cacheId: number; refcnt: number; leaked?: boolean };
|
|
208
99
|
/**
|
|
209
|
-
* Create a new PyProxy
|
|
100
|
+
* Create a new PyProxy wrapping ptrobj which is a PyObject*.
|
|
210
101
|
*
|
|
211
102
|
* The argument cache is only needed to implement the PyProxy.copy API, it
|
|
212
103
|
* allows the copy of the PyProxy to share its attribute cache with the original
|
|
@@ -218,7 +109,7 @@ Module.disable_pyproxy_allocation_tracing();
|
|
|
218
109
|
* many as possible.
|
|
219
110
|
* @private
|
|
220
111
|
*/
|
|
221
|
-
Module.pyproxy_new = function (ptrobj, cache) {
|
|
112
|
+
Module.pyproxy_new = function (ptrobj: number, cache?: PyProxyCache) {
|
|
222
113
|
let flags = Module._pyproxy_getflags(ptrobj);
|
|
223
114
|
let cls = Module.getPyProxyClass(flags);
|
|
224
115
|
// Reflect.construct calls the constructor of Module.PyProxyClass but sets
|
|
@@ -243,12 +134,12 @@ Module.pyproxy_new = function (ptrobj, cache) {
|
|
|
243
134
|
if (!cache) {
|
|
244
135
|
// The cache needs to be accessed primarily from the C function
|
|
245
136
|
// _pyproxy_getattr so we make a hiwire id.
|
|
246
|
-
let cacheId =
|
|
137
|
+
let cacheId = Hiwire.new_value(new Map());
|
|
247
138
|
cache = { cacheId, refcnt: 0 };
|
|
248
139
|
}
|
|
249
140
|
cache.refcnt++;
|
|
250
141
|
Object.defineProperty(target, "$$", {
|
|
251
|
-
value: { ptr: ptrobj, type: "PyProxy",
|
|
142
|
+
value: { ptr: ptrobj, type: "PyProxy", cache },
|
|
252
143
|
});
|
|
253
144
|
Module._Py_IncRef(ptrobj);
|
|
254
145
|
let proxy = new Proxy(target, PyProxyHandlers);
|
|
@@ -256,33 +147,24 @@ Module.pyproxy_new = function (ptrobj, cache) {
|
|
|
256
147
|
Module.finalizationRegistry.register(proxy, [ptrobj, cache], proxy);
|
|
257
148
|
return proxy;
|
|
258
149
|
};
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
throw new Error(
|
|
264
|
-
jsobj.$$.destroyed_msg || "Object has already been destroyed"
|
|
265
|
-
);
|
|
150
|
+
function _getPtr(jsobj: any) {
|
|
151
|
+
let ptr: number = jsobj.$$.ptr;
|
|
152
|
+
if (ptr === 0) {
|
|
153
|
+
throw new Error(jsobj.$$.destroyed_msg);
|
|
266
154
|
}
|
|
267
155
|
return ptr;
|
|
268
156
|
}
|
|
269
|
-
|
|
270
157
|
let pyproxyClassMap = new Map();
|
|
271
158
|
/**
|
|
272
|
-
*
|
|
159
|
+
* Retrieve the appropriate mixins based on the features requested in flags.
|
|
273
160
|
* Used by pyproxy_new. The "flags" variable is produced by the C function
|
|
274
161
|
* pyproxy_getflags. Multiple PyProxies with the same set of feature flags
|
|
275
162
|
* will share the same prototype, so the memory footprint of each individual
|
|
276
163
|
* PyProxy is minimal.
|
|
277
164
|
* @private
|
|
278
165
|
*/
|
|
279
|
-
Module.getPyProxyClass = function (flags) {
|
|
280
|
-
|
|
281
|
-
if (result) {
|
|
282
|
-
return result;
|
|
283
|
-
}
|
|
284
|
-
let descriptors = {};
|
|
285
|
-
for (let [feature_flag, methods] of [
|
|
166
|
+
Module.getPyProxyClass = function (flags: number) {
|
|
167
|
+
const FLAG_TYPE_PAIRS: [number, any][] = [
|
|
286
168
|
[(1 << 0), PyProxyLengthMethods],
|
|
287
169
|
[(1 << 1), PyProxyGetItemMethods],
|
|
288
170
|
[(1 << 2), PyProxySetItemMethods],
|
|
@@ -292,7 +174,13 @@ Module.getPyProxyClass = function (flags) {
|
|
|
292
174
|
[(1 << 6), PyProxyAwaitableMethods],
|
|
293
175
|
[(1 << 7), PyProxyBufferMethods],
|
|
294
176
|
[(1 << 8), PyProxyCallableMethods],
|
|
295
|
-
]
|
|
177
|
+
];
|
|
178
|
+
let result = pyproxyClassMap.get(flags);
|
|
179
|
+
if (result) {
|
|
180
|
+
return result;
|
|
181
|
+
}
|
|
182
|
+
let descriptors: any = {};
|
|
183
|
+
for (let [feature_flag, methods] of FLAG_TYPE_PAIRS) {
|
|
296
184
|
if (flags & feature_flag) {
|
|
297
185
|
Object.assign(
|
|
298
186
|
descriptors,
|
|
@@ -315,54 +203,64 @@ Module.getPyProxyClass = function (flags) {
|
|
|
315
203
|
pyproxyClassMap.set(flags, NewPyProxyClass);
|
|
316
204
|
return NewPyProxyClass;
|
|
317
205
|
};
|
|
318
|
-
|
|
319
206
|
// Static methods
|
|
320
207
|
Module.PyProxy_getPtr = _getPtr;
|
|
321
|
-
Module.pyproxy_mark_borrowed = function (proxy) {
|
|
322
|
-
proxy.$$.borrowed = (!!1);
|
|
323
|
-
};
|
|
324
|
-
|
|
325
208
|
const pyproxy_cache_destroyed_msg =
|
|
326
209
|
"This borrowed attribute proxy was automatically destroyed in the " +
|
|
327
210
|
"process of destroying the proxy it was borrowed from. Try using the 'copy' method.";
|
|
328
|
-
|
|
329
|
-
function pyproxy_decref_cache(cache) {
|
|
211
|
+
function pyproxy_decref_cache(cache: PyProxyCache) {
|
|
330
212
|
if (!cache) {
|
|
331
213
|
return;
|
|
332
214
|
}
|
|
333
215
|
cache.refcnt--;
|
|
334
216
|
if (cache.refcnt === 0) {
|
|
335
|
-
let cache_map =
|
|
217
|
+
let cache_map = Hiwire.pop_value(cache.cacheId);
|
|
336
218
|
for (let proxy_id of cache_map.values()) {
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
pyproxy_cache_destroyed_msg
|
|
340
|
-
|
|
219
|
+
const cache_entry = Hiwire.pop_value(proxy_id);
|
|
220
|
+
if (!cache.leaked) {
|
|
221
|
+
Module.pyproxy_destroy(cache_entry, pyproxy_cache_destroyed_msg);
|
|
222
|
+
}
|
|
341
223
|
}
|
|
342
224
|
}
|
|
343
225
|
}
|
|
344
|
-
|
|
345
|
-
|
|
226
|
+
Module.pyproxy_destroy = function (proxy: PyProxy, destroyed_msg: string) {
|
|
227
|
+
if (proxy.$$.ptr === 0) {
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
346
230
|
let ptrobj = _getPtr(proxy);
|
|
347
231
|
Module.finalizationRegistry.unregister(proxy);
|
|
348
|
-
|
|
232
|
+
destroyed_msg = destroyed_msg || "Object has already been destroyed";
|
|
233
|
+
let proxy_type = proxy.type;
|
|
234
|
+
let proxy_repr;
|
|
235
|
+
try {
|
|
236
|
+
proxy_repr = proxy.toString();
|
|
237
|
+
} catch (e) {
|
|
238
|
+
if (e.pyodide_fatal_error) {
|
|
239
|
+
throw e;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
// Maybe the destructor will call JavaScript code that will somehow try
|
|
349
243
|
// to use this proxy. Mark it deleted before decrementing reference count
|
|
350
244
|
// just in case!
|
|
351
|
-
proxy.$$.ptr =
|
|
245
|
+
proxy.$$.ptr = 0;
|
|
246
|
+
destroyed_msg += "\n" + `The object was of type "${proxy_type}" and `;
|
|
247
|
+
if (proxy_repr) {
|
|
248
|
+
destroyed_msg += `had repr "${proxy_repr}"`;
|
|
249
|
+
} else {
|
|
250
|
+
destroyed_msg += "an error was raised when trying to generate its repr";
|
|
251
|
+
}
|
|
352
252
|
proxy.$$.destroyed_msg = destroyed_msg;
|
|
353
253
|
pyproxy_decref_cache(proxy.$$.cache);
|
|
354
254
|
try {
|
|
355
255
|
Module._Py_DecRef(ptrobj);
|
|
356
256
|
trace_pyproxy_dealloc(proxy);
|
|
357
257
|
} catch (e) {
|
|
358
|
-
|
|
258
|
+
API.fatal_error(e);
|
|
359
259
|
}
|
|
360
260
|
};
|
|
361
|
-
|
|
362
261
|
// Now a lot of boilerplate to wrap the abstract Object protocol wrappers
|
|
363
|
-
// defined in pyproxy.c in
|
|
364
|
-
|
|
365
|
-
Module.callPyObjectKwargs = function (ptrobj, ...jsargs) {
|
|
262
|
+
// defined in pyproxy.c in JavaScript functions.
|
|
263
|
+
Module.callPyObjectKwargs = function (ptrobj: number, ...jsargs: any) {
|
|
366
264
|
// We don't do any checking for kwargs, checks are in PyProxy.callKwargs
|
|
367
265
|
// which only is used when the keyword arguments come from the user.
|
|
368
266
|
let kwargs = jsargs.pop();
|
|
@@ -371,9 +269,8 @@ Module.callPyObjectKwargs = function (ptrobj, ...jsargs) {
|
|
|
371
269
|
let kwargs_values = Object.values(kwargs);
|
|
372
270
|
let num_kwargs = kwargs_names.length;
|
|
373
271
|
jsargs.push(...kwargs_values);
|
|
374
|
-
|
|
375
|
-
let
|
|
376
|
-
let idkwnames = Module.hiwire.new_value(kwargs_names);
|
|
272
|
+
let idargs = Hiwire.new_value(jsargs);
|
|
273
|
+
let idkwnames = Hiwire.new_value(kwargs_names);
|
|
377
274
|
let idresult;
|
|
378
275
|
try {
|
|
379
276
|
idresult = Module.__pyproxy_apply(
|
|
@@ -384,30 +281,27 @@ Module.callPyObjectKwargs = function (ptrobj, ...jsargs) {
|
|
|
384
281
|
num_kwargs
|
|
385
282
|
);
|
|
386
283
|
} catch (e) {
|
|
387
|
-
|
|
284
|
+
API.fatal_error(e);
|
|
388
285
|
} finally {
|
|
389
|
-
|
|
390
|
-
|
|
286
|
+
Hiwire.decref(idargs);
|
|
287
|
+
Hiwire.decref(idkwnames);
|
|
391
288
|
}
|
|
392
289
|
if (idresult === 0) {
|
|
393
290
|
Module._pythonexc2js();
|
|
394
291
|
}
|
|
395
|
-
return
|
|
292
|
+
return Hiwire.pop_value(idresult);
|
|
396
293
|
};
|
|
397
|
-
|
|
398
|
-
Module.callPyObject = function (ptrobj, ...jsargs) {
|
|
294
|
+
Module.callPyObject = function (ptrobj: number, ...jsargs: any) {
|
|
399
295
|
return Module.callPyObjectKwargs(ptrobj, ...jsargs, {});
|
|
400
296
|
};
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
*/
|
|
406
|
-
class PyProxyClass {
|
|
297
|
+
export type PyProxy = PyProxyClass & { [x: string]: any };
|
|
298
|
+
export class PyProxyClass {
|
|
299
|
+
$$: { ptr: number; cache: PyProxyCache; destroyed_msg?: string };
|
|
300
|
+
$$flags: number;
|
|
301
|
+
/** @private */
|
|
407
302
|
constructor() {
|
|
408
303
|
throw new TypeError("PyProxy is not a constructor");
|
|
409
304
|
}
|
|
410
|
-
|
|
411
305
|
get [Symbol.toStringTag]() {
|
|
412
306
|
return "PyProxy";
|
|
413
307
|
}
|
|
@@ -425,180 +319,188 @@ class PyProxyClass {
|
|
|
425
319
|
* else:
|
|
426
320
|
* ty.__module__ + "." + ty.__name__
|
|
427
321
|
*
|
|
428
|
-
* @type {string}
|
|
429
322
|
*/
|
|
430
|
-
get type() {
|
|
323
|
+
get type(): string {
|
|
431
324
|
let ptrobj = _getPtr(this);
|
|
432
|
-
return
|
|
325
|
+
return Hiwire.pop_value(Module.__pyproxy_type(ptrobj));
|
|
433
326
|
}
|
|
434
|
-
|
|
435
|
-
* @returns {string}
|
|
436
|
-
*/
|
|
437
|
-
toString() {
|
|
327
|
+
toString(): string {
|
|
438
328
|
let ptrobj = _getPtr(this);
|
|
439
329
|
let jsref_repr;
|
|
440
330
|
try {
|
|
441
331
|
jsref_repr = Module.__pyproxy_repr(ptrobj);
|
|
442
332
|
} catch (e) {
|
|
443
|
-
|
|
333
|
+
API.fatal_error(e);
|
|
444
334
|
}
|
|
445
335
|
if (jsref_repr === 0) {
|
|
446
336
|
Module._pythonexc2js();
|
|
447
337
|
}
|
|
448
|
-
return
|
|
338
|
+
return Hiwire.pop_value(jsref_repr);
|
|
449
339
|
}
|
|
450
340
|
/**
|
|
451
|
-
* Destroy the ``PyProxy``. This will release the memory. Any further
|
|
452
|
-
*
|
|
341
|
+
* Destroy the ``PyProxy``. This will release the memory. Any further attempt
|
|
342
|
+
* to use the object will raise an error.
|
|
453
343
|
*
|
|
454
344
|
* In a browser supporting `FinalizationRegistry
|
|
455
345
|
* <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/FinalizationRegistry>`_
|
|
456
346
|
* Pyodide will automatically destroy the ``PyProxy`` when it is garbage
|
|
457
|
-
* collected, however there is no guarantee that the finalizer will be run
|
|
458
|
-
*
|
|
347
|
+
* collected, however there is no guarantee that the finalizer will be run in
|
|
348
|
+
* a timely manner so it is better to ``destroy`` the proxy explicitly.
|
|
459
349
|
*
|
|
460
|
-
* @param
|
|
461
|
-
*
|
|
462
|
-
* destroyed".
|
|
350
|
+
* @param destroyed_msg The error message to print if use is attempted after
|
|
351
|
+
* destroying. Defaults to "Object has already been destroyed".
|
|
463
352
|
*/
|
|
464
|
-
destroy(destroyed_msg) {
|
|
465
|
-
|
|
466
|
-
Module.pyproxy_destroy(this, destroyed_msg);
|
|
467
|
-
}
|
|
353
|
+
destroy(destroyed_msg?: string) {
|
|
354
|
+
Module.pyproxy_destroy(this, destroyed_msg);
|
|
468
355
|
}
|
|
469
356
|
/**
|
|
470
357
|
* Make a new PyProxy pointing to the same Python object.
|
|
471
358
|
* Useful if the PyProxy is destroyed somewhere else.
|
|
472
|
-
* @returns {PyProxy}
|
|
473
359
|
*/
|
|
474
|
-
copy() {
|
|
360
|
+
copy(): PyProxy {
|
|
475
361
|
let ptrobj = _getPtr(this);
|
|
476
362
|
return Module.pyproxy_new(ptrobj, this.$$.cache);
|
|
477
363
|
}
|
|
478
364
|
/**
|
|
479
|
-
* Converts the ``PyProxy`` into a
|
|
365
|
+
* Converts the ``PyProxy`` into a JavaScript object as best as possible. By
|
|
480
366
|
* default does a deep conversion, if a shallow conversion is desired, you can
|
|
481
367
|
* use ``proxy.toJs({depth : 1})``. See :ref:`Explicit Conversion of PyProxy
|
|
482
368
|
* <type-translations-pyproxy-to-js>` for more info.
|
|
483
|
-
*
|
|
484
|
-
* @
|
|
485
|
-
* @param {number} [options.depth] How many layers deep to perform the
|
|
486
|
-
* conversion. Defaults to infinite.
|
|
487
|
-
* @param {array} [options.pyproxies] If provided, ``toJs`` will store all
|
|
488
|
-
* PyProxies created in this list. This allows you to easily destroy all the
|
|
489
|
-
* PyProxies by iterating the list without having to recurse over the
|
|
490
|
-
* generated structure. The most common use case is to create a new empty
|
|
491
|
-
* list, pass the list as `pyproxies`, and then later iterate over `pyproxies`
|
|
492
|
-
* to destroy all of created proxies.
|
|
493
|
-
* @param {bool} [options.create_pyproxies] If false, ``toJs`` will throw a
|
|
494
|
-
* ``ConversionError`` rather than producing a ``PyProxy``.
|
|
495
|
-
* @param {bool} [options.dict_converter] A function to be called on an
|
|
496
|
-
* iterable of pairs ``[key, value]``. Convert this iterable of pairs to the
|
|
497
|
-
* desired output. For instance, ``Object.fromEntries`` would convert the dict
|
|
498
|
-
* to an object, ``Array.from`` converts it to an array of entries, and ``(it) =>
|
|
499
|
-
* new Map(it)`` converts it to a ``Map`` (which is the default behavior).
|
|
500
|
-
* @return {any} The Javascript object resulting from the conversion.
|
|
369
|
+
* @param options
|
|
370
|
+
* @return The JavaScript object resulting from the conversion.
|
|
501
371
|
*/
|
|
502
372
|
toJs({
|
|
503
373
|
depth = -1,
|
|
504
|
-
pyproxies,
|
|
374
|
+
pyproxies = undefined,
|
|
505
375
|
create_pyproxies = (!!1),
|
|
506
|
-
dict_converter,
|
|
507
|
-
|
|
376
|
+
dict_converter = undefined,
|
|
377
|
+
default_converter = undefined,
|
|
378
|
+
}: {
|
|
379
|
+
/** How many layers deep to perform the conversion. Defaults to infinite */
|
|
380
|
+
depth?: number;
|
|
381
|
+
/**
|
|
382
|
+
* If provided, ``toJs`` will store all PyProxies created in this list. This
|
|
383
|
+
* allows you to easily destroy all the PyProxies by iterating the list
|
|
384
|
+
* without having to recurse over the generated structure. The most common
|
|
385
|
+
* use case is to create a new empty list, pass the list as `pyproxies`, and
|
|
386
|
+
* then later iterate over `pyproxies` to destroy all of created proxies.
|
|
387
|
+
*/
|
|
388
|
+
pyproxies?: PyProxy[];
|
|
389
|
+
/**
|
|
390
|
+
* If false, ``toJs`` will throw a ``ConversionError`` rather than
|
|
391
|
+
* producing a ``PyProxy``.
|
|
392
|
+
*/
|
|
393
|
+
create_pyproxies?: boolean;
|
|
394
|
+
/**
|
|
395
|
+
* A function to be called on an iterable of pairs ``[key, value]``. Convert
|
|
396
|
+
* this iterable of pairs to the desired output. For instance,
|
|
397
|
+
* ``Object.fromEntries`` would convert the dict to an object, ``Array.from``
|
|
398
|
+
* converts it to an array of entries, and ``(it) => new Map(it)`` converts
|
|
399
|
+
* it to a ``Map`` (which is the default behavior).
|
|
400
|
+
*/
|
|
401
|
+
dict_converter?: (array: Iterable<[key: string, value: any]>) => any;
|
|
402
|
+
/**
|
|
403
|
+
* Optional argument to convert objects with no default conversion. See the
|
|
404
|
+
* documentation of :any:`pyodide.to_js`.
|
|
405
|
+
*/
|
|
406
|
+
default_converter?: (
|
|
407
|
+
obj: PyProxy,
|
|
408
|
+
convert: (obj: PyProxy) => any,
|
|
409
|
+
cacheConversion: (obj: PyProxy, result: any) => void
|
|
410
|
+
) => any;
|
|
411
|
+
} = {}): any {
|
|
508
412
|
let ptrobj = _getPtr(this);
|
|
509
413
|
let idresult;
|
|
510
414
|
let proxies_id;
|
|
511
415
|
let dict_converter_id = 0;
|
|
416
|
+
let default_converter_id = 0;
|
|
512
417
|
if (!create_pyproxies) {
|
|
513
418
|
proxies_id = 0;
|
|
514
419
|
} else if (pyproxies) {
|
|
515
|
-
proxies_id =
|
|
420
|
+
proxies_id = Hiwire.new_value(pyproxies);
|
|
516
421
|
} else {
|
|
517
|
-
proxies_id =
|
|
422
|
+
proxies_id = Hiwire.new_value([]);
|
|
518
423
|
}
|
|
519
424
|
if (dict_converter) {
|
|
520
|
-
dict_converter_id =
|
|
425
|
+
dict_converter_id = Hiwire.new_value(dict_converter);
|
|
426
|
+
}
|
|
427
|
+
if (default_converter) {
|
|
428
|
+
default_converter_id = Hiwire.new_value(default_converter);
|
|
521
429
|
}
|
|
522
430
|
try {
|
|
523
|
-
idresult = Module.
|
|
431
|
+
idresult = Module._python2js_custom(
|
|
524
432
|
ptrobj,
|
|
525
433
|
depth,
|
|
526
434
|
proxies_id,
|
|
527
|
-
dict_converter_id
|
|
435
|
+
dict_converter_id,
|
|
436
|
+
default_converter_id
|
|
528
437
|
);
|
|
529
438
|
} catch (e) {
|
|
530
|
-
|
|
439
|
+
API.fatal_error(e);
|
|
531
440
|
} finally {
|
|
532
|
-
|
|
533
|
-
|
|
441
|
+
Hiwire.decref(proxies_id);
|
|
442
|
+
Hiwire.decref(dict_converter_id);
|
|
443
|
+
Hiwire.decref(default_converter_id);
|
|
534
444
|
}
|
|
535
445
|
if (idresult === 0) {
|
|
536
446
|
Module._pythonexc2js();
|
|
537
447
|
}
|
|
538
|
-
return
|
|
448
|
+
return Hiwire.pop_value(idresult);
|
|
539
449
|
}
|
|
540
450
|
/**
|
|
541
451
|
* Check whether the :any:`PyProxy.length` getter is available on this PyProxy. A
|
|
542
452
|
* Typescript type guard.
|
|
543
|
-
* @returns {this is PyProxyWithLength}
|
|
544
453
|
*/
|
|
545
|
-
supportsLength() {
|
|
454
|
+
supportsLength(): this is PyProxyWithLength {
|
|
546
455
|
return !!(this.$$flags & (1 << 0));
|
|
547
456
|
}
|
|
548
457
|
/**
|
|
549
458
|
* Check whether the :any:`PyProxy.get` method is available on this PyProxy. A
|
|
550
459
|
* Typescript type guard.
|
|
551
|
-
* @returns {this is PyProxyWithGet}
|
|
552
460
|
*/
|
|
553
|
-
supportsGet() {
|
|
461
|
+
supportsGet(): this is PyProxyWithGet {
|
|
554
462
|
return !!(this.$$flags & (1 << 1));
|
|
555
463
|
}
|
|
556
464
|
/**
|
|
557
465
|
* Check whether the :any:`PyProxy.set` method is available on this PyProxy. A
|
|
558
466
|
* Typescript type guard.
|
|
559
|
-
* @returns {this is PyProxyWithSet}
|
|
560
467
|
*/
|
|
561
|
-
supportsSet() {
|
|
468
|
+
supportsSet(): this is PyProxyWithSet {
|
|
562
469
|
return !!(this.$$flags & (1 << 2));
|
|
563
470
|
}
|
|
564
471
|
/**
|
|
565
472
|
* Check whether the :any:`PyProxy.has` method is available on this PyProxy. A
|
|
566
473
|
* Typescript type guard.
|
|
567
|
-
* @returns {this is PyProxyWithHas}
|
|
568
474
|
*/
|
|
569
|
-
supportsHas() {
|
|
475
|
+
supportsHas(): this is PyProxyWithHas {
|
|
570
476
|
return !!(this.$$flags & (1 << 3));
|
|
571
477
|
}
|
|
572
478
|
/**
|
|
573
479
|
* Check whether the PyProxy is iterable. A Typescript type guard for
|
|
574
|
-
* :any:`PyProxy.[
|
|
575
|
-
* @returns {this is PyProxyIterable}
|
|
480
|
+
* :any:`PyProxy.[iterator]`.
|
|
576
481
|
*/
|
|
577
|
-
isIterable() {
|
|
482
|
+
isIterable(): this is PyProxyIterable {
|
|
578
483
|
return !!(this.$$flags & ((1 << 4) | (1 << 5)));
|
|
579
484
|
}
|
|
580
485
|
/**
|
|
581
486
|
* Check whether the PyProxy is iterable. A Typescript type guard for
|
|
582
487
|
* :any:`PyProxy.next`.
|
|
583
|
-
* @returns {this is PyProxyIterator}
|
|
584
488
|
*/
|
|
585
|
-
isIterator() {
|
|
489
|
+
isIterator(): this is PyProxyIterator {
|
|
586
490
|
return !!(this.$$flags & (1 << 5));
|
|
587
491
|
}
|
|
588
492
|
/**
|
|
589
493
|
* Check whether the PyProxy is awaitable. A Typescript type guard, if this
|
|
590
494
|
* function returns true Typescript considers the PyProxy to be a ``Promise``.
|
|
591
|
-
* @returns {this is PyProxyAwaitable}
|
|
592
495
|
*/
|
|
593
|
-
isAwaitable() {
|
|
496
|
+
isAwaitable(): this is PyProxyAwaitable {
|
|
594
497
|
return !!(this.$$flags & (1 << 6));
|
|
595
498
|
}
|
|
596
499
|
/**
|
|
597
500
|
* Check whether the PyProxy is a buffer. A Typescript type guard for
|
|
598
501
|
* :any:`PyProxy.getBuffer`.
|
|
599
|
-
* @returns {this is PyProxyBuffer}
|
|
600
502
|
*/
|
|
601
|
-
isBuffer() {
|
|
503
|
+
isBuffer(): this is PyProxyBuffer {
|
|
602
504
|
return !!(this.$$flags & (1 << 7));
|
|
603
505
|
}
|
|
604
506
|
/**
|
|
@@ -606,32 +508,27 @@ class PyProxyClass {
|
|
|
606
508
|
* returns true then Typescript considers the Proxy to be callable of
|
|
607
509
|
* signature ``(args... : any[]) => PyProxy | number | bigint | string |
|
|
608
510
|
* boolean | undefined``.
|
|
609
|
-
* @returns {this is PyProxyCallable}
|
|
610
511
|
*/
|
|
611
|
-
isCallable() {
|
|
512
|
+
isCallable(): this is PyProxyCallable {
|
|
612
513
|
return !!(this.$$flags & (1 << 8));
|
|
613
514
|
}
|
|
614
515
|
}
|
|
615
|
-
|
|
616
|
-
/**
|
|
617
|
-
* @typedef { PyProxy & PyProxyLengthMethods } PyProxyWithLength
|
|
618
|
-
*/
|
|
516
|
+
export type PyProxyWithLength = PyProxy & PyProxyLengthMethods;
|
|
619
517
|
// Controlled by HAS_LENGTH, appears for any object with __len__ or sq_length
|
|
620
518
|
// or mp_length methods
|
|
621
|
-
class PyProxyLengthMethods {
|
|
519
|
+
export class PyProxyLengthMethods {
|
|
622
520
|
/**
|
|
623
521
|
* The length of the object.
|
|
624
522
|
*
|
|
625
523
|
* Present only if the proxied Python object has a ``__len__`` method.
|
|
626
|
-
* @returns {number}
|
|
627
524
|
*/
|
|
628
|
-
get length() {
|
|
525
|
+
get length(): number {
|
|
629
526
|
let ptrobj = _getPtr(this);
|
|
630
527
|
let length;
|
|
631
528
|
try {
|
|
632
529
|
length = Module._PyObject_Size(ptrobj);
|
|
633
530
|
} catch (e) {
|
|
634
|
-
|
|
531
|
+
API.fatal_error(e);
|
|
635
532
|
}
|
|
636
533
|
if (length === -1) {
|
|
637
534
|
Module._pythonexc2js();
|
|
@@ -639,35 +536,28 @@ class PyProxyLengthMethods {
|
|
|
639
536
|
return length;
|
|
640
537
|
}
|
|
641
538
|
}
|
|
642
|
-
|
|
643
|
-
/**
|
|
644
|
-
* @typedef {PyProxy & PyProxyGetItemMethods} PyProxyWithGet
|
|
645
|
-
*/
|
|
646
|
-
|
|
539
|
+
export type PyProxyWithGet = PyProxy & PyProxyGetItemMethods;
|
|
647
540
|
// Controlled by HAS_GET, appears for any class with __getitem__,
|
|
648
541
|
// mp_subscript, or sq_item methods
|
|
649
|
-
|
|
650
|
-
* @interface
|
|
651
|
-
*/
|
|
652
|
-
class PyProxyGetItemMethods {
|
|
542
|
+
export class PyProxyGetItemMethods {
|
|
653
543
|
/**
|
|
654
544
|
* This translates to the Python code ``obj[key]``.
|
|
655
545
|
*
|
|
656
546
|
* Present only if the proxied Python object has a ``__getitem__`` method.
|
|
657
547
|
*
|
|
658
|
-
* @param
|
|
659
|
-
* @returns
|
|
548
|
+
* @param key The key to look up.
|
|
549
|
+
* @returns The corresponding value.
|
|
660
550
|
*/
|
|
661
|
-
get(key) {
|
|
551
|
+
get(key: any): any {
|
|
662
552
|
let ptrobj = _getPtr(this);
|
|
663
|
-
let idkey =
|
|
553
|
+
let idkey = Hiwire.new_value(key);
|
|
664
554
|
let idresult;
|
|
665
555
|
try {
|
|
666
556
|
idresult = Module.__pyproxy_getitem(ptrobj, idkey);
|
|
667
557
|
} catch (e) {
|
|
668
|
-
|
|
558
|
+
API.fatal_error(e);
|
|
669
559
|
} finally {
|
|
670
|
-
|
|
560
|
+
Hiwire.decref(idkey);
|
|
671
561
|
}
|
|
672
562
|
if (idresult === 0) {
|
|
673
563
|
if (Module._PyErr_Occurred()) {
|
|
@@ -676,36 +566,33 @@ class PyProxyGetItemMethods {
|
|
|
676
566
|
return undefined;
|
|
677
567
|
}
|
|
678
568
|
}
|
|
679
|
-
return
|
|
569
|
+
return Hiwire.pop_value(idresult);
|
|
680
570
|
}
|
|
681
571
|
}
|
|
682
|
-
|
|
683
|
-
/**
|
|
684
|
-
* @typedef {PyProxy & PyProxySetItemMethods} PyProxyWithSet
|
|
685
|
-
*/
|
|
572
|
+
export type PyProxyWithSet = PyProxy & PyProxySetItemMethods;
|
|
686
573
|
// Controlled by HAS_SET, appears for any class with __setitem__, __delitem__,
|
|
687
574
|
// mp_ass_subscript, or sq_ass_item.
|
|
688
|
-
class PyProxySetItemMethods {
|
|
575
|
+
export class PyProxySetItemMethods {
|
|
689
576
|
/**
|
|
690
577
|
* This translates to the Python code ``obj[key] = value``.
|
|
691
578
|
*
|
|
692
579
|
* Present only if the proxied Python object has a ``__setitem__`` method.
|
|
693
580
|
*
|
|
694
|
-
* @param
|
|
695
|
-
* @param
|
|
581
|
+
* @param key The key to set.
|
|
582
|
+
* @param value The value to set it to.
|
|
696
583
|
*/
|
|
697
|
-
set(key, value) {
|
|
584
|
+
set(key: any, value: any) {
|
|
698
585
|
let ptrobj = _getPtr(this);
|
|
699
|
-
let idkey =
|
|
700
|
-
let idval =
|
|
586
|
+
let idkey = Hiwire.new_value(key);
|
|
587
|
+
let idval = Hiwire.new_value(value);
|
|
701
588
|
let errcode;
|
|
702
589
|
try {
|
|
703
590
|
errcode = Module.__pyproxy_setitem(ptrobj, idkey, idval);
|
|
704
591
|
} catch (e) {
|
|
705
|
-
|
|
592
|
+
API.fatal_error(e);
|
|
706
593
|
} finally {
|
|
707
|
-
|
|
708
|
-
|
|
594
|
+
Hiwire.decref(idkey);
|
|
595
|
+
Hiwire.decref(idval);
|
|
709
596
|
}
|
|
710
597
|
if (errcode === -1) {
|
|
711
598
|
Module._pythonexc2js();
|
|
@@ -716,50 +603,46 @@ class PyProxySetItemMethods {
|
|
|
716
603
|
*
|
|
717
604
|
* Present only if the proxied Python object has a ``__delitem__`` method.
|
|
718
605
|
*
|
|
719
|
-
* @param
|
|
606
|
+
* @param key The key to delete.
|
|
720
607
|
*/
|
|
721
|
-
delete(key) {
|
|
608
|
+
delete(key: any) {
|
|
722
609
|
let ptrobj = _getPtr(this);
|
|
723
|
-
let idkey =
|
|
610
|
+
let idkey = Hiwire.new_value(key);
|
|
724
611
|
let errcode;
|
|
725
612
|
try {
|
|
726
613
|
errcode = Module.__pyproxy_delitem(ptrobj, idkey);
|
|
727
614
|
} catch (e) {
|
|
728
|
-
|
|
615
|
+
API.fatal_error(e);
|
|
729
616
|
} finally {
|
|
730
|
-
|
|
617
|
+
Hiwire.decref(idkey);
|
|
731
618
|
}
|
|
732
619
|
if (errcode === -1) {
|
|
733
620
|
Module._pythonexc2js();
|
|
734
621
|
}
|
|
735
622
|
}
|
|
736
623
|
}
|
|
737
|
-
|
|
738
|
-
/**
|
|
739
|
-
* @typedef {PyProxy & PyProxyContainsMethods} PyProxyWithHas
|
|
740
|
-
*/
|
|
741
|
-
|
|
624
|
+
export type PyProxyWithHas = PyProxy & PyProxyContainsMethods;
|
|
742
625
|
// Controlled by HAS_CONTAINS flag, appears for any class with __contains__ or
|
|
743
626
|
// sq_contains
|
|
744
|
-
class PyProxyContainsMethods {
|
|
627
|
+
export class PyProxyContainsMethods {
|
|
745
628
|
/**
|
|
746
629
|
* This translates to the Python code ``key in obj``.
|
|
747
630
|
*
|
|
748
631
|
* Present only if the proxied Python object has a ``__contains__`` method.
|
|
749
632
|
*
|
|
750
|
-
* @param
|
|
751
|
-
* @returns
|
|
633
|
+
* @param key The key to check for.
|
|
634
|
+
* @returns Is ``key`` present?
|
|
752
635
|
*/
|
|
753
|
-
has(key) {
|
|
636
|
+
has(key: any): boolean {
|
|
754
637
|
let ptrobj = _getPtr(this);
|
|
755
|
-
let idkey =
|
|
638
|
+
let idkey = Hiwire.new_value(key);
|
|
756
639
|
let result;
|
|
757
640
|
try {
|
|
758
641
|
result = Module.__pyproxy_contains(ptrobj, idkey);
|
|
759
642
|
} catch (e) {
|
|
760
|
-
|
|
643
|
+
API.fatal_error(e);
|
|
761
644
|
} finally {
|
|
762
|
-
|
|
645
|
+
Hiwire.decref(idkey);
|
|
763
646
|
}
|
|
764
647
|
if (result === -1) {
|
|
765
648
|
Module._pythonexc2js();
|
|
@@ -767,9 +650,6 @@ class PyProxyContainsMethods {
|
|
|
767
650
|
return result === 1;
|
|
768
651
|
}
|
|
769
652
|
}
|
|
770
|
-
|
|
771
|
-
class TempError extends Error {}
|
|
772
|
-
|
|
773
653
|
/**
|
|
774
654
|
* A helper for [Symbol.iterator].
|
|
775
655
|
*
|
|
@@ -786,39 +666,28 @@ class TempError extends Error {}
|
|
|
786
666
|
*
|
|
787
667
|
* @private
|
|
788
668
|
*/
|
|
789
|
-
function* iter_helper(iterptr, token) {
|
|
669
|
+
function* iter_helper(iterptr: number, token: {}): Generator<any> {
|
|
790
670
|
try {
|
|
791
|
-
if (iterptr === 0) {
|
|
792
|
-
throw new TempError();
|
|
793
|
-
}
|
|
794
671
|
let item;
|
|
795
672
|
while ((item = Module.__pyproxy_iter_next(iterptr))) {
|
|
796
|
-
yield
|
|
797
|
-
}
|
|
798
|
-
if (Module._PyErr_Occurred()) {
|
|
799
|
-
throw new TempError();
|
|
673
|
+
yield Hiwire.pop_value(item);
|
|
800
674
|
}
|
|
801
675
|
} catch (e) {
|
|
802
|
-
|
|
803
|
-
Module._pythonexc2js();
|
|
804
|
-
} else {
|
|
805
|
-
Module.fatal_error(e);
|
|
806
|
-
}
|
|
676
|
+
API.fatal_error(e);
|
|
807
677
|
} finally {
|
|
808
678
|
Module.finalizationRegistry.unregister(token);
|
|
809
679
|
Module._Py_DecRef(iterptr);
|
|
810
680
|
}
|
|
681
|
+
if (Module._PyErr_Occurred()) {
|
|
682
|
+
Module._pythonexc2js();
|
|
683
|
+
}
|
|
811
684
|
}
|
|
812
|
-
|
|
813
|
-
/**
|
|
814
|
-
* @typedef {PyProxy & PyProxyIterableMethods} PyProxyIterable
|
|
815
|
-
*/
|
|
816
|
-
|
|
685
|
+
export type PyProxyIterable = PyProxy & PyProxyIterableMethods;
|
|
817
686
|
// Controlled by IS_ITERABLE, appears for any object with __iter__ or tp_iter,
|
|
818
687
|
// unless they are iterators. See: https://docs.python.org/3/c-api/iter.html
|
|
819
688
|
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols
|
|
820
689
|
// This avoids allocating a PyProxy wrapper for the temporary iterator.
|
|
821
|
-
class PyProxyIterableMethods {
|
|
690
|
+
export class PyProxyIterableMethods {
|
|
822
691
|
/**
|
|
823
692
|
* This translates to the Python code ``iter(obj)``. Return an iterator
|
|
824
693
|
* associated to the proxy. See the documentation for `Symbol.iterator
|
|
@@ -828,114 +697,110 @@ class PyProxyIterableMethods {
|
|
|
828
697
|
* ``__iter__`` method).
|
|
829
698
|
*
|
|
830
699
|
* This will be used implicitly by ``for(let x of proxy){}``.
|
|
831
|
-
*
|
|
832
|
-
* @returns {Iterator<Py2JsResult, Py2JsResult, any>} An iterator for the proxied Python object.
|
|
833
700
|
*/
|
|
834
|
-
[Symbol.iterator]() {
|
|
701
|
+
[Symbol.iterator](): Iterator<any, any, any> {
|
|
835
702
|
let ptrobj = _getPtr(this);
|
|
836
703
|
let token = {};
|
|
837
704
|
let iterptr;
|
|
838
705
|
try {
|
|
839
706
|
iterptr = Module._PyObject_GetIter(ptrobj);
|
|
840
707
|
} catch (e) {
|
|
841
|
-
|
|
708
|
+
API.fatal_error(e);
|
|
709
|
+
}
|
|
710
|
+
if (iterptr === 0) {
|
|
711
|
+
Module._pythonexc2js();
|
|
842
712
|
}
|
|
843
|
-
|
|
844
713
|
let result = iter_helper(iterptr, token);
|
|
845
714
|
Module.finalizationRegistry.register(result, [iterptr, undefined], token);
|
|
846
715
|
return result;
|
|
847
716
|
}
|
|
848
717
|
}
|
|
849
|
-
|
|
850
|
-
/**
|
|
851
|
-
* @typedef {PyProxy & PyProxyIteratorMethods} PyProxyIterator
|
|
852
|
-
*/
|
|
853
|
-
|
|
718
|
+
export type PyProxyIterator = PyProxy & PyProxyIteratorMethods;
|
|
854
719
|
// Controlled by IS_ITERATOR, appears for any object with a __next__ or
|
|
855
720
|
// tp_iternext method.
|
|
856
|
-
class PyProxyIteratorMethods {
|
|
721
|
+
export class PyProxyIteratorMethods {
|
|
722
|
+
/** @private */
|
|
857
723
|
[Symbol.iterator]() {
|
|
858
724
|
return this;
|
|
859
725
|
}
|
|
860
726
|
/**
|
|
861
|
-
* This translates to the Python code ``next(obj)``. Returns the next value
|
|
862
|
-
*
|
|
727
|
+
* This translates to the Python code ``next(obj)``. Returns the next value of
|
|
728
|
+
* the generator. See the documentation for `Generator.prototype.next
|
|
863
729
|
* <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Generator/next>`_.
|
|
864
730
|
* The argument will be sent to the Python generator.
|
|
865
731
|
*
|
|
866
732
|
* This will be used implicitly by ``for(let x of proxy){}``.
|
|
867
733
|
*
|
|
868
|
-
* Present only if the proxied Python object is a generator or iterator
|
|
869
|
-
*
|
|
734
|
+
* Present only if the proxied Python object is a generator or iterator (i.e.,
|
|
735
|
+
* has a ``send`` or ``__next__`` method).
|
|
870
736
|
*
|
|
871
|
-
* @param
|
|
872
|
-
*
|
|
873
|
-
* @returns
|
|
874
|
-
*
|
|
875
|
-
*
|
|
876
|
-
*
|
|
877
|
-
* true, value : result_value}``.
|
|
737
|
+
* @param any The value to send to the generator. The value will be assigned
|
|
738
|
+
* as a result of a yield expression.
|
|
739
|
+
* @returns An Object with two properties: ``done`` and ``value``. When the
|
|
740
|
+
* generator yields ``some_value``, ``next`` returns ``{done : false, value :
|
|
741
|
+
* some_value}``. When the generator raises a ``StopIteration(result_value)``
|
|
742
|
+
* exception, ``next`` returns ``{done : true, value : result_value}``.
|
|
878
743
|
*/
|
|
879
|
-
next(arg = undefined) {
|
|
880
|
-
let idresult;
|
|
744
|
+
next(arg: any = undefined): IteratorResult<any, any> {
|
|
881
745
|
// Note: arg is optional, if arg is not supplied, it will be undefined
|
|
882
746
|
// which gets converted to "Py_None". This is as intended.
|
|
883
|
-
let idarg =
|
|
747
|
+
let idarg = Hiwire.new_value(arg);
|
|
748
|
+
let status;
|
|
884
749
|
let done;
|
|
750
|
+
let stackTop = Module.stackSave();
|
|
751
|
+
let res_ptr = Module.stackAlloc(4);
|
|
885
752
|
try {
|
|
886
|
-
|
|
887
|
-
done = idresult === 0;
|
|
888
|
-
if (done) {
|
|
889
|
-
idresult = Module.__pyproxyGen_FetchStopIterationValue();
|
|
890
|
-
}
|
|
753
|
+
status = Module.__pyproxyGen_Send(_getPtr(this), idarg, res_ptr);
|
|
891
754
|
} catch (e) {
|
|
892
|
-
|
|
755
|
+
API.fatal_error(e);
|
|
893
756
|
} finally {
|
|
894
|
-
|
|
757
|
+
Hiwire.decref(idarg);
|
|
895
758
|
}
|
|
896
|
-
|
|
759
|
+
let HEAPU32 = Module.HEAPU32;
|
|
760
|
+
let idresult = HEAPU32[(res_ptr >> 2) + 0];
|
|
761
|
+
Module.stackRestore(stackTop);
|
|
762
|
+
if (status === -1) {
|
|
897
763
|
Module._pythonexc2js();
|
|
898
764
|
}
|
|
899
|
-
let value =
|
|
765
|
+
let value = Hiwire.pop_value(idresult);
|
|
766
|
+
done = status === 0;
|
|
900
767
|
return { done, value };
|
|
901
768
|
}
|
|
902
769
|
}
|
|
903
|
-
|
|
904
770
|
// Another layer of boilerplate. The PyProxyHandlers have some annoying logic
|
|
905
771
|
// to deal with straining out the spurious "Function" properties "prototype",
|
|
906
772
|
// "arguments", and "length", to deal with correctly satisfying the Proxy
|
|
907
773
|
// invariants, and to deal with the mro
|
|
908
|
-
function python_hasattr(jsobj, jskey) {
|
|
774
|
+
function python_hasattr(jsobj: PyProxyClass, jskey: any) {
|
|
909
775
|
let ptrobj = _getPtr(jsobj);
|
|
910
|
-
let idkey =
|
|
776
|
+
let idkey = Hiwire.new_value(jskey);
|
|
911
777
|
let result;
|
|
912
778
|
try {
|
|
913
779
|
result = Module.__pyproxy_hasattr(ptrobj, idkey);
|
|
914
780
|
} catch (e) {
|
|
915
|
-
|
|
781
|
+
API.fatal_error(e);
|
|
916
782
|
} finally {
|
|
917
|
-
|
|
783
|
+
Hiwire.decref(idkey);
|
|
918
784
|
}
|
|
919
785
|
if (result === -1) {
|
|
920
786
|
Module._pythonexc2js();
|
|
921
787
|
}
|
|
922
788
|
return result !== 0;
|
|
923
789
|
}
|
|
924
|
-
|
|
925
790
|
// Returns a JsRef in order to allow us to differentiate between "not found"
|
|
926
791
|
// (in which case we return 0) and "found 'None'" (in which case we return
|
|
927
792
|
// Js_undefined).
|
|
928
|
-
function python_getattr(jsobj, jskey) {
|
|
793
|
+
function python_getattr(jsobj: PyProxyClass, jskey: any) {
|
|
929
794
|
let ptrobj = _getPtr(jsobj);
|
|
930
|
-
let idkey =
|
|
795
|
+
let idkey = Hiwire.new_value(jskey);
|
|
931
796
|
let idresult;
|
|
932
797
|
let cacheId = jsobj.$$.cache.cacheId;
|
|
933
798
|
try {
|
|
934
799
|
idresult = Module.__pyproxy_getattr(ptrobj, idkey, cacheId);
|
|
935
800
|
} catch (e) {
|
|
936
|
-
|
|
801
|
+
API.fatal_error(e);
|
|
937
802
|
} finally {
|
|
938
|
-
|
|
803
|
+
Hiwire.decref(idkey);
|
|
939
804
|
}
|
|
940
805
|
if (idresult === 0) {
|
|
941
806
|
if (Module._PyErr_Occurred()) {
|
|
@@ -944,41 +809,38 @@ function python_getattr(jsobj, jskey) {
|
|
|
944
809
|
}
|
|
945
810
|
return idresult;
|
|
946
811
|
}
|
|
947
|
-
|
|
948
|
-
function python_setattr(jsobj, jskey, jsval) {
|
|
812
|
+
function python_setattr(jsobj: PyProxyClass, jskey: any, jsval: any) {
|
|
949
813
|
let ptrobj = _getPtr(jsobj);
|
|
950
|
-
let idkey =
|
|
951
|
-
let idval =
|
|
814
|
+
let idkey = Hiwire.new_value(jskey);
|
|
815
|
+
let idval = Hiwire.new_value(jsval);
|
|
952
816
|
let errcode;
|
|
953
817
|
try {
|
|
954
818
|
errcode = Module.__pyproxy_setattr(ptrobj, idkey, idval);
|
|
955
819
|
} catch (e) {
|
|
956
|
-
|
|
820
|
+
API.fatal_error(e);
|
|
957
821
|
} finally {
|
|
958
|
-
|
|
959
|
-
|
|
822
|
+
Hiwire.decref(idkey);
|
|
823
|
+
Hiwire.decref(idval);
|
|
960
824
|
}
|
|
961
825
|
if (errcode === -1) {
|
|
962
826
|
Module._pythonexc2js();
|
|
963
827
|
}
|
|
964
828
|
}
|
|
965
|
-
|
|
966
|
-
function python_delattr(jsobj, jskey) {
|
|
829
|
+
function python_delattr(jsobj: PyProxyClass, jskey: any) {
|
|
967
830
|
let ptrobj = _getPtr(jsobj);
|
|
968
|
-
let idkey =
|
|
831
|
+
let idkey = Hiwire.new_value(jskey);
|
|
969
832
|
let errcode;
|
|
970
833
|
try {
|
|
971
834
|
errcode = Module.__pyproxy_delattr(ptrobj, idkey);
|
|
972
835
|
} catch (e) {
|
|
973
|
-
|
|
836
|
+
API.fatal_error(e);
|
|
974
837
|
} finally {
|
|
975
|
-
|
|
838
|
+
Hiwire.decref(idkey);
|
|
976
839
|
}
|
|
977
840
|
if (errcode === -1) {
|
|
978
841
|
Module._pythonexc2js();
|
|
979
842
|
}
|
|
980
843
|
}
|
|
981
|
-
|
|
982
844
|
// See explanation of which methods should be defined here and what they do
|
|
983
845
|
// here:
|
|
984
846
|
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy
|
|
@@ -986,7 +848,7 @@ let PyProxyHandlers = {
|
|
|
986
848
|
isExtensible() {
|
|
987
849
|
return (!!1);
|
|
988
850
|
},
|
|
989
|
-
has(jsobj, jskey) {
|
|
851
|
+
has(jsobj: PyProxyClass, jskey: any) {
|
|
990
852
|
// Note: must report "prototype" in proxy when we are callable.
|
|
991
853
|
// (We can return the wrong value from "get" handler though.)
|
|
992
854
|
let objHasKey = Reflect.has(jsobj, jskey);
|
|
@@ -1002,11 +864,10 @@ let PyProxyHandlers = {
|
|
|
1002
864
|
}
|
|
1003
865
|
return python_hasattr(jsobj, jskey);
|
|
1004
866
|
},
|
|
1005
|
-
get(jsobj, jskey) {
|
|
867
|
+
get(jsobj: PyProxyClass, jskey: any) {
|
|
1006
868
|
// Preference order:
|
|
1007
|
-
// 1. stuff from
|
|
869
|
+
// 1. stuff from JavaScript
|
|
1008
870
|
// 2. the result of Python getattr
|
|
1009
|
-
|
|
1010
871
|
// python_getattr will crash if given a Symbol.
|
|
1011
872
|
if (jskey in jsobj || typeof jskey === "symbol") {
|
|
1012
873
|
return Reflect.get(jsobj, jskey);
|
|
@@ -1019,10 +880,10 @@ let PyProxyHandlers = {
|
|
|
1019
880
|
// 2. The result of getattr
|
|
1020
881
|
let idresult = python_getattr(jsobj, jskey);
|
|
1021
882
|
if (idresult !== 0) {
|
|
1022
|
-
return
|
|
883
|
+
return Hiwire.pop_value(idresult);
|
|
1023
884
|
}
|
|
1024
885
|
},
|
|
1025
|
-
set(jsobj, jskey, jsval) {
|
|
886
|
+
set(jsobj: PyProxyClass, jskey: any, jsval: any) {
|
|
1026
887
|
let descr = Object.getOwnPropertyDescriptor(jsobj, jskey);
|
|
1027
888
|
if (descr && !descr.writable) {
|
|
1028
889
|
throw new TypeError(`Cannot set read only field '${jskey}'`);
|
|
@@ -1037,7 +898,7 @@ let PyProxyHandlers = {
|
|
|
1037
898
|
python_setattr(jsobj, jskey, jsval);
|
|
1038
899
|
return (!!1);
|
|
1039
900
|
},
|
|
1040
|
-
deleteProperty(jsobj, jskey) {
|
|
901
|
+
deleteProperty(jsobj: PyProxyClass, jskey: any): boolean {
|
|
1041
902
|
let descr = Object.getOwnPropertyDescriptor(jsobj, jskey);
|
|
1042
903
|
if (descr && !descr.writable) {
|
|
1043
904
|
throw new TypeError(`Cannot delete read only field '${jskey}'`);
|
|
@@ -1050,45 +911,45 @@ let PyProxyHandlers = {
|
|
|
1050
911
|
}
|
|
1051
912
|
python_delattr(jsobj, jskey);
|
|
1052
913
|
// Must return "false" if "jskey" is a nonconfigurable own property.
|
|
1053
|
-
// Otherwise
|
|
1054
|
-
return !descr || descr.configurable;
|
|
914
|
+
// Otherwise JavaScript will throw a TypeError.
|
|
915
|
+
return !descr || !!descr.configurable;
|
|
1055
916
|
},
|
|
1056
|
-
ownKeys(jsobj) {
|
|
917
|
+
ownKeys(jsobj: PyProxyClass) {
|
|
1057
918
|
let ptrobj = _getPtr(jsobj);
|
|
1058
919
|
let idresult;
|
|
1059
920
|
try {
|
|
1060
921
|
idresult = Module.__pyproxy_ownKeys(ptrobj);
|
|
1061
922
|
} catch (e) {
|
|
1062
|
-
|
|
923
|
+
API.fatal_error(e);
|
|
1063
924
|
}
|
|
1064
925
|
if (idresult === 0) {
|
|
1065
926
|
Module._pythonexc2js();
|
|
1066
927
|
}
|
|
1067
|
-
let result =
|
|
928
|
+
let result = Hiwire.pop_value(idresult);
|
|
1068
929
|
result.push(...Reflect.ownKeys(jsobj));
|
|
1069
930
|
return result;
|
|
1070
931
|
},
|
|
1071
|
-
apply(jsobj, jsthis, jsargs) {
|
|
932
|
+
apply(jsobj: PyProxyClass & Function, jsthis: any, jsargs: any) {
|
|
1072
933
|
return jsobj.apply(jsthis, jsargs);
|
|
1073
934
|
},
|
|
1074
935
|
};
|
|
1075
|
-
|
|
1076
|
-
/**
|
|
1077
|
-
* @typedef {PyProxy & Promise<Py2JsResult>} PyProxyAwaitable
|
|
1078
|
-
*/
|
|
1079
|
-
|
|
936
|
+
export type PyProxyAwaitable = PyProxy & Promise<any>;
|
|
1080
937
|
/**
|
|
1081
|
-
* The Promise /
|
|
938
|
+
* The Promise / JavaScript awaitable API.
|
|
1082
939
|
* @private
|
|
1083
940
|
*/
|
|
1084
|
-
class PyProxyAwaitableMethods {
|
|
941
|
+
export class PyProxyAwaitableMethods {
|
|
942
|
+
$$: any;
|
|
1085
943
|
/**
|
|
1086
944
|
* This wraps __pyproxy_ensure_future and makes a function that converts a
|
|
1087
945
|
* Python awaitable to a promise, scheduling the awaitable on the Python
|
|
1088
946
|
* event loop if necessary.
|
|
1089
947
|
* @private
|
|
1090
948
|
*/
|
|
1091
|
-
_ensure_future() {
|
|
949
|
+
_ensure_future(): Promise<any> {
|
|
950
|
+
if (this.$$.promise) {
|
|
951
|
+
return this.$$.promise;
|
|
952
|
+
}
|
|
1092
953
|
let ptrobj = _getPtr(this);
|
|
1093
954
|
let resolveHandle;
|
|
1094
955
|
let rejectHandle;
|
|
@@ -1096,8 +957,8 @@ class PyProxyAwaitableMethods {
|
|
|
1096
957
|
resolveHandle = resolve;
|
|
1097
958
|
rejectHandle = reject;
|
|
1098
959
|
});
|
|
1099
|
-
let resolve_handle_id =
|
|
1100
|
-
let reject_handle_id =
|
|
960
|
+
let resolve_handle_id = Hiwire.new_value(resolveHandle);
|
|
961
|
+
let reject_handle_id = Hiwire.new_value(rejectHandle);
|
|
1101
962
|
let errcode;
|
|
1102
963
|
try {
|
|
1103
964
|
errcode = Module.__pyproxy_ensure_future(
|
|
@@ -1106,21 +967,24 @@ class PyProxyAwaitableMethods {
|
|
|
1106
967
|
reject_handle_id
|
|
1107
968
|
);
|
|
1108
969
|
} catch (e) {
|
|
1109
|
-
|
|
970
|
+
API.fatal_error(e);
|
|
1110
971
|
} finally {
|
|
1111
|
-
|
|
1112
|
-
|
|
972
|
+
Hiwire.decref(reject_handle_id);
|
|
973
|
+
Hiwire.decref(resolve_handle_id);
|
|
1113
974
|
}
|
|
1114
975
|
if (errcode === -1) {
|
|
1115
976
|
Module._pythonexc2js();
|
|
1116
977
|
}
|
|
978
|
+
this.$$.promise = promise;
|
|
979
|
+
// @ts-ignore
|
|
980
|
+
this.destroy();
|
|
1117
981
|
return promise;
|
|
1118
982
|
}
|
|
1119
983
|
/**
|
|
1120
984
|
* Runs ``asyncio.ensure_future(awaitable)``, executes
|
|
1121
985
|
* ``onFulfilled(result)`` when the ``Future`` resolves successfully,
|
|
1122
986
|
* executes ``onRejected(error)`` when the ``Future`` fails. Will be used
|
|
1123
|
-
*
|
|
987
|
+
* implicitly by ``await obj``.
|
|
1124
988
|
*
|
|
1125
989
|
* See the documentation for
|
|
1126
990
|
* `Promise.then
|
|
@@ -1129,13 +993,16 @@ class PyProxyAwaitableMethods {
|
|
|
1129
993
|
* Present only if the proxied Python object is `awaitable
|
|
1130
994
|
* <https://docs.python.org/3/library/asyncio-task.html?highlight=awaitable#awaitables>`_.
|
|
1131
995
|
*
|
|
1132
|
-
* @param
|
|
996
|
+
* @param onFulfilled A handler called with the result as an
|
|
1133
997
|
* argument if the awaitable succeeds.
|
|
1134
|
-
* @param
|
|
998
|
+
* @param onRejected A handler called with the error as an
|
|
1135
999
|
* argument if the awaitable fails.
|
|
1136
|
-
* @returns
|
|
1000
|
+
* @returns The resulting Promise.
|
|
1137
1001
|
*/
|
|
1138
|
-
then(
|
|
1002
|
+
then(
|
|
1003
|
+
onFulfilled: (value: any) => any,
|
|
1004
|
+
onRejected: (reason: any) => any
|
|
1005
|
+
): Promise<any> {
|
|
1139
1006
|
let promise = this._ensure_future();
|
|
1140
1007
|
return promise.then(onFulfilled, onRejected);
|
|
1141
1008
|
}
|
|
@@ -1150,11 +1017,11 @@ class PyProxyAwaitableMethods {
|
|
|
1150
1017
|
* Present only if the proxied Python object is `awaitable
|
|
1151
1018
|
* <https://docs.python.org/3/library/asyncio-task.html?highlight=awaitable#awaitables>`_.
|
|
1152
1019
|
*
|
|
1153
|
-
* @param
|
|
1020
|
+
* @param onRejected A handler called with the error as an
|
|
1154
1021
|
* argument if the awaitable fails.
|
|
1155
|
-
* @returns
|
|
1022
|
+
* @returns The resulting Promise.
|
|
1156
1023
|
*/
|
|
1157
|
-
catch(onRejected) {
|
|
1024
|
+
catch(onRejected: (reason: any) => any) {
|
|
1158
1025
|
let promise = this._ensure_future();
|
|
1159
1026
|
return promise.catch(onRejected);
|
|
1160
1027
|
}
|
|
@@ -1170,33 +1037,32 @@ class PyProxyAwaitableMethods {
|
|
|
1170
1037
|
* <https://docs.python.org/3/library/asyncio-task.html?highlight=awaitable#awaitables>`_.
|
|
1171
1038
|
*
|
|
1172
1039
|
*
|
|
1173
|
-
* @param
|
|
1040
|
+
* @param onFinally A handler that is called with zero arguments
|
|
1174
1041
|
* when the awaitable resolves.
|
|
1175
|
-
* @returns
|
|
1042
|
+
* @returns A Promise that resolves or rejects with the same
|
|
1176
1043
|
* result as the original Promise, but only after executing the
|
|
1177
1044
|
* ``onFinally`` handler.
|
|
1178
1045
|
*/
|
|
1179
|
-
finally(onFinally) {
|
|
1046
|
+
finally(onFinally: () => void) {
|
|
1180
1047
|
let promise = this._ensure_future();
|
|
1181
1048
|
return promise.finally(onFinally);
|
|
1182
1049
|
}
|
|
1183
1050
|
}
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
apply(jsthis, jsargs) {
|
|
1051
|
+
export type PyProxyCallable = PyProxy &
|
|
1052
|
+
PyProxyCallableMethods &
|
|
1053
|
+
((...args: any[]) => any);
|
|
1054
|
+
export class PyProxyCallableMethods {
|
|
1055
|
+
apply(jsthis: PyProxyClass, jsargs: any) {
|
|
1190
1056
|
return Module.callPyObject(_getPtr(this), ...jsargs);
|
|
1191
1057
|
}
|
|
1192
|
-
call(jsthis, ...jsargs) {
|
|
1058
|
+
call(jsthis: PyProxyClass, ...jsargs: any) {
|
|
1193
1059
|
return Module.callPyObject(_getPtr(this), ...jsargs);
|
|
1194
1060
|
}
|
|
1195
1061
|
/**
|
|
1196
1062
|
* Call the function with key word arguments.
|
|
1197
1063
|
* The last argument must be an object with the keyword arguments.
|
|
1198
1064
|
*/
|
|
1199
|
-
callKwargs(...jsargs) {
|
|
1065
|
+
callKwargs(...jsargs: any) {
|
|
1200
1066
|
if (jsargs.length === 0) {
|
|
1201
1067
|
throw new TypeError(
|
|
1202
1068
|
"callKwargs requires at least one argument (the key word argument object)"
|
|
@@ -1212,9 +1078,10 @@ class PyProxyCallableMethods {
|
|
|
1212
1078
|
return Module.callPyObjectKwargs(_getPtr(this), ...jsargs);
|
|
1213
1079
|
}
|
|
1214
1080
|
}
|
|
1081
|
+
// @ts-ignore
|
|
1215
1082
|
PyProxyCallableMethods.prototype.prototype = Function.prototype;
|
|
1216
|
-
|
|
1217
|
-
let type_to_array_map = new Map([
|
|
1083
|
+
// @ts-ignore
|
|
1084
|
+
let type_to_array_map: Map<string, any> = new Map([
|
|
1218
1085
|
["i8", Int8Array],
|
|
1219
1086
|
["u8", Uint8Array],
|
|
1220
1087
|
["u8clamped", Uint8ClampedArray],
|
|
@@ -1232,20 +1099,17 @@ let type_to_array_map = new Map([
|
|
|
1232
1099
|
["f64", Float64Array],
|
|
1233
1100
|
["dataview", DataView],
|
|
1234
1101
|
]);
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
* @typedef {PyProxy & PyProxyBufferMethods} PyProxyBuffer
|
|
1238
|
-
*/
|
|
1239
|
-
class PyProxyBufferMethods {
|
|
1102
|
+
export type PyProxyBuffer = PyProxy & PyProxyBufferMethods;
|
|
1103
|
+
export class PyProxyBufferMethods {
|
|
1240
1104
|
/**
|
|
1241
|
-
* Get a view of the buffer data which is usable from
|
|
1105
|
+
* Get a view of the buffer data which is usable from JavaScript. No copy is
|
|
1242
1106
|
* ever performed.
|
|
1243
1107
|
*
|
|
1244
1108
|
* Present only if the proxied Python object supports the `Python Buffer
|
|
1245
1109
|
* Protocol <https://docs.python.org/3/c-api/buffer.html>`_.
|
|
1246
1110
|
*
|
|
1247
1111
|
* We do not support suboffsets, if the buffer requires suboffsets we will
|
|
1248
|
-
* throw an error.
|
|
1112
|
+
* throw an error. JavaScript nd array libraries can't handle suboffsets
|
|
1249
1113
|
* anyways. In this case, you should use the :any:`toJs` api or copy the
|
|
1250
1114
|
* buffer to one that doesn't use suboffets (using e.g.,
|
|
1251
1115
|
* `numpy.ascontiguousarray
|
|
@@ -1258,16 +1122,16 @@ class PyProxyBufferMethods {
|
|
|
1258
1122
|
* have support for big endian data, so you might want to pass
|
|
1259
1123
|
* ``'dataview'`` as the type argument in that case.
|
|
1260
1124
|
*
|
|
1261
|
-
* @param
|
|
1125
|
+
* @param type The type of the :any:`PyBuffer.data <pyodide.PyBuffer.data>` field in the
|
|
1262
1126
|
* output. Should be one of: ``"i8"``, ``"u8"``, ``"u8clamped"``, ``"i16"``,
|
|
1263
1127
|
* ``"u16"``, ``"i32"``, ``"u32"``, ``"i32"``, ``"u32"``, ``"i64"``,
|
|
1264
1128
|
* ``"u64"``, ``"f32"``, ``"f64``, or ``"dataview"``. This argument is
|
|
1265
1129
|
* optional, if absent ``getBuffer`` will try to determine the appropriate
|
|
1266
1130
|
* output type based on the buffer `format string
|
|
1267
1131
|
* <https://docs.python.org/3/library/struct.html#format-strings>`_.
|
|
1268
|
-
* @returns
|
|
1132
|
+
* @returns :any:`PyBuffer <pyodide.PyBuffer>`
|
|
1269
1133
|
*/
|
|
1270
|
-
getBuffer(type) {
|
|
1134
|
+
getBuffer(type?: string): PyBuffer {
|
|
1271
1135
|
let ArrayType = undefined;
|
|
1272
1136
|
if (type) {
|
|
1273
1137
|
ArrayType = type_to_array_map.get(type);
|
|
@@ -1285,30 +1149,25 @@ class PyProxyBufferMethods {
|
|
|
1285
1149
|
try {
|
|
1286
1150
|
errcode = Module.__pyproxy_get_buffer(buffer_struct_ptr, this_ptr);
|
|
1287
1151
|
} catch (e) {
|
|
1288
|
-
|
|
1152
|
+
API.fatal_error(e);
|
|
1289
1153
|
}
|
|
1290
1154
|
if (errcode === -1) {
|
|
1291
1155
|
Module._pythonexc2js();
|
|
1292
1156
|
}
|
|
1293
|
-
|
|
1294
1157
|
// This has to match the fields in buffer_struct
|
|
1295
1158
|
let startByteOffset = HEAPU32[(buffer_struct_ptr >> 2) + 0];
|
|
1296
1159
|
let minByteOffset = HEAPU32[(buffer_struct_ptr >> 2) + 1];
|
|
1297
1160
|
let maxByteOffset = HEAPU32[(buffer_struct_ptr >> 2) + 2];
|
|
1298
|
-
|
|
1299
1161
|
let readonly = !!HEAPU32[(buffer_struct_ptr >> 2) + 3];
|
|
1300
1162
|
let format_ptr = HEAPU32[(buffer_struct_ptr >> 2) + 4];
|
|
1301
1163
|
let itemsize = HEAPU32[(buffer_struct_ptr >> 2) + 5];
|
|
1302
|
-
let shape =
|
|
1303
|
-
let strides =
|
|
1304
|
-
|
|
1164
|
+
let shape = Hiwire.pop_value(HEAPU32[(buffer_struct_ptr >> 2) + 6]);
|
|
1165
|
+
let strides = Hiwire.pop_value(HEAPU32[(buffer_struct_ptr >> 2) + 7]);
|
|
1305
1166
|
let view_ptr = HEAPU32[(buffer_struct_ptr >> 2) + 8];
|
|
1306
1167
|
let c_contiguous = !!HEAPU32[(buffer_struct_ptr >> 2) + 9];
|
|
1307
1168
|
let f_contiguous = !!HEAPU32[(buffer_struct_ptr >> 2) + 10];
|
|
1308
|
-
|
|
1309
1169
|
let format = Module.UTF8ToString(format_ptr);
|
|
1310
1170
|
Module.stackRestore(orig_stack_ptr);
|
|
1311
|
-
|
|
1312
1171
|
let success = (!!0);
|
|
1313
1172
|
try {
|
|
1314
1173
|
let bigEndian = (!!0);
|
|
@@ -1351,7 +1210,6 @@ class PyProxyBufferMethods {
|
|
|
1351
1210
|
for (let i of strides.keys()) {
|
|
1352
1211
|
strides[i] /= alignment;
|
|
1353
1212
|
}
|
|
1354
|
-
|
|
1355
1213
|
success = (!!1);
|
|
1356
1214
|
let result = Object.create(
|
|
1357
1215
|
PyBuffer.prototype,
|
|
@@ -1379,19 +1237,25 @@ class PyProxyBufferMethods {
|
|
|
1379
1237
|
Module._PyBuffer_Release(view_ptr);
|
|
1380
1238
|
Module._PyMem_Free(view_ptr);
|
|
1381
1239
|
} catch (e) {
|
|
1382
|
-
|
|
1240
|
+
API.fatal_error(e);
|
|
1383
1241
|
}
|
|
1384
1242
|
}
|
|
1385
1243
|
}
|
|
1386
1244
|
}
|
|
1387
1245
|
}
|
|
1388
|
-
|
|
1246
|
+
export type TypedArray =
|
|
1247
|
+
| Int8Array
|
|
1248
|
+
| Uint8Array
|
|
1249
|
+
| Int16Array
|
|
1250
|
+
| Uint16Array
|
|
1251
|
+
| Int32Array
|
|
1252
|
+
| Uint32Array
|
|
1253
|
+
| Uint8ClampedArray
|
|
1254
|
+
| Float32Array
|
|
1255
|
+
| Float64Array;
|
|
1256
|
+
export type PyProxyDict = PyProxyWithGet & PyProxyWithSet & PyProxyWithHas;
|
|
1389
1257
|
/**
|
|
1390
|
-
*
|
|
1391
|
-
*/
|
|
1392
|
-
|
|
1393
|
-
/**
|
|
1394
|
-
* A class to allow access to a Python data buffers from Javascript. These are
|
|
1258
|
+
* A class to allow access to a Python data buffers from JavaScript. These are
|
|
1395
1259
|
* produced by :any:`PyProxy.getBuffer` and cannot be constructed directly.
|
|
1396
1260
|
* When you are done, release it with the :any:`release <PyBuffer.release>`
|
|
1397
1261
|
* method. See
|
|
@@ -1457,93 +1321,82 @@ class PyProxyBufferMethods {
|
|
|
1457
1321
|
* );
|
|
1458
1322
|
*/
|
|
1459
1323
|
export class PyBuffer {
|
|
1324
|
+
/**
|
|
1325
|
+
* The offset of the first entry of the array. For instance if our array
|
|
1326
|
+
* is 3d, then you will find ``array[0,0,0]`` at
|
|
1327
|
+
* ``pybuf.data[pybuf.offset]``
|
|
1328
|
+
*/
|
|
1329
|
+
offset: number;
|
|
1330
|
+
/**
|
|
1331
|
+
* If the data is readonly, you should not modify it. There is no way
|
|
1332
|
+
* for us to enforce this, but it may cause very weird behavior.
|
|
1333
|
+
*/
|
|
1334
|
+
readonly: boolean;
|
|
1335
|
+
/**
|
|
1336
|
+
* The format string for the buffer. See `the Python documentation on
|
|
1337
|
+
* format strings
|
|
1338
|
+
* <https://docs.python.org/3/library/struct.html#format-strings>`_.
|
|
1339
|
+
*/
|
|
1340
|
+
format: string;
|
|
1341
|
+
/**
|
|
1342
|
+
* How large is each entry (in bytes)?
|
|
1343
|
+
*/
|
|
1344
|
+
itemsize: number;
|
|
1345
|
+
/**
|
|
1346
|
+
* The number of dimensions of the buffer. If ``ndim`` is 0, the buffer
|
|
1347
|
+
* represents a single scalar or struct. Otherwise, it represents an
|
|
1348
|
+
* array.
|
|
1349
|
+
*/
|
|
1350
|
+
ndim: number;
|
|
1351
|
+
/**
|
|
1352
|
+
* The total number of bytes the buffer takes up. This is equal to
|
|
1353
|
+
* ``buff.data.byteLength``.
|
|
1354
|
+
*/
|
|
1355
|
+
nbytes: number;
|
|
1356
|
+
/**
|
|
1357
|
+
* The shape of the buffer, that is how long it is in each dimension.
|
|
1358
|
+
* The length will be equal to ``ndim``. For instance, a 2x3x4 array
|
|
1359
|
+
* would have shape ``[2, 3, 4]``.
|
|
1360
|
+
*/
|
|
1361
|
+
shape: number[];
|
|
1362
|
+
/**
|
|
1363
|
+
* An array of of length ``ndim`` giving the number of elements to skip
|
|
1364
|
+
* to get to a new element in each dimension. See the example definition
|
|
1365
|
+
* of a ``multiIndexToIndex`` function above.
|
|
1366
|
+
*/
|
|
1367
|
+
strides: number[];
|
|
1368
|
+
/**
|
|
1369
|
+
* The actual data. A typed array of an appropriate size backed by a
|
|
1370
|
+
* segment of the WASM memory.
|
|
1371
|
+
*
|
|
1372
|
+
* The ``type`` argument of :any:`PyProxy.getBuffer`
|
|
1373
|
+
* determines which sort of ``TypedArray`` this is. By default
|
|
1374
|
+
* :any:`PyProxy.getBuffer` will look at the format string to determine the most
|
|
1375
|
+
* appropriate option.
|
|
1376
|
+
*/
|
|
1377
|
+
data: TypedArray;
|
|
1378
|
+
/**
|
|
1379
|
+
* Is it C contiguous?
|
|
1380
|
+
*/
|
|
1381
|
+
c_contiguous: boolean;
|
|
1382
|
+
/**
|
|
1383
|
+
* Is it Fortran contiguous?
|
|
1384
|
+
*/
|
|
1385
|
+
f_contiguous: boolean;
|
|
1386
|
+
/**
|
|
1387
|
+
* @private
|
|
1388
|
+
*/
|
|
1389
|
+
_released: boolean;
|
|
1390
|
+
/**
|
|
1391
|
+
* @private
|
|
1392
|
+
*/
|
|
1393
|
+
_view_ptr: number;
|
|
1394
|
+
/**
|
|
1395
|
+
* @private
|
|
1396
|
+
*/
|
|
1460
1397
|
constructor() {
|
|
1461
|
-
/**
|
|
1462
|
-
* The offset of the first entry of the array. For instance if our array
|
|
1463
|
-
* is 3d, then you will find ``array[0,0,0]`` at
|
|
1464
|
-
* ``pybuf.data[pybuf.offset]``
|
|
1465
|
-
* @type {number}
|
|
1466
|
-
*/
|
|
1467
|
-
this.offset;
|
|
1468
|
-
|
|
1469
|
-
/**
|
|
1470
|
-
* If the data is readonly, you should not modify it. There is no way
|
|
1471
|
-
* for us to enforce this, but it may cause very weird behavior.
|
|
1472
|
-
* @type {boolean}
|
|
1473
|
-
*/
|
|
1474
|
-
this.readonly;
|
|
1475
|
-
|
|
1476
|
-
/**
|
|
1477
|
-
* The format string for the buffer. See `the Python documentation on
|
|
1478
|
-
* format strings
|
|
1479
|
-
* <https://docs.python.org/3/library/struct.html#format-strings>`_.
|
|
1480
|
-
* @type {string}
|
|
1481
|
-
*/
|
|
1482
|
-
this.format;
|
|
1483
|
-
|
|
1484
|
-
/**
|
|
1485
|
-
* How large is each entry (in bytes)?
|
|
1486
|
-
* @type {number}
|
|
1487
|
-
*/
|
|
1488
|
-
this.itemsize;
|
|
1489
|
-
|
|
1490
|
-
/**
|
|
1491
|
-
* The number of dimensions of the buffer. If ``ndim`` is 0, the buffer
|
|
1492
|
-
* represents a single scalar or struct. Otherwise, it represents an
|
|
1493
|
-
* array.
|
|
1494
|
-
* @type {number}
|
|
1495
|
-
*/
|
|
1496
|
-
this.ndim;
|
|
1497
|
-
|
|
1498
|
-
/**
|
|
1499
|
-
* The total number of bytes the buffer takes up. This is equal to
|
|
1500
|
-
* ``buff.data.byteLength``.
|
|
1501
|
-
* @type {number}
|
|
1502
|
-
*/
|
|
1503
|
-
this.nbytes;
|
|
1504
|
-
|
|
1505
|
-
/**
|
|
1506
|
-
* The shape of the buffer, that is how long it is in each dimension.
|
|
1507
|
-
* The length will be equal to ``ndim``. For instance, a 2x3x4 array
|
|
1508
|
-
* would have shape ``[2, 3, 4]``.
|
|
1509
|
-
* @type {number[]}
|
|
1510
|
-
*/
|
|
1511
|
-
this.shape;
|
|
1512
|
-
|
|
1513
|
-
/**
|
|
1514
|
-
* An array of of length ``ndim`` giving the number of elements to skip
|
|
1515
|
-
* to get to a new element in each dimension. See the example definition
|
|
1516
|
-
* of a ``multiIndexToIndex`` function above.
|
|
1517
|
-
* @type {number[]}
|
|
1518
|
-
*/
|
|
1519
|
-
this.strides;
|
|
1520
|
-
|
|
1521
|
-
/**
|
|
1522
|
-
* The actual data. A typed array of an appropriate size backed by a
|
|
1523
|
-
* segment of the WASM memory.
|
|
1524
|
-
*
|
|
1525
|
-
* The ``type`` argument of :any:`PyProxy.getBuffer`
|
|
1526
|
-
* determines which sort of ``TypedArray`` this is. By default
|
|
1527
|
-
* :any:`PyProxy.getBuffer` will look at the format string to determine the most
|
|
1528
|
-
* appropriate option.
|
|
1529
|
-
* @type {TypedArray}
|
|
1530
|
-
*/
|
|
1531
|
-
this.data;
|
|
1532
|
-
|
|
1533
|
-
/**
|
|
1534
|
-
* Is it C contiguous?
|
|
1535
|
-
* @type {boolean}
|
|
1536
|
-
*/
|
|
1537
|
-
this.c_contiguous;
|
|
1538
|
-
|
|
1539
|
-
/**
|
|
1540
|
-
* Is it Fortran contiguous?
|
|
1541
|
-
* @type {boolean}
|
|
1542
|
-
*/
|
|
1543
|
-
this.f_contiguous;
|
|
1544
1398
|
throw new TypeError("PyBuffer is not a constructor");
|
|
1545
1399
|
}
|
|
1546
|
-
|
|
1547
1400
|
/**
|
|
1548
1401
|
* Release the buffer. This allows the memory to be reclaimed.
|
|
1549
1402
|
*/
|
|
@@ -1556,56 +1409,10 @@ export class PyBuffer {
|
|
|
1556
1409
|
Module._PyBuffer_Release(this._view_ptr);
|
|
1557
1410
|
Module._PyMem_Free(this._view_ptr);
|
|
1558
1411
|
} catch (e) {
|
|
1559
|
-
|
|
1412
|
+
API.fatal_error(e);
|
|
1560
1413
|
}
|
|
1561
1414
|
this._released = (!!1);
|
|
1415
|
+
// @ts-ignore
|
|
1562
1416
|
this.data = null;
|
|
1563
1417
|
}
|
|
1564
1418
|
}
|
|
1565
|
-
|
|
1566
|
-
// A special proxy that we use to wrap pyodide.globals to allow property
|
|
1567
|
-
// access like `pyodide.globals.x`.
|
|
1568
|
-
let globalsPropertyAccessWarned = (!!0);
|
|
1569
|
-
let globalsPropertyAccessWarningMsg =
|
|
1570
|
-
"Access to pyodide.globals via pyodide.globals.key is deprecated and " +
|
|
1571
|
-
"will be removed in version 0.18.0. Use pyodide.globals.get('key'), " +
|
|
1572
|
-
"pyodide.globals.set('key', value), pyodide.globals.delete('key') instead.";
|
|
1573
|
-
let NamespaceProxyHandlers = {
|
|
1574
|
-
has(obj, key) {
|
|
1575
|
-
return Reflect.has(obj, key) || obj.has(key);
|
|
1576
|
-
},
|
|
1577
|
-
get(obj, key) {
|
|
1578
|
-
if (Reflect.has(obj, key)) {
|
|
1579
|
-
return Reflect.get(obj, key);
|
|
1580
|
-
}
|
|
1581
|
-
let result = obj.get(key);
|
|
1582
|
-
if (!globalsPropertyAccessWarned && result !== undefined) {
|
|
1583
|
-
console.warn(globalsPropertyAccessWarningMsg);
|
|
1584
|
-
globalsPropertyAccessWarned = (!!1);
|
|
1585
|
-
}
|
|
1586
|
-
return result;
|
|
1587
|
-
},
|
|
1588
|
-
set(obj, key, value) {
|
|
1589
|
-
if (Reflect.has(obj, key)) {
|
|
1590
|
-
throw new Error(`Cannot set read only field ${key}`);
|
|
1591
|
-
}
|
|
1592
|
-
if (!globalsPropertyAccessWarned) {
|
|
1593
|
-
globalsPropertyAccessWarned = (!!1);
|
|
1594
|
-
console.warn(globalsPropertyAccessWarningMsg);
|
|
1595
|
-
}
|
|
1596
|
-
obj.set(key, value);
|
|
1597
|
-
},
|
|
1598
|
-
ownKeys(obj) {
|
|
1599
|
-
let result = new Set(Reflect.ownKeys(obj));
|
|
1600
|
-
let iter = obj.keys();
|
|
1601
|
-
for (let key of iter) {
|
|
1602
|
-
result.add(key);
|
|
1603
|
-
}
|
|
1604
|
-
iter.destroy();
|
|
1605
|
-
return Array.from(result);
|
|
1606
|
-
},
|
|
1607
|
-
};
|
|
1608
|
-
|
|
1609
|
-
export function wrapNamespace(ns) {
|
|
1610
|
-
return new Proxy(ns, NamespaceProxyHandlers);
|
|
1611
|
-
}
|