pyodide 0.19.1 → 0.20.1-alpha.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/pyproxy.gen.ts DELETED
@@ -1,1418 +0,0 @@
1
- // This file is generated by applying the C preprocessor to core/pyproxy.ts
2
- // It uses the macros defined in core/pyproxy.c
3
- // Do not edit it directly!
4
-
5
-
6
-
7
-
8
-
9
-
10
-
11
-
12
-
13
-
14
-
15
-
16
-
17
-
18
-
19
-
20
-
21
-
22
-
23
-
24
- /**
25
- * Every public Python entrypoint goes through this file! The main entrypoint is
26
- * the callPyObject method, but of course one can also execute arbitrary code
27
- * via the various __dundermethods__ associated to classes.
28
- *
29
- * Any time we call into wasm, the call should be wrapped in a try catch block.
30
- * This way if a JavaScript error emerges from the wasm, we can escalate it to a
31
- * fatal error.
32
- *
33
- * This is file is preprocessed with -imacros "pyproxy.c". As a result of this,
34
- * any macros available in pyproxy.c are available here. We only need the flags
35
- * macros HAS_LENGTH, etc.
36
- *
37
- * See Makefile recipe for src/js/pyproxy.gen.ts
38
- */
39
- import { Module, API, Hiwire } from "./module.js";
40
- /**
41
- * Is the argument a :any:`PyProxy`?
42
- * @param jsobj Object to test.
43
- * @returns Is ``jsobj`` a :any:`PyProxy`?
44
- */
45
- export function isPyProxy(jsobj: any): jsobj is PyProxy {
46
- return !!jsobj && jsobj.$$ !== undefined && jsobj.$$.type === "PyProxy";
47
- }
48
- API.isPyProxy = isPyProxy;
49
- declare var FinalizationRegistry: any;
50
- declare var globalThis: any;
51
- if (globalThis.FinalizationRegistry) {
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
- }
63
- }
64
- );
65
- // For some unclear reason this code screws up selenium FirefoxDriver. Works
66
- // fine in chrome and when I test it in browser. It seems to be sensitive to
67
- // changes that don't make a difference to the semantics.
68
- // TODO: after 0.18.0, fix selenium issues with this code.
69
- // Module.bufferFinalizationRegistry = new FinalizationRegistry((ptr) => {
70
- // try {
71
- // Module._PyBuffer_Release(ptr);
72
- // Module._PyMem_Free(ptr);
73
- // } catch (e) {
74
- // API.fatal_error(e);
75
- // }
76
- // });
77
- } else {
78
- Module.finalizationRegistry = { register() {}, unregister() {} };
79
- // Module.bufferFinalizationRegistry = finalizationRegistry;
80
- }
81
- let pyproxy_alloc_map = new Map();
82
- Module.pyproxy_alloc_map = pyproxy_alloc_map;
83
- let trace_pyproxy_alloc: (proxy: any) => void;
84
- let trace_pyproxy_dealloc: (proxy: any) => void;
85
- Module.enable_pyproxy_allocation_tracing = function () {
86
- trace_pyproxy_alloc = function (proxy: any) {
87
- pyproxy_alloc_map.set(proxy, Error().stack);
88
- };
89
- trace_pyproxy_dealloc = function (proxy: any) {
90
- pyproxy_alloc_map.delete(proxy);
91
- };
92
- };
93
- Module.disable_pyproxy_allocation_tracing = function () {
94
- trace_pyproxy_alloc = function (proxy: any) {};
95
- trace_pyproxy_dealloc = function (proxy: any) {};
96
- };
97
- Module.disable_pyproxy_allocation_tracing();
98
- type PyProxyCache = { cacheId: number; refcnt: number; leaked?: boolean };
99
- /**
100
- * Create a new PyProxy wrapping ptrobj which is a PyObject*.
101
- *
102
- * The argument cache is only needed to implement the PyProxy.copy API, it
103
- * allows the copy of the PyProxy to share its attribute cache with the original
104
- * version. In all other cases, pyproxy_new should be called with one argument.
105
- *
106
- * In the case that the Python object is callable, PyProxyClass inherits from
107
- * Function so that PyProxy objects can be callable. In that case we MUST expose
108
- * certain properties inherited from Function, but we do our best to remove as
109
- * many as possible.
110
- * @private
111
- */
112
- Module.pyproxy_new = function (ptrobj: number, cache?: PyProxyCache) {
113
- let flags = Module._pyproxy_getflags(ptrobj);
114
- let cls = Module.getPyProxyClass(flags);
115
- // Reflect.construct calls the constructor of Module.PyProxyClass but sets
116
- // the prototype as cls.prototype. This gives us a way to dynamically create
117
- // subclasses of PyProxyClass (as long as we don't need to use the "new
118
- // cls(ptrobj)" syntax).
119
- let target;
120
- if (flags & (1 << 8)) {
121
- // To make a callable proxy, we must call the Function constructor.
122
- // In this case we are effectively subclassing Function.
123
- target = Reflect.construct(Function, [], cls);
124
- // Remove undesirable properties added by Function constructor. Note: we
125
- // can't remove "arguments" or "caller" because they are not configurable
126
- // and not writable
127
- delete target.length;
128
- delete target.name;
129
- // prototype isn't configurable so we can't delete it but it's writable.
130
- target.prototype = undefined;
131
- } else {
132
- target = Object.create(cls.prototype);
133
- }
134
- if (!cache) {
135
- // The cache needs to be accessed primarily from the C function
136
- // _pyproxy_getattr so we make a hiwire id.
137
- let cacheId = Hiwire.new_value(new Map());
138
- cache = { cacheId, refcnt: 0 };
139
- }
140
- cache.refcnt++;
141
- Object.defineProperty(target, "$$", {
142
- value: { ptr: ptrobj, type: "PyProxy", cache },
143
- });
144
- Module._Py_IncRef(ptrobj);
145
- let proxy = new Proxy(target, PyProxyHandlers);
146
- trace_pyproxy_alloc(proxy);
147
- Module.finalizationRegistry.register(proxy, [ptrobj, cache], proxy);
148
- return proxy;
149
- };
150
- function _getPtr(jsobj: any) {
151
- let ptr: number = jsobj.$$.ptr;
152
- if (ptr === 0) {
153
- throw new Error(jsobj.$$.destroyed_msg);
154
- }
155
- return ptr;
156
- }
157
- let pyproxyClassMap = new Map();
158
- /**
159
- * Retrieve the appropriate mixins based on the features requested in flags.
160
- * Used by pyproxy_new. The "flags" variable is produced by the C function
161
- * pyproxy_getflags. Multiple PyProxies with the same set of feature flags
162
- * will share the same prototype, so the memory footprint of each individual
163
- * PyProxy is minimal.
164
- * @private
165
- */
166
- Module.getPyProxyClass = function (flags: number) {
167
- const FLAG_TYPE_PAIRS: [number, any][] = [
168
- [(1 << 0), PyProxyLengthMethods],
169
- [(1 << 1), PyProxyGetItemMethods],
170
- [(1 << 2), PyProxySetItemMethods],
171
- [(1 << 3), PyProxyContainsMethods],
172
- [(1 << 4), PyProxyIterableMethods],
173
- [(1 << 5), PyProxyIteratorMethods],
174
- [(1 << 6), PyProxyAwaitableMethods],
175
- [(1 << 7), PyProxyBufferMethods],
176
- [(1 << 8), PyProxyCallableMethods],
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) {
184
- if (flags & feature_flag) {
185
- Object.assign(
186
- descriptors,
187
- Object.getOwnPropertyDescriptors(methods.prototype)
188
- );
189
- }
190
- }
191
- // Use base constructor (just throws an error if construction is attempted).
192
- descriptors.constructor = Object.getOwnPropertyDescriptor(
193
- PyProxyClass.prototype,
194
- "constructor"
195
- );
196
- Object.assign(
197
- descriptors,
198
- Object.getOwnPropertyDescriptors({ $$flags: flags })
199
- );
200
- let new_proto = Object.create(PyProxyClass.prototype, descriptors);
201
- function NewPyProxyClass() {}
202
- NewPyProxyClass.prototype = new_proto;
203
- pyproxyClassMap.set(flags, NewPyProxyClass);
204
- return NewPyProxyClass;
205
- };
206
- // Static methods
207
- Module.PyProxy_getPtr = _getPtr;
208
- const pyproxy_cache_destroyed_msg =
209
- "This borrowed attribute proxy was automatically destroyed in the " +
210
- "process of destroying the proxy it was borrowed from. Try using the 'copy' method.";
211
- function pyproxy_decref_cache(cache: PyProxyCache) {
212
- if (!cache) {
213
- return;
214
- }
215
- cache.refcnt--;
216
- if (cache.refcnt === 0) {
217
- let cache_map = Hiwire.pop_value(cache.cacheId);
218
- for (let proxy_id of cache_map.values()) {
219
- const cache_entry = Hiwire.pop_value(proxy_id);
220
- if (!cache.leaked) {
221
- Module.pyproxy_destroy(cache_entry, pyproxy_cache_destroyed_msg);
222
- }
223
- }
224
- }
225
- }
226
- Module.pyproxy_destroy = function (proxy: PyProxy, destroyed_msg: string) {
227
- if (proxy.$$.ptr === 0) {
228
- return;
229
- }
230
- let ptrobj = _getPtr(proxy);
231
- Module.finalizationRegistry.unregister(proxy);
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
243
- // to use this proxy. Mark it deleted before decrementing reference count
244
- // just in case!
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
- }
252
- proxy.$$.destroyed_msg = destroyed_msg;
253
- pyproxy_decref_cache(proxy.$$.cache);
254
- try {
255
- Module._Py_DecRef(ptrobj);
256
- trace_pyproxy_dealloc(proxy);
257
- } catch (e) {
258
- API.fatal_error(e);
259
- }
260
- };
261
- // Now a lot of boilerplate to wrap the abstract Object protocol wrappers
262
- // defined in pyproxy.c in JavaScript functions.
263
- Module.callPyObjectKwargs = function (ptrobj: number, ...jsargs: any) {
264
- // We don't do any checking for kwargs, checks are in PyProxy.callKwargs
265
- // which only is used when the keyword arguments come from the user.
266
- let kwargs = jsargs.pop();
267
- let num_pos_args = jsargs.length;
268
- let kwargs_names = Object.keys(kwargs);
269
- let kwargs_values = Object.values(kwargs);
270
- let num_kwargs = kwargs_names.length;
271
- jsargs.push(...kwargs_values);
272
- let idargs = Hiwire.new_value(jsargs);
273
- let idkwnames = Hiwire.new_value(kwargs_names);
274
- let idresult;
275
- try {
276
- idresult = Module.__pyproxy_apply(
277
- ptrobj,
278
- idargs,
279
- num_pos_args,
280
- idkwnames,
281
- num_kwargs
282
- );
283
- } catch (e) {
284
- API.fatal_error(e);
285
- } finally {
286
- Hiwire.decref(idargs);
287
- Hiwire.decref(idkwnames);
288
- }
289
- if (idresult === 0) {
290
- Module._pythonexc2js();
291
- }
292
- return Hiwire.pop_value(idresult);
293
- };
294
- Module.callPyObject = function (ptrobj: number, ...jsargs: any) {
295
- return Module.callPyObjectKwargs(ptrobj, ...jsargs, {});
296
- };
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 */
302
- constructor() {
303
- throw new TypeError("PyProxy is not a constructor");
304
- }
305
- get [Symbol.toStringTag]() {
306
- return "PyProxy";
307
- }
308
- /**
309
- * The name of the type of the object.
310
- *
311
- * Usually the value is ``"module.name"`` but for builtins or
312
- * interpreter-defined types it is just ``"name"``. As pseudocode this is:
313
- *
314
- * .. code-block:: python
315
- *
316
- * ty = type(x)
317
- * if ty.__module__ == 'builtins' or ty.__module__ == "__main__":
318
- * return ty.__name__
319
- * else:
320
- * ty.__module__ + "." + ty.__name__
321
- *
322
- */
323
- get type(): string {
324
- let ptrobj = _getPtr(this);
325
- return Hiwire.pop_value(Module.__pyproxy_type(ptrobj));
326
- }
327
- toString(): string {
328
- let ptrobj = _getPtr(this);
329
- let jsref_repr;
330
- try {
331
- jsref_repr = Module.__pyproxy_repr(ptrobj);
332
- } catch (e) {
333
- API.fatal_error(e);
334
- }
335
- if (jsref_repr === 0) {
336
- Module._pythonexc2js();
337
- }
338
- return Hiwire.pop_value(jsref_repr);
339
- }
340
- /**
341
- * Destroy the ``PyProxy``. This will release the memory. Any further attempt
342
- * to use the object will raise an error.
343
- *
344
- * In a browser supporting `FinalizationRegistry
345
- * <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/FinalizationRegistry>`_
346
- * Pyodide will automatically destroy the ``PyProxy`` when it is garbage
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.
349
- *
350
- * @param destroyed_msg The error message to print if use is attempted after
351
- * destroying. Defaults to "Object has already been destroyed".
352
- */
353
- destroy(destroyed_msg?: string) {
354
- Module.pyproxy_destroy(this, destroyed_msg);
355
- }
356
- /**
357
- * Make a new PyProxy pointing to the same Python object.
358
- * Useful if the PyProxy is destroyed somewhere else.
359
- */
360
- copy(): PyProxy {
361
- let ptrobj = _getPtr(this);
362
- return Module.pyproxy_new(ptrobj, this.$$.cache);
363
- }
364
- /**
365
- * Converts the ``PyProxy`` into a JavaScript object as best as possible. By
366
- * default does a deep conversion, if a shallow conversion is desired, you can
367
- * use ``proxy.toJs({depth : 1})``. See :ref:`Explicit Conversion of PyProxy
368
- * <type-translations-pyproxy-to-js>` for more info.
369
- * @param options
370
- * @return The JavaScript object resulting from the conversion.
371
- */
372
- toJs({
373
- depth = -1,
374
- pyproxies = undefined,
375
- create_pyproxies = (!!1),
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 {
412
- let ptrobj = _getPtr(this);
413
- let idresult;
414
- let proxies_id;
415
- let dict_converter_id = 0;
416
- let default_converter_id = 0;
417
- if (!create_pyproxies) {
418
- proxies_id = 0;
419
- } else if (pyproxies) {
420
- proxies_id = Hiwire.new_value(pyproxies);
421
- } else {
422
- proxies_id = Hiwire.new_value([]);
423
- }
424
- if (dict_converter) {
425
- dict_converter_id = Hiwire.new_value(dict_converter);
426
- }
427
- if (default_converter) {
428
- default_converter_id = Hiwire.new_value(default_converter);
429
- }
430
- try {
431
- idresult = Module._python2js_custom(
432
- ptrobj,
433
- depth,
434
- proxies_id,
435
- dict_converter_id,
436
- default_converter_id
437
- );
438
- } catch (e) {
439
- API.fatal_error(e);
440
- } finally {
441
- Hiwire.decref(proxies_id);
442
- Hiwire.decref(dict_converter_id);
443
- Hiwire.decref(default_converter_id);
444
- }
445
- if (idresult === 0) {
446
- Module._pythonexc2js();
447
- }
448
- return Hiwire.pop_value(idresult);
449
- }
450
- /**
451
- * Check whether the :any:`PyProxy.length` getter is available on this PyProxy. A
452
- * Typescript type guard.
453
- */
454
- supportsLength(): this is PyProxyWithLength {
455
- return !!(this.$$flags & (1 << 0));
456
- }
457
- /**
458
- * Check whether the :any:`PyProxy.get` method is available on this PyProxy. A
459
- * Typescript type guard.
460
- */
461
- supportsGet(): this is PyProxyWithGet {
462
- return !!(this.$$flags & (1 << 1));
463
- }
464
- /**
465
- * Check whether the :any:`PyProxy.set` method is available on this PyProxy. A
466
- * Typescript type guard.
467
- */
468
- supportsSet(): this is PyProxyWithSet {
469
- return !!(this.$$flags & (1 << 2));
470
- }
471
- /**
472
- * Check whether the :any:`PyProxy.has` method is available on this PyProxy. A
473
- * Typescript type guard.
474
- */
475
- supportsHas(): this is PyProxyWithHas {
476
- return !!(this.$$flags & (1 << 3));
477
- }
478
- /**
479
- * Check whether the PyProxy is iterable. A Typescript type guard for
480
- * :any:`PyProxy.[iterator]`.
481
- */
482
- isIterable(): this is PyProxyIterable {
483
- return !!(this.$$flags & ((1 << 4) | (1 << 5)));
484
- }
485
- /**
486
- * Check whether the PyProxy is iterable. A Typescript type guard for
487
- * :any:`PyProxy.next`.
488
- */
489
- isIterator(): this is PyProxyIterator {
490
- return !!(this.$$flags & (1 << 5));
491
- }
492
- /**
493
- * Check whether the PyProxy is awaitable. A Typescript type guard, if this
494
- * function returns true Typescript considers the PyProxy to be a ``Promise``.
495
- */
496
- isAwaitable(): this is PyProxyAwaitable {
497
- return !!(this.$$flags & (1 << 6));
498
- }
499
- /**
500
- * Check whether the PyProxy is a buffer. A Typescript type guard for
501
- * :any:`PyProxy.getBuffer`.
502
- */
503
- isBuffer(): this is PyProxyBuffer {
504
- return !!(this.$$flags & (1 << 7));
505
- }
506
- /**
507
- * Check whether the PyProxy is a Callable. A Typescript type guard, if this
508
- * returns true then Typescript considers the Proxy to be callable of
509
- * signature ``(args... : any[]) => PyProxy | number | bigint | string |
510
- * boolean | undefined``.
511
- */
512
- isCallable(): this is PyProxyCallable {
513
- return !!(this.$$flags & (1 << 8));
514
- }
515
- }
516
- export type PyProxyWithLength = PyProxy & PyProxyLengthMethods;
517
- // Controlled by HAS_LENGTH, appears for any object with __len__ or sq_length
518
- // or mp_length methods
519
- export class PyProxyLengthMethods {
520
- /**
521
- * The length of the object.
522
- *
523
- * Present only if the proxied Python object has a ``__len__`` method.
524
- */
525
- get length(): number {
526
- let ptrobj = _getPtr(this);
527
- let length;
528
- try {
529
- length = Module._PyObject_Size(ptrobj);
530
- } catch (e) {
531
- API.fatal_error(e);
532
- }
533
- if (length === -1) {
534
- Module._pythonexc2js();
535
- }
536
- return length;
537
- }
538
- }
539
- export type PyProxyWithGet = PyProxy & PyProxyGetItemMethods;
540
- // Controlled by HAS_GET, appears for any class with __getitem__,
541
- // mp_subscript, or sq_item methods
542
- export class PyProxyGetItemMethods {
543
- /**
544
- * This translates to the Python code ``obj[key]``.
545
- *
546
- * Present only if the proxied Python object has a ``__getitem__`` method.
547
- *
548
- * @param key The key to look up.
549
- * @returns The corresponding value.
550
- */
551
- get(key: any): any {
552
- let ptrobj = _getPtr(this);
553
- let idkey = Hiwire.new_value(key);
554
- let idresult;
555
- try {
556
- idresult = Module.__pyproxy_getitem(ptrobj, idkey);
557
- } catch (e) {
558
- API.fatal_error(e);
559
- } finally {
560
- Hiwire.decref(idkey);
561
- }
562
- if (idresult === 0) {
563
- if (Module._PyErr_Occurred()) {
564
- Module._pythonexc2js();
565
- } else {
566
- return undefined;
567
- }
568
- }
569
- return Hiwire.pop_value(idresult);
570
- }
571
- }
572
- export type PyProxyWithSet = PyProxy & PyProxySetItemMethods;
573
- // Controlled by HAS_SET, appears for any class with __setitem__, __delitem__,
574
- // mp_ass_subscript, or sq_ass_item.
575
- export class PyProxySetItemMethods {
576
- /**
577
- * This translates to the Python code ``obj[key] = value``.
578
- *
579
- * Present only if the proxied Python object has a ``__setitem__`` method.
580
- *
581
- * @param key The key to set.
582
- * @param value The value to set it to.
583
- */
584
- set(key: any, value: any) {
585
- let ptrobj = _getPtr(this);
586
- let idkey = Hiwire.new_value(key);
587
- let idval = Hiwire.new_value(value);
588
- let errcode;
589
- try {
590
- errcode = Module.__pyproxy_setitem(ptrobj, idkey, idval);
591
- } catch (e) {
592
- API.fatal_error(e);
593
- } finally {
594
- Hiwire.decref(idkey);
595
- Hiwire.decref(idval);
596
- }
597
- if (errcode === -1) {
598
- Module._pythonexc2js();
599
- }
600
- }
601
- /**
602
- * This translates to the Python code ``del obj[key]``.
603
- *
604
- * Present only if the proxied Python object has a ``__delitem__`` method.
605
- *
606
- * @param key The key to delete.
607
- */
608
- delete(key: any) {
609
- let ptrobj = _getPtr(this);
610
- let idkey = Hiwire.new_value(key);
611
- let errcode;
612
- try {
613
- errcode = Module.__pyproxy_delitem(ptrobj, idkey);
614
- } catch (e) {
615
- API.fatal_error(e);
616
- } finally {
617
- Hiwire.decref(idkey);
618
- }
619
- if (errcode === -1) {
620
- Module._pythonexc2js();
621
- }
622
- }
623
- }
624
- export type PyProxyWithHas = PyProxy & PyProxyContainsMethods;
625
- // Controlled by HAS_CONTAINS flag, appears for any class with __contains__ or
626
- // sq_contains
627
- export class PyProxyContainsMethods {
628
- /**
629
- * This translates to the Python code ``key in obj``.
630
- *
631
- * Present only if the proxied Python object has a ``__contains__`` method.
632
- *
633
- * @param key The key to check for.
634
- * @returns Is ``key`` present?
635
- */
636
- has(key: any): boolean {
637
- let ptrobj = _getPtr(this);
638
- let idkey = Hiwire.new_value(key);
639
- let result;
640
- try {
641
- result = Module.__pyproxy_contains(ptrobj, idkey);
642
- } catch (e) {
643
- API.fatal_error(e);
644
- } finally {
645
- Hiwire.decref(idkey);
646
- }
647
- if (result === -1) {
648
- Module._pythonexc2js();
649
- }
650
- return result === 1;
651
- }
652
- }
653
- /**
654
- * A helper for [Symbol.iterator].
655
- *
656
- * Because "it is possible for a generator to be garbage collected without
657
- * ever running its finally block", we take extra care to try to ensure that
658
- * we don't leak the iterator. We register it with the finalizationRegistry,
659
- * but if the finally block is executed, we decref the pointer and unregister.
660
- *
661
- * In order to do this, we create the generator with this inner method,
662
- * register the finalizer, and then return it.
663
- *
664
- * Quote from:
665
- * https://hacks.mozilla.org/2015/07/es6-in-depth-generators-continued/
666
- *
667
- * @private
668
- */
669
- function* iter_helper(iterptr: number, token: {}): Generator<any> {
670
- try {
671
- let item;
672
- while ((item = Module.__pyproxy_iter_next(iterptr))) {
673
- yield Hiwire.pop_value(item);
674
- }
675
- } catch (e) {
676
- API.fatal_error(e);
677
- } finally {
678
- Module.finalizationRegistry.unregister(token);
679
- Module._Py_DecRef(iterptr);
680
- }
681
- if (Module._PyErr_Occurred()) {
682
- Module._pythonexc2js();
683
- }
684
- }
685
- export type PyProxyIterable = PyProxy & PyProxyIterableMethods;
686
- // Controlled by IS_ITERABLE, appears for any object with __iter__ or tp_iter,
687
- // unless they are iterators. See: https://docs.python.org/3/c-api/iter.html
688
- // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols
689
- // This avoids allocating a PyProxy wrapper for the temporary iterator.
690
- export class PyProxyIterableMethods {
691
- /**
692
- * This translates to the Python code ``iter(obj)``. Return an iterator
693
- * associated to the proxy. See the documentation for `Symbol.iterator
694
- * <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/iterator>`_.
695
- *
696
- * Present only if the proxied Python object is iterable (i.e., has an
697
- * ``__iter__`` method).
698
- *
699
- * This will be used implicitly by ``for(let x of proxy){}``.
700
- */
701
- [Symbol.iterator](): Iterator<any, any, any> {
702
- let ptrobj = _getPtr(this);
703
- let token = {};
704
- let iterptr;
705
- try {
706
- iterptr = Module._PyObject_GetIter(ptrobj);
707
- } catch (e) {
708
- API.fatal_error(e);
709
- }
710
- if (iterptr === 0) {
711
- Module._pythonexc2js();
712
- }
713
- let result = iter_helper(iterptr, token);
714
- Module.finalizationRegistry.register(result, [iterptr, undefined], token);
715
- return result;
716
- }
717
- }
718
- export type PyProxyIterator = PyProxy & PyProxyIteratorMethods;
719
- // Controlled by IS_ITERATOR, appears for any object with a __next__ or
720
- // tp_iternext method.
721
- export class PyProxyIteratorMethods {
722
- /** @private */
723
- [Symbol.iterator]() {
724
- return this;
725
- }
726
- /**
727
- * This translates to the Python code ``next(obj)``. Returns the next value of
728
- * the generator. See the documentation for `Generator.prototype.next
729
- * <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Generator/next>`_.
730
- * The argument will be sent to the Python generator.
731
- *
732
- * This will be used implicitly by ``for(let x of proxy){}``.
733
- *
734
- * Present only if the proxied Python object is a generator or iterator (i.e.,
735
- * has a ``send`` or ``__next__`` method).
736
- *
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}``.
743
- */
744
- next(arg: any = undefined): IteratorResult<any, any> {
745
- // Note: arg is optional, if arg is not supplied, it will be undefined
746
- // which gets converted to "Py_None". This is as intended.
747
- let idarg = Hiwire.new_value(arg);
748
- let status;
749
- let done;
750
- let stackTop = Module.stackSave();
751
- let res_ptr = Module.stackAlloc(4);
752
- try {
753
- status = Module.__pyproxyGen_Send(_getPtr(this), idarg, res_ptr);
754
- } catch (e) {
755
- API.fatal_error(e);
756
- } finally {
757
- Hiwire.decref(idarg);
758
- }
759
- let HEAPU32 = Module.HEAPU32;
760
- let idresult = HEAPU32[(res_ptr >> 2) + 0];
761
- Module.stackRestore(stackTop);
762
- if (status === -1) {
763
- Module._pythonexc2js();
764
- }
765
- let value = Hiwire.pop_value(idresult);
766
- done = status === 0;
767
- return { done, value };
768
- }
769
- }
770
- // Another layer of boilerplate. The PyProxyHandlers have some annoying logic
771
- // to deal with straining out the spurious "Function" properties "prototype",
772
- // "arguments", and "length", to deal with correctly satisfying the Proxy
773
- // invariants, and to deal with the mro
774
- function python_hasattr(jsobj: PyProxyClass, jskey: any) {
775
- let ptrobj = _getPtr(jsobj);
776
- let idkey = Hiwire.new_value(jskey);
777
- let result;
778
- try {
779
- result = Module.__pyproxy_hasattr(ptrobj, idkey);
780
- } catch (e) {
781
- API.fatal_error(e);
782
- } finally {
783
- Hiwire.decref(idkey);
784
- }
785
- if (result === -1) {
786
- Module._pythonexc2js();
787
- }
788
- return result !== 0;
789
- }
790
- // Returns a JsRef in order to allow us to differentiate between "not found"
791
- // (in which case we return 0) and "found 'None'" (in which case we return
792
- // Js_undefined).
793
- function python_getattr(jsobj: PyProxyClass, jskey: any) {
794
- let ptrobj = _getPtr(jsobj);
795
- let idkey = Hiwire.new_value(jskey);
796
- let idresult;
797
- let cacheId = jsobj.$$.cache.cacheId;
798
- try {
799
- idresult = Module.__pyproxy_getattr(ptrobj, idkey, cacheId);
800
- } catch (e) {
801
- API.fatal_error(e);
802
- } finally {
803
- Hiwire.decref(idkey);
804
- }
805
- if (idresult === 0) {
806
- if (Module._PyErr_Occurred()) {
807
- Module._pythonexc2js();
808
- }
809
- }
810
- return idresult;
811
- }
812
- function python_setattr(jsobj: PyProxyClass, jskey: any, jsval: any) {
813
- let ptrobj = _getPtr(jsobj);
814
- let idkey = Hiwire.new_value(jskey);
815
- let idval = Hiwire.new_value(jsval);
816
- let errcode;
817
- try {
818
- errcode = Module.__pyproxy_setattr(ptrobj, idkey, idval);
819
- } catch (e) {
820
- API.fatal_error(e);
821
- } finally {
822
- Hiwire.decref(idkey);
823
- Hiwire.decref(idval);
824
- }
825
- if (errcode === -1) {
826
- Module._pythonexc2js();
827
- }
828
- }
829
- function python_delattr(jsobj: PyProxyClass, jskey: any) {
830
- let ptrobj = _getPtr(jsobj);
831
- let idkey = Hiwire.new_value(jskey);
832
- let errcode;
833
- try {
834
- errcode = Module.__pyproxy_delattr(ptrobj, idkey);
835
- } catch (e) {
836
- API.fatal_error(e);
837
- } finally {
838
- Hiwire.decref(idkey);
839
- }
840
- if (errcode === -1) {
841
- Module._pythonexc2js();
842
- }
843
- }
844
- // See explanation of which methods should be defined here and what they do
845
- // here:
846
- // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy
847
- let PyProxyHandlers = {
848
- isExtensible() {
849
- return (!!1);
850
- },
851
- has(jsobj: PyProxyClass, jskey: any) {
852
- // Note: must report "prototype" in proxy when we are callable.
853
- // (We can return the wrong value from "get" handler though.)
854
- let objHasKey = Reflect.has(jsobj, jskey);
855
- if (objHasKey) {
856
- return (!!1);
857
- }
858
- // python_hasattr will crash if given a Symbol.
859
- if (typeof jskey === "symbol") {
860
- return (!!0);
861
- }
862
- if (jskey.startsWith("$")) {
863
- jskey = jskey.slice(1);
864
- }
865
- return python_hasattr(jsobj, jskey);
866
- },
867
- get(jsobj: PyProxyClass, jskey: any) {
868
- // Preference order:
869
- // 1. stuff from JavaScript
870
- // 2. the result of Python getattr
871
- // python_getattr will crash if given a Symbol.
872
- if (jskey in jsobj || typeof jskey === "symbol") {
873
- return Reflect.get(jsobj, jskey);
874
- }
875
- // If keys start with $ remove the $. User can use initial $ to
876
- // unambiguously ask for a key on the Python object.
877
- if (jskey.startsWith("$")) {
878
- jskey = jskey.slice(1);
879
- }
880
- // 2. The result of getattr
881
- let idresult = python_getattr(jsobj, jskey);
882
- if (idresult !== 0) {
883
- return Hiwire.pop_value(idresult);
884
- }
885
- },
886
- set(jsobj: PyProxyClass, jskey: any, jsval: any) {
887
- let descr = Object.getOwnPropertyDescriptor(jsobj, jskey);
888
- if (descr && !descr.writable) {
889
- throw new TypeError(`Cannot set read only field '${jskey}'`);
890
- }
891
- // python_setattr will crash if given a Symbol.
892
- if (typeof jskey === "symbol") {
893
- return Reflect.set(jsobj, jskey, jsval);
894
- }
895
- if (jskey.startsWith("$")) {
896
- jskey = jskey.slice(1);
897
- }
898
- python_setattr(jsobj, jskey, jsval);
899
- return (!!1);
900
- },
901
- deleteProperty(jsobj: PyProxyClass, jskey: any): boolean {
902
- let descr = Object.getOwnPropertyDescriptor(jsobj, jskey);
903
- if (descr && !descr.writable) {
904
- throw new TypeError(`Cannot delete read only field '${jskey}'`);
905
- }
906
- if (typeof jskey === "symbol") {
907
- return Reflect.deleteProperty(jsobj, jskey);
908
- }
909
- if (jskey.startsWith("$")) {
910
- jskey = jskey.slice(1);
911
- }
912
- python_delattr(jsobj, jskey);
913
- // Must return "false" if "jskey" is a nonconfigurable own property.
914
- // Otherwise JavaScript will throw a TypeError.
915
- return !descr || !!descr.configurable;
916
- },
917
- ownKeys(jsobj: PyProxyClass) {
918
- let ptrobj = _getPtr(jsobj);
919
- let idresult;
920
- try {
921
- idresult = Module.__pyproxy_ownKeys(ptrobj);
922
- } catch (e) {
923
- API.fatal_error(e);
924
- }
925
- if (idresult === 0) {
926
- Module._pythonexc2js();
927
- }
928
- let result = Hiwire.pop_value(idresult);
929
- result.push(...Reflect.ownKeys(jsobj));
930
- return result;
931
- },
932
- apply(jsobj: PyProxyClass & Function, jsthis: any, jsargs: any) {
933
- return jsobj.apply(jsthis, jsargs);
934
- },
935
- };
936
- export type PyProxyAwaitable = PyProxy & Promise<any>;
937
- /**
938
- * The Promise / JavaScript awaitable API.
939
- * @private
940
- */
941
- export class PyProxyAwaitableMethods {
942
- $$: any;
943
- /**
944
- * This wraps __pyproxy_ensure_future and makes a function that converts a
945
- * Python awaitable to a promise, scheduling the awaitable on the Python
946
- * event loop if necessary.
947
- * @private
948
- */
949
- _ensure_future(): Promise<any> {
950
- if (this.$$.promise) {
951
- return this.$$.promise;
952
- }
953
- let ptrobj = _getPtr(this);
954
- let resolveHandle;
955
- let rejectHandle;
956
- let promise = new Promise((resolve, reject) => {
957
- resolveHandle = resolve;
958
- rejectHandle = reject;
959
- });
960
- let resolve_handle_id = Hiwire.new_value(resolveHandle);
961
- let reject_handle_id = Hiwire.new_value(rejectHandle);
962
- let errcode;
963
- try {
964
- errcode = Module.__pyproxy_ensure_future(
965
- ptrobj,
966
- resolve_handle_id,
967
- reject_handle_id
968
- );
969
- } catch (e) {
970
- API.fatal_error(e);
971
- } finally {
972
- Hiwire.decref(reject_handle_id);
973
- Hiwire.decref(resolve_handle_id);
974
- }
975
- if (errcode === -1) {
976
- Module._pythonexc2js();
977
- }
978
- this.$$.promise = promise;
979
- // @ts-ignore
980
- this.destroy();
981
- return promise;
982
- }
983
- /**
984
- * Runs ``asyncio.ensure_future(awaitable)``, executes
985
- * ``onFulfilled(result)`` when the ``Future`` resolves successfully,
986
- * executes ``onRejected(error)`` when the ``Future`` fails. Will be used
987
- * implicitly by ``await obj``.
988
- *
989
- * See the documentation for
990
- * `Promise.then
991
- * <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/then>`_
992
- *
993
- * Present only if the proxied Python object is `awaitable
994
- * <https://docs.python.org/3/library/asyncio-task.html?highlight=awaitable#awaitables>`_.
995
- *
996
- * @param onFulfilled A handler called with the result as an
997
- * argument if the awaitable succeeds.
998
- * @param onRejected A handler called with the error as an
999
- * argument if the awaitable fails.
1000
- * @returns The resulting Promise.
1001
- */
1002
- then(
1003
- onFulfilled: (value: any) => any,
1004
- onRejected: (reason: any) => any
1005
- ): Promise<any> {
1006
- let promise = this._ensure_future();
1007
- return promise.then(onFulfilled, onRejected);
1008
- }
1009
- /**
1010
- * Runs ``asyncio.ensure_future(awaitable)`` and executes
1011
- * ``onRejected(error)`` if the future fails.
1012
- *
1013
- * See the documentation for
1014
- * `Promise.catch
1015
- * <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/catch>`_.
1016
- *
1017
- * Present only if the proxied Python object is `awaitable
1018
- * <https://docs.python.org/3/library/asyncio-task.html?highlight=awaitable#awaitables>`_.
1019
- *
1020
- * @param onRejected A handler called with the error as an
1021
- * argument if the awaitable fails.
1022
- * @returns The resulting Promise.
1023
- */
1024
- catch(onRejected: (reason: any) => any) {
1025
- let promise = this._ensure_future();
1026
- return promise.catch(onRejected);
1027
- }
1028
- /**
1029
- * Runs ``asyncio.ensure_future(awaitable)`` and executes
1030
- * ``onFinally(error)`` when the future resolves.
1031
- *
1032
- * See the documentation for
1033
- * `Promise.finally
1034
- * <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/finally>`_.
1035
- *
1036
- * Present only if the proxied Python object is `awaitable
1037
- * <https://docs.python.org/3/library/asyncio-task.html?highlight=awaitable#awaitables>`_.
1038
- *
1039
- *
1040
- * @param onFinally A handler that is called with zero arguments
1041
- * when the awaitable resolves.
1042
- * @returns A Promise that resolves or rejects with the same
1043
- * result as the original Promise, but only after executing the
1044
- * ``onFinally`` handler.
1045
- */
1046
- finally(onFinally: () => void) {
1047
- let promise = this._ensure_future();
1048
- return promise.finally(onFinally);
1049
- }
1050
- }
1051
- export type PyProxyCallable = PyProxy &
1052
- PyProxyCallableMethods &
1053
- ((...args: any[]) => any);
1054
- export class PyProxyCallableMethods {
1055
- apply(jsthis: PyProxyClass, jsargs: any) {
1056
- return Module.callPyObject(_getPtr(this), ...jsargs);
1057
- }
1058
- call(jsthis: PyProxyClass, ...jsargs: any) {
1059
- return Module.callPyObject(_getPtr(this), ...jsargs);
1060
- }
1061
- /**
1062
- * Call the function with key word arguments.
1063
- * The last argument must be an object with the keyword arguments.
1064
- */
1065
- callKwargs(...jsargs: any) {
1066
- if (jsargs.length === 0) {
1067
- throw new TypeError(
1068
- "callKwargs requires at least one argument (the key word argument object)"
1069
- );
1070
- }
1071
- let kwargs = jsargs[jsargs.length - 1];
1072
- if (
1073
- kwargs.constructor !== undefined &&
1074
- kwargs.constructor.name !== "Object"
1075
- ) {
1076
- throw new TypeError("kwargs argument is not an object");
1077
- }
1078
- return Module.callPyObjectKwargs(_getPtr(this), ...jsargs);
1079
- }
1080
- }
1081
- // @ts-ignore
1082
- PyProxyCallableMethods.prototype.prototype = Function.prototype;
1083
- // @ts-ignore
1084
- let type_to_array_map: Map<string, any> = new Map([
1085
- ["i8", Int8Array],
1086
- ["u8", Uint8Array],
1087
- ["u8clamped", Uint8ClampedArray],
1088
- ["i16", Int16Array],
1089
- ["u16", Uint16Array],
1090
- ["i32", Int32Array],
1091
- ["u32", Uint32Array],
1092
- ["i32", Int32Array],
1093
- ["u32", Uint32Array],
1094
- // if these aren't available, will be globalThis.BigInt64Array will be
1095
- // undefined rather than raising a ReferenceError.
1096
- ["i64", globalThis.BigInt64Array],
1097
- ["u64", globalThis.BigUint64Array],
1098
- ["f32", Float32Array],
1099
- ["f64", Float64Array],
1100
- ["dataview", DataView],
1101
- ]);
1102
- export type PyProxyBuffer = PyProxy & PyProxyBufferMethods;
1103
- export class PyProxyBufferMethods {
1104
- /**
1105
- * Get a view of the buffer data which is usable from JavaScript. No copy is
1106
- * ever performed.
1107
- *
1108
- * Present only if the proxied Python object supports the `Python Buffer
1109
- * Protocol <https://docs.python.org/3/c-api/buffer.html>`_.
1110
- *
1111
- * We do not support suboffsets, if the buffer requires suboffsets we will
1112
- * throw an error. JavaScript nd array libraries can't handle suboffsets
1113
- * anyways. In this case, you should use the :any:`toJs` api or copy the
1114
- * buffer to one that doesn't use suboffets (using e.g.,
1115
- * `numpy.ascontiguousarray
1116
- * <https://numpy.org/doc/stable/reference/generated/numpy.ascontiguousarray.html>`_).
1117
- *
1118
- * If the buffer stores big endian data or half floats, this function will
1119
- * fail without an explicit type argument. For big endian data you can use
1120
- * ``toJs``. `DataViews
1121
- * <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView>`_
1122
- * have support for big endian data, so you might want to pass
1123
- * ``'dataview'`` as the type argument in that case.
1124
- *
1125
- * @param type The type of the :any:`PyBuffer.data <pyodide.PyBuffer.data>` field in the
1126
- * output. Should be one of: ``"i8"``, ``"u8"``, ``"u8clamped"``, ``"i16"``,
1127
- * ``"u16"``, ``"i32"``, ``"u32"``, ``"i32"``, ``"u32"``, ``"i64"``,
1128
- * ``"u64"``, ``"f32"``, ``"f64``, or ``"dataview"``. This argument is
1129
- * optional, if absent ``getBuffer`` will try to determine the appropriate
1130
- * output type based on the buffer `format string
1131
- * <https://docs.python.org/3/library/struct.html#format-strings>`_.
1132
- * @returns :any:`PyBuffer <pyodide.PyBuffer>`
1133
- */
1134
- getBuffer(type?: string): PyBuffer {
1135
- let ArrayType = undefined;
1136
- if (type) {
1137
- ArrayType = type_to_array_map.get(type);
1138
- if (ArrayType === undefined) {
1139
- throw new Error(`Unknown type ${type}`);
1140
- }
1141
- }
1142
- let HEAPU32 = Module.HEAPU32;
1143
- let orig_stack_ptr = Module.stackSave();
1144
- let buffer_struct_ptr = Module.stackAlloc(
1145
- HEAPU32[(Module._buffer_struct_size >> 2) + 0]
1146
- );
1147
- let this_ptr = _getPtr(this);
1148
- let errcode;
1149
- try {
1150
- errcode = Module.__pyproxy_get_buffer(buffer_struct_ptr, this_ptr);
1151
- } catch (e) {
1152
- API.fatal_error(e);
1153
- }
1154
- if (errcode === -1) {
1155
- Module._pythonexc2js();
1156
- }
1157
- // This has to match the fields in buffer_struct
1158
- let startByteOffset = HEAPU32[(buffer_struct_ptr >> 2) + 0];
1159
- let minByteOffset = HEAPU32[(buffer_struct_ptr >> 2) + 1];
1160
- let maxByteOffset = HEAPU32[(buffer_struct_ptr >> 2) + 2];
1161
- let readonly = !!HEAPU32[(buffer_struct_ptr >> 2) + 3];
1162
- let format_ptr = HEAPU32[(buffer_struct_ptr >> 2) + 4];
1163
- let itemsize = HEAPU32[(buffer_struct_ptr >> 2) + 5];
1164
- let shape = Hiwire.pop_value(HEAPU32[(buffer_struct_ptr >> 2) + 6]);
1165
- let strides = Hiwire.pop_value(HEAPU32[(buffer_struct_ptr >> 2) + 7]);
1166
- let view_ptr = HEAPU32[(buffer_struct_ptr >> 2) + 8];
1167
- let c_contiguous = !!HEAPU32[(buffer_struct_ptr >> 2) + 9];
1168
- let f_contiguous = !!HEAPU32[(buffer_struct_ptr >> 2) + 10];
1169
- let format = Module.UTF8ToString(format_ptr);
1170
- Module.stackRestore(orig_stack_ptr);
1171
- let success = (!!0);
1172
- try {
1173
- let bigEndian = (!!0);
1174
- if (ArrayType === undefined) {
1175
- [ArrayType, bigEndian] = Module.processBufferFormatString(
1176
- format,
1177
- " In this case, you can pass an explicit type argument."
1178
- );
1179
- }
1180
- let alignment = parseInt(ArrayType.name.replace(/[^0-9]/g, "")) / 8 || 1;
1181
- if (bigEndian && alignment > 1) {
1182
- throw new Error(
1183
- "Javascript has no native support for big endian buffers. " +
1184
- "In this case, you can pass an explicit type argument. " +
1185
- "For instance, `getBuffer('dataview')` will return a `DataView`" +
1186
- "which has native support for reading big endian data. " +
1187
- "Alternatively, toJs will automatically convert the buffer " +
1188
- "to little endian."
1189
- );
1190
- }
1191
- let numBytes = maxByteOffset - minByteOffset;
1192
- if (
1193
- numBytes !== 0 &&
1194
- (startByteOffset % alignment !== 0 ||
1195
- minByteOffset % alignment !== 0 ||
1196
- maxByteOffset % alignment !== 0)
1197
- ) {
1198
- throw new Error(
1199
- `Buffer does not have valid alignment for a ${ArrayType.name}`
1200
- );
1201
- }
1202
- let numEntries = numBytes / alignment;
1203
- let offset = (startByteOffset - minByteOffset) / alignment;
1204
- let data;
1205
- if (numBytes === 0) {
1206
- data = new ArrayType();
1207
- } else {
1208
- data = new ArrayType(HEAPU32.buffer, minByteOffset, numEntries);
1209
- }
1210
- for (let i of strides.keys()) {
1211
- strides[i] /= alignment;
1212
- }
1213
- success = (!!1);
1214
- let result = Object.create(
1215
- PyBuffer.prototype,
1216
- Object.getOwnPropertyDescriptors({
1217
- offset,
1218
- readonly,
1219
- format,
1220
- itemsize,
1221
- ndim: shape.length,
1222
- nbytes: numBytes,
1223
- shape,
1224
- strides,
1225
- data,
1226
- c_contiguous,
1227
- f_contiguous,
1228
- _view_ptr: view_ptr,
1229
- _released: (!!0),
1230
- })
1231
- );
1232
- // Module.bufferFinalizationRegistry.register(result, view_ptr, result);
1233
- return result;
1234
- } finally {
1235
- if (!success) {
1236
- try {
1237
- Module._PyBuffer_Release(view_ptr);
1238
- Module._PyMem_Free(view_ptr);
1239
- } catch (e) {
1240
- API.fatal_error(e);
1241
- }
1242
- }
1243
- }
1244
- }
1245
- }
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;
1257
- /**
1258
- * A class to allow access to a Python data buffers from JavaScript. These are
1259
- * produced by :any:`PyProxy.getBuffer` and cannot be constructed directly.
1260
- * When you are done, release it with the :any:`release <PyBuffer.release>`
1261
- * method. See
1262
- * `Python buffer protocol documentation
1263
- * <https://docs.python.org/3/c-api/buffer.html>`_ for more information.
1264
- *
1265
- * To find the element ``x[a_1, ..., a_n]``, you could use the following code:
1266
- *
1267
- * .. code-block:: js
1268
- *
1269
- * function multiIndexToIndex(pybuff, multiIndex){
1270
- * if(multindex.length !==pybuff.ndim){
1271
- * throw new Error("Wrong length index");
1272
- * }
1273
- * let idx = pybuff.offset;
1274
- * for(let i = 0; i < pybuff.ndim; i++){
1275
- * if(multiIndex[i] < 0){
1276
- * multiIndex[i] = pybuff.shape[i] - multiIndex[i];
1277
- * }
1278
- * if(multiIndex[i] < 0 || multiIndex[i] >= pybuff.shape[i]){
1279
- * throw new Error("Index out of range");
1280
- * }
1281
- * idx += multiIndex[i] * pybuff.stride[i];
1282
- * }
1283
- * return idx;
1284
- * }
1285
- * console.log("entry is", pybuff.data[multiIndexToIndex(pybuff, [2, 0, -1])]);
1286
- *
1287
- * .. admonition:: Contiguity
1288
- * :class: warning
1289
- *
1290
- * If the buffer is not contiguous, the ``data`` TypedArray will contain
1291
- * data that is not part of the buffer. Modifying this data may lead to
1292
- * undefined behavior.
1293
- *
1294
- * .. admonition:: Readonly buffers
1295
- * :class: warning
1296
- *
1297
- * If ``buffer.readonly`` is ``true``, you should not modify the buffer.
1298
- * Modifying a readonly buffer may lead to undefined behavior.
1299
- *
1300
- * .. admonition:: Converting between TypedArray types
1301
- * :class: warning
1302
- *
1303
- * The following naive code to change the type of a typed array does not
1304
- * work:
1305
- *
1306
- * .. code-block:: js
1307
- *
1308
- * // Incorrectly convert a TypedArray.
1309
- * // Produces a Uint16Array that points to the entire WASM memory!
1310
- * let myarray = new Uint16Array(buffer.data.buffer);
1311
- *
1312
- * Instead, if you want to convert the output TypedArray, you need to say:
1313
- *
1314
- * .. code-block:: js
1315
- *
1316
- * // Correctly convert a TypedArray.
1317
- * let myarray = new Uint16Array(
1318
- * buffer.data.buffer,
1319
- * buffer.data.byteOffset,
1320
- * buffer.data.byteLength
1321
- * );
1322
- */
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
- */
1397
- constructor() {
1398
- throw new TypeError("PyBuffer is not a constructor");
1399
- }
1400
- /**
1401
- * Release the buffer. This allows the memory to be reclaimed.
1402
- */
1403
- release() {
1404
- if (this._released) {
1405
- return;
1406
- }
1407
- // Module.bufferFinalizationRegistry.unregister(this);
1408
- try {
1409
- Module._PyBuffer_Release(this._view_ptr);
1410
- Module._PyMem_Free(this._view_ptr);
1411
- } catch (e) {
1412
- API.fatal_error(e);
1413
- }
1414
- this._released = (!!1);
1415
- // @ts-ignore
1416
- this.data = null;
1417
- }
1418
- }