pythonista-jscore-runtime 0.0.1__tar.gz → 0.0.2__tar.gz
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.
- {pythonista_jscore_runtime-0.0.1 → pythonista_jscore_runtime-0.0.2}/PKG-INFO +8 -10
- {pythonista_jscore_runtime-0.0.1 → pythonista_jscore_runtime-0.0.2}/README.md +7 -9
- {pythonista_jscore_runtime-0.0.1 → pythonista_jscore_runtime-0.0.2}/jscore_runtime.py +103 -41
- {pythonista_jscore_runtime-0.0.1 → pythonista_jscore_runtime-0.0.2}/LICENSE +0 -0
- {pythonista_jscore_runtime-0.0.1 → pythonista_jscore_runtime-0.0.2}/pyproject.toml +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pythonista-jscore-runtime
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.2
|
|
4
4
|
Summary: Pythonista JSCore Runtime Framework - Execute JavaScript and WebAssembly with seamless interop support natively in Pythonista 3.
|
|
5
5
|
Author-email: M4nw3l <63550247+M4nw3l@users.noreply.github.com>
|
|
6
6
|
Requires-Python: >= 3.10
|
|
@@ -43,20 +43,18 @@ with (jscore.runtime() as runtime, runtime.context() as context):
|
|
|
43
43
|
|
|
44
44
|
## Installation
|
|
45
45
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
<!--
|
|
49
|
-
Install with pip via StaSh
|
|
46
|
+
Install with pip using [StaSh](https://github.com/ywangd/stash).
|
|
50
47
|
|
|
51
48
|
```bash
|
|
52
49
|
pip install pythonista-jscore-runtime
|
|
53
50
|
```
|
|
54
|
-
|
|
51
|
+
|
|
52
|
+
Or download [jscore_runtime.py](jscore_runtime.py) from the repository and copy to your site-packages folder.
|
|
55
53
|
|
|
56
54
|
## Usage
|
|
57
55
|
### Javascript Runtime
|
|
58
56
|
JSCore Runtime supports both the context management and explicit create/destroy usage paradigms.
|
|
59
|
-
|
|
57
|
+
It provides singletons for convenience evaluation and while also allows more explicit management of multiple virtual machines and contexts with its class model.
|
|
60
58
|
|
|
61
59
|
A runtime singleton can be obtained from the `jscore` static class.
|
|
62
60
|
```python
|
|
@@ -142,7 +140,7 @@ context.js.my_function() # returns 1234
|
|
|
142
140
|
The `wasm_runtime` class, and its associated `wasm_context` and `wasm_module` classes allow WebAssembly modules to be loaded
|
|
143
141
|
with files and byte arrays from Python. They efficiently load WebAssembly modules via a direct buffer copy of an NSData objects bytes into a Uint8Arrays backing store in JavaScriptCore. Allowing a module and its instance to then instantiated, and its exports are bridged directly to Python. Interop with JavaScriptCore allows WebAssembly functions to be mapped and called as any other normal Python callable function. WebAssembly methods are exposed from JavaScriptCore as `function() { [Native Code] }` bodied functions. The performance should be close to excuting code natively but is still being interpreted by JavaScriptCore. It is also likely that JavaScriptCore's WebAssembly runtime may be subject to some restrictions imposed by Apple's general security policies.
|
|
144
142
|
|
|
145
|
-
To create a `wasm_context`
|
|
143
|
+
To create a `wasm_context` a `wasm_runtime` instance needs to be created first. This currently works the same way as the `javascript_runtime`.
|
|
146
144
|
|
|
147
145
|
A singleton runtime instance, with a lifetime of the program, may be obtained from the `jscore.runtime` accessor.
|
|
148
146
|
```python
|
|
@@ -161,7 +159,7 @@ A `wasm_context` may be obtained from runtime instance:
|
|
|
161
159
|
context = runtime.context()
|
|
162
160
|
```
|
|
163
161
|
|
|
164
|
-
Although a `wasm_context` may execute JavaScript and vice versa, `wasm_runtime` and `wasm_context` are designed to integrate Python with WebAssembly as a first class runtime, without need for any JavaScript by default, to load and call WebAssembly modules from Python.
|
|
162
|
+
Although a `wasm_context` may execute JavaScript and vice versa, `wasm_runtime` and `wasm_context` are designed to integrate Python with WebAssembly as a first class runtime, without need for any JavaScript by default, to load and call WebAssembly modules from Python. A `wasm_context` therefore focuses on loading `wasm_module` instances.
|
|
165
163
|
|
|
166
164
|
WebAssembly modules can be loaded from files or as raw bytes with the `wasm_module` class and `wasm_context.load_module` function.
|
|
167
165
|
|
|
@@ -232,7 +230,7 @@ function _jscore_wasm_load(name, wasm_bin, namespace){
|
|
|
232
230
|
Calling `wasm_context.load_module` will call this function to create `WebAssembly.Module` and `WebAssebly.Instance` instances in JavaScript with a WebAssembly binary passed as an `Uint8Array` typed array instance and an imports namespace.
|
|
233
231
|
|
|
234
232
|
#### Example: Loading and calling Mozilla's simple.wasm
|
|
235
|
-
An end to end example of loading and using a WebAssembly from Pythonista can be demonstrated by replicating [Mozilla's Loading Wasm Modules in Javascript](https://developer.mozilla.org/en-US/docs/WebAssembly/Guides/Using_the_JavaScript_API#loading_wasm_modules_in_javascript) example.
|
|
233
|
+
An end to end example of loading and using a WebAssembly module from Pythonista can be demonstrated by replicating [Mozilla's Loading Wasm Modules in Javascript](https://developer.mozilla.org/en-US/docs/WebAssembly/Guides/Using_the_JavaScript_API#loading_wasm_modules_in_javascript) example.
|
|
236
234
|
|
|
237
235
|
- Firstly, download the [simple.wasm](https://raw.githubusercontent.com/mdn/webassembly-examples/master/js-api-examples/simple.wasm) module from the page.
|
|
238
236
|
- After downloading simple.wasm, the next step is to copy this into Pythonista. To do this, navigate to the simple.wasm file in your Files app, then select the file and open the sharing sheet, then tap "Run Pythonista Script" and then choose the "Import File" option.
|
|
@@ -26,20 +26,18 @@ with (jscore.runtime() as runtime, runtime.context() as context):
|
|
|
26
26
|
|
|
27
27
|
## Installation
|
|
28
28
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
<!--
|
|
32
|
-
Install with pip via StaSh
|
|
29
|
+
Install with pip using [StaSh](https://github.com/ywangd/stash).
|
|
33
30
|
|
|
34
31
|
```bash
|
|
35
32
|
pip install pythonista-jscore-runtime
|
|
36
33
|
```
|
|
37
|
-
|
|
34
|
+
|
|
35
|
+
Or download [jscore_runtime.py](jscore_runtime.py) from the repository and copy to your site-packages folder.
|
|
38
36
|
|
|
39
37
|
## Usage
|
|
40
38
|
### Javascript Runtime
|
|
41
39
|
JSCore Runtime supports both the context management and explicit create/destroy usage paradigms.
|
|
42
|
-
|
|
40
|
+
It provides singletons for convenience evaluation and while also allows more explicit management of multiple virtual machines and contexts with its class model.
|
|
43
41
|
|
|
44
42
|
A runtime singleton can be obtained from the `jscore` static class.
|
|
45
43
|
```python
|
|
@@ -125,7 +123,7 @@ context.js.my_function() # returns 1234
|
|
|
125
123
|
The `wasm_runtime` class, and its associated `wasm_context` and `wasm_module` classes allow WebAssembly modules to be loaded
|
|
126
124
|
with files and byte arrays from Python. They efficiently load WebAssembly modules via a direct buffer copy of an NSData objects bytes into a Uint8Arrays backing store in JavaScriptCore. Allowing a module and its instance to then instantiated, and its exports are bridged directly to Python. Interop with JavaScriptCore allows WebAssembly functions to be mapped and called as any other normal Python callable function. WebAssembly methods are exposed from JavaScriptCore as `function() { [Native Code] }` bodied functions. The performance should be close to excuting code natively but is still being interpreted by JavaScriptCore. It is also likely that JavaScriptCore's WebAssembly runtime may be subject to some restrictions imposed by Apple's general security policies.
|
|
127
125
|
|
|
128
|
-
To create a `wasm_context`
|
|
126
|
+
To create a `wasm_context` a `wasm_runtime` instance needs to be created first. This currently works the same way as the `javascript_runtime`.
|
|
129
127
|
|
|
130
128
|
A singleton runtime instance, with a lifetime of the program, may be obtained from the `jscore.runtime` accessor.
|
|
131
129
|
```python
|
|
@@ -144,7 +142,7 @@ A `wasm_context` may be obtained from runtime instance:
|
|
|
144
142
|
context = runtime.context()
|
|
145
143
|
```
|
|
146
144
|
|
|
147
|
-
Although a `wasm_context` may execute JavaScript and vice versa, `wasm_runtime` and `wasm_context` are designed to integrate Python with WebAssembly as a first class runtime, without need for any JavaScript by default, to load and call WebAssembly modules from Python.
|
|
145
|
+
Although a `wasm_context` may execute JavaScript and vice versa, `wasm_runtime` and `wasm_context` are designed to integrate Python with WebAssembly as a first class runtime, without need for any JavaScript by default, to load and call WebAssembly modules from Python. A `wasm_context` therefore focuses on loading `wasm_module` instances.
|
|
148
146
|
|
|
149
147
|
WebAssembly modules can be loaded from files or as raw bytes with the `wasm_module` class and `wasm_context.load_module` function.
|
|
150
148
|
|
|
@@ -215,7 +213,7 @@ function _jscore_wasm_load(name, wasm_bin, namespace){
|
|
|
215
213
|
Calling `wasm_context.load_module` will call this function to create `WebAssembly.Module` and `WebAssebly.Instance` instances in JavaScript with a WebAssembly binary passed as an `Uint8Array` typed array instance and an imports namespace.
|
|
216
214
|
|
|
217
215
|
#### Example: Loading and calling Mozilla's simple.wasm
|
|
218
|
-
An end to end example of loading and using a WebAssembly from Pythonista can be demonstrated by replicating [Mozilla's Loading Wasm Modules in Javascript](https://developer.mozilla.org/en-US/docs/WebAssembly/Guides/Using_the_JavaScript_API#loading_wasm_modules_in_javascript) example.
|
|
216
|
+
An end to end example of loading and using a WebAssembly module from Pythonista can be demonstrated by replicating [Mozilla's Loading Wasm Modules in Javascript](https://developer.mozilla.org/en-US/docs/WebAssembly/Guides/Using_the_JavaScript_API#loading_wasm_modules_in_javascript) example.
|
|
219
217
|
|
|
220
218
|
- Firstly, download the [simple.wasm](https://raw.githubusercontent.com/mdn/webassembly-examples/master/js-api-examples/simple.wasm) module from the page.
|
|
221
219
|
- After downloading simple.wasm, the next step is to copy this into Pythonista. To do this, navigate to the simple.wasm file in your Files app, then select the file and open the sharing sheet, then tap "Run Pythonista Script" and then choose the "Import File" option.
|
|
@@ -5,7 +5,7 @@ Develop apps with Python, JavaScript and WebAssembly libraries, components and c
|
|
|
5
5
|
https://github.com/M4nw3l/pythonista-jscore-runtime
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
|
-
__version__ = '0.0.
|
|
8
|
+
__version__ = '0.0.2'
|
|
9
9
|
|
|
10
10
|
from ctypes import *
|
|
11
11
|
from ctypes.util import find_library
|
|
@@ -549,7 +549,9 @@ class jscore:
|
|
|
549
549
|
])
|
|
550
550
|
|
|
551
551
|
_runtime_vm = None
|
|
552
|
+
_runtime_context = None
|
|
552
553
|
_runtimes = {}
|
|
554
|
+
_runtimes_contexts = {}
|
|
553
555
|
_runtime_cleanups = []
|
|
554
556
|
@classmethod
|
|
555
557
|
def new_runtime(cls, runtime_class, *args, **kwargs):
|
|
@@ -562,14 +564,58 @@ class jscore:
|
|
|
562
564
|
def runtime(cls, runtime_class = None):
|
|
563
565
|
if runtime_class is None:
|
|
564
566
|
runtime_class = javascript_runtime
|
|
565
|
-
runtime =
|
|
567
|
+
runtime = cls._runtimes.get(runtime_class)
|
|
566
568
|
if runtime is None:
|
|
567
569
|
if cls._runtime_vm is None:
|
|
568
570
|
cls._runtime_vm = cls.vm_allocate()
|
|
569
|
-
|
|
570
|
-
|
|
571
|
+
cls._runtime_context = cls.context_allocate(cls._runtime_vm)
|
|
572
|
+
runtime = cls.new_runtime(runtime_class, cls._runtime_vm, cls._runtime_context)
|
|
573
|
+
cls._runtimes[runtime_class] = runtime
|
|
571
574
|
return runtime
|
|
572
|
-
|
|
575
|
+
|
|
576
|
+
@classmethod
|
|
577
|
+
def _runtimes_cleanup(cls):
|
|
578
|
+
cls.context_deallocate(cls._runtime_context)
|
|
579
|
+
cls.vm_deallocate(cls._runtime_vm) # if we destroyed the last singleton runtime reference cleanup shared context and vm
|
|
580
|
+
for cleanup in cls._runtime_cleanups:
|
|
581
|
+
cleanup()
|
|
582
|
+
# reset everything
|
|
583
|
+
cls._runtimes = {}
|
|
584
|
+
cls._runtime_cleanups = []
|
|
585
|
+
cls._runtimes_contexts = {}
|
|
586
|
+
cls._runtime_vm = None
|
|
587
|
+
cls._runtime_context = None
|
|
588
|
+
|
|
589
|
+
@classmethod
|
|
590
|
+
def context(cls, runtime_class = None):
|
|
591
|
+
if runtime_class is None:
|
|
592
|
+
runtime_class = javascript_runtime
|
|
593
|
+
context = cls._runtimes_contexts.get(runtime_class)
|
|
594
|
+
if context is None:
|
|
595
|
+
runtime = cls.runtime(runtime_class)
|
|
596
|
+
context = runtime.context()
|
|
597
|
+
cls._runtimes_contexts[runtime_class] = context
|
|
598
|
+
return context
|
|
599
|
+
|
|
600
|
+
@classmethod
|
|
601
|
+
def javascript(cls):
|
|
602
|
+
return cls.context(javascript_runtime)
|
|
603
|
+
#aliases
|
|
604
|
+
js = javascript
|
|
605
|
+
|
|
606
|
+
@classmethod
|
|
607
|
+
def webassembly(cls):
|
|
608
|
+
return cls.context(wasm_runtime)
|
|
609
|
+
#aliases
|
|
610
|
+
wasm = webassembly
|
|
611
|
+
|
|
612
|
+
@classmethod
|
|
613
|
+
def destroy(cls):
|
|
614
|
+
for typ,context in dict(cls._runtimes_contexts).items():
|
|
615
|
+
context.destroy()
|
|
616
|
+
for typ,runtime in dict(cls._runtimes).items():
|
|
617
|
+
runtime.destroy()
|
|
618
|
+
|
|
573
619
|
@classmethod
|
|
574
620
|
def vm_allocate(cls):
|
|
575
621
|
vm = jscore.JSVirtualMachine.alloc().init()
|
|
@@ -605,11 +651,7 @@ class jscore:
|
|
|
605
651
|
if runtime is rt: # remove destroyed runtime if its a tracked singleton instance
|
|
606
652
|
del cls._runtimes[key]
|
|
607
653
|
if len(cls._runtimes) == 0:
|
|
608
|
-
cls.
|
|
609
|
-
cls._runtime_vm = None
|
|
610
|
-
for cleanup in cls._runtime_cleanups:
|
|
611
|
-
cleanup()
|
|
612
|
-
cls._runtime_cleanups = []
|
|
654
|
+
cls._runtimes_cleanup()
|
|
613
655
|
|
|
614
656
|
_context_ref_context_lookup = {}
|
|
615
657
|
@classmethod
|
|
@@ -1402,35 +1444,44 @@ class jscore_module_loader:
|
|
|
1402
1444
|
|
|
1403
1445
|
# base runtime framework
|
|
1404
1446
|
class jscore_context:
|
|
1405
|
-
def __init__(self, runtime):
|
|
1447
|
+
def __init__(self, runtime, context = None):
|
|
1406
1448
|
self.runtime = runtime
|
|
1407
|
-
self.context =
|
|
1449
|
+
self.context = context
|
|
1450
|
+
self.context_owner = self.context is None
|
|
1451
|
+
self.allocated = False
|
|
1408
1452
|
self.depth = 0
|
|
1453
|
+
self.accessor = None
|
|
1409
1454
|
self.callbacks = None
|
|
1410
1455
|
|
|
1411
1456
|
def allocate(self):
|
|
1412
|
-
if self.
|
|
1413
|
-
|
|
1414
|
-
|
|
1457
|
+
if self.context_owner:
|
|
1458
|
+
if self.context is not None:
|
|
1459
|
+
raise Exception("Context already allocated. Do not call allocate/deallocate manually.")
|
|
1460
|
+
self.context = jscore.context_allocate(self.runtime.vm)
|
|
1415
1461
|
self.loader = jscore_module_loader(self)
|
|
1462
|
+
self.accessor = javascript_context_accessor(self)
|
|
1416
1463
|
self.callbacks = {}
|
|
1464
|
+
self.allocated = True
|
|
1417
1465
|
|
|
1418
1466
|
def deallocate(self):
|
|
1419
1467
|
if self.context is None:
|
|
1420
1468
|
raise Exception("Context already deallocated. Do not call allocate/deallocate manually.")
|
|
1421
1469
|
self.loader.release()
|
|
1422
1470
|
self.loader = None
|
|
1423
|
-
|
|
1424
|
-
self.
|
|
1471
|
+
self.accessor = None
|
|
1472
|
+
if self.context_owner:
|
|
1473
|
+
jscore.context_deallocate(self.context)
|
|
1474
|
+
self.context = None
|
|
1425
1475
|
self.callbacks = None
|
|
1476
|
+
self.allocated = False
|
|
1426
1477
|
|
|
1427
1478
|
def alloc(self):
|
|
1428
|
-
if self.
|
|
1479
|
+
if self.allocated:
|
|
1429
1480
|
return
|
|
1430
1481
|
self.allocate()
|
|
1431
1482
|
|
|
1432
1483
|
def destroy(self):
|
|
1433
|
-
if self.
|
|
1484
|
+
if not self.allocated:
|
|
1434
1485
|
return
|
|
1435
1486
|
self.deallocate()
|
|
1436
1487
|
|
|
@@ -1512,12 +1563,20 @@ class jscore_context:
|
|
|
1512
1563
|
callback = javascript_callback(func, name)
|
|
1513
1564
|
self.callbacks[key] = callback
|
|
1514
1565
|
return callback
|
|
1566
|
+
|
|
1567
|
+
@property
|
|
1568
|
+
def js(self):
|
|
1569
|
+
self.alloc()
|
|
1570
|
+
return self.accessor
|
|
1515
1571
|
|
|
1516
1572
|
|
|
1517
1573
|
class jscore_runtime:
|
|
1518
|
-
def __init__(self, vm = None):
|
|
1574
|
+
def __init__(self, vm = None, shared_context = None):
|
|
1519
1575
|
self.vm = vm
|
|
1520
1576
|
self.vm_owner = self.vm is None
|
|
1577
|
+
if self.vm_owner and shared_context is not None:
|
|
1578
|
+
raise Exception("A matching vm must be specified with a shared context. Contexts must not be shared from another virtual machine.")
|
|
1579
|
+
self.shared_context = shared_context # never owner when shared_context is not None
|
|
1521
1580
|
self.depth = 0
|
|
1522
1581
|
self.module_paths = {}
|
|
1523
1582
|
self.scripts = []
|
|
@@ -1627,12 +1686,14 @@ class jscore_runtime:
|
|
|
1627
1686
|
self.destroy()
|
|
1628
1687
|
return self
|
|
1629
1688
|
|
|
1630
|
-
def new_context(self):
|
|
1689
|
+
def new_context(self, context):
|
|
1631
1690
|
raise NotImplementedError()
|
|
1632
1691
|
|
|
1633
|
-
def context(self):
|
|
1692
|
+
def context(self, context = None):
|
|
1634
1693
|
self.alloc()
|
|
1635
|
-
|
|
1694
|
+
if context is None:
|
|
1695
|
+
context = self.shared_context
|
|
1696
|
+
return self.new_context(context)
|
|
1636
1697
|
|
|
1637
1698
|
# helper class to determine the minimum set of changes to build a jsvalue in terms of javascript statements
|
|
1638
1699
|
class jsvalue_evaluator:
|
|
@@ -1884,27 +1945,19 @@ class javascript_context_accessor:
|
|
|
1884
1945
|
|
|
1885
1946
|
# javascript
|
|
1886
1947
|
class javascript_context(jscore_context):
|
|
1887
|
-
def __init__(self, runtime):
|
|
1888
|
-
super().__init__(runtime)
|
|
1889
|
-
self.accessor = None
|
|
1948
|
+
def __init__(self, runtime, context = None):
|
|
1949
|
+
super().__init__(runtime, context)
|
|
1890
1950
|
|
|
1891
1951
|
def allocate(self):
|
|
1892
1952
|
super().allocate()
|
|
1893
|
-
self.accessor = javascript_context_accessor(self)
|
|
1894
1953
|
|
|
1895
1954
|
def deallocate(self):
|
|
1896
|
-
self.accessor = None
|
|
1897
1955
|
super().deallocate()
|
|
1898
|
-
|
|
1899
|
-
@property
|
|
1900
|
-
def js(self):
|
|
1901
|
-
self.alloc()
|
|
1902
|
-
return self.accessor
|
|
1903
1956
|
|
|
1904
1957
|
|
|
1905
1958
|
class javascript_runtime(jscore_runtime):
|
|
1906
|
-
def new_context(self):
|
|
1907
|
-
return javascript_context(self)
|
|
1959
|
+
def new_context(self, context):
|
|
1960
|
+
return javascript_context(self, context)
|
|
1908
1961
|
|
|
1909
1962
|
# wasm (WebAssembly)
|
|
1910
1963
|
class wasm_namespace:
|
|
@@ -2092,8 +2145,8 @@ class wasm_module:
|
|
|
2092
2145
|
|
|
2093
2146
|
|
|
2094
2147
|
class wasm_context(jscore_context):
|
|
2095
|
-
def __init__(self, runtime):
|
|
2096
|
-
super().__init__(runtime)
|
|
2148
|
+
def __init__(self, runtime, context = None):
|
|
2149
|
+
super().__init__(runtime, context)
|
|
2097
2150
|
self._modules = {}
|
|
2098
2151
|
self._imports = {}
|
|
2099
2152
|
self._namespace = wasm_namespace(self._imports)
|
|
@@ -2176,8 +2229,8 @@ class wasm_context(jscore_context):
|
|
|
2176
2229
|
|
|
2177
2230
|
|
|
2178
2231
|
class wasm_runtime(jscore_runtime):
|
|
2179
|
-
def new_context(self):
|
|
2180
|
-
return wasm_context(self)
|
|
2232
|
+
def new_context(self, context):
|
|
2233
|
+
return wasm_context(self, context)
|
|
2181
2234
|
|
|
2182
2235
|
|
|
2183
2236
|
if __name__ == '__main__':
|
|
@@ -2444,5 +2497,14 @@ if __name__ == '__main__':
|
|
|
2444
2497
|
|
|
2445
2498
|
context.destroy()
|
|
2446
2499
|
runtime.destroy()
|
|
2447
|
-
|
|
2448
|
-
|
|
2500
|
+
|
|
2501
|
+
js_context = jscore.js()
|
|
2502
|
+
js_context.js.hello = "javascript"
|
|
2503
|
+
|
|
2504
|
+
wasm_context = jscore.wasm()
|
|
2505
|
+
print(wasm_context.js.hello)
|
|
2506
|
+
wasm_context.js.hello = "wasm"
|
|
2507
|
+
|
|
2508
|
+
print(js_context.js.hello)
|
|
2509
|
+
|
|
2510
|
+
jscore.destroy()
|
|
File without changes
|
|
File without changes
|