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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pythonista-jscore-runtime
3
- Version: 0.0.1
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
- Download [jscore_runtime.py](jscore_runtime.py) from the repository and copy to your site-packages folder.
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
- Alongside also automatically managed (singleton) quick/convenience evaluation and more explicit/multiple virtual machine and contexts instancing for advanced control.
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` to instantiate `wasm_module` instances from a `wasm_runtime` instance needs to be created first. This currently works the same way as the `javascript_runtime`.
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
- Download [jscore_runtime.py](jscore_runtime.py) from the repository and copy to your site-packages folder.
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
- Alongside also automatically managed (singleton) quick/convenience evaluation and more explicit/multiple virtual machine and contexts instancing for advanced control.
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` to instantiate `wasm_module` instances from a `wasm_runtime` instance needs to be created first. This currently works the same way as the `javascript_runtime`.
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.1'
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 = jscore._runtimes.get(runtime_class)
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
- runtime = cls.new_runtime(runtime_class, cls._runtime_vm)
570
- jscore._runtimes[runtime_class] = runtime
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.vm_deallocate(cls._runtime_vm) # if we destroyed the last singleton runtime reference cleanup vm
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 = None
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.context is not None:
1413
- raise Exception("Context already allocated. Do not call allocate/deallocate manually.")
1414
- self.context = jscore.context_allocate(self.runtime.vm)
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
- jscore.context_deallocate(self.context)
1424
- self.context = None
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.context is not None:
1479
+ if self.allocated:
1429
1480
  return
1430
1481
  self.allocate()
1431
1482
 
1432
1483
  def destroy(self):
1433
- if self.context is None:
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
- return self.new_context()
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
- print(jscore._runtimes)
2448
- print(jscore._runtime_vm)
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()