ipymuvue 0.7.0__py3-none-any.whl
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.
- ipymuvue/.gitignore +4 -0
- ipymuvue/__init__.py +36 -0
- ipymuvue/pyodide/__init__.py +0 -0
- ipymuvue/pyodide/proxies/__init__.py +9 -0
- ipymuvue/pyodide/proxies/array_wrapper.py +93 -0
- ipymuvue/pyodide/proxies/conversion.py +267 -0
- ipymuvue/pyodide/proxies/object_wrapper.py +98 -0
- ipymuvue/pyodide/proxies/proxy_ref.py +53 -0
- ipymuvue/pyodide/types.py +53 -0
- ipymuvue/pyodide/vue.py +414 -0
- ipymuvue/special.py +74 -0
- ipymuvue/version.py +19 -0
- ipymuvue/widgets/__init__.py +20 -0
- ipymuvue/widgets/asynchronous.py +52 -0
- ipymuvue/widgets/references.py +267 -0
- ipymuvue/widgets/vue_widget.py +295 -0
- ipymuvue-0.7.0.data/data/share/jupyter/labextensions/ipymuvue/install.json +5 -0
- ipymuvue-0.7.0.data/data/share/jupyter/labextensions/ipymuvue/package.json +75 -0
- ipymuvue-0.7.0.data/data/share/jupyter/labextensions/ipymuvue/static/122.7a34384551849300a6a9.js +1 -0
- ipymuvue-0.7.0.data/data/share/jupyter/labextensions/ipymuvue/static/451.00a4599f661716908ca8.js +1 -0
- ipymuvue-0.7.0.data/data/share/jupyter/labextensions/ipymuvue/static/824.dd5f2da83d5decb4f7c7.js +1 -0
- ipymuvue-0.7.0.data/data/share/jupyter/labextensions/ipymuvue/static/853.40517be6a27f5ac09d5e.js +1797 -0
- ipymuvue-0.7.0.data/data/share/jupyter/labextensions/ipymuvue/static/853.40517be6a27f5ac09d5e.js.LICENSE.txt +74 -0
- ipymuvue-0.7.0.data/data/share/jupyter/labextensions/ipymuvue/static/remoteEntry.e0c219b7fb90947b62a1.js +1 -0
- ipymuvue-0.7.0.data/data/share/jupyter/labextensions/ipymuvue/static/style.js +4 -0
- ipymuvue-0.7.0.data/data/share/jupyter/labextensions/ipymuvue/static/third-party-licenses.json +64 -0
- ipymuvue-0.7.0.dist-info/METADATA +149 -0
- ipymuvue-0.7.0.dist-info/RECORD +30 -0
- ipymuvue-0.7.0.dist-info/WHEEL +4 -0
- ipymuvue-0.7.0.dist-info/licenses/COPYING +674 -0
ipymuvue/.gitignore
ADDED
ipymuvue/__init__.py
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# ******************************************************************************
|
|
2
|
+
# Copyright (c) 2022 Julian Rüth <julian.rueth@fsfe.org>
|
|
3
|
+
#
|
|
4
|
+
# ipymuvue is free software: you can redistribute it and/or modify
|
|
5
|
+
# it under the terms of the GNU General Public License as published by
|
|
6
|
+
# the Free Software Foundation, either version 3 of the License, or
|
|
7
|
+
# (at your option) any later version.
|
|
8
|
+
#
|
|
9
|
+
# ipymuvue is distributed in the hope that it will be useful,
|
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
12
|
+
# GNU General Public License for more details.
|
|
13
|
+
#
|
|
14
|
+
# You should have received a copy of the GNU General Public License
|
|
15
|
+
# along with ipymuvue. If not, see <https://www.gnu.org/licenses/>.
|
|
16
|
+
# ******************************************************************************
|
|
17
|
+
|
|
18
|
+
from ipymuvue.version import version_info, __version__
|
|
19
|
+
|
|
20
|
+
__all__ = ["version_info", "__version__"]
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def _jupyter_labextension_paths():
|
|
24
|
+
r"""
|
|
25
|
+
Called by JupyterLab to find out which JavaScript assets need to be copied.
|
|
26
|
+
"""
|
|
27
|
+
# The command `jupyter labextension build` creates a package in
|
|
28
|
+
# labextensions/ipymuvue/, see jupyterlab.outputDir in ts/package.json
|
|
29
|
+
# These files are copied to extensions/ipymuvue/ in
|
|
30
|
+
# JupyterLab when this package is pip-installed.
|
|
31
|
+
return [
|
|
32
|
+
{
|
|
33
|
+
"src": "labextensions/ipymuvue",
|
|
34
|
+
"dest": "ipymuvue",
|
|
35
|
+
}
|
|
36
|
+
]
|
|
File without changes
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
r"""
|
|
2
|
+
Wrappers for JavaScript arrays.
|
|
3
|
+
|
|
4
|
+
pyodide wraps JavaScript arrays in a way that is very convenient to
|
|
5
|
+
use from Python. However, we want to wrap the entries of that array
|
|
6
|
+
with our own object wrapper, see
|
|
7
|
+
:module:`ipymuvue.pyodide.proxies.object_wrapper` so we need to
|
|
8
|
+
intercept some of the operations on an array.
|
|
9
|
+
"""
|
|
10
|
+
# ******************************************************************************
|
|
11
|
+
# Copyright (c) 2022 Julian Rüth <julian.rueth@fsfe.org>
|
|
12
|
+
#
|
|
13
|
+
# ipymuvue is free software: you can redistribute it and/or modify
|
|
14
|
+
# it under the terms of the GNU General Public License as published by
|
|
15
|
+
# the Free Software Foundation, either version 3 of the License, or
|
|
16
|
+
# (at your option) any later version.
|
|
17
|
+
#
|
|
18
|
+
# ipymuvue is distributed in the hope that it will be useful,
|
|
19
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
20
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
21
|
+
# GNU General Public License for more details.
|
|
22
|
+
#
|
|
23
|
+
# You should have received a copy of the GNU General Public License
|
|
24
|
+
# along with ipymuvue. If not, see <https://www.gnu.org/licenses/>.
|
|
25
|
+
# ******************************************************************************
|
|
26
|
+
|
|
27
|
+
from collections.abc import MutableSequence
|
|
28
|
+
from ipymuvue.pyodide.proxies import python_compatible, vue_compatible
|
|
29
|
+
from ipymuvue.pyodide.types import is_vue_proxy
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class ArrayWrapper(MutableSequence):
|
|
33
|
+
r"""
|
|
34
|
+
Wraps a JavaScript Array with a Python list interface.
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
def __init__(self, array):
|
|
38
|
+
import js
|
|
39
|
+
|
|
40
|
+
if not js.Array.isArray(array):
|
|
41
|
+
raise TypeError("array must be a JavaScript array")
|
|
42
|
+
|
|
43
|
+
self._array = array
|
|
44
|
+
|
|
45
|
+
def _vue_compatible(self, object):
|
|
46
|
+
return vue_compatible(object)
|
|
47
|
+
|
|
48
|
+
def insert(self, index, object):
|
|
49
|
+
if index < 0:
|
|
50
|
+
index = 0
|
|
51
|
+
if index >= len(self):
|
|
52
|
+
index = len(self)
|
|
53
|
+
self._array.splice(index, 0, self._vue_compatible(object))
|
|
54
|
+
|
|
55
|
+
def __getitem__(self, index):
|
|
56
|
+
return python_compatible(self._array[index])
|
|
57
|
+
|
|
58
|
+
def __setitem__(self, index, value):
|
|
59
|
+
if index < 0:
|
|
60
|
+
index = len(self) + index
|
|
61
|
+
if index < 0:
|
|
62
|
+
raise IndexError("assignment index out of range")
|
|
63
|
+
if index >= len(self):
|
|
64
|
+
raise IndexError("assignment index out of range")
|
|
65
|
+
|
|
66
|
+
self._array[index] = self._vue_compatible(object)
|
|
67
|
+
|
|
68
|
+
def __delitem__(self, index):
|
|
69
|
+
if index < 0:
|
|
70
|
+
index = len(self) + index
|
|
71
|
+
if index < 0:
|
|
72
|
+
raise IndexError("deletion index out of range")
|
|
73
|
+
if index >= len(self):
|
|
74
|
+
raise IndexError("deletion index out of range")
|
|
75
|
+
|
|
76
|
+
self._array.splice(index, 1)
|
|
77
|
+
|
|
78
|
+
def __len__(self):
|
|
79
|
+
return self._array.length
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
class ProxyList(ArrayWrapper):
|
|
83
|
+
r"""
|
|
84
|
+
Wraps a Vue proxy with a Python list interface.
|
|
85
|
+
"""
|
|
86
|
+
|
|
87
|
+
def __init__(self, proxy):
|
|
88
|
+
if not is_vue_proxy(proxy):
|
|
89
|
+
raise TypeError("proxy must be a Vue proxy")
|
|
90
|
+
super().__init__(proxy)
|
|
91
|
+
|
|
92
|
+
def _vue_compatible(self, object):
|
|
93
|
+
return vue_compatible(object, reference=False)
|
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
r"""
|
|
2
|
+
Generic conversion between JavaScript and Python.
|
|
3
|
+
|
|
4
|
+
pyodide wraps JavaScript objects in a PyProxy and Python objects in a
|
|
5
|
+
JSProxy when the cross the boundary between JavaScript and Python and
|
|
6
|
+
vice versa.
|
|
7
|
+
|
|
8
|
+
However, this proxying causes trouble, see
|
|
9
|
+
:module:`ipymuvue.pyodide.vue`. In short, Python programmers expect a
|
|
10
|
+
Python dict interface and Vue gets confused by installing its own
|
|
11
|
+
proxy on top of the JsProxy.
|
|
12
|
+
|
|
13
|
+
Here, we provide some cenversions that seem to work better to enable
|
|
14
|
+
communication between Python and Vue.js JavaScript API.
|
|
15
|
+
"""
|
|
16
|
+
# ******************************************************************************
|
|
17
|
+
# Copyright (c) 2022 Julian Rüth <julian.rueth@fsfe.org>
|
|
18
|
+
#
|
|
19
|
+
# ipymuvue is free software: you can redistribute it and/or modify
|
|
20
|
+
# it under the terms of the GNU General Public License as published by
|
|
21
|
+
# the Free Software Foundation, either version 3 of the License, or
|
|
22
|
+
# (at your option) any later version.
|
|
23
|
+
#
|
|
24
|
+
# ipymuvue is distributed in the hope that it will be useful,
|
|
25
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
26
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
27
|
+
# GNU General Public License for more details.
|
|
28
|
+
#
|
|
29
|
+
# You should have received a copy of the GNU General Public License
|
|
30
|
+
# along with ipymuvue. If not, see <https://www.gnu.org/licenses/>.
|
|
31
|
+
# ******************************************************************************
|
|
32
|
+
|
|
33
|
+
import js
|
|
34
|
+
from contextvars import ContextVar
|
|
35
|
+
from contextlib import contextmanager
|
|
36
|
+
from ipymuvue.pyodide.types import is_vue_ref, is_vue_proxy
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def python_compatible(x):
|
|
40
|
+
r"""
|
|
41
|
+
Wrap ``x`` that came out of a Vue API for use in Python.
|
|
42
|
+
"""
|
|
43
|
+
import pyodide.ffi
|
|
44
|
+
|
|
45
|
+
from ipymuvue.pyodide.proxies.object_wrapper import ObjectWrapper, ProxyDict
|
|
46
|
+
from ipymuvue.pyodide.proxies.array_wrapper import ArrayWrapper, ProxyList
|
|
47
|
+
from ipymuvue.pyodide.proxies.proxy_ref import ProxyRef
|
|
48
|
+
|
|
49
|
+
# Some elementary types are converted automatically by pyodide.
|
|
50
|
+
if type(x) in [int, str, float, bool, type(None)]:
|
|
51
|
+
return x
|
|
52
|
+
|
|
53
|
+
if callable(x):
|
|
54
|
+
assert isinstance(x, pyodide.ffi.JsProxy), (
|
|
55
|
+
"received a function from Vue that is not defined in JavaScript"
|
|
56
|
+
)
|
|
57
|
+
raise NotImplementedError("cannot properly wrap functions from the Vue API yet")
|
|
58
|
+
|
|
59
|
+
if is_vue_ref(x):
|
|
60
|
+
return ProxyRef(x)
|
|
61
|
+
|
|
62
|
+
if js.Array.isArray(x):
|
|
63
|
+
if is_vue_proxy(x):
|
|
64
|
+
return ProxyList(x)
|
|
65
|
+
return ArrayWrapper(x)
|
|
66
|
+
|
|
67
|
+
if x.typeof == "object":
|
|
68
|
+
if is_vue_proxy(x):
|
|
69
|
+
return ProxyDict(x)
|
|
70
|
+
return ObjectWrapper(x)
|
|
71
|
+
|
|
72
|
+
raise Exception(f"not implemented, wrapping a proxy {x.typeof}")
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def vue_compatible(x, reference=True, shallow=False, owner=None):
|
|
76
|
+
r"""
|
|
77
|
+
Return ``x`` as a native JavaScript object that can be safely consumed by
|
|
78
|
+
the Vue API.
|
|
79
|
+
|
|
80
|
+
If ``x`` is a primitive value such as a number or a string, return it
|
|
81
|
+
unchanged.
|
|
82
|
+
|
|
83
|
+
If ``reference`` is ``True``, returns a value that behaves like a reference
|
|
84
|
+
of ``x``. Changes to ``x`` are reflected in the returned
|
|
85
|
+
value and vice versa. If ``shallow`` is not set, verify that values nested
|
|
86
|
+
in ``x`` are also compatible with Vue's JavaScript API. (That last part is
|
|
87
|
+
sometimes not actually verified yet.)
|
|
88
|
+
|
|
89
|
+
If ``reference`` is ``False``, return clone of ``x``; the clone is deep
|
|
90
|
+
unless ``shallow`` is set.
|
|
91
|
+
|
|
92
|
+
If ``reference`` is ``None``, return whatever is chepest to accomplish.
|
|
93
|
+
|
|
94
|
+
If a proxy of a callable gets created, its lifetime is tied to ``owner``,
|
|
95
|
+
see :meth:`owner` for details.
|
|
96
|
+
"""
|
|
97
|
+
import pyodide.ffi
|
|
98
|
+
|
|
99
|
+
from ipymuvue.pyodide.proxies.object_wrapper import ObjectWrapper
|
|
100
|
+
from ipymuvue.pyodide.proxies.array_wrapper import ArrayWrapper
|
|
101
|
+
from ipymuvue.pyodide.proxies.proxy_ref import ProxyRef
|
|
102
|
+
|
|
103
|
+
if type(x) in [int, str, float, bool, type(None)]:
|
|
104
|
+
return x
|
|
105
|
+
|
|
106
|
+
if callable(x):
|
|
107
|
+
if isinstance(x, pyodide.ffi.JsProxy):
|
|
108
|
+
return x
|
|
109
|
+
|
|
110
|
+
import asyncio
|
|
111
|
+
|
|
112
|
+
if asyncio.iscoroutine(x):
|
|
113
|
+
raise NotImplementedError(
|
|
114
|
+
"coroutines cannot be export by setup() yet, see "
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
# Load helper implemented in TypeScript in ipymuvue_js.ts
|
|
118
|
+
from ipymuvue_js import asVueCompatibleFunction
|
|
119
|
+
|
|
120
|
+
return asVueCompatibleFunction(
|
|
121
|
+
create_proxy(x, owner),
|
|
122
|
+
python_compatible_js,
|
|
123
|
+
vue_compatible_js,
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
if isinstance(x, pyodide.ffi.JsProxy):
|
|
127
|
+
if reference is not False:
|
|
128
|
+
# Note that we are not yet checking whether the insides of this
|
|
129
|
+
# object contain no PyProxy instances.
|
|
130
|
+
return x
|
|
131
|
+
else:
|
|
132
|
+
# Load helper implemented in TypeScript in ipymuvue_js.ts
|
|
133
|
+
from ipymuvue_js import clone, cloneDeep
|
|
134
|
+
|
|
135
|
+
return clone(x) if shallow else cloneDeep(x)
|
|
136
|
+
|
|
137
|
+
if isinstance(x, ProxyRef):
|
|
138
|
+
assert is_vue_ref(x._ref)
|
|
139
|
+
return vue_compatible(x._ref, reference=reference, shallow=shallow)
|
|
140
|
+
|
|
141
|
+
if isinstance(x, ObjectWrapper):
|
|
142
|
+
return vue_compatible(x._object, reference=reference, shallow=shallow)
|
|
143
|
+
|
|
144
|
+
if isinstance(x, ArrayWrapper):
|
|
145
|
+
return vue_compatible(x._array, reference=reference, shallow=shallow)
|
|
146
|
+
|
|
147
|
+
from collections.abc import Sequence
|
|
148
|
+
|
|
149
|
+
if isinstance(x, Sequence):
|
|
150
|
+
if reference is True:
|
|
151
|
+
raise TypeError(
|
|
152
|
+
"cannot call Vue API with this Python sequence; use vue_compatible(..., reference=False) to create a deep clone that can be consumed by thue Vue API"
|
|
153
|
+
)
|
|
154
|
+
else:
|
|
155
|
+
y = js.Array.new()
|
|
156
|
+
for item in x:
|
|
157
|
+
y.push(vue_compatible(item, reference=None, shallow=shallow))
|
|
158
|
+
return y
|
|
159
|
+
|
|
160
|
+
from collections.abc import Mapping
|
|
161
|
+
|
|
162
|
+
if isinstance(x, Mapping):
|
|
163
|
+
if reference is True:
|
|
164
|
+
raise TypeError(
|
|
165
|
+
"cannot call Vue API with this Python mapping; use vue_compatible(..., reference=False) to create a deep clone that can be consumed by the Vue API"
|
|
166
|
+
)
|
|
167
|
+
else:
|
|
168
|
+
o = js.Object.new()
|
|
169
|
+
for key in x.keys():
|
|
170
|
+
if not isinstance(key, (str, int)):
|
|
171
|
+
raise TypeError(f"key must be str or int but was {type(key)}")
|
|
172
|
+
strkey = str(key)
|
|
173
|
+
if hasattr(o, strkey):
|
|
174
|
+
raise ValueError("duplicate key in dict after conversion to str")
|
|
175
|
+
setattr(
|
|
176
|
+
o, strkey, vue_compatible(x[key], reference=None, shallow=shallow)
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
return o
|
|
180
|
+
|
|
181
|
+
raise NotImplementedError(f"cannot wrap this {type(x)} yet")
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
_owner = ContextVar("owner", default=None)
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
@contextmanager
|
|
188
|
+
def owner(owner):
|
|
189
|
+
r"""
|
|
190
|
+
Tie the lifetime of any proxies created during this context to ``owner``.
|
|
191
|
+
|
|
192
|
+
If no owner has been set when creating a proxy (of a callable) a warning is
|
|
193
|
+
emitted and we are never going to release the proxy (and leak memory).
|
|
194
|
+
|
|
195
|
+
If ``owner`` is a callable, it gets called with the proxy once created to
|
|
196
|
+
register its own lifetime handling (or just silence the warning.)
|
|
197
|
+
|
|
198
|
+
If ``owner`` is any other JavaScript object, we register it with a
|
|
199
|
+
``FinalizationRegistry`` in JavaScript.
|
|
200
|
+
"""
|
|
201
|
+
import pyodide.ffi
|
|
202
|
+
|
|
203
|
+
if owner is None:
|
|
204
|
+
pass
|
|
205
|
+
elif callable(owner):
|
|
206
|
+
pass
|
|
207
|
+
elif isinstance(owner, pyodide.ffi.JsProxy):
|
|
208
|
+
pass
|
|
209
|
+
else:
|
|
210
|
+
raise TypeError("owner must be None, a callable, or a JsProxy")
|
|
211
|
+
|
|
212
|
+
token = _owner.set(owner)
|
|
213
|
+
try:
|
|
214
|
+
yield None
|
|
215
|
+
finally:
|
|
216
|
+
_owner.reset(token)
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
def create_proxy(x, owner=None):
|
|
220
|
+
r"""
|
|
221
|
+
Create a JavaScript JsProxy proxy of ``x`` (typically a callable.) This
|
|
222
|
+
wraps pyodide's ``create_proxy`` to get better control over the lifetime of
|
|
223
|
+
``x``.
|
|
224
|
+
|
|
225
|
+
If ``owner`` is ``None``, we try to deduce the ownership from a context
|
|
226
|
+
variable. See :meth:`owner` for details.
|
|
227
|
+
"""
|
|
228
|
+
import pyodide.ffi
|
|
229
|
+
|
|
230
|
+
proxy = pyodide.ffi.create_proxy(x)
|
|
231
|
+
|
|
232
|
+
if owner is None:
|
|
233
|
+
owner = _owner.get()
|
|
234
|
+
|
|
235
|
+
if owner is None:
|
|
236
|
+
js.console.warn(
|
|
237
|
+
f"No owner set when creating proxy of {x}. Memory will be leaked. Wrap your code in a `with ipymuvue.pyodide.proxies.owner(...)` context to avoid this warning."
|
|
238
|
+
)
|
|
239
|
+
|
|
240
|
+
elif callable(owner):
|
|
241
|
+
owner(proxy)
|
|
242
|
+
elif isinstance(owner, pyodide.ffi.JsProxy):
|
|
243
|
+
_registry.register(owner, proxy)
|
|
244
|
+
else:
|
|
245
|
+
raise NotImplementedError("cannot handle this kind of owner yet")
|
|
246
|
+
|
|
247
|
+
# Enable to understand memory leaks.
|
|
248
|
+
# print("created proxy for", x, "owner by", owner)
|
|
249
|
+
|
|
250
|
+
return proxy
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
def release_proxy(proxy):
|
|
254
|
+
# Enable to understand memory leaks.
|
|
255
|
+
# print("releasing proxy", proxy)
|
|
256
|
+
|
|
257
|
+
import pyodide.ffi
|
|
258
|
+
|
|
259
|
+
pyodide.ffi.destroy_proxies(js.Array.new([proxy]))
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
# Singleton proxies of these conversion functions with infinite lifetime
|
|
263
|
+
python_compatible_js = create_proxy(python_compatible, owner=lambda proxy: None)
|
|
264
|
+
vue_compatible_js = create_proxy(vue_compatible, owner=lambda proxy: None)
|
|
265
|
+
release_proxy_js = create_proxy(release_proxy, owner=lambda proxy: None)
|
|
266
|
+
|
|
267
|
+
_registry = js.FinalizationRegistry.new(release_proxy_js)
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
r"""
|
|
2
|
+
Wrappers for JavaScript objects.
|
|
3
|
+
|
|
4
|
+
pyodide wraps JavaScript objects with a PyProxy. However, that proxy does not
|
|
5
|
+
provide them with a Python dict like interface that most Python programmers
|
|
6
|
+
expect. Here we provide such a wrapper :class:`ObjectWrapper` and a specialized
|
|
7
|
+
version, :class:`ProxyDict` that makes sure that no actual Python objects end
|
|
8
|
+
up in a Vue proxy without converting them to native JavaScript objects first.
|
|
9
|
+
"""
|
|
10
|
+
# ******************************************************************************
|
|
11
|
+
# Copyright (c) 2022 Julian Rüth <julian.rueth@fsfe.org>
|
|
12
|
+
#
|
|
13
|
+
# ipymuvue is free software: you can redistribute it and/or modify
|
|
14
|
+
# it under the terms of the GNU General Public License as published by
|
|
15
|
+
# the Free Software Foundation, either version 3 of the License, or
|
|
16
|
+
# (at your option) any later version.
|
|
17
|
+
#
|
|
18
|
+
# ipymuvue is distributed in the hope that it will be useful,
|
|
19
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
20
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
21
|
+
# GNU General Public License for more details.
|
|
22
|
+
#
|
|
23
|
+
# You should have received a copy of the GNU General Public License
|
|
24
|
+
# along with ipymuvue. If not, see <https://www.gnu.org/licenses/>.
|
|
25
|
+
# ******************************************************************************
|
|
26
|
+
|
|
27
|
+
from collections.abc import MutableMapping
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class ObjectWrapper(MutableMapping):
|
|
31
|
+
r"""
|
|
32
|
+
Wraps a JavaScript object with a Python dict interface.
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
def __init__(self, object):
|
|
36
|
+
if not object.typeof == "object":
|
|
37
|
+
raise TypeError("object must be a JavaScript object")
|
|
38
|
+
|
|
39
|
+
super().__setattr__("_object", object)
|
|
40
|
+
|
|
41
|
+
def _vue_compatible(self, object):
|
|
42
|
+
from ipymuvue.pyodide.proxies import vue_compatible
|
|
43
|
+
|
|
44
|
+
return vue_compatible(object)
|
|
45
|
+
|
|
46
|
+
def __delitem__(self, key):
|
|
47
|
+
raise Exception("not implemented __delitem__")
|
|
48
|
+
|
|
49
|
+
def __getattr__(self, key):
|
|
50
|
+
if not isinstance(key, (int, str)):
|
|
51
|
+
raise TypeError(f"key must be int or str but was {type(key)}")
|
|
52
|
+
|
|
53
|
+
from ipymuvue.pyodide.proxies import python_compatible
|
|
54
|
+
|
|
55
|
+
return python_compatible(getattr(self._object, str(key)))
|
|
56
|
+
|
|
57
|
+
def __setattr__(self, key, value):
|
|
58
|
+
if not isinstance(key, (int, str)):
|
|
59
|
+
raise TypeError(f"key must be int or str but was {type(key)}")
|
|
60
|
+
|
|
61
|
+
setattr(self._object, str(key), self._vue_compatible(value))
|
|
62
|
+
|
|
63
|
+
def __setitem__(self, key, value):
|
|
64
|
+
self.__setattr__(key, value)
|
|
65
|
+
|
|
66
|
+
def __iter__(self):
|
|
67
|
+
import js
|
|
68
|
+
|
|
69
|
+
for key in js.Object.keys(self._object):
|
|
70
|
+
yield key
|
|
71
|
+
|
|
72
|
+
def __len__(self):
|
|
73
|
+
return len(self._object)
|
|
74
|
+
|
|
75
|
+
def __getitem__(self, key):
|
|
76
|
+
try:
|
|
77
|
+
return self.__getattr__(key)
|
|
78
|
+
except AttributeError:
|
|
79
|
+
raise KeyError(key) from None
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
class ProxyDict(ObjectWrapper):
|
|
83
|
+
r"""
|
|
84
|
+
Wraps a Vue proxy with a Python dict interface.
|
|
85
|
+
"""
|
|
86
|
+
|
|
87
|
+
def __init__(self, proxy):
|
|
88
|
+
from ipymuvue.pyodide.types import is_vue_proxy
|
|
89
|
+
|
|
90
|
+
if not is_vue_proxy(proxy):
|
|
91
|
+
raise TypeError("proxy must be a Vue proxy")
|
|
92
|
+
|
|
93
|
+
super().__init__(proxy)
|
|
94
|
+
|
|
95
|
+
def _vue_compatible(self, object):
|
|
96
|
+
from ipymuvue.pyodide.proxies import vue_compatible
|
|
97
|
+
|
|
98
|
+
return vue_compatible(object, reference=False)
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
r"""
|
|
2
|
+
Wrapper for a Vue.js Ref.
|
|
3
|
+
|
|
4
|
+
We provide a Vue.js Ref with a more convenient interface from Python
|
|
5
|
+
and make sure that no Python objects that Vue.js cannot understand
|
|
6
|
+
enter the Vue.js API.
|
|
7
|
+
"""
|
|
8
|
+
# ******************************************************************************
|
|
9
|
+
# Copyright (c) 2022 Julian Rüth <julian.rueth@fsfe.org>
|
|
10
|
+
#
|
|
11
|
+
# ipymuvue is free software: you can redistribute it and/or modify
|
|
12
|
+
# it under the terms of the GNU General Public License as published by
|
|
13
|
+
# the Free Software Foundation, either version 3 of the License, or
|
|
14
|
+
# (at your option) any later version.
|
|
15
|
+
#
|
|
16
|
+
# ipymuvue is distributed in the hope that it will be useful,
|
|
17
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
18
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
19
|
+
# GNU General Public License for more details.
|
|
20
|
+
#
|
|
21
|
+
# You should have received a copy of the GNU General Public License
|
|
22
|
+
# along with ipymuvue. If not, see <https://www.gnu.org/licenses/>.
|
|
23
|
+
# ******************************************************************************
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class ProxyRef:
|
|
27
|
+
r"""
|
|
28
|
+
Wraps a Vue ref object with a Python property interface.
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
def __init__(self, ref):
|
|
32
|
+
from ipymuvue.pyodide.types import is_vue_ref
|
|
33
|
+
|
|
34
|
+
if not is_vue_ref(ref):
|
|
35
|
+
raise TypeError("ref must be a Vue ref")
|
|
36
|
+
self._ref = ref
|
|
37
|
+
|
|
38
|
+
@property
|
|
39
|
+
def value(self):
|
|
40
|
+
# For a readonly ref, Python does not see its .value attribute so we
|
|
41
|
+
# run the .value in JavaScript.
|
|
42
|
+
from ipymuvue.pyodide.proxies import python_compatible
|
|
43
|
+
|
|
44
|
+
# Load Vue from ipymuvue_js.ts implemented in TypeScript
|
|
45
|
+
from ipymuvue_js import Vue
|
|
46
|
+
|
|
47
|
+
return python_compatible(Vue.unref(self._ref))
|
|
48
|
+
|
|
49
|
+
@value.setter
|
|
50
|
+
def value(self, value):
|
|
51
|
+
from ipymuvue.pyodide.proxies import vue_compatible
|
|
52
|
+
|
|
53
|
+
self._ref.value = vue_compatible(value, reference=False)
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
r"""
|
|
2
|
+
Type checks for objects coming out of the Vue.js API.
|
|
3
|
+
"""
|
|
4
|
+
# ******************************************************************************
|
|
5
|
+
# Copyright (c) 2022 Julian Rüth <julian.rueth@fsfe.org>
|
|
6
|
+
#
|
|
7
|
+
# ipymuvue is free software: you can redistribute it and/or modify
|
|
8
|
+
# it under the terms of the GNU General Public License as published by
|
|
9
|
+
# the Free Software Foundation, either version 3 of the License, or
|
|
10
|
+
# (at your option) any later version.
|
|
11
|
+
#
|
|
12
|
+
# ipymuvue is distributed in the hope that it will be useful,
|
|
13
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
14
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
15
|
+
# GNU General Public License for more details.
|
|
16
|
+
#
|
|
17
|
+
# You should have received a copy of the GNU General Public License
|
|
18
|
+
# along with ipymuvue. If not, see <https://www.gnu.org/licenses/>.
|
|
19
|
+
# ******************************************************************************
|
|
20
|
+
|
|
21
|
+
# The interface to JavaScript, only available when running in pyodide.
|
|
22
|
+
import js
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def is_vue_proxy(x):
|
|
26
|
+
r"""
|
|
27
|
+
Return whether ``x`` is a un unwrapped reactive Vue Proxy.
|
|
28
|
+
"""
|
|
29
|
+
import pyodide.ffi
|
|
30
|
+
from ipymuvue_js import Vue
|
|
31
|
+
|
|
32
|
+
return isinstance(x, pyodide.ffi.JsProxy) and Vue.isProxy(x)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def is_vue_ref(x):
|
|
36
|
+
r"""
|
|
37
|
+
Return whether ``x`` is a un unwrapped reactive Vue Ref.
|
|
38
|
+
"""
|
|
39
|
+
import pyodide.ffi
|
|
40
|
+
from ipymuvue_js import Vue
|
|
41
|
+
|
|
42
|
+
return isinstance(x, pyodide.ffi.JsProxy) and Vue.isRef(x)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
# Types for Runtime Type Checks of props
|
|
46
|
+
String = js.String
|
|
47
|
+
Number = js.Number
|
|
48
|
+
Boolean = js.Boolean
|
|
49
|
+
Array = js.Array
|
|
50
|
+
Object = js.Object
|
|
51
|
+
Date = js.Date
|
|
52
|
+
Function = js.Function
|
|
53
|
+
Symbol = js.Symbol
|